Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ bitflags = "2.2"
bit_field = "0.10"
crate_interface = "0.1"

riscv = "0.14.0"
riscv = { version = "0.14.0", features = ["s-mode"] }
riscv-h = "0.1"

riscv-decode = "0.2.3"
Expand Down
5 changes: 3 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ mod vcpu;
pub use self::percpu::RISCVPerCpu;
pub use self::vcpu::RISCVVCpu;
pub use detect::detect_h_extension as has_hardware_support;
pub use regs::GprIndex;

/// Extension ID for hypercall, defined by ourselves.
/// `0x48`, `0x56`, `0x43` is "HVC" in ASCII.
Expand All @@ -34,14 +35,14 @@ pub struct RISCVVCpuCreateConfig {
pub hart_id: usize,
/// The physical address of the device tree blob.
/// Default to `0x9000_0000`.
pub dtb_addr: axaddrspace::GuestPhysAddr,
pub dtb_addr: usize,
}

impl Default for RISCVVCpuCreateConfig {
fn default() -> Self {
Self {
hart_id: 0,
dtb_addr: axaddrspace::GuestPhysAddr::from_usize(0x9000_0000),
dtb_addr: 0x9000_0000,
}
}
}
63 changes: 61 additions & 2 deletions src/trap.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,67 @@
use crate::regs::*;
use core::mem::size_of;

use memoffset::offset_of;
use riscv::ExceptionNumber;
use riscv::result::{Error, Result};

use crate::regs::*;
/// Exception
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[repr(usize)]
pub enum Exception {
InstructionMisaligned = 0,
InstructionFault = 1,
IllegalInstruction = 2,
Breakpoint = 3,
LoadMisaligned = 4,
LoadFault = 5,
StoreMisaligned = 6,
StoreFault = 7,
UserEnvCall = 8,
SupervisorEnvCall = 9,
VirtualSupervisorEnvCall = 10,
InstructionPageFault = 12,
LoadPageFault = 13,
StorePageFault = 15,
InstructionGuestPageFault = 20,
LoadGuestPageFault = 21,
VirtualInstruction = 22,
StoreGuestPageFault = 23,
}

/// SAFETY: `Exception` represents the standard RISC-V exceptions
unsafe impl ExceptionNumber for Exception {
const MAX_EXCEPTION_NUMBER: usize = Self::StoreGuestPageFault as usize;

#[inline]
fn number(self) -> usize {
self as usize
}

#[inline]
fn from_number(value: usize) -> Result<Self> {
match value {
0 => Ok(Self::InstructionMisaligned),
1 => Ok(Self::InstructionFault),
2 => Ok(Self::IllegalInstruction),
3 => Ok(Self::Breakpoint),
4 => Ok(Self::LoadMisaligned),
5 => Ok(Self::LoadFault),
6 => Ok(Self::StoreMisaligned),
7 => Ok(Self::StoreFault),
8 => Ok(Self::UserEnvCall),
9 => Ok(Self::SupervisorEnvCall),
10 => Ok(Self::VirtualSupervisorEnvCall),
12 => Ok(Self::InstructionPageFault),
13 => Ok(Self::LoadPageFault),
15 => Ok(Self::StorePageFault),
20 => Ok(Self::InstructionGuestPageFault),
21 => Ok(Self::LoadGuestPageFault),
22 => Ok(Self::VirtualInstruction),
23 => Ok(Self::StoreGuestPageFault),
_ => Err(Error::InvalidVariant(value)),
}
}
}

#[allow(dead_code)]
const fn hyp_gpr_offset(index: GprIndex) -> usize {
Expand Down
143 changes: 121 additions & 22 deletions src/vcpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,23 @@ use riscv_decode::{
Instruction,
types::{IType, SType},
};
use riscv_h::register::{hstatus, hvip};
use riscv_h::register::{
hstatus, htimedelta, hvip,
vsatp::{self, Vsatp},
vscause::{self, Vscause},
vsepc,
vsie::{self, Vsie},
vsscratch,
vsstatus::{self, Vsstatus},
vstval,
vstvec::{self, Vstvec},
};
use rustsbi::{Forward, RustSBI};
use sbi_spec::{hsm, legacy};
use sbi_spec::{hsm, legacy, srst};

use crate::{EID_HVC, RISCVVCpuCreateConfig, guest_mem, regs::*, sbi_console::*};
use crate::{
EID_HVC, RISCVVCpuCreateConfig, consts::traps::irq::S_EXT, guest_mem, regs::*, sbi_console::*,
};

use axaddrspace::{GuestPhysAddr, GuestVirtAddr, HostPhysAddr, MappingFlags, device::AccessWidth};
use axerrno::{AxError::InvalidData, AxResult};
Expand All @@ -17,6 +29,14 @@ unsafe extern "C" {
fn _run_guest(state: *mut VmCpuRegisters);
}

const TINST_PSEUDO_STORE: u32 = 0x3020;
const TINST_PSEUDO_LOAD: u32 = 0x3000;

#[inline]
fn instr_is_pseudo(ins: u32) -> bool {
ins == TINST_PSEUDO_STORE || ins == TINST_PSEUDO_LOAD
}

/// The architecture dependent configuration of a `AxArchVCpu`.
#[derive(Clone, Copy, Debug, Default)]
pub struct VCpuConfig {}
Expand Down Expand Up @@ -53,9 +73,7 @@ impl<H: AxVCpuHal> axvcpu::AxArchVCpu for RISCVVCpu<H> {
// `a0` is the hartid
regs.guest_regs.gprs.set_reg(GprIndex::A0, config.hart_id);
// `a1` is the address of the device tree blob.
regs.guest_regs
.gprs
.set_reg(GprIndex::A1, config.dtb_addr.as_usize());
regs.guest_regs.gprs.set_reg(GprIndex::A1, config.dtb_addr);

Ok(Self {
regs,
Expand All @@ -67,12 +85,15 @@ impl<H: AxVCpuHal> axvcpu::AxArchVCpu for RISCVVCpu<H> {
fn setup(&mut self, _config: Self::SetupConfig) -> AxResult {
// Set sstatus.
let mut sstatus = sstatus::read();
sstatus.set_sie(false);
sstatus.set_spie(false);
sstatus.set_spp(sstatus::SPP::Supervisor);
self.regs.guest_regs.sstatus = sstatus.bits();

// Set hstatus.
let mut hstatus = hstatus::read();
hstatus.set_spv(true);
hstatus.set_vsxl(hstatus::VsxlValues::Vsxl64);
// Set SPVP bit in order to accessing VS-mode memory from HS-mode.
hstatus.set_spvp(true);
unsafe {
Expand Down Expand Up @@ -114,7 +135,26 @@ impl<H: AxVCpuHal> axvcpu::AxArchVCpu for RISCVVCpu<H> {
}

fn bind(&mut self) -> AxResult {
// Load the vCPU's CSRs from the stored state.
unsafe {
let vsatp = Vsatp::from_bits(self.regs.vs_csrs.vsatp);
vsatp.write();
let vstvec = Vstvec::from_bits(self.regs.vs_csrs.vstvec);
vstvec.write();
let vsepc = self.regs.vs_csrs.vsepc;
vsepc::write(vsepc);
let vstval = self.regs.vs_csrs.vstval;
vstval::write(vstval);
let htimedelta = self.regs.vs_csrs.htimedelta;
htimedelta::write(htimedelta);
let vscause = Vscause::from_bits(self.regs.vs_csrs.vscause);
vscause.write();
let vsscratch = self.regs.vs_csrs.vsscratch;
vsscratch::write(vsscratch);
let vsstatus = Vsstatus::from_bits(self.regs.vs_csrs.vsstatus);
vsstatus.write();
let vsie = Vsie::from_bits(self.regs.vs_csrs.vsie);
vsie.write();
core::arch::asm!(
"csrw hgatp, {hgatp}",
hgatp = in(reg) self.regs.virtual_hs_csrs.hgatp,
Expand All @@ -125,14 +165,35 @@ impl<H: AxVCpuHal> axvcpu::AxArchVCpu for RISCVVCpu<H> {
}

fn unbind(&mut self) -> AxResult {
// Store the vCPU's CSRs to the stored state.
unsafe {
self.regs.vs_csrs.vsatp = vsatp::read().bits();
self.regs.vs_csrs.vstvec = vstvec::read().bits();
self.regs.vs_csrs.vsepc = vsepc::read();
self.regs.vs_csrs.vstval = vstval::read();
self.regs.vs_csrs.htimedelta = htimedelta::read();
self.regs.vs_csrs.vscause = vscause::read().bits();
self.regs.vs_csrs.vsscratch = vsscratch::read();
self.regs.vs_csrs.vsstatus = vsstatus::read().bits();
self.regs.vs_csrs.vsie = vsie::read().bits();
core::arch::asm!(
"csrr {hgatp}, hgatp",
hgatp = out(reg) self.regs.virtual_hs_csrs.hgatp,
);
core::arch::asm!("csrw hgatp, x0");
core::arch::riscv64::hfence_gvma_all();
}
Ok(())
}

/// Set one of the vCPU's general purpose register.
fn set_gpr(&mut self, index: usize, val: usize) {
match index {
0..=7 => {
self.set_gpr_from_gpr_index(GprIndex::from_raw(index as u32 + 10).unwrap(), val);
0 => {
// Do nothing, x0 is hardwired to zero
}
1..=31 => {
self.set_gpr_from_gpr_index(GprIndex::from_raw(index as u32).unwrap(), val);
}
_ => {
warn!(
Expand Down Expand Up @@ -179,7 +240,8 @@ impl<H: AxVCpuHal> RISCVVCpu<H> {
self.regs.trap_csrs.load_from_hw();

let scause = scause::read();
use riscv::interrupt::{Exception, Interrupt, Trap};
use super::trap::Exception;
use riscv::interrupt::{Interrupt, Trap};

trace!(
"vmexit_handler: {:?}, sepc: {:#x}, stval: {:#x}",
Expand All @@ -189,13 +251,13 @@ impl<H: AxVCpuHal> RISCVVCpu<H> {
);

// Try to convert the raw trap cause to a standard RISC-V trap cause.
let trap = scause.cause().try_into().map_err(|_| {
let trap: Trap<Interrupt, Exception> = scause.cause().try_into().map_err(|_| {
error!("Unknown trap cause: scause={:#x}", scause.bits());
InvalidData
})?;

match trap {
Trap::Exception(Exception::SupervisorEnvCall) => {
Trap::Exception(Exception::VirtualSupervisorEnvCall) => {
let a = self.regs.guest_regs.gprs.a_regs();
let param = [a[0], a[1], a[2], a[3], a[4], a[5]];
let extension_id = a[7];
Expand Down Expand Up @@ -348,6 +410,21 @@ impl<H: AxVCpuHal> RISCVVCpu<H> {
return Ok(AxVCpuExitReason::Nothing);
}
},
srst::EID_SRST => match function_id {
srst::SYSTEM_RESET => {
let reset_type = param[0];
if reset_type == srst::RESET_TYPE_SHUTDOWN as _ {
// Shutdown the system.
return Ok(AxVCpuExitReason::SystemDown);
} else {
unimplemented!("Unsupported reset type {}", reset_type);
}
}
_ => {
self.sbi_return(RET_ERR_NOT_SUPPORTED, 0);
return Ok(AxVCpuExitReason::Nothing);
}
},
// By default, forward the SBI call to the RustSBI implementation.
// See [`RISCVVCpuSbi`].
_ => {
Expand Down Expand Up @@ -381,11 +458,11 @@ impl<H: AxVCpuHal> RISCVVCpu<H> {
// It's a great fault in the `riscv` crate that `Interrupt` and `Exception` are not
// explicitly numbered, and they provide no way to convert them to a number. Also,
// `as usize` will give use a wrong value.
Ok(AxVCpuExitReason::ExternalInterrupt { vector: 9 })
}
Trap::Exception(gpf @ (Exception::LoadPageFault | Exception::StorePageFault)) => {
self.handle_guest_page_fault(gpf == Exception::StorePageFault)
Ok(AxVCpuExitReason::ExternalInterrupt { vector: S_EXT as _ })
}
Trap::Exception(
gpf @ (Exception::LoadGuestPageFault | Exception::StoreGuestPageFault),
) => self.handle_guest_page_fault(gpf == Exception::StoreGuestPageFault),
_ => {
panic!(
"Unhandled trap: {:?}, sepc: {:#x}, stval: {:#x}",
Expand All @@ -409,13 +486,35 @@ impl<H: AxVCpuHal> RISCVVCpu<H> {
fn decode_instr_at(&self, vaddr: GuestVirtAddr) -> AxResult<(Instruction, usize)> {
// The htinst CSR contains "transformed instruction" that caused the page fault. We
// can use it but we use the sepc to fetch the original instruction instead for now.
let instr = guest_mem::fetch_guest_instruction(vaddr);
let instr_len = riscv_decode::instruction_length(instr as u16);
let instr = match instr_len {
2 => instr & 0xffff,
4 => instr,
_ => unreachable!("Unsupported instruction length: {}", instr_len),
};
let mut instr = riscv_h::register::htinst::read();
let mut instr_len = 0;
if instr == 0 {
// Read the instruction from guest memory.
instr = guest_mem::fetch_guest_instruction(vaddr) as _;
instr_len = riscv_decode::instruction_length(instr as u16);
instr = match instr_len {
2 => instr & 0xffff,
4 => instr,
_ => unreachable!("Unsupported instruction length: {}", instr_len),
};
} else if instr_is_pseudo(instr as u32) {
error!("fault on 1st stage page table walk");
return Err(axerrno::ax_err_type!(
Unsupported,
"risc-v vcpu guest page fault handler encountered pseudo instruction"
));
} else {
// Transform htinst value to standard instruction.
// According to RISC-V Spec:
// Bits 1:0 of a transformed standard instruction will be binary 01 if
// the trapping instruction is compressed and 11 if not.
instr_len = match (instr as u16) & 0x3 {
0x1 => 2,
0x3 => 4,
_ => unreachable!("Unsupported instruction length"),
};
instr |= 0x2;
}

riscv_decode::decode(instr as u32)
.map_err(|_| {
Expand Down