diff --git a/Cargo.toml b/Cargo.toml index 1b0f53b..c1e8a3d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,3 +22,9 @@ memory_addr = "0.2.0" axaddrspace = { git = "https://github.com/arceos-hypervisor/axaddrspace.git" } axvcpu = { git = "https://github.com/arceos-hypervisor/axvcpu.git" } + +kspin = "0.1" +lazyinit = "0.2" +timer_list = "0.1.0" +handler_table = "0.1" +percpu = { version = "0.1.4", features = ["arm-el2"] } \ No newline at end of file diff --git a/README.md b/README.md index 0cec464..f0b9097 100644 --- a/README.md +++ b/README.md @@ -2,4 +2,4 @@ [![CI](https://github.com/arceos-hypervisor/riscv_vcpu/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/arceos-hypervisor/riscv_vcpu/actions/workflows/ci.yml) -Definition of the vCPU structure and virtualization-related interface support for the AArch64 architecture. \ No newline at end of file +Definition of the vCPU structure and virtualization-related interface support for the riscv64 architecture. \ No newline at end of file diff --git a/src/consts.rs b/src/consts.rs new file mode 100644 index 0000000..e58e9d9 --- /dev/null +++ b/src/consts.rs @@ -0,0 +1,50 @@ +pub mod traps { + pub mod interrupt { + pub const VIRTUAL_SUPERVISOR_SOFT: usize = 1 << 2; + pub const VIRTUAL_SUPERVISOR_TIMER: usize = 1 << 6; + pub const VIRTUAL_SUPERVISOR_EXTERNAL: usize = 1 << 10; + } + + pub mod irq { + /// `Interrupt` bit in `scause` + pub const INTC_IRQ_BASE: usize = 1 << (usize::BITS - 1); + + /// Supervisor software interrupt in `scause` + #[allow(unused)] + pub const S_SOFT: usize = INTC_IRQ_BASE + 1; + + /// Supervisor timer interrupt in `scause` + pub const S_TIMER: usize = INTC_IRQ_BASE + 5; + + /// Supervisor external interrupt in `scause` + pub const S_EXT: usize = INTC_IRQ_BASE + 9; + + /// The maximum number of IRQs. + pub const MAX_IRQ_COUNT: usize = 1024; + + /// The timer IRQ number (supervisor timer interrupt in `scause`). + pub const TIMER_IRQ_NUM: usize = S_TIMER; + } + + pub mod exception { + pub const INST_ADDR_MISALIGN: usize = 1 << 0; + pub const ILLEGAL_INST: usize = 1 << 2; + pub const BREAKPOINT: usize = 1 << 3; + pub const ENV_CALL_FROM_U_OR_VU: usize = 1 << 8; + pub const INST_PAGE_FAULT: usize = 1 << 12; + pub const LOAD_PAGE_FAULT: usize = 1 << 13; + pub const STORE_PAGE_FAULT: usize = 1 << 15; + } +} + +pub mod timers { + pub const TICKS_PER_SEC: u64 = 100; + pub const NANOS_PER_SEC: u64 = 1_000_000_000; + pub const PERIODIC_INTERVAL_NANOS: u64 = NANOS_PER_SEC / TICKS_PER_SEC; + pub const TIMER_FREQUENCY: u64 = 10_000_000; + pub const NANOS_PER_TICK: u64 = NANOS_PER_SEC / TIMER_FREQUENCY; +} + +pub mod stack { + pub const EXCEPTION_STACK_SIZE: usize = 8192; +} diff --git a/src/csrs.rs b/src/csrs.rs deleted file mode 100644 index d42b579..0000000 --- a/src/csrs.rs +++ /dev/null @@ -1,310 +0,0 @@ -use defs::*; -use tock_registers::interfaces::{Readable, Writeable}; -use tock_registers::RegisterLongName; - -/// Define each registers of hypervisor using. -pub struct CSR { - pub sie: ReadWriteCsr, - pub hstatus: ReadWriteCsr, - pub hedeleg: ReadWriteCsr, - pub hideleg: ReadWriteCsr, - pub hcounteren: ReadWriteCsr, - pub hvip: ReadWriteCsr, -} - -#[allow(clippy::identity_op, clippy::erasing_op)] -pub const CSR: &CSR = &CSR { - sie: ReadWriteCsr::new(), - hstatus: ReadWriteCsr::new(), - hedeleg: ReadWriteCsr::new(), - hideleg: ReadWriteCsr::new(), - hcounteren: ReadWriteCsr::new(), - hvip: ReadWriteCsr::new(), -}; - -/// Trait defining the possible operations on a RISC-V CSR. -pub trait RiscvCsrTrait { - type R: RegisterLongName; - /// Reads the value of the CSR. - fn get_value(&self) -> usize; - - /// Writes the value of the CSR. - fn write_value(&self, value: usize); - - /// Atomicllt swaps the value of CSRs. - fn atomic_replace(&self, value: usize) -> usize; - - /// Atomically read a CSR and set bits specified in a bitmask - fn read_and_set_bits(&self, bitmasks: usize) -> usize; - - /// Atomically read a CSR and set bits specified in a bitmask - fn read_and_clear_bits(&self, bitmasks: usize) -> usize; -} - -/// Read/Write register. -pub struct ReadWriteCsr { - associated_register: core::marker::PhantomData, -} - -impl ReadWriteCsr { - pub const fn new() -> Self { - Self { - associated_register: core::marker::PhantomData, - } - } -} - -impl RiscvCsrTrait for ReadWriteCsr { - type R = R; - - fn get_value(&self) -> usize { - let r: usize; - unsafe { - core::arch::asm!("csrr {rd}, {csr}", rd = out(reg) r, csr = const V); - } - r - } - - fn write_value(&self, value: usize) { - unsafe { - core::arch::asm!("csrw {csr}, {rs}", csr = const V, rs = in(reg) value); - } - } - - fn atomic_replace(&self, value: usize) -> usize { - let r: usize; - unsafe { - core::arch::asm!("csrrw {rd}, {csr}, {rs}", rd = out(reg) r, csr = const V, rs = in(reg) value); - } - r - } - - fn read_and_set_bits(&self, bitmask: usize) -> usize { - let r: usize; - unsafe { - core::arch::asm!("csrrs {rd}, {csr}, {rs}", rd = out(reg) r, csr = const V, rs = in(reg) bitmask); - } - r - } - - fn read_and_clear_bits(&self, bitmask: usize) -> usize { - let r: usize; - unsafe { - core::arch::asm!("csrrc {rd}, {csr}, {rs}", rd = out(reg) r, csr = const V, rs = in(reg) bitmask); - } - r - } -} - -// The Readable and Writeable traits aren't object-safe so unfortunately we can't implement them -// for RiscvCsrInterface. -impl Readable for ReadWriteCsr { - type T = usize; - type R = R; - - fn get(&self) -> usize { - self.get_value() - } -} - -impl Writeable for ReadWriteCsr { - type T = usize; - type R = R; - - fn set(&self, val_to_set: usize) { - self.write_value(val_to_set); - } -} - -pub mod defs { - use tock_registers::register_bitfields; - pub const CSR_SSTATUS: u16 = 0x100; - pub const CSR_SEDELEG: u16 = 0x102; - pub const CSR_SIDELEG: u16 = 0x103; - pub const CSR_SIE: u16 = 0x104; - pub const CSR_STVEC: u16 = 0x105; - pub const CSR_SCOUNTEREN: u16 = 0x106; - pub const CSR_SENVCFG: u16 = 0x10a; - pub const CSR_SSCRATCH: u16 = 0x140; - pub const CSR_SEPC: u16 = 0x141; - pub const CSR_SCAUSE: u16 = 0x142; - pub const CSR_STVAL: u16 = 0x143; - pub const CSR_SIP: u16 = 0x144; - pub const CSR_STIMECMP: u16 = 0x14d; - pub const CSR_SISELECT: u16 = 0x150; - pub const CSR_SIREG: u16 = 0x151; - pub const CSR_STOPEI: u16 = 0x15c; - pub const CSR_SATP: u16 = 0x180; - pub const CSR_STOPI: u16 = 0xdb0; - pub const CSR_SCONTEXT: u16 = 0x5a8; - pub const CSR_VSSTATUS: u16 = 0x200; - pub const CSR_VSIE: u16 = 0x204; - pub const CSR_VSTVEC: u16 = 0x205; - pub const CSR_VSSCRATCH: u16 = 0x240; - pub const CSR_VSEPC: u16 = 0x241; - pub const CSR_VSCAUSE: u16 = 0x242; - pub const CSR_VSTVAL: u16 = 0x243; - pub const CSR_VSIP: u16 = 0x244; - pub const CSR_VSTIMECMP: u16 = 0x24d; - pub const CSR_VSISELECT: u16 = 0x250; - pub const CSR_VSIREG: u16 = 0x251; - pub const CSR_VSTOPEI: u16 = 0x25c; - pub const CSR_VSATP: u16 = 0x280; - pub const CSR_VSTOPI: u16 = 0xeb0; - pub const CSR_HSTATUS: u16 = 0x600; - pub const CSR_HEDELEG: u16 = 0x602; - pub const CSR_HIDELEG: u16 = 0x603; - pub const CSR_HIE: u16 = 0x604; - pub const CSR_HTIMEDELTA: u16 = 0x605; - pub const CSR_HCOUNTEREN: u16 = 0x606; - pub const CSR_HGEIE: u16 = 0x607; - pub const CSR_HVICTL: u16 = 0x609; - pub const CSR_HENVCFG: u16 = 0x60a; - pub const CSR_HTVAL: u16 = 0x643; - pub const CSR_HIP: u16 = 0x644; - pub const CSR_HVIP: u16 = 0x645; - pub const CSR_HTINST: u16 = 0x64a; - pub const CSR_HGATP: u16 = 0x680; - pub const CSR_HCONTEXT: u16 = 0x6a8; - pub const CSR_HGEIP: u16 = 0xe12; - - // Hypervisor exception delegation register. - register_bitfields![usize, - pub hedeleg [ - instr_misaligned OFFSET(0) NUMBITS(1) [], - instr_fault OFFSET(1) NUMBITS(1) [], - illegal_instr OFFSET(2) NUMBITS(1) [], - breakpoint OFFSET(3) NUMBITS(1) [], - load_misaligned OFFSET(4) NUMBITS(1) [], - load_fault OFFSET(5) NUMBITS(1) [], - store_misaligned OFFSET(6) NUMBITS(1) [], - store_fault OFFSET(7) NUMBITS(1) [], - u_ecall OFFSET(8) NUMBITS(1) [], - instr_page_fault OFFSET(12) NUMBITS(1) [], - load_page_fault OFFSET(13) NUMBITS(1) [], - store_page_fault OFFSET(15) NUMBITS(1) [], - ] - ]; - - // Supervisor interrupt enable register. - register_bitfields![usize, - pub sie [ - ssoft OFFSET(1) NUMBITS(1) [], - stimer OFFSET(5) NUMBITS(1) [], - sext OFFSET(9) NUMBITS(1) [], - ] - ]; - - // Hypervisor status register. - register_bitfields![usize, - pub hstatus [ - // VS mode endianness control. - vsbe OFFSET(6) NUMBITS(1) [], - // A guest virtual address was written to stval as a result of the trap. - gva OFFSET(6) NUMBITS(1) [], - // Virtualization mode at time of trap. - spv OFFSET(7) NUMBITS(1) [ - User = 0, - Supervisor = 1, - ], - // Privilege level the virtual hart was executing before entering HS-mode. - spvp OFFSET(8) NUMBITS(1) [ - User = 0, - Supervisor = 1, - ], - // Allow hypervisor instructions in U-mode. - hu OFFSET(9) NUMBITS(1) [], - // Selects the guest external interrupt source for VS external interrupts. - vgein OFFSET(12) NUMBITS(6) [], - // Trap on SFENCE, SINVAL, or changes to vsatp. - vtvm OFFSET(20) NUMBITS(1) [], - // Trap on WFI timeout. - vtw OFFSET(21) NUMBITS(1) [], - // Trap SRET instruction. - vtsr OFFSET(22) NUMBITS(1) [], - // Native base integer ISA width for VS-mode. - vsxl OFFSET(32) NUMBITS(2) [ - Xlen32 = 1, - Xlen64 = 2, - ], - ] - ]; - - // Hypervisor interrupt delegation register. - register_bitfields![usize, - pub hideleg [ - vssoft OFFSET(2) NUMBITS(1) [], - vstimer OFFSET(6) NUMBITS(1) [], - vsext OFFSET(10) NUMBITS(1) [], - ] - ]; - - // Hypervisor interrupt enable register. - register_bitfields![usize, - pub hie [ - vssoft OFFSET(2) NUMBITS(1) [], - vstimer OFFSET(6) NUMBITS(1) [], - vsext OFFSET(10) NUMBITS(1) [], - sgext OFFSET(12) NUMBITS(1) [], - ] - ]; - - // VS-mode counter availability control. - register_bitfields![usize, - pub hcounteren [ - cycle OFFSET(0) NUMBITS(1) [], - time OFFSET(1) NUMBITS(1) [], - instret OFFSET(2) NUMBITS(1) [], - hpm OFFSET(3) NUMBITS(29) [], - ] - ]; - - // Hypervisor virtual interrupt pending. - register_bitfields![usize, - pub hvip [ - vssoft OFFSET(2) NUMBITS(1) [], - vstimer OFFSET(6) NUMBITS(1) [], - vsext OFFSET(10) NUMBITS(1) [], - ] - ]; -} - -pub mod traps { - pub mod interrupt { - pub const USER_SOFT: usize = 1 << 0; - pub const SUPERVISOR_SOFT: usize = 1 << 1; - pub const VIRTUAL_SUPERVISOR_SOFT: usize = 1 << 2; - pub const MACHINE_SOFT: usize = 1 << 3; - pub const USER_TIMER: usize = 1 << 4; - pub const SUPERVISOR_TIMER: usize = 1 << 5; - pub const VIRTUAL_SUPERVISOR_TIMER: usize = 1 << 6; - pub const MACHINE_TIMER: usize = 1 << 7; - pub const USER_EXTERNAL: usize = 1 << 8; - pub const SUPERVISOR_EXTERNAL: usize = 1 << 9; - pub const VIRTUAL_SUPERVISOR_EXTERNAL: usize = 1 << 10; - pub const MACHINEL_EXTERNAL: usize = 1 << 11; - pub const SUPERVISOR_GUEST_EXTERNEL: usize = 1 << 12; - } - - pub mod exception { - pub const INST_ADDR_MISALIGN: usize = 1 << 0; - pub const INST_ACCESSS_FAULT: usize = 1 << 1; - pub const ILLEGAL_INST: usize = 1 << 2; - pub const BREAKPOINT: usize = 1 << 3; - pub const LOAD_ADDR_MISALIGNED: usize = 1 << 4; - pub const LOAD_ACCESS_FAULT: usize = 1 << 5; - pub const STORE_ADDR_MISALIGNED: usize = 1 << 6; - pub const STORE_ACCESS_FAULT: usize = 1 << 7; - pub const ENV_CALL_FROM_U_OR_VU: usize = 1 << 8; - pub const ENV_CALL_FROM_HS: usize = 1 << 9; - pub const ENV_CALL_FROM_VS: usize = 1 << 10; - pub const ENV_CALL_FROM_M: usize = 1 << 11; - pub const INST_PAGE_FAULT: usize = 1 << 12; - pub const LOAD_PAGE_FAULT: usize = 1 << 13; - pub const STORE_PAGE_FAULT: usize = 1 << 15; - pub const INST_GUEST_PAGE_FAULT: usize = 1 << 20; - pub const LOAD_GUEST_PAGE_FAULT: usize = 1 << 21; - pub const VIRTUAL_INST: usize = 1 << 22; - pub const STORE_GUEST_PAGE_FAULT: usize = 1 << 23; - } -} diff --git a/src/detect.rs b/src/detect.rs index 8322f6a..2849b54 100644 --- a/src/detect.rs +++ b/src/detect.rs @@ -3,7 +3,7 @@ //! First, it disables all S-level interrupts. Remaining traps in RISC-V core //! are all exceptions. //! Then, it filters out illegal instruction from exceptions. -//! ref: https://github.com/luojia65/zihai/blob/main/zihai/src/detect.rs +//! ref: use core::arch::asm; use riscv::register::{ @@ -12,9 +12,9 @@ use riscv::register::{ stvec::{self, Stvec, TrapMode}, }; -// Detect if hypervisor extension exists on current hart environment -// -// This function tries to read hgatp and returns false if the read operation failed. +/// Detect if hypervisor extension exists on current hart environment +/// +/// This function tries to read hgatp and returns false if the read operation failed. pub fn detect_h_extension() -> bool { // run detection by trap on csrr instruction. let ans = with_detect_trap(0, || unsafe { diff --git a/src/guest.S b/src/guest.S deleted file mode 100644 index aa341db..0000000 --- a/src/guest.S +++ /dev/null @@ -1,183 +0,0 @@ - -/// Enter the guest given in `VmCpuRegisters` from `a0` -.global _run_guest -_run_guest: - /* Save hypervisor state */ - - /* Save hypervisor GPRs (except T0-T6 and a0, which is GuestInfo and stashed in sscratch) */ - sd ra, ({hyp_ra})(a0) - sd gp, ({hyp_gp})(a0) - sd tp, ({hyp_tp})(a0) - sd s0, ({hyp_s0})(a0) - sd s1, ({hyp_s1})(a0) - sd a1, ({hyp_a1})(a0) - sd a2, ({hyp_a2})(a0) - sd a3, ({hyp_a3})(a0) - sd a4, ({hyp_a4})(a0) - sd a5, ({hyp_a5})(a0) - sd a6, ({hyp_a6})(a0) - sd a7, ({hyp_a7})(a0) - sd s2, ({hyp_s2})(a0) - sd s3, ({hyp_s3})(a0) - sd s4, ({hyp_s4})(a0) - sd s5, ({hyp_s5})(a0) - sd s6, ({hyp_s6})(a0) - sd s7, ({hyp_s7})(a0) - sd s8, ({hyp_s8})(a0) - sd s9, ({hyp_s9})(a0) - sd s10, ({hyp_s10})(a0) - sd s11, ({hyp_s11})(a0) - sd sp, ({hyp_sp})(a0) - - /* Swap in guest CSRs. */ - ld t1, ({guest_sstatus})(a0) - csrrw t1, sstatus, t1 - sd t1, ({hyp_sstatus})(a0) - - ld t1, ({guest_hstatus})(a0) - csrrw t1, hstatus, t1 - sd t1, ({hyp_hstatus})(a0) - - ld t1, ({guest_scounteren})(a0) - csrrw t1, scounteren, t1 - sd t1, ({hyp_scounteren})(a0) - - ld t1, ({guest_sepc})(a0) - csrw sepc, t1 - - /* Set stvec so that hypervisor resumes after the sret when the guest exits. */ - la t1, _guest_exit - csrrw t1, stvec, t1 - sd t1, ({hyp_stvec})(a0) - - /* Save sscratch and replace with pointer to GuestInfo. */ - csrrw t1, sscratch, a0 - sd t1, ({hyp_sscratch})(a0) - - /* Restore the gprs from this GuestInfo */ - ld ra, ({guest_ra})(a0) - ld gp, ({guest_gp})(a0) - ld tp, ({guest_tp})(a0) - ld s0, ({guest_s0})(a0) - ld s1, ({guest_s1})(a0) - ld a1, ({guest_a1})(a0) - ld a2, ({guest_a2})(a0) - ld a3, ({guest_a3})(a0) - ld a4, ({guest_a4})(a0) - ld a5, ({guest_a5})(a0) - ld a6, ({guest_a6})(a0) - ld a7, ({guest_a7})(a0) - ld s2, ({guest_s2})(a0) - ld s3, ({guest_s3})(a0) - ld s4, ({guest_s4})(a0) - ld s5, ({guest_s5})(a0) - ld s6, ({guest_s6})(a0) - ld s7, ({guest_s7})(a0) - ld s8, ({guest_s8})(a0) - ld s9, ({guest_s9})(a0) - ld s10, ({guest_s10})(a0) - ld s11, ({guest_s11})(a0) - ld t0, ({guest_t0})(a0) - ld t1, ({guest_t1})(a0) - ld t2, ({guest_t2})(a0) - ld t3, ({guest_t3})(a0) - ld t4, ({guest_t4})(a0) - ld t5, ({guest_t5})(a0) - ld t6, ({guest_t6})(a0) - ld sp, ({guest_sp})(a0) - ld a0, ({guest_a0})(a0) - - sret - -.align 2 -_guest_exit: - /* Pull GuestInfo out of sscratch, swapping with guest's a0 */ - csrrw a0, sscratch, a0 - - /* Save guest GPRs. */ - sd ra, ({guest_ra})(a0) - sd gp, ({guest_gp})(a0) - sd tp, ({guest_tp})(a0) - sd s0, ({guest_s0})(a0) - sd s1, ({guest_s1})(a0) - sd a1, ({guest_a1})(a0) - sd a2, ({guest_a2})(a0) - sd a3, ({guest_a3})(a0) - sd a4, ({guest_a4})(a0) - sd a5, ({guest_a5})(a0) - sd a6, ({guest_a6})(a0) - sd a7, ({guest_a7})(a0) - sd s2, ({guest_s2})(a0) - sd s3, ({guest_s3})(a0) - sd s4, ({guest_s4})(a0) - sd s5, ({guest_s5})(a0) - sd s6, ({guest_s6})(a0) - sd s7, ({guest_s7})(a0) - sd s8, ({guest_s8})(a0) - sd s9, ({guest_s9})(a0) - sd s10, ({guest_s10})(a0) - sd s11, ({guest_s11})(a0) - sd t0, ({guest_t0})(a0) - sd t1, ({guest_t1})(a0) - sd t2, ({guest_t2})(a0) - sd t3, ({guest_t3})(a0) - sd t4, ({guest_t4})(a0) - sd t5, ({guest_t5})(a0) - sd t6, ({guest_t6})(a0) - sd sp, ({guest_sp})(a0) - - /* Save Guest a0 after recovering from sscratch. */ - csrr t0, sscratch - sd t0, ({guest_a0})(a0) - -_restore_csrs: - /* Swap in hypervisor CSRs. */ - ld t1, ({hyp_sstatus})(a0) - csrrw t1, sstatus, t1 - sd t1, ({guest_sstatus})(a0) - - ld t1, ({hyp_hstatus})(a0) - csrrw t1, hstatus, t1 - sd t1, ({guest_hstatus})(a0) - - ld t1, ({hyp_scounteren})(a0) - csrrw t1, scounteren, t1 - sd t1, ({guest_scounteren})(a0) - - ld t1, ({hyp_stvec})(a0) - csrw stvec, t1 - - ld t1, ({hyp_sscratch})(a0) - csrw sscratch, t1 - - /* Save guest EPC. */ - csrr t1, sepc - sd t1, ({guest_sepc})(a0) - - - /* Restore hypervisor GPRs. */ - ld ra, ({hyp_ra})(a0) - ld gp, ({hyp_gp})(a0) - ld tp, ({hyp_tp})(a0) - ld s0, ({hyp_s0})(a0) - ld s1, ({hyp_s1})(a0) - ld a1, ({hyp_a1})(a0) - ld a2, ({hyp_a2})(a0) - ld a3, ({hyp_a3})(a0) - ld a4, ({hyp_a4})(a0) - ld a5, ({hyp_a5})(a0) - ld a6, ({hyp_a6})(a0) - ld a7, ({hyp_a7})(a0) - ld s2, ({hyp_s2})(a0) - ld s3, ({hyp_s3})(a0) - ld s4, ({hyp_s4})(a0) - ld s5, ({hyp_s5})(a0) - ld s6, ({hyp_s6})(a0) - ld s7, ({hyp_s7})(a0) - ld s8, ({hyp_s8})(a0) - ld s9, ({hyp_s9})(a0) - ld s10, ({hyp_s10})(a0) - ld s11, ({hyp_s11})(a0) - ld sp, ({hyp_sp})(a0) - - ret \ No newline at end of file diff --git a/src/irq.rs b/src/irq.rs new file mode 100644 index 0000000..afde723 --- /dev/null +++ b/src/irq.rs @@ -0,0 +1,81 @@ +//! Interrupt management. +use handler_table::HandlerTable; +use lazyinit::LazyInit; + +use crate::consts::traps::irq::*; + +/// The type if an IRQ handler. +pub type IrqHandler = handler_table::Handler; + +static IRQ_HANDLER_TABLE: HandlerTable = HandlerTable::new(); +static TIMER_HANDLER: LazyInit = LazyInit::new(); + +/// Platform-independent IRQ dispatching. +#[allow(dead_code)] +pub(crate) fn dispatch_irq_common(irq_num: usize) { + trace!("IRQ {}", irq_num); + if !IRQ_HANDLER_TABLE.handle(irq_num) { + warn!("Unhandled IRQ {}", irq_num); + } +} + +/// Platform-independent IRQ handler registration. +/// +/// It also enables the IRQ if the registration succeeds. It returns `false` if +/// the registration failed. +#[allow(dead_code)] +pub(crate) fn register_handler_common(irq_num: usize, handler: IrqHandler) -> bool { + if irq_num < MAX_IRQ_COUNT && IRQ_HANDLER_TABLE.register_handler(irq_num, handler) { + return true; + } + warn!("register handler for IRQ {} failed", irq_num); + false +} + +pub fn handler_irq(irq_num: usize) -> bool { + dispatch_irq(irq_num); + true +} + +macro_rules! with_cause { + ($cause: expr, @TIMER => $timer_op: expr, @EXT => $ext_op: expr $(,)?) => { + match $cause { + S_TIMER => $timer_op, + S_EXT => $ext_op, + _ => panic!("invalid trap cause: {:#x}", $cause), + } + }; +} + +/// Registers an IRQ handler for the given IRQ. +/// +/// It also enables the IRQ if the registration succeeds. It returns `false` if +/// the registration failed. +pub fn register_handler(scause: usize, handler: IrqHandler) -> bool { + with_cause!( + scause, + @TIMER => if !TIMER_HANDLER.is_inited() { + TIMER_HANDLER.init_once(handler); + true + } else { + false + }, + @EXT => register_handler_common(scause & !INTC_IRQ_BASE, handler), + ) +} + +/// Dispatches the IRQ. +/// +/// This function is called by the common interrupt handler. It looks +/// up in the IRQ handler table and calls the corresponding handler. If +/// necessary, it also acknowledges the interrupt controller after handling. +pub fn dispatch_irq(scause: usize) { + with_cause!( + scause, + @TIMER => { + trace!("IRQ: timer"); + TIMER_HANDLER(); + }, + @EXT => dispatch_irq_common(0), // TODO: get IRQ number from PLIC + ); +} diff --git a/src/lib.rs b/src/lib.rs index 41cb2ee..28f8953 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,11 +8,14 @@ #[macro_use] extern crate log; -pub mod csrs; +mod consts; mod detect; +mod irq; mod percpu; mod regs; -pub mod sbi; +mod sbi; +mod timers; +mod trap; mod vcpu; pub use self::percpu::RISCVPerCpu; diff --git a/src/mem_extable.S b/src/mem_extable.S deleted file mode 100644 index ea19738..0000000 --- a/src/mem_extable.S +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright (c) 2022 by Rivos Inc. -// Licensed under the Apache License, Version 2.0, see LICENSE for details. -// SPDX-License-Identifier: Apache-2.0 - -// Very unoptimized memcpy() to/from guest memory functions, using the HLV/HSV instructions. - -// Adds the instruction at 'lbl' to the exception table. -.macro add_extable lbl -.pushsection .extable, "a" -.balign 8 -.quad \lbl -.popsection -.endm -.option push -.option arch, +h -.section .text - -// memcpy() to a guest physical address using HSV. -.global _copy_to_guest -_copy_to_guest: - // handle_trap assumes t0 holds the address of where we want to jump to when we encounter - // a fault and will stick SCAUSE in t1. - la t0, _ret_from_copy - // _ret_from_copy assumes the return value is in t2. - mv t2, zero -1: - beq t2, a2, _ret_from_copy - lb t3, (a1) -2: - hsv.b t3, (a0) - add_extable 2b - addi a0, a0, 1 - addi a1, a1, 1 - addi t2, t2, 1 - j 1b - -// memcpy() from a guest physical address using HLV. -.global _copy_from_guest -_copy_from_guest: - // handle_trap assumes t0 holds the address of where we want to jump to when we encounter - // a fault and will stick SCAUSE in t1. - la t0, _ret_from_copy - // _ret_from_copy assumes the return value is in t2. - mv t2, zero -1: - beq t2, a2, _ret_from_copy -2: - hlv.b t3, (a1) - add_extable 2b - sb t3, (a0) - addi a0, a0, 1 - addi a1, a1, 1 - addi t2, t2, 1 - j 1b - -// Fetch an instruction from guest memory using HLVX. Only supports 2 or 4 byte instructions. -// -// Arguments: -// A0: Guest address of the instruction to fetch, using the translation modes/tables currently -// programmed in HGATP and VSATP. -// A1: Pointer to a u32 where the instruction will be written. -// -// Returns -1 on error. -.global _fetch_guest_instruction -_fetch_guest_instruction: - // handle_trap assumes t0 holds the address of where we want to jump to when we encounter - // a fault and will stick SCAUSE in t1. - la t0, 4f -1: - hlvx.hu t2, (a0) - add_extable 1b - sh t2, (a1) - addi a0, a0, 2 - addi a1, a1, 2 - // If it's a compressed instrution (bits [1:0] != 'b11) then we're done. - li t3, 3 - and t2, t2, t3 - bne t2, t3, 3f - // Load the next half-word. -2: - hlvx.hu t2, (a0) - add_extable 2b - sh t2, (a1) -3: - mv a0, zero - ret -4: - // Took a fault, return -1. - not a0, zero - ret - -// memcpy() to a user address. -.global _copy_to_user -_copy_to_user: - // handle_trap assumes t0 holds the address of where we want to jump to when we encounter - // a fault and will stick SCAUSE in t1. - la t0, _ret_from_copy - // _ret_from_copy assumes the return value is in t2. - mv t2, zero -1: - beq t2, a2, _ret_from_copy - lb t3, (a1) -2: - sb t3, (a0) - add_extable 2b - addi a0, a0, 1 - addi a1, a1, 1 - addi t2, t2, 1 - j 1b - -// memcpy() from a user address. -.global _copy_from_user -_copy_from_user: - // handle_trap assumes t0 holds the address of where we want to jump to when we encounter - // a fault and will stick SCAUSE in t1. - la t0, _ret_from_copy - // _ret_from_copy assumes the return value is in t2. - mv t2, zero -1: - beq t2, a2, _ret_from_copy -2: - lb t3, (a1) - add_extable 2b - sb t3, (a0) - addi a0, a0, 1 - addi a1, a1, 1 - addi t2, t2, 1 - j 1b - -.align 2 -_ret_from_copy: - mv a0, t2 - ret -.option pop \ No newline at end of file diff --git a/src/percpu.rs b/src/percpu.rs index 0652b62..7bceb62 100644 --- a/src/percpu.rs +++ b/src/percpu.rs @@ -1,18 +1,42 @@ use axerrno::{AxError, AxResult}; - use axvcpu::AxArchPerCpu; +use riscv::register::{hedeleg, hideleg, hvip, sie, stvec}; -use crate::csrs::{traps, RiscvCsrTrait, CSR}; +use crate::consts::traps; +use crate::consts::traps::irq::TIMER_IRQ_NUM; use crate::has_hardware_support; +use crate::irq; +use crate::timers; + +extern "C" { + fn trap_base(); +} +/// The architecture dependent configuration of a `AxArchPerCpu`. pub struct RISCVPerCpu {} impl AxArchPerCpu for RISCVPerCpu { - fn new(_cpu_id: usize) -> AxResult { + fn new(cpu_id: usize) -> AxResult { unsafe { setup_csrs(); } + if cpu_id == 0 { + irq::register_handler(TIMER_IRQ_NUM, || { + unsafe { + sie::clear_stimer(); + } + + timers::check_events(); + timers::scheduler_next_event(); + unsafe { + sie::set_stimer(); + } + }); + } + + timers::init(); + sbi_rt::set_timer(0); Ok(Self {}) } @@ -36,7 +60,7 @@ impl AxArchPerCpu for RISCVPerCpu { /// Initialize (H)S-level CSRs to a reasonable state. unsafe fn setup_csrs() { // Delegate some synchronous exceptions. - CSR.hedeleg.write_value( + hedeleg::Hedeleg::from_bits( traps::exception::INST_ADDR_MISALIGN | traps::exception::BREAKPOINT | traps::exception::ENV_CALL_FROM_U_OR_VU @@ -44,30 +68,31 @@ unsafe fn setup_csrs() { | traps::exception::LOAD_PAGE_FAULT | traps::exception::STORE_PAGE_FAULT | traps::exception::ILLEGAL_INST, - ); + ) + .write(); // Delegate all interupts. - CSR.hideleg.write_value( + hideleg::Hideleg::from_bits( traps::interrupt::VIRTUAL_SUPERVISOR_TIMER | traps::interrupt::VIRTUAL_SUPERVISOR_EXTERNAL | traps::interrupt::VIRTUAL_SUPERVISOR_SOFT, - ); + ) + .write(); // Clear all interrupts. - CSR.hvip.read_and_clear_bits( - traps::interrupt::VIRTUAL_SUPERVISOR_TIMER - | traps::interrupt::VIRTUAL_SUPERVISOR_EXTERNAL - | traps::interrupt::VIRTUAL_SUPERVISOR_SOFT, - ); + hvip::clear_vssip(); + hvip::clear_vstip(); + hvip::clear_vseip(); // clear all interrupts. - CSR.hcounteren.write_value(0xffff_ffff); + // the csr num of hcounteren is 0x606, the riscv repo is error!!! + // hcounteren::Hcounteren::from_bits(0xffff_ffff).write(); + core::arch::asm!("csrw {csr}, {rs}", csr = const 0x606, rs = in(reg) -1); // enable interrupt - CSR.sie.write_value( - traps::interrupt::SUPERVISOR_EXTERNAL - | traps::interrupt::SUPERVISOR_SOFT - | traps::interrupt::SUPERVISOR_TIMER, - ); - debug!("sie: {:#x}", CSR.sie.get_value()); + sie::set_sext(); + sie::set_ssoft(); + sie::set_stimer(); + + stvec::write(trap_base as usize, stvec::TrapMode::Direct); } diff --git a/src/regs.rs b/src/regs.rs index 5d7aadc..7afa40c 100644 --- a/src/regs.rs +++ b/src/regs.rs @@ -1,4 +1,4 @@ -#[derive(Default)] +#[derive(Debug, Default, Clone)] #[repr(C)] pub struct GeneralPurposeRegisters([usize; 32]); @@ -112,3 +112,85 @@ impl GeneralPurposeRegisters { &mut self.0[GprIndex::A0 as usize..=GprIndex::A7 as usize] } } + +/// Hypervisor GPR and CSR state which must be saved/restored when entering/exiting virtualization. +#[derive(Debug, Default, Clone)] +#[repr(C)] +pub struct HypervisorCpuState { + pub gprs: GeneralPurposeRegisters, + pub sstatus: usize, + pub hstatus: usize, + pub scounteren: usize, + pub stvec: usize, + pub sscratch: usize, +} + +/// Guest GPR and CSR state which must be saved/restored when exiting/entering virtualization. +#[derive(Debug, Default, Clone)] +#[repr(C)] +pub struct GuestCpuState { + pub gprs: GeneralPurposeRegisters, + pub sstatus: usize, + pub hstatus: usize, + pub scounteren: usize, + pub sepc: usize, +} + +/// The CSRs that are only in effect when virtualization is enabled (V=1) and must be saved and +/// restored whenever we switch between VMs. +#[derive(Debug, Default, Clone)] +#[repr(C)] +pub struct GuestVsCsrs { + pub htimedelta: usize, + pub vsstatus: usize, + pub vsie: usize, + pub vstvec: usize, + pub vsscratch: usize, + pub vsepc: usize, + pub vscause: usize, + pub vstval: usize, + pub vsatp: usize, + pub vstimecmp: usize, +} + +/// Virtualized HS-level CSRs that are used to emulate (part of) the hypervisor extension for the +/// guest. +#[derive(Debug, Default, Clone)] +#[repr(C)] +pub struct GuestVirtualHsCsrs { + pub hie: usize, + pub hgeie: usize, + pub hgatp: usize, +} + +/// CSRs written on an exit from virtualization that are used by the hypervisor to determine the cause +/// of the trap. +#[derive(Debug, Default, Clone)] +#[repr(C)] +pub struct VmCpuTrapState { + pub scause: usize, + pub stval: usize, + pub htval: usize, + pub htinst: usize, +} + +/// (v)CPU register state that must be saved or restored when entering/exiting a VM or switching +/// between VMs. +#[derive(Debug, Default, Clone)] +#[repr(C)] +pub struct VmCpuRegisters { + // CPU state that's shared between our's and the guest's execution environment. Saved/restored + // when entering/exiting a VM. + pub hyp_regs: HypervisorCpuState, + pub guest_regs: GuestCpuState, + + // CPU state that only applies when V=1, e.g. the VS-level CSRs. Saved/restored on activation of + // the vCPU. + pub vs_csrs: GuestVsCsrs, + + // Virtualized HS-level CPU state. + pub virtual_hs_csrs: GuestVirtualHsCsrs, + + // Read on VM exit. + pub trap_csrs: VmCpuTrapState, +} diff --git a/src/timers.rs b/src/timers.rs new file mode 100644 index 0000000..1dfa116 --- /dev/null +++ b/src/timers.rs @@ -0,0 +1,64 @@ +extern crate alloc; + +use alloc::boxed::Box; +use kspin::SpinNoIrq; +use lazyinit::LazyInit; +use timer_list::{TimeValue, TimerEvent, TimerList}; + +use crate::consts::timers::*; + +// TODO:complete TimerEventFn: including guest owmer, ... +pub struct TimerEventFn(Box); + +impl TimerEventFn { + /// Constructs a new [`TimerEventFn`] from a closure. + pub fn new(f: F) -> Self + where + F: FnOnce(TimeValue) + Send + 'static, + { + Self(Box::new(f)) + } +} + +impl TimerEvent for TimerEventFn { + fn callback(self, now: TimeValue) { + (self.0)(now) + } +} + +#[percpu::def_percpu] +static TIMER_LIST: LazyInit>> = LazyInit::new(); + +// deadline: ns +pub fn register_timer(deadline: usize, handler: TimerEventFn) { + let timer_list = unsafe { TIMER_LIST.current_ref_mut_raw() }; + let mut timers = timer_list.lock(); + timers.set(TimeValue::from_nanos(deadline as u64), handler); +} + +pub fn check_events() { + // info!("1"); + loop { + let now = TimeValue::from_nanos(riscv::register::time::read() as u64 * NANOS_PER_TICK); + let timer_list = unsafe { TIMER_LIST.current_ref_mut_raw() }; + let mut event = timer_list.lock(); + let event = event.expire_one(now); + if let Some((_deadline, event)) = event { + event.callback(now); + } else { + break; + } + } +} + +pub fn scheduler_next_event() { + // info!("set deadline!!!"); + let now_ns = riscv::register::time::read() as u64 * NANOS_PER_TICK; + let deadline = now_ns + PERIODIC_INTERVAL_NANOS; + sbi_rt::set_timer(deadline / NANOS_PER_TICK as u64); +} + +pub fn init() { + let timer_list = unsafe { TIMER_LIST.current_ref_mut_raw() }; + timer_list.init_once(SpinNoIrq::new(TimerList::new())); +} diff --git a/src/trap.S b/src/trap.S new file mode 100644 index 0000000..f4fc930 --- /dev/null +++ b/src/trap.S @@ -0,0 +1,260 @@ +.macro SAVE_STATE, from_user + addi sp, sp, -{trapframe_size} + sd ra, ({guest_ra})(sp) + sd s0, ({guest_s0})(sp) + sd s1, ({guest_s1})(sp) + sd a0, ({guest_a0})(sp) + sd a1, ({guest_a1})(sp) + sd a2, ({guest_a2})(sp) + sd a3, ({guest_a3})(sp) + sd a4, ({guest_a4})(sp) + sd a5, ({guest_a5})(sp) + sd a6, ({guest_a6})(sp) + sd a7, ({guest_a7})(sp) + sd s2, ({guest_s2})(sp) + sd s3, ({guest_s3})(sp) + sd s4, ({guest_s4})(sp) + sd s5, ({guest_s5})(sp) + sd s6, ({guest_s6})(sp) + sd s7, ({guest_s7})(sp) + sd s8, ({guest_s8})(sp) + sd s9, ({guest_s9})(sp) + sd s10, ({guest_s10})(sp) + sd s11, ({guest_s11})(sp) + sd t0, ({guest_t0})(sp) + sd t1, ({guest_t1})(sp) + sd t2, ({guest_t2})(sp) + sd t3, ({guest_t3})(sp) + sd t4, ({guest_t4})(sp) + sd t5, ({guest_t5})(sp) + sd t6, ({guest_t6})(sp) + + csrr t0, sepc + sd t0, ({guest_sepc})(sp) + csrr t0, sstatus + sd t0, ({guest_sstatus})(sp) + csrr t0, hstatus + sd t0, ({guest_hstatus})(sp) + csrr t0, scounteren + sd t0, ({guest_scounteren})(sp) + + csrrw t0, sscratch, zero + sd t0, ({guest_sp})(sp) + +.if \from_user == 1 + sd gp, ({guest_gp})(sp) + sd tp, ({guest_tp})(sp) + ; ld tp, ({hyp_tp})(sp) +.endif + +.endm + +/* U mode and S mode use, guests use run_guest to resume guests. */ +.macro RESTORE_STATE, from_user +.if \from_user == 1 + ld gp, ({guest_gp})(sp) + ; sd tp, ({hyp_tp})(sp) + ld tp, ({guest_tp})(sp) + addi t0, sp, {trapframe_size} + csrw sscratch, t0 +.endif + + ld t1, ({guest_sstatus})(sp) + csrw sstatus, t1 + ld t1, ({guest_hstatus})(sp) + csrw hstatus, t1 + ld t1, ({guest_scounteren})(sp) + csrw scounteren, t1 + ld t1, ({guest_sepc})(sp) + csrw sepc, t1 + + ld ra, ({guest_ra})(sp) + ld s0, ({guest_s0})(sp) + ld s1, ({guest_s1})(sp) + ld a0, ({guest_a0})(sp) + ld a1, ({guest_a1})(sp) + ld a2, ({guest_a2})(sp) + ld a3, ({guest_a3})(sp) + ld a4, ({guest_a4})(sp) + ld a5, ({guest_a5})(sp) + ld a6, ({guest_a6})(sp) + ld a7, ({guest_a7})(sp) + ld s2, ({guest_s2})(sp) + ld s3, ({guest_s3})(sp) + ld s4, ({guest_s4})(sp) + ld s5, ({guest_s5})(sp) + ld s6, ({guest_s6})(sp) + ld s7, ({guest_s7})(sp) + ld s8, ({guest_s8})(sp) + ld s9, ({guest_s9})(sp) + ld s10, ({guest_s10})(sp) + ld s11, ({guest_s11})(sp) + ld t0, ({guest_t0})(sp) + ld t1, ({guest_t1})(sp) + ld t2, ({guest_t2})(sp) + ld t3, ({guest_t3})(sp) + ld t4, ({guest_t4})(sp) + ld t5, ({guest_t5})(sp) + ld t6, ({guest_t6})(sp) + ld sp, ({guest_sp})(sp) +.endm + +/* this macro is used for hypervisor to restore host state */ +.macro RESTORE_HOST_STATE + ld ra, ({hyp_ra})(a0) + ld gp, ({hyp_gp})(a0) + ld tp, ({hyp_tp})(a0) + ld s0, ({hyp_s0})(a0) + ld s1, ({hyp_s1})(a0) + ld a1, ({hyp_a1})(a0) + ld a2, ({hyp_a2})(a0) + ld a3, ({hyp_a3})(a0) + ld a4, ({hyp_a4})(a0) + ld a5, ({hyp_a5})(a0) + ld a6, ({hyp_a6})(a0) + ld a7, ({hyp_a7})(a0) + ld s2, ({hyp_s2})(a0) + ld s3, ({hyp_s3})(a0) + ld s4, ({hyp_s4})(a0) + ld s5, ({hyp_s5})(a0) + ld s6, ({hyp_s6})(a0) + ld s7, ({hyp_s7})(a0) + ld s8, ({hyp_s8})(a0) + ld s9, ({hyp_s9})(a0) + ld s10, ({hyp_s10})(a0) + ld s11, ({hyp_s11})(a0) + ld sp, ({hyp_sp})(a0) + + + ld t1, ({hyp_hstatus})(a0) + csrw hstatus, t1 + ld t1, ({hyp_scounteren})(a0) + csrw scounteren, t1 + ld t1, ({hyp_sscratch})(a0) + csrw sscratch, t1 + ld t1, ({hyp_sstatus})(a0) + csrw sstatus, t1 +.endm + +.section .text +.balign 4 +.global trap_base +trap_base: + // sscratch == 0: trap from S mode + // sscratch != 0: trap from guest + csrrw sp, sscratch, sp + bnez sp, .entry_u + + csrr sp, sscratch + j .entry_s + +.entry_s: + SAVE_STATE 0 + mv a0, sp + li a1, 0 + call trap_handler + RESTORE_STATE 0 + sret + +.entry_u: + SAVE_STATE 1 + mv a0, sp + li a1, 1 + la sp, {exception_stack} + li t0, {exception_stack_size} + add sp, sp, t0 + call trap_handler + RESTORE_STATE 1 + sret + + +/// Enter the guest given in `VmCpuRegisters` from `a0` +.global _run_guest +_run_guest: + /* Save hypervisor state */ + + /* Save hypervisor GPRs (except T0-T6 and a0, which is GuestInfo and stashed in sscratch) */ + sd ra, ({hyp_ra})(a0) + sd gp, ({hyp_gp})(a0) + sd tp, ({hyp_tp})(a0) + sd s0, ({hyp_s0})(a0) + sd s1, ({hyp_s1})(a0) + sd a1, ({hyp_a1})(a0) + sd a2, ({hyp_a2})(a0) + sd a3, ({hyp_a3})(a0) + sd a4, ({hyp_a4})(a0) + sd a5, ({hyp_a5})(a0) + sd a6, ({hyp_a6})(a0) + sd a7, ({hyp_a7})(a0) + sd s2, ({hyp_s2})(a0) + sd s3, ({hyp_s3})(a0) + sd s4, ({hyp_s4})(a0) + sd s5, ({hyp_s5})(a0) + sd s6, ({hyp_s6})(a0) + sd s7, ({hyp_s7})(a0) + sd s8, ({hyp_s8})(a0) + sd s9, ({hyp_s9})(a0) + sd s10, ({hyp_s10})(a0) + sd s11, ({hyp_s11})(a0) + sd sp, ({hyp_sp})(a0) + + /* Swap in guest CSRs. */ + ld t1, ({guest_sstatus})(a0) + csrrw t1, sstatus, t1 + sd t1, ({hyp_sstatus})(a0) + + ld t1, ({guest_hstatus})(a0) + csrrw t1, hstatus, t1 + sd t1, ({hyp_hstatus})(a0) + + ld t1, ({guest_scounteren})(a0) + csrrw t1, scounteren, t1 + sd t1, ({hyp_scounteren})(a0) + + ld t1, ({guest_sepc})(a0) + csrw sepc, t1 + + /* Save sscratch and replace with pointer to GuestInfo. */ + addi t1, a0, {trapframe_size} + csrrw t1, sscratch, t1 + sd t1, ({hyp_sscratch})(a0) + + /* Restore the gprs from this GuestInfo */ + ld ra, ({guest_ra})(a0) + ld gp, ({guest_gp})(a0) + ld tp, ({guest_tp})(a0) + ld s0, ({guest_s0})(a0) + ld s1, ({guest_s1})(a0) + ld a1, ({guest_a1})(a0) + ld a2, ({guest_a2})(a0) + ld a3, ({guest_a3})(a0) + ld a4, ({guest_a4})(a0) + ld a5, ({guest_a5})(a0) + ld a6, ({guest_a6})(a0) + ld a7, ({guest_a7})(a0) + ld s2, ({guest_s2})(a0) + ld s3, ({guest_s3})(a0) + ld s4, ({guest_s4})(a0) + ld s5, ({guest_s5})(a0) + ld s6, ({guest_s6})(a0) + ld s7, ({guest_s7})(a0) + ld s8, ({guest_s8})(a0) + ld s9, ({guest_s9})(a0) + ld s10, ({guest_s10})(a0) + ld s11, ({guest_s11})(a0) + ld t0, ({guest_t0})(a0) + ld t1, ({guest_t1})(a0) + ld t2, ({guest_t2})(a0) + ld t3, ({guest_t3})(a0) + ld t4, ({guest_t4})(a0) + ld t5, ({guest_t5})(a0) + ld t6, ({guest_t6})(a0) + ld sp, ({guest_sp})(a0) + ld a0, ({guest_a0})(a0) + + sret + +.global vmexit_riscv_handler +vmexit_riscv_handler: + RESTORE_HOST_STATE + ret \ No newline at end of file diff --git a/src/trap.rs b/src/trap.rs new file mode 100644 index 0000000..2ddbe37 --- /dev/null +++ b/src/trap.rs @@ -0,0 +1,186 @@ +use core::mem::size_of; + +use memoffset::offset_of; +use memory_addr::VirtAddr; +use page_table_entry::MappingFlags; +use riscv::register::scause::{self, Exception as E, Trap}; +use riscv::register::{hstatus, htinst, htval, stval}; + +use crate::consts::stack::EXCEPTION_STACK_SIZE; +use crate::irq::handler_irq; +use crate::regs::*; + +extern "C" { + fn vmexit_riscv_handler(state: *mut VmCpuRegisters); +} + +static mut EXCEPTION_STACK: [u8; EXCEPTION_STACK_SIZE] = [0; EXCEPTION_STACK_SIZE]; + +#[allow(dead_code)] +const fn hyp_gpr_offset(index: GprIndex) -> usize { + offset_of!(VmCpuRegisters, hyp_regs) + + offset_of!(HypervisorCpuState, gprs) + + (index as usize) * size_of::() +} + +#[allow(dead_code)] +const fn guest_gpr_offset(index: GprIndex) -> usize { + offset_of!(VmCpuRegisters, guest_regs) + + offset_of!(GuestCpuState, gprs) + + (index as usize) * size_of::() +} + +#[allow(unused_macros)] +macro_rules! hyp_csr_offset { + ($reg:tt) => { + offset_of!(VmCpuRegisters, hyp_regs) + offset_of!(HypervisorCpuState, $reg) + }; +} + +#[allow(unused_macros)] +macro_rules! guest_csr_offset { + ($reg:tt) => { + offset_of!(VmCpuRegisters, guest_regs) + offset_of!(GuestCpuState, $reg) + }; +} + +core::arch::global_asm!( + include_str!("trap.S"), + trapframe_size = const core::mem::size_of::(), + hyp_ra = const hyp_gpr_offset(GprIndex::RA), + hyp_gp = const hyp_gpr_offset(GprIndex::GP), + hyp_tp = const hyp_gpr_offset(GprIndex::TP), + hyp_s0 = const hyp_gpr_offset(GprIndex::S0), + hyp_s1 = const hyp_gpr_offset(GprIndex::S1), + // hyp_a0 = const hyp_gpr_offset(GprIndex::A0), + hyp_a1 = const hyp_gpr_offset(GprIndex::A1), + hyp_a2 = const hyp_gpr_offset(GprIndex::A2), + hyp_a3 = const hyp_gpr_offset(GprIndex::A3), + hyp_a4 = const hyp_gpr_offset(GprIndex::A4), + hyp_a5 = const hyp_gpr_offset(GprIndex::A5), + hyp_a6 = const hyp_gpr_offset(GprIndex::A6), + hyp_a7 = const hyp_gpr_offset(GprIndex::A7), + hyp_s2 = const hyp_gpr_offset(GprIndex::S2), + hyp_s3 = const hyp_gpr_offset(GprIndex::S3), + hyp_s4 = const hyp_gpr_offset(GprIndex::S4), + hyp_s5 = const hyp_gpr_offset(GprIndex::S5), + hyp_s6 = const hyp_gpr_offset(GprIndex::S6), + hyp_s7 = const hyp_gpr_offset(GprIndex::S7), + hyp_s8 = const hyp_gpr_offset(GprIndex::S8), + hyp_s9 = const hyp_gpr_offset(GprIndex::S9), + hyp_s10 = const hyp_gpr_offset(GprIndex::S10), + hyp_s11 = const hyp_gpr_offset(GprIndex::S11), + hyp_sp = const hyp_gpr_offset(GprIndex::SP), + hyp_sstatus = const hyp_csr_offset!(sstatus), + hyp_hstatus = const hyp_csr_offset!(hstatus), + hyp_scounteren = const hyp_csr_offset!(scounteren), + // hyp_stvec = const hyp_csr_offset!(stvec), + hyp_sscratch = const hyp_csr_offset!(sscratch), + guest_ra = const guest_gpr_offset(GprIndex::RA), + guest_gp = const guest_gpr_offset(GprIndex::GP), + guest_tp = const guest_gpr_offset(GprIndex::TP), + guest_s0 = const guest_gpr_offset(GprIndex::S0), + guest_s1 = const guest_gpr_offset(GprIndex::S1), + guest_a0 = const guest_gpr_offset(GprIndex::A0), + guest_a1 = const guest_gpr_offset(GprIndex::A1), + guest_a2 = const guest_gpr_offset(GprIndex::A2), + guest_a3 = const guest_gpr_offset(GprIndex::A3), + guest_a4 = const guest_gpr_offset(GprIndex::A4), + guest_a5 = const guest_gpr_offset(GprIndex::A5), + guest_a6 = const guest_gpr_offset(GprIndex::A6), + guest_a7 = const guest_gpr_offset(GprIndex::A7), + guest_s2 = const guest_gpr_offset(GprIndex::S2), + guest_s3 = const guest_gpr_offset(GprIndex::S3), + guest_s4 = const guest_gpr_offset(GprIndex::S4), + guest_s5 = const guest_gpr_offset(GprIndex::S5), + guest_s6 = const guest_gpr_offset(GprIndex::S6), + guest_s7 = const guest_gpr_offset(GprIndex::S7), + guest_s8 = const guest_gpr_offset(GprIndex::S8), + guest_s9 = const guest_gpr_offset(GprIndex::S9), + guest_s10 = const guest_gpr_offset(GprIndex::S10), + guest_s11 = const guest_gpr_offset(GprIndex::S11), + guest_t0 = const guest_gpr_offset(GprIndex::T0), + guest_t1 = const guest_gpr_offset(GprIndex::T1), + guest_t2 = const guest_gpr_offset(GprIndex::T2), + guest_t3 = const guest_gpr_offset(GprIndex::T3), + guest_t4 = const guest_gpr_offset(GprIndex::T4), + guest_t5 = const guest_gpr_offset(GprIndex::T5), + guest_t6 = const guest_gpr_offset(GprIndex::T6), + guest_sp = const guest_gpr_offset(GprIndex::SP), + + guest_sstatus = const guest_csr_offset!(sstatus), + guest_hstatus = const guest_csr_offset!(hstatus), + guest_scounteren = const guest_csr_offset!(scounteren), + guest_sepc = const guest_csr_offset!(sepc), + exception_stack = sym EXCEPTION_STACK, + exception_stack_size = const EXCEPTION_STACK_SIZE, +); + +fn handle_breakpoint(sepc: &mut usize) { + debug!("Exception(Breakpoint) @ {:#x} ", sepc); + *sepc += 2 +} + +fn handle_page_fault(tf: &VmCpuRegisters, access_flags: MappingFlags, is_user: bool) { + let vaddr = VirtAddr::from(stval::read()); + + panic!( + "Unhandled {} Page Fault @ {:#x}, fault_vaddr={:#x} ({:?}):\n{:#x?}", + if is_user { "User" } else { "Supervisor" }, + tf.guest_regs.sepc, + vaddr, + access_flags, + tf.guest_regs, + ); +} + +#[no_mangle] +fn trap_handler(tf: &mut VmCpuRegisters, from_user: bool) { + let hstatus = hstatus::read(); + + match hstatus.spv() { + true => { + // from V = 1 + // info!("trap from guest!"); + tf.trap_csrs.scause = scause::read().bits(); + // info!("scause:{:x}", scause::read().bits()); + tf.trap_csrs.stval = stval::read(); + tf.trap_csrs.htval = htval::read(); + tf.trap_csrs.htinst = htinst::read(); + + unsafe { + vmexit_riscv_handler(tf); + } + } + _ => { + // from V = 0 + + let scause = scause::read(); + // info!("trap not from guest! scause: {:?}", scause.cause()); + match scause.cause() { + Trap::Exception(E::LoadPageFault) => { + handle_page_fault(tf, MappingFlags::READ, from_user) + } + Trap::Exception(E::StorePageFault) => { + handle_page_fault(tf, MappingFlags::WRITE, from_user) + } + Trap::Exception(E::InstructionPageFault) => { + handle_page_fault(tf, MappingFlags::EXECUTE, from_user) + } + Trap::Exception(E::Breakpoint) => handle_breakpoint(&mut tf.guest_regs.sepc), + Trap::Interrupt(_) => { + // handle_trap!(IRQ, scause.bits()); + handler_irq(scause.bits()); + } + _ => { + panic!( + "Unhandled trap {:?} @ {:#x}:\n{:#x?}", + scause.cause(), + tf.guest_regs.sepc, + tf.guest_regs + ); + } + } + } + } +} diff --git a/src/vcpu.rs b/src/vcpu.rs index edf5ada..6b8f618 100644 --- a/src/vcpu.rs +++ b/src/vcpu.rs @@ -1,199 +1,19 @@ -use core::arch::global_asm; use core::mem::size_of; -use memoffset::offset_of; -use riscv::register::{htinst, htval, scause, sstatus, stval}; -use sbi_rt::{pmu_counter_get_info, pmu_counter_stop}; -use tock_registers::LocalRegisterCopy; - use axaddrspace::{GuestPhysAddr, HostPhysAddr, MappingFlags}; use axerrno::AxResult; +use axvcpu::AxArchVCpu; use axvcpu::AxVCpuExitReason; +use riscv::addr::BitField; +use riscv::register::{hstatus, hvip, scause, sstatus}; +use sbi_rt::{pmu_counter_get_info, pmu_counter_stop}; -use super::csrs::defs::hstatus; -use super::csrs::{traps, RiscvCsrTrait, CSR}; -use super::sbi::{BaseFunction, PmuFunction, RemoteFenceFunction, SbiMessage}; - -use super::regs::{GeneralPurposeRegisters, GprIndex}; - -/// Hypervisor GPR and CSR state which must be saved/restored when entering/exiting virtualization. -#[derive(Default)] -#[repr(C)] -struct HypervisorCpuState { - gprs: GeneralPurposeRegisters, - sstatus: usize, - hstatus: usize, - scounteren: usize, - stvec: usize, - sscratch: usize, -} - -/// Guest GPR and CSR state which must be saved/restored when exiting/entering virtualization. -#[derive(Default)] -#[repr(C)] -pub struct GuestCpuState { - pub gprs: GeneralPurposeRegisters, - pub sstatus: usize, - pub hstatus: usize, - pub scounteren: usize, - pub sepc: usize, -} - -/// The CSRs that are only in effect when virtualization is enabled (V=1) and must be saved and -/// restored whenever we switch between VMs. -#[derive(Default)] -#[repr(C)] -pub struct GuestVsCsrs { - htimedelta: usize, - vsstatus: usize, - vsie: usize, - vstvec: usize, - vsscratch: usize, - vsepc: usize, - vscause: usize, - vstval: usize, - vsatp: usize, - vstimecmp: usize, -} - -/// Virtualized HS-level CSRs that are used to emulate (part of) the hypervisor extension for the -/// guest. -#[derive(Default)] -#[repr(C)] -pub struct GuestVirtualHsCsrs { - hie: usize, - hgeie: usize, - hgatp: usize, -} - -/// CSRs written on an exit from virtualization that are used by the hypervisor to determine the cause -/// of the trap. -#[derive(Default, Clone)] -#[repr(C)] -pub struct VmCpuTrapState { - pub scause: usize, - pub stval: usize, - pub htval: usize, - pub htinst: usize, -} - -/// (v)CPU register state that must be saved or restored when entering/exiting a VM or switching -/// between VMs. -#[derive(Default)] -#[repr(C)] -pub struct VmCpuRegisters { - // CPU state that's shared between our's and the guest's execution environment. Saved/restored - // when entering/exiting a VM. - hyp_regs: HypervisorCpuState, - pub guest_regs: GuestCpuState, - - // CPU state that only applies when V=1, e.g. the VS-level CSRs. Saved/restored on activation of - // the vCPU. - vs_csrs: GuestVsCsrs, - - // Virtualized HS-level CPU state. - virtual_hs_csrs: GuestVirtualHsCsrs, - - // Read on VM exit. - pub trap_csrs: VmCpuTrapState, -} - -#[allow(dead_code)] -const fn hyp_gpr_offset(index: GprIndex) -> usize { - offset_of!(VmCpuRegisters, hyp_regs) - + offset_of!(HypervisorCpuState, gprs) - + (index as usize) * size_of::() -} - -#[allow(dead_code)] -const fn guest_gpr_offset(index: GprIndex) -> usize { - offset_of!(VmCpuRegisters, guest_regs) - + offset_of!(GuestCpuState, gprs) - + (index as usize) * size_of::() -} - -#[allow(unused_macros)] -macro_rules! hyp_csr_offset { - ($reg:tt) => { - offset_of!(VmCpuRegisters, hyp_regs) + offset_of!(HypervisorCpuState, $reg) - }; -} - -#[allow(unused_macros)] -macro_rules! guest_csr_offset { - ($reg:tt) => { - offset_of!(VmCpuRegisters, guest_regs) + offset_of!(GuestCpuState, $reg) - }; -} - -global_asm!( - include_str!("guest.S"), - hyp_ra = const hyp_gpr_offset(GprIndex::RA), - hyp_gp = const hyp_gpr_offset(GprIndex::GP), - hyp_tp = const hyp_gpr_offset(GprIndex::TP), - hyp_s0 = const hyp_gpr_offset(GprIndex::S0), - hyp_s1 = const hyp_gpr_offset(GprIndex::S1), - hyp_a1 = const hyp_gpr_offset(GprIndex::A1), - hyp_a2 = const hyp_gpr_offset(GprIndex::A2), - hyp_a3 = const hyp_gpr_offset(GprIndex::A3), - hyp_a4 = const hyp_gpr_offset(GprIndex::A4), - hyp_a5 = const hyp_gpr_offset(GprIndex::A5), - hyp_a6 = const hyp_gpr_offset(GprIndex::A6), - hyp_a7 = const hyp_gpr_offset(GprIndex::A7), - hyp_s2 = const hyp_gpr_offset(GprIndex::S2), - hyp_s3 = const hyp_gpr_offset(GprIndex::S3), - hyp_s4 = const hyp_gpr_offset(GprIndex::S4), - hyp_s5 = const hyp_gpr_offset(GprIndex::S5), - hyp_s6 = const hyp_gpr_offset(GprIndex::S6), - hyp_s7 = const hyp_gpr_offset(GprIndex::S7), - hyp_s8 = const hyp_gpr_offset(GprIndex::S8), - hyp_s9 = const hyp_gpr_offset(GprIndex::S9), - hyp_s10 = const hyp_gpr_offset(GprIndex::S10), - hyp_s11 = const hyp_gpr_offset(GprIndex::S11), - hyp_sp = const hyp_gpr_offset(GprIndex::SP), - hyp_sstatus = const hyp_csr_offset!(sstatus), - hyp_hstatus = const hyp_csr_offset!(hstatus), - hyp_scounteren = const hyp_csr_offset!(scounteren), - hyp_stvec = const hyp_csr_offset!(stvec), - hyp_sscratch = const hyp_csr_offset!(sscratch), - guest_ra = const guest_gpr_offset(GprIndex::RA), - guest_gp = const guest_gpr_offset(GprIndex::GP), - guest_tp = const guest_gpr_offset(GprIndex::TP), - guest_s0 = const guest_gpr_offset(GprIndex::S0), - guest_s1 = const guest_gpr_offset(GprIndex::S1), - guest_a0 = const guest_gpr_offset(GprIndex::A0), - guest_a1 = const guest_gpr_offset(GprIndex::A1), - guest_a2 = const guest_gpr_offset(GprIndex::A2), - guest_a3 = const guest_gpr_offset(GprIndex::A3), - guest_a4 = const guest_gpr_offset(GprIndex::A4), - guest_a5 = const guest_gpr_offset(GprIndex::A5), - guest_a6 = const guest_gpr_offset(GprIndex::A6), - guest_a7 = const guest_gpr_offset(GprIndex::A7), - guest_s2 = const guest_gpr_offset(GprIndex::S2), - guest_s3 = const guest_gpr_offset(GprIndex::S3), - guest_s4 = const guest_gpr_offset(GprIndex::S4), - guest_s5 = const guest_gpr_offset(GprIndex::S5), - guest_s6 = const guest_gpr_offset(GprIndex::S6), - guest_s7 = const guest_gpr_offset(GprIndex::S7), - guest_s8 = const guest_gpr_offset(GprIndex::S8), - guest_s9 = const guest_gpr_offset(GprIndex::S9), - guest_s10 = const guest_gpr_offset(GprIndex::S10), - guest_s11 = const guest_gpr_offset(GprIndex::S11), - guest_t0 = const guest_gpr_offset(GprIndex::T0), - guest_t1 = const guest_gpr_offset(GprIndex::T1), - guest_t2 = const guest_gpr_offset(GprIndex::T2), - guest_t3 = const guest_gpr_offset(GprIndex::T3), - guest_t4 = const guest_gpr_offset(GprIndex::T4), - guest_t5 = const guest_gpr_offset(GprIndex::T5), - guest_t6 = const guest_gpr_offset(GprIndex::T6), - guest_sp = const guest_gpr_offset(GprIndex::SP), - - guest_sstatus = const guest_csr_offset!(sstatus), - guest_hstatus = const guest_csr_offset!(hstatus), - guest_scounteren = const guest_csr_offset!(scounteren), - guest_sepc = const guest_csr_offset!(sepc), - -); +use crate::consts::traps::irq::TIMER_IRQ_NUM; +use crate::irq; +use crate::regs::*; +use crate::sbi::{BaseFunction, PmuFunction, RemoteFenceFunction, SbiMessage}; +use crate::timers::register_timer; +use crate::timers::TimerEventFn; extern "C" { fn _run_guest(state: *mut VmCpuRegisters); @@ -209,7 +29,7 @@ pub struct RISCVVCpu { regs: VmCpuRegisters, } -impl axvcpu::AxArchVCpu for RISCVVCpu { +impl AxArchVCpu for RISCVVCpu { type CreateConfig = (); type SetupConfig = (); @@ -217,14 +37,14 @@ impl axvcpu::AxArchVCpu for RISCVVCpu { fn new(_config: Self::CreateConfig) -> AxResult { let mut regs = VmCpuRegisters::default(); // Set hstatus - let mut hstatus = LocalRegisterCopy::::new( - riscv::register::hstatus::read().bits(), - ); - hstatus.modify(hstatus::spv::Supervisor); + let mut hstatus = hstatus::read(); + hstatus.set_spv(true); // Set SPVP bit in order to accessing VS-mode memory from HS-mode. - hstatus.modify(hstatus::spvp::Supervisor); - CSR.hstatus.write_value(hstatus.get()); - regs.guest_regs.hstatus = hstatus.get(); + hstatus.set_spvp(true); + unsafe { + hstatus.write(); + } + regs.guest_regs.hstatus = hstatus.bits(); // Set sstatus let mut sstatus = sstatus::read(); @@ -232,6 +52,7 @@ impl axvcpu::AxArchVCpu for RISCVVCpu { regs.guest_regs.sstatus = sstatus.bits(); regs.guest_regs.gprs.set_reg(GprIndex::A0, 0); + // TODO:from _config regs.guest_regs.gprs.set_reg(GprIndex::A1, 0x9000_0000); Ok(Self { regs }) @@ -291,8 +112,7 @@ impl RISCVVCpu { self.regs.guest_regs.gprs.reg(index) } - /// Set one of the vCPU's general purpose register. - pub fn set_gpr_from_gpr_index(&mut self, index: GprIndex, val: usize) { + fn set_gpr_from_gpr_index(&mut self, index: GprIndex, val: usize) { self.regs.guest_regs.gprs.set_reg(index, val); } @@ -309,83 +129,102 @@ impl RISCVVCpu { impl RISCVVCpu { fn vmexit_handler(&mut self) -> AxResult { - self.regs.trap_csrs.scause = scause::read().bits(); - self.regs.trap_csrs.stval = stval::read(); - self.regs.trap_csrs.htval = htval::read(); - self.regs.trap_csrs.htinst = htinst::read(); - - let scause = scause::read(); + let scause = self.regs.trap_csrs.scause; use scause::{Exception, Interrupt, Trap}; - match scause.cause() { - Trap::Exception(Exception::VirtualSupervisorEnvCall) => { - let sbi_msg = SbiMessage::from_regs(self.regs.guest_regs.gprs.a_regs()).ok(); - if let Some(sbi_msg) = sbi_msg { - match sbi_msg { - SbiMessage::Base(base) => { - self.handle_base_function(base).unwrap(); - } - SbiMessage::GetChar => { - let c = sbi_rt::legacy::console_getchar(); - self.set_gpr_from_gpr_index(GprIndex::A0, c); - } - SbiMessage::PutChar(c) => { - sbi_rt::legacy::console_putchar(c); - } - SbiMessage::SetTimer(timer) => { - sbi_rt::set_timer(timer as u64); - // Clear guest timer interrupt - CSR.hvip - .read_and_clear_bits(traps::interrupt::VIRTUAL_SUPERVISOR_TIMER); - // Enable host timer interrupt - CSR.sie - .read_and_set_bits(traps::interrupt::SUPERVISOR_TIMER); - } - SbiMessage::Reset(_) => { - sbi_rt::system_reset(sbi_rt::Shutdown, sbi_rt::SystemFailure); - } - SbiMessage::RemoteFence(rfnc) => { - self.handle_rfnc_function(rfnc).unwrap(); - } - SbiMessage::PMU(pmu) => { - self.handle_pmu_function(pmu).unwrap(); - } - _ => todo!(), - } - self.advance_pc(4); + if scause.get_bit(size_of::() * 8 - 1) { + match Trap::Interrupt(Interrupt::from( + scause & !(1 << (size_of::() * 8 - 1)), + )) { + Trap::Interrupt(Interrupt::SupervisorTimer) => { + // info!("timer irq emulation"); + irq::handler_irq(TIMER_IRQ_NUM); Ok(AxVCpuExitReason::Nothing) - } else { - panic!() + } + Trap::Interrupt(Interrupt::SupervisorExternal) => { + Ok(AxVCpuExitReason::ExternalInterrupt { vector: 0 }) + } + _ => { + panic!( + "Unhandled trap: {:?}, sepc: {:#x}, stval: {:#x}", + Trap::Interrupt(Interrupt::from(scause)), + self.regs.guest_regs.sepc, + self.regs.trap_csrs.stval + ); } } - Trap::Interrupt(Interrupt::SupervisorTimer) => { - // debug!("timer irq emulation"); - // Enable guest timer interrupt - CSR.hvip - .read_and_set_bits(traps::interrupt::VIRTUAL_SUPERVISOR_TIMER); - // Clear host timer interrupt - CSR.sie - .read_and_clear_bits(traps::interrupt::SUPERVISOR_TIMER); - Ok(AxVCpuExitReason::Nothing) - } - Trap::Interrupt(Interrupt::SupervisorExternal) => { - Ok(AxVCpuExitReason::ExternalInterrupt { vector: 0 }) - } - Trap::Exception(Exception::LoadGuestPageFault) - | Trap::Exception(Exception::StoreGuestPageFault) => { - let fault_addr = self.regs.trap_csrs.htval << 2 | self.regs.trap_csrs.stval & 0x3; - Ok(AxVCpuExitReason::NestedPageFault { - addr: GuestPhysAddr::from(fault_addr), - access_flags: MappingFlags::empty(), - }) + } else { + match Trap::Exception(Exception::from( + scause & !(1 << (size_of::() * 8 - 1)), + )) { + Trap::Exception(Exception::VirtualSupervisorEnvCall) => self.handle_sbi_msg(), + Trap::Exception(Exception::LoadGuestPageFault) + | Trap::Exception(Exception::StoreGuestPageFault) => { + let fault_addr = + self.regs.trap_csrs.htval << 2 | self.regs.trap_csrs.stval & 0x3; + Ok(AxVCpuExitReason::NestedPageFault { + addr: GuestPhysAddr::from(fault_addr), + access_flags: MappingFlags::empty(), + }) + } + _ => { + panic!( + "Unhandled trap: {:?}, sepc: {:#x}, stval: {:#x}", + Trap::Exception(Exception::from(scause)), + self.regs.guest_regs.sepc, + self.regs.trap_csrs.stval + ); + } } - _ => { - panic!( - "Unhandled trap: {:?}, sepc: {:#x}, stval: {:#x}", - scause.cause(), - self.regs.guest_regs.sepc, - self.regs.trap_csrs.stval - ); + } + } + + fn handle_sbi_msg(&mut self) -> AxResult { + let sbi_msg = SbiMessage::from_regs(self.regs.guest_regs.gprs.a_regs()).ok(); + if let Some(sbi_msg) = sbi_msg { + match sbi_msg { + SbiMessage::Base(base) => { + self.handle_base_function(base)?; + } + SbiMessage::GetChar => { + let c = sbi_rt::legacy::console_getchar(); + self.set_gpr_from_gpr_index(GprIndex::A0, c); + } + SbiMessage::PutChar(c) => { + sbi_rt::legacy::console_putchar(c); + } + SbiMessage::SetTimer(timer) => { + // Clear guest timer interrupt + unsafe { + hvip::clear_vstip(); + } + + register_timer( + timer * 100, + TimerEventFn::new(|_now| unsafe { + hvip::set_vstip(); + }), + ); + } + SbiMessage::Reset(_) => { + sbi_rt::system_reset(sbi_rt::Shutdown, sbi_rt::SystemFailure); + } + SbiMessage::RemoteFence(rfnc) => { + self.handle_rfnc_function(rfnc)?; + } + SbiMessage::PMU(pmu) => { + self.handle_pmu_function(pmu)?; + } + _ => todo!(), } + self.advance_pc(4); + Ok(AxVCpuExitReason::Nothing) + } else { + panic!( + "Unhandled Trap: {:?}, sepc: {:#x}, stval: {:#x}", + scause::read().cause(), + self.regs.guest_regs.sepc, + self.regs.trap_csrs.stval + ); } } diff --git a/src/vmexit.rs b/src/vmexit.rs deleted file mode 100644 index e69de29..0000000