diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 5c64859..ed61ebf 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -33,6 +33,9 @@ jobs:
runs-on: ubuntu-latest
strategy:
fail-fast: false
+ matrix:
+ rust-toolchain: [nightly]
+ targets: [riscv64gc-unknown-none-elf]
permissions:
contents: write
env:
@@ -41,11 +44,15 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly
+ with:
+ toolchain: ${{ matrix.rust-toolchain }}
+ components: rust-src, clippy, rustfmt
+ targets: ${{ matrix.targets }}
- name: Build docs
continue-on-error: ${{ github.ref != env.default-branch && github.event_name != 'pull_request' }}
run: |
- cargo doc --no-deps --all-features
- printf '' $(cargo tree | head -1 | cut -d' ' -f1) > target/doc/index.html
+ cargo doc --no-deps --all-features --target ${{ matrix.targets }}
+ printf '' $(cargo tree | head -1 | cut -d' ' -f1) > target/${{ matrix.targets }}/doc/riscv_vcpu/index.html
- name: Deploy to Github Pages
if: ${{ github.ref == env.default-branch }}
uses: JamesIves/github-pages-deploy-action@v4
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 af920fb..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 })
@@ -278,6 +99,11 @@ impl axvcpu::AxArchVCpu for RISCVVCpu {
// unimplemented!()
Ok(())
}
+
+ /// Set one of the vCPU's general purpose register.
+ fn set_gpr(&mut self, index: usize, val: usize) {
+ self.set_gpr_from_gpr_index(GprIndex::from_raw(index as u32).unwrap(), val);
+ }
}
impl RISCVVCpu {
@@ -286,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(&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);
}
@@ -304,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(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
+ );
}
}
@@ -388,7 +232,7 @@ impl RISCVVCpu {
match base {
BaseFunction::GetSepcificationVersion => {
let version = sbi_rt::get_spec_version();
- self.set_gpr(GprIndex::A1, version.major() << 24 | version.minor());
+ self.set_gpr_from_gpr_index(GprIndex::A1, version.major() << 24 | version.minor());
debug!(
"GetSepcificationVersion: {}",
version.major() << 24 | version.minor()
@@ -396,43 +240,43 @@ impl RISCVVCpu {
}
BaseFunction::GetImplementationID => {
let id = sbi_rt::get_sbi_impl_id();
- self.set_gpr(GprIndex::A1, id);
+ self.set_gpr_from_gpr_index(GprIndex::A1, id);
}
BaseFunction::GetImplementationVersion => {
let impl_version = sbi_rt::get_sbi_impl_version();
- self.set_gpr(GprIndex::A1, impl_version);
+ self.set_gpr_from_gpr_index(GprIndex::A1, impl_version);
}
BaseFunction::ProbeSbiExtension(extension) => {
let extension = sbi_rt::probe_extension(extension as usize).raw;
- self.set_gpr(GprIndex::A1, extension);
+ self.set_gpr_from_gpr_index(GprIndex::A1, extension);
}
BaseFunction::GetMachineVendorID => {
let mvendorid = sbi_rt::get_mvendorid();
- self.set_gpr(GprIndex::A1, mvendorid);
+ self.set_gpr_from_gpr_index(GprIndex::A1, mvendorid);
}
BaseFunction::GetMachineArchitectureID => {
let marchid = sbi_rt::get_marchid();
- self.set_gpr(GprIndex::A1, marchid);
+ self.set_gpr_from_gpr_index(GprIndex::A1, marchid);
}
BaseFunction::GetMachineImplementationID => {
let mimpid = sbi_rt::get_mimpid();
- self.set_gpr(GprIndex::A1, mimpid);
+ self.set_gpr_from_gpr_index(GprIndex::A1, mimpid);
}
}
- self.set_gpr(GprIndex::A0, 0);
+ self.set_gpr_from_gpr_index(GprIndex::A0, 0);
Ok(())
}
fn handle_rfnc_function(&mut self, rfnc: RemoteFenceFunction) -> AxResult<()> {
- self.set_gpr(GprIndex::A0, 0);
+ self.set_gpr_from_gpr_index(GprIndex::A0, 0);
match rfnc {
RemoteFenceFunction::FenceI {
hart_mask,
hart_mask_base,
} => {
let sbi_ret = sbi_rt::remote_fence_i(hart_mask as usize, hart_mask_base as usize);
- self.set_gpr(GprIndex::A0, sbi_ret.error);
- self.set_gpr(GprIndex::A1, sbi_ret.value);
+ self.set_gpr_from_gpr_index(GprIndex::A0, sbi_ret.error);
+ self.set_gpr_from_gpr_index(GprIndex::A1, sbi_ret.value);
}
RemoteFenceFunction::RemoteSFenceVMA {
hart_mask,
@@ -446,21 +290,23 @@ impl RISCVVCpu {
start_addr as usize,
size as usize,
);
- self.set_gpr(GprIndex::A0, sbi_ret.error);
- self.set_gpr(GprIndex::A1, sbi_ret.value);
+ self.set_gpr_from_gpr_index(GprIndex::A0, sbi_ret.error);
+ self.set_gpr_from_gpr_index(GprIndex::A1, sbi_ret.value);
}
}
Ok(())
}
fn handle_pmu_function(&mut self, pmu: PmuFunction) -> AxResult<()> {
- self.set_gpr(GprIndex::A0, 0);
+ self.set_gpr_from_gpr_index(GprIndex::A0, 0);
match pmu {
- PmuFunction::GetNumCounters => self.set_gpr(GprIndex::A1, sbi_rt::pmu_num_counters()),
+ PmuFunction::GetNumCounters => {
+ self.set_gpr_from_gpr_index(GprIndex::A1, sbi_rt::pmu_num_counters())
+ }
PmuFunction::GetCounterInfo(counter_index) => {
let sbi_ret = pmu_counter_get_info(counter_index as usize);
- self.set_gpr(GprIndex::A0, sbi_ret.error);
- self.set_gpr(GprIndex::A1, sbi_ret.value);
+ self.set_gpr_from_gpr_index(GprIndex::A0, sbi_ret.error);
+ self.set_gpr_from_gpr_index(GprIndex::A1, sbi_ret.value);
}
PmuFunction::StopCounter {
counter_index,
@@ -472,8 +318,8 @@ impl RISCVVCpu {
counter_mask as usize,
stop_flags as usize,
);
- self.set_gpr(GprIndex::A0, sbi_ret.error);
- self.set_gpr(GprIndex::A1, sbi_ret.value);
+ self.set_gpr_from_gpr_index(GprIndex::A0, sbi_ret.error);
+ self.set_gpr_from_gpr_index(GprIndex::A1, sbi_ret.value);
}
}
Ok(())
diff --git a/src/vmexit.rs b/src/vmexit.rs
deleted file mode 100644
index e69de29..0000000