Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
150 changes: 128 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,42 @@ 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 => {
if let Some(gpr_index) = GprIndex::from_raw(index as u32) {
self.set_gpr_from_gpr_index(gpr_index, val);
} else {
warn!(
"RISCVVCpu: Failed to map general purpose register index: {}",
index
);
}
}
_ => {
warn!(
Expand Down Expand Up @@ -179,7 +247,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 +258,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 +417,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 +465,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 +493,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