From f2a97077ca2497aac7988494987fe84a08d576fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Sch=C3=B6ning?= Date: Thu, 9 Dec 2021 20:15:35 +0100 Subject: [PATCH] Add riscv support Add support for riscv KVM and hugetlbfs. Using hugetlbfs instead of transparent huge pages can increase the performance. --- Cargo.lock | 8 +- Cargo.toml | 11 +- src/bin/uhyve.rs | 15 +- src/linux/arch/mod.rs | 11 ++ src/linux/arch/riscv/consts.rs | 110 ++++++++++++ src/linux/arch/riscv/mod.rs | 3 + src/linux/arch/riscv/vcpu.rs | 261 ++++++++++++++++++++++++++++ src/linux/arch/x86_64/mod.rs | 2 + src/linux/{ => arch/x86_64}/vcpu.rs | 0 src/linux/mod.rs | 21 ++- src/linux/uhyve.rs | 224 +++++++++++++++++------- src/macos/uhyve.rs | 4 + src/utils.rs | 2 +- src/vm.rs | 80 ++++++++- 14 files changed, 675 insertions(+), 77 deletions(-) create mode 100644 src/linux/arch/mod.rs create mode 100644 src/linux/arch/riscv/consts.rs create mode 100644 src/linux/arch/riscv/mod.rs create mode 100755 src/linux/arch/riscv/vcpu.rs create mode 100644 src/linux/arch/x86_64/mod.rs rename src/linux/{ => arch/x86_64}/vcpu.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 77412a8d..636f0f68 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -474,17 +474,15 @@ dependencies = [ [[package]] name = "kvm-bindings" version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a78c049190826fff959994b7c1d8a2930d0a348f1b8f3aa4f9bb34cd5d7f2952" +source = "git+https://github.com/simonschoening/kvm-bindings?branch=riscv#d2632e55fc340239eca64f1605bf851652dab12a" dependencies = [ "vmm-sys-util", ] [[package]] name = "kvm-ioctls" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48dc14f9047df1873cf6942caccc7431d19c3d496ca7a0d162260c4cf0f64b76" +version = "0.11.0" +source = "git+https://github.com/simonschoening/kvm-ioctls?branch=riscv#94985ccca7cfa479c753af4979c59aa873f53577" dependencies = [ "kvm-bindings", "libc", diff --git a/Cargo.toml b/Cargo.toml index 121fc36b..52303586 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,8 @@ instrument = ["rftrace", "rftrace-frontend"] gdbstub = { git = "https://github.com/daniel5151/gdbstub", branch = "dev/0.6" } gdbstub_arch = { git = "https://github.com/daniel5151/gdbstub", branch = "dev/0.6" } x86_64 = { git = "https://github.com/mkroening/x86_64", branch = "debug-stable" } +kvm-bindings = { git = "https://github.com/simonschoening/kvm-bindings", branch = "riscv" } +kvm-ioctls = { git = "https://github.com/simonschoening/kvm-ioctls", branch = "riscv" } [dependencies] bitflags = "1.3" @@ -47,21 +49,19 @@ env_logger = "0.9" envmnt = "0.9" gdbstub = "0.5" gdbstub_arch = "0.1.0" -goblin = { version = "0.4", default-features = false, features = ["elf64", "elf32", "endian_fd", "std"] } +goblin = { version = "0.4.3", default-features = false, features = ["elf64", "elf32", "endian_fd", "std"] } lazy_static = "1.4" libc = "0.2" log = "0.4" -raw-cpuid = "10.2" rustc-serialize = "0.3" thiserror = "1.0" -x86_64 = "0.14" rftrace = { version = "0.1", optional = true } rftrace-frontend = { version = "0.1", optional = true } [target.'cfg(target_os = "linux")'.dependencies] kvm-bindings = "0.5" -kvm-ioctls = "0.10" +kvm-ioctls = "0.11.0" mac_address = "1.1" nix = "0.23" tun-tap = { version = "0.1", default-features = false } @@ -75,6 +75,9 @@ xhypervisor = "0.0" [target.'cfg(target_arch = "x86_64")'.dependencies] x86_64 = { version = "0.14", default-features = false } +[target.'cfg(target_arch = "x86_64")'.dependencies.raw-cpuid] +version = "10.2.0" + [target.'cfg(all(target_arch = "x86_64", target_os = "macos"))'.dependencies] x86 = { version = "0.43", default-features = false } diff --git a/src/bin/uhyve.rs b/src/bin/uhyve.rs index b4247f5c..02300e9a 100644 --- a/src/bin/uhyve.rs +++ b/src/bin/uhyve.rs @@ -69,7 +69,7 @@ fn main() { .arg( Arg::with_name("DISABLE_HUGEPAGE") .long("disable-hugepages") - .help("Disable the usage of huge pages"), + .help("Disable the usage of transparent huge pages"), ) .arg( Arg::with_name("MERGEABLE") @@ -116,6 +116,13 @@ fn main() { .takes_value(true) .env("HERMIT_GDB_PORT"), ) + .arg( + Arg::with_name("HUGETLBFS") + .long("hugetlbfs") + .value_name("HUGETLBFS") + .help("Hugetlbfs path") + .takes_value(true), + ) .arg( Arg::with_name("NETIF") .long("nic") @@ -241,6 +248,7 @@ fn main() { if matches.is_present("VERBOSE") { verbose = true; } + #[cfg(target_arch = "x86_64")] let gdbport = matches .value_of("GDB_PORT") .map(|p| p.parse::().expect("Could not parse gdb port")) @@ -249,6 +257,10 @@ fn main() { .ok() .map(|p| p.parse::().expect("Could not parse gdb port")) }); + #[cfg(target_arch = "riscv64")] + let gdbport = None; + + let hugetlbfs_path = matches.value_of("HUGETLBFS"); let params = vm::Parameter { mem_size, @@ -256,6 +268,7 @@ fn main() { verbose, hugepage, mergeable, + hugetlbfs_path, ip, gateway, mask, diff --git a/src/linux/arch/mod.rs b/src/linux/arch/mod.rs new file mode 100644 index 00000000..10c5cad4 --- /dev/null +++ b/src/linux/arch/mod.rs @@ -0,0 +1,11 @@ +#[cfg(target_arch = "x86_64")] +pub mod x86_64; + +#[cfg(target_arch = "riscv64")] +pub mod riscv; + +#[cfg(target_arch = "x86_64")] +pub use self::x86_64::vcpu; + +#[cfg(target_arch = "riscv64")] +pub use self::riscv::vcpu; diff --git a/src/linux/arch/riscv/consts.rs b/src/linux/arch/riscv/consts.rs new file mode 100644 index 00000000..3f371274 --- /dev/null +++ b/src/linux/arch/riscv/consts.rs @@ -0,0 +1,110 @@ +use kvm_bindings::*; + +pub const SBI_CONSOLE_PUTCHAR: u64 = 0x01; +pub const SBI_CONSOLE_GETCHAR: u64 = 0x02; + +#[cfg(target_arch = "riscv32")] +const KVM_REG_SIZE_ULONG: u64 = KVM_REG_SIZE_U32; + +#[cfg(target_arch = "riscv64")] +const KVM_REG_SIZE_ULONG: u64 = KVM_REG_SIZE_U64; + +pub const KVM_REG_RISCV_CONFIG_ISA: u64 = + KVM_REG_RISCV as u64 | KVM_REG_RISCV_CONFIG as u64 | KVM_REG_SIZE_ULONG | 0x00; + +pub const KVM_REG_RISCV_CORE_PC: u64 = + KVM_REG_RISCV as u64 | KVM_REG_RISCV_CORE as u64 | KVM_REG_SIZE_ULONG | 0x00; +pub const KVM_REG_RISCV_CORE_RA: u64 = + KVM_REG_RISCV as u64 | KVM_REG_RISCV_CORE as u64 | KVM_REG_SIZE_ULONG | 0x01; +pub const KVM_REG_RISCV_CORE_SP: u64 = + KVM_REG_RISCV as u64 | KVM_REG_RISCV_CORE as u64 | KVM_REG_SIZE_ULONG | 0x02; +pub const KVM_REG_RISCV_CORE_GP: u64 = + KVM_REG_RISCV as u64 | KVM_REG_RISCV_CORE as u64 | KVM_REG_SIZE_ULONG | 0x03; +pub const KVM_REG_RISCV_CORE_TP: u64 = + KVM_REG_RISCV as u64 | KVM_REG_RISCV_CORE as u64 | KVM_REG_SIZE_ULONG | 0x04; +pub const KVM_REG_RISCV_CORE_T0: u64 = + KVM_REG_RISCV as u64 | KVM_REG_RISCV_CORE as u64 | KVM_REG_SIZE_ULONG | 0x05; +pub const KVM_REG_RISCV_CORE_T1: u64 = + KVM_REG_RISCV as u64 | KVM_REG_RISCV_CORE as u64 | KVM_REG_SIZE_ULONG | 0x06; +pub const KVM_REG_RISCV_CORE_T2: u64 = + KVM_REG_RISCV as u64 | KVM_REG_RISCV_CORE as u64 | KVM_REG_SIZE_ULONG | 0x07; +pub const KVM_REG_RISCV_CORE_S0: u64 = + KVM_REG_RISCV as u64 | KVM_REG_RISCV_CORE as u64 | KVM_REG_SIZE_ULONG | 0x08; +pub const KVM_REG_RISCV_CORE_S1: u64 = + KVM_REG_RISCV as u64 | KVM_REG_RISCV_CORE as u64 | KVM_REG_SIZE_ULONG | 0x09; +pub const KVM_REG_RISCV_CORE_A0: u64 = + KVM_REG_RISCV as u64 | KVM_REG_RISCV_CORE as u64 | KVM_REG_SIZE_ULONG | 0x0a; +pub const KVM_REG_RISCV_CORE_A1: u64 = + KVM_REG_RISCV as u64 | KVM_REG_RISCV_CORE as u64 | KVM_REG_SIZE_ULONG | 0x0b; +pub const KVM_REG_RISCV_CORE_A2: u64 = + KVM_REG_RISCV as u64 | KVM_REG_RISCV_CORE as u64 | KVM_REG_SIZE_ULONG | 0x0c; +pub const KVM_REG_RISCV_CORE_A3: u64 = + KVM_REG_RISCV as u64 | KVM_REG_RISCV_CORE as u64 | KVM_REG_SIZE_ULONG | 0x0d; +pub const KVM_REG_RISCV_CORE_A4: u64 = + KVM_REG_RISCV as u64 | KVM_REG_RISCV_CORE as u64 | KVM_REG_SIZE_ULONG | 0x0e; +pub const KVM_REG_RISCV_CORE_A5: u64 = + KVM_REG_RISCV as u64 | KVM_REG_RISCV_CORE as u64 | KVM_REG_SIZE_ULONG | 0x0f; +pub const KVM_REG_RISCV_CORE_A6: u64 = + KVM_REG_RISCV as u64 | KVM_REG_RISCV_CORE as u64 | KVM_REG_SIZE_ULONG | 0x10; +pub const KVM_REG_RISCV_CORE_A7: u64 = + KVM_REG_RISCV as u64 | KVM_REG_RISCV_CORE as u64 | KVM_REG_SIZE_ULONG | 0x11; +pub const KVM_REG_RISCV_CORE_S2: u64 = + KVM_REG_RISCV as u64 | KVM_REG_RISCV_CORE as u64 | KVM_REG_SIZE_ULONG | 0x12; +pub const KVM_REG_RISCV_CORE_S3: u64 = + KVM_REG_RISCV as u64 | KVM_REG_RISCV_CORE as u64 | KVM_REG_SIZE_ULONG | 0x13; +pub const KVM_REG_RISCV_CORE_S4: u64 = + KVM_REG_RISCV as u64 | KVM_REG_RISCV_CORE as u64 | KVM_REG_SIZE_ULONG | 0x14; +pub const KVM_REG_RISCV_CORE_S5: u64 = + KVM_REG_RISCV as u64 | KVM_REG_RISCV_CORE as u64 | KVM_REG_SIZE_ULONG | 0x15; +pub const KVM_REG_RISCV_CORE_S6: u64 = + KVM_REG_RISCV as u64 | KVM_REG_RISCV_CORE as u64 | KVM_REG_SIZE_ULONG | 0x16; +pub const KVM_REG_RISCV_CORE_S7: u64 = + KVM_REG_RISCV as u64 | KVM_REG_RISCV_CORE as u64 | KVM_REG_SIZE_ULONG | 0x17; +pub const KVM_REG_RISCV_CORE_S8: u64 = + KVM_REG_RISCV as u64 | KVM_REG_RISCV_CORE as u64 | KVM_REG_SIZE_ULONG | 0x18; +pub const KVM_REG_RISCV_CORE_S9: u64 = + KVM_REG_RISCV as u64 | KVM_REG_RISCV_CORE as u64 | KVM_REG_SIZE_ULONG | 0x19; +pub const KVM_REG_RISCV_CORE_S10: u64 = + KVM_REG_RISCV as u64 | KVM_REG_RISCV_CORE as u64 | KVM_REG_SIZE_ULONG | 0x1a; +pub const KVM_REG_RISCV_CORE_S11: u64 = + KVM_REG_RISCV as u64 | KVM_REG_RISCV_CORE as u64 | KVM_REG_SIZE_ULONG | 0x1b; +pub const KVM_REG_RISCV_CORE_T3: u64 = + KVM_REG_RISCV as u64 | KVM_REG_RISCV_CORE as u64 | KVM_REG_SIZE_ULONG | 0x1c; +pub const KVM_REG_RISCV_CORE_T4: u64 = + KVM_REG_RISCV as u64 | KVM_REG_RISCV_CORE as u64 | KVM_REG_SIZE_ULONG | 0x1d; +pub const KVM_REG_RISCV_CORE_T5: u64 = + KVM_REG_RISCV as u64 | KVM_REG_RISCV_CORE as u64 | KVM_REG_SIZE_ULONG | 0x1e; +pub const KVM_REG_RISCV_CORE_T6: u64 = + KVM_REG_RISCV as u64 | KVM_REG_RISCV_CORE as u64 | KVM_REG_SIZE_ULONG | 0x1f; +pub const KVM_REG_RISCV_CORE_MODE: u64 = + KVM_REG_RISCV as u64 | KVM_REG_RISCV_CORE as u64 | KVM_REG_SIZE_ULONG | 0x20; + +pub const KVM_REG_RISCV_CSR_SSTATUS: u64 = + KVM_REG_RISCV as u64 | KVM_REG_RISCV_CSR as u64 | KVM_REG_SIZE_ULONG | 0x00; +pub const KVM_REG_RISCV_CSR_SIE: u64 = + KVM_REG_RISCV as u64 | KVM_REG_RISCV_CSR as u64 | KVM_REG_SIZE_ULONG | 0x01; +pub const KVM_REG_RISCV_CSR_STVEC: u64 = + KVM_REG_RISCV as u64 | KVM_REG_RISCV_CSR as u64 | KVM_REG_SIZE_ULONG | 0x02; +pub const KVM_REG_RISCV_CSR_SSCRATCH: u64 = + KVM_REG_RISCV as u64 | KVM_REG_RISCV_CSR as u64 | KVM_REG_SIZE_ULONG | 0x03; +pub const KVM_REG_RISCV_CSR_SEPC: u64 = + KVM_REG_RISCV as u64 | KVM_REG_RISCV_CSR as u64 | KVM_REG_SIZE_ULONG | 0x04; +pub const KVM_REG_RISCV_CSR_SCAUSE: u64 = + KVM_REG_RISCV as u64 | KVM_REG_RISCV_CSR as u64 | KVM_REG_SIZE_ULONG | 0x05; +pub const KVM_REG_RISCV_CSR_STVAL: u64 = + KVM_REG_RISCV as u64 | KVM_REG_RISCV_CSR as u64 | KVM_REG_SIZE_ULONG | 0x06; +pub const KVM_REG_RISCV_CSR_SIP: u64 = + KVM_REG_RISCV as u64 | KVM_REG_RISCV_CSR as u64 | KVM_REG_SIZE_ULONG | 0x07; +pub const KVM_REG_RISCV_CSR_SATP: u64 = + KVM_REG_RISCV as u64 | KVM_REG_RISCV_CSR as u64 | KVM_REG_SIZE_ULONG | 0x08; + +pub const KVM_REG_RISCV_TIMER_FREQUENCY: u64 = + KVM_REG_RISCV as u64 | KVM_REG_RISCV_TIMER as u64 | KVM_REG_SIZE_U64 | 0x00; +pub const KVM_REG_RISCV_TIMER_TIME: u64 = + KVM_REG_RISCV as u64 | KVM_REG_RISCV_TIMER as u64 | KVM_REG_SIZE_U64 | 0x01; +pub const KVM_REG_RISCV_TIMER_COMPARE: u64 = + KVM_REG_RISCV as u64 | KVM_REG_RISCV_TIMER as u64 | KVM_REG_SIZE_U64 | 0x02; +pub const KVM_REG_RISCV_TIMER_STATE: u64 = + KVM_REG_RISCV as u64 | KVM_REG_RISCV_TIMER as u64 | KVM_REG_SIZE_U64 | 0x03; + +//TODO: RISC-V F-extension registers, RISC-V D-extension registers diff --git a/src/linux/arch/riscv/mod.rs b/src/linux/arch/riscv/mod.rs new file mode 100644 index 00000000..7c2046f5 --- /dev/null +++ b/src/linux/arch/riscv/mod.rs @@ -0,0 +1,3 @@ +pub mod consts; +/// Arch riscv +pub mod vcpu; diff --git a/src/linux/arch/riscv/vcpu.rs b/src/linux/arch/riscv/vcpu.rs new file mode 100755 index 00000000..87e90cc3 --- /dev/null +++ b/src/linux/arch/riscv/vcpu.rs @@ -0,0 +1,261 @@ +use crate::consts::*; +use crate::linux::arch::riscv::consts::*; +use crate::vm::HypervisorResult; +use crate::vm::VcpuStopReason; +use crate::vm::{BootInfo, VirtualCPU}; +use kvm_bindings::*; +use kvm_ioctls::{VcpuExit, VcpuFd}; +use std::cell::RefCell; +use std::path::Path; +use std::path::PathBuf; +use std::ptr::write; +use std::slice; + +thread_local!(static SBI_UTF_BUFFER: RefCell> = RefCell::new(Vec::new())); +pub struct UhyveCPU { + id: u32, + vcpu: VcpuFd, + vm_start: usize, + kernel_path: PathBuf, +} + +impl UhyveCPU { + pub unsafe fn memory(&mut self, start_addr: u64, len: usize) -> &mut [u8] { + let host = self.host_address(start_addr as usize); + slice::from_raw_parts_mut(host as *mut u8, len) + } + + pub fn new(id: u32, kernel_path: PathBuf, vcpu: VcpuFd, vm_start: usize) -> UhyveCPU { + UhyveCPU { + id, + vcpu, + vm_start, + kernel_path, + } + } + + pub fn get_vcpu(&self) -> &VcpuFd { + &self.vcpu + } + + pub fn get_vcpu_mut(&mut self) -> &mut VcpuFd { + &mut self.vcpu + } +} + +impl VirtualCPU for UhyveCPU { + fn init(&mut self, entry_point: u64, boot_info: *const BootInfo) -> HypervisorResult<()> { + // be sure that the multiprocessor is runable + let mp_state = kvm_mp_state { + mp_state: KVM_MP_STATE_RUNNABLE, + }; + + self.vcpu.set_mp_state(mp_state)?; + + self.vcpu + .set_one_reg(KVM_REG_RISCV_CORE_PC, entry_point) + .expect("Failed to set pc register"); + + let isa = self + .vcpu + .get_one_reg(KVM_REG_RISCV_CONFIG_ISA) + .expect("Failed to read ISA!"); + debug!("Detected ISA {:X}", isa); + let timebase_freq = self + .vcpu + .get_one_reg(KVM_REG_RISCV_TIMER_FREQUENCY) + .expect("Failed to read timebase freq!"); + debug!("Detected a timebase frequency of {} Hz", timebase_freq); + unsafe { + write( + &mut (*(boot_info as *mut BootInfo)).timebase_freq, + timebase_freq, + ) + }; + + self.vcpu + .set_one_reg(KVM_REG_RISCV_CORE_A0, self.id as u64) + .expect("Failed to set a0 register"); + + self.vcpu + .set_one_reg(KVM_REG_RISCV_CORE_A1, BOOT_INFO_ADDR) + .expect("Failed to set a1 register"); + + Ok(()) + } + + fn kernel_path(&self) -> &Path { + self.kernel_path.as_path() + } + + fn host_address(&self, addr: usize) -> usize { + addr + self.vm_start + } + + fn virt_to_phys(&self, addr: usize) -> usize { + addr + } + + fn r#continue(&mut self) -> HypervisorResult { + loop { + match self.vcpu.run() { + Ok(vcpu_stop_reason) => match vcpu_stop_reason { + VcpuExit::Hlt => { + // Ignore `VcpuExit::Hlt` + debug!("{:?}", VcpuExit::Hlt); + } + VcpuExit::Shutdown => { + return Ok(VcpuStopReason::Exit(0)); + } + VcpuExit::Debug(debug) => { + info!("Caught Debug Interrupt!"); + return Ok(VcpuStopReason::Debug(debug)); + } + VcpuExit::Sbi(sbi_reason) => { + //debug!("SBI {:?}", sbi_reason); + match sbi_reason.extension_id { + SBI_CONSOLE_PUTCHAR => { + SBI_UTF_BUFFER.with(|buffer_cell| { + let mut buffer = buffer_cell.borrow_mut(); + buffer.push((sbi_reason.args[0] & 0xFF) as u8); + self.uart(&buffer).unwrap(); + buffer.clear(); + }); + /* let c = char::from_u32((sbi_reason.args[0] & 0xFF) as u32); + assert!(c.is_some(), "Error: {:#X}", sbi_reason.args[0]); + self.uart(c.unwrap().to_string()) + .expect("UART failed"); */ + } + _ => info!("Unhandled SBI call: {:?}", sbi_reason), + } + } + VcpuExit::SystemEvent(ev_type, ev_flags) => match ev_type { + KVM_SYSTEM_EVENT_SHUTDOWN => { + debug!("Shutdown Exit, flags: {}", ev_flags); + return Ok(VcpuStopReason::Exit(0)); + } + _ => info!("Unhandled SystemEvent: {}", ev_type), + }, + VcpuExit::InternalError => { + panic!("{:?}", VcpuExit::InternalError) + } + vcpu_exit => { + unimplemented!("{:?}", vcpu_exit) + } + }, + Err(err) => match err.errno() { + libc::EINTR => return Ok(VcpuStopReason::Kick), + _ => return Err(err), + }, + } + } + } + + fn run(&mut self) -> HypervisorResult> { + match self.r#continue()? { + VcpuStopReason::Debug(_) => { + unreachable!("reached debug exit without running in debugging mode") + } + VcpuStopReason::Exit(code) => Ok(Some(code)), + VcpuStopReason::Kick => Ok(None), + } + } + + fn print_registers(&self) { + let regs = Registers::from_kvm(self.get_vcpu()); + + println!(); + println!("Dump state of CPU {}", self.id); + println!(); + println!("Registers:"); + println!("----------"); + println!("{:x?}", regs); + } +} + +impl Drop for UhyveCPU { + fn drop(&mut self) { + debug!("Drop vCPU {}", self.id); + self.print_registers(); + } +} + +#[derive(Default, Debug)] +pub struct Registers { + // Gotten from gnu-binutils/gdb/riscv-tdep.c + pub zero: Option, + pub ra: Option, + pub sp: Option, + pub gp: Option, + pub tp: Option, + pub t0: Option, + pub t1: Option, + pub t2: Option, + pub s0: Option, + pub s1: Option, + pub a0: Option, + pub a1: Option, + pub a2: Option, + pub a3: Option, + pub a4: Option, + pub a5: Option, + pub a6: Option, + pub a7: Option, + pub s2: Option, + pub s3: Option, + pub s4: Option, + pub s5: Option, + pub s6: Option, + pub s7: Option, + pub s8: Option, + pub s9: Option, + pub s10: Option, + pub s11: Option, + pub t3: Option, + pub t4: Option, + pub t5: Option, + pub t6: Option, + pub pc: Option, +} + +impl Registers { + /// Loads the register set from kvm into the register struct + pub fn from_kvm(cpu: &VcpuFd) -> Self { + let mut registers = Registers::default(); + registers.zero = Some(0); + registers.ra = cpu.get_one_reg(KVM_REG_RISCV_CORE_RA).ok(); + registers.sp = cpu.get_one_reg(KVM_REG_RISCV_CORE_SP).ok(); + registers.gp = cpu.get_one_reg(KVM_REG_RISCV_CORE_GP).ok(); + registers.tp = cpu.get_one_reg(KVM_REG_RISCV_CORE_TP).ok(); + registers.t0 = cpu.get_one_reg(KVM_REG_RISCV_CORE_T0).ok(); + registers.t1 = cpu.get_one_reg(KVM_REG_RISCV_CORE_T1).ok(); + registers.t2 = cpu.get_one_reg(KVM_REG_RISCV_CORE_T2).ok(); + registers.s0 = cpu.get_one_reg(KVM_REG_RISCV_CORE_S0).ok(); + registers.s1 = cpu.get_one_reg(KVM_REG_RISCV_CORE_S1).ok(); + registers.a0 = cpu.get_one_reg(KVM_REG_RISCV_CORE_A0).ok(); + registers.a1 = cpu.get_one_reg(KVM_REG_RISCV_CORE_A1).ok(); + registers.a2 = cpu.get_one_reg(KVM_REG_RISCV_CORE_A2).ok(); + registers.a3 = cpu.get_one_reg(KVM_REG_RISCV_CORE_A3).ok(); + registers.a4 = cpu.get_one_reg(KVM_REG_RISCV_CORE_A4).ok(); + registers.a5 = cpu.get_one_reg(KVM_REG_RISCV_CORE_A5).ok(); + registers.a6 = cpu.get_one_reg(KVM_REG_RISCV_CORE_A6).ok(); + registers.a7 = cpu.get_one_reg(KVM_REG_RISCV_CORE_A7).ok(); + registers.s2 = cpu.get_one_reg(KVM_REG_RISCV_CORE_S2).ok(); + registers.s3 = cpu.get_one_reg(KVM_REG_RISCV_CORE_S3).ok(); + registers.s4 = cpu.get_one_reg(KVM_REG_RISCV_CORE_S4).ok(); + registers.s5 = cpu.get_one_reg(KVM_REG_RISCV_CORE_S5).ok(); + registers.s6 = cpu.get_one_reg(KVM_REG_RISCV_CORE_S6).ok(); + registers.s7 = cpu.get_one_reg(KVM_REG_RISCV_CORE_S7).ok(); + registers.s8 = cpu.get_one_reg(KVM_REG_RISCV_CORE_S8).ok(); + registers.s9 = cpu.get_one_reg(KVM_REG_RISCV_CORE_S9).ok(); + registers.s10 = cpu.get_one_reg(KVM_REG_RISCV_CORE_S10).ok(); + registers.s11 = cpu.get_one_reg(KVM_REG_RISCV_CORE_S11).ok(); + registers.t3 = cpu.get_one_reg(KVM_REG_RISCV_CORE_T3).ok(); + registers.t4 = cpu.get_one_reg(KVM_REG_RISCV_CORE_T4).ok(); + registers.t5 = cpu.get_one_reg(KVM_REG_RISCV_CORE_T5).ok(); + registers.t6 = cpu.get_one_reg(KVM_REG_RISCV_CORE_T6).ok(); + registers.pc = cpu.get_one_reg(KVM_REG_RISCV_CORE_PC).ok(); + + registers + } +} diff --git a/src/linux/arch/x86_64/mod.rs b/src/linux/arch/x86_64/mod.rs new file mode 100644 index 00000000..fe7efb8e --- /dev/null +++ b/src/linux/arch/x86_64/mod.rs @@ -0,0 +1,2 @@ +/// Arch x86_64 +pub mod vcpu; diff --git a/src/linux/vcpu.rs b/src/linux/arch/x86_64/vcpu.rs similarity index 100% rename from src/linux/vcpu.rs rename to src/linux/arch/x86_64/vcpu.rs diff --git a/src/linux/mod.rs b/src/linux/mod.rs index ba6e3b94..41eb069c 100755 --- a/src/linux/mod.rs +++ b/src/linux/mod.rs @@ -1,12 +1,18 @@ +pub mod arch; +#[cfg(target_arch = "x86_64")] pub mod gdb; pub mod uhyve; -pub mod vcpu; +//pub mod vcpu; +#[cfg(target_arch = "x86_64")] pub mod virtio; +#[cfg(target_arch = "x86_64")] pub mod virtqueue; pub type HypervisorError = kvm_ioctls::Error; pub type DebugExitInfo = kvm_bindings::kvm_debug_exit_arch; +pub use arch::vcpu; + use std::{ hint, io, mem, net::{TcpListener, TcpStream}, @@ -16,6 +22,7 @@ use std::{ }; use core_affinity::CoreId; +#[cfg(target_arch = "x86_64")] use gdbstub::DisconnectReason; use kvm_ioctls::Kvm; use lazy_static::lazy_static; @@ -25,8 +32,10 @@ use nix::sys::{ signal::{signal, SigHandler, Signal}, }; +#[cfg(target_arch = "x86_64")] +use crate::linux::gdb::{GdbUhyve, UhyveGdbEventLoop}; + use crate::{ - linux::gdb::{GdbUhyve, UhyveGdbEventLoop}, vm::{VirtualCPU, Vm}, Uhyve, }; @@ -79,11 +88,15 @@ impl Uhyve { self.load_kernel().expect("Unabled to load the kernel"); } + #[cfg(target_arch = "x86_64")] if self.gdb_port.is_none() { self.run_no_gdb(cpu_affinity) } else { self.run_gdb(cpu_affinity) } + + #[cfg(target_arch = "riscv64")] + self.run_no_gdb(cpu_affinity) } fn run_no_gdb(self, cpu_affinity: Option>) -> i32 { @@ -111,6 +124,9 @@ impl Uhyve { } let mut cpu = vm.create_cpu(cpu_id).unwrap(); + #[cfg(target_arch = "riscv64")] + cpu.init(vm.get_entry_point(), vm.get_boot_info()).unwrap(); + #[cfg(target_arch = "x86_64")] cpu.init(vm.get_entry_point()).unwrap(); // only one core is able to enter startup code @@ -155,6 +171,7 @@ impl Uhyve { code[0] } + #[cfg(target_arch = "x86_64")] fn run_gdb(self, cpu_affinity: Option>) -> i32 { let cpu_id = 0; diff --git a/src/linux/uhyve.rs b/src/linux/uhyve.rs index 6d0302cb..614f0b12 100755 --- a/src/linux/uhyve.rs +++ b/src/linux/uhyve.rs @@ -3,6 +3,8 @@ use crate::consts::*; use crate::linux::vcpu::*; + +#[cfg(target_arch = "x86_64")] use crate::linux::virtio::*; use crate::linux::KVM; use crate::shared_queue::*; @@ -12,6 +14,9 @@ use kvm_bindings::*; use kvm_ioctls::VmFd; use log::debug; use nix::sys::mman::*; +use nix::sys::statfs::statfs; +use nix::sys::statfs::HUGETLBFS_MAGIC; +use nix::unistd::{close, ftruncate, mkstemp, unlink}; use std::fmt; use std::hint; use std::mem; @@ -23,7 +28,9 @@ use std::ptr; use std::ptr::{read_volatile, write_volatile}; use std::str::FromStr; use std::sync::mpsc::sync_channel; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; +#[cfg(target_arch = "x86_64")] +use std::sync::Mutex; use std::thread; use tun_tap::{Iface, Mode}; use vmm_sys_util::eventfd::EventFd; @@ -134,10 +141,12 @@ pub struct Uhyve { gateway: Option, mask: Option, uhyve_device: Option, + #[cfg(target_arch = "x86_64")] virtio_device: Arc>, pub(super) gdb_port: Option, } +#[cfg(target_arch = "x86_64")] impl fmt::Debug for Uhyve { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Uhyve") @@ -156,6 +165,24 @@ impl fmt::Debug for Uhyve { } } +#[cfg(target_arch = "riscv64")] +impl fmt::Debug for Uhyve { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Uhyve") + .field("entry_point", &self.entry_point) + .field("mem", &self.mem) + .field("num_cpus", &self.num_cpus) + .field("path", &self.path) + .field("boot_info", &self.boot_info) + .field("verbose", &self.verbose) + .field("ip", &self.ip) + .field("gateway", &self.gateway) + .field("mask", &self.mask) + .field("uhyve_device", &self.uhyve_device) + .finish() + } +} + impl Uhyve { pub fn new(kernel_path: PathBuf, specs: &Parameter<'_>) -> HypervisorResult { // parse string to get IP address @@ -178,7 +205,14 @@ impl Uhyve { let vm = KVM.create_vm()?; - let mem = MmapMemory::new(0, specs.mem_size, 0, specs.hugepage, specs.mergeable); + let mem = MmapMemory::new( + 0, + specs.mem_size, + 0, + specs.hugepage, + specs.hugetlbfs_path, + specs.mergeable, + ); let sz = if specs.mem_size < KVM_32BIT_GAP_START { specs.mem_size @@ -187,6 +221,7 @@ impl Uhyve { }; // create virtio interface + #[cfg(target_arch = "x86_64")] let virtio_device = Arc::new(Mutex::new(VirtioNetPciDevice::new())); let kvm_mem = kvm_userspace_memory_region { @@ -213,53 +248,60 @@ impl Uhyve { unsafe { vm.set_user_memory_region(kvm_mem) }?; } - debug!("Initialize interrupt controller"); + #[cfg(target_arch = "x86_64")] + { + debug!("Initialize interrupt controller"); - // create basic interrupt controller - vm.create_irq_chip()?; + // create basic interrupt controller + vm.create_irq_chip()?; - // enable x2APIC support - let mut cap: kvm_enable_cap = kvm_bindings::kvm_enable_cap { - cap: KVM_CAP_X2APIC_API, - flags: 0, - ..Default::default() - }; - cap.args[0] = - (KVM_X2APIC_API_USE_32BIT_IDS | KVM_X2APIC_API_DISABLE_BROADCAST_QUIRK).into(); - vm.enable_cap(&cap) - .expect("Unable to enable x2apic support"); - - // currently, we support only system, which provides the - // cpu feature TSC_DEADLINE - let mut cap: kvm_enable_cap = kvm_bindings::kvm_enable_cap { - cap: KVM_CAP_TSC_DEADLINE_TIMER, - ..Default::default() - }; - cap.args[0] = 0; - vm.enable_cap(&cap) - .expect_err("Processor feature `tsc deadline` isn't supported!"); + // enable x2APIC support + let mut cap: kvm_enable_cap = kvm_bindings::kvm_enable_cap { + cap: KVM_CAP_X2APIC_API, + flags: 0, + ..Default::default() + }; + cap.args[0] = + (KVM_X2APIC_API_USE_32BIT_IDS | KVM_X2APIC_API_DISABLE_BROADCAST_QUIRK).into(); + vm.enable_cap(&cap) + .expect("Unable to enable x2apic support"); + + // currently, we support only system, which provides the + // cpu feature TSC_DEADLINE + let mut cap: kvm_enable_cap = kvm_bindings::kvm_enable_cap { + cap: KVM_CAP_TSC_DEADLINE_TIMER, + ..Default::default() + }; + cap.args[0] = 0; + vm.enable_cap(&cap) + .expect_err("Processor feature `tsc deadline` isn't supported!"); - let cap: kvm_enable_cap = kvm_bindings::kvm_enable_cap { - cap: KVM_CAP_IRQFD, - ..Default::default() - }; - vm.enable_cap(&cap) - .expect_err("The support of KVM_CAP_IRQFD is currently required"); + let cap: kvm_enable_cap = kvm_bindings::kvm_enable_cap { + cap: KVM_CAP_IRQFD, + ..Default::default() + }; + vm.enable_cap(&cap) + .expect_err("The support of KVM_CAP_IRQFD is currently required"); - let mut cap: kvm_enable_cap = kvm_bindings::kvm_enable_cap { - cap: KVM_CAP_X86_DISABLE_EXITS, - flags: 0, - ..Default::default() - }; - cap.args[0] = - (KVM_X86_DISABLE_EXITS_PAUSE | KVM_X86_DISABLE_EXITS_MWAIT | KVM_X86_DISABLE_EXITS_HLT) + let mut cap: kvm_enable_cap = kvm_bindings::kvm_enable_cap { + cap: KVM_CAP_X86_DISABLE_EXITS, + flags: 0, + ..Default::default() + }; + cap.args[0] = (KVM_X86_DISABLE_EXITS_PAUSE + | KVM_X86_DISABLE_EXITS_MWAIT + | KVM_X86_DISABLE_EXITS_HLT) .into(); - vm.enable_cap(&cap) - .expect("Unable to disable exists due pause instructions"); - + vm.enable_cap(&cap) + .expect("Unable to disable exists due pause instructions"); + } + #[cfg(target_arch = "x86_64")] let evtfd = EventFd::new(0).unwrap(); + #[cfg(target_arch = "x86_64")] vm.register_irqfd(&evtfd, UHYVE_IRQ_NET)?; + // create TUN/TAP device + #[cfg(target_arch = "x86_64")] let uhyve_device = match &specs.nic { Some(nic) => { debug!("Initialize network interface"); @@ -272,6 +314,9 @@ impl Uhyve { _ => None, }; + #[cfg(target_arch = "riscv64")] + let uhyve_device = None; + assert!( specs.gdbport.is_none() || specs.num_cpus == 1, "gdbstub is only supported with one CPU" @@ -290,6 +335,7 @@ impl Uhyve { gateway: gw_addr, mask, uhyve_device, + #[cfg(target_arch = "x86_64")] virtio_device, gdb_port: specs.gdbport, }; @@ -345,6 +391,7 @@ impl Vm for Uhyve { self.path.as_path() } + #[cfg(target_arch = "x86_64")] fn create_cpu(&self, id: u32) -> HypervisorResult { let vm_start = self.mem.host_address as usize; let tx = self.uhyve_device.as_ref().map(|dev| dev.tx.clone()); @@ -359,6 +406,24 @@ impl Vm for Uhyve { )) } + #[cfg(target_arch = "riscv64")] + fn create_cpu(&self, id: u32) -> HypervisorResult { + let vm_start = self.mem.host_address as usize; + #[cfg(target_arch = "x86_64")] + let tx = self.uhyve_device.as_ref().map(|dev| dev.tx.clone()); + + Ok(UhyveCPU::new( + id, + self.path.clone(), + self.vm.create_vcpu(id.try_into().unwrap())?, + vm_start, + )) + } + + fn get_boot_info(&self) -> *const BootInfo { + self.boot_info + } + fn set_boot_info(&mut self, header: *const BootInfo) { self.boot_info = header; } @@ -398,31 +463,68 @@ impl MmapMemory { memory_size: usize, guest_address: u64, huge_pages: bool, + hugetlbfs_path: Option<&str>, mergeable: bool, ) -> MmapMemory { - let host_address = unsafe { - mmap( - std::ptr::null_mut(), - memory_size, - ProtFlags::PROT_READ | ProtFlags::PROT_WRITE, - MapFlags::MAP_PRIVATE | MapFlags::MAP_ANONYMOUS | MapFlags::MAP_NORESERVE, - -1, - 0, - ) - .expect("mmap failed") - }; + let host_address: *mut c_void; + if let Some(path) = hugetlbfs_path { + debug!("Uhyve uses explicit huge pages"); + // Derived from kvmtool + let sfs = statfs(path).expect("No stat for given hugetlbfs path"); + if sfs.filesystem_type() != HUGETLBFS_MAGIC { + panic!("Given hugetlbfs is not a hugetlbfs"); + } + let blk_size = sfs.block_size() as usize; + if blk_size == 0 || blk_size > memory_size { + panic!( + "Can't use hugetlbfs pagesize {} for mem size {}", + blk_size, memory_size + ); + } + let mpath = format!("{}/uhyve_XXXXXX", path); + let (fd, new_mpath) = + mkstemp(mpath.as_str()).expect("Can't open file for hugetlbfs map"); + debug!("new_mpath: {:?}", new_mpath); + unlink(&new_mpath).expect("Unlink failed"); + ftruncate(fd, memory_size as i64).expect("Can't ftruncate for mem mapping"); + host_address = unsafe { + mmap( + std::ptr::null_mut(), + memory_size, + ProtFlags::PROT_READ | ProtFlags::PROT_WRITE, + MapFlags::MAP_PRIVATE, + fd, + 0, + ) + .expect("mmap failed") + }; + close(fd).expect("File cannot be closed"); + } else { + // host_address may not be aligned to huge page + host_address = unsafe { + mmap( + std::ptr::null_mut(), + memory_size, + ProtFlags::PROT_READ | ProtFlags::PROT_WRITE, + MapFlags::MAP_PRIVATE | MapFlags::MAP_ANONYMOUS | MapFlags::MAP_NORESERVE, + -1, + 0, + ) + .expect("mmap failed") + }; - if mergeable { - debug!("Enable kernel feature to merge same pages"); - unsafe { - madvise(host_address, memory_size, MmapAdvise::MADV_MERGEABLE).unwrap(); + if mergeable { + debug!("Enable kernel feature to merge same pages"); + unsafe { + madvise(host_address, memory_size, MmapAdvise::MADV_MERGEABLE).unwrap(); + } } - } - if huge_pages { - debug!("Uhyve uses huge pages"); - unsafe { - madvise(host_address, memory_size, MmapAdvise::MADV_HUGEPAGE).unwrap(); + if huge_pages { + debug!("Uhyve uses transparent huge pages"); + unsafe { + madvise(host_address, memory_size, MmapAdvise::MADV_HUGEPAGE).unwrap(); + } } } diff --git a/src/macos/uhyve.rs b/src/macos/uhyve.rs index 2a89a080..c3097d5f 100644 --- a/src/macos/uhyve.rs +++ b/src/macos/uhyve.rs @@ -143,6 +143,10 @@ impl Vm for Uhyve { None } + fn get_boot_info(&self) -> *const BootInfo { + self.boot_info + } + fn set_boot_info(&mut self, header: *const BootInfo) { self.boot_info = header; } diff --git a/src/utils.rs b/src/utils.rs index 66a07e8e..a771c7f1 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -15,7 +15,7 @@ pub fn transparent_hugepages_available() -> io::Result { let transp_hugepage_enabled = Path::new("/sys/kernel/mm/transparent_hugepage/enabled"); if !transp_hugepage_enabled.is_file() { debug!( - "`{}` does not exist. Assuming Hugepages are not available", + "`{}` does not exist. Assuming transparent Hugepages are not available", transp_hugepage_enabled.display() ); Ok(false) diff --git a/src/vm.rs b/src/vm.rs index 8f97e5b1..ed9288b4 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -2,17 +2,24 @@ use super::paging::*; #[cfg(target_arch = "x86_64")] use core::arch::x86_64::_rdtsc as rdtsc; use goblin::elf; -use goblin::elf64::header::{EM_X86_64, ET_DYN}; +#[cfg(target_arch = "riscv64")] +use goblin::elf64::header::EM_RISCV; +#[cfg(target_arch = "x86_64")] +use goblin::elf64::header::EM_X86_64; +use goblin::elf64::header::ET_DYN; use goblin::elf64::program_header::{PT_LOAD, PT_TLS}; use goblin::elf64::reloc::*; use log::{debug, error, warn}; +#[cfg(target_arch = "x86_64")] use raw_cpuid::CpuId; use std::io::Write; use std::net::Ipv4Addr; use std::os::unix::ffi::OsStrExt; use std::path::Path; use std::ptr::write; -use std::time::{Duration, Instant, SystemTime}; +use std::time::SystemTime; +#[cfg(target_arch = "x86_64")] +use std::time::{Duration, Instant}; use std::{fs, io, mem, slice}; use thiserror::Error; @@ -21,7 +28,9 @@ use crate::os::vcpu::UhyveCPU; use crate::os::DebugExitInfo; use crate::os::HypervisorError; +#[cfg(target_arch = "x86_64")] const MHZ_TO_HZ: u64 = 1000000; +#[cfg(target_arch = "x86_64")] const KHZ_TO_HZ: u64 = 1000; #[repr(C)] @@ -53,7 +62,14 @@ pub struct BootInfo { pub hcip: [u8; 4], pub hcgateway: [u8; 4], pub hcmask: [u8; 4], + #[cfg(target_arch = "x86_64")] pub tls_align: u64, + #[cfg(target_arch = "riscv64")] + pub dtb_ptr: u64, + #[cfg(target_arch = "riscv64")] + pub hart_mask: u64, + #[cfg(target_arch = "riscv64")] + pub timebase_freq: u64, } impl BootInfo { @@ -85,7 +101,14 @@ impl BootInfo { hcip: [255, 255, 255, 255], hcgateway: [255, 255, 255, 255], hcmask: [255, 255, 255, 0], + #[cfg(target_arch = "x86_64")] tls_align: 0, + #[cfg(target_arch = "riscv64")] + dtb_ptr: 0, + #[cfg(target_arch = "riscv64")] + hart_mask: 0, + #[cfg(target_arch = "riscv64")] + timebase_freq: 0, } } } @@ -102,6 +125,7 @@ pub struct Parameter<'a> { pub num_cpus: u32, pub verbose: bool, pub hugepage: bool, + pub hugetlbfs_path: Option<&'a str>, pub mergeable: bool, pub ip: Option<&'a str>, pub gateway: Option<&'a str>, @@ -204,8 +228,12 @@ pub enum VcpuStopReason { pub trait VirtualCPU { /// Initialize the cpu to start running the code ad entry_point. + #[cfg(target_arch = "x86_64")] fn init(&mut self, entry_point: u64) -> HypervisorResult<()>; + #[cfg(target_arch = "riscv64")] + fn init(&mut self, entry_point: u64, boot_info: *const BootInfo) -> HypervisorResult<()>; + /// Continues execution. fn r#continue(&mut self) -> HypervisorResult; @@ -341,12 +369,20 @@ pub trait VirtualCPU { /// unlink delets a name from the filesystem. This is used to handle `unlink` syscalls from the guest. /// TODO: UNSAFE AS *%@#. It has to be checked that the VM is allowed to unlink that file! + #[cfg(target_arch = "x86_64")] fn unlink(&self, args_ptr: usize) { unsafe { let sysunlink = &mut *(args_ptr as *mut SysUnlink); sysunlink.ret = libc::unlink(self.host_address(sysunlink.name as usize) as *const i8); } } + #[cfg(target_arch = "riscv64")] + fn unlink(&self, args_ptr: usize) { + unsafe { + let sysunlink = &mut *(args_ptr as *mut SysUnlink); + sysunlink.ret = libc::unlink(self.host_address(sysunlink.name as usize) as *const u8); + } + } /// Reads the exit code from an VM and returns it fn exit(&self, args_ptr: usize) -> i32 { @@ -355,6 +391,7 @@ pub trait VirtualCPU { } /// Handles an open syscall by opening a file on the host. + #[cfg(target_arch = "x86_64")] fn open(&self, args_ptr: usize) { unsafe { let sysopen = &mut *(args_ptr as *mut SysOpen); @@ -365,6 +402,17 @@ pub trait VirtualCPU { ); } } + #[cfg(target_arch = "riscv64")] + fn open(&self, args_ptr: usize) { + unsafe { + let sysopen = &mut *(args_ptr as *mut SysOpen); + sysopen.ret = libc::open( + self.host_address(sysopen.name as usize) as *const u8, + sysopen.flags, + sysopen.mode, + ); + } + } /// Handles an close syscall by closing the file on the host. fn close(&self, args_ptr: usize) { @@ -455,6 +503,7 @@ pub trait Vm { fn get_entry_point(&self) -> u64; fn kernel_path(&self) -> &Path; fn create_cpu(&self, id: u32) -> HypervisorResult; + fn get_boot_info(&self) -> *const BootInfo; fn set_boot_info(&mut self, header: *const BootInfo); fn cpu_online(&self) -> u32; fn get_ip(&self) -> Option; @@ -530,6 +579,12 @@ pub trait Vm { let is_dyn = elf.header.e_type == ET_DYN; debug!("ELF file is a shared object file: {}", is_dyn); + #[cfg(target_arch = "riscv64")] + if elf.header.e_machine != EM_RISCV { + return Err(LoadKernelError::Io(io::ErrorKind::InvalidData.into())); + } + + #[cfg(target_arch = "x86_64")] if elf.header.e_machine != EM_X86_64 { return Err(LoadKernelError::Io(io::ErrorKind::InvalidData.into())); } @@ -602,7 +657,9 @@ pub trait Vm { .expect("SystemTime before UNIX EPOCH!"); write(&mut (*boot_info).boot_gtod, n.as_secs() * 1000000); + #[cfg(target_arch = "x86_64")] let cpuid = CpuId::new(); + #[cfg(target_arch = "x86_64")] let mhz: u32 = detect_freq_from_cpuid(&cpuid).unwrap_or_else(|_| { debug!("Failed to detect from cpuid"); detect_freq_from_cpuid_hypervisor_info(&cpuid).unwrap_or_else(|_| { @@ -610,12 +667,18 @@ pub trait Vm { get_cpu_frequency_from_os().unwrap_or(0) }) }); + #[cfg(target_arch = "x86_64")] debug!("detected a cpu frequency of {} Mhz", mhz); + #[cfg(target_arch = "x86_64")] write(&mut (*boot_info).cpu_freq, mhz); + if (*boot_info).cpu_freq == 0 { warn!("Unable to determine processor frequency"); } + #[cfg(target_arch = "riscv64")] + write(&mut (*boot_info).hart_mask, (1 << self.num_cpus()) - 1); + // load kernel and determine image size let vm_slice = std::slice::from_raw_parts_mut(vm_mem, vm_mem_length); let mut image_size = 0; @@ -670,7 +733,7 @@ pub trait Vm { write(&mut (*boot_info).tls_start, tls_start); write(&mut (*boot_info).tls_filesz, program_header.p_filesz); write(&mut (*boot_info).tls_memsz, program_header.p_memsz); - write(&mut (*boot_info).tls_align, program_header.p_align); + //write(&mut (*boot_info).tls_align, program_header.p_align); Ok(()) } @@ -679,10 +742,16 @@ pub trait Vm { // relocate entries (strings, copy-data, etc.) with an addend elf.dynrelas.iter().for_each(|rela| match rela.r_type { + #[cfg(target_arch = "x86_64")] R_X86_64_RELATIVE => { let offset = (vm_mem as u64 + start_address + rela.r_offset) as *mut u64; *offset = (start_address as i64 + rela.r_addend.unwrap_or(0)) as u64; } + #[cfg(target_arch = "riscv64")] + R_RISCV_RELATIVE => { + let offset = (vm_mem as u64 + start_address + rela.r_offset) as *mut u64; + *offset = (start_address as i64 + rela.r_addend.unwrap_or(0)) as u64; + } _ => { debug!("Unsupported relocation type {}", rela.r_type); } @@ -696,6 +765,7 @@ pub trait Vm { } } +#[cfg(target_arch = "x86_64")] fn detect_freq_from_cpuid(cpuid: &CpuId) -> Result { debug!("Trying to detect CPU frequency by tsc info"); @@ -736,6 +806,7 @@ fn detect_freq_from_cpuid(cpuid: &CpuId) -> Result { } } +#[cfg(target_arch = "x86_64")] fn detect_freq_from_cpuid_hypervisor_info(cpuid: &CpuId) -> Result { debug!("Trying to detect CPU frequency by hypervisor info"); let hypervisor_info = cpuid.get_hypervisor_info().ok_or(())?; @@ -752,6 +823,7 @@ fn detect_freq_from_cpuid_hypervisor_info(cpuid: &CpuId) -> Result { } } +#[cfg(target_arch = "x86_64")] fn get_cpu_frequency_from_os() -> Result { // Determine TSC frequency by measuring it (loop for a second, record ticks) let duration = Duration::from_millis(10); @@ -777,6 +849,7 @@ mod tests { // test is derived from // https://github.com/gz/rust-cpuid/blob/master/examples/tsc_frequency.rs #[test] + #[cfg(target_arch = "x86_64")] fn test_detect_freq_from_cpuid() { let cpuid = CpuId::new(); let has_tsc = cpuid @@ -848,6 +921,7 @@ mod tests { } #[test] + #[cfg(target_arch = "x86_64")] fn test_get_cpu_frequency_from_os() { let freq_res = get_cpu_frequency_from_os(); assert!(freq_res.is_ok());