Skip to main content

Summer 2024 progress report

· 7 min read

Three months and 110 commits after the open-sourcing of Miralis we have come a long way, and now sounds like a good time for our first ever progress report. This summer we had the chance to host Sofia and Noé at the lab, they both have been doing stellar work and Miralis would be nowhere close to where it is today without them, so kudos to both of them, it has really been a pleasure to work together!

Miralis in a nutshell

Miralis is the first implementation of what we call a virtual firmware monitor, or VFM for short. A VFM is a system that virtualizes the firmware privilege mode, similar to how software-based hypervisors can virtualize the kernel mode. Miralis is written in Rust and targets RISC-V platforms, it can run unmodified de-privileged firmware in U-mode, which we call virtual M-mode or vM-mode for short. The purpose of Miralis is to protect sensitive workloads from potentially untrusted firmware, a thorny problem especially in the era of confidential computing and trusted execution environments.

Summer 2024

Over the summer we focused on two major milestones: full Linux support and porting Miralis to the VisionFive 2 board. While we need to polish out a few rough edges, I think we can say that we delivered on both objectives. As a bonus thanks to the great work of Sofia and Noé we even managed to submit a first paper that got accepted to the KISV 2024 workshop, the final version will be published soon (Yay! 🥳).

Full Linux support

This summer we achieved full Linux support on QEMU, which means that Linux can run seamlessly with a virtualized OpenSBI. When running an Operating system along with a virtualized firmware with Miralis the deployment looks like this:

        ┌─────────────┐ ┌───────────┐
U-mode │ │ │ OpenSBI │ vM-mode
│ Linux │ └───────────┘
S-mode │ │
├─────────────┴─────────────┐
M-mode │ Miralis │
└───────────────────────────┘

An important note is that Miralis only virtualizes the firmware, which means that the OS (both the Linux kernel and user-space applications) executes natively at full speed until it traps to the firmware, at which point Miralis triggers a world switch toward the virtualized M-mode. This is similar to software-based virtual machine monitors that pre-dates hardware virtualization extensions, such as the original VMWare Workstation. Those VMMs run user-space applications natively but switch to an efficient virtualized mode for kernel code execution. Miralis is heavily inspired by those early designs, but virtualizes the firmware rather than the kernel.

The interesting bits here is that neither OpenSBI nor Linux need to be modified to run with Miralis. This works without re-compilation because both of them are position independent, but even for non-position independent software we would not need to change the source code (beside the base address if hardcoded) because RISC-V has the nice property of being a virtualizable architecture. This is very important for us, as we want to provide full backward-compatibility with Miralis.

The main challenge in getting Linux to work properly is interrupt virtualization. While we mostly had to handle M-mode timer interrupts before, Linux virtualization requires proper handling of all kinds of interrupts. The trickiest one was the supervisor external interrupt (SEI), which has a different semantic than other interrupt bits: there is not one, but two SEI bits, one writable by the hardware, the other by the software, but software can only read the binary OR of those two bits.

We now reached a point where Linux runs smoothly on top of Miralis on the QEMU platform. We tested with both a small BusyBox-based user-space (which we now run against each pull requests in the CI) and a full ubuntu distribution. Running Linux on Miralis is now as simple as executing just run linux-shell.

Porting Miralis to the VisionFive 2

Our second milestone is porting Miralis to the VisionFive 2 board. The VisionFive 2 features a JH7110 SoC, with a quad-core SiFive U74 (RV64GC) processor plus one smaller SiFive S7 (RV64IMAC) monitor core. The feature set of the VisionFive 2 is quite different from what QEMU exposes, which rose quite a few challenges.

The first challenge with the VisionFive 2 was multi-core support. Not only is the JH7110 a multi-core platform, but it is also heterogeneous. The smaller SiFive S7 core does not support S-mode, for instance, and has some more restrictions we had to take into account. We added proper multi-core support to Miralis, which required carefully re-designing the initialization sequence and boot assembly to setup disjoint stacks and initialize global resources such as zeroing-out the .bss section.

Another interesting part is how to insert Miralis in the boot chain. When the VisionFive 2 boot it starts executing a small read-only bootstrap code, the reset vector, which then jumps to the first writable executable memory. The vendor-provided software boots like this: reset-vector -> u-boot SPL -> OpenSBI -> u-boot proper -> Linux. The first run-time resident firmware in the boot chain is OpenSBI, the role of u-boot SPL is mostly to initialize DRAM and transfer control to the next stage but it does not stay active at run time. We decided to insert Miralis after u-boot SPL, and then transfer control to OpenSBI in the virtual M-mode. That way Miralis is the only M-mode software present at runtime, as OpenSBI runs deprivileged.

Finally the VisionFive 2 has a more limited set of hardware features compared to QEMU. One major difference, for instance, is the lack of support for misaligned reads and writes. On the VisionFive 2 misaligned reads and writes from the kernel are emulated by OpenSBI by changing the memory access privilege (the using the MPRV bit in mstatus). The MPRV bit allow M-mode software to read and write memory as if it was executing with S or U-mode privilege (but instruction fetches keep using M-mode privileges), which simplifies accessing OS memory by removing the need for software page table walk and permission checks. However the MPRV bit can't be enabled while running virtualized firmware with Miralis as the firmware already executes in U-mode. We emulated the MPRV bit in Miralis by forcing traps from the firmware to Miralis during reads and writes when the virtual MPRV is set to 1, which enables Miralis to emulate the read or write using S or U-mode privileges using the physical MPRV bit. There are other differences between QEMU and the VisionFive 2, in particular around devices, but we already managed to close most of the gaps.

What is next

With the start of the new academic semester François and Frédéric are joining forces to work on Miralis. Our objectives for the next few months is to demonstrate how to enforce security policies with Miralis. By default Miralis grants (almost) full access to the machine to the virtualized firmware, therefore it does not provide any security guarantee per se. There can be many desirable isolation policies, therefore Miralis does not try to provide the 'one true' security policy, but our goal is rather to enable the implementation of many custom policies. For instance it might be desirable to isolate confidential VMs or enclaves from the firmware and host OS, or even the whole host OS form the firmware.

Overall I am very enthusiastic about Miralis, the project has already gone a long way and built up a very good momentum. There is still a lot on our plate to achieve our vision of securing the lowest layer of the software stack, but I am confident we will get there.