arceos-guestvdev 0.1.1

ArceOS Guest Virtual Device (Hypervisor): runs a guest OS with virtual device support (timer, console, NPF passthrough) — RISC-V H-extension, ARM AArch64 EL2, and AMD SVM
arceos-guestvdev-0.1.1 is not a library.

arceos-guestvdev

A standalone hypervisor application running on ArceOS unikernel, with all dependencies sourced from crates.io. Implements guest virtual device support with timer virtualization, console I/O forwarding, and nested page fault (NPF) passthrough across three architectures.

This crate is derived from the h_3_0 tutorial crate in the ArceOS ecosystem, extending it to support multiple processor architectures. The h_3_0 crate runs u_6_0 (a preemptive multi-tasking demo) as the guest OS.

What It Does

The hypervisor (arceos-guestvdev) performs the following:

  1. Creates a guest address space with second-stage/nested page tables
  2. Pre-allocates guest RAM (16 MB on RISC-V, 2 MB on x86_64) to minimize NPF exits
  3. Loads a guest kernel (gkernel) from a VirtIO block device (FAT32 filesystem)
  4. Virtualizes timer device for guest preemptive scheduling (RISC-V: SBI SetTimer + hvip injection)
  5. Forwards console I/O via SBI/SVC/VMMCALL hypercalls
  6. Handles nested page faults with MMIO passthrough mapping
  7. Demonstrates the h_3_0 control flow: loop → guest entry → VM exit → handle → repeat

The guest kernel (gkernel) behavior varies by architecture:

  • RISC-V 64: Full ArceOS multi-tasking demo (u_6_0 style) with CFS scheduler and preemptive scheduling. Two worker threads communicate via a shared queue.
  • AArch64: Bare-metal EL0 program that tests virtual device interaction (console I/O via SVC, PFlash read via NPF).
  • x86_64: Bare-metal long-mode program that tests virtual device interaction (console I/O via VMMCALL, PFlash read via NPF).

Architecture Support

Architecture Virtualization Guest Mode Virtual Devices Shutdown Mechanism
RISC-V 64 H-extension (hgatp) VS-mode Timer (SBI), Console (SBI PutChar), PFlash (NPF) SBI ecall (Reset)
AArch64 EL1→EL0 (TTBR0) EL0 Console (SVC), PFlash (NPF) SVC hypercall (exit)
x86_64 AMD SVM (NPT) Long mode Console (VMMCALL), PFlash (NPF) vmmcall (PSCI)

Note on RISC-V Timer Virtualization: The hypervisor intercepts SBI SetTimer calls, forwards them to the host SBI (OpenSBI), and injects virtual supervisor timer interrupts to the guest via hvip. This enables the guest's CFS scheduler to perform preemptive context switching between worker threads.

Note on AArch64: Because the ArceOS platform crate drops from EL2 to EL1 during boot, the hypervisor runs at EL1 and the guest at EL0. Guest page tables are managed via TTBR0_EL1, and data aborts from EL0 serve as the equivalent of nested page faults.

Note on x86_64 AMD SVM: The hypervisor uses VMRUN/VMEXIT with hardware Nested Page Tables (NPT). Guest GPRs (RCX–R15) are saved/restored by software via an SvmGuestGprs structure. PFlash is emulated in software.

Control Flow (h_3_0 Compatible)

Hypervisor starts
    │
    ├─ Create guest address space (AddrSpace)
    ├─ Pre-allocate guest RAM
    ├─ Load guest binary from /sbin/gkernel
    ├─ Setup vCPU context
    │
    └─ VM Run Loop ──────────────────────┐
         │                               │
         ├─ Enter guest (vmrun/eret)     │
         │                               │
         ├─ SBI call (PutChar/SetTimer) │
         │   └─ Forward to host SBI ────┘
         │                               │
         ├─ Timer interrupt              │
         │   └─ Inject to guest (hvip) ──┘
         │                               │
         ├─ Guest accesses unmapped addr │
         │   └─ NPF / Page Fault exit   │
         │       └─ Map the page ────────┘
         │
         ├─ Guest issues shutdown call
         │   └─ Shutdown exit
         │       └─ Break loop
         │
         └─ "Hypervisor ok!"

Comparison with Related Crates

Crate Role Description
arceos-guestvdev (this) Hypervisor Runs guest with virtual device support (like h_3_0)
arceos-guestaspace Hypervisor Runs guest with NPF handling (like h_2_0)
arceos-guestmode Hypervisor Runs minimal guest, single VM exit (like h_1_0)

Prerequisites

  • Rust nightly toolchain (edition 2024)

    rustup install nightly
    rustup default nightly
    
  • Bare-metal targets

    rustup target add riscv64gc-unknown-none-elf
    rustup target add aarch64-unknown-none-softfloat
    rustup target add x86_64-unknown-none
    
  • QEMU (with virtualization support)

    # Ubuntu/Debian
    sudo apt install qemu-system-riscv64 qemu-system-aarch64 qemu-system-x86
    
    # macOS (Homebrew)
    brew install qemu
    
  • rust-objcopy (from cargo-binutils)

    cargo install cargo-binutils
    rustup component add llvm-tools
    

Quick Start

# Install cargo-clone
cargo install cargo-clone

# Get source code from crates.io
cargo clone arceos-guestvdev
cd arceos-guestvdev

# Build and run on RISC-V 64 (default)
cargo xtask run

# Build and run on other architectures
cargo xtask run --arch aarch64
cargo xtask run --arch x86_64

# Build only (no QEMU)
cargo xtask build --arch riscv64

Expected Output

RISC-V 64

Starting virtualization...
Pre-allocating 16 MB guest RAM at 0x80000000...
VM created success, loading images...
app: /sbin/gkernel
Loaded XXXXX bytes from /sbin/gkernel
bsp_entry: 0x80200000; ept: 0x...
Entering VM run loop...
       d8888                            .d88888b.   .d8888b.   (guest ArceOS banner)
       ...
Multi-task(Preemptible) is starting ...
worker1 ... ThreadId(2)
worker1 [0]
worker2 ... ThreadId(3)
worker2: nothing to do!
worker1 [1]
...
worker2 [256]
worker2 ok!
Wait for workers to exit ...
worker1 ok!
Multi-task(Preemptible) ok!
Guest: SBI SRST shutdown
Shutdown vm normally!

AArch64

Starting virtualization...
app: /sbin/gkernel
...
Entering VM run loop...
       d8888                            .d88888b.   .d8888b.   (guest ArceOS banner)
       ...
arch = aarch64
platform = aarch64-qemu-virt
smp = 1

Virtual Device (vdev) Test
Reading PFlash at physical address 0x04000000...
Try to access pflash dev region [0x04000000], got 0x646c6670
Got pflash magic: pfld
Shutdown vm normally!
Hypervisor ok!

x86_64 (AMD SVM)

Starting virtualization...
Pre-allocating 2048 KB guest RAM at GPA 0x0...
VM created success, loading images...
app: /sbin/gkernel
Loaded XXXX bytes from /sbin/gkernel
Entering VM run loop...

       d8888                            .d88888b.   .d8888b.
       ...

arch = x86_64
platform = x86-pc
smp = 1

Virtual Device (vdev) Test
Reading PFlash at physical address 0xFFC00000...
Try to access pflash dev region [0xFFC00000], got 0x646c6670
Got pflash magic: pfld
Shutdown vm normally!
Hypervisor ok!

Project Structure

app-guestvdev/
├── .cargo/
│   └── config.toml            # cargo xtask alias & AX_CONFIG_PATH
├── payload/
│   └── gkernel/               # Guest kernel payload
│       ├── Cargo.toml          #   riscv64: ArceOS multitask; others: bare-metal
│       └── src/main.rs         #   Architecture-specific guest code
├── xtask/
│   └── src/main.rs            # Build/run tool (disk image, pflash, QEMU)
├── configs/
│   ├── riscv64.toml           # Platform config for riscv64-qemu-virt
│   ├── aarch64.toml           # Platform config for aarch64-qemu-virt
│   └── x86_64.toml            # Platform config for x86-pc
├── src/
│   ├── main.rs                # Hypervisor entry: h_3_0 style VM exit handling
│   ├── loader.rs              # Guest binary loader (FAT32 → address space)
│   ├── vcpu.rs                # RISC-V vCPU context (registers, guest.S)
│   ├── guest.S                # RISC-V guest entry/exit assembly
│   ├── regs.rs                # RISC-V general-purpose registers
│   ├── csrs.rs                # RISC-V hypervisor CSR definitions
│   ├── sbi/                   # SBI message parsing (base, reset, fence, ...)
│   ├── aarch64/               # AArch64 EL1→EL0 vCPU, guest.S, SVC handling
│   └── x86_64/                # AMD SVM: VMCB, GPR save/restore, vmrun assembly
├── build.rs                   # Linker script auto-detection
├── Cargo.toml
├── rust-toolchain.toml
└── README.md

How It Works

cargo xtask run --arch <ARCH>

  1. Copies configs/<ARCH>.toml.axconfig.toml
  2. Builds the guest payload (gkernel) for the target architecture
  3. Creates a 64MB FAT32 disk image with /sbin/gkernel
  4. For riscv64/aarch64: creates a pflash image with "pfld" magic at offset 0
  5. Builds the hypervisor kernel with --features axstd
  6. Launches QEMU with VirtIO block device and pflash

VM Exit Handling

Architecture NPF Exit SBI/Hypercall Exit Timer Handling
RISC-V 64 scause = 20/21/23 scause = 10 (VSupervisorEnvCall) SetTimer → hvip injection
AArch64 ESR EC = 0x24 (Data Abort) ESR EC = 0x15 (SVC) N/A (bare-metal guest)
x86_64 SVM VMEXIT 0x400 (NPF) VMEXIT 0x81 (VMMCALL) N/A (bare-metal guest)

Key Dependencies

Crate Role
axstd ArceOS standard library (no_std replacement)
axhal Hardware abstraction layer (paging, traps)
axmm Memory management (address spaces, page tables)
axfs Filesystem access (FAT32 disk image)
riscv RISC-V register access (riscv64 only)
sbi-spec / sbi-rt SBI specification and runtime (riscv64 only)

License

GPL-3.0-or-later OR Apache-2.0 OR MulanPSL-2.0