徐崇智, 邱家浩, 林育丞
QEMU (Quick Emulator) is a free and open-source machine emulator and virtualizer that leverages dynamic binary translation to emulate a computer's processor. It allows operating systems and applications built for one architecture to run on another by translating binary code during runtime.
QEMU offers an extensive range of hardware and device models for virtual mahcines, supporting the emulation of various architectures, including x86, ARM, PowerPC, RISC-V, and more.
Environment : Ubuntu LTS 24.04.1
Avoid unnecessary indentions.
Got it!
NOTE: watch out for hardcoded absolute paths (QEMU and GNU toolchain).
TODO: Bump QEMU versions.
Finish. We update the QEMU version to 9.2.0.
To support the RISC-V architecture required for running the xv6 operating system, we specify --target-list=riscv64-softmmu
when configuring QEMU.
Create a new folder to place the RISC-V toolchain, and install several standard packages are needed to build the toolchain.
The build defaults to targeting RV64imafdc with glibc. Then, simply run the following command:
Always write in English!
We'll remember it!
When I ran make kernel/kernel fs.img
, I encountered this error. It’s worth noting that the path to the tool riscv64-unknown-elf-gcc
is set to a strange location: /home/marko/shit/riscv-gnu-toolchain-from-source/mybuild/bin/riscv64-unknown-elf-gcc
. This seems like a personal path from someone else’s computer.
To resolve the issue, I edited the Makefile
located at /home/neat/riscv_hypervisor/src/guest/xv6-riscv-guest/Makefile
. In this file, I modified the TOOLPREFIX
and QEMU
variables to point to the correct personal location on my system like:
Before we build the hypervisor, we need to run the relevant make commands to generate the required files, such as xv6.bin
, keygrab.bin
, and printer.bin
.
Remember to change Makefile
in /riscv_hypervisor/src/guest/keygrab
CROSS_COMPILE = /usr/local/bin/riscv64-unknown-elf-
When I ran the command make
, I encountered the following error:
This issue indicates that the ctags command is missing on your system. Do the following command to solve:
xv6.bin
has been produced at Build xv6-riscv-guest step.
The refresh.sh
script performs the following actions:
.bin
files in the current directory (guest/imgs)..bin
files starting from the parent directory using find .. -name '*.bin'
..bin
files to the current directory.So, you should now see your imgs folder structured as follows:
Ultimately, we can build up the RISC-V hypervisor using the following command:
When I ran the command make kernel, I encountered the following error:
The issue seems to arise because we are only building one instance of the xv6 OS, but two image files are being referenced at the same time. We are still missing the file fs2.img
.
We went back to Step Build xv6-riscv-guest run make kernel/kernel fs2.img
.
Expected Output (On QEMU 8.0.2):
After successfully booting xv6, we should see the following:
Observed Output (Bumped QEMU to 9.2.0):
Following the QEMU update, the output unexecpectedly changed to:
It's appears that an issue occurred during initialization, preventing the system from booting successfully.
We want to find out where in the program it gets stuck during execution by using GDB.
We discovered that the program gets stuck after executing the w_satp
function within kvminithart()
in the main.c
file.
The purpose of w_satp()
:
In RISC-V, the satp
CSR determines the root node of the current paging mode and the translation mode, in xv6 is Sv39.
satp
format:Once w_satp()
is executed, the CPU immediately switches to using the new Page Table for instruction fetching and data access translation.
Therefore, immediately after w_satp()
, when the CPU fetches the next instruction from memory, it uses the new Page Table for address resolution.
In our case, we observed a crash right after switching to the new page table. To investigate, we attached GDB and examined several key registers and CSRs:
From the RISC-V privileged specification, scause = 0xc
indicates an Instruction Page Fault – the CPU failed to fetch an instruction at stval = 0x80100f56
.
In spec Volume 2:
If stval is written with a nonzero value when an instruction access-fault or page-fault exception occurs on a system with variable-length instructions, then stval will contain the virtual address of the portion of the instruction that caused the fault, while sepc will point to the beginning of the instruction.
This strongly hinted that our newly switched page table disallowed execution at that address.
After observing the Instruction Page Fault and noticing it occurred right after switching to the new page table, we suspected that the CPU considered the instruction's virtual address "non-executable".
Typically, this could be because:
In many RISC-V kernels (like xv6), kernel code pages are given X permission, so scenario(1) is less likely. Hence, scenario (2) the CPU failing to auto-update the A bit became the prime suspect.
To verify this, we inspected two hypervisor-mode configuration registers:
QEMU 8.0.2:
Under QEMU 8.0.2, henvcfg
was 0xa000000000000000
, indicating bit 61 (ADUE) was set, thus the CPU would auto-update A and D bits.
By contrast, on QEMU 9.2.0, we observed:
QEMU 9.2.0:
Meaning bit 61 was not set at reset time.
In QEMU source code target/riscv/cpu.c
, we found the key difference between these two versions.
HADE was renamed to ADUE in QEMU commit <ed67d637>
In 9.2.0, henvcfg
is explicitly zeroed out instead of automatically setting bit 61.
A related in QEMU commit <148189ff> states:
The hypervisor should decide what it wants to enable.
Zero all configuration enable bits on reset.
Hence, we had to explicitly enable ADUE in source file src/core/vm_run.c
:
After modified the code above, we can successfully boot multiple xv6 on new version of QEMU.