Skip to content

Commit

Permalink
Hypervisor extension emulation
Browse files Browse the repository at this point in the history
In the RISC-V architecture, the Hypervisor extension (often referred to as H-extension) introduces a new privilege mode (HS-mode) that provides a virtualized environment. This allows a hypervisor to manage virtual machines, trapping certain privileged instructions and handling them in a way that maintains isolation between the host and guest operating systems. This commit emulates the behavior of this ISA extension in Miralis.
  • Loading branch information
francois141 committed Sep 20, 2024
1 parent 0e5cc95 commit e06a794
Show file tree
Hide file tree
Showing 17 changed files with 833 additions and 40 deletions.
8 changes: 8 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ members = [
"firmware/csr_ops",
"firmware/default",
"firmware/ecall",
"firmware/hypervisor",
"firmware/pmp",
"firmware/breakpoint",
"firmware/mepc",
Expand Down
14 changes: 14 additions & 0 deletions firmware/hypervisor/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "hypervisor"
version = "0.1.0"
edition = "2021"

license = "MIT"

[[bin]]
name = "hypervisor"
path = "main.rs"

[dependencies]
miralis_abi = { path = "../../crates/abi" }
log = { workspace = true }
60 changes: 60 additions & 0 deletions firmware/hypervisor/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#![no_std]
#![no_main]

use core::arch::asm;

use miralis_abi::{setup_binary, success};

setup_binary!(main);

fn main() -> ! {
log::info!("Hello from hypervisor firmware!");

let misa: u64;
unsafe {
// Read the misa CSR into the variable misa
asm!("csrr {}, misa", out(reg) misa);
}

if (misa & (1 << 7)) == 0 {
log::info!("H extension is not available skipping the hypervisor payload");
success();
}

unsafe {
asm!(
// Read the Hypervisor Status Register (hstatus)
"csrr t0, hstatus", // Store hstatus in t0
"csrw hstatus, t0", // Write t0 back into hstatus (just for demonstration)

// Read Hypervisor Exception Delegation Register (hedeleg)
"csrr t1, hedeleg", // Store hedeleg in t1
"csrw hedeleg, t1", // Write t1 back into hedeleg

// Read Hypervisor Interrupt Delegation Register (hideleg)
"csrr t2, hideleg", // Store hideleg in t2
"csrw hideleg, t2", // Write t2 back into hideleg

// Example of hypervisor trap handling (setting the virtual supervisor address)
"csrr t3, htval", // Read htval (Hypervisor Trap Value Register)
"csrw htval, t3", // Write back to htval

// Hypervisor virtual interrupt enable
"csrr t4, hvip", // Read Hypervisor Virtual Interrupt Pending Register
"csrw hvip, t4", // Write back to hvip

// Insert fences for hypervisor virtual memory synchronization
// HFENCE.VVMA vaddr, asid
"li t5, 0", // Load immediate 0 into t7 (representing ASID = 0 for all guests)
"hfence.vvma zero, t5", // Synchronize virtual memory for all guest virtual machines

// HFENCE.GVMA gaddr, gpid
"li t5, 0", // Load immediate 0 into t8 (representing GPID = 0 for all guest physical addresses)
"hfence.gvma zero, t5", // Synchronize virtual memory for the hypervisor

out("t0") _, out("t1") _, out("t2") _, out("t3") _, out("t4") _, out("t5") _,
);
}

success();
}
1 change: 1 addition & 0 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ test:
cargo run -- run --config {{qemu_virt}} --firmware os_ecall
cargo run -- run --config {{qemu_virt}} --firmware vectored_mtvec
cargo run -- run --config {{qemu_virt}} --firmware device
cargo run -- run --config {{qemu_virt}} --firmware hypervisor
cargo run -- run --config {{qemu_virt_release}} --firmware default

# Testing with Miralis as firmware
Expand Down
2 changes: 1 addition & 1 deletion misc/riscv-unknown-miralis.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@
"panic-strategy": "abort",
"disable-redzone": true,
"eh-frame-header": false,
"features": "+m,+a,+c",
"features": "+m,+a,+c,+h",
"executables": true
}
2 changes: 1 addition & 1 deletion misc/riscv-unknown-unprivileged.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@
"panic-strategy": "abort",
"disable-redzone": true,
"eh-frame-header": false,
"features": "+m,+a",
"features": "+m,+a,+h",
"executables": true
}
2 changes: 2 additions & 0 deletions runner/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ pub struct Debug {
#[serde(deny_unknown_fields)]
pub struct VCpu {
pub s_mode: Option<bool>,
pub h_mode: Option<bool>,
pub max_pmp: Option<usize>,
}

Expand Down Expand Up @@ -241,6 +242,7 @@ impl VCpu {
fn build_envs(&self) -> HashMap<String, String> {
let mut envs = EnvVars::new();
envs.insert("MIRALIS_VCPU_S_MODE", &self.s_mode);
envs.insert("MIRALIS_VCPU_H_MODE", &self.h_mode);
envs.insert("MIRALIS_VCPU_MAX_PMP", &self.max_pmp);
envs.envs
}
Expand Down
144 changes: 143 additions & 1 deletion src/arch/metal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,29 @@ impl Architecture for MetalArch {
Csr::Sip => asm_write_csr!("sip"),
Csr::Satp => asm_write_csr!("satp"),
Csr::Scontext => asm_write_csr!("scontext"),
Csr::Hstatus => asm_write_csr!("hstatus"),
Csr::Hedeleg => asm_write_csr!("hedeleg"),
Csr::Hideleg => asm_write_csr!("hideleg"),
Csr::Hvip => asm_write_csr!("hvip"),
Csr::Hip => asm_write_csr!("hip"),
Csr::Hie => asm_write_csr!("hie"),
Csr::Hgeip => {} // Read-only register
Csr::Hgeie => asm_write_csr!("hgeie"),
Csr::Henvcfg => asm_write_csr!("henvcfg"),
Csr::Hcounteren => asm_write_csr!("hcounteren"),
Csr::Htimedelta => asm_write_csr!("htimedelta"),
Csr::Htval => asm_write_csr!("htval"),
Csr::Htinst => asm_write_csr!("htinst"),
Csr::Hgatp => asm_write_csr!("hgatp"),
Csr::Vsstatus => asm_write_csr!("vsstatus"),
Csr::Vsie => asm_write_csr!("vsie"),
Csr::Vstvec => asm_write_csr!("vstvec"),
Csr::Vsscratch => asm_write_csr!("vsscratch"),
Csr::Vsepc => asm_write_csr!("vsepc"),
Csr::Vscause => asm_write_csr!("vscause"),
Csr::Vstval => asm_write_csr!("vstval"),
Csr::Vsip => asm_write_csr!("vsip"),
Csr::Vsatp => asm_write_csr!("vsatp"),
Csr::Unknown => (),
};

Expand Down Expand Up @@ -172,6 +195,29 @@ impl Architecture for MetalArch {
Csr::Sip => asm_read_csr!("sip"),
Csr::Satp => asm_read_csr!("satp"),
Csr::Scontext => asm_read_csr!("scontext"),
Csr::Hstatus => asm_read_csr!("hstatus"),
Csr::Hedeleg => asm_read_csr!("hedeleg"),
Csr::Hideleg => asm_read_csr!("hideleg"),
Csr::Hvip => asm_read_csr!("hvip"),
Csr::Hip => asm_read_csr!("hip"),
Csr::Hie => asm_read_csr!("hie"),
Csr::Hgeip => asm_read_csr!("hgeip"),
Csr::Hgeie => asm_read_csr!("hgeie"),
Csr::Henvcfg => asm_read_csr!("henvcfg"),
Csr::Hcounteren => asm_read_csr!("hcounteren"),
Csr::Htimedelta => asm_read_csr!("htimedelta"),
Csr::Htval => asm_read_csr!("htval"),
Csr::Htinst => asm_read_csr!("htinst"),
Csr::Hgatp => asm_read_csr!("hgatp"),
Csr::Vsstatus => asm_read_csr!("vsstatus"),
Csr::Vsie => asm_read_csr!("vsie"),
Csr::Vstvec => asm_read_csr!("vstvec"),
Csr::Vsscratch => asm_read_csr!("vsscratch"),
Csr::Vsepc => asm_read_csr!("vsepc"),
Csr::Vscause => asm_read_csr!("vscause"),
Csr::Vstval => asm_read_csr!("vstval"),
Csr::Vsip => asm_read_csr!("vsip"),
Csr::Vsatp => asm_read_csr!("vsatp"),
Csr::Unknown => value = 0,
};

Expand Down Expand Up @@ -375,7 +421,7 @@ impl Architecture for MetalArch {
);
}

unsafe fn sfence_vma(vaddr: Option<usize>, asid: Option<usize>) {
unsafe fn sfencevma(vaddr: Option<usize>, asid: Option<usize>) {
match (vaddr, asid) {
(None, None) => asm!("sfence.vma"),
(None, Some(asid)) => {
Expand All @@ -400,6 +446,56 @@ impl Architecture for MetalArch {
}
}

unsafe fn hfencegvma(vaddr: Option<usize>, asid: Option<usize>) {
match (vaddr, asid) {
(None, None) => asm!("hfence.gvma"),
(None, Some(asid)) => {
asm!(
"hfence.gvma x0, {asid}",
asid = in(reg) asid,
)
}
(Some(vaddr), None) => {
asm!(
"hfence.gvma {vaddr}, x0",
vaddr = in(reg) vaddr
)
}
(Some(vaddr), Some(asid)) => {
asm!(
"hfence.gvma {vaddr}, {asid}",
vaddr = in(reg) vaddr,
asid = in(reg) asid
)
}
}
}

unsafe fn hfencevvma(vaddr: Option<usize>, asid: Option<usize>) {
match (vaddr, asid) {
(None, None) => asm!("hfence.vvma"),
(None, Some(asid)) => {
asm!(
"hfence.vvma x0, {asid}",
asid = in(reg) asid,
)
}
(Some(vaddr), None) => {
asm!(
"hfence.vvma {vaddr}, x0",
vaddr = in(reg) vaddr
)
}
(Some(vaddr), Some(asid)) => {
asm!(
"hfence.vvma {vaddr}, {asid}",
vaddr = in(reg) vaddr,
asid = in(reg) asid
)
}
}
}

unsafe fn clear_csr_bits(csr: Csr, bits_mask: usize) {
macro_rules! asm_clear_csr_bits {
($reg:literal) => {
Expand Down Expand Up @@ -461,6 +557,29 @@ impl Architecture for MetalArch {
Csr::Sip => asm_clear_csr_bits!("sip"),
Csr::Satp => asm_clear_csr_bits!("satp"),
Csr::Scontext => asm_clear_csr_bits!("scontext"),
Csr::Hstatus => asm_clear_csr_bits!("hstatus"),
Csr::Hedeleg => asm_clear_csr_bits!("hedeleg"),
Csr::Hideleg => asm_clear_csr_bits!("hideleg"),
Csr::Hvip => asm_clear_csr_bits!("hvip"),
Csr::Hip => asm_clear_csr_bits!("hip"),
Csr::Hie => asm_clear_csr_bits!("hie"),
Csr::Hgeip => {} // Read only register
Csr::Hgeie => asm_clear_csr_bits!("hgeie"),
Csr::Henvcfg => asm_clear_csr_bits!("henvcfg"),
Csr::Hcounteren => asm_clear_csr_bits!("hcounteren"),
Csr::Htimedelta => asm_clear_csr_bits!("htimedelta"),
Csr::Htval => asm_clear_csr_bits!("htval"),
Csr::Htinst => asm_clear_csr_bits!("htinst"),
Csr::Hgatp => asm_clear_csr_bits!("hgatp"),
Csr::Vsstatus => asm_clear_csr_bits!("vsstatus"),
Csr::Vsie => asm_clear_csr_bits!("vsie"),
Csr::Vstvec => asm_clear_csr_bits!("vstvec"),
Csr::Vsscratch => asm_clear_csr_bits!("vsscratch"),
Csr::Vsepc => asm_clear_csr_bits!("vsepc"),
Csr::Vscause => asm_clear_csr_bits!("vscause"),
Csr::Vstval => asm_clear_csr_bits!("vstval"),
Csr::Vsip => asm_clear_csr_bits!("vsip"),
Csr::Vsatp => asm_clear_csr_bits!("vsatp"),
Csr::Unknown => (),
};
}
Expand Down Expand Up @@ -528,6 +647,29 @@ impl Architecture for MetalArch {
Csr::Sip => asm_set_csr_bits!("sip"),
Csr::Satp => asm_set_csr_bits!("satp"),
Csr::Scontext => asm_set_csr_bits!("scontext"),
Csr::Hstatus => asm_set_csr_bits!("hstatus"),
Csr::Hedeleg => asm_set_csr_bits!("hedeleg"),
Csr::Hideleg => asm_set_csr_bits!("hideleg"),
Csr::Hvip => asm_set_csr_bits!("hvip"),
Csr::Hip => asm_set_csr_bits!("hip"),
Csr::Hie => asm_set_csr_bits!("hie"),
Csr::Hgeip => asm_set_csr_bits!("hgeip"),
Csr::Hgeie => asm_set_csr_bits!("hgeie"),
Csr::Henvcfg => asm_set_csr_bits!("henvcfg"),
Csr::Hcounteren => asm_set_csr_bits!("hcounteren"),
Csr::Htimedelta => asm_set_csr_bits!("htimedelta"),
Csr::Htval => asm_set_csr_bits!("htval"),
Csr::Htinst => asm_set_csr_bits!("htinst"),
Csr::Hgatp => asm_set_csr_bits!("hgatp"),
Csr::Vsstatus => asm_set_csr_bits!("vsstatus"),
Csr::Vsie => asm_set_csr_bits!("vsie"),
Csr::Vstvec => asm_set_csr_bits!("vstvec"),
Csr::Vsscratch => asm_set_csr_bits!("vsscratch"),
Csr::Vsepc => asm_set_csr_bits!("vsepc"),
Csr::Vscause => asm_set_csr_bits!("vscause"),
Csr::Vstval => asm_set_csr_bits!("vstval"),
Csr::Vsip => asm_set_csr_bits!("vsip"),
Csr::Vsatp => asm_set_csr_bits!("vsatp"),
Csr::Unknown => (),
};
}
Expand Down
10 changes: 8 additions & 2 deletions src/arch/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ pub trait Architecture {
unsafe fn set_csr_bits(csr: Csr, bits_mask: usize);
unsafe fn set_mpp(mode: Mode);
unsafe fn write_pmp(pmp: &PmpGroup);
unsafe fn sfence_vma(vaddr: Option<usize>, asid: Option<usize>);
unsafe fn sfencevma(vaddr: Option<usize>, asid: Option<usize>);
unsafe fn hfencegvma(vaddr: Option<usize>, asid: Option<usize>);
unsafe fn hfencevvma(vaddr: Option<usize>, asid: Option<usize>);
unsafe fn run_vcpu(ctx: &mut VirtContext);

/// Wait for interrupt
Expand Down Expand Up @@ -188,8 +190,12 @@ pub mod misa {
// In addition, we disable floating points because we encountered some issues with those
// and they will require special handling when context switching from the OS (checking the
// mstatus.FS bits).
let mut disabled = C | D | F | H | Q;
let mut disabled = C | D | F | Q;
// For the rest we look up the configuration
if !Plat::HAS_H_MODE {
disabled |= H;
}

if !Plat::HAS_S_MODE {
disabled |= S;
}
Expand Down
Loading

0 comments on commit e06a794

Please sign in to comment.