Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RISC-V 64 support for RVM #1

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open
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
16 changes: 11 additions & 5 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: CI

on: [push, pull_request]
on: [push, pull_request, workflow_dispatch]

jobs:
check:
Expand All @@ -10,7 +10,7 @@ jobs:
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly-2021-03-01
toolchain: nightly-2020-06-04
override: true
components: rustfmt, clippy
- name: Check code format
Expand All @@ -30,13 +30,19 @@ jobs:
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly-2021-03-01
toolchain: nightly-2020-06-04
components: rust-src
- name: Build
target: riscv64imac-unknown-none-elf
- name: Build x86_64
uses: actions-rs/cargo@v1
with:
command: build
args: --all-features
- name: Build RISC-V 64
uses: actions-rs/cargo@v1
with:
command: build
args: --target riscv64imac-unknown-none-elf --all-features
- name: Build example UEFI
run: cd examples/uefi && make build
- name: Build example KO
Expand All @@ -54,7 +60,7 @@ jobs:
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly-2021-03-01
toolchain: nightly-2020-06-04
components: rust-src
- name: Test
uses: actions-rs/cargo@v1
Expand Down
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,8 @@ rvm_macros = { path = "./rvm_macros" }

[target.'cfg(target_arch = "x86_64")'.dependencies]
x86 = "0.36"
x86_64 = "0.13.2"
x86_64 = "0.11.7"
raw-cpuid = "9.0"

[target.'cfg(any(target_arch = "riscv32", target_arch = "riscv64"))'.dependencies]
riscv = { git = "https://github.com/rcore-riscv-hypervisor-dev/riscv.git", rev = "3f5efb1", features = ["inline-asm", "hypervisor"] }
2 changes: 1 addition & 1 deletion rust-toolchain
Original file line number Diff line number Diff line change
@@ -1 +1 @@
nightly-2021-03-01
nightly-2020-06-04
6 changes: 4 additions & 2 deletions rvm_macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ use proc_macro::TokenStream;
use syn::{Abi, Attribute, ItemFn};

const ALLOWED_FN_LIST: &[&str] = &[
"alloc_frame",
"dealloc_frame",
"alloc_frames",
"dealloc_frames",
"phys_to_virt",
"is_host_timer_interrupt",
"is_host_serial_interrupt",
"riscv_check_hypervisor_extension",
"riscv_trap_handler_no_frame",
];

#[proc_macro_attribute]
Expand Down
8 changes: 8 additions & 0 deletions src/arch/aarch64/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#[repr(C)]
#[derive(Debug, Default)]
pub struct VcpuState {
pub x: [u64; 31],
pub sp: u64,
pub cpsr: u64,
pub _padding1: [u8; 4],
}
155 changes: 155 additions & 0 deletions src/arch/riscv/decode.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
//! Decode riscv instructions, only for load/store.
//! Note that only general registers are supported. No float registers.
use bit_field::*;
#[derive(Debug, Copy, Clone)]
pub struct MemOp {
pub access_size: u8,
pub is_store: bool,
pub src: u8, // for read.
pub dst: u8, // for write.
pub is_rvc: bool,
pub sign_extension: bool,
}
fn transform_general_reg(r: u8) -> u8 {
if r <= 7 {
return r + 8;
}
unreachable!()
}
enum RVLS {
Load { rd: u8, access_size: u8 },
Store { rs2: u8, access_size: u8 },
}
use RVLS::*;
fn parse_rvc_mem(insn: u16) -> Option<RVLS> {
let r = |hi: usize, lo: usize| insn.get_bits(lo..(hi + 1)) as usize;
if r(1, 0) == 0b10 {
let rd = r(11, 7) as u8;
//let rs1 = rd;
let rs2 = r(6, 2) as u8;

return match r(15, 13) {
0b010 if rd != 0 => Some(Load { rd, access_size: 4 }),
0b011 if rd != 0 => Some(Load { rd, access_size: 8 }),
0b110 => Some(Store {
rs2,
access_size: 4,
}),
0b111 => Some(Store {
rs2,
access_size: 8,
}),
_ => None,
};
}
if r(1, 0) == 0b00 {
//let rs1 = transform_general_reg(r(9,7) as u8);
let rs2 = transform_general_reg(r(4, 2) as u8);
//let rs = rs1;
let rd = rs2;
return match r(15, 13) {
0b010 => Some(Load { rd, access_size: 4 }),
0b011 => Some(Load { rd, access_size: 8 }),
0b110 => Some(Store {
rs2,
access_size: 4,
}),
0b111 => Some(Store {
rs2,
access_size: 8,
}),
_ => None,
};
}
None
}

// Some(Some(op)) for success, Some(None) for please-try-rvi, and None for unknown rvc.
pub fn decode_memory_ops_rvc(insn: u16) -> Option<Option<MemOp>> {
if insn.get_bits(0..2) == 0b00 {
match parse_rvc_mem(insn)? {
Load { rd, access_size } => Some(Some(MemOp {
is_store: false,
access_size,
dst: rd,
is_rvc: true,
src: 0,
sign_extension: true,
})),
Store { rs2, access_size } => Some(Some(MemOp {
is_store: true,
access_size,
src: rs2,
is_rvc: true,
dst: 0,
sign_extension: true,
})),
}
} else {
Some(None)
}
}

pub fn decode_memory_ops_rvi(insn: u32) -> Option<MemOp> {
let opcode = insn.get_bits(0..7);
//let funct = insn.get_bits(12..15);
//let rs1 = insn.get_bits(15..20);
let rd = insn.get_bits(7..12) as u8;
let rs2 = insn.get_bits(20..25) as u8;
let access_size = match insn.get_bits(12..14) {
0b00 => 1,
0b01 => 2,
0b10 => 4,
0b11 => 8,
_ => unreachable!(),
};
if opcode == 0b0000011 {
// load
let sign_extension = !insn.get_bit(14);
return Some(MemOp {
is_store: false,
access_size,
dst: rd,
is_rvc: false,
src: 0,
sign_extension,
});
} else if insn.get_bits(0..7) == 0b0100011 {
if !insn.get_bit(14) {
return Some(MemOp {
is_store: false,
access_size,
src: rs2,
is_rvc: false,
dst: 0,
sign_extension: false,
});
}
}
None
}

pub fn load_half(epc: usize) -> u16 {
(unsafe { riscv::asm::hlvx_hu(epc) }) as u16
}
pub fn load_word(epc: usize) -> u32 {
(load_half(epc) as u32) | ((load_half(epc + 2) as u32) << 16)
}

pub enum Insn {
C(u16),
I(u32),
}
pub use Insn::*;
pub fn read_instruction(epc: usize) -> Option<Insn> {
let insn_16 = load_half(epc);
if insn_16 & 0b11 == 0b00 {
Some(C(insn_16))
} else if insn_16 & 0b11 == 0b11 {
let insn_32 = load_word(epc);
Some(I(insn_32))
} else {
error!("bad instruction: at epc {:x}: {}", epc, insn_16);
None
}
}
135 changes: 135 additions & 0 deletions src/arch/riscv/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
mod ptx4;

mod decode;
mod pseudoinsn;
mod runvm;
pub mod sbi;
mod traps;
mod vcpu;
pub use runvm::{VMMContext, VMMContextPriv};
pub use vcpu::{InterruptState, Vcpu, VcpuState};

pub type ArchRvmPageTable = ptx4::PageTableSv48X4;
use crate::memory::{GuestPhysAddr, GuestPhysMemorySetTrait, HostPhysAddr};
use crate::trap_map::{RvmPort, TrapKind, TrapMap};
use crate::PAGE_SIZE;
use crate::{RvmError, RvmResult};
use alloc::sync::Arc;
use alloc::vec::Vec;
use riscv::register::*;
use spin::Mutex;
use spin::RwLock;
use traps::*;
pub fn check_hypervisor_feature() -> bool {
// RISC-V does now allow checking hypervisor feature directly.
// Instead, throw back the task to OS.
crate::ffi::riscv_check_hypervisor_extension()
}
use core::sync::atomic::*;
static VMID_ALLOCATOR: AtomicUsize = AtomicUsize::new(1);
static INITIALIZED: AtomicUsize = AtomicUsize::new(0);
pub struct Guest {
vmid: usize,
gpm: Arc<dyn GuestPhysMemorySetTrait>,
traps: Mutex<TrapMap>,
interrupt_handlers: RwLock<Vec<Arc<InterruptState>>>,
}
impl Guest {
pub fn get_irq_by_id(&self, cpuid: usize) -> Arc<InterruptState> {
Arc::clone(self.interrupt_handlers.read().get(cpuid).unwrap())
}
fn alloc_cpuid(&self) -> usize {
let mut irh = self.interrupt_handlers.write();
let new_id = irh.len();
irh.push(Arc::new(InterruptState::new()));
new_id
}
pub fn use_pt(&self) {
let mut val = hgatp::Hgatp::from_bits(0);
val.set_vmid(self.vmid);
val.set_ppn(self.rvm_page_table_phys() >> 12);
val.set_mode(hgatp::HgatpValues::Sv48x4);
unsafe {
val.write();
}
}
/// Create a new Guest.
pub fn new(gpm: Arc<dyn GuestPhysMemorySetTrait>) -> RvmResult<Arc<Self>> {
if INITIALIZED.swap(1, Ordering::Relaxed) == 0 {
init_traps();
}
Ok(Arc::new(Self {
vmid: VMID_ALLOCATOR.fetch_add(1, Ordering::Relaxed),
gpm,
traps: Mutex::new(TrapMap::default()),
interrupt_handlers: RwLock::default(),
}))
}

/// Get the page table base address.
pub(crate) fn rvm_page_table_phys(&self) -> usize {
self.gpm.table_phys()
}

pub fn add_memory_region(
&self,
gpaddr: GuestPhysAddr,
size: usize,
hpaddr: Option<HostPhysAddr>,
) -> RvmResult {
if gpaddr & (PAGE_SIZE - 1) != 0 || size & (PAGE_SIZE - 1) != 0 {
return Err(RvmError::InvalidParam);
}
if let Some(hpaddr) = hpaddr {
if hpaddr & (PAGE_SIZE - 1) != 0 {
return Err(RvmError::InvalidParam);
}
}
self.gpm.map(gpaddr, size, hpaddr)
}

pub fn set_trap(
&self,
kind: TrapKind,
addr: usize,
size: usize,
port: Option<Arc<dyn RvmPort>>,
key: u64,
) -> RvmResult {
if size == 0 {
return Err(RvmError::InvalidParam);
}
if addr > usize::MAX - size {
return Err(RvmError::OutOfRange);
}
match kind {
TrapKind::GuestTrapIo => {
return Err(RvmError::NotSupported);
}
TrapKind::GuestTrapBell => {
return Err(RvmError::NotSupported);
}
TrapKind::GuestTrapMem => {
if kind == TrapKind::GuestTrapBell && port.is_none() {
return Err(RvmError::InvalidParam);
}
if kind == TrapKind::GuestTrapMem && port.is_some() {
return Err(RvmError::InvalidParam);
}
if addr & (PAGE_SIZE - 1) != 0 || size & (PAGE_SIZE - 1) != 0 {
Err(RvmError::InvalidParam)
} else {
self.gpm.unmap(addr, size)?;
self.traps.lock().push(kind, addr, size, port, key)
}
}
_ => Err(RvmError::InvalidParam),
}
}
}

#[repr(C)]
#[derive(Debug)]
pub struct VcpuIo {
// ?
}
Loading