mirror of
https://github.com/mmueller41/genode.git
synced 2026-01-21 12:32:56 +01:00
Merge branch 'master' of github.com:mmueller41/genode
This commit is contained in:
61
doc/news.txt
61
doc/news.txt
@@ -4,6 +4,67 @@
|
||||
===========
|
||||
|
||||
|
||||
Sculpt OS release 22.10 | 2022-10-13
|
||||
####################################
|
||||
|
||||
| Sculpt OS 22.10 is a maintenance release of our Genode-based
|
||||
| general-purpose OS. It imposes a new rigid regime to the management of
|
||||
| low-level devices, improves USB hotplug support, and comes with numerous
|
||||
| performance optimizations.
|
||||
|
||||
The just released version 22.10 of the Sculpt operating system bears the fruit
|
||||
of our year-long effort to apply the rigidity of Genode's architecture to the
|
||||
management of PCI configuration and device interrupts. This sweeping change
|
||||
left no single device driver unturned. If we did our job right, you should not
|
||||
notice any visible difference from the previous Sculpt version.
|
||||
|
||||
However, you should definitely _feel_ a difference when using the new version.
|
||||
We put several performance optimizations in place - from accelerated system
|
||||
startup, over increased network thoughput, to improved user-interface
|
||||
responsiveness. Moreover, we put much emphasis on stressing Sculpt's USB
|
||||
hotplug support, which includes the dynamic assignment and revocation of
|
||||
USB devices to and from virtual machines.
|
||||
|
||||
With respect to available software, Sculpt users can enjoy an updated
|
||||
Chromium engine - via the Falkon or Morph web browsers - and an updated
|
||||
audio driver based on OpenBSD 7.1.
|
||||
|
||||
Sculpt OS 22.10 is available as ready-to-use system image at the
|
||||
[https://genode.org/download/sculpt - Sculpt download page] along with
|
||||
updated [https://genode.org/documentation/articles/sculpt-22-10 - documentation].
|
||||
|
||||
|
||||
Genode OS Framework release 22.08 | 2022-08-31
|
||||
##############################################
|
||||
|
||||
| The overarching theme of Genode 22.08 is the emerging phone variant of
|
||||
| Sculpt OS, touching topics as diverse as USB ECM, Mali-400 GPU, SD-card
|
||||
| access, telephony, mobile-data connectivity, the Morph web browser, and a
|
||||
| custom user interface. Among the further highlights are new tracing tools,
|
||||
| improved network performance USB smart-card support, and VirtIO drivers for
|
||||
| RISC-V.
|
||||
|
||||
The vision of a Genode-based smart phone is certainly our most ambitious
|
||||
undertaking since we created Sculpt OS for the PC. Over the past two years, we
|
||||
relentlessly pursued this vision while targeting the PinePhone hardware.
|
||||
The scope of work reaches from custom firmware for the system-control
|
||||
processor, over kernel development, a staggering variety of device drivers, to
|
||||
the user-interface and application level. With Genode 22.08, those efforts
|
||||
culminate in a first complete system - a phone variant of Sculpt OS. The
|
||||
[https://genode.org/documentation/release-notes/22.08 - release documentation]
|
||||
tells the story behind this line of work in great detail.
|
||||
|
||||
Beside phone-related topics, the new release features new tooling for
|
||||
gathering and analyzing system traces that allow for holistic performance
|
||||
studies covering the interplay between components. One particular success
|
||||
story of the new trace recorder is a profoundly improved network performance.
|
||||
Further highlights are the support for USB smart cards via PKCS#11, VirtIO
|
||||
drivers for RISC-V, and the update of Qt5 to version 5.15.2.
|
||||
|
||||
For the complete picture, please enjoy the official
|
||||
[https:/documentation/release-notes/22.08 - release documentation of version 22.08...]
|
||||
|
||||
|
||||
Genode OS Framework release 22.05 | 2022-05-31
|
||||
##############################################
|
||||
|
||||
|
||||
866
doc/release_notes/22-08.txt
Normal file
866
doc/release_notes/22-08.txt
Normal file
@@ -0,0 +1,866 @@
|
||||
|
||||
|
||||
===============================================
|
||||
Release notes for the Genode OS Framework 22.08
|
||||
===============================================
|
||||
|
||||
Genode Labs
|
||||
|
||||
|
||||
|
||||
The overarching topic of version 22.08 is the emerging phone version of the
|
||||
Genode-based Sculpt OS, targeting the PinePhone. The immense breadth and depth
|
||||
of this line of work presented in Section [Genode coming to the phone]
|
||||
touches topics as diverse as telephony, mobile-data connectivity, a custom
|
||||
user interface, a mobile web browser, the GPU, SD-card access, USB, and audio
|
||||
control.
|
||||
|
||||
With the growing sophistication of Genode-based systems, performance
|
||||
optimizations come more and more into focus. Aided by the new tools introduced
|
||||
in Section [Enhanced tooling for system tracing], we were able to profoundly
|
||||
improve the network performance of Genode's user-level network routing
|
||||
component. Speaking of optimizations, the current release reduces the CPU
|
||||
overhead of our Linux device-driver environment
|
||||
(Section [Linux-device-driver environment (DDE Linux)]) and
|
||||
improves the responsiveness of GUIs based on Genode's menu-view component
|
||||
(Section [Menu-view performance]).
|
||||
|
||||
Further topics of the new version reach from our forthcoming platform-driver
|
||||
consolidation across PC and ARM-based devices, over the use of USB smart
|
||||
cards, to new VirtIO drivers on RISC-V.
|
||||
|
||||
|
||||
Genode coming to the phone
|
||||
##########################
|
||||
|
||||
Our [https://genode.org/about/road-map - road map] for this year states the
|
||||
goal of reaching a useful base line of functionality of Genode on the
|
||||
PinePhone. This entails the principle ability to use the device as a phone -
|
||||
receiving and issuing voice calls - and a mobile internet browser. Once
|
||||
reached, this base line of functionality will allow us to routinely use Genode
|
||||
on the device ("eating our own dog food"), experience pain points, guide
|
||||
optimization efforts towards user-visible areas that matter, and faithfully
|
||||
evaluate non-functional aspects like battery lifetime with real-world work
|
||||
loads under realistic conditions.
|
||||
|
||||
For the Genode-based phone, we pursue the combination of a minimally-complex
|
||||
trustworthy base system with a generally untrusted Web browser as application
|
||||
runtime. The feature set of the base system corresponds to the bare-bones
|
||||
[https://genode.org/download/sculpt - Sculpt OS] extended with appliance-like
|
||||
feature-phone functionality. Thanks to Sculpt's rigid component-based
|
||||
structure and the overall low complexity, it promises high reliability and
|
||||
security. The application runtime is hosted on top of the base system without
|
||||
tainting the assurance of the base system. In contrast to the appliance-like
|
||||
and rather static feature set of the base system, the application runtime
|
||||
anticipates a great variety of modern-day application scenarios, universally
|
||||
expected commodity user-interface paradigms, and fast-paced software updates.
|
||||
E.g., we aspire the use of WebRTC-based video conferencing via Jitsi as one
|
||||
reference scenario.
|
||||
|
||||
Since we succeeded in bringing the Chromium web engine - the base technology
|
||||
of most modern web browsers - to life as a
|
||||
[https://genodians.org/nfeske/2022-01-27-browser-odyssey - native Genode component],
|
||||
users of Sculpt OS are able to use a fully featured web browser without
|
||||
relying on virtualization. With the use case of the browser on a mobile phone
|
||||
in sight, we already ensured that the browser would work on 64-bit ARM
|
||||
hardware. However, whereas we could showcase the technical feasibility of
|
||||
Chromium on Genode, the practical usability eventually depends on a suitable
|
||||
mobile user experience, which was largely disregarded by the desktop-oriented
|
||||
Falkon browser that we enabled on Genode.
|
||||
|
||||
|
||||
Assessment
|
||||
----------
|
||||
|
||||
Fortunately, we discovered the Morph web browser while experimenting with
|
||||
[https://xnux.eu/p-boot-demo/ - various Linux distributions] on the PinePhone.
|
||||
Among the various alternatives to Android, the Ubuntu Touch UI - alongside
|
||||
Sailfish OS - stood out for its refined user experience, subjectively.
|
||||
The unobtrusive Morph browser as used by default on Ubuntu Touch left a
|
||||
particularly good impression on us. To our delight, we found that this
|
||||
browser relies on Qt5 and the Chromium web engine as its foundation, both of
|
||||
which we already had enabled on Genode. Out of this observation grew the idea
|
||||
of reusing the Morph browser as application runtime on our Genode-based phone.
|
||||
But we had to consider several risks.
|
||||
|
||||
First, would the heaviness of Chromium overwhelm the rather resource-constrained
|
||||
PinePhone hardware when executed on Genode? In contrast to Linux, Genode's
|
||||
POSIX environment is less sophisticated and - most importantly - does not
|
||||
provide the over-provisioning of memory resources. The latter could be a show
|
||||
stopper.
|
||||
|
||||
Second, the build mechanics of the browser deviate from the beaten track we
|
||||
covered so far, specifically the use of QMake. The Morph browser
|
||||
unconditionally depends on CMake as build tool. Even though we gathered
|
||||
[https://genodians.org/nfeske/2019-11-25-goa - early experiences], with using
|
||||
CMake for building Genode executables, we did not attempt using CMake for
|
||||
complex Qt5 applications targeting Genode so far.
|
||||
|
||||
Finally, we discovered a so-called Ubuntu-Touch-UI toolkit as an
|
||||
additional dependency over Qt5. It presumably extends Qt5's QML with
|
||||
custom user-interface widgets for mobile user interfaces. In contrast
|
||||
to the multi-platform Qt5 framework, Ubuntu Touch generally targets
|
||||
Linux only, which raised a number of concerns with respect to hidden
|
||||
assumptions on the underlying platform. For example, the expectation
|
||||
of a certain service manager, the direct use of the Linux kernel interface,
|
||||
or accidentally complex library dependencies.
|
||||
|
||||
|
||||
Methodology
|
||||
===========
|
||||
|
||||
As practiced during our work with bringing the Chromium-based Falkon web
|
||||
browser to Genode, we took several intermediate steps to mitigate technical
|
||||
risks as far as possible.
|
||||
|
||||
Pruning dependencies
|
||||
--------------------
|
||||
|
||||
The first step was building the Morph browser from source for its regular
|
||||
designated target platform, namely Linux. This step allowed us to validate the
|
||||
functionality of the browser built from source as opposed to merely testing a
|
||||
binary package. During this process, we learned about the mandatory dependence
|
||||
on CMake as build tool. We also identified the following library dependencies
|
||||
as sources of uncertainty.
|
||||
|
||||
*Ubuntu-UI toolkit* is a collection of QML widgets for smartphone apps.
|
||||
It is built via QMake and comes with its own set of dependencies.
|
||||
We were specifically concerned by QtSystemInfo, QtOrganizer, D-Bus, and
|
||||
gettext. Genode has no meaningful equivalent to any of these dependencies.
|
||||
The *Ubuntu Themes* dependency comprises graphical assets, used on Ubuntu
|
||||
Touch. *Ubuntu-UI extras* extends Qt's feature set by functionality like the
|
||||
'TabsBar' QML-Widget introduces additional transitive dependencies
|
||||
such as the [https://www.cups.org/ - CUPS printing system] or
|
||||
the [https://exiv2.org/ - Exiv2] image metadata library.
|
||||
|
||||
Further dependencies worth noting are QNetworkInterface, QtConcurrent, QtDBus,
|
||||
QtSystemInfo, unity-action-api, and D-Bus. Those libraries do not exist in
|
||||
Genode and may even raise conceptual problems. For example, the D-Bus
|
||||
inter-component mechanism on Linux is not in line with Genode's
|
||||
capability-based inter-component communication.
|
||||
|
||||
With the first manually built executable of Morph created on Linux, we could
|
||||
repeatedly remove dependencies piece by piece and validate the functioning of
|
||||
the browser after each step. We ultimately reached a point where most of the
|
||||
library dependencies could be cut off while the core functionality of the
|
||||
browser - the ability to view web pages - stayed intact. The resulting
|
||||
minimized version of the Morph browser thereby served as starting point for
|
||||
the subsequent porting work to Genode.
|
||||
|
||||
Re-targeting to Genode
|
||||
----------------------
|
||||
|
||||
To stay as close as possible to the original browser, we decided to reuse the
|
||||
browser's build system by tweaking the CMake build tool such that the project
|
||||
could be cross compiled for Genode, similar to the approach we successfully
|
||||
employed for QMake in the past. At first, we targeted Genode/Linux on x86,
|
||||
which is still close to the browser's natural environment. Once the first
|
||||
version of the browser came to life, we immediately cross-validated the result
|
||||
on the 64-bit ARM architecture as this is our primary target. Subsequently, we
|
||||
moved away from Linux by moving the browser over to NOVA (on Sculpt) on PC
|
||||
hardware as well as our custom base-hw microkernel in order to target the
|
||||
actual PinePhone.
|
||||
|
||||
[image touch_ui]
|
||||
Ubuntu-Touch UI gallery demo running on Genode
|
||||
|
||||
The methodology mirrored in large parts the approach we took for the original
|
||||
porting of the Chromium web engine, but it was a much smoother experience
|
||||
given that all road blocks we encountered during our Chromium work are solved
|
||||
problems by now. Image [touch_ui] shows the browser's underlying
|
||||
user-interface tool kit in action, running directly on Genode. Image [morph]
|
||||
shows the Morph browser hosted in Genode's window system.
|
||||
|
||||
[image morph]
|
||||
Morph browser running on Genode
|
||||
|
||||
|
||||
Unexpected caveats
|
||||
==================
|
||||
|
||||
However, the smooth ride of re-targeting the browser to Genode ended once
|
||||
we discovered the extremely poor interactive performance of the browser
|
||||
running on Genode. This is in contrast to our prior experience with the
|
||||
Chromium-based Falkon browser which achieved comparable performance to Linux.
|
||||
|
||||
The performance degradation originated from the Ubuntu-UI toolkit, which
|
||||
has a hard dependency on OpenGL despite being built atop the Qt5 framework.
|
||||
In several instances, the Ubuntu-UI toolkit accesses the OpenGL context
|
||||
directly, which is handled by a software fallback implementation in the
|
||||
Mesa library. We found the removal of those offending accesses infeasible
|
||||
because this change would cause several widgets appearing incomplete.
|
||||
To attain the visual completeness of the user interface, we also had to
|
||||
enhance the Genode-specific back end of Qt (QPA). However, even though
|
||||
we achieved correctly looking results, the performance of Mesa3D without
|
||||
GPU acceleration made the user interface practically unusable, even on
|
||||
powerful PC hardware, not speaking of the resource-constrained PinePhone.
|
||||
We came to the conclusion that the Morph browser's hard dependency
|
||||
on hardware-accelerated graphics cannot be worked around. This realization,
|
||||
in turn, spawned the line of work reported in
|
||||
Section [Hardware-accelerated graphics].
|
||||
|
||||
As another - but arguably much less dramatic - caveat, we found the touch user
|
||||
interface behaving strangely in some situations when running on Genode. The
|
||||
reason turned out to be a disparity of Genode's notion of touch-release events
|
||||
from the expectations of Qt. Whereas Genode's input-event interface does not
|
||||
report a positional value of a touch-release event, Qt expects a positional
|
||||
value that corresponds to the original touch event. Fortunately, once this
|
||||
disparity had been identified, we could easily emulate the expected behavior
|
||||
locally in Genode's QPA plugin.
|
||||
|
||||
|
||||
Hardware-accelerated graphics
|
||||
=============================
|
||||
|
||||
As mentioned above, we were taken by surprise by the hard dependency of the
|
||||
Morph browser on GPU-accelerated graphics. Even though we have explored the
|
||||
principle use of a GPU on an ARM-based platform before, our prior line of work
|
||||
was targeting the Vivante GPU of the NXP i.MX8 SoC, which is different from
|
||||
the Mali-400 GPU as present in the PinePhone's A64 SoC. Originally, we did not
|
||||
plan to deal with the PinePhone's GPU at the current stage. But the
|
||||
requirement of the Morph browser abruptly changed our priorities.
|
||||
|
||||
As a rapid experiment, we took the challenge to port the Lima driver for the
|
||||
Mali-400 GPU from Linux to Genode and combine it with the matching user-level
|
||||
driver code of the Mesa library. Even though this experiment was pursued on
|
||||
short notice and risky, it was at least a tangible straw. To our delight,
|
||||
however, the first functional rapid prototype came to life after merely two
|
||||
weeks of work, which is almost an order of magnitude faster than our past
|
||||
efforts. The reason of this success is many-fold. First, our recently
|
||||
established methodology and tooling for porting Linux device drivers - as
|
||||
described in our comprehensive
|
||||
[https://genode.org/documentation/genode-platforms-22-05.pdf - Porting Guide] -
|
||||
streamlines the formerly labor-intensive grunt work. Second, we greatly
|
||||
benefited from our practical experience with GPUs accumulated over the past
|
||||
few years. And third, even though the Mali-400 is different from the Vivante
|
||||
GPU, the integration into the Linux GPU stack follows very similar patterns,
|
||||
unlike Intel GPUs. So we found our existing knowledge largely applicable.
|
||||
|
||||
[image glmark2]
|
||||
GLMark2 reference application using the GPU
|
||||
|
||||
Following the initial rapid prototype, we successively refined this work to
|
||||
the point where the GPU driver became usable for the Morph browser on the
|
||||
PinePhone. Thanks to the added driver, the interactive performance got boosted
|
||||
to an acceptable level.
|
||||
|
||||
|
||||
Mobile data connectivity
|
||||
========================
|
||||
|
||||
It goes without saying that a web browser requires network connectivity,
|
||||
which is a topic we had left unaddressed on the PinePhone until now.
|
||||
However, given our
|
||||
[https://genode.org/documentation/release-notes/22.05#Telephony - recent line]
|
||||
of modem-related work in the context of telephony, we foresaw a low-complexity
|
||||
solution to attain mobile data connectivity.
|
||||
|
||||
Today's LTE modems offer
|
||||
[https://genodians.org/ssumpf/2020-12-04-mbim - QMI or MBIM] protocol support
|
||||
in order to configure and handle mobile data connections. Both protocols are
|
||||
in binary format and require a separate USB device (called Wireless Mobile
|
||||
Communication Device). For Genode, this would mean to add support for this
|
||||
device to USB while additionally the QMI or MBIM library would have to be
|
||||
ported and adjusted to Genode. For the
|
||||
[https://www.quectel.com/product/lte-eg25-g - Quectel EG25 modem]
|
||||
in the PinePhone, we found a much simpler solution to handle mobile data
|
||||
connections. The modem can be configured to emulate a USB Ethernet device
|
||||
([https://en.wikipedia.org/wiki/Ethernet_over_USB - ECM device]).
|
||||
In this operational mode, the modem will automatically connect to the carrier
|
||||
and register itself as USB Ethernet device at the PinePhone's USB host
|
||||
controller. Genode can thereby access the device through the USB networking
|
||||
and CDC Ethernet drivers. The modem also offers a DHCP server and will hand
|
||||
out a local IP address upon a DHCP request to Genode. Internally the modem
|
||||
will use [https://en.wikipedia.org/wiki/Network_address_translation - NAT] in
|
||||
order to translate IP requests from Genode to the address received from the
|
||||
carrier.
|
||||
|
||||
As a prerequisite to conduct this solution, we had to enable a USB
|
||||
host-controller driver for the PinePhone. Of course, we took advantage of our
|
||||
modern DDE Linux porting approach for this work, which allowed to attain a
|
||||
functional USB driver in merely two weeks. This driver must be combined with
|
||||
our existing USB Ethernet driver (usb_net) that we swiftly extended to support
|
||||
ECM based devices.
|
||||
|
||||
With this driver infrastructure in place, the USB network device of the modem
|
||||
appears as uplink to Genode's NIC router. The NIC router, in turn,
|
||||
successfully obtains a local IP address that is network-translated by the
|
||||
modem. At the carrier side, IP network connectivity can be established by
|
||||
issuing AT-protocol commands over UART. So the first prototype of the
|
||||
low-level network connectivity worked as anticipated. With this practical way
|
||||
of keeping the complexity of binary configuration protocols out of the loop,
|
||||
we can maintain the low-complexity implementation of telephony and SIM
|
||||
configuration via the UART control channel while regarding IP connectivity -
|
||||
and the unavoidable complexity of USB - as an entirely complementary feature.
|
||||
|
||||
|
||||
Phone flavor of Sculpt OS
|
||||
=========================
|
||||
|
||||
Seeing the various puzzle pieces of the Morph browser scenario - GPU
|
||||
acceleration, data connectivity, the browser itself - coming together, it was
|
||||
time for the integration of those pieces into an overall system. The natural
|
||||
basis of such a Genode-based system is
|
||||
[https://genode.org/download/sculpt - Sculpt OS],
|
||||
which complements Genode with universally expected operating-system features
|
||||
such as interactive system configuration as well as the installation and
|
||||
deployment of software packages.
|
||||
|
||||
Sculpt OS was originally designed for PC-based use cases. Its administrative
|
||||
user interface is largely mouse and keyboard driven, and network connectivity
|
||||
is usually attained by a wired or wireless LAN connection. Although we
|
||||
presented a first version of
|
||||
[https://fosdem.org/2022/schedule/event/nfeske/ - Sculpt OS on the PinePhone]
|
||||
earlier this year, the call for a touch-oriented user interface is more than
|
||||
obvious. Hence, we went forward with creating a phone-specific variant
|
||||
of Sculpt. Similar to the original Sculpt OS, the system consists of two
|
||||
largely isolated domains, the administrative domain called Leitzentrale and
|
||||
the domain of user-installed components called desktop. The user can switch
|
||||
between both domains at any time using a secure attention key or gesture.
|
||||
On the phone, the Leitzentrale domain plays the role of a feature-phone
|
||||
appliance that provides the most fundamental device functionality such
|
||||
as the interaction with the SIM card, power control, telephony, network
|
||||
configuration, storage management, and software installation. We approached
|
||||
the concept of the user interface from a clean slate striving for simplicity.
|
||||
|
||||
[image sim_pin]
|
||||
Emerging mobile-phone flavor of Sculpt OS
|
||||
|
||||
As the first use case, we addressed telephony, displaying incoming calls,
|
||||
presenting the options for accepting/rejecting calls, and initiating calls
|
||||
using a dial pad. By modelling these scenarios, we could validate the
|
||||
user-interface concept of the evolving phone version of Sculpt's Leitzentrale.
|
||||
|
||||
|
||||
User interaction with the SIM card
|
||||
==================================
|
||||
|
||||
The administrative user interface mentioned above must be matched by the
|
||||
underlying middleware that talks to the modem. Remember that our
|
||||
[https://genode.org/documentation/release-notes/22.05#Telephony - original]
|
||||
telephony scenario relied on the manual use of the modem's AT commands.
|
||||
We ultimately have to control the modem's control channel by software using an
|
||||
AT protocol stack. To bridge this gap with the lowest complexity possible, we
|
||||
created a simple AT protocol implementation that is specifically designed for
|
||||
Genode's state-driven component model.
|
||||
The modem driver - hosting the AT protocol driver - accepts a configuration
|
||||
that expresses the desired state (as opposed to desired actions). For example,
|
||||
a configuration may look as simple as follows.
|
||||
|
||||
! <config speaker="yes" pin="1234">
|
||||
! <call number="+49123123123"/>
|
||||
! </config>
|
||||
|
||||
The AT protocol implementation takes this configuration and the current modem
|
||||
state as the basis for determining a sequence of modem commands needed to
|
||||
attain the desired state. For example, if the modem is not powered, the driver
|
||||
steps through the powering sequence. Or in case the SIM PIN is required, the
|
||||
driver supplies the corresponding command to supply the configured PIN.
|
||||
|
||||
To allow interactive usage, the driver supports dynamic reconfiguration.
|
||||
E.g., to cancel the outbound call of the example above, the configuration
|
||||
would be updated with the '<call>' node removed. Given this approach, an
|
||||
interactive user interface comes down to generating such simple
|
||||
configurations.
|
||||
|
||||
Vice versa, the driver exports the modem's state as a state report, which is
|
||||
updated whenever the modem state changes. E.g., an incoming call is reflected
|
||||
to the consumer of this state report with all information relevant for an
|
||||
interactive user interface. For example, the state report entails the power
|
||||
state, PIN state, and call states (incoming, outbound, alerting, rejected).
|
||||
This design nicely hides the peculiarities of the AT protocol from Genode's
|
||||
component interfaces.
|
||||
|
||||
At the current stage, with less than 1000 lines of code, the AT protocol
|
||||
implementation suffices for basic telephony needs, supporting the interaction
|
||||
with the SIM card, managing call states, initiating calls, and driving the
|
||||
modem power up and down. It also takes care of establishing the modem
|
||||
configuration needed for USB ECM networking.
|
||||
|
||||
|
||||
Current state
|
||||
=============
|
||||
|
||||
The current version of the phone variant of Sculpt OS is able to control the
|
||||
power state of the modem, interact with the SIM card (PIN entry), initiate
|
||||
phone calls via a dial pad, pick up inbound calls, establish mobile-data
|
||||
network connectivity, and deploy a preconfigured application scenario.
|
||||
The interactive switching between the base system and the application runtime
|
||||
can be triggered at any time by touching the left border of the touch screen.
|
||||
|
||||
[image sculpt_pinephone]
|
||||
The runtime graph of the base system (left) reveals the relationships of the
|
||||
Morph browser with other components (right).
|
||||
|
||||
This flavor of Sculpt OS evolves in the
|
||||
[https://github.com/nfeske/genode-allwinner - genode-allwinner] repository,
|
||||
specifically within the _sculpt/_ and _src/app/phone_manager/_ directories.
|
||||
The latter asserts the role of Sculpt's _gems/src/app/sculpt_manager_.
|
||||
We invite seasoned developers - especially those who are following the
|
||||
[https://genodians.org/nfeske/index - Pine-fun article series] - to experiment
|
||||
with the new phone variant. It can be built via the following command:
|
||||
|
||||
! built/arm_v8a$ make run/sculpt KERNEL=hw BOARD=pinephone SCULPT=phone
|
||||
|
||||
For a broader audience, we plan to provide a ready-to-use SD-card image for
|
||||
the PinePhone in tandem with the next release of Sculpt OS.
|
||||
|
||||
|
||||
Enhanced tooling for system tracing
|
||||
###################################
|
||||
|
||||
Since release 13.08, Genode features a
|
||||
[https://genode.org/documentation/release-notes/13.08#Light-weight_event_tracing - light-weight event-tracing facility]
|
||||
that comes in form of core's TRACE service. Up to now, it has merely been used
|
||||
for capturing textual trace messages. The two prominent monitor components are
|
||||
the
|
||||
[https://genode.org/documentation/release-notes/18.02#New_trace-logging_component - trace_logger]
|
||||
and the
|
||||
[https://genode.org/documentation/release-notes/19.08#Tracing - VFS plugin]
|
||||
|
||||
The trace recorder is a new monitor component that is designed for binary trace
|
||||
formats. Currently, it supports the Common Trace Format (CTF) and pcapng.
|
||||
CTF is a compact and scalable format for storing event traces. It is supported
|
||||
by [https://www.eclipse.org/tracecompass/ - TraceCompass], an Eclipse-based
|
||||
tool for trace analysis and visualization. Pcapng is a packet-capture format
|
||||
used by Wireshark.
|
||||
|
||||
In order to support capturing network packets, we added a 'trace_eth_packet()'
|
||||
method to Genode's trace-policy API and equipped the NIC router with a
|
||||
'trace_packets' option to control packet capturing on domain level. For manual
|
||||
instrumentation of components, we also added a 'checkpoint()' method to the
|
||||
trace-policy API.
|
||||
|
||||
For more details, please refer to the following Genodians article.
|
||||
|
||||
:Identifying network-throughput bottlenecks with trace recording:
|
||||
|
||||
[https://genodians.org/jschlatow/2022-08-29-trace-recorder]
|
||||
|
||||
|
||||
Base framework and OS-level infrastructure
|
||||
##########################################
|
||||
|
||||
Networking optimizations
|
||||
========================
|
||||
|
||||
With the new trace recorder at hand, we took an effort in optimizing Genode's
|
||||
network throughput. First, we implemented a benchmark component called
|
||||
"nic_perf" that sends and/or receives an infinite stream of UDP packets in
|
||||
order to stimulate the involved networking components in separation. As a
|
||||
consequence of its central role, we particularly focused on the NIC router as
|
||||
a starting point.
|
||||
|
||||
As a base line, we took two 'nic_perf' components: one as a sender and the other
|
||||
as a receiver. By taking any copying or packet inspection out of the loop, we
|
||||
could verify that the packet-stream interface holds up to our expectations with
|
||||
respect to attainable throughput. However, as soon as we put a NIC router in
|
||||
between, the throughput dropped to approx. 10% of our base line. On older
|
||||
ThinkPads, this meant sub-gigabit throughput and on a Cortex-A9 @ 666MHz we
|
||||
barely jumped over the 100Mbit mark.
|
||||
|
||||
Since we were not able to explain the substantial decrease in packet throughput,
|
||||
we investigated with the help of the trace recorder and 'GENODE_LOG_TSC'.
|
||||
As it turned out, the NIC router spent most of its time with exception handling
|
||||
during routing-rule lookup, which is done for every packet. Since there are
|
||||
multiple types of rules, a lookup takes place for every rule type. If no rule
|
||||
was found for particular type, an exception was thrown and caught, which
|
||||
turned out to be incredibly expensive. We therefore eliminated exceptions from
|
||||
common-case code paths, more precisely from rule lookup, from ARP-cache
|
||||
lookup, and from packet allocation. The result impressed us with a tripled
|
||||
throughput.
|
||||
|
||||
Another bottleneck that we identified were frequent 'trigger_once' and
|
||||
'elapsed_ms' RPCs. Given that the NIC router only maintains very
|
||||
coarse-grained timeouts, such frequent RPCs to the timer seemed finical.
|
||||
Sparing the details, we were able to significantly reduce the number of
|
||||
these RPCs by relaxing the precision of the NIC router's time keeping.
|
||||
|
||||
Along the way, we identified a few more, minor, tweaks:
|
||||
|
||||
* We increased the default value of 'max_packets_per_signal' from 32 to 150.
|
||||
This value determines the maximum number of packets that are consumed from an
|
||||
interface at once.
|
||||
* We eliminated eager packet-stream signalling from the NIC router to improve
|
||||
batch processing of packets. With this change, packet-stream signals are only
|
||||
emitted once the NIC router processed all available or
|
||||
'max_packets_per_signal' packets.
|
||||
* We implemented incremental checksum update for UDP/TCP according to RFC1071.
|
||||
* We discovered and fixed a few corner cases in the packet-stream interface
|
||||
with respect to the signalling.
|
||||
* We fixed allocation errors in the 'ipxe_nic_drv' that popped up during high
|
||||
TX load.
|
||||
|
||||
In the end, we attained a ~5x speed up (exact values depending on the hardware)
|
||||
for the NIC router.
|
||||
|
||||
|
||||
Event-filter improvements for touch devices
|
||||
===========================================
|
||||
|
||||
The phone variant of Sculpt OS calls for a way to trigger certain low-level
|
||||
buttons or keys using the touch screen. In particular, the switch between the
|
||||
administrative user interface and the runtime system must be possible at any
|
||||
time. On the [https://genode.org/download/sculpt - PC version], this switch
|
||||
is triggered by pressing F12, which is remapped to KEY_DASHBOARD. Even though
|
||||
a physical button could be used on the phone in principle, there are three
|
||||
arguments in favor of a virtual key. First, there are only three physical
|
||||
buttons available (volume +/- and power) on the PinePhone. Remapping one of
|
||||
those buttons to KEY_DASHBOARD deprives the button of its original purpose.
|
||||
Second, the force needed for pressing a physical button may impede the
|
||||
ergonomics of the device depending on how often the switch is needed. And
|
||||
third, the physical buttons require a driver. When enabling a new device, this
|
||||
barrier can be nicely sidestepped by a virtual key.
|
||||
|
||||
Given this rationale, we extended Genode's event-filter component with a new
|
||||
'<touch-key>' filter type. Once added to the filter chain, it triggers an
|
||||
artificial key tap (a press event followed by a release event) whenever the
|
||||
user touches a preconfigured area on the touch screen. The filter node can
|
||||
host any number of '<tap>' sub nodes. Each sub node must define a rectangular
|
||||
area - using the attributes 'xpos', 'ypos', 'width', and 'height' - and the
|
||||
name of the tapped key as 'key' attribute.
|
||||
|
||||
! <touch-key>
|
||||
! <tap xpos="0" ypos="400" width="25" height="600" key="KEY_DASHBOARD"/>
|
||||
! ...
|
||||
! </touch-key>
|
||||
|
||||
The example above repurposes the 25 left-most pixels of the touch screen as
|
||||
dashboard key. When touched, a pair of press and release events is fired at
|
||||
once.
|
||||
|
||||
|
||||
Menu-view performance
|
||||
=====================
|
||||
|
||||
The administrative user interface of Sculpt OS is based on Genode's custom
|
||||
menu-view component, which renders and updates graphical dialogs based on
|
||||
high-level XML descriptions. Up to now, the component operated on Genode's
|
||||
GUI-session interface with alpha-channel support. However, the alpha channel
|
||||
noticeably impedes the software-rendering performance on lower-end devices
|
||||
like the PinePhone. In the latter case, we'd prefer to trade the nice-looking
|
||||
alpha blending for a better UI responsiveness.
|
||||
|
||||
We have now enhanced the menu-view component with two new optional
|
||||
configuration attributes 'opaque' and 'background'. Setting 'opaque' to "yes"
|
||||
suppresses the use of the alpha channel at the GUI session. This improves the
|
||||
drawing performance by 20% on the PinePhone. The 'background' attribute can be
|
||||
specified to define the reset color of the GUI buffer. It alleviates the need
|
||||
to create a frame widget for the top level, significantly reducing the costs
|
||||
for drawing the background pixels.
|
||||
|
||||
Finally, we found that the use of GCC's optimization level -O3 instead of the
|
||||
default level -O2 increases the drawing performance on the PinePhone by 30%.
|
||||
Combined, those optimizations result in an acceptable user experience of
|
||||
Sculpt's administrative user interface on the PinePhone.
|
||||
|
||||
|
||||
Device drivers
|
||||
##############
|
||||
|
||||
USB networking via Ethernet control model (ECM)
|
||||
===============================================
|
||||
|
||||
To implement mobile data connectivity on the PinePhone
|
||||
(Section [Mobile data connectivity]), we added USB host-controller support
|
||||
(EHCI) for the Allwinner A64 SoC to Genode by porting the corresponding
|
||||
host-controller driver from Linux using our DDE approach. Since our existing
|
||||
USB-over-Ethernet
|
||||
[https://github.com/genodelabs/genode/tree/master/repos/dde_linux/src/drivers/usb_net - driver]
|
||||
on Genode lacked support for the Ethernet Control Model, which is provided by
|
||||
the modem, we added support for ECM as well.
|
||||
|
||||
|
||||
GPU and Mesa driver for Mali-400
|
||||
================================
|
||||
|
||||
As mentioned in Section [Genode coming to the phone], we enabled the principle
|
||||
ability to use the Mali-400 GPU of the PinePhone under Genode. This support
|
||||
entails two parts. The first part is the low-level driver code called Lima
|
||||
that normally resides in the Linux kernel. This component provides a GPU
|
||||
session interface. We transplanted the driver code to a dedicated Genode
|
||||
component, which is hosted at the
|
||||
[https://github.com/genodelabs/genode-allwinner - genode-allwinner] repository.
|
||||
The second part is the user-level Mesa3D driver stack - hosted at the libports
|
||||
repository - that is linked local to the application and uses the GPU session
|
||||
to access the GPU.
|
||||
|
||||
The combination of both parts was successfully tested on the PinePhone and
|
||||
the Pine-A64-LTS V1.2 board. Given that the primary motivation for this
|
||||
line of work was our ambition to run the Morph web browser, we disregarded the
|
||||
multiplexing of the GPU for now. The GPU driver currently supports only one
|
||||
client at a time.
|
||||
|
||||
|
||||
SD-card driver for the PinePhone
|
||||
================================
|
||||
|
||||
In anticipation of running Sculpt OS on the PinePhone, we ported the Linux
|
||||
SD/MMC-card driver to Genode. The driver - hosted at the
|
||||
[https://github.com/genodelabs/genode-allwinner - genode-allwinner] repository -
|
||||
was successfully tested with the PinePhone and Pine-A64LTS V1.2 board. For the
|
||||
moment, only SD cards (no eMMC) are supported.
|
||||
The provided _a64_sd_card_drv.run_ script illustrates the integration and use
|
||||
of the driver.
|
||||
|
||||
|
||||
Linux-device-driver environment (DDE Linux)
|
||||
===========================================
|
||||
|
||||
Tickless idle operation
|
||||
-----------------------
|
||||
|
||||
The DDE-Linux emulation library and thereby all ported drivers now support
|
||||
the NO_HZ_IDLE Linux kernel configuration option, which disables periodic
|
||||
timer ticks when ported drivers are idle. With this option, energy and up to
|
||||
3% CPU time per driver can be preserved, which becomes significant especially
|
||||
if multiple ported drivers are in use in sophisticated scenarios like Sculpt
|
||||
OS.
|
||||
|
||||
Consistent use of SMP configuration
|
||||
-----------------------------------
|
||||
|
||||
All kernel threads in the Linux device driver ports are currently mapped to one
|
||||
and the same native Genode thread, using cooperative scheduling within the
|
||||
emulation environment. Intuitively, it does not make much sense to address
|
||||
multi-processing support provided by the original Linux kernel code.
|
||||
Nonetheless, the drivers that we ported are normally used in the context of
|
||||
SMP-aware Linux kernel configurations only. To not leave the well tested and
|
||||
beaten track, we decided to switch on SMP support in all kernel configurations
|
||||
we use as porting base.
|
||||
|
||||
This especially applies to the Linux drivers within the _repos/pc_
|
||||
sub-directory, and the WireGuard port. Other driver ports already used SMP
|
||||
support in their configuration.
|
||||
|
||||
As a side effect, we removed the insufficient emulation of so called "softirqs"
|
||||
formerly used by the non-SMP driver ports, and replaced them with the original
|
||||
implementation.
|
||||
|
||||
|
||||
Forthcoming platform-driver modernization
|
||||
=========================================
|
||||
|
||||
During the past year, we switched from board-specific platform driver APIs
|
||||
step-by-step to one generic interface. But PC-related drivers still depend on
|
||||
the legacy x86-specific platform driver and API, especially to the PCI-related
|
||||
part of it.
|
||||
|
||||
To finalize the unification and modernization of the platform driver and its
|
||||
API, there were still some pieces missing, which we added with the current
|
||||
release.
|
||||
|
||||
While trying to switch PC-related Linux device driver ports to the new API, we
|
||||
recognized that some drivers depend on additional information of the PCI
|
||||
configuration space that were not exported so far. Namely, the fields for
|
||||
sub-vendor, sub-product, and revision IDs were needed. Moreover, some ported
|
||||
drivers use hard-coded indices of PCI base-address registers (BAR) to refer to
|
||||
I/O resources of the device.
|
||||
|
||||
Therefore, we extended the pci_decode tool to export this additional
|
||||
information, and to annotate I/O port ranges and memory attributes with the
|
||||
corresponding BAR index. The generic platform driver parses this additional
|
||||
information from a given devices ROM, and exports it to the corresponding
|
||||
clients accordingly. The correlation between I/O resources and BAR indices is
|
||||
only unveiled to clients where the platform driver's policy states that
|
||||
physical information has to be provided, like in this example:
|
||||
|
||||
! <config>
|
||||
! <policy label="usb_drv -> " info="yes">
|
||||
! <pci class="USB"/>
|
||||
! </policy>
|
||||
! ...
|
||||
! </config>
|
||||
|
||||
UHCI-specific platform extensions
|
||||
---------------------------------
|
||||
|
||||
Some device-specific registers are only present within the PCI configuration
|
||||
space. For instance UHCI controllers in the PC architecture provide a special
|
||||
legacy support register only accessible via the PCI configuration space. This
|
||||
register is used to hand over the USB hardware from the BIOS to the operating
|
||||
system.
|
||||
|
||||
We did not want to pollute the platform API with a lot of device specific
|
||||
tweaks nor provide unlimited access to the PCI configuration space to a
|
||||
driver. Therefore, we implement the hand-over of the UHCI PCI device in the
|
||||
platform driver if available. Moreover, we handle the Intel-specific resume
|
||||
register whenever a session to the corresponding UHCI controller is opened.
|
||||
|
||||
Intel GPU information from Host Bridge
|
||||
--------------------------------------
|
||||
|
||||
Some information needed by Intel GPU and framebuffer drivers is derived from
|
||||
the Intel Graphics and Controller HUB (GMCH) respectively its control
|
||||
register. It is used to calculate the GPU's Global Translation Table (GTT),
|
||||
and the stolen memory sizes. Again we do not want to give access to the whole
|
||||
configuration space of this sensitive device to either the GPU or the
|
||||
framebuffer driver. Instead, the platform driver now detects Intel PCI graphic
|
||||
cards, and exports the information found within the GMCH control register to
|
||||
the corresponding client as part of the platform session's devices ROM.
|
||||
|
||||
Transition of PC drivers
|
||||
------------------------
|
||||
|
||||
Although there is everything in place now to switch the remaining PC-drivers
|
||||
to the generic platform driver and its API, we decided to do this step after
|
||||
the current release. This way, we have time to stress-test the drivers during
|
||||
our daily use of Genode, the remaining transitional work is planned for the
|
||||
upcoming Sculpt OS release instead.
|
||||
|
||||
|
||||
Libraries and applications
|
||||
##########################
|
||||
|
||||
Qt5 and Morph browser
|
||||
=====================
|
||||
|
||||
As mentioned in Section [Genode coming to the phone], we had to improve
|
||||
Genode's Qt support to get the Morph browser to work. This work includes
|
||||
added support for building Qt projects with CMake, the addition of missing Qt
|
||||
modules like QtGraphicalEffects, and improving the OpenGL support of the QPA
|
||||
plugin. The latter was needed for the Ubuntu UI Toolkit to display its widgets
|
||||
correctly. Note that this change implies that QtQuick applications now use
|
||||
OpenGL by default instead of the QtQuick software rendering fallback back end.
|
||||
This can improve the experience when an accelerated GPU driver is available
|
||||
but can also slow down a QtQuick application if only the Mesa software driver
|
||||
('softpipe') is available on the target platform. In that case, it is possible
|
||||
to enforce the use of the software QtQuick renderer by setting the following
|
||||
environment variable in the configuration of the application:
|
||||
|
||||
! <env key="QT_QUICK_BACKEND" value="software"/>
|
||||
|
||||
When we tried to use the free public Jitsi server at [https://meet.jit.si] with
|
||||
our ported web browsers, we noticed that our QtWebEngine Chromium version was
|
||||
too old and caused issues like a non-working join button and failed WebRTC
|
||||
connections. For this reason, we updated our Qt port to the latest version with
|
||||
QtWebEngine support on FreeBSD, which at this time is version 5.15.2.
|
||||
|
||||
To use this new version, it is necessary to update the Qt5 host tools with the
|
||||
'tool/tool_chain_qt5' script.
|
||||
|
||||
We also updated the Falkon web browser to the latest version 3.2.0.
|
||||
|
||||
Up-to-date Sculpt packages of both the Falkon and Morph browsers for x86_64 are
|
||||
available in the 'cproc' depot.
|
||||
|
||||
|
||||
USB smart cards via PKCS#11
|
||||
===========================
|
||||
|
||||
With this release, Genode gains support for accessing USB smart-card devices
|
||||
via PKCS#11. This is achieved through a port of the OpenSC PKCS#11 tool that is
|
||||
now available as package for the Sculpt OS. A quick look into the features and
|
||||
integration of the tool is possible using the new _pkcs11_tool_ run script
|
||||
hosted in the [https://github.com/genodelabs/genode-world - genode-world]
|
||||
repository. For a more detailed guide to the tool, you may read the
|
||||
corresponding Genodians article.
|
||||
|
||||
:USB smart cards via PKCS#11:
|
||||
|
||||
[https://genodians.org/m-stein/2022-08-18-pkcs11-tool-1]
|
||||
|
||||
|
||||
Sculpt OS improvements
|
||||
======================
|
||||
|
||||
In addition to the major developments described in
|
||||
Section [Genode coming to the phone], Sculpt OS has received several minor
|
||||
refinements.
|
||||
|
||||
When integrating a
|
||||
[https://genode.org/documentation/release-notes/22.02#Framework_for_special-purpose_Sculpt-based_operating_systems - Sculpt-based appliance]
|
||||
with a predefined deploy configuration, the _sculpt.run_ script automatically
|
||||
adds the required software packages as tar archive to the boot image. However,
|
||||
for complex scenarios, it is sometimes desirable to keep the boot image small
|
||||
and fetch the packages at runtime over the network. To support such use cases,
|
||||
we added the new run-script argument 'DEPOT' with the possible values 'tar'
|
||||
(default) and 'omit'. If the latter is specified, the deployed software
|
||||
packages are excluded from the boot image and the run script merely prints the
|
||||
versions of the required packages. This information can conveniently be used
|
||||
as input for publishing the packages.
|
||||
|
||||
We added two new packages 'part_block' and 'ext2_fs' that simplify the access
|
||||
of multiple block devices and partitions in manually curated deploy
|
||||
configurations. The part_block package can be used in Sculpt's
|
||||
_/config/deploy_ as follows.
|
||||
|
||||
! <start name="nvme-0.part_block" pkg="part_block">
|
||||
! <route>
|
||||
! <service name="Block">
|
||||
! <parent label="nvme-0"/>
|
||||
! </service>
|
||||
! <service name="Report" label="partitions">
|
||||
! <parent/>
|
||||
! </service>
|
||||
! </route>
|
||||
! </start>
|
||||
|
||||
It can be combined with the 'ext2_fs' package to access the files stored on a
|
||||
particular partition.
|
||||
|
||||
! <start name="nvme-0.4.fs" pkg="ext2_fs">
|
||||
! <route>
|
||||
! <service name="Block">
|
||||
! <child name="nvme-0.part_block" label="4"/>
|
||||
! </service>
|
||||
! <service name="RM">
|
||||
! <parent/>
|
||||
! </service>
|
||||
! </route>
|
||||
! </start>
|
||||
|
||||
|
||||
Platforms
|
||||
#########
|
||||
|
||||
Qemu virtual platform
|
||||
=====================
|
||||
|
||||
Because more and more architectures on Genode now support VirtIO drivers on
|
||||
Qemu (ARMv7, ARMv8, and RISC-V), the generic board name "virt_qemu" did not
|
||||
suffice for keeping a clean distinction between the separate architecture
|
||||
requirements. Therefore, we decided to make the board name architecture
|
||||
specific. The following board names are now supported on base-hw:
|
||||
"virt_qemu_arm_v7a", "virt_qemu_arm_v8a", and "virt_qemu_riscv".
|
||||
The "virt_qemu" board name was removed.
|
||||
|
||||
|
||||
RISC-V
|
||||
======
|
||||
|
||||
As suggested above Genode's RISC-V support got extended by VirtIO drivers.
|
||||
This includes a block driver, a networking driver, keyboard and mouse handling
|
||||
as well as basic framebuffer support. This way, it has become possible to test
|
||||
interactive and networking scenarios on Genode's RISC-V version using Qemu.
|
||||
|
||||
This work was contributed by Piotr Tworek. Thanks a lot!
|
||||
|
||||
|
||||
Allwinner A64
|
||||
=============
|
||||
|
||||
In the
|
||||
[https://genode.org/documentation/release-notes/22.05#Custom_system-control_processor__SCP__firmware - previous release],
|
||||
we introduced our custom firmware for the PinePhone's system-control processor
|
||||
(SCP). We have now generalized the firmware to cover also the Pine-A64-LTS
|
||||
board. By establishing our custom SCP firmware as a base line for all A64-based
|
||||
boards, we can make our A64 platform driver depend on the SCP for accessing the
|
||||
PMIC (power management chip) instead of driving the RSB and PMIC by itself.
|
||||
|
||||
|
||||
Build system and tools
|
||||
######################
|
||||
|
||||
In this release, we improve support for booting Genode/Sculpt on UEFI
|
||||
platforms in several aspects. First, the Bender tool gains a more robust
|
||||
UEFI-boot detection mechanism while retrieving serial-device parameters. Also,
|
||||
the GRUB boot loader was updated to version 2.06 and now keeps lower RAM
|
||||
untouched from internal memory allocations, which prevents nasty surprises on
|
||||
booting some UEFI devices. And last, our [https://ipxe.org/ - iPXE-based] boot
|
||||
option received support for UEFI images when using the following run-tool
|
||||
configuration.
|
||||
|
||||
! RUN_OPT += --include image/uefi
|
||||
! RUN_OPT += --include load/ipxe
|
||||
|
||||
@@ -21,6 +21,5 @@ content:
|
||||
for spec in x86_32; do \
|
||||
mv lib/mk/spec/$$spec/ld-fiasco.mk lib/mk/spec/$$spec/ld.mk; \
|
||||
done;
|
||||
sed -i "s/ld-fiasco/ld/" src/lib/ld/fiasco/target.mk
|
||||
sed -i "s/fiasco_timer_drv/timer/" src/timer/fiasco/target.mk
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
2022-05-24 1790ce242c001ed77aab9695f69923a44d1dc1d1
|
||||
2022-10-11 1f0607de6493bad0e47b24e66d84474652e8b6be
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
TARGET = ld-fiasco
|
||||
LIBS = ld-fiasco
|
||||
@@ -1 +1 @@
|
||||
2022-05-24 f7900083623a2009d35234c47d2475dea8f6cf53
|
||||
2022-10-11 d258920f8664460c78eeea25fafb89eaa5e7adf5
|
||||
|
||||
@@ -1 +1 @@
|
||||
2022-05-24 f7d228f6419c2fc9b1b0faf4ba8d88862ba61e81
|
||||
2022-10-11 1c94d29566bccccced246eeaf90702348e2b1a7f
|
||||
|
||||
@@ -1 +1 @@
|
||||
2022-05-24 391b798b7c1d1b44ff65d855980eb41a8f4a87c1
|
||||
2022-10-11 2668fd23d5cbd45b8f632073fc7c155f96ecb848
|
||||
|
||||
@@ -1 +1 @@
|
||||
2022-05-24 79eab679e71dd70803b0e1647a23e2ba86c76f50
|
||||
2022-10-11 8da054ff9e4c37895816fd30857b3c42d9e75eb0
|
||||
|
||||
@@ -1 +1 @@
|
||||
2022-05-24 7a16aeb081d1392c36d83f526936f17cc9560442
|
||||
2022-10-11 f41df6b57d2c4b090a84427e02950df84fb385ad
|
||||
|
||||
@@ -39,5 +39,4 @@ content:
|
||||
for spec in x86_32 x86_64 arm arm_64; do \
|
||||
mv lib/mk/spec/$$spec/ld-foc.mk lib/mk/spec/$$spec/ld.mk; \
|
||||
done;
|
||||
sed -i "s/ld-foc/ld/" src/lib/ld/foc/target.mk
|
||||
sed -i "s/foc_timer_drv/timer/" src/timer/foc/target.mk
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
TARGET = ld-foc
|
||||
LIBS = ld-foc
|
||||
@@ -1,2 +1 @@
|
||||
arm_v7a
|
||||
arm_v8a
|
||||
1
repos/base-hw/board/virt_qemu_arm_v8a/arch
Normal file
1
repos/base-hw/board/virt_qemu_arm_v8a/arch
Normal file
@@ -0,0 +1 @@
|
||||
arm_v8a
|
||||
1
repos/base-hw/board/virt_qemu_arm_v8a/image_link_address
Normal file
1
repos/base-hw/board/virt_qemu_arm_v8a/image_link_address
Normal file
@@ -0,0 +1 @@
|
||||
0x40000000
|
||||
@@ -1,6 +1,6 @@
|
||||
REP_INC_DIR += src/bootstrap/board/virt_qemu
|
||||
REP_INC_DIR += src/bootstrap/board/virt_qemu_arm_v7a
|
||||
|
||||
SRC_CC += bootstrap/board/virt_qemu/platform.cc
|
||||
SRC_CC += bootstrap/board/virt_qemu_arm_v7a/platform.cc
|
||||
SRC_CC += bootstrap/spec/arm/arm_v7_cpu.cc
|
||||
SRC_CC += bootstrap/spec/arm/cortex_a15_cpu.cc
|
||||
SRC_CC += bootstrap/spec/arm/gicv2.cc
|
||||
@@ -1,4 +1,4 @@
|
||||
REP_INC_DIR += src/core/board/virt_qemu
|
||||
REP_INC_DIR += src/core/board/virt_qemu_arm_v7a
|
||||
REP_INC_DIR += src/core/spec/arm/virtualization
|
||||
|
||||
# add C++ sources
|
||||
@@ -1,8 +1,8 @@
|
||||
REP_INC_DIR += src/bootstrap/board/virt_qemu_64
|
||||
REP_INC_DIR += src/bootstrap/board/virt_qemu_arm_v8a
|
||||
|
||||
SRC_CC += bootstrap/spec/arm/gicv3.cc
|
||||
SRC_CC += bootstrap/spec/arm_64/cortex_a53_mmu.cc
|
||||
SRC_CC += bootstrap/board/virt_qemu_64/platform.cc
|
||||
SRC_CC += bootstrap/board/virt_qemu_arm_v8a/platform.cc
|
||||
SRC_CC += lib/base/arm_64/kernel/interface.cc
|
||||
SRC_CC += spec/64bit/memory_map.cc
|
||||
SRC_S += bootstrap/spec/arm_64/crt0.s
|
||||
@@ -1,4 +1,4 @@
|
||||
REP_INC_DIR += src/core/board/virt_qemu_64
|
||||
REP_INC_DIR += src/core/board/virt_qemu_arm_v8a
|
||||
REP_INC_DIR += src/core/spec/arm/virtualization
|
||||
|
||||
# add C++ sources
|
||||
@@ -4,6 +4,6 @@ SRC_CC += timer_connection_time.cc
|
||||
SRC_CC += hw/timer_connection_timestamp.cc
|
||||
SRC_CC += duration.cc
|
||||
|
||||
INC_DIR += $(BASE_DIR)/src/include
|
||||
REP_INC_DIR += src/include
|
||||
|
||||
vpath % $(BASE_DIR)/src/lib/timeout
|
||||
vpath % $(call select_from_repositories,src/lib/timeout)
|
||||
|
||||
@@ -1 +1 @@
|
||||
2022-05-24 ab1cb582165e76bda4abf27870f44ad7d1ae5b6d
|
||||
2022-10-11 50db06fe21eca6c46c9b4bf7fcbc81538ac74f32
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
CONTENT += src/core/board/imx53_qsb \
|
||||
src/bootstrap/board/imx53_qsb
|
||||
src/bootstrap/board/imx53_qsb \
|
||||
lib/mk/spec/arm_v7/core-hw-imx53_qsb.inc \
|
||||
lib/mk/spec/arm_v7/bootstrap-hw-imx53_qsb.inc
|
||||
|
||||
include $(GENODE_DIR)/repos/base-hw/recipes/src/base-hw_content.inc
|
||||
|
||||
@@ -1 +1 @@
|
||||
2022-05-24 0fff6ce83b962b3fd54cf6eda0a157cb0cb0c9d5
|
||||
2022-10-11 1377d3a2b7afaa265cc5ae6bbd515679be527c40
|
||||
|
||||
@@ -1 +1 @@
|
||||
2022-05-24 8c17512664a648eaed876c815ea678770eda3280
|
||||
2022-10-11 c32cf899ce00bd69aff5bbd4f7b6b611d2bfa47d
|
||||
|
||||
@@ -1 +1 @@
|
||||
2022-05-24 edc396d9bc9a2ebf73590e70c1363020226909be
|
||||
2022-10-11 39ff297bc573b8e8bf4f2e6e233bf0b1b21f13af
|
||||
|
||||
@@ -1 +1 @@
|
||||
2022-05-24 da90478c4c0b8993041bc59488eedb124e680e78
|
||||
2022-10-11 f5456c3ed55b53ccaefee603fdb8d9b1e3ca84ab
|
||||
|
||||
@@ -1 +1 @@
|
||||
2022-05-24 1b34e317209c48bfc88af6118db32be261ce3e0c
|
||||
2022-10-11 de2f50d9164952dbbf6ce76d29abad5d96da8512
|
||||
|
||||
@@ -1 +1 @@
|
||||
2022-05-24 46e9f88209bbc95228d3882cc0831770315402e4
|
||||
2022-10-11 5d72eb4e34f582c06c086345b225cee91ce539cc
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
CONTENT += src/core/board/virt_qemu_64 \
|
||||
src/bootstrap/board/virt_qemu_64
|
||||
|
||||
include $(GENODE_DIR)/repos/base-hw/recipes/src/base-hw_content.inc
|
||||
@@ -1 +0,0 @@
|
||||
2022-05-24 bb6c39c093a24d2ec4ff1d00e397529c51e95fa7
|
||||
@@ -0,0 +1,4 @@
|
||||
CONTENT += src/core/board/virt_qemu_arm_v7a \
|
||||
src/bootstrap/board/virt_qemu_arm_v7a
|
||||
|
||||
include $(GENODE_DIR)/repos/base-hw/recipes/src/base-hw_content.inc
|
||||
1
repos/base-hw/recipes/src/base-hw-virt_qemu_arm_v7a/hash
Normal file
1
repos/base-hw/recipes/src/base-hw-virt_qemu_arm_v7a/hash
Normal file
@@ -0,0 +1 @@
|
||||
2022-10-11 70e53c98ef4b3215440efb2ea09e07ff7cd97c4f
|
||||
@@ -0,0 +1,4 @@
|
||||
CONTENT += src/core/board/virt_qemu_arm_v8a \
|
||||
src/bootstrap/board/virt_qemu_arm_v8a
|
||||
|
||||
include $(GENODE_DIR)/repos/base-hw/recipes/src/base-hw_content.inc
|
||||
1
repos/base-hw/recipes/src/base-hw-virt_qemu_arm_v8a/hash
Normal file
1
repos/base-hw/recipes/src/base-hw-virt_qemu_arm_v8a/hash
Normal file
@@ -0,0 +1 @@
|
||||
2022-10-11 c146d70c9bde3f928110c868a54b8c800beffd79
|
||||
@@ -0,0 +1,2 @@
|
||||
base-hw
|
||||
base
|
||||
@@ -115,6 +115,12 @@ SRC_LIB_BASE += $(notdir $(wildcard $(BASE_HW_DIR)/src/lib/base/*.cc)) \
|
||||
$(notdir $(wildcard $(BASE_DIR)/src/lib/base/*.cc)) \
|
||||
${call selected_content,SRC_LIB_BASE_SPECS}
|
||||
|
||||
SRC_LIB_TIMEOUT += duration.cc \
|
||||
hw/timer_connection_timestamp.cc \
|
||||
timeout.cc \
|
||||
timer_connection.cc \
|
||||
timer_connection_time.cc
|
||||
|
||||
SRC_LIB_STARTUP += init_main_thread.cc _main.cc \
|
||||
$(addprefix spec/,${call selected_content,SRC_LIB_STARTUP_SPECS})
|
||||
|
||||
@@ -125,15 +131,24 @@ SRC_CORE += $(notdir $(wildcard $(BASE_HW_DIR)/src/core/*.cc)) \
|
||||
$(addprefix board/,$(BOARD)) \
|
||||
version.inc target.inc include hw kernel
|
||||
|
||||
LIB_MK := base-hw-common.mk base-hw.mk bootstrap-hw.inc core-hw.inc \
|
||||
timeout-hw.mk cxx.mk base.inc base-common.inc startup.inc \
|
||||
$(addprefix spec/,${call selected_content,LIB_MK_SPECS})
|
||||
# names of the lib/mk/ files to consider for inclusion in the src archive
|
||||
LIB_MK_FILES := base-common.inc base-hw-common.mk \
|
||||
base.inc base-hw.mk \
|
||||
bootstrap-hw.inc bootstrap-hw-$(BOARD).inc bootstrap-hw-$(BOARD).mk \
|
||||
core-hw.inc core-hw-$(BOARD).inc core-hw-$(BOARD).mk \
|
||||
startup.inc startup-hw.mk \
|
||||
timeout-hw.mk cxx.mk ld-hw.mk syscall-hw.mk
|
||||
|
||||
LIB_MK_DIRS := lib/mk $(addprefix lib/mk/spec/,${call selected_content,LIB_MK_SPECS})
|
||||
|
||||
CONTENT += $(foreach D,$(LIB_MK_DIRS),$(addprefix $D/,$(LIB_MK_FILES)))
|
||||
|
||||
CONTENT += $(addprefix src/timer/,$(SRC_TIMER)) \
|
||||
$(addprefix src/include/hw/,$(SRC_INCLUDE_HW)) \
|
||||
$(addprefix src/bootstrap/,$(SRC_BOOTSTRAP)) \
|
||||
$(addprefix lib/mk/,$(LIB_MK)) \
|
||||
$(addprefix src/lib/base/,$(SRC_LIB_BASE)) \
|
||||
$(addprefix src/lib/timeout/,$(SRC_LIB_TIMEOUT)) \
|
||||
$(addprefix src/lib/startup/,$(SRC_LIB_STARTUP)) \
|
||||
$(addprefix src/core/,$(SRC_CORE)) \
|
||||
src/lib/hw src/lib/ld src/lib/cxx \
|
||||
@@ -180,7 +195,6 @@ generalize_target_names: $(CONTENT)
|
||||
# apply kernel-agnostic convention of naming the timer and ld.lib.so
|
||||
for subdir in ${call selected_content,LD_MK_DIRS}; do \
|
||||
mv $$subdir/ld-hw.mk $$subdir/ld.mk; done
|
||||
sed -i "s/ld-hw/ld/" src/lib/ld/hw/target.mk
|
||||
sed -i "s/hw_timer_drv/timer/" src/timer/hw/target.mk
|
||||
# supplement BOARD definition that normally comes form the build dir
|
||||
sed -i "s/\?= unknown/:= $(BOARD)/" src/core/hw/target.mk
|
||||
@@ -189,5 +203,4 @@ generalize_target_names: $(CONTENT)
|
||||
sed -i "1aREQUIRES := $(ARCH)" src/core/hw/target.mk
|
||||
sed -i "1aREQUIRES := $(ARCH)" src/bootstrap/hw/target.mk
|
||||
sed -i "/REQUIRES/s/hw/hw $(ARCH)/" src/timer/hw/target.mk
|
||||
sed -i "1aREQUIRES := $(ARCH)" src/lib/ld/hw/target.mk
|
||||
|
||||
|
||||
@@ -109,7 +109,8 @@ Cpu::Idle_thread::Idle_thread(Board::Address_space_id_allocator &addr_space_id_a
|
||||
Cpu &cpu,
|
||||
Pd &core_pd)
|
||||
:
|
||||
Thread { addr_space_id_alloc, user_irq_pool, cpu_pool, core_pd, "idle" }
|
||||
Thread { addr_space_id_alloc, user_irq_pool, cpu_pool, core_pd,
|
||||
Cpu_priority::min(), 0, "idle", Thread::IDLE }
|
||||
{
|
||||
regs->ip = (addr_t)&idle_thread_main;
|
||||
|
||||
@@ -120,14 +121,9 @@ Cpu::Idle_thread::Idle_thread(Board::Address_space_id_allocator &addr_space_id_a
|
||||
|
||||
void Cpu::schedule(Job * const job)
|
||||
{
|
||||
if (_id == executing_id())
|
||||
_scheduler.ready(job->share());
|
||||
else {
|
||||
_scheduler.ready_check(job->share());
|
||||
|
||||
if (_scheduler.need_to_schedule())
|
||||
trigger_ip_interrupt();
|
||||
}
|
||||
_scheduler.ready(job->share());
|
||||
if (_id != executing_id() && _scheduler.need_to_schedule())
|
||||
trigger_ip_interrupt();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -191,45 +191,17 @@ void Cpu_scheduler::update(time_t time)
|
||||
}
|
||||
|
||||
|
||||
void Cpu_scheduler::ready_check(Share &s1)
|
||||
{
|
||||
assert(_head);
|
||||
|
||||
ready(s1);
|
||||
|
||||
if (_need_to_schedule)
|
||||
return;
|
||||
|
||||
Share * s2 = _head;
|
||||
if (!s1._claim) {
|
||||
_need_to_schedule = s2 == &_idle;
|
||||
} else if (!_head_claims) {
|
||||
_need_to_schedule = true;
|
||||
} else if (s1._prio != s2->_prio) {
|
||||
_need_to_schedule = s1._prio > s2->_prio;
|
||||
} else {
|
||||
for (
|
||||
; s2 && s2 != &s1;
|
||||
s2 =
|
||||
Double_list<Cpu_share>::next(&s2->_claim_item) != nullptr ?
|
||||
&Double_list<Cpu_share>::next(&s2->_claim_item)->payload() :
|
||||
nullptr) ;
|
||||
|
||||
_need_to_schedule = !s2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Cpu_scheduler::ready(Share &s)
|
||||
{
|
||||
assert(!s._ready && &s != &_idle);
|
||||
|
||||
_need_to_schedule = true;
|
||||
|
||||
s._ready = 1;
|
||||
s._fill = _fill;
|
||||
_fills.insert_tail(&s._fill_item);
|
||||
|
||||
if (_head == &_idle)
|
||||
_need_to_schedule = true;
|
||||
|
||||
if (!s._quota)
|
||||
return;
|
||||
|
||||
@@ -239,6 +211,28 @@ void Cpu_scheduler::ready(Share &s)
|
||||
_rcl[s._prio].insert_head(&s._claim_item);
|
||||
else
|
||||
_rcl[s._prio].insert_tail(&s._claim_item);
|
||||
|
||||
/*
|
||||
* Check whether we need to re-schedule
|
||||
*/
|
||||
if (_need_to_schedule)
|
||||
return;
|
||||
|
||||
/* current head has no quota left */
|
||||
if (!_head_claims) {
|
||||
_need_to_schedule = true;
|
||||
return;
|
||||
}
|
||||
|
||||
/* if current head has different priority */
|
||||
if (s._prio != _head->_prio) {
|
||||
_need_to_schedule = s._prio > _head->_prio;
|
||||
return;
|
||||
}
|
||||
|
||||
/* if current head has same priority, the ready share gets active */
|
||||
if (s._claim)
|
||||
_need_to_schedule = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -246,7 +240,8 @@ void Cpu_scheduler::unready(Share &s)
|
||||
{
|
||||
assert(s._ready && &s != &_idle);
|
||||
|
||||
_need_to_schedule = true;
|
||||
if (&s == _head)
|
||||
_need_to_schedule = true;
|
||||
|
||||
s._ready = 0;
|
||||
_fills.remove(&s._fill_item);
|
||||
@@ -270,21 +265,15 @@ void Cpu_scheduler::remove(Share &s)
|
||||
{
|
||||
assert(&s != &_idle);
|
||||
|
||||
_need_to_schedule = true;
|
||||
if (s._ready) unready(s);
|
||||
|
||||
if (&s == _head)
|
||||
_head = nullptr;
|
||||
|
||||
if (s._ready)
|
||||
_fills.remove(&s._fill_item);
|
||||
|
||||
if (!s._quota)
|
||||
return;
|
||||
|
||||
if (s._ready)
|
||||
_rcl[s._prio].remove(&s._claim_item);
|
||||
else
|
||||
_ucl[s._prio].remove(&s._claim_item);
|
||||
_ucl[s._prio].remove(&s._claim_item);
|
||||
}
|
||||
|
||||
|
||||
@@ -292,8 +281,6 @@ void Cpu_scheduler::insert(Share &s)
|
||||
{
|
||||
assert(!s._ready);
|
||||
|
||||
_need_to_schedule = true;
|
||||
|
||||
if (!s._quota)
|
||||
return;
|
||||
|
||||
|
||||
@@ -180,15 +180,10 @@ class Kernel::Cpu_scheduler
|
||||
void timeout() { _need_to_schedule = true; }
|
||||
|
||||
/**
|
||||
* Update head according to the consumed time
|
||||
* Update head according to the current (absolute) time
|
||||
*/
|
||||
void update(time_t time);
|
||||
|
||||
/**
|
||||
* Set 's1' ready and return wether this outdates current head
|
||||
*/
|
||||
void ready_check(Share &s1);
|
||||
|
||||
/**
|
||||
* Set share 's' ready
|
||||
*/
|
||||
|
||||
@@ -329,6 +329,23 @@ void Thread::_call_start_thread()
|
||||
/* join protection domain */
|
||||
thread._pd = (Pd *) user_arg_3();
|
||||
thread._ipc_init(*(Native_utcb *)user_arg_4(), *this);
|
||||
|
||||
/*
|
||||
* Sanity check core threads!
|
||||
*
|
||||
* Currently, the model assumes that there is only one core
|
||||
* entrypoint, which serves requests, and which can destroy
|
||||
* threads and pds. If this changes, we have to inform all
|
||||
* cpus about pd destructions to remove their page-tables
|
||||
* from the hardware in case that a core-thread running with
|
||||
* that same pd is currently active. Therefore, warn if the
|
||||
* semantic changes, and additional core threads are started
|
||||
* across cpu cores.
|
||||
*/
|
||||
if (thread._pd == &_core_pd && cpu.id() != _cpu_pool.primary_cpu().id())
|
||||
Genode::raw("Error: do not start core threads"
|
||||
" on CPU cores different than boot cpu");
|
||||
|
||||
thread._become_active();
|
||||
}
|
||||
|
||||
@@ -369,7 +386,7 @@ void Thread::_call_restart_thread()
|
||||
|
||||
Thread &thread = *thread_ptr;
|
||||
|
||||
if (!_core && (&pd() != &thread.pd())) {
|
||||
if (_type == USER && (&pd() != &thread.pd())) {
|
||||
raw(*this, ": failed to lookup thread ", (unsigned)user_arg_1(),
|
||||
" to restart it");
|
||||
_die();
|
||||
@@ -447,6 +464,18 @@ void Thread::_call_delete_thread()
|
||||
}
|
||||
|
||||
|
||||
void Thread::_call_delete_pd()
|
||||
{
|
||||
Genode::Kernel_object<Pd> & pd =
|
||||
*(Genode::Kernel_object<Pd>*)user_arg_1();
|
||||
|
||||
if (_cpu->active(pd->mmu_regs))
|
||||
_cpu->switch_to(_core_pd.mmu_regs);
|
||||
|
||||
_call_delete<Pd>();
|
||||
}
|
||||
|
||||
|
||||
void Thread::_call_await_request_msg()
|
||||
{
|
||||
if (_ipc_node.can_await_request()) {
|
||||
@@ -793,7 +822,7 @@ void Thread::_call()
|
||||
case call_id_pause_vm(): _call_pause_vm(); return;
|
||||
default:
|
||||
/* check wether this is a core thread */
|
||||
if (!_core) {
|
||||
if (_type != CORE) {
|
||||
Genode::raw(*this, ": not entitled to do kernel call");
|
||||
_die();
|
||||
return;
|
||||
@@ -805,7 +834,7 @@ void Thread::_call()
|
||||
_call_new<Thread>(_addr_space_id_alloc, _user_irq_pool, _cpu_pool,
|
||||
_core_pd, (unsigned) user_arg_2(),
|
||||
(unsigned) _core_to_kernel_quota(user_arg_3()),
|
||||
(char const *) user_arg_4());
|
||||
(char const *) user_arg_4(), USER);
|
||||
return;
|
||||
case call_id_new_core_thread():
|
||||
_call_new<Thread>(_addr_space_id_alloc, _user_irq_pool, _cpu_pool,
|
||||
@@ -822,7 +851,7 @@ void Thread::_call()
|
||||
*(Genode::Platform_pd *) user_arg_3(),
|
||||
_addr_space_id_alloc);
|
||||
return;
|
||||
case call_id_delete_pd(): _call_delete<Pd>(); return;
|
||||
case call_id_delete_pd(): _call_delete_pd(); return;
|
||||
case call_id_new_signal_receiver(): _call_new<Signal_receiver>(); return;
|
||||
case call_id_new_signal_context():
|
||||
_call_new<Signal_context>(*(Signal_receiver*) user_arg_2(), user_arg_3());
|
||||
@@ -857,7 +886,7 @@ void Thread::_mmu_exception()
|
||||
return;
|
||||
}
|
||||
|
||||
if (_core)
|
||||
if (_type != USER)
|
||||
Genode::raw(*this, " raised a fault, which should never happen ",
|
||||
_fault);
|
||||
|
||||
@@ -874,7 +903,7 @@ Thread::Thread(Board::Address_space_id_allocator &addr_space_id_alloc,
|
||||
unsigned const priority,
|
||||
unsigned const quota,
|
||||
char const *const label,
|
||||
bool core)
|
||||
Type type)
|
||||
:
|
||||
Kernel::Object { *this },
|
||||
Cpu_job { priority, quota },
|
||||
@@ -885,8 +914,8 @@ Thread::Thread(Board::Address_space_id_allocator &addr_space_id_alloc,
|
||||
_ipc_node { *this },
|
||||
_state { AWAITS_START },
|
||||
_label { label },
|
||||
_core { core },
|
||||
regs { core }
|
||||
_type { type },
|
||||
regs { type != USER }
|
||||
{ }
|
||||
|
||||
|
||||
|
||||
@@ -57,6 +57,10 @@ struct Kernel::Thread_fault
|
||||
*/
|
||||
class Kernel::Thread : private Kernel::Object, public Cpu_job, private Timeout
|
||||
{
|
||||
public:
|
||||
|
||||
enum Type { USER, CORE, IDLE };
|
||||
|
||||
private:
|
||||
|
||||
/*
|
||||
@@ -149,7 +153,7 @@ class Kernel::Thread : private Kernel::Object, public Cpu_job, private Timeout
|
||||
capid_t _timeout_sigid { 0 };
|
||||
bool _paused { false };
|
||||
bool _cancel_next_await_signal { false };
|
||||
bool const _core { false };
|
||||
Type const _type;
|
||||
|
||||
Genode::Constructible<Tlb_invalidation> _tlb_invalidation {};
|
||||
Genode::Constructible<Destroy> _destroy {};
|
||||
@@ -230,6 +234,7 @@ class Kernel::Thread : private Kernel::Object, public Cpu_job, private Timeout
|
||||
void _call_restart_thread();
|
||||
void _call_yield_thread();
|
||||
void _call_delete_thread();
|
||||
void _call_delete_pd();
|
||||
void _call_await_request_msg();
|
||||
void _call_send_request_msg();
|
||||
void _call_send_reply_msg();
|
||||
@@ -301,7 +306,7 @@ class Kernel::Thread : private Kernel::Object, public Cpu_job, private Timeout
|
||||
unsigned const priority,
|
||||
unsigned const quota,
|
||||
char const *const label,
|
||||
bool core = false);
|
||||
Type const type);
|
||||
|
||||
/**
|
||||
* Constructor for core/kernel thread
|
||||
@@ -315,7 +320,7 @@ class Kernel::Thread : private Kernel::Object, public Cpu_job, private Timeout
|
||||
char const *const label)
|
||||
:
|
||||
Thread(addr_space_id_alloc, user_irq_pool, cpu_pool, core_pd,
|
||||
Cpu_priority::min(), 0, label, true)
|
||||
Cpu_priority::min(), 0, label, CORE)
|
||||
{ }
|
||||
|
||||
~Thread();
|
||||
@@ -432,6 +437,7 @@ class Kernel::Thread : private Kernel::Object, public Cpu_job, private Timeout
|
||||
char const * label() const { return _label; }
|
||||
Thread_fault fault() const { return _fault; }
|
||||
Genode::Native_utcb *utcb() { return _utcb; }
|
||||
Type type() const { return _type; }
|
||||
|
||||
Pd &pd() const
|
||||
{
|
||||
|
||||
@@ -77,6 +77,8 @@ class Kernel::Vm : private Kernel::Object, public Cpu_job
|
||||
Kernel::Signal_context & context,
|
||||
Identity & id);
|
||||
|
||||
~Vm();
|
||||
|
||||
/**
|
||||
* Inject an interrupt to this VM
|
||||
*
|
||||
|
||||
@@ -22,13 +22,17 @@
|
||||
|
||||
using namespace Genode;
|
||||
using namespace Kernel;
|
||||
using Device = Board::Timer;
|
||||
|
||||
using Device = Board::Timer;
|
||||
using counter_t = Board::Timer::Counter::access_t;
|
||||
|
||||
|
||||
enum {
|
||||
TICS_PER_MS =
|
||||
Board::CORTEX_A9_PRIVATE_TIMER_CLK /
|
||||
Board::CORTEX_A9_PRIVATE_TIMER_DIV / 1000
|
||||
Board::CORTEX_A9_PRIVATE_TIMER_DIV / 1000,
|
||||
|
||||
MAX_COUNTER_VAL = ~(counter_t)0
|
||||
};
|
||||
|
||||
|
||||
@@ -79,12 +83,33 @@ time_t Timer::us_to_ticks(time_t const us) const {
|
||||
|
||||
time_t Timer::_duration() const
|
||||
{
|
||||
Device::Counter::access_t last = _last_timeout_duration;
|
||||
Device::Counter::access_t cnt = _device.read<Device::Counter>();
|
||||
Device::Counter::access_t ret = (_device.read<Device::Interrupt_status::Event>())
|
||||
? _max_value() - cnt + last : last - cnt;
|
||||
return ret;
|
||||
counter_t const start_counter_val { (counter_t)_last_timeout_duration };
|
||||
counter_t const curr_counter_val { _device.read<Device::Counter>() };
|
||||
|
||||
/*
|
||||
* Calculate result depending on whether the counter already wrapped or
|
||||
* not. See the comment in the implementation of '_max_value' for an
|
||||
* explanation why this comparison is done instead of checking the IRQ
|
||||
* status and why it is sufficient.
|
||||
*/
|
||||
if (curr_counter_val > start_counter_val)
|
||||
return start_counter_val + (MAX_COUNTER_VAL - curr_counter_val);
|
||||
|
||||
return start_counter_val - curr_counter_val;
|
||||
}
|
||||
|
||||
|
||||
time_t Timer::_max_value() const { return 0xfffffffe; }
|
||||
time_t Timer::_max_value() const
|
||||
{
|
||||
/*
|
||||
* We propagate a max timeout value far lower than the one required
|
||||
* by the hardware. This is because on some platforms (Qemu 4.2.1 PBXA9),
|
||||
* the IRQ status register is not reliable. Sometimes, it indicates an IRQ
|
||||
* too early, i.e., shortly before the counter wraps. Therefore we have to
|
||||
* accomplish wrap detection via counter comparison only. Therefore, we
|
||||
* have to make sure that we always read out the counter before it hits
|
||||
* the max timout value again. And, therefore, the max timeout value has
|
||||
* to be far away from the first value the counter has after wrapping.
|
||||
*/
|
||||
return MAX_COUNTER_VAL >> 1;
|
||||
}
|
||||
|
||||
@@ -87,24 +87,25 @@ void Arm_cpu::mmu_fault_status(Fsr::access_t fsr, Thread_fault & fault)
|
||||
}
|
||||
|
||||
|
||||
void Arm_cpu::switch_to(Arm_cpu::Context&, Arm_cpu::Mmu_context & o)
|
||||
bool Arm_cpu::active(Arm_cpu::Mmu_context & ctx)
|
||||
{
|
||||
if (o.cidr == 0) return;
|
||||
return (Cidr::read() == ctx.cidr);
|
||||
}
|
||||
|
||||
Cidr::access_t cidr = Cidr::read();
|
||||
if (cidr != o.cidr) {
|
||||
/**
|
||||
* First switch to global mappings only to prevent
|
||||
* that wrong branch predicts result due to ASID
|
||||
* and Page-Table not being in sync (see ARM RM B 3.10.4)
|
||||
*/
|
||||
Cidr::write(0);
|
||||
Cpu::synchronization_barrier();
|
||||
Ttbr0::write(o.ttbr0);
|
||||
Cpu::synchronization_barrier();
|
||||
Cidr::write(o.cidr);
|
||||
Cpu::synchronization_barrier();
|
||||
}
|
||||
|
||||
void Arm_cpu::switch_to(Arm_cpu::Mmu_context & ctx)
|
||||
{
|
||||
/**
|
||||
* First switch to global mappings only to prevent
|
||||
* that wrong branch predicts result due to ASID
|
||||
* and Page-Table not being in sync (see ARM RM B 3.10.4)
|
||||
*/
|
||||
Cidr::write(0);
|
||||
Cpu::synchronization_barrier();
|
||||
Ttbr0::write(ctx.ttbr0);
|
||||
Cpu::synchronization_barrier();
|
||||
Cidr::write(ctx.cidr);
|
||||
Cpu::synchronization_barrier();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -104,7 +104,8 @@ struct Genode::Arm_cpu : public Hw::Arm_cpu
|
||||
else Tlbiall::write(0);
|
||||
}
|
||||
|
||||
void switch_to(Context&, Mmu_context & o);
|
||||
bool active(Mmu_context &);
|
||||
void switch_to(Mmu_context &);
|
||||
|
||||
static void mmu_fault(Context & c, Kernel::Thread_fault & fault);
|
||||
static void mmu_fault_status(Fsr::access_t fsr,
|
||||
|
||||
@@ -67,7 +67,8 @@ void Kernel::Thread::Tlb_invalidation::execute() { };
|
||||
|
||||
void Thread::proceed(Cpu & cpu)
|
||||
{
|
||||
cpu.switch_to(*regs, pd().mmu_regs);
|
||||
if (!cpu.active(pd().mmu_regs) && type() != CORE)
|
||||
cpu.switch_to(pd().mmu_regs);
|
||||
|
||||
regs->cpu_exception = cpu.stack_start();
|
||||
kernel_to_user_context_switch((static_cast<Cpu::Context*>(&*regs)),
|
||||
|
||||
@@ -38,6 +38,9 @@ Vm::Vm(Irq::Pool & user_irq_pool,
|
||||
}
|
||||
|
||||
|
||||
Vm::~Vm() {}
|
||||
|
||||
|
||||
void Vm::exception(Cpu & cpu)
|
||||
{
|
||||
switch(_state.cpu_exception) {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
push { r0 }
|
||||
mrc p15, 4, r0, c1, c1, 0 /* read HCR register */
|
||||
tst r0, #1 /* check VM bit */
|
||||
beq _host_to_vm
|
||||
beq _from_host
|
||||
mov r0, #\exception_type
|
||||
b _vm_to_host
|
||||
.endm /* _vm_exit */
|
||||
@@ -47,10 +47,27 @@ _vt_dab_entry: _vm_exit 5
|
||||
_vt_irq_entry: _vm_exit 6
|
||||
_vt_trp_entry: _vm_exit 8
|
||||
|
||||
_host_to_vm:
|
||||
_from_host:
|
||||
pop { r0 }
|
||||
cmp r0, #0
|
||||
beq _to_vm
|
||||
cmp r0, #1
|
||||
beq _invalidate_tlb
|
||||
eret
|
||||
|
||||
|
||||
_invalidate_tlb:
|
||||
push { r3, r4 }
|
||||
mrrc p15, 6, r3, r4, c2 /* save VTTBR */
|
||||
mcrr p15, 6, r1, r2, c2 /* write VTTBR */
|
||||
mcr p15, 0, r0, c8, c3, 0 /* TLBIALLIS */
|
||||
mcrr p15, 6, r3, r4, c2 /* restore VTTBR */
|
||||
eret
|
||||
|
||||
_to_vm:
|
||||
push { r1 }
|
||||
ldr r0, [sp, #1*4]
|
||||
add r0, r0, #13*4
|
||||
push { r2 }
|
||||
add r0, r1, #13*4
|
||||
ldmia r0!, { r1-r5 }
|
||||
msr sp_usr, r1
|
||||
mov lr, r2
|
||||
@@ -115,6 +132,7 @@ _host_to_vm:
|
||||
ldmia r0, {r0-r12} /* load vm's r0-r12 */
|
||||
eret
|
||||
|
||||
|
||||
_vm_to_host:
|
||||
push { r0 } /* push cpu excep. */
|
||||
ldr r0, [sp, #3*4] /* load vm state ptr */
|
||||
@@ -218,6 +236,7 @@ _vm_to_host:
|
||||
|
||||
|
||||
/* host kernel must jump to this point to switch to a vm */
|
||||
.global hypervisor_enter_vm
|
||||
hypervisor_enter_vm:
|
||||
.global hypervisor_call
|
||||
hypervisor_call:
|
||||
hvc #0
|
||||
bx lr
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* \brief Interface between kernel and hypervisor
|
||||
* \author Stefan Kalkowski
|
||||
* \date 2022-06-13
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2022 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _SPEC__ARM_V7__VIRTUALIZATION_HYPERVISOR_H_
|
||||
#define _SPEC__ARM_V7__VIRTUALIZATION_HYPERVISOR_H_
|
||||
|
||||
#include <base/stdint.h>
|
||||
#include <cpu/vm_state_virtualization.h>
|
||||
|
||||
namespace Hypervisor {
|
||||
|
||||
struct Host_context;
|
||||
|
||||
enum Call_number {
|
||||
WORLD_SWITCH = 0,
|
||||
TLB_INVALIDATE = 1,
|
||||
};
|
||||
|
||||
using Call_arg = Genode::umword_t;
|
||||
using Call_ret = Genode::umword_t;
|
||||
|
||||
extern "C"
|
||||
Call_ret hypervisor_call(Call_arg call_id,
|
||||
Call_arg arg0,
|
||||
Call_arg arg1);
|
||||
|
||||
|
||||
inline void invalidate_tlb(Genode::uint64_t vttbr)
|
||||
{
|
||||
hypervisor_call(TLB_INVALIDATE,
|
||||
(vttbr & 0xffffffff),
|
||||
((vttbr >> 32U) & 0xffffffff));
|
||||
}
|
||||
|
||||
|
||||
inline void switch_world(Genode::Vm_state & vm_state,
|
||||
Host_context & host_state)
|
||||
{
|
||||
hypervisor_call(WORLD_SWITCH,
|
||||
(Call_arg)&vm_state,
|
||||
(Call_arg)&host_state);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* _SPEC__ARM_V7__VIRTUALIZATION_HYPERVISOR_H_ */
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <kernel/cpu.h>
|
||||
#include <kernel/vm.h>
|
||||
#include <kernel/main.h>
|
||||
#include <spec/arm_v7/virtualization/hypervisor.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
@@ -41,7 +42,7 @@ namespace Kernel {
|
||||
using namespace Kernel;
|
||||
|
||||
|
||||
struct Host_context
|
||||
struct Hypervisor::Host_context
|
||||
{
|
||||
Cpu::Ttbr_64bit::access_t vttbr;
|
||||
Cpu::Hcr::access_t hcr;
|
||||
@@ -61,15 +62,13 @@ struct Host_context
|
||||
} vt_host_context;
|
||||
|
||||
|
||||
extern "C" void hypervisor_enter_vm(Genode::Vm_state&, Host_context&);
|
||||
|
||||
|
||||
static Host_context & host_context(Cpu & cpu)
|
||||
static Hypervisor::Host_context & host_context(Cpu & cpu)
|
||||
{
|
||||
static Genode::Constructible<Host_context> host_context[NR_OF_CPUS];
|
||||
static Genode::Constructible<Hypervisor::Host_context>
|
||||
host_context[NR_OF_CPUS];
|
||||
if (!host_context[cpu.id()].constructed()) {
|
||||
host_context[cpu.id()].construct();
|
||||
Host_context & c = *host_context[cpu.id()];
|
||||
Hypervisor::Host_context & c = *host_context[cpu.id()];
|
||||
c.sp = cpu.stack_start();
|
||||
c.ttbr0 = Cpu::Ttbr0_64bit::read();
|
||||
c.ttbr1 = Cpu::Ttbr1_64bit::read();
|
||||
@@ -152,6 +151,15 @@ Kernel::Vm::Vm(Irq::Pool & user_irq_pool,
|
||||
}
|
||||
|
||||
|
||||
Kernel::Vm::~Vm()
|
||||
{
|
||||
Cpu::Ttbr_64bit::access_t vttbr =
|
||||
Cpu::Ttbr_64bit::Ba::masked((Cpu::Ttbr_64bit::access_t)_id.table);
|
||||
Cpu::Ttbr_64bit::Asid::set(vttbr, _id.id);
|
||||
Hypervisor::invalidate_tlb(vttbr);
|
||||
}
|
||||
|
||||
|
||||
void Kernel::Vm::exception(Cpu & cpu)
|
||||
{
|
||||
switch(_state.cpu_exception) {
|
||||
@@ -190,7 +198,7 @@ void Kernel::Vm::proceed(Cpu & cpu)
|
||||
_state.esr_el2 = Cpu::Hstr::init();
|
||||
_state.hpfar_el2 = Cpu::Hcr::init();
|
||||
|
||||
hypervisor_enter_vm(_state, host_context(cpu));
|
||||
Hypervisor::switch_world(_state, host_context(cpu));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -26,12 +26,15 @@ Genode::Cpu::Context::Context(bool privileged)
|
||||
}
|
||||
|
||||
|
||||
void Genode::Cpu::switch_to(Context&, Mmu_context & mmu_context)
|
||||
bool Genode::Cpu::active(Mmu_context & mmu_context)
|
||||
{
|
||||
if (mmu_context.id() == 0) return;
|
||||
return (mmu_context.id() == Ttbr::Asid::get(Ttbr0_el1::read()));
|
||||
}
|
||||
|
||||
if (mmu_context.id() != Ttbr::Asid::get(Ttbr0_el1::read()))
|
||||
Ttbr0_el1::write(mmu_context.ttbr);
|
||||
|
||||
void Genode::Cpu::switch_to(Mmu_context & mmu_context)
|
||||
{
|
||||
Ttbr0_el1::write(mmu_context.ttbr);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -99,7 +99,8 @@ struct Genode::Cpu : Hw::Arm_64_cpu
|
||||
return Ttbr::Asid::get(ttbr) & 0xffff; }
|
||||
};
|
||||
|
||||
void switch_to(Context&, Mmu_context &);
|
||||
bool active(Mmu_context &);
|
||||
void switch_to(Mmu_context &);
|
||||
|
||||
static void mmu_fault(Context &, Kernel::Thread_fault &);
|
||||
|
||||
|
||||
@@ -55,6 +55,16 @@ void Thread::exception(Cpu & cpu)
|
||||
" ISS=", Cpu::Esr::Iss::get(esr),
|
||||
" ip=", (void*)regs->ip);
|
||||
};
|
||||
|
||||
/*
|
||||
* If the machine exception is caused by a non-privileged
|
||||
* component, mark it dead, and continue execution.
|
||||
*/
|
||||
if (regs->exception_type == Cpu::SYNC_LEVEL_EL0) {
|
||||
Genode::raw("Will freeze thread ", *this);
|
||||
_become_inactive(DEAD);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -75,10 +85,14 @@ void Thread::exception(Cpu & cpu)
|
||||
void Kernel::Thread::Tlb_invalidation::execute() { };
|
||||
|
||||
|
||||
bool Kernel::Pd::invalidate_tlb(Cpu &, addr_t addr, size_t size)
|
||||
bool Kernel::Pd::invalidate_tlb(Cpu & cpu, addr_t addr, size_t size)
|
||||
{
|
||||
using namespace Genode;
|
||||
|
||||
/* only apply to the active cpu */
|
||||
if (cpu.id() != Cpu::executing_id())
|
||||
return false;
|
||||
|
||||
/**
|
||||
* The kernel part of the address space is mapped as global
|
||||
* therefore we have to invalidate it differently
|
||||
@@ -108,7 +122,9 @@ bool Kernel::Pd::invalidate_tlb(Cpu &, addr_t addr, size_t size)
|
||||
|
||||
void Thread::proceed(Cpu & cpu)
|
||||
{
|
||||
cpu.switch_to(*regs, pd().mmu_regs);
|
||||
if (!cpu.active(pd().mmu_regs) && type() != CORE)
|
||||
cpu.switch_to(pd().mmu_regs);
|
||||
|
||||
kernel_to_user_context_switch((static_cast<Cpu::Context*>(&*regs)),
|
||||
(void*)cpu.stack_start());
|
||||
}
|
||||
|
||||
@@ -22,24 +22,34 @@
|
||||
.global hypervisor_exception_vector
|
||||
hypervisor_exception_vector:
|
||||
.rept 16
|
||||
add sp, sp, #-16 /* push x0, x1 to stack */
|
||||
stp x0, x1, [sp]
|
||||
mrs x1, hcr_el2 /* read HCR register */
|
||||
tst x1, #1 /* check VM bit */
|
||||
beq _host_to_vm /* if VM bit is not set, switch to VM */
|
||||
ldr x0, [sp, #32] /* otherwise, load vm_state pointer */
|
||||
adr x1, . /* hold exception vector offset in x1 */
|
||||
and x1, x1, #0xf80
|
||||
b _vm_to_host
|
||||
add sp, sp, #-16 /* push x29, x30 to stack */
|
||||
stp x29, x30, [sp]
|
||||
mrs x30, hcr_el2 /* read HCR register */
|
||||
tst x30, #1 /* check VM bit */
|
||||
beq _from_host /* if VM bit is not set, its a host call */
|
||||
ldr x29, [sp, #32] /* otherwise, load vm_state pointer */
|
||||
adr x30, . /* hold exception vector offset in x30 */
|
||||
and x30, x30, #0xf80
|
||||
b _from_vm
|
||||
.balign 128
|
||||
.endr
|
||||
|
||||
_host_to_vm:
|
||||
_from_host:
|
||||
ldp x29, x30, [sp], #2*8 /* pop x29, x30 from stack */
|
||||
cmp x0, #0
|
||||
beq _to_vm
|
||||
cmp x0, #1
|
||||
beq _invalidate_tlb
|
||||
eret
|
||||
|
||||
add sp, sp, #-16 /* push arg2 (vm pic state) to stack */
|
||||
str x2, [sp]
|
||||
_to_vm:
|
||||
add sp, sp, #-16 /* push arg1/2 (vm/host state to stack */
|
||||
stp x1, x2, [sp]
|
||||
add sp, sp, #-16 /* push arg3 (vm pic state) to stack */
|
||||
str x3, [sp]
|
||||
|
||||
msr vttbr_el2, x3 /* stage2 table pointer was arg3 */
|
||||
msr vttbr_el2, x4 /* stage2 table pointer was arg4 */
|
||||
mov x0, x1
|
||||
|
||||
add x0, x0, #31*8 /* skip x0...x30, loaded later */
|
||||
|
||||
@@ -179,27 +189,38 @@ _host_to_vm:
|
||||
|
||||
eret
|
||||
|
||||
_vm_to_host:
|
||||
|
||||
_invalidate_tlb:
|
||||
msr vttbr_el2, x1
|
||||
tlbi vmalle1is
|
||||
msr vttbr_el2, xzr
|
||||
eret
|
||||
|
||||
|
||||
_from_vm:
|
||||
|
||||
/*********************
|
||||
** Save vm context **
|
||||
*********************/
|
||||
|
||||
/** general-purpose register **/
|
||||
add x0, x0, #2*8 /* skip x0 and x1 for now */
|
||||
stp x2, x3, [x0], #2*8
|
||||
stp x4, x5, [x0], #2*8
|
||||
stp x6, x7, [x0], #2*8
|
||||
stp x8, x9, [x0], #2*8
|
||||
stp x10, x11, [x0], #2*8
|
||||
stp x12, x13, [x0], #2*8
|
||||
stp x14, x15, [x0], #2*8
|
||||
stp x16, x17, [x0], #2*8
|
||||
stp x18, x19, [x0], #2*8
|
||||
stp x20, x21, [x0], #2*8
|
||||
stp x22, x23, [x0], #2*8
|
||||
stp x24, x25, [x0], #2*8
|
||||
stp x26, x27, [x0], #2*8
|
||||
stp x0, x1, [x29], #2*8
|
||||
stp x2, x3, [x29], #2*8
|
||||
stp x4, x5, [x29], #2*8
|
||||
stp x6, x7, [x29], #2*8
|
||||
stp x8, x9, [x29], #2*8
|
||||
stp x10, x11, [x29], #2*8
|
||||
stp x12, x13, [x29], #2*8
|
||||
stp x14, x15, [x29], #2*8
|
||||
stp x16, x17, [x29], #2*8
|
||||
stp x18, x19, [x29], #2*8
|
||||
stp x20, x21, [x29], #2*8
|
||||
stp x22, x23, [x29], #2*8
|
||||
stp x24, x25, [x29], #2*8
|
||||
stp x26, x27, [x29], #2*8
|
||||
mov x0, x29
|
||||
mov x1, x30
|
||||
ldp x29, x30, [sp], #2*8 /* pop x29, x30 from stack */
|
||||
stp x28, x29, [x0], #2*8
|
||||
str x30, [x0], #1*8
|
||||
|
||||
@@ -284,11 +305,8 @@ _vm_to_host:
|
||||
mov x0, #0b111
|
||||
msr cnthctl_el2, x0
|
||||
|
||||
|
||||
ldp x0, x1, [sp], #2*8 /* pop x0, x1 from stack */
|
||||
ldr x29, [sp], #2*8 /* pop vm pic state from stack */
|
||||
ldp x2, x30, [sp], #2*8 /* pop vm, and host state from stack */
|
||||
stp x0, x1, [x2] /* save x0, x1 to vm state */
|
||||
ldr x29, [sp], #2*8 /* pop vm pic state from stack */
|
||||
ldp x2, x30, [sp], #2*8 /* pop vm, and host state from stack */
|
||||
|
||||
|
||||
/**********************
|
||||
@@ -364,6 +382,7 @@ _vm_to_host:
|
||||
eret
|
||||
|
||||
/* host kernel must jump to this point to switch to a vm */
|
||||
.global hypervisor_enter_vm
|
||||
hypervisor_enter_vm:
|
||||
.global hypervisor_call
|
||||
hypervisor_call:
|
||||
hvc #0
|
||||
ret
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* \brief Interface between kernel and hypervisor
|
||||
* \author Stefan Kalkowski
|
||||
* \date 2022-06-13
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2022 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _SPEC__ARM_V8__VIRTUALIZATION_HYPERVISOR_H_
|
||||
#define _SPEC__ARM_V8__VIRTUALIZATION_HYPERVISOR_H_
|
||||
|
||||
#include <base/stdint.h>
|
||||
|
||||
namespace Hypervisor {
|
||||
|
||||
enum Call_number {
|
||||
WORLD_SWITCH = 0,
|
||||
TLB_INVALIDATE = 1,
|
||||
};
|
||||
|
||||
using Call_arg = Genode::umword_t;
|
||||
using Call_ret = Genode::umword_t;
|
||||
|
||||
extern "C"
|
||||
Call_ret hypervisor_call(Call_arg call_id,
|
||||
Call_arg arg0,
|
||||
Call_arg arg1,
|
||||
Call_arg arg2,
|
||||
Call_arg arg3);
|
||||
|
||||
|
||||
inline void invalidate_tlb(Call_arg ttbr)
|
||||
{
|
||||
hypervisor_call(TLB_INVALIDATE, ttbr, 0, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
inline void switch_world(Call_arg guest_state,
|
||||
Call_arg host_state,
|
||||
Call_arg pic_state,
|
||||
Call_arg ttbr)
|
||||
{
|
||||
hypervisor_call(WORLD_SWITCH, guest_state, host_state, pic_state, ttbr);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* _SPEC__ARM_V8__VIRTUALIZATION_HYPERVISOR_H_ */
|
||||
@@ -23,14 +23,12 @@
|
||||
#include <kernel/vm.h>
|
||||
#include <kernel/main.h>
|
||||
|
||||
#include <spec/arm_v8/virtualization/hypervisor.h>
|
||||
|
||||
using Genode::addr_t;
|
||||
using Kernel::Cpu;
|
||||
using Kernel::Vm;
|
||||
|
||||
extern void * kernel_stack;
|
||||
extern "C" void hypervisor_enter_vm(addr_t vm, addr_t host,
|
||||
addr_t pic, addr_t guest_table);
|
||||
|
||||
|
||||
static Genode::Vm_state & host_context(Cpu & cpu)
|
||||
{
|
||||
@@ -154,6 +152,15 @@ Vm::Vm(Irq::Pool & user_irq_pool,
|
||||
}
|
||||
|
||||
|
||||
Vm::~Vm()
|
||||
{
|
||||
Cpu::Vttbr_el2::access_t vttbr_el2 =
|
||||
Cpu::Vttbr_el2::Ba::masked((Cpu::Vttbr_el2::access_t)_id.table);
|
||||
Cpu::Vttbr_el2::Asid::set(vttbr_el2, _id.id);
|
||||
Hypervisor::invalidate_tlb(vttbr_el2);
|
||||
}
|
||||
|
||||
|
||||
void Vm::exception(Cpu & cpu)
|
||||
{
|
||||
switch (_state.exception_type) {
|
||||
@@ -197,7 +204,7 @@ void Vm::proceed(Cpu & cpu)
|
||||
addr_t pic = Hw::Mm::el2_addr(&_vcpu_context.pic);
|
||||
addr_t host = Hw::Mm::el2_addr(&host_context(cpu));
|
||||
|
||||
hypervisor_enter_vm(guest, host, pic, vttbr_el2);
|
||||
Hypervisor::switch_world(guest, host, pic, vttbr_el2);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -115,11 +115,14 @@ class Genode::Cpu : public Arm_v7_cpu
|
||||
*/
|
||||
static unsigned executing_id() { return Mpidr::Aff_0::get(Mpidr::read()); }
|
||||
|
||||
|
||||
void switch_to(Context &, Mmu_context & mmu_context)
|
||||
bool active(Mmu_context & mmu_context)
|
||||
{
|
||||
if (mmu_context.id() && (Ttbr0_64bit::read() != mmu_context.ttbr0))
|
||||
Ttbr0_64bit::write(mmu_context.ttbr0);
|
||||
return (Ttbr0_64bit::read() == mmu_context.ttbr0);
|
||||
}
|
||||
|
||||
void switch_to(Mmu_context & mmu_context)
|
||||
{
|
||||
Ttbr0_64bit::write(mmu_context.ttbr0);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -53,22 +53,16 @@ Mmu_context::~Mmu_context()
|
||||
}
|
||||
|
||||
|
||||
bool Genode::Cpu::active(Mmu_context & context)
|
||||
{
|
||||
return Satp::read() == context.satp;
|
||||
}
|
||||
|
||||
|
||||
void Genode::Cpu::switch_to(Mmu_context & context)
|
||||
{
|
||||
/*
|
||||
* The sstatus register defines to which privilege level
|
||||
* the machin returns when doing an exception return
|
||||
*/
|
||||
bool user = Satp::Asid::get(context.satp);
|
||||
Sstatus::access_t v = Sstatus::read();
|
||||
Sstatus::Spp::set(v, user ? 0 : 1);
|
||||
Sstatus::write(v);
|
||||
|
||||
/* change the translation table when necessary */
|
||||
if (user) {
|
||||
Satp::write(context.satp);
|
||||
sfence();
|
||||
}
|
||||
Satp::write(context.satp);
|
||||
sfence();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -97,6 +97,7 @@ class Genode::Cpu : public Hw::Riscv_cpu
|
||||
|
||||
static void invalidate_tlb_by_pid(unsigned const /* pid */) { sfence(); }
|
||||
|
||||
bool active(Mmu_context & context);
|
||||
void switch_to(Mmu_context & context);
|
||||
static void mmu_fault(Context & c, Kernel::Thread_fault & f);
|
||||
|
||||
|
||||
@@ -100,7 +100,16 @@ void Kernel::Thread::_call_cache_invalidate_data_region() { }
|
||||
|
||||
void Kernel::Thread::proceed(Cpu & cpu)
|
||||
{
|
||||
cpu.switch_to(_pd->mmu_regs);
|
||||
/*
|
||||
* The sstatus register defines to which privilege level
|
||||
* the machine returns when doing an exception return
|
||||
*/
|
||||
Cpu::Sstatus::access_t v = Cpu::Sstatus::read();
|
||||
Cpu::Sstatus::Spp::set(v, (type() == USER) ? 0 : 1);
|
||||
Cpu::Sstatus::write(v);
|
||||
|
||||
if (!cpu.active(pd().mmu_regs) && type() != CORE)
|
||||
cpu.switch_to(_pd->mmu_regs);
|
||||
|
||||
asm volatile("csrw sscratch, %1 \n"
|
||||
"mv x31, %0 \n"
|
||||
|
||||
@@ -111,11 +111,20 @@ extern void const * const kernel_stack;
|
||||
extern Genode::size_t const kernel_stack_size;
|
||||
|
||||
|
||||
void Genode::Cpu::switch_to(Context & context, Mmu_context &mmu_context)
|
||||
bool Genode::Cpu::active(Mmu_context &mmu_context)
|
||||
{
|
||||
if ((context.cs != 0x8) && (mmu_context.cr3 != Cr3::read()))
|
||||
Cr3::write(mmu_context.cr3);
|
||||
return (mmu_context.cr3 == Cr3::read());
|
||||
}
|
||||
|
||||
|
||||
void Genode::Cpu::switch_to(Mmu_context &mmu_context)
|
||||
{
|
||||
Cr3::write(mmu_context.cr3);
|
||||
}
|
||||
|
||||
|
||||
void Genode::Cpu::switch_to(Context & context)
|
||||
{
|
||||
tss.ist[0] = (addr_t)&context + sizeof(Genode::Cpu_state);
|
||||
|
||||
addr_t const stack_base = reinterpret_cast<addr_t>(&kernel_stack);
|
||||
|
||||
@@ -126,7 +126,10 @@ class Genode::Cpu : public Hw::X86_64_cpu
|
||||
*
|
||||
* \param context next CPU context
|
||||
*/
|
||||
void switch_to(Context & context, Mmu_context &mmu_context);
|
||||
void switch_to(Context & context);
|
||||
|
||||
bool active(Mmu_context &mmu_context);
|
||||
void switch_to(Mmu_context &mmu_context);
|
||||
|
||||
static void mmu_fault(Context & regs, Kernel::Thread_fault & fault);
|
||||
|
||||
|
||||
@@ -43,7 +43,10 @@ void Kernel::Thread::_call_cache_invalidate_data_region() { }
|
||||
|
||||
void Kernel::Thread::proceed(Cpu & cpu)
|
||||
{
|
||||
cpu.switch_to(*regs, pd().mmu_regs);
|
||||
if (!cpu.active(pd().mmu_regs) && type() != CORE)
|
||||
cpu.switch_to(pd().mmu_regs);
|
||||
|
||||
cpu.switch_to(*regs);
|
||||
|
||||
asm volatile("fxrstor (%1) \n"
|
||||
"mov %0, %%rsp \n"
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
TARGET = ld-hw
|
||||
LIBS = ld-hw
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Genode Labs GmbH
|
||||
* Copyright (C) 2014-2022 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
@@ -18,125 +18,110 @@
|
||||
/* core includes */
|
||||
#include <kernel/cpu_scheduler.h>
|
||||
|
||||
/*
|
||||
* Utilities
|
||||
*/
|
||||
using namespace Genode;
|
||||
using namespace Kernel;
|
||||
|
||||
using Genode::size_t;
|
||||
using Genode::addr_t;
|
||||
using Genode::construct_at;
|
||||
using Kernel::Cpu_share;
|
||||
using Kernel::Cpu_scheduler;
|
||||
|
||||
|
||||
struct Data
|
||||
struct Main
|
||||
{
|
||||
Cpu_share idle;
|
||||
Cpu_scheduler scheduler;
|
||||
char shares[9][sizeof(Cpu_share)];
|
||||
enum { MAX_SHARES = 10 };
|
||||
|
||||
Data() : idle(0, 0), scheduler(idle, 1000, 100) { }
|
||||
Constructible<Cpu_share> shares[MAX_SHARES] {};
|
||||
Cpu_scheduler scheduler;
|
||||
time_t current_time { 0 };
|
||||
|
||||
Cpu_share & _idle()
|
||||
{
|
||||
if (!shares[0].constructed()) shares[0].construct(0, 0);
|
||||
return *shares[0];
|
||||
}
|
||||
|
||||
Main() : scheduler(_idle(), 1000, 100) { }
|
||||
|
||||
void done()
|
||||
{
|
||||
Genode::log("done");
|
||||
while (1) ;
|
||||
}
|
||||
|
||||
unsigned share_id(Cpu_share & share)
|
||||
{
|
||||
for (unsigned i = 0; i < MAX_SHARES; i++)
|
||||
if (shares[i].constructed() && (&*shares[i] == &share))
|
||||
return i;
|
||||
return ~0U;
|
||||
}
|
||||
|
||||
Cpu_share & share(unsigned const id)
|
||||
{
|
||||
return *shares[id];
|
||||
}
|
||||
|
||||
void create(unsigned const id)
|
||||
{
|
||||
switch (id) {
|
||||
case 1: shares[id].construct(2, 230); break;
|
||||
case 2: shares[id].construct(0, 170); break;
|
||||
case 3: shares[id].construct(3, 110); break;
|
||||
case 4: shares[id].construct(1, 90); break;
|
||||
case 5: shares[id].construct(3, 120); break;
|
||||
case 6: shares[id].construct(3, 0); break;
|
||||
case 7: shares[id].construct(2, 180); break;
|
||||
case 8: shares[id].construct(2, 100); break;
|
||||
case 9: shares[id].construct(2, 0); break;
|
||||
default: return;
|
||||
}
|
||||
scheduler.insert(*shares[id]);
|
||||
}
|
||||
|
||||
void destroy(unsigned const id)
|
||||
{
|
||||
if (!id || id >= MAX_SHARES)
|
||||
return;
|
||||
|
||||
scheduler.remove(share(id));
|
||||
shares[id].destruct();
|
||||
}
|
||||
|
||||
unsigned time()
|
||||
{
|
||||
return scheduler.quota() - scheduler.residual();
|
||||
}
|
||||
|
||||
void update_check(unsigned const l, unsigned const c, unsigned const t,
|
||||
unsigned const s, unsigned const q)
|
||||
{
|
||||
current_time += c;
|
||||
scheduler.update(current_time);
|
||||
unsigned const st = time();
|
||||
if (t != st) {
|
||||
log("wrong time ", st, " in line ", l);
|
||||
done();
|
||||
}
|
||||
Cpu_share & hs = scheduler.head();
|
||||
unsigned const hq = scheduler.head_quota();
|
||||
if (&hs != &share(s)) {
|
||||
log("wrong share ", share_id(hs), " in line ", l);
|
||||
done();
|
||||
}
|
||||
if (hq != q) {
|
||||
log("wrong quota ", hq, " in line ", l);
|
||||
done();
|
||||
}
|
||||
}
|
||||
|
||||
void ready_check(unsigned const l, unsigned const s, bool const x)
|
||||
{
|
||||
scheduler.ready(share(s));
|
||||
if (scheduler.need_to_schedule() != x) {
|
||||
log("wrong check result ", scheduler.need_to_schedule(), " in line ", l);
|
||||
done();
|
||||
}
|
||||
}
|
||||
|
||||
void test();
|
||||
};
|
||||
|
||||
|
||||
Data * data()
|
||||
{
|
||||
static Data d;
|
||||
return &d;
|
||||
}
|
||||
|
||||
|
||||
void done()
|
||||
{
|
||||
Genode::log("done");
|
||||
while (1) ;
|
||||
}
|
||||
|
||||
|
||||
unsigned share_id(void * const pointer)
|
||||
{
|
||||
addr_t const address = (addr_t)pointer;
|
||||
addr_t const base = (addr_t)data()->shares;
|
||||
if (address < base || address >= base + sizeof(data()->shares)) {
|
||||
return 0; }
|
||||
return (unsigned)((address - base) / sizeof(Cpu_share) + 1);
|
||||
}
|
||||
|
||||
|
||||
Cpu_share * share(unsigned const id)
|
||||
{
|
||||
if (!id) { return &data()->idle; }
|
||||
return reinterpret_cast<Cpu_share *>(&data()->shares[id - 1]);
|
||||
}
|
||||
|
||||
|
||||
void create(unsigned const id)
|
||||
{
|
||||
Cpu_share * const s = share(id);
|
||||
void * const p = (void *)s;
|
||||
switch (id) {
|
||||
case 1: construct_at<Cpu_share>(p, 2, 230); break;
|
||||
case 2: construct_at<Cpu_share>(p, 0, 170); break;
|
||||
case 3: construct_at<Cpu_share>(p, 3, 110); break;
|
||||
case 4: construct_at<Cpu_share>(p, 1, 90); break;
|
||||
case 5: construct_at<Cpu_share>(p, 3, 120); break;
|
||||
case 6: construct_at<Cpu_share>(p, 3, 0); break;
|
||||
case 7: construct_at<Cpu_share>(p, 2, 180); break;
|
||||
case 8: construct_at<Cpu_share>(p, 2, 100); break;
|
||||
case 9: construct_at<Cpu_share>(p, 2, 0); break;
|
||||
default: return;
|
||||
}
|
||||
data()->scheduler.insert(*s);
|
||||
}
|
||||
|
||||
|
||||
void destroy(unsigned const id)
|
||||
{
|
||||
Cpu_share * const s = share(id);
|
||||
data()->scheduler.remove(*s);
|
||||
s->~Cpu_share();
|
||||
}
|
||||
|
||||
|
||||
unsigned time()
|
||||
{
|
||||
return data()->scheduler.quota() -
|
||||
data()->scheduler.residual();
|
||||
}
|
||||
|
||||
|
||||
void update_check(unsigned const l, unsigned const c, unsigned const t,
|
||||
unsigned const s, unsigned const q)
|
||||
{
|
||||
data()->scheduler.update(c);
|
||||
unsigned const st = time();
|
||||
if (t != st) {
|
||||
Genode::log("wrong time ", st, " in line ", l);
|
||||
done();
|
||||
}
|
||||
Cpu_share &hs = data()->scheduler.head();
|
||||
unsigned const hq = data()->scheduler.head_quota();
|
||||
if (&hs != share(s)) {
|
||||
unsigned const hi = share_id(&hs);
|
||||
Genode::log("wrong share ", hi, " in line ", l);
|
||||
done();
|
||||
}
|
||||
if (hq != q) {
|
||||
Genode::log("wrong quota ", hq, " in line ", l);
|
||||
done();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ready_check(unsigned const l, unsigned const s, bool const x)
|
||||
{
|
||||
data()->scheduler.ready_check(*share(s));
|
||||
if (data()->scheduler.need_to_schedule() != x) {
|
||||
Genode::log("wrong check result ", data()->scheduler.need_to_schedule(), " in line ", l);
|
||||
done();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Shortcuts for all basic operations that the test consists of
|
||||
@@ -144,10 +129,10 @@ void ready_check(unsigned const l, unsigned const s, bool const x)
|
||||
|
||||
#define C(s) create(s);
|
||||
#define D(s) destroy(s);
|
||||
#define A(s) data()->scheduler.ready(*share(s));
|
||||
#define I(s) data()->scheduler.unready(*share(s));
|
||||
#define Y data()->scheduler.yield();
|
||||
#define Q(s, q) data()->scheduler.quota(*share(s), q);
|
||||
#define A(s) scheduler.ready(share(s));
|
||||
#define I(s) scheduler.unready(share(s));
|
||||
#define Y scheduler.yield();
|
||||
#define Q(s, q) scheduler.quota(share(s), q);
|
||||
#define U(c, t, s, q) update_check(__LINE__, c, t, s, q);
|
||||
#define O(s) ready_check(__LINE__, s, true);
|
||||
#define N(s) ready_check(__LINE__, s, false);
|
||||
@@ -157,6 +142,13 @@ void ready_check(unsigned const l, unsigned const s, bool const x)
|
||||
* Main routine
|
||||
*/
|
||||
void Component::construct(Genode::Env &)
|
||||
{
|
||||
static Main main;
|
||||
main.test();
|
||||
}
|
||||
|
||||
|
||||
void Main::test()
|
||||
{
|
||||
/*
|
||||
* Step-by-step testing
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
FROM_BASE_LINUX := etc src/lib/syscall src/lib/lx_hybrid lib/import include
|
||||
FROM_BASE_LINUX_AND_BASE := lib/mk src/lib/base src/include
|
||||
FROM_BASE := src/lib/timeout
|
||||
FROM_BASE_LINUX_AND_BASE := src/lib/base src/include
|
||||
|
||||
content: $(FROM_BASE_LINUX) $(FROM_BASE_LINUX_AND_BASE) $(FROM_BASE) LICENSE
|
||||
content: $(FROM_BASE_LINUX) $(FROM_BASE_LINUX_AND_BASE) LICENSE
|
||||
|
||||
$(FROM_BASE_LINUX):
|
||||
mkdir -p $@
|
||||
@@ -13,9 +12,30 @@ $(FROM_BASE_LINUX_AND_BASE):
|
||||
cp -r $(GENODE_DIR)/repos/base/$@/* $@
|
||||
cp -r $(REP_DIR)/$@/* $@
|
||||
|
||||
$(FROM_BASE):
|
||||
BASE_LIB_MK_CONTENT := \
|
||||
$(addprefix lib/mk/,base-common.inc timeout.mk)
|
||||
|
||||
content: $(BASE_LIB_MK_CONTENT)
|
||||
|
||||
$(BASE_LIB_MK_CONTENT):
|
||||
mkdir -p $(dir $@)
|
||||
cp $(GENODE_DIR)/repos/base/$@ $@
|
||||
|
||||
content: src/lib/timeout
|
||||
|
||||
src/lib/timeout:
|
||||
mkdir -p $@
|
||||
cp -r $(GENODE_DIR)/repos/base/$@/* $@
|
||||
|
||||
BASE_LINUX_LIB_MK_CONTENT := \
|
||||
$(addprefix lib/mk/,lx_hybrid.mk base-linux.inc base-linux-common.mk) \
|
||||
$(foreach S,arm arm_64 x86_32 x86_64,lib/mk/spec/$S/syscall-linux.mk)
|
||||
|
||||
content: $(BASE_LINUX_LIB_MK_CONTENT)
|
||||
|
||||
$(BASE_LINUX_LIB_MK_CONTENT):
|
||||
mkdir -p $(dir $@)
|
||||
cp $(REP_DIR)/$@ $@
|
||||
|
||||
LICENSE:
|
||||
cp $(GENODE_DIR)/LICENSE $@
|
||||
|
||||
@@ -1 +1 @@
|
||||
2022-04-12 dcb2c9200b333adb17f9a8737620cbd84f641408
|
||||
2022-10-11 4544924c73b2ee1d8d2717672320f14732807267
|
||||
|
||||
@@ -10,7 +10,6 @@ content:
|
||||
mv lib/mk/spec/$$spec/ld-linux.mk lib/mk/spec/$$spec/ld.mk; done;
|
||||
sed -i "/TARGET/s/core-linux/core/" src/core/linux/target.mk
|
||||
sed -i "s/BOARD.*unknown/BOARD := linux/" lib/mk/core-linux.inc
|
||||
sed -i "s/ld-linux/ld/" src/lib/ld/linux/target.mk
|
||||
sed -i "s/linux_timer_drv/timer/" src/timer/linux/target.mk
|
||||
rm -rf src/lib/initramfs
|
||||
rm -rf src/initramfs
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
2022-05-24 4aea382035415c79bf5d551642ebfa64d42e4d21
|
||||
2022-10-11 d7e12d81f12f081bb7c00233c18f3c8ac2f00d67
|
||||
|
||||
@@ -5,14 +5,14 @@ REQUIRES = x86_64
|
||||
INITRAMFS = initramfs
|
||||
INITRAMFS_SRC_C = init.c
|
||||
|
||||
EXT_OBJECTS += $(BUILD_BASE_DIR)/lib/initramfs/$(INITRAMFS)
|
||||
EXT_OBJECTS += $(BUILD_BASE_DIR)/initramfs/$(INITRAMFS)
|
||||
|
||||
$(TARGET): $(INITRAMFS)
|
||||
|
||||
$(INITRAMFS): $(INITRAMFS_SRC_C)
|
||||
$(MSG_BUILD)$(INITRAMFS)
|
||||
$(VERBOSE)gcc $^ -O0 $(CC_MARCH) -Wall -W -Wextra -Werror -std=gnu99 -o $@ -Wl,-O3 -Wl,--as-needed -static
|
||||
$(VERBOSE)ln -sf $(BUILD_BASE_DIR)/lib/initramfs/$(INITRAMFS) $(BUILD_BASE_DIR)/bin/
|
||||
$(VERBOSE)ln -sf $(BUILD_BASE_DIR)/initramfs/$(INITRAMFS) $(BUILD_BASE_DIR)/bin/
|
||||
|
||||
clean_initramfs:
|
||||
$(VERBOSE)rm -rf $(INITRAMFS)
|
||||
@@ -172,6 +172,24 @@ void Region_map_mmap::_add_to_rmap(Region const ®ion)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Tracing must be inhibited in attach/detach as RPC trace points may trigger
|
||||
* attachment of trace dataspaces, which would result in nested mutex
|
||||
* acquisition.
|
||||
*/
|
||||
|
||||
namespace Genode { extern bool inhibit_tracing; }
|
||||
|
||||
struct Inhibit_tracing_guard
|
||||
{
|
||||
bool old_value = inhibit_tracing;
|
||||
|
||||
Inhibit_tracing_guard() { inhibit_tracing = true; }
|
||||
|
||||
~Inhibit_tracing_guard() { inhibit_tracing = old_value; }
|
||||
};
|
||||
|
||||
|
||||
Region_map::Local_addr Region_map_mmap::attach(Dataspace_capability ds,
|
||||
size_t size, off_t offset,
|
||||
bool use_local_addr,
|
||||
@@ -180,6 +198,8 @@ Region_map::Local_addr Region_map_mmap::attach(Dataspace_capability ds,
|
||||
{
|
||||
Mutex::Guard mutex_guard(mutex());
|
||||
|
||||
Inhibit_tracing_guard it_guard { };
|
||||
|
||||
/* only support attach_at for sub RM sessions */
|
||||
if (_sub_rm && !use_local_addr) {
|
||||
error("Region_map_mmap::attach: attaching w/o local addr not supported");
|
||||
@@ -325,6 +345,8 @@ void Region_map_mmap::detach(Region_map::Local_addr local_addr)
|
||||
{
|
||||
Mutex::Guard mutex_guard(mutex());
|
||||
|
||||
Inhibit_tracing_guard it_guard { };
|
||||
|
||||
/*
|
||||
* Cases
|
||||
*
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
TARGET = ld-linux.lib
|
||||
LIBS = ld-linux
|
||||
|
||||
BUILD_ARTIFACTS := ld-linux.lib.so
|
||||
@@ -260,6 +260,30 @@ namespace Nova {
|
||||
*/
|
||||
enum Pd_op { TRANSFER_QUOTA = 0U, PD_DEBUG = 2U };
|
||||
|
||||
class Gsi_flags
|
||||
{
|
||||
private:
|
||||
|
||||
uint8_t _value { 0 };
|
||||
|
||||
public:
|
||||
|
||||
enum Mode { HIGH, LOW, EDGE };
|
||||
|
||||
Gsi_flags() { }
|
||||
|
||||
Gsi_flags(Mode m)
|
||||
{
|
||||
switch (m) {
|
||||
case HIGH: _value = 0b110; break; /* level-high */
|
||||
case LOW: _value = 0b111; break; /* level-low */
|
||||
case EDGE: _value = 0b100; break; /* edge-triggered */
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t value() const { return _value; }
|
||||
};
|
||||
|
||||
|
||||
class Descriptor
|
||||
{
|
||||
|
||||
@@ -457,12 +457,12 @@ namespace Nova {
|
||||
ALWAYS_INLINE
|
||||
inline uint8_t assign_gsi(mword_t sm, mword_t dev, mword_t cpu,
|
||||
mword_t &msi_addr, mword_t &msi_data,
|
||||
mword_t si = ~0UL)
|
||||
mword_t si = ~0UL, Gsi_flags flags = Gsi_flags())
|
||||
{
|
||||
msi_addr = dev;
|
||||
msi_data = cpu;
|
||||
|
||||
return syscall_5(NOVA_ASSIGN_GSI, 0, sm, msi_addr, msi_data, si);
|
||||
return syscall_5(NOVA_ASSIGN_GSI, flags.value(), sm, msi_addr, msi_data, si);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -402,11 +402,11 @@ namespace Nova {
|
||||
ALWAYS_INLINE
|
||||
inline uint8_t assign_gsi(mword_t sm, mword_t dev, mword_t cpu,
|
||||
mword_t &msi_addr, mword_t &msi_data,
|
||||
mword_t si = ~0UL)
|
||||
mword_t si = ~0UL, Gsi_flags flags = Gsi_flags())
|
||||
{
|
||||
msi_addr = dev;
|
||||
msi_data = cpu;
|
||||
return syscall_5(NOVA_ASSIGN_GSI, 0, sm, msi_addr, msi_data, si);
|
||||
return syscall_5(NOVA_ASSIGN_GSI, flags.value(), sm, msi_addr, msi_data, si);
|
||||
}
|
||||
}
|
||||
#endif /* _INCLUDE__SPEC__64BIT__NOVA__SYSCALLS_H_ */
|
||||
|
||||
@@ -1 +1 @@
|
||||
33a2fa953ec52b0f63b921f4d33d68891c0aada0
|
||||
9ad770935115d201863fd83924e4684b14b8b56f
|
||||
|
||||
@@ -4,7 +4,7 @@ DOWNLOADS := nova.git
|
||||
|
||||
# r10 branch
|
||||
URL(nova) := https://github.com/alex-ab/NOVA.git
|
||||
REV(nova) := 00dc49bc18e7f72a9c85487e8f94fd859511d89d
|
||||
REV(nova) := a34076e7b8d48d08c2edee7754eadad8b6ea5312
|
||||
DIR(nova) := src/kernel/nova
|
||||
|
||||
PATCHES := $(sort $(wildcard $(REP_DIR)/patches/*.patch))
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
FROM_BASE_NOVA := etc include
|
||||
FROM_BASE := lib/mk/timeout.mk src/lib/timeout
|
||||
|
||||
# base-nova.lib.a depends on timeout.lib.a, which includes base/internal/gloabls.h
|
||||
FROM_BASE := lib/mk/timeout.mk src/lib/timeout \
|
||||
src/include/base/internal/globals.h
|
||||
|
||||
content: $(FROM_BASE_NOVA) $(FROM_BASE) LICENSE
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
2022-05-24 91bc8d51bbe703d56f5671019d14e4636f21bf1f
|
||||
2022-10-11 4458ea63a69ae070e19a3cb09a403137755d2cb0
|
||||
|
||||
@@ -15,8 +15,7 @@ src/kernel/nova: src/kernel
|
||||
|
||||
content:
|
||||
for spec in x86_32 x86_64; do \
|
||||
mv lib/mk/spec/$$spec/ld-nova.mk lib/mk/spec/$$spec/ld.mk; \
|
||||
mv lib/mk/spec/$$spec/ld-nova.mk lib/mk/spec/$$spec/ld.mk; \
|
||||
done;
|
||||
sed -i "s/ld-nova/ld/" src/lib/ld/nova/target.mk
|
||||
sed -i "s/nova_timer_drv/timer/" src/timer/nova/target.mk
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
2022-05-24 8b59a28ade1392bae4aa772bbead1584a2dde1de
|
||||
2022-10-11 574204b7d442811236bba60e4fe3f79e34fe9985
|
||||
|
||||
@@ -13,7 +13,9 @@
|
||||
#ifndef _CORE__INCLUDE__IRQ_OBJECT_H_
|
||||
#define _CORE__INCLUDE__IRQ_OBJECT_H_
|
||||
|
||||
namespace Genode { class Irq_object; }
|
||||
#include <nova/syscall-generic.h> /* Gsi_flags */
|
||||
|
||||
namespace Genode { class Irq_object; class Irq_args; }
|
||||
|
||||
class Genode::Irq_object
|
||||
{
|
||||
@@ -26,22 +28,24 @@ class Genode::Irq_object
|
||||
addr_t _msi_data;
|
||||
addr_t _device_phys = 0; /* PCI config extended address */
|
||||
|
||||
Nova::Gsi_flags _gsi_flags { };
|
||||
|
||||
enum { KERNEL_CAP_COUNT_LOG2 = 0 };
|
||||
|
||||
Genode::addr_t irq_sel() const { return _kernel_caps; }
|
||||
addr_t irq_sel() const { return _kernel_caps; }
|
||||
|
||||
public:
|
||||
|
||||
Irq_object();
|
||||
~Irq_object();
|
||||
|
||||
Genode::addr_t msi_address() const { return _msi_addr; }
|
||||
Genode::addr_t msi_value() const { return _msi_data; }
|
||||
addr_t msi_address() const { return _msi_addr; }
|
||||
addr_t msi_value() const { return _msi_data; }
|
||||
|
||||
void sigh(Signal_context_capability cap);
|
||||
void ack_irq();
|
||||
|
||||
void start(unsigned irq, Genode::addr_t);
|
||||
void start(unsigned irq, addr_t, Irq_args const &);
|
||||
};
|
||||
|
||||
#endif /* _CORE__INCLUDE__IRQ_OBJECT_H_ */
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
/* core includes */
|
||||
#include <irq_root.h>
|
||||
#include <irq_args.h>
|
||||
#include <platform.h>
|
||||
|
||||
/* NOVA includes */
|
||||
@@ -27,13 +28,12 @@
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
static bool irq_ctrl(Genode::addr_t irq_sel,
|
||||
Genode::addr_t &msi_addr, Genode::addr_t &msi_data,
|
||||
Genode::addr_t sig_sel, Genode::addr_t virt_addr = 0)
|
||||
static bool irq_ctrl(addr_t irq_sel, addr_t &msi_addr, addr_t &msi_data,
|
||||
addr_t sig_sel, Nova::Gsi_flags flags, addr_t virt_addr)
|
||||
{
|
||||
/* assign IRQ to CPU && request msi data to be used by driver */
|
||||
uint8_t res = Nova::assign_gsi(irq_sel, virt_addr, boot_cpu(),
|
||||
msi_addr, msi_data, sig_sel);
|
||||
msi_addr, msi_data, sig_sel, flags);
|
||||
|
||||
if (res != Nova::NOVA_OK)
|
||||
error("setting up MSI failed - error ", res);
|
||||
@@ -46,30 +46,28 @@ static bool irq_ctrl(Genode::addr_t irq_sel,
|
||||
}
|
||||
|
||||
|
||||
static bool associate(Genode::addr_t irq_sel,
|
||||
Genode::addr_t &msi_addr, Genode::addr_t &msi_data,
|
||||
Genode::Signal_context_capability sig_cap,
|
||||
Genode::addr_t virt_addr = 0)
|
||||
{
|
||||
return irq_ctrl(irq_sel, msi_addr, msi_data, sig_cap.local_name(),
|
||||
virt_addr);
|
||||
}
|
||||
|
||||
|
||||
static void deassociate(Genode::addr_t irq_sel)
|
||||
static bool associate_gsi(addr_t irq_sel, Signal_context_capability sig_cap,
|
||||
Nova::Gsi_flags gsi_flags)
|
||||
{
|
||||
addr_t dummy1 = 0, dummy2 = 0;
|
||||
|
||||
if (!irq_ctrl(irq_sel, dummy1, dummy2, irq_sel))
|
||||
return irq_ctrl(irq_sel, dummy1, dummy2, sig_cap.local_name(), gsi_flags, 0);
|
||||
}
|
||||
|
||||
|
||||
static void deassociate(addr_t irq_sel)
|
||||
{
|
||||
addr_t dummy1 = 0, dummy2 = 0;
|
||||
|
||||
if (!irq_ctrl(irq_sel, dummy1, dummy2, irq_sel, Nova::Gsi_flags(), 0))
|
||||
warning("Irq could not be de-associated");
|
||||
}
|
||||
|
||||
|
||||
static bool msi(Genode::addr_t irq_sel, Genode::addr_t phys_mem,
|
||||
Genode::addr_t &msi_addr, Genode::addr_t &msi_data,
|
||||
Genode::Signal_context_capability sig_cap)
|
||||
static bool associate_msi(addr_t irq_sel, addr_t phys_mem, addr_t &msi_addr,
|
||||
addr_t &msi_data, Signal_context_capability sig_cap)
|
||||
{
|
||||
return platform().region_alloc().alloc_aligned(4096, 12).convert<bool>(
|
||||
return platform().region_alloc().alloc_aligned(4096, 12).convert<bool>(
|
||||
|
||||
[&] (void *virt_ptr) {
|
||||
|
||||
@@ -89,7 +87,7 @@ static bool msi(Genode::addr_t irq_sel, Genode::addr_t phys_mem,
|
||||
}
|
||||
|
||||
/* try to assign MSI to device */
|
||||
bool res = associate(irq_sel, msi_addr, msi_data, sig_cap, virt_addr);
|
||||
bool res = irq_ctrl(irq_sel, msi_addr, msi_data, sig_cap.local_name(), Nova::Gsi_flags(), virt_addr);
|
||||
|
||||
unmap_local(Nova::Mem_crd(virt_addr >> 12, 0, Rights(true, true, true)));
|
||||
platform().region_alloc().free(virt_ptr, 4096);
|
||||
@@ -118,11 +116,12 @@ void Irq_object::sigh(Signal_context_capability cap)
|
||||
return;
|
||||
}
|
||||
|
||||
/* associate GSI or MSI to device belonging to device_phys */
|
||||
bool ok = false;
|
||||
if (_device_phys)
|
||||
ok = msi(irq_sel(), _device_phys, _msi_addr, _msi_data, cap);
|
||||
ok = associate_msi(irq_sel(), _device_phys, _msi_addr, _msi_data, cap);
|
||||
else
|
||||
ok = associate(irq_sel(), _msi_addr, _msi_data, cap);
|
||||
ok = associate_gsi(irq_sel(), cap, _gsi_flags);
|
||||
|
||||
if (!ok) {
|
||||
deassociate(irq_sel());
|
||||
@@ -141,7 +140,7 @@ void Irq_object::ack_irq()
|
||||
}
|
||||
|
||||
|
||||
void Irq_object::start(unsigned irq, Genode::addr_t const device_phys)
|
||||
void Irq_object::start(unsigned irq, addr_t const device_phys, Irq_args const &irq_args)
|
||||
{
|
||||
/* map IRQ SM cap from kernel to core at irq_sel selector */
|
||||
using Nova::Obj_crd;
|
||||
@@ -158,12 +157,29 @@ void Irq_object::start(unsigned irq, Genode::addr_t const device_phys)
|
||||
throw Service_denied();
|
||||
}
|
||||
|
||||
/* initialize GSI IRQ flags */
|
||||
auto gsi_flags = [] (Irq_args const &args) {
|
||||
if (args.trigger() == Irq_session::TRIGGER_UNCHANGED
|
||||
|| args.polarity() == Irq_session::POLARITY_UNCHANGED)
|
||||
return Nova::Gsi_flags();
|
||||
|
||||
if (args.trigger() == Irq_session::TRIGGER_EDGE)
|
||||
return Nova::Gsi_flags(Nova::Gsi_flags::EDGE);
|
||||
|
||||
if (args.polarity() == Irq_session::POLARITY_HIGH)
|
||||
return Nova::Gsi_flags(Nova::Gsi_flags::HIGH);
|
||||
else
|
||||
return Nova::Gsi_flags(Nova::Gsi_flags::LOW);
|
||||
};
|
||||
|
||||
_gsi_flags = gsi_flags(irq_args);
|
||||
|
||||
/* associate GSI or MSI to device belonging to device_phys */
|
||||
bool ok = false;
|
||||
if (device_phys)
|
||||
ok = msi(irq_sel(), device_phys, _msi_addr, _msi_data, _sigh_cap);
|
||||
ok = associate_msi(irq_sel(), device_phys, _msi_addr, _msi_data, _sigh_cap);
|
||||
else
|
||||
ok = associate(irq_sel(), _msi_addr, _msi_data, _sigh_cap);
|
||||
ok = associate_gsi(irq_sel(), _sigh_cap, _gsi_flags);
|
||||
|
||||
if (!ok)
|
||||
throw Service_denied();
|
||||
@@ -212,7 +228,9 @@ Irq_session_component::Irq_session_component(Range_allocator &irq_alloc,
|
||||
:
|
||||
_irq_number(~0U), _irq_alloc(irq_alloc), _irq_object()
|
||||
{
|
||||
long irq_number = Arg_string::find_arg(args, "irq_number").long_value(-1);
|
||||
Irq_args const irq_args(args);
|
||||
|
||||
long irq_number = irq_args.irq_number();
|
||||
long device_phys = Arg_string::find_arg(args, "device_config_phys").long_value(0);
|
||||
if (device_phys) {
|
||||
|
||||
@@ -232,7 +250,7 @@ Irq_session_component::Irq_session_component(Range_allocator &irq_alloc,
|
||||
|
||||
_irq_number = (unsigned)irq_number;
|
||||
|
||||
_irq_object.start(_irq_number, device_phys);
|
||||
_irq_object.start(_irq_number, device_phys, irq_args);
|
||||
}
|
||||
|
||||
|
||||
@@ -241,7 +259,7 @@ Irq_session_component::~Irq_session_component()
|
||||
if (_irq_number == ~0U)
|
||||
return;
|
||||
|
||||
Genode::addr_t free_irq = _irq_number;
|
||||
addr_t free_irq = _irq_number;
|
||||
_irq_alloc.free((void *)free_irq);
|
||||
}
|
||||
|
||||
@@ -252,13 +270,13 @@ void Irq_session_component::ack_irq()
|
||||
}
|
||||
|
||||
|
||||
void Irq_session_component::sigh(Genode::Signal_context_capability cap)
|
||||
void Irq_session_component::sigh(Signal_context_capability cap)
|
||||
{
|
||||
_irq_object.sigh(cap);
|
||||
}
|
||||
|
||||
|
||||
Genode::Irq_session::Info Irq_session_component::info()
|
||||
Irq_session::Info Irq_session_component::info()
|
||||
{
|
||||
if (!_irq_object.msi_address() || !_irq_object.msi_value())
|
||||
return { .type = Info::Type::INVALID, .address = 0, .value = 0 };
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
TARGET = ld-nova
|
||||
LIBS = ld-nova
|
||||
@@ -19,8 +19,7 @@ src/kernel/okl4: src/kernel
|
||||
|
||||
content:
|
||||
for spec in x86_32; do \
|
||||
mv lib/mk/spec/$$spec/ld-okl4.mk lib/mk/spec/$$spec/ld.mk; \
|
||||
mv lib/mk/spec/$$spec/ld-okl4.mk lib/mk/spec/$$spec/ld.mk; \
|
||||
done;
|
||||
sed -i "s/ld-okl4/ld/" src/lib/ld/okl4/target.mk
|
||||
sed -i "s/pit_timer_drv/timer/" src/timer/pit/target.inc
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
2022-05-24 3b2acba4ebd649394e26217802598cf650a4b226
|
||||
2022-10-11 b81b8b94731cda35017a740b0110ff4e8e233e07
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
TARGET = ld-okl4
|
||||
LIBS = ld-okl4
|
||||
@@ -21,6 +21,5 @@ content:
|
||||
for spec in x86_32; do \
|
||||
mv lib/mk/spec/$$spec/ld-pistachio.mk lib/mk/spec/$$spec/ld.mk; \
|
||||
done;
|
||||
sed -i "s/ld-pistachio/ld/" src/lib/ld/pistachio/target.mk
|
||||
sed -i "s/pit_timer_drv/timer/" src/timer/pit/target.inc
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
2022-05-24 ca2c90ebcbaa61ade7373d6ea48a608912cd2629
|
||||
2022-10-11 b522663f9c8c779f255e2a5eb37f98b4301c5446
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
TARGET = ld-pistachio
|
||||
LIBS = ld-pistachio
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user