From fe01207661f6eec49925759f0b986419ad3be575 Mon Sep 17 00:00:00 2001 From: Jonathan Klimt Date: Mon, 2 Jan 2023 18:04:09 +0100 Subject: [PATCH 01/21] Moved hypercall related consts and fns out into a separate crate --- Cargo.lock | 8 ++ Cargo.toml | 1 + src/consts.rs | 20 ----- src/linux/vcpu.rs | 6 +- src/vm.rs | 80 ++------------------ uhypercall-interface/.gitignore | 2 + uhypercall-interface/Cargo.toml | 14 ++++ uhypercall-interface/src/lib.rs | 130 ++++++++++++++++++++++++++++++++ 8 files changed, 165 insertions(+), 96 deletions(-) create mode 100644 uhypercall-interface/.gitignore create mode 100644 uhypercall-interface/Cargo.toml create mode 100644 uhypercall-interface/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index d1018c00..31a2a1c8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1096,6 +1096,13 @@ dependencies = [ "cc", ] +[[package]] +name = "uhypercall-interface" +version = "0.1.0" +dependencies = [ + "x86_64", +] + [[package]] name = "uhyve" version = "0.2.2" @@ -1125,6 +1132,7 @@ dependencies = [ "thiserror", "time", "tun-tap", + "uhypercall-interface", "virtio-bindings", "vmm-sys-util", "x86_64", diff --git a/Cargo.toml b/Cargo.toml index 732ed024..ea71f472 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,6 +50,7 @@ libc = "0.2" log = "0.4" thiserror = "1.0" time = "0.3" +uhypercall-interface = {path = "./uhypercall-interface"} rftrace = { version = "0.1", optional = true } rftrace-frontend = { version = "0.1", optional = true } diff --git a/src/consts.rs b/src/consts.rs index e3657d24..c36aed25 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -25,23 +25,3 @@ pub const UHYVE_QUEUE_SIZE: usize = 8; pub const UHYVE_IRQ_NET: u32 = 11; pub const GUEST_PAGE_SIZE: u64 = 0x200000; /* 2 MB pages in guest */ - -pub const UHYVE_PORT_WRITE: u16 = 0x400; -pub const UHYVE_PORT_OPEN: u16 = 0x440; -pub const UHYVE_PORT_CLOSE: u16 = 0x480; -pub const UHYVE_PORT_READ: u16 = 0x500; -pub const UHYVE_PORT_EXIT: u16 = 0x540; -pub const UHYVE_PORT_LSEEK: u16 = 0x580; - -// Networkports -pub const UHYVE_PORT_NETWRITE: u16 = 0x640; -pub const UHYVE_PORT_NETREAD: u16 = 0x680; -pub const UHYVE_PORT_NETSTAT: u16 = 0x700; - -/* Ports and data structures for uhyve command line arguments and envp - * forwarding */ -pub const UHYVE_PORT_CMDSIZE: u16 = 0x740; -pub const UHYVE_PORT_CMDVAL: u16 = 0x780; - -pub const UHYVE_UART_PORT: u16 = 0x800; -pub const UHYVE_PORT_UNLINK: u16 = 0x840; diff --git a/src/linux/vcpu.rs b/src/linux/vcpu.rs index deb7a390..632357c1 100755 --- a/src/linux/vcpu.rs +++ b/src/linux/vcpu.rs @@ -7,6 +7,7 @@ use std::{ use kvm_bindings::*; use kvm_ioctls::{VcpuExit, VcpuFd}; +use uhypercall_interface::*; use x86_64::{ registers::control::{Cr0Flags, Cr4Flags}, structures::paging::PageTableFlags, @@ -15,10 +16,7 @@ use x86_64::{ use crate::{ consts::*, linux::{virtio::*, KVM}, - vm::{ - HypervisorResult, SysClose, SysCmdsize, SysCmdval, SysExit, SysLseek, SysOpen, SysRead, - SysUnlink, SysWrite, VcpuStopReason, VirtualCPU, - }, + vm::{HypervisorResult, VcpuStopReason, VirtualCPU}, }; const CPUID_EXT_HYPERVISOR: u32 = 1 << 31; diff --git a/src/vm.rs b/src/vm.rs index 7adc6860..7cc3c225 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -9,6 +9,7 @@ use hermit_entry::{ }; use log::{error, warn}; use thiserror::Error; +use uhypercall_interface::*; #[cfg(target_arch = "x86_64")] use crate::arch::x86_64::{ @@ -20,72 +21,6 @@ use crate::{ os::{vcpu::UhyveCPU, DebugExitInfo, HypervisorError}, }; -#[repr(C, packed)] -pub struct SysWrite { - fd: i32, - buf: *const u8, - len: usize, -} - -#[repr(C, packed)] -pub struct SysRead { - fd: i32, - buf: *const u8, - len: usize, - ret: isize, -} - -#[repr(C, packed)] -pub struct SysClose { - fd: i32, - ret: i32, -} - -#[repr(C, packed)] -pub struct SysOpen { - name: *const u8, - flags: i32, - mode: i32, - ret: i32, -} - -#[repr(C, packed)] -pub struct SysLseek { - fd: i32, - offset: isize, - whence: i32, -} - -#[repr(C, packed)] -pub struct SysExit { - arg: i32, -} - -// FIXME: Do not use a fix number of arguments -const MAX_ARGC: usize = 128; -// FIXME: Do not use a fix number of environment variables -const MAX_ENVC: usize = 128; - -#[repr(C, packed)] -pub struct SysCmdsize { - argc: i32, - argsz: [i32; MAX_ARGC], - envc: i32, - envsz: [i32; MAX_ENVC], -} - -#[repr(C, packed)] -pub struct SysCmdval { - argv: *const u8, - envp: *const u8, -} - -#[repr(C, packed)] -pub struct SysUnlink { - name: *const u8, - ret: i32, -} - pub type HypervisorResult = Result; #[derive(Error, Debug)] @@ -154,21 +89,21 @@ pub trait VirtualCPU { let mut counter = 0; for (key, value) in std::env::vars_os() { - if counter < MAX_ENVC.try_into().unwrap() { + if counter < MAX_ARGC_ENVC.try_into().unwrap() { syssize.envsz[counter as usize] = (key.len() + value.len()) as i32 + 2; counter += 1; } } syssize.envc = counter; - if counter >= MAX_ENVC.try_into().unwrap() { + if counter >= MAX_ARGC_ENVC.try_into().unwrap() { warn!("Environment is too large!"); } } /// Copies the arguments end environment of the application into the VM's memory. fn cmdval(&self, syscmdval: &SysCmdval) { - let argv = self.host_address(syscmdval.argv as usize); + let argv = self.host_address(syscmdval.argv.as_u64() as usize); // copy kernel path as first argument { @@ -200,9 +135,9 @@ pub trait VirtualCPU { // Copy the environment variables into the vm memory let mut counter = 0; - let envp = self.host_address(syscmdval.envp as usize); + let envp = self.host_address(syscmdval.envp.as_u64() as usize); for (key, value) in std::env::vars_os() { - if counter < MAX_ENVC.try_into().unwrap() { + if counter < MAX_ARGC_ENVC.try_into().unwrap() { let envptr = unsafe { self.host_address( *((envp + counter as usize * mem::size_of::()) as *mut *mut u8) @@ -226,7 +161,8 @@ pub trait VirtualCPU { /// TODO: UNSAFE AS *%@#. It has to be checked that the VM is allowed to unlink that file! fn unlink(&self, sysunlink: &mut SysUnlink) { unsafe { - sysunlink.ret = libc::unlink(self.host_address(sysunlink.name as usize) as *const i8); + sysunlink.ret = + libc::unlink(self.host_address(sysunlink.name.as_u64() as usize) as *const i8); } } diff --git a/uhypercall-interface/.gitignore b/uhypercall-interface/.gitignore new file mode 100644 index 00000000..1e7caa9e --- /dev/null +++ b/uhypercall-interface/.gitignore @@ -0,0 +1,2 @@ +Cargo.lock +target/ diff --git a/uhypercall-interface/Cargo.toml b/uhypercall-interface/Cargo.toml new file mode 100644 index 00000000..2a654f0a --- /dev/null +++ b/uhypercall-interface/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "uhypercall-interface" +version = "0.1.0" +edition = "2021" +description = "The interface between uhyve and a guest VM" +repository = "https://github.com/hermitcore/uhyve" +license = "MIT/Apache-2.0" +keywords = ["hypervisor", "unikernel", "hermit"] +categories = ["os"] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +x86_64 = { version = "0.14", default-features = false } diff --git a/uhypercall-interface/src/lib.rs b/uhypercall-interface/src/lib.rs new file mode 100644 index 00000000..db5f2d57 --- /dev/null +++ b/uhypercall-interface/src/lib.rs @@ -0,0 +1,130 @@ +//! # Uhyve Hypercall Interface +//! +//! The uhyve hypercall interface works as follows: +//! +//! - On `x86_64` you use an out port instruction. The address of the `out`-port corresponds to the +//! hypercall you want to use. The data send to that port is the physical memory address (of the VM) +//! of the parameters of that hypercall. + +#![no_std] + +use x86_64::PhysAddr; + +pub const UHYVE_PORT_WRITE: u16 = 0x400; +pub const UHYVE_PORT_OPEN: u16 = 0x440; +pub const UHYVE_PORT_CLOSE: u16 = 0x480; +pub const UHYVE_PORT_READ: u16 = 0x500; +pub const UHYVE_PORT_EXIT: u16 = 0x540; +pub const UHYVE_PORT_LSEEK: u16 = 0x580; + +// Networkports (not used at the moment) +// TODO: Update interface +pub const UHYVE_PORT_NETWRITE: u16 = 0x640; +pub const UHYVE_PORT_NETREAD: u16 = 0x680; +pub const UHYVE_PORT_NETSTAT: u16 = 0x700; + +/* Ports and data structures for uhyve command line arguments and envp + * forwarding */ +pub const UHYVE_PORT_CMDSIZE: u16 = 0x740; +pub const UHYVE_PORT_CMDVAL: u16 = 0x780; + +pub const UHYVE_UART_PORT: u16 = 0x800; +pub const UHYVE_PORT_UNLINK: u16 = 0x840; + +// FIXME: Do not use a fix number of arguments +pub const MAX_ARGC_ENVC: usize = 128; + +#[repr(C, packed)] +#[derive(Debug, Copy, Clone)] +pub struct SysCmdsize { + pub argc: i32, + pub argsz: [i32; MAX_ARGC_ENVC], + pub envc: i32, + pub envsz: [i32; MAX_ARGC_ENVC], +} +impl SysCmdsize { + pub fn new() -> SysCmdsize { + SysCmdsize { + argc: 0, + argsz: [0; MAX_ARGC_ENVC], + envc: 0, + envsz: [0; MAX_ARGC_ENVC], + } + } +} + +#[repr(C, packed)] +#[derive(Debug, Copy, Clone)] +pub struct SysCmdval { + pub argv: PhysAddr, + pub envp: PhysAddr, +} +impl SysCmdval { + pub fn new(argv: PhysAddr, envp: PhysAddr) -> SysCmdval { + SysCmdval { argv, envp } + } +} + +#[repr(C, packed)] +#[derive(Debug, Copy, Clone)] +pub struct SysExit { + pub arg: i32, +} +impl SysExit { + pub fn new(arg: i32) -> SysExit { + SysExit { arg } + } +} + +#[repr(C, packed)] +#[derive(Debug, Copy, Clone)] +pub struct SysUnlink { + pub name: PhysAddr, + pub ret: i32, +} +impl SysUnlink { + pub fn new(name: PhysAddr) -> SysUnlink { + SysUnlink { name, ret: -1 } + } +} + +#[repr(C, packed)] +#[derive(Debug, Copy, Clone)] +pub struct SysWrite { + pub fd: i32, + pub buf: *const u8, + pub len: usize, +} + +#[repr(C, packed)] +#[derive(Debug, Copy, Clone)] +pub struct SysRead { + pub fd: i32, + pub buf: *const u8, + pub len: usize, + pub ret: isize, +} + +#[repr(C, packed)] +#[derive(Debug, Copy, Clone)] +pub struct SysClose { + pub fd: i32, + pub ret: i32, +} + +#[repr(C, packed)] +#[derive(Debug, Copy, Clone)] +pub struct SysOpen { + pub name: *const u8, + pub flags: i32, + pub mode: i32, + pub ret: i32, +} + +#[repr(C, packed)] +#[derive(Debug, Copy, Clone)] +pub struct SysLseek { + pub fd: i32, + pub offset: isize, + pub whence: i32, +} From 3137500758ea91338f5ba0c7a63425d494271ad0 Mon Sep 17 00:00:00 2001 From: Jonathan Klimt Date: Tue, 3 Jan 2023 11:02:21 +0100 Subject: [PATCH 02/21] Added an enum containing the hypercalls and the ports --- uhypercall-interface/src/lib.rs | 56 +++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/uhypercall-interface/src/lib.rs b/uhypercall-interface/src/lib.rs index db5f2d57..0c376fc9 100644 --- a/uhypercall-interface/src/lib.rs +++ b/uhypercall-interface/src/lib.rs @@ -10,6 +10,62 @@ use x86_64::PhysAddr; +/// Enum containing all valid port mappings for hypercalls. +/// +/// The discriminants of this enum are the respective ports, so one can get the code by calling +/// e.g., `HypercallPorts::FileWrite as u16`. +#[non_exhaustive] +#[repr(u16)] +pub enum HypercallPorts { + FileWrite = 0x400, + FileOpen = 0x440, + FileClose = 0x480, + FileRead = 0x500, + Exit = 0x540, + FileLseek = 0x580, + Netwrite = 0x640, + Netread = 0x680, + Netstat = 0x700, + Cmdsize = 0x740, + Cmdval = 0x780, + FileUnlink = 0x840, +} +impl From for HypercallPorts { + fn from(value: Hypercall) -> Self { + match value { + Hypercall::Cmdsize(_) => Self::Cmdsize, + Hypercall::Cmdval(_) => Self::Cmdval, + Hypercall::Exit(_) => Self::Exit, + Hypercall::FileClose(_) => Self::FileClose, + Hypercall::FileLseek(_) => Self::FileLseek, + Hypercall::FileOpen(_) => Self::FileOpen, + Hypercall::FileRead(_) => Self::FileRead, + Hypercall::FileWrite(_) => Self::FileWrite, + Hypercall::FileUnlink(_) => Self::FileUnlink, + } + } +} + +/// Hypervisor calls available in uhyve with their respective parameters. +#[non_exhaustive] +#[derive(Debug, Copy, Clone)] +pub enum Hypercall { + Cmdsize(SysCmdsize), + Cmdval(SysCmdval), + Exit(SysExit), + FileClose(SysClose), + FileLseek(SysLseek), + FileOpen(SysOpen), + FileRead(SysRead), + FileWrite(SysWrite), + FileUnlink(SysUnlink), +} +impl Hypercall { + pub fn port(self) -> u16 { + HypercallPorts::from(self) as u16 + } +} + pub const UHYVE_PORT_WRITE: u16 = 0x400; pub const UHYVE_PORT_OPEN: u16 = 0x440; pub const UHYVE_PORT_CLOSE: u16 = 0x480; From 1d132c1af0dd93fd0301b8bc1176fb89165dd2da Mon Sep 17 00:00:00 2001 From: Jonathan Klimt Date: Tue, 3 Jan 2023 12:58:05 +0100 Subject: [PATCH 03/21] use num-derive to easily convert u16 into HypercallPorts --- Cargo.lock | 21 +++++++++++++++++++++ uhypercall-interface/Cargo.toml | 3 +-- uhypercall-interface/src/lib.rs | 3 +++ 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 31a2a1c8..5e97fce6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -715,6 +715,26 @@ dependencies = [ "libc", ] +[[package]] +name = "num_enum" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf5395665662ef45796a4ff5486c5d41d29e0c09640af4c5f17fd94ee2c119c9" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "once_cell" version = "1.17.1" @@ -1100,6 +1120,7 @@ dependencies = [ name = "uhypercall-interface" version = "0.1.0" dependencies = [ + "num_enum", "x86_64", ] diff --git a/uhypercall-interface/Cargo.toml b/uhypercall-interface/Cargo.toml index 2a654f0a..ceb42a05 100644 --- a/uhypercall-interface/Cargo.toml +++ b/uhypercall-interface/Cargo.toml @@ -8,7 +8,6 @@ license = "MIT/Apache-2.0" keywords = ["hypervisor", "unikernel", "hermit"] categories = ["os"] -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] +num_enum = { version = "0.5", default-features = false } x86_64 = { version = "0.14", default-features = false } diff --git a/uhypercall-interface/src/lib.rs b/uhypercall-interface/src/lib.rs index 0c376fc9..b8a74104 100644 --- a/uhypercall-interface/src/lib.rs +++ b/uhypercall-interface/src/lib.rs @@ -8,6 +8,8 @@ #![no_std] +// TODO: Throw this out, once https://github.com/rust-lang/rfcs/issues/2783 or https://github.com/rust-lang/rust/issues/86772 is resolved +use num_enum::TryFromPrimitive; use x86_64::PhysAddr; /// Enum containing all valid port mappings for hypercalls. @@ -16,6 +18,7 @@ use x86_64::PhysAddr; /// e.g., `HypercallPorts::FileWrite as u16`. #[non_exhaustive] #[repr(u16)] +#[derive(Debug, Eq, PartialEq, TryFromPrimitive)] pub enum HypercallPorts { FileWrite = 0x400, FileOpen = 0x440, From ab14a2951e75666248d99f1eaf7c75baef50cc70 Mon Sep 17 00:00:00 2001 From: Jonathan Klimt Date: Tue, 3 Jan 2023 12:59:51 +0100 Subject: [PATCH 04/21] Hypercall enum includes references instead of copies of the parameters --- uhypercall-interface/src/lib.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/uhypercall-interface/src/lib.rs b/uhypercall-interface/src/lib.rs index b8a74104..ef953741 100644 --- a/uhypercall-interface/src/lib.rs +++ b/uhypercall-interface/src/lib.rs @@ -33,7 +33,7 @@ pub enum HypercallPorts { Cmdval = 0x780, FileUnlink = 0x840, } -impl From for HypercallPorts { +impl From> for HypercallPorts { fn from(value: Hypercall) -> Self { match value { Hypercall::Cmdsize(_) => Self::Cmdsize, @@ -51,19 +51,19 @@ impl From for HypercallPorts { /// Hypervisor calls available in uhyve with their respective parameters. #[non_exhaustive] -#[derive(Debug, Copy, Clone)] -pub enum Hypercall { - Cmdsize(SysCmdsize), - Cmdval(SysCmdval), - Exit(SysExit), - FileClose(SysClose), - FileLseek(SysLseek), - FileOpen(SysOpen), - FileRead(SysRead), - FileWrite(SysWrite), - FileUnlink(SysUnlink), -} -impl Hypercall { +#[derive(Debug)] +pub enum Hypercall<'a> { + Cmdsize(&'a mut SysCmdsize), + Cmdval(&'a SysCmdval), + Exit(&'a SysExit), + FileClose(&'a mut SysClose), + FileLseek(&'a mut SysLseek), + FileOpen(&'a mut SysOpen), + FileRead(&'a mut SysRead), + FileWrite(&'a SysWrite), + FileUnlink(&'a mut SysUnlink), +} +impl<'a> Hypercall<'a> { pub fn port(self) -> u16 { HypercallPorts::from(self) as u16 } From 80bb93592e1a7b4f805c14df75734503bf9d4a00 Mon Sep 17 00:00:00 2001 From: Jonathan Klimt Date: Wed, 4 Jan 2023 15:18:52 +0100 Subject: [PATCH 05/21] Improved documentation of the hypercall interface --- uhypercall-interface/src/lib.rs | 63 +++++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 3 deletions(-) diff --git a/uhypercall-interface/src/lib.rs b/uhypercall-interface/src/lib.rs index ef953741..84d442a1 100644 --- a/uhypercall-interface/src/lib.rs +++ b/uhypercall-interface/src/lib.rs @@ -3,8 +3,11 @@ //! The uhyve hypercall interface works as follows: //! //! - On `x86_64` you use an out port instruction. The address of the `out`-port corresponds to the -//! hypercall you want to use. The data send to that port is the physical memory address (of the VM) -//! of the parameters of that hypercall. +//! hypercall you want to use. You can obtain it from the [`HypercallPorts`] enum. The data send to +//! that port is the physical memory address (of the VM) of the parameters of that hypercall. +//! - On `aarch64` you write to the respective [`HypercallAddress`]. The 64-bit value written to that location is the guest's physical memory address of the hypercall's parameter. + +// TODO: only x86 allows io instructions. Other architectures should use MMIO #![no_std] @@ -20,17 +23,29 @@ use x86_64::PhysAddr; #[repr(u16)] #[derive(Debug, Eq, PartialEq, TryFromPrimitive)] pub enum HypercallPorts { + /// Port address = `0x400` FileWrite = 0x400, + /// Port address = `0x440` FileOpen = 0x440, + /// Port address = `0x480` FileClose = 0x480, + /// Port address = `0x500` FileRead = 0x500, + /// Port address = `0x540` Exit = 0x540, + /// Port address = `0x580` FileLseek = 0x580, + /// Port address = `0x640` Netwrite = 0x640, + /// Port address = `0x680` Netread = 0x680, + /// Port address = `0x700` Netstat = 0x700, + /// Port address = `0x740` Cmdsize = 0x740, + /// Port address = `0x780` Cmdval = 0x780, + /// Port address = `0x840` FileUnlink = 0x840, } impl From> for HypercallPorts { @@ -49,12 +64,17 @@ impl From> for HypercallPorts { } } -/// Hypervisor calls available in uhyve with their respective parameters. +/// Hypervisor calls available in uhyve with their respective parameters. See the [module level documentation](crate) on how to invoke them. #[non_exhaustive] #[derive(Debug)] pub enum Hypercall<'a> { + /// Get the size of the argument and environment strings. Used to allocate memory for + /// [`Hypercall::Cmdval`]. Cmdsize(&'a mut SysCmdsize), + /// Copy the argument and environment strings into the VM. Usually preceeeded by + /// [`Hypercall::Cmdsize`] so that the guest can allocate the memory for this call. Cmdval(&'a SysCmdval), + /// Exit the VM and return a status. Exit(&'a SysExit), FileClose(&'a mut SysClose), FileLseek(&'a mut SysLseek), @@ -64,6 +84,7 @@ pub enum Hypercall<'a> { FileUnlink(&'a mut SysUnlink), } impl<'a> Hypercall<'a> { + /// Get a hypercall's port address. pub fn port(self) -> u16 { HypercallPorts::from(self) as u16 } @@ -93,15 +114,21 @@ pub const UHYVE_PORT_UNLINK: u16 = 0x840; // FIXME: Do not use a fix number of arguments pub const MAX_ARGC_ENVC: usize = 128; +/// Parameters for a [`Cmdsize`](Hypercall::Cmdsize) hypercall which provides the lengths of the items in the argument end environment vector. #[repr(C, packed)] #[derive(Debug, Copy, Clone)] pub struct SysCmdsize { + /// Nr of items in the kernel command line. pub argc: i32, + /// Lengths of the items in the kernel command line. pub argsz: [i32; MAX_ARGC_ENVC], + /// Nr of items in the environment. pub envc: i32, + /// Length of the items in the environment. pub envsz: [i32; MAX_ARGC_ENVC], } impl SysCmdsize { + // TODO: Default not new pub fn new() -> SysCmdsize { SysCmdsize { argc: 0, @@ -112,10 +139,13 @@ impl SysCmdsize { } } +/// Parameters for a [`Cmdval`](Hypercall::Cmdval) hypercall, which copies the arguments end environment of the application into the VM's memory. #[repr(C, packed)] #[derive(Debug, Copy, Clone)] pub struct SysCmdval { + /// Pointer to a memory section in the VM memory large enough to store the argument string. pub argv: PhysAddr, + /// Pointer to a memory section in the VM memory large enough to store the environment values. pub envp: PhysAddr, } impl SysCmdval { @@ -124,9 +154,11 @@ impl SysCmdval { } } +/// Parameters for a [`Exit`](Hypercall::Exit) hypercall. #[repr(C, packed)] #[derive(Debug, Copy, Clone)] pub struct SysExit { + /// The return code of the guest. pub arg: i32, } impl SysExit { @@ -135,10 +167,13 @@ impl SysExit { } } +/// Parameters for a [`FileUnlink`](Hypercall::FileUnlink) hypercall. #[repr(C, packed)] #[derive(Debug, Copy, Clone)] pub struct SysUnlink { + /// Address of the file that should be unlinked. pub name: PhysAddr, + /// On success, `0` is returned. On error, `-1` is returned. pub ret: i32, } impl SysUnlink { @@ -147,43 +182,65 @@ impl SysUnlink { } } +/// Parameters for a [`FileWrite`](Hypercall::FileWrite) hypercall. #[repr(C, packed)] #[derive(Debug, Copy, Clone)] pub struct SysWrite { + /// File descriptor of the file. pub fd: i32, + /// Buffer to be written into the file. pub buf: *const u8, + /// Number of bytes in the buffer to be written. pub len: usize, } +/// Parameters for a [`FileRead`](Hypercall::FileRead) hypercall. #[repr(C, packed)] #[derive(Debug, Copy, Clone)] pub struct SysRead { + /// File descriptor of the file. pub fd: i32, + /// Buffer to read the file into. pub buf: *const u8, + /// Number of bytes to read into the buffer. pub len: usize, + /// Number of bytes read on success. `-1` on failure. pub ret: isize, } +/// Parameters for a [`FileClose`](Hypercall::FileClose) hypercall #[repr(C, packed)] #[derive(Debug, Copy, Clone)] pub struct SysClose { + /// File descriptor of the file. pub fd: i32, + /// Zero on success, `-1` on failure. pub ret: i32, } +/// Parameters for a [`FileOpen`](Hypercall::FileOpen) hypercall #[repr(C, packed)] #[derive(Debug, Copy, Clone)] pub struct SysOpen { + /// Pathname of the file to be opened. + // TODO PhysAddr??? pub name: *const u8, + /// Posix file access mode flags. pub flags: i32, + /// Access permissions upon opening/creating a file. pub mode: i32, + /// File descriptor upon successful opening or `-1` upon failure. pub ret: i32, } +/// Parameters for a [`FileLseek`](Hypercall::FileLseek) hypercall #[repr(C, packed)] #[derive(Debug, Copy, Clone)] pub struct SysLseek { + /// File descriptor of the file. pub fd: i32, + /// Offset in the file. pub offset: isize, + /// `whence` value of the lseek call. pub whence: i32, } From e8908aa057655273cc669278c68177388f220306 Mon Sep 17 00:00:00 2001 From: Jonathan Klimt Date: Wed, 4 Jan 2023 16:17:38 +0100 Subject: [PATCH 06/21] Use hypercall interface in vcpu --- src/linux/vcpu.rs | 172 ++++++++++++++------------------------- src/macos/x86_64/vcpu.rs | 130 +++++++++-------------------- src/vm.rs | 55 ++++++++++++- 3 files changed, 151 insertions(+), 206 deletions(-) diff --git a/src/linux/vcpu.rs b/src/linux/vcpu.rs index 632357c1..dc5d9f56 100755 --- a/src/linux/vcpu.rs +++ b/src/linux/vcpu.rs @@ -7,7 +7,7 @@ use std::{ use kvm_bindings::*; use kvm_ioctls::{VcpuExit, VcpuFd}; -use uhypercall_interface::*; +use uhypercall_interface::{Hypercall, UHYVE_PORT_NETWRITE, UHYVE_UART_PORT}; use x86_64::{ registers::control::{Cr0Flags, Cr4Flags}, structures::paging::PageTableFlags, @@ -361,118 +361,70 @@ impl VirtualCPU for UhyveCPU { } }, VcpuExit::IoOut(port, addr) => { - match port { - UHYVE_UART_PORT => { - self.uart(addr)?; - } - UHYVE_PORT_CMDSIZE => { - let data_addr: usize = - unsafe { (*(addr.as_ptr() as *const u32)) as usize }; - let syssize = unsafe { - &mut *(self.host_address(data_addr) as *mut SysCmdsize) - }; - self.cmdsize(syssize); - } - UHYVE_PORT_CMDVAL => { - let data_addr: usize = - unsafe { (*(addr.as_ptr() as *const u32)) as usize }; - let syscmdval = - unsafe { &*(self.host_address(data_addr) as *const SysCmdval) }; - self.cmdval(syscmdval); - } - UHYVE_PORT_NETWRITE => { - match &self.tx { - Some(tx_channel) => tx_channel.send(1).unwrap(), + let data_addr: usize = unsafe { (*(addr.as_ptr() as *const u32)) as usize }; + if let Some(hypercall) = self.port_to_hypercall(port, data_addr) { + match hypercall { + Hypercall::Cmdsize(syssize) => self.cmdsize(syssize), + Hypercall::Cmdval(syscmdval) => self.cmdval(syscmdval), + Hypercall::Exit(sysexit) => { + return Ok(VcpuStopReason::Exit(self.exit(sysexit))); + } + Hypercall::FileClose(sysclose) => self.close(sysclose), + Hypercall::FileLseek(syslseek) => self.lseek(syslseek), + Hypercall::FileOpen(sysopen) => self.open(sysopen), + Hypercall::FileRead(sysread) => self.read(sysread), + Hypercall::FileWrite(syswrite) => self.write(syswrite)?, + Hypercall::FileUnlink(sysunlink) => self.unlink(sysunlink), + _ => panic!("Got unknown hypercall {:?}", hypercall), + }; + } else { + match port { + UHYVE_UART_PORT => { + self.uart(addr)?; + } + UHYVE_PORT_NETWRITE => { + match &self.tx { + Some(tx_channel) => tx_channel.send(1).unwrap(), - None => {} - }; - } - UHYVE_PORT_EXIT => { - let data_addr: usize = - unsafe { (*(addr.as_ptr() as *const u32)) as usize }; - let sysexit = - unsafe { &*(self.host_address(data_addr) as *const SysExit) }; - return Ok(VcpuStopReason::Exit(self.exit(sysexit))); - } - UHYVE_PORT_OPEN => { - let data_addr: usize = - unsafe { (*(addr.as_ptr() as *const u32)) as usize }; - let sysopen = - unsafe { &mut *(self.host_address(data_addr) as *mut SysOpen) }; - self.open(sysopen); - } - UHYVE_PORT_WRITE => { - let data_addr: usize = - unsafe { (*(addr.as_ptr() as *const u32)) as usize }; - let syswrite = - unsafe { &*(self.host_address(data_addr) as *const SysWrite) }; - self.write(syswrite)?; - } - UHYVE_PORT_READ => { - let data_addr: usize = - unsafe { (*(addr.as_ptr() as *const u32)) as usize }; - let sysread = - unsafe { &mut *(self.host_address(data_addr) as *mut SysRead) }; - self.read(sysread); - } - UHYVE_PORT_UNLINK => { - let data_addr: usize = - unsafe { (*(addr.as_ptr() as *const u32)) as usize }; - let sysunlink = unsafe { - &mut *(self.host_address(data_addr) as *mut SysUnlink) - }; - self.unlink(sysunlink); - } - UHYVE_PORT_LSEEK => { - let data_addr: usize = - unsafe { (*(addr.as_ptr() as *const u32)) as usize }; - let syslseek = unsafe { - &mut *(self.host_address(data_addr) as *mut SysLseek) - }; - self.lseek(syslseek); - } - UHYVE_PORT_CLOSE => { - let data_addr: usize = - unsafe { (*(addr.as_ptr() as *const u32)) as usize }; - let sysclose = unsafe { - &mut *(self.host_address(data_addr) as *mut SysClose) - }; - self.close(sysclose); - } - //TODO: - PCI_CONFIG_DATA_PORT => { - if let Some(pci_addr) = self.pci_addr { - if pci_addr & 0x1ff800 == 0 { - let mut virtio_device = self.virtio_device.lock().unwrap(); - virtio_device.handle_write(pci_addr & 0x3ff, addr); + None => {} + }; + } + //TODO: + PCI_CONFIG_DATA_PORT => { + if let Some(pci_addr) = self.pci_addr { + if pci_addr & 0x1ff800 == 0 { + let mut virtio_device = + self.virtio_device.lock().unwrap(); + virtio_device.handle_write(pci_addr & 0x3ff, addr); + } } } - } - PCI_CONFIG_ADDRESS_PORT => { - self.pci_addr = Some(unsafe { *(addr.as_ptr() as *const u32) }); - } - VIRTIO_PCI_STATUS => { - let mut virtio_device = self.virtio_device.lock().unwrap(); - virtio_device.write_status(addr); - } - VIRTIO_PCI_GUEST_FEATURES => { - let mut virtio_device = self.virtio_device.lock().unwrap(); - virtio_device.write_requested_features(addr); - } - VIRTIO_PCI_QUEUE_NOTIFY => { - let mut virtio_device = self.virtio_device.lock().unwrap(); - virtio_device.handle_notify_output(addr, self); - } - VIRTIO_PCI_QUEUE_SEL => { - let mut virtio_device = self.virtio_device.lock().unwrap(); - virtio_device.write_selected_queue(addr); - } - VIRTIO_PCI_QUEUE_PFN => { - let mut virtio_device = self.virtio_device.lock().unwrap(); - virtio_device.write_pfn(addr, self); - } - _ => { - panic!("Unhandled IO exit: 0x{port:x}"); + PCI_CONFIG_ADDRESS_PORT => { + self.pci_addr = Some(unsafe { *(addr.as_ptr() as *const u32) }); + } + VIRTIO_PCI_STATUS => { + let mut virtio_device = self.virtio_device.lock().unwrap(); + virtio_device.write_status(addr); + } + VIRTIO_PCI_GUEST_FEATURES => { + let mut virtio_device = self.virtio_device.lock().unwrap(); + virtio_device.write_requested_features(addr); + } + VIRTIO_PCI_QUEUE_NOTIFY => { + let mut virtio_device = self.virtio_device.lock().unwrap(); + virtio_device.handle_notify_output(addr, self); + } + VIRTIO_PCI_QUEUE_SEL => { + let mut virtio_device = self.virtio_device.lock().unwrap(); + virtio_device.write_selected_queue(addr); + } + VIRTIO_PCI_QUEUE_PFN => { + let mut virtio_device = self.virtio_device.lock().unwrap(); + virtio_device.write_pfn(addr, self); + } + _ => { + panic!("Unhandled IO exit: 0x{:x}", port); + } } } } diff --git a/src/macos/x86_64/vcpu.rs b/src/macos/x86_64/vcpu.rs index 6220715c..ef26a560 100644 --- a/src/macos/x86_64/vcpu.rs +++ b/src/macos/x86_64/vcpu.rs @@ -10,6 +10,7 @@ use std::{ use burst::x86::{disassemble_64, InstructionOperation, OperandType}; use lazy_static::lazy_static; use log::{debug, trace}; +use uhypercall_interface::{Hypercall, UHYVE_UART_PORT}; use x86_64::{ registers::control::{Cr0Flags, Cr4Flags}, structures::{gdt::SegmentSelector, paging::PageTableFlags}, @@ -33,10 +34,7 @@ use xhypervisor::{ use crate::{ consts::*, macos::x86_64::ioapic::IoApic, - vm::{ - HypervisorResult, SysClose, SysCmdsize, SysCmdval, SysExit, SysLseek, SysOpen, SysRead, - SysUnlink, SysWrite, VcpuStopReason, VirtualCPU, - }, + vm::{HypervisorResult, VcpuStopReason, VirtualCPU}, }; /// Extracted from `x86::msr`. @@ -746,96 +744,42 @@ impl VirtualCPU for UhyveCPU { assert!(!input, "Invalid I/O operation"); - match port { - UHYVE_UART_PORT => { - let al = (self.vcpu.read_register(&Register::RAX)? & 0xFF) as u8; - - self.uart(&[al]).unwrap(); - self.vcpu.write_register(&Register::RIP, rip + len)?; - } - UHYVE_PORT_CMDSIZE => { - let data_addr: u64 = - self.vcpu.read_register(&Register::RAX)? & 0xFFFFFFFF; - let syssize = unsafe { - &mut *(self.host_address(data_addr as usize) as *mut SysCmdsize) - }; - self.cmdsize(syssize); - self.vcpu.write_register(&Register::RIP, rip + len)?; - } - UHYVE_PORT_CMDVAL => { - let data_addr: u64 = - self.vcpu.read_register(&Register::RAX)? & 0xFFFFFFFF; - let syscmdval = unsafe { - &*(self.host_address(data_addr as usize) as *const SysCmdval) - }; - self.cmdval(syscmdval); - self.vcpu.write_register(&Register::RIP, rip + len)?; - } - UHYVE_PORT_EXIT => { - let data_addr: u64 = - self.vcpu.read_register(&Register::RAX)? & 0xFFFFFFFF; - let sysexit = unsafe { - &*(self.host_address(data_addr as usize) as *const SysExit) - }; - return Ok(VcpuStopReason::Exit(self.exit(sysexit))); - } - UHYVE_PORT_OPEN => { - let data_addr: u64 = - self.vcpu.read_register(&Register::RAX)? & 0xFFFFFFFF; - let sysopen = unsafe { - &mut *(self.host_address(data_addr as usize) as *mut SysOpen) - }; - self.open(sysopen); - self.vcpu.write_register(&Register::RIP, rip + len)?; - } - UHYVE_PORT_WRITE => { - let data_addr: u64 = - self.vcpu.read_register(&Register::RAX)? & 0xFFFFFFFF; - let syswrite = unsafe { - &*(self.host_address(data_addr as usize) as *const SysWrite) - }; - self.write(syswrite).unwrap(); - self.vcpu.write_register(&Register::RIP, rip + len)?; - } - UHYVE_PORT_READ => { - let data_addr: u64 = - self.vcpu.read_register(&Register::RAX)? & 0xFFFFFFFF; - let sysread = unsafe { - &mut *(self.host_address(data_addr as usize) as *mut SysRead) - }; - self.read(sysread); - self.vcpu.write_register(&Register::RIP, rip + len)?; - } - UHYVE_PORT_UNLINK => { - let data_addr: u64 = - self.vcpu.read_register(&Register::RAX)? & 0xFFFFFFFF; - let sysunlink = unsafe { - &mut *(self.host_address(data_addr as usize) as *mut SysUnlink) - }; - self.unlink(sysunlink); - self.vcpu.write_register(&Register::RIP, rip + len)?; - } - UHYVE_PORT_LSEEK => { - let data_addr: u64 = - self.vcpu.read_register(&Register::RAX)? & 0xFFFFFFFF; - let syslseek = unsafe { - &mut *(self.host_address(data_addr as usize) as *mut SysLseek) - }; - self.lseek(syslseek); - self.vcpu.write_register(&Register::RIP, rip + len)?; - } - UHYVE_PORT_CLOSE => { - let data_addr: u64 = - self.vcpu.read_register(&Register::RAX)? & 0xFFFFFFFF; - let sysclose = unsafe { - &mut *(self.host_address(data_addr as usize) as *mut SysClose) - }; - self.close(sysclose); - self.vcpu.write_register(&Register::RIP, rip + len)?; + let data_addr: u64 = self.vcpu.read_register(&Register::RAX)? & 0xFFFFFFFF; + if let Some(hypercall) = self.port_to_hypercall(port, data_addr as usize) { + match hypercall { + Hypercall::Cmdsize(syssize) => self.cmdsize(syssize), + Hypercall::Cmdval(syscmdval) => self.cmdval(syscmdval), + Hypercall::Exit(sysexit) => { + return Ok(VcpuStopReason::Exit(self.exit(sysexit))) + } + Hypercall::FileClose(sysclose) => self.close(sysclose), + Hypercall::FileLseek(syslseek) => self.lseek(syslseek), + Hypercall::FileOpen(sysopen) => self.open(sysopen), + Hypercall::FileRead(sysread) => self.read(sysread), + Hypercall::FileWrite(syswrite) => { + // Return an error for proper handling + self.write(syswrite).unwrap() + } + Hypercall::FileUnlink(sysunlink) => self.unlink(sysunlink), + _ => panic!("Got unknown hypercall {:?}", hypercall), } - _ => { - trace!("Receive unhandled output command at port 0x{:x}", port); - self.vcpu.write_register(&Register::RIP, rip + len)?; + self.vcpu.write_register(&Register::RIP, rip + len)?; + } else { + match port { + // TODO: Deprecate (not used in Linux anyway) + SHUTDOWN_PORT => { + return Ok(VcpuStopReason::Exit(0)); + } + UHYVE_UART_PORT => { + let al = (self.vcpu.read_register(&Register::RAX)? & 0xFF) as u8; + + self.uart(&[al]).unwrap(); + self.vcpu.write_register(&Register::RIP, rip + len)?; + } + _ => { + error!("Receive unhandled output command at port 0x{:x}", port); + self.vcpu.write_register(&Register::RIP, rip + len)?; + } } } } diff --git a/src/vm.rs b/src/vm.rs index 7cc3c225..0792af75 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -71,6 +71,55 @@ pub trait VirtualCPU { fn args(&self) -> &[OsString]; + /// addr is the address of the hypercall parameter in the guest's memory space. + fn port_to_hypercall(&self, port: u16, data_addr: usize) -> Option> { + if let Ok(hypercall_port) = HypercallPorts::try_from(port) { + Some(match hypercall_port { + HypercallPorts::FileClose => { + let sysclose = unsafe { &mut *(self.host_address(data_addr) as *mut SysClose) }; + Hypercall::FileClose(sysclose) + } + HypercallPorts::FileLseek => { + let syslseek = unsafe { &mut *(self.host_address(data_addr) as *mut SysLseek) }; + Hypercall::FileLseek(syslseek) + } + HypercallPorts::FileOpen => { + let sysopen = unsafe { &mut *(self.host_address(data_addr) as *mut SysOpen) }; + Hypercall::FileOpen(sysopen) + } + HypercallPorts::FileRead => { + let sysread = unsafe { &mut *(self.host_address(data_addr) as *mut SysRead) }; + Hypercall::FileRead(sysread) + } + HypercallPorts::FileWrite => { + let syswrite = unsafe { &*(self.host_address(data_addr) as *const SysWrite) }; + Hypercall::FileWrite(syswrite) + } + HypercallPorts::FileUnlink => { + let sysunlink = + unsafe { &mut *(self.host_address(data_addr) as *mut SysUnlink) }; + Hypercall::FileUnlink(sysunlink) + } + HypercallPorts::Exit => { + let sysexit = unsafe { &*(self.host_address(data_addr) as *const SysExit) }; + Hypercall::Exit(sysexit) + } + HypercallPorts::Cmdsize => { + let syssize = + unsafe { &mut *(self.host_address(data_addr) as *mut SysCmdsize) }; + Hypercall::Cmdsize(syssize) + } + HypercallPorts::Cmdval => { + let syscmdval = unsafe { &*(self.host_address(data_addr) as *const SysCmdval) }; + Hypercall::Cmdval(syscmdval) + } + _ => unimplemented!(), + }) + } else { + None + } + } + fn cmdsize(&self, syssize: &mut SysCmdsize) { syssize.argc = 0; syssize.envc = 0; @@ -175,7 +224,7 @@ pub trait VirtualCPU { fn open(&self, sysopen: &mut SysOpen) { unsafe { sysopen.ret = libc::open( - self.host_address(sysopen.name as usize) as *const i8, + self.host_address(sysopen.name.as_u64() as usize) as *const i8, sysopen.flags, sysopen.mode, ); @@ -192,7 +241,7 @@ pub trait VirtualCPU { /// Handles an read syscall on the host. fn read(&self, sysread: &mut SysRead) { unsafe { - let buffer = self.virt_to_phys(sysread.buf as usize); + let buffer = self.virt_to_phys(sysread.buf.as_u64() as usize); let bytes_read = libc::read( sysread.fd, @@ -210,7 +259,7 @@ pub trait VirtualCPU { /// Handles an write syscall on the host. fn write(&self, syswrite: &SysWrite) -> io::Result<()> { let mut bytes_written: usize = 0; - let buffer = self.virt_to_phys(syswrite.buf as usize); + let buffer = self.virt_to_phys(syswrite.buf.as_u64() as usize); while bytes_written != syswrite.len { unsafe { From a48b067507892aabbb38f63e056e0c30ee7d6a7a Mon Sep 17 00:00:00 2001 From: Jonathan Klimt Date: Wed, 4 Jan 2023 16:26:33 +0100 Subject: [PATCH 07/21] Make the project a cargo workspace containing uhyve and the uhypercall interface --- Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index ea71f472..6ecfbab6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,6 @@ +[workspace] +exclude = ["tests/test-kernels", "rusty-hermit", "rusty-hermit/*", "libhermit-rs"] + [package] name = "uhyve" version = "0.2.2" From d0049f6660275a3e4439bcf30557934142cb8f41 Mon Sep 17 00:00:00 2001 From: Jonathan Klimt Date: Wed, 4 Jan 2023 16:54:05 +0100 Subject: [PATCH 08/21] Renamed uhypercall-interface to uhyve-interface --- Cargo.lock | 18 +++++++++--------- Cargo.toml | 2 +- src/linux/vcpu.rs | 2 +- src/macos/x86_64/vcpu.rs | 2 +- src/vm.rs | 2 +- .../.gitignore | 0 .../Cargo.toml | 8 ++++++-- .../src/lib.rs | 0 8 files changed, 19 insertions(+), 15 deletions(-) rename {uhypercall-interface => uhyve-interface}/.gitignore (100%) rename {uhypercall-interface => uhyve-interface}/Cargo.toml (66%) rename {uhypercall-interface => uhyve-interface}/src/lib.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 5e97fce6..175efefe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1116,14 +1116,6 @@ dependencies = [ "cc", ] -[[package]] -name = "uhypercall-interface" -version = "0.1.0" -dependencies = [ - "num_enum", - "x86_64", -] - [[package]] name = "uhyve" version = "0.2.2" @@ -1153,13 +1145,21 @@ dependencies = [ "thiserror", "time", "tun-tap", - "uhypercall-interface", + "uhyve-interface", "virtio-bindings", "vmm-sys-util", "x86_64", "xhypervisor", ] +[[package]] +name = "uhyve-interface" +version = "0.1.0" +dependencies = [ + "num_enum", + "x86_64", +] + [[package]] name = "unicode-ident" version = "1.0.8" diff --git a/Cargo.toml b/Cargo.toml index 6ecfbab6..1ecfbb94 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,7 +53,7 @@ libc = "0.2" log = "0.4" thiserror = "1.0" time = "0.3" -uhypercall-interface = {path = "./uhypercall-interface"} +uhyve-interface = { path = "./uhyve-interface" } rftrace = { version = "0.1", optional = true } rftrace-frontend = { version = "0.1", optional = true } diff --git a/src/linux/vcpu.rs b/src/linux/vcpu.rs index dc5d9f56..955eb9d2 100755 --- a/src/linux/vcpu.rs +++ b/src/linux/vcpu.rs @@ -7,7 +7,7 @@ use std::{ use kvm_bindings::*; use kvm_ioctls::{VcpuExit, VcpuFd}; -use uhypercall_interface::{Hypercall, UHYVE_PORT_NETWRITE, UHYVE_UART_PORT}; +use uhyve_interface::{Hypercall, UHYVE_PORT_NETWRITE, UHYVE_UART_PORT}; use x86_64::{ registers::control::{Cr0Flags, Cr4Flags}, structures::paging::PageTableFlags, diff --git a/src/macos/x86_64/vcpu.rs b/src/macos/x86_64/vcpu.rs index ef26a560..0aacd8c3 100644 --- a/src/macos/x86_64/vcpu.rs +++ b/src/macos/x86_64/vcpu.rs @@ -10,7 +10,7 @@ use std::{ use burst::x86::{disassemble_64, InstructionOperation, OperandType}; use lazy_static::lazy_static; use log::{debug, trace}; -use uhypercall_interface::{Hypercall, UHYVE_UART_PORT}; +use uhyve_interface::{Hypercall, UHYVE_UART_PORT}; use x86_64::{ registers::control::{Cr0Flags, Cr4Flags}, structures::{gdt::SegmentSelector, paging::PageTableFlags}, diff --git a/src/vm.rs b/src/vm.rs index 0792af75..eb3e9cdc 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -9,7 +9,7 @@ use hermit_entry::{ }; use log::{error, warn}; use thiserror::Error; -use uhypercall_interface::*; +use uhyve_interface::*; #[cfg(target_arch = "x86_64")] use crate::arch::x86_64::{ diff --git a/uhypercall-interface/.gitignore b/uhyve-interface/.gitignore similarity index 100% rename from uhypercall-interface/.gitignore rename to uhyve-interface/.gitignore diff --git a/uhypercall-interface/Cargo.toml b/uhyve-interface/Cargo.toml similarity index 66% rename from uhypercall-interface/Cargo.toml rename to uhyve-interface/Cargo.toml index ceb42a05..4640fb06 100644 --- a/uhypercall-interface/Cargo.toml +++ b/uhyve-interface/Cargo.toml @@ -1,10 +1,14 @@ [package] -name = "uhypercall-interface" +name = "uhyve-interface" version = "0.1.0" edition = "2021" +authors = [ + "Jonathan Klimt ", + "Stefan Lankes ", +] description = "The interface between uhyve and a guest VM" repository = "https://github.com/hermitcore/uhyve" -license = "MIT/Apache-2.0" +license = "MIT OR Apache-2.0" keywords = ["hypervisor", "unikernel", "hermit"] categories = ["os"] diff --git a/uhypercall-interface/src/lib.rs b/uhyve-interface/src/lib.rs similarity index 100% rename from uhypercall-interface/src/lib.rs rename to uhyve-interface/src/lib.rs From 16bca62387adb7b5d8532abb920087e7e1551a5d Mon Sep 17 00:00:00 2001 From: Jonathan Klimt Date: Wed, 4 Jan 2023 17:43:44 +0100 Subject: [PATCH 09/21] Renamed HypercallPorts to IoPorts --- src/vm.rs | 20 ++++++++++---------- uhyve-interface/src/lib.rs | 8 ++++---- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/vm.rs b/src/vm.rs index eb3e9cdc..f58da339 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -73,43 +73,43 @@ pub trait VirtualCPU { /// addr is the address of the hypercall parameter in the guest's memory space. fn port_to_hypercall(&self, port: u16, data_addr: usize) -> Option> { - if let Ok(hypercall_port) = HypercallPorts::try_from(port) { + if let Ok(hypercall_port) = IoPorts::try_from(port) { Some(match hypercall_port { - HypercallPorts::FileClose => { + IoPorts::FileClose => { let sysclose = unsafe { &mut *(self.host_address(data_addr) as *mut SysClose) }; Hypercall::FileClose(sysclose) } - HypercallPorts::FileLseek => { + IoPorts::FileLseek => { let syslseek = unsafe { &mut *(self.host_address(data_addr) as *mut SysLseek) }; Hypercall::FileLseek(syslseek) } - HypercallPorts::FileOpen => { + IoPorts::FileOpen => { let sysopen = unsafe { &mut *(self.host_address(data_addr) as *mut SysOpen) }; Hypercall::FileOpen(sysopen) } - HypercallPorts::FileRead => { + IoPorts::FileRead => { let sysread = unsafe { &mut *(self.host_address(data_addr) as *mut SysRead) }; Hypercall::FileRead(sysread) } - HypercallPorts::FileWrite => { + IoPorts::FileWrite => { let syswrite = unsafe { &*(self.host_address(data_addr) as *const SysWrite) }; Hypercall::FileWrite(syswrite) } - HypercallPorts::FileUnlink => { + IoPorts::FileUnlink => { let sysunlink = unsafe { &mut *(self.host_address(data_addr) as *mut SysUnlink) }; Hypercall::FileUnlink(sysunlink) } - HypercallPorts::Exit => { + IoPorts::Exit => { let sysexit = unsafe { &*(self.host_address(data_addr) as *const SysExit) }; Hypercall::Exit(sysexit) } - HypercallPorts::Cmdsize => { + IoPorts::Cmdsize => { let syssize = unsafe { &mut *(self.host_address(data_addr) as *mut SysCmdsize) }; Hypercall::Cmdsize(syssize) } - HypercallPorts::Cmdval => { + IoPorts::Cmdval => { let syscmdval = unsafe { &*(self.host_address(data_addr) as *const SysCmdval) }; Hypercall::Cmdval(syscmdval) } diff --git a/uhyve-interface/src/lib.rs b/uhyve-interface/src/lib.rs index 84d442a1..3060ca67 100644 --- a/uhyve-interface/src/lib.rs +++ b/uhyve-interface/src/lib.rs @@ -3,7 +3,7 @@ //! The uhyve hypercall interface works as follows: //! //! - On `x86_64` you use an out port instruction. The address of the `out`-port corresponds to the -//! hypercall you want to use. You can obtain it from the [`HypercallPorts`] enum. The data send to +//! hypercall you want to use. You can obtain it from the [`IoPorts`] enum. The data send to //! that port is the physical memory address (of the VM) of the parameters of that hypercall. //! - On `aarch64` you write to the respective [`HypercallAddress`]. The 64-bit value written to that location is the guest's physical memory address of the hypercall's parameter. @@ -22,7 +22,7 @@ use x86_64::PhysAddr; #[non_exhaustive] #[repr(u16)] #[derive(Debug, Eq, PartialEq, TryFromPrimitive)] -pub enum HypercallPorts { +pub enum IoPorts { /// Port address = `0x400` FileWrite = 0x400, /// Port address = `0x440` @@ -48,7 +48,7 @@ pub enum HypercallPorts { /// Port address = `0x840` FileUnlink = 0x840, } -impl From> for HypercallPorts { +impl From> for IoPorts { fn from(value: Hypercall) -> Self { match value { Hypercall::Cmdsize(_) => Self::Cmdsize, @@ -86,7 +86,7 @@ pub enum Hypercall<'a> { impl<'a> Hypercall<'a> { /// Get a hypercall's port address. pub fn port(self) -> u16 { - HypercallPorts::from(self) as u16 + IoPorts::from(self) as u16 } } From b2cebb2d1c344d4c40250c4c55d92ba33a205237 Mon Sep 17 00:00:00 2001 From: Jonathan Klimt Date: Wed, 4 Jan 2023 17:44:17 +0100 Subject: [PATCH 10/21] Added the UART/Serial output to the hypercalls --- src/linux/vcpu.rs | 7 ++----- src/macos/x86_64/vcpu.rs | 13 ++++++------- src/vm.rs | 6 +++++- uhyve-interface/src/lib.rs | 5 +++++ 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/linux/vcpu.rs b/src/linux/vcpu.rs index 955eb9d2..71835350 100755 --- a/src/linux/vcpu.rs +++ b/src/linux/vcpu.rs @@ -7,7 +7,7 @@ use std::{ use kvm_bindings::*; use kvm_ioctls::{VcpuExit, VcpuFd}; -use uhyve_interface::{Hypercall, UHYVE_PORT_NETWRITE, UHYVE_UART_PORT}; +use uhyve_interface::{Hypercall, UHYVE_PORT_NETWRITE}; use x86_64::{ registers::control::{Cr0Flags, Cr4Flags}, structures::paging::PageTableFlags, @@ -375,17 +375,14 @@ impl VirtualCPU for UhyveCPU { Hypercall::FileRead(sysread) => self.read(sysread), Hypercall::FileWrite(syswrite) => self.write(syswrite)?, Hypercall::FileUnlink(sysunlink) => self.unlink(sysunlink), + Hypercall::SerialWrite(buf) => self.uart(buf)?, _ => panic!("Got unknown hypercall {:?}", hypercall), }; } else { match port { - UHYVE_UART_PORT => { - self.uart(addr)?; - } UHYVE_PORT_NETWRITE => { match &self.tx { Some(tx_channel) => tx_channel.send(1).unwrap(), - None => {} }; } diff --git a/src/macos/x86_64/vcpu.rs b/src/macos/x86_64/vcpu.rs index 0aacd8c3..10a22286 100644 --- a/src/macos/x86_64/vcpu.rs +++ b/src/macos/x86_64/vcpu.rs @@ -10,7 +10,7 @@ use std::{ use burst::x86::{disassemble_64, InstructionOperation, OperandType}; use lazy_static::lazy_static; use log::{debug, trace}; -use uhyve_interface::{Hypercall, UHYVE_UART_PORT}; +use uhyve_interface::Hypercall; use x86_64::{ registers::control::{Cr0Flags, Cr4Flags}, structures::{gdt::SegmentSelector, paging::PageTableFlags}, @@ -761,6 +761,11 @@ impl VirtualCPU for UhyveCPU { self.write(syswrite).unwrap() } Hypercall::FileUnlink(sysunlink) => self.unlink(sysunlink), + Hypercall::SerialWrite(_buf) => { + // TODO Not sure why this call works different on macos... + let al = (self.vcpu.read_register(&Register::RAX)? & 0xFF) as u8; + self.uart(&[al]).unwrap(); + } _ => panic!("Got unknown hypercall {:?}", hypercall), } self.vcpu.write_register(&Register::RIP, rip + len)?; @@ -770,12 +775,6 @@ impl VirtualCPU for UhyveCPU { SHUTDOWN_PORT => { return Ok(VcpuStopReason::Exit(0)); } - UHYVE_UART_PORT => { - let al = (self.vcpu.read_register(&Register::RAX)? & 0xFF) as u8; - - self.uart(&[al]).unwrap(); - self.vcpu.write_register(&Register::RIP, rip + len)?; - } _ => { error!("Receive unhandled output command at port 0x{:x}", port); self.vcpu.write_register(&Register::RIP, rip + len)?; diff --git a/src/vm.rs b/src/vm.rs index f58da339..222f38e3 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -113,6 +113,10 @@ pub trait VirtualCPU { let syscmdval = unsafe { &*(self.host_address(data_addr) as *const SysCmdval) }; Hypercall::Cmdval(syscmdval) } + IoPorts::Uart => { + let buf = unsafe { &*(self.host_address(data_addr) as *const &[u8]) }; + Hypercall::SerialWrite(buf) + } _ => unimplemented!(), }) } else { @@ -342,7 +346,7 @@ pub trait Vm { phys_addr_range: arch::RAM_START..arch::RAM_START + vm_mem_len as u64, serial_port_base: self .verbose() - .then(|| SerialPortBase::new(UHYVE_UART_PORT.into()).unwrap()), + .then(|| SerialPortBase::new(uhyve_interface::IoPorts::Uart as u16).unwrap()), device_tree: None, }, load_info, diff --git a/uhyve-interface/src/lib.rs b/uhyve-interface/src/lib.rs index 3060ca67..22fc13be 100644 --- a/uhyve-interface/src/lib.rs +++ b/uhyve-interface/src/lib.rs @@ -45,6 +45,8 @@ pub enum IoPorts { Cmdsize = 0x740, /// Port address = `0x780` Cmdval = 0x780, + /// Port address = `0x800` + Uart = 0x800, /// Port address = `0x840` FileUnlink = 0x840, } @@ -60,6 +62,7 @@ impl From> for IoPorts { Hypercall::FileRead(_) => Self::FileRead, Hypercall::FileWrite(_) => Self::FileWrite, Hypercall::FileUnlink(_) => Self::FileUnlink, + Hypercall::SerialWrite(_) => Self::Uart, } } } @@ -82,6 +85,8 @@ pub enum Hypercall<'a> { FileRead(&'a mut SysRead), FileWrite(&'a SysWrite), FileUnlink(&'a mut SysUnlink), + /// Write a buffer to the terminal. + SerialWrite(&'a [u8]), } impl<'a> Hypercall<'a> { /// Get a hypercall's port address. From e57795adc9c42d23f3777d34ac334682f4e11e5d Mon Sep 17 00:00:00 2001 From: Jonathan Klimt Date: Wed, 4 Jan 2023 17:49:55 +0100 Subject: [PATCH 11/21] removed new fns on hypercall interface parameters. --- uhyve-interface/src/lib.rs | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/uhyve-interface/src/lib.rs b/uhyve-interface/src/lib.rs index 22fc13be..b2f03b46 100644 --- a/uhyve-interface/src/lib.rs +++ b/uhyve-interface/src/lib.rs @@ -132,17 +132,6 @@ pub struct SysCmdsize { /// Length of the items in the environment. pub envsz: [i32; MAX_ARGC_ENVC], } -impl SysCmdsize { - // TODO: Default not new - pub fn new() -> SysCmdsize { - SysCmdsize { - argc: 0, - argsz: [0; MAX_ARGC_ENVC], - envc: 0, - envsz: [0; MAX_ARGC_ENVC], - } - } -} /// Parameters for a [`Cmdval`](Hypercall::Cmdval) hypercall, which copies the arguments end environment of the application into the VM's memory. #[repr(C, packed)] @@ -153,11 +142,6 @@ pub struct SysCmdval { /// Pointer to a memory section in the VM memory large enough to store the environment values. pub envp: PhysAddr, } -impl SysCmdval { - pub fn new(argv: PhysAddr, envp: PhysAddr) -> SysCmdval { - SysCmdval { argv, envp } - } -} /// Parameters for a [`Exit`](Hypercall::Exit) hypercall. #[repr(C, packed)] @@ -166,11 +150,6 @@ pub struct SysExit { /// The return code of the guest. pub arg: i32, } -impl SysExit { - pub fn new(arg: i32) -> SysExit { - SysExit { arg } - } -} /// Parameters for a [`FileUnlink`](Hypercall::FileUnlink) hypercall. #[repr(C, packed)] @@ -181,11 +160,6 @@ pub struct SysUnlink { /// On success, `0` is returned. On error, `-1` is returned. pub ret: i32, } -impl SysUnlink { - pub fn new(name: PhysAddr) -> SysUnlink { - SysUnlink { name, ret: -1 } - } -} /// Parameters for a [`FileWrite`](Hypercall::FileWrite) hypercall. #[repr(C, packed)] From a2d5ee8345af7f0e3e11d0765ea7b83d9a84d99d Mon Sep 17 00:00:00 2001 From: Jonathan Klimt Date: Wed, 4 Jan 2023 17:59:20 +0100 Subject: [PATCH 12/21] Remove the consts from the interface --- uhyve-interface/src/lib.rs | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/uhyve-interface/src/lib.rs b/uhyve-interface/src/lib.rs index b2f03b46..8c10413f 100644 --- a/uhyve-interface/src/lib.rs +++ b/uhyve-interface/src/lib.rs @@ -95,26 +95,9 @@ impl<'a> Hypercall<'a> { } } -pub const UHYVE_PORT_WRITE: u16 = 0x400; -pub const UHYVE_PORT_OPEN: u16 = 0x440; -pub const UHYVE_PORT_CLOSE: u16 = 0x480; -pub const UHYVE_PORT_READ: u16 = 0x500; -pub const UHYVE_PORT_EXIT: u16 = 0x540; -pub const UHYVE_PORT_LSEEK: u16 = 0x580; - // Networkports (not used at the moment) // TODO: Update interface pub const UHYVE_PORT_NETWRITE: u16 = 0x640; -pub const UHYVE_PORT_NETREAD: u16 = 0x680; -pub const UHYVE_PORT_NETSTAT: u16 = 0x700; - -/* Ports and data structures for uhyve command line arguments and envp - * forwarding */ -pub const UHYVE_PORT_CMDSIZE: u16 = 0x740; -pub const UHYVE_PORT_CMDVAL: u16 = 0x780; - -pub const UHYVE_UART_PORT: u16 = 0x800; -pub const UHYVE_PORT_UNLINK: u16 = 0x840; // FIXME: Do not use a fix number of arguments pub const MAX_ARGC_ENVC: usize = 128; From b681e8d1161446a4c04a91fbac8db3ed1820feba Mon Sep 17 00:00:00 2001 From: Jonathan Klimt Date: Wed, 4 Jan 2023 18:05:23 +0100 Subject: [PATCH 13/21] Moved hypercall parameters into separate module --- src/vm.rs | 2 +- uhyve-interface/src/lib.rs | 111 ++---------------------------- uhyve-interface/src/parameters.rs | 109 +++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+), 108 deletions(-) create mode 100644 uhyve-interface/src/parameters.rs diff --git a/src/vm.rs b/src/vm.rs index 222f38e3..094a72d0 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -9,7 +9,7 @@ use hermit_entry::{ }; use log::{error, warn}; use thiserror::Error; -use uhyve_interface::*; +use uhyve_interface::{parameters::*, Hypercall, IoPorts, MAX_ARGC_ENVC}; #[cfg(target_arch = "x86_64")] use crate::arch::x86_64::{ diff --git a/uhyve-interface/src/lib.rs b/uhyve-interface/src/lib.rs index 8c10413f..594888e7 100644 --- a/uhyve-interface/src/lib.rs +++ b/uhyve-interface/src/lib.rs @@ -13,7 +13,9 @@ // TODO: Throw this out, once https://github.com/rust-lang/rfcs/issues/2783 or https://github.com/rust-lang/rust/issues/86772 is resolved use num_enum::TryFromPrimitive; -use x86_64::PhysAddr; + +pub mod parameters; +use parameters::*; /// Enum containing all valid port mappings for hypercalls. /// @@ -96,113 +98,8 @@ impl<'a> Hypercall<'a> { } // Networkports (not used at the moment) -// TODO: Update interface +// TODO: Remove this pub const UHYVE_PORT_NETWRITE: u16 = 0x640; // FIXME: Do not use a fix number of arguments pub const MAX_ARGC_ENVC: usize = 128; - -/// Parameters for a [`Cmdsize`](Hypercall::Cmdsize) hypercall which provides the lengths of the items in the argument end environment vector. -#[repr(C, packed)] -#[derive(Debug, Copy, Clone)] -pub struct SysCmdsize { - /// Nr of items in the kernel command line. - pub argc: i32, - /// Lengths of the items in the kernel command line. - pub argsz: [i32; MAX_ARGC_ENVC], - /// Nr of items in the environment. - pub envc: i32, - /// Length of the items in the environment. - pub envsz: [i32; MAX_ARGC_ENVC], -} - -/// Parameters for a [`Cmdval`](Hypercall::Cmdval) hypercall, which copies the arguments end environment of the application into the VM's memory. -#[repr(C, packed)] -#[derive(Debug, Copy, Clone)] -pub struct SysCmdval { - /// Pointer to a memory section in the VM memory large enough to store the argument string. - pub argv: PhysAddr, - /// Pointer to a memory section in the VM memory large enough to store the environment values. - pub envp: PhysAddr, -} - -/// Parameters for a [`Exit`](Hypercall::Exit) hypercall. -#[repr(C, packed)] -#[derive(Debug, Copy, Clone)] -pub struct SysExit { - /// The return code of the guest. - pub arg: i32, -} - -/// Parameters for a [`FileUnlink`](Hypercall::FileUnlink) hypercall. -#[repr(C, packed)] -#[derive(Debug, Copy, Clone)] -pub struct SysUnlink { - /// Address of the file that should be unlinked. - pub name: PhysAddr, - /// On success, `0` is returned. On error, `-1` is returned. - pub ret: i32, -} - -/// Parameters for a [`FileWrite`](Hypercall::FileWrite) hypercall. -#[repr(C, packed)] -#[derive(Debug, Copy, Clone)] -pub struct SysWrite { - /// File descriptor of the file. - pub fd: i32, - /// Buffer to be written into the file. - pub buf: *const u8, - /// Number of bytes in the buffer to be written. - pub len: usize, -} - -/// Parameters for a [`FileRead`](Hypercall::FileRead) hypercall. -#[repr(C, packed)] -#[derive(Debug, Copy, Clone)] -pub struct SysRead { - /// File descriptor of the file. - pub fd: i32, - /// Buffer to read the file into. - pub buf: *const u8, - /// Number of bytes to read into the buffer. - pub len: usize, - /// Number of bytes read on success. `-1` on failure. - pub ret: isize, -} - -/// Parameters for a [`FileClose`](Hypercall::FileClose) hypercall -#[repr(C, packed)] -#[derive(Debug, Copy, Clone)] -pub struct SysClose { - /// File descriptor of the file. - pub fd: i32, - /// Zero on success, `-1` on failure. - pub ret: i32, -} - -/// Parameters for a [`FileOpen`](Hypercall::FileOpen) hypercall -#[repr(C, packed)] -#[derive(Debug, Copy, Clone)] -pub struct SysOpen { - /// Pathname of the file to be opened. - // TODO PhysAddr??? - pub name: *const u8, - /// Posix file access mode flags. - pub flags: i32, - /// Access permissions upon opening/creating a file. - pub mode: i32, - /// File descriptor upon successful opening or `-1` upon failure. - pub ret: i32, -} - -/// Parameters for a [`FileLseek`](Hypercall::FileLseek) hypercall -#[repr(C, packed)] -#[derive(Debug, Copy, Clone)] -pub struct SysLseek { - /// File descriptor of the file. - pub fd: i32, - /// Offset in the file. - pub offset: isize, - /// `whence` value of the lseek call. - pub whence: i32, -} diff --git a/uhyve-interface/src/parameters.rs b/uhyve-interface/src/parameters.rs new file mode 100644 index 00000000..46647f8a --- /dev/null +++ b/uhyve-interface/src/parameters.rs @@ -0,0 +1,109 @@ +//! Parameters for hypercalls. + +use crate::MAX_ARGC_ENVC; +use x86_64::PhysAddr; + +/// Parameters for a [`Cmdsize`](Hypercall::Cmdsize) hypercall which provides the lengths of the items in the argument end environment vector. +/// Parameters for a [`Cmdsize`](Hypercall::Cmdsize) hypercall which provides the lengths of the items in the argument end environment vector. +#[repr(C, packed)] +#[derive(Debug, Copy, Clone)] +pub struct SysCmdsize { + /// Nr of items in the kernel command line. + pub argc: i32, + /// Lengths of the items in the kernel command line. + pub argsz: [i32; MAX_ARGC_ENVC], + /// Nr of items in the environment. + pub envc: i32, + /// Length of the items in the environment. + pub envsz: [i32; MAX_ARGC_ENVC], +} + +/// Parameters for a [`Cmdval`](Hypercall::Cmdval) hypercall, which copies the arguments end environment of the application into the VM's memory. +#[repr(C, packed)] +#[derive(Debug, Copy, Clone)] +pub struct SysCmdval { + /// Pointer to a memory section in the VM memory large enough to store the argument string. + pub argv: PhysAddr, + /// Pointer to a memory section in the VM memory large enough to store the environment values. + pub envp: PhysAddr, +} + +/// Parameters for a [`Exit`](Hypercall::Exit) hypercall. +#[repr(C, packed)] +#[derive(Debug, Copy, Clone)] +pub struct SysExit { + /// The return code of the guest. + pub arg: i32, +} + +/// Parameters for a [`FileUnlink`](Hypercall::FileUnlink) hypercall. +#[repr(C, packed)] +#[derive(Debug, Copy, Clone)] +pub struct SysUnlink { + /// Address of the file that should be unlinked. + pub name: PhysAddr, + /// On success, `0` is returned. On error, `-1` is returned. + pub ret: i32, +} + +/// Parameters for a [`FileWrite`](Hypercall::FileWrite) hypercall. +#[repr(C, packed)] +#[derive(Debug, Copy, Clone)] +pub struct SysWrite { + /// File descriptor of the file. + pub fd: i32, + /// Buffer to be written into the file. + pub buf: PhysAddr, + /// Number of bytes in the buffer to be written. + pub len: usize, +} + +/// Parameters for a [`FileRead`](Hypercall::FileRead) hypercall. +#[repr(C, packed)] +#[derive(Debug, Copy, Clone)] +pub struct SysRead { + /// File descriptor of the file. + pub fd: i32, + /// Buffer to read the file into. + pub buf: PhysAddr, + /// Number of bytes to read into the buffer. + pub len: usize, + /// Number of bytes read on success. `-1` on failure. + pub ret: isize, +} + +/// Parameters for a [`FileClose`](Hypercall::FileClose) hypercall +#[repr(C, packed)] +#[derive(Debug, Copy, Clone)] +pub struct SysClose { + /// File descriptor of the file. + pub fd: i32, + /// Zero on success, `-1` on failure. + pub ret: i32, +} + +/// Parameters for a [`FileOpen`](Hypercall::FileOpen) hypercall +#[repr(C, packed)] +#[derive(Debug, Copy, Clone)] +pub struct SysOpen { + /// Pathname of the file to be opened. + pub name: PhysAddr, + /// Posix file access mode flags. + pub flags: i32, + /// Access permissions upon opening/creating a file. + pub mode: i32, + /// File descriptor upon successful opening or `-1` upon failure. + pub ret: i32, +} + +/// Parameters for a [`FileLseek`](Hypercall::FileLseek) hypercall +#[repr(C, packed)] +#[derive(Debug, Copy, Clone)] +pub struct SysLseek { + /// File descriptor of the file. + pub fd: i32, + /// Offset in the file. + pub offset: isize, + /// `whence` value of the lseek call. + pub whence: i32, +} From f4b4c1160dc88753fa73bc5a30995596d9bd3919 Mon Sep 17 00:00:00 2001 From: Jonathan Klimt Date: Wed, 4 Jan 2023 18:08:44 +0100 Subject: [PATCH 14/21] Renamed Hypercall parameters --- src/vm.rs | 36 +++++++++++++++---------------- uhyve-interface/src/lib.rs | 18 ++++++++-------- uhyve-interface/src/parameters.rs | 18 ++++++++-------- 3 files changed, 36 insertions(+), 36 deletions(-) diff --git a/src/vm.rs b/src/vm.rs index 094a72d0..3e405580 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -76,41 +76,41 @@ pub trait VirtualCPU { if let Ok(hypercall_port) = IoPorts::try_from(port) { Some(match hypercall_port { IoPorts::FileClose => { - let sysclose = unsafe { &mut *(self.host_address(data_addr) as *mut SysClose) }; + let sysclose = unsafe { &mut *(self.host_address(data_addr) as *mut CloseParams) }; Hypercall::FileClose(sysclose) } IoPorts::FileLseek => { - let syslseek = unsafe { &mut *(self.host_address(data_addr) as *mut SysLseek) }; + let syslseek = unsafe { &mut *(self.host_address(data_addr) as *mut LseekParams) }; Hypercall::FileLseek(syslseek) } IoPorts::FileOpen => { - let sysopen = unsafe { &mut *(self.host_address(data_addr) as *mut SysOpen) }; + let sysopen = unsafe { &mut *(self.host_address(data_addr) as *mut OpenParams) }; Hypercall::FileOpen(sysopen) } IoPorts::FileRead => { - let sysread = unsafe { &mut *(self.host_address(data_addr) as *mut SysRead) }; + let sysread = unsafe { &mut *(self.host_address(data_addr) as *mut ReadPrams) }; Hypercall::FileRead(sysread) } IoPorts::FileWrite => { - let syswrite = unsafe { &*(self.host_address(data_addr) as *const SysWrite) }; + let syswrite = unsafe { &*(self.host_address(data_addr) as *const WriteParams) }; Hypercall::FileWrite(syswrite) } IoPorts::FileUnlink => { let sysunlink = - unsafe { &mut *(self.host_address(data_addr) as *mut SysUnlink) }; + unsafe { &mut *(self.host_address(data_addr) as *mut UnlinkParams) }; Hypercall::FileUnlink(sysunlink) } IoPorts::Exit => { - let sysexit = unsafe { &*(self.host_address(data_addr) as *const SysExit) }; + let sysexit = unsafe { &*(self.host_address(data_addr) as *const ExitParams) }; Hypercall::Exit(sysexit) } IoPorts::Cmdsize => { let syssize = - unsafe { &mut *(self.host_address(data_addr) as *mut SysCmdsize) }; + unsafe { &mut *(self.host_address(data_addr) as *mut CmdsizeParams) }; Hypercall::Cmdsize(syssize) } IoPorts::Cmdval => { - let syscmdval = unsafe { &*(self.host_address(data_addr) as *const SysCmdval) }; + let syscmdval = unsafe { &*(self.host_address(data_addr) as *const CmdvalParams) }; Hypercall::Cmdval(syscmdval) } IoPorts::Uart => { @@ -124,7 +124,7 @@ pub trait VirtualCPU { } } - fn cmdsize(&self, syssize: &mut SysCmdsize) { + fn cmdsize(&self, syssize: &mut CmdsizeParams) { syssize.argc = 0; syssize.envc = 0; @@ -155,7 +155,7 @@ pub trait VirtualCPU { } /// Copies the arguments end environment of the application into the VM's memory. - fn cmdval(&self, syscmdval: &SysCmdval) { + fn cmdval(&self, syscmdval: &CmdvalParams) { let argv = self.host_address(syscmdval.argv.as_u64() as usize); // copy kernel path as first argument @@ -212,7 +212,7 @@ pub trait VirtualCPU { /// unlink deletes 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! - fn unlink(&self, sysunlink: &mut SysUnlink) { + fn unlink(&self, sysunlink: &mut UnlinkParams) { unsafe { sysunlink.ret = libc::unlink(self.host_address(sysunlink.name.as_u64() as usize) as *const i8); @@ -220,12 +220,12 @@ pub trait VirtualCPU { } /// Reads the exit code from an VM and returns it - fn exit(&self, sysexit: &SysExit) -> i32 { + fn exit(&self, sysexit: &ExitParams) -> i32 { sysexit.arg } /// Handles an open syscall by opening a file on the host. - fn open(&self, sysopen: &mut SysOpen) { + fn open(&self, sysopen: &mut OpenParams) { unsafe { sysopen.ret = libc::open( self.host_address(sysopen.name.as_u64() as usize) as *const i8, @@ -236,14 +236,14 @@ pub trait VirtualCPU { } /// Handles an close syscall by closing the file on the host. - fn close(&self, sysclose: &mut SysClose) { + fn close(&self, sysclose: &mut CloseParams) { unsafe { sysclose.ret = libc::close(sysclose.fd); } } /// Handles an read syscall on the host. - fn read(&self, sysread: &mut SysRead) { + fn read(&self, sysread: &mut ReadPrams) { unsafe { let buffer = self.virt_to_phys(sysread.buf.as_u64() as usize); @@ -261,7 +261,7 @@ pub trait VirtualCPU { } /// Handles an write syscall on the host. - fn write(&self, syswrite: &SysWrite) -> io::Result<()> { + fn write(&self, syswrite: &WriteParams) -> io::Result<()> { let mut bytes_written: usize = 0; let buffer = self.virt_to_phys(syswrite.buf.as_u64() as usize); @@ -284,7 +284,7 @@ pub trait VirtualCPU { } /// Handles an write syscall on the host. - fn lseek(&self, syslseek: &mut SysLseek) { + fn lseek(&self, syslseek: &mut LseekParams) { unsafe { syslseek.offset = libc::lseek(syslseek.fd, syslseek.offset as i64, syslseek.whence) as isize; diff --git a/uhyve-interface/src/lib.rs b/uhyve-interface/src/lib.rs index 594888e7..5732c52f 100644 --- a/uhyve-interface/src/lib.rs +++ b/uhyve-interface/src/lib.rs @@ -75,18 +75,18 @@ impl From> for IoPorts { pub enum Hypercall<'a> { /// Get the size of the argument and environment strings. Used to allocate memory for /// [`Hypercall::Cmdval`]. - Cmdsize(&'a mut SysCmdsize), + Cmdsize(&'a mut CmdsizeParams), /// Copy the argument and environment strings into the VM. Usually preceeeded by /// [`Hypercall::Cmdsize`] so that the guest can allocate the memory for this call. - Cmdval(&'a SysCmdval), + Cmdval(&'a CmdvalParams), /// Exit the VM and return a status. - Exit(&'a SysExit), - FileClose(&'a mut SysClose), - FileLseek(&'a mut SysLseek), - FileOpen(&'a mut SysOpen), - FileRead(&'a mut SysRead), - FileWrite(&'a SysWrite), - FileUnlink(&'a mut SysUnlink), + Exit(&'a ExitParams), + FileClose(&'a mut CloseParams), + FileLseek(&'a mut LseekParams), + FileOpen(&'a mut OpenParams), + FileRead(&'a mut ReadPrams), + FileWrite(&'a WriteParams), + FileUnlink(&'a mut UnlinkParams), /// Write a buffer to the terminal. SerialWrite(&'a [u8]), } diff --git a/uhyve-interface/src/parameters.rs b/uhyve-interface/src/parameters.rs index 46647f8a..eabf2d38 100644 --- a/uhyve-interface/src/parameters.rs +++ b/uhyve-interface/src/parameters.rs @@ -7,7 +7,7 @@ use x86_64::PhysAddr; /// Parameters for a [`Cmdsize`](Hypercall::Cmdsize) hypercall which provides the lengths of the items in the argument end environment vector. #[repr(C, packed)] #[derive(Debug, Copy, Clone)] -pub struct SysCmdsize { +pub struct CmdsizeParams { /// Nr of items in the kernel command line. pub argc: i32, /// Lengths of the items in the kernel command line. @@ -21,7 +21,7 @@ pub struct SysCmdsize { /// Parameters for a [`Cmdval`](Hypercall::Cmdval) hypercall, which copies the arguments end environment of the application into the VM's memory. #[repr(C, packed)] #[derive(Debug, Copy, Clone)] -pub struct SysCmdval { +pub struct CmdvalParams { /// Pointer to a memory section in the VM memory large enough to store the argument string. pub argv: PhysAddr, /// Pointer to a memory section in the VM memory large enough to store the environment values. @@ -31,7 +31,7 @@ pub struct SysCmdval { /// Parameters for a [`Exit`](Hypercall::Exit) hypercall. #[repr(C, packed)] #[derive(Debug, Copy, Clone)] -pub struct SysExit { +pub struct ExitParams { /// The return code of the guest. pub arg: i32, } @@ -39,7 +39,7 @@ pub struct SysExit { /// Parameters for a [`FileUnlink`](Hypercall::FileUnlink) hypercall. #[repr(C, packed)] #[derive(Debug, Copy, Clone)] -pub struct SysUnlink { +pub struct UnlinkParams { /// Address of the file that should be unlinked. pub name: PhysAddr, /// On success, `0` is returned. On error, `-1` is returned. @@ -49,7 +49,7 @@ pub struct SysUnlink { /// Parameters for a [`FileWrite`](Hypercall::FileWrite) hypercall. #[repr(C, packed)] #[derive(Debug, Copy, Clone)] -pub struct SysWrite { +pub struct WriteParams { /// File descriptor of the file. pub fd: i32, /// Buffer to be written into the file. @@ -61,7 +61,7 @@ pub struct SysWrite { /// Parameters for a [`FileRead`](Hypercall::FileRead) hypercall. #[repr(C, packed)] #[derive(Debug, Copy, Clone)] -pub struct SysRead { +pub struct ReadPrams { /// File descriptor of the file. pub fd: i32, /// Buffer to read the file into. @@ -75,7 +75,7 @@ pub struct SysRead { /// Parameters for a [`FileClose`](Hypercall::FileClose) hypercall #[repr(C, packed)] #[derive(Debug, Copy, Clone)] -pub struct SysClose { +pub struct CloseParams { /// File descriptor of the file. pub fd: i32, /// Zero on success, `-1` on failure. @@ -85,7 +85,7 @@ pub struct SysClose { /// Parameters for a [`FileOpen`](Hypercall::FileOpen) hypercall #[repr(C, packed)] #[derive(Debug, Copy, Clone)] -pub struct SysOpen { +pub struct OpenParams { /// Pathname of the file to be opened. pub name: PhysAddr, /// Posix file access mode flags. @@ -99,7 +99,7 @@ pub struct SysOpen { /// Parameters for a [`FileLseek`](Hypercall::FileLseek) hypercall #[repr(C, packed)] #[derive(Debug, Copy, Clone)] -pub struct SysLseek { +pub struct LseekParams { /// File descriptor of the file. pub fd: i32, /// Offset in the file. From 541aa2ed1b870e2157f366ca6eddf88d600e284d Mon Sep 17 00:00:00 2001 From: Jonathan Klimt Date: Wed, 4 Jan 2023 19:47:14 +0100 Subject: [PATCH 15/21] Documentation fixes and minor tuning --- src/vm.rs | 15 ++++++++++----- uhyve-interface/src/lib.rs | 6 +++++- uhyve-interface/src/parameters.rs | 22 +++++++++++----------- 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/src/vm.rs b/src/vm.rs index 3e405580..1714df53 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -76,15 +76,18 @@ pub trait VirtualCPU { if let Ok(hypercall_port) = IoPorts::try_from(port) { Some(match hypercall_port { IoPorts::FileClose => { - let sysclose = unsafe { &mut *(self.host_address(data_addr) as *mut CloseParams) }; + let sysclose = + unsafe { &mut *(self.host_address(data_addr) as *mut CloseParams) }; Hypercall::FileClose(sysclose) } IoPorts::FileLseek => { - let syslseek = unsafe { &mut *(self.host_address(data_addr) as *mut LseekParams) }; + let syslseek = + unsafe { &mut *(self.host_address(data_addr) as *mut LseekParams) }; Hypercall::FileLseek(syslseek) } IoPorts::FileOpen => { - let sysopen = unsafe { &mut *(self.host_address(data_addr) as *mut OpenParams) }; + let sysopen = + unsafe { &mut *(self.host_address(data_addr) as *mut OpenParams) }; Hypercall::FileOpen(sysopen) } IoPorts::FileRead => { @@ -92,7 +95,8 @@ pub trait VirtualCPU { Hypercall::FileRead(sysread) } IoPorts::FileWrite => { - let syswrite = unsafe { &*(self.host_address(data_addr) as *const WriteParams) }; + let syswrite = + unsafe { &*(self.host_address(data_addr) as *const WriteParams) }; Hypercall::FileWrite(syswrite) } IoPorts::FileUnlink => { @@ -110,7 +114,8 @@ pub trait VirtualCPU { Hypercall::Cmdsize(syssize) } IoPorts::Cmdval => { - let syscmdval = unsafe { &*(self.host_address(data_addr) as *const CmdvalParams) }; + let syscmdval = + unsafe { &*(self.host_address(data_addr) as *const CmdvalParams) }; Hypercall::Cmdval(syscmdval) } IoPorts::Uart => { diff --git a/uhyve-interface/src/lib.rs b/uhyve-interface/src/lib.rs index 5732c52f..2b191f51 100644 --- a/uhyve-interface/src/lib.rs +++ b/uhyve-interface/src/lib.rs @@ -1,4 +1,4 @@ -//! # Uhyve Hypercall Interface +//! # Uhyve Hypervisor Interface //! //! The uhyve hypercall interface works as follows: //! @@ -37,10 +37,13 @@ pub enum IoPorts { Exit = 0x540, /// Port address = `0x580` FileLseek = 0x580, + #[deprecated = "was never really in use"] /// Port address = `0x640` Netwrite = 0x640, + #[deprecated = "was never really in use"] /// Port address = `0x680` Netread = 0x680, + #[deprecated = "was never really in use"] /// Port address = `0x700` Netstat = 0x700, /// Port address = `0x740` @@ -102,4 +105,5 @@ impl<'a> Hypercall<'a> { pub const UHYVE_PORT_NETWRITE: u16 = 0x640; // FIXME: Do not use a fix number of arguments +/// The maximum number of items in an argument of environment vector. pub const MAX_ARGC_ENVC: usize = 128; diff --git a/uhyve-interface/src/parameters.rs b/uhyve-interface/src/parameters.rs index eabf2d38..888c53cb 100644 --- a/uhyve-interface/src/parameters.rs +++ b/uhyve-interface/src/parameters.rs @@ -1,10 +1,10 @@ //! Parameters for hypercalls. -use crate::MAX_ARGC_ENVC; use x86_64::PhysAddr; -/// Parameters for a [`Cmdsize`](Hypercall::Cmdsize) hypercall which provides the lengths of the items in the argument end environment vector. -/// Parameters for a [`Cmdsize`](Hypercall::Cmdsize) hypercall which provides the lengths of the items in the argument end environment vector. +use crate::MAX_ARGC_ENVC; + +/// Parameters for a [`Cmdsize`](crate::Hypercall::Cmdsize) hypercall which provides the lengths of the items in the argument end environment vector. #[repr(C, packed)] #[derive(Debug, Copy, Clone)] pub struct CmdsizeParams { @@ -18,7 +18,7 @@ pub struct CmdsizeParams { pub envsz: [i32; MAX_ARGC_ENVC], } -/// Parameters for a [`Cmdval`](Hypercall::Cmdval) hypercall, which copies the arguments end environment of the application into the VM's memory. +/// Parameters for a [`Cmdval`](crate::Hypercall::Cmdval) hypercall, which copies the arguments end environment of the application into the VM's memory. #[repr(C, packed)] #[derive(Debug, Copy, Clone)] pub struct CmdvalParams { @@ -28,7 +28,7 @@ pub struct CmdvalParams { pub envp: PhysAddr, } -/// Parameters for a [`Exit`](Hypercall::Exit) hypercall. +/// Parameters for a [`Exit`](crate::Hypercall::Exit) hypercall. #[repr(C, packed)] #[derive(Debug, Copy, Clone)] pub struct ExitParams { @@ -36,7 +36,7 @@ pub struct ExitParams { pub arg: i32, } -/// Parameters for a [`FileUnlink`](Hypercall::FileUnlink) hypercall. +/// Parameters for a [`FileUnlink`](crate::Hypercall::FileUnlink) hypercall. #[repr(C, packed)] #[derive(Debug, Copy, Clone)] pub struct UnlinkParams { @@ -46,7 +46,7 @@ pub struct UnlinkParams { pub ret: i32, } -/// Parameters for a [`FileWrite`](Hypercall::FileWrite) hypercall. +/// Parameters for a [`FileWrite`](crate::Hypercall::FileWrite) hypercall. #[repr(C, packed)] #[derive(Debug, Copy, Clone)] pub struct WriteParams { @@ -58,7 +58,7 @@ pub struct WriteParams { pub len: usize, } -/// Parameters for a [`FileRead`](Hypercall::FileRead) hypercall. +/// Parameters for a [`FileRead`](crate::Hypercall::FileRead) hypercall. #[repr(C, packed)] #[derive(Debug, Copy, Clone)] pub struct ReadPrams { @@ -72,7 +72,7 @@ pub struct ReadPrams { pub ret: isize, } -/// Parameters for a [`FileClose`](Hypercall::FileClose) hypercall +/// Parameters for a [`FileClose`](crate::Hypercall::FileClose) hypercall. #[repr(C, packed)] #[derive(Debug, Copy, Clone)] pub struct CloseParams { @@ -82,7 +82,7 @@ pub struct CloseParams { pub ret: i32, } -/// Parameters for a [`FileOpen`](Hypercall::FileOpen) hypercall +/// Parameters for a [`FileOpen`](crate::Hypercall::FileOpen) hypercall. #[repr(C, packed)] #[derive(Debug, Copy, Clone)] pub struct OpenParams { @@ -96,7 +96,7 @@ pub struct OpenParams { pub ret: i32, } -/// Parameters for a [`FileLseek`](Hypercall::FileLseek) hypercall +/// Parameters for a [`FileLseek`](crate::Hypercall::FileLseek) hypercall #[repr(C, packed)] #[derive(Debug, Copy, Clone)] pub struct LseekParams { From a0ea687e244f7034f9d5ff488cac1f64ffdb4c4a Mon Sep 17 00:00:00 2001 From: Jonathan Klimt Date: Fri, 6 Jan 2023 11:43:54 +0100 Subject: [PATCH 16/21] Added version number for elf headers. Shamelessly copied from hermit-entry --- uhyve-interface/src/elf.rs | 47 ++++++++++++++++++++++++++++++++++++++ uhyve-interface/src/lib.rs | 5 ++++ 2 files changed, 52 insertions(+) create mode 100644 uhyve-interface/src/elf.rs diff --git a/uhyve-interface/src/elf.rs b/uhyve-interface/src/elf.rs new file mode 100644 index 00000000..d058987c --- /dev/null +++ b/uhyve-interface/src/elf.rs @@ -0,0 +1,47 @@ +//! Utility to place the uhyve interface version in the elf header of the hermit kernel. + +/// Defines the uhyve interface version in the note section. +/// +/// This macro must be used in a module that is guaranteed to be linked. +/// See . +#[macro_export] +macro_rules! define_uhyve_interface_version { + () => { + #[used] + #[link_section = ".note.hermit.uhyve-interface-version"] + static INTERFACE_VERSION: $crate::elf::Note = $crate::elf::Note::uhyveif_version(); + }; +} + +/// Note type for specifying the uhyve interface version in an elf header. +pub const NT_UHYVE_INTERFACE_VERSION: u32 = 0x5b00; + +/// A elf note header entry containing the used Uhyve interface version as little-endian 32-bit value. +#[repr(C)] +pub struct Note { + header: Nhdr32, + name: [u8; 8], + data: [u8; 4], +} + +impl Note { + pub const fn uhyveif_version() -> Self { + Self { + header: Nhdr32 { + n_namesz: 8, + n_descsz: 4, + n_type: NT_UHYVE_INTERFACE_VERSION, + }, + name: *b"UHYVEIF\0", + data: crate::UHYVE_INTERFACE_VERSION.to_be_bytes(), + } + } +} + +/// The sizes of the fields in [`Note`] +#[repr(C)] +struct Nhdr32 { + n_namesz: u32, + n_descsz: u32, + n_type: u32, +} diff --git a/uhyve-interface/src/lib.rs b/uhyve-interface/src/lib.rs index 2b191f51..52c2b036 100644 --- a/uhyve-interface/src/lib.rs +++ b/uhyve-interface/src/lib.rs @@ -14,9 +14,14 @@ // TODO: Throw this out, once https://github.com/rust-lang/rfcs/issues/2783 or https://github.com/rust-lang/rust/issues/86772 is resolved use num_enum::TryFromPrimitive; +pub mod elf; pub mod parameters; use parameters::*; +/// The version of the uhyve interface. Note: This is not the same as the semver of the crate but +/// should be increased on every version bump that changes the API. +pub const UHYVE_INTERFACE_VERSION: u32 = 1; + /// Enum containing all valid port mappings for hypercalls. /// /// The discriminants of this enum are the respective ports, so one can get the code by calling From 01be1912f5cee87de3d23f6eaa3d80448e6ca879 Mon Sep 17 00:00:00 2001 From: Jonathan Klimt Date: Fri, 6 Jan 2023 13:26:17 +0100 Subject: [PATCH 17/21] Fixed and used hypercall interface on macos-aarch64 --- src/macos/aarch64/vcpu.rs | 42 +++++++++++++++++++++++---------------- src/vm.rs | 6 +++--- 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/src/macos/aarch64/vcpu.rs b/src/macos/aarch64/vcpu.rs index 6c747256..85da36aa 100644 --- a/src/macos/aarch64/vcpu.rs +++ b/src/macos/aarch64/vcpu.rs @@ -7,6 +7,7 @@ use std::{ }; use log::debug; +use uhyve_interface::Hypercall; use xhypervisor::{self, Register, SystemRegister, VirtualCpuExitReason}; use crate::{ @@ -15,7 +16,7 @@ use crate::{ PSR, TCR_FLAGS, TCR_TG1_4K, VA_BITS, }, consts::*, - vm::{HypervisorResult, SysExit, VcpuStopReason, VirtualCPU}, + vm::{HypervisorResult, VcpuStopReason, VirtualCPU}, }; pub struct UhyveCPU { @@ -176,24 +177,31 @@ impl VirtualCPU for UhyveCPU { let addr: u16 = exception.physical_address.try_into().unwrap(); let pc = self.vcpu.read_register(Register::PC)?; - match addr { - UHYVE_UART_PORT => { - let x8 = (self.vcpu.read_register(Register::X8)? & 0xFF) as u8; + let data_addr = self.vcpu.read_register(Register::X8)?; + if let Some(hypercall) = self.port_to_hypercall(addr, data_addr as usize) { + match hypercall { + Hypercall::SerialWrite(_buf) => { + let x8 = (self.vcpu.read_register(Register::X8)? & 0xFF) as u8; - self.uart(&[x8]).unwrap(); - self.vcpu.write_register(Register::PC, pc + 4)?; + self.uart(&[x8]).unwrap(); + } + Hypercall::Exit(sysexit) => { + return Ok(VcpuStopReason::Exit(self.exit(sysexit))); + } + _ => { + panic! {"Hypercall {hypercall:?} not implemented on macos-aarch64"} + } } - UHYVE_PORT_EXIT => { - let data_addr = self.vcpu.read_register(Register::X8)?; - let sysexit = unsafe { - &*(self.host_address(data_addr as usize) as *const SysExit) - }; - return Ok(VcpuStopReason::Exit(self.exit(sysexit))); - } - _ => { - error!("Unable to handle exception {:?}", exception); - self.print_registers(); - return Err(xhypervisor::Error::Error); + // increase the pc to the instruction after the exception to continue execution + self.vcpu.write_register(Register::PC, pc + 4)?; + } else { + #[allow(clippy::match_single_binding)] + match addr { + _ => { + error!("Unable to handle exception {:?}", exception); + self.print_registers(); + return Err(xhypervisor::Error::Error); + } } } } else { diff --git a/src/vm.rs b/src/vm.rs index 1714df53..50bffd45 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -349,9 +349,9 @@ pub trait Vm { let boot_info = BootInfo { hardware_info: HardwareInfo { phys_addr_range: arch::RAM_START..arch::RAM_START + vm_mem_len as u64, - serial_port_base: self - .verbose() - .then(|| SerialPortBase::new(uhyve_interface::IoPorts::Uart as u16).unwrap()), + serial_port_base: self.verbose().then(|| { + SerialPortBase::new((uhyve_interface::IoPorts::Uart as u16).into()).unwrap() + }), device_tree: None, }, load_info, From 5fd5b7e93af9d4a4411102f8db6c7abdbb909911 Mon Sep 17 00:00:00 2001 From: Jonathan Klimt Date: Fri, 6 Jan 2023 13:39:10 +0100 Subject: [PATCH 18/21] Renamed IOPort in uhyve interface to HypercallAddress, to make it generic for non x86 architectures --- src/linux/vcpu.rs | 2 +- src/macos/aarch64/vcpu.rs | 3 ++- src/macos/x86_64/vcpu.rs | 2 +- src/vm.rs | 29 +++++++++++++++-------------- uhyve-interface/src/lib.rs | 8 +++----- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/linux/vcpu.rs b/src/linux/vcpu.rs index 71835350..ab160761 100755 --- a/src/linux/vcpu.rs +++ b/src/linux/vcpu.rs @@ -362,7 +362,7 @@ impl VirtualCPU for UhyveCPU { }, VcpuExit::IoOut(port, addr) => { let data_addr: usize = unsafe { (*(addr.as_ptr() as *const u32)) as usize }; - if let Some(hypercall) = self.port_to_hypercall(port, data_addr) { + if let Some(hypercall) = self.address_to_hypercall(port, data_addr) { match hypercall { Hypercall::Cmdsize(syssize) => self.cmdsize(syssize), Hypercall::Cmdval(syscmdval) => self.cmdval(syscmdval), diff --git a/src/macos/aarch64/vcpu.rs b/src/macos/aarch64/vcpu.rs index 85da36aa..cfb64ace 100644 --- a/src/macos/aarch64/vcpu.rs +++ b/src/macos/aarch64/vcpu.rs @@ -178,7 +178,8 @@ impl VirtualCPU for UhyveCPU { let pc = self.vcpu.read_register(Register::PC)?; let data_addr = self.vcpu.read_register(Register::X8)?; - if let Some(hypercall) = self.port_to_hypercall(addr, data_addr as usize) { + if let Some(hypercall) = self.address_to_hypercall(addr, data_addr as usize) + { match hypercall { Hypercall::SerialWrite(_buf) => { let x8 = (self.vcpu.read_register(Register::X8)? & 0xFF) as u8; diff --git a/src/macos/x86_64/vcpu.rs b/src/macos/x86_64/vcpu.rs index 10a22286..88d599e1 100644 --- a/src/macos/x86_64/vcpu.rs +++ b/src/macos/x86_64/vcpu.rs @@ -745,7 +745,7 @@ impl VirtualCPU for UhyveCPU { assert!(!input, "Invalid I/O operation"); let data_addr: u64 = self.vcpu.read_register(&Register::RAX)? & 0xFFFFFFFF; - if let Some(hypercall) = self.port_to_hypercall(port, data_addr as usize) { + if let Some(hypercall) = self.address_to_hypercall(port, data_addr as usize) { match hypercall { Hypercall::Cmdsize(syssize) => self.cmdsize(syssize), Hypercall::Cmdval(syscmdval) => self.cmdval(syscmdval), diff --git a/src/vm.rs b/src/vm.rs index 50bffd45..69ba150c 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -9,7 +9,7 @@ use hermit_entry::{ }; use log::{error, warn}; use thiserror::Error; -use uhyve_interface::{parameters::*, Hypercall, IoPorts, MAX_ARGC_ENVC}; +use uhyve_interface::{parameters::*, Hypercall, HypercallAddress, MAX_ARGC_ENVC}; #[cfg(target_arch = "x86_64")] use crate::arch::x86_64::{ @@ -72,53 +72,53 @@ pub trait VirtualCPU { fn args(&self) -> &[OsString]; /// addr is the address of the hypercall parameter in the guest's memory space. - fn port_to_hypercall(&self, port: u16, data_addr: usize) -> Option> { - if let Ok(hypercall_port) = IoPorts::try_from(port) { + fn address_to_hypercall(&self, addr: u16, data_addr: usize) -> Option> { + if let Ok(hypercall_port) = HypercallAddress::try_from(addr) { Some(match hypercall_port { - IoPorts::FileClose => { + HypercallAddress::FileClose => { let sysclose = unsafe { &mut *(self.host_address(data_addr) as *mut CloseParams) }; Hypercall::FileClose(sysclose) } - IoPorts::FileLseek => { + HypercallAddress::FileLseek => { let syslseek = unsafe { &mut *(self.host_address(data_addr) as *mut LseekParams) }; Hypercall::FileLseek(syslseek) } - IoPorts::FileOpen => { + HypercallAddress::FileOpen => { let sysopen = unsafe { &mut *(self.host_address(data_addr) as *mut OpenParams) }; Hypercall::FileOpen(sysopen) } - IoPorts::FileRead => { + HypercallAddress::FileRead => { let sysread = unsafe { &mut *(self.host_address(data_addr) as *mut ReadPrams) }; Hypercall::FileRead(sysread) } - IoPorts::FileWrite => { + HypercallAddress::FileWrite => { let syswrite = unsafe { &*(self.host_address(data_addr) as *const WriteParams) }; Hypercall::FileWrite(syswrite) } - IoPorts::FileUnlink => { + HypercallAddress::FileUnlink => { let sysunlink = unsafe { &mut *(self.host_address(data_addr) as *mut UnlinkParams) }; Hypercall::FileUnlink(sysunlink) } - IoPorts::Exit => { + HypercallAddress::Exit => { let sysexit = unsafe { &*(self.host_address(data_addr) as *const ExitParams) }; Hypercall::Exit(sysexit) } - IoPorts::Cmdsize => { + HypercallAddress::Cmdsize => { let syssize = unsafe { &mut *(self.host_address(data_addr) as *mut CmdsizeParams) }; Hypercall::Cmdsize(syssize) } - IoPorts::Cmdval => { + HypercallAddress::Cmdval => { let syscmdval = unsafe { &*(self.host_address(data_addr) as *const CmdvalParams) }; Hypercall::Cmdval(syscmdval) } - IoPorts::Uart => { + HypercallAddress::Uart => { let buf = unsafe { &*(self.host_address(data_addr) as *const &[u8]) }; Hypercall::SerialWrite(buf) } @@ -350,7 +350,8 @@ pub trait Vm { hardware_info: HardwareInfo { phys_addr_range: arch::RAM_START..arch::RAM_START + vm_mem_len as u64, serial_port_base: self.verbose().then(|| { - SerialPortBase::new((uhyve_interface::IoPorts::Uart as u16).into()).unwrap() + SerialPortBase::new((uhyve_interface::HypercallAddress::Uart as u16).into()) + .unwrap() }), device_tree: None, }, diff --git a/uhyve-interface/src/lib.rs b/uhyve-interface/src/lib.rs index 52c2b036..27d2d643 100644 --- a/uhyve-interface/src/lib.rs +++ b/uhyve-interface/src/lib.rs @@ -7,8 +7,6 @@ //! that port is the physical memory address (of the VM) of the parameters of that hypercall. //! - On `aarch64` you write to the respective [`HypercallAddress`]. The 64-bit value written to that location is the guest's physical memory address of the hypercall's parameter. -// TODO: only x86 allows io instructions. Other architectures should use MMIO - #![no_std] // TODO: Throw this out, once https://github.com/rust-lang/rfcs/issues/2783 or https://github.com/rust-lang/rust/issues/86772 is resolved @@ -29,7 +27,7 @@ pub const UHYVE_INTERFACE_VERSION: u32 = 1; #[non_exhaustive] #[repr(u16)] #[derive(Debug, Eq, PartialEq, TryFromPrimitive)] -pub enum IoPorts { +pub enum HypercallAddress { /// Port address = `0x400` FileWrite = 0x400, /// Port address = `0x440` @@ -60,7 +58,7 @@ pub enum IoPorts { /// Port address = `0x840` FileUnlink = 0x840, } -impl From> for IoPorts { +impl From> for HypercallAddress { fn from(value: Hypercall) -> Self { match value { Hypercall::Cmdsize(_) => Self::Cmdsize, @@ -101,7 +99,7 @@ pub enum Hypercall<'a> { impl<'a> Hypercall<'a> { /// Get a hypercall's port address. pub fn port(self) -> u16 { - IoPorts::from(self) as u16 + HypercallAddress::from(self) as u16 } } From d83fef6b3cbfc4f3c9fec566e9bb4fd2a3e06904 Mon Sep 17 00:00:00 2001 From: Jonathan Klimt Date: Fri, 6 Jan 2023 15:19:40 +0100 Subject: [PATCH 19/21] Fixed incorrect serial write call: This sends a single byte not a whole buffer --- src/linux/vcpu.rs | 2 +- src/macos/aarch64/vcpu.rs | 2 +- src/macos/x86_64/vcpu.rs | 2 +- src/vm.rs | 35 +++++++++++++---------------------- uhyve-interface/src/lib.rs | 6 +++--- 5 files changed, 19 insertions(+), 28 deletions(-) diff --git a/src/linux/vcpu.rs b/src/linux/vcpu.rs index ab160761..b5dbfd15 100755 --- a/src/linux/vcpu.rs +++ b/src/linux/vcpu.rs @@ -375,7 +375,7 @@ impl VirtualCPU for UhyveCPU { Hypercall::FileRead(sysread) => self.read(sysread), Hypercall::FileWrite(syswrite) => self.write(syswrite)?, Hypercall::FileUnlink(sysunlink) => self.unlink(sysunlink), - Hypercall::SerialWrite(buf) => self.uart(buf)?, + Hypercall::SerialWriteByte(buf) => self.uart(&[buf])?, _ => panic!("Got unknown hypercall {:?}", hypercall), }; } else { diff --git a/src/macos/aarch64/vcpu.rs b/src/macos/aarch64/vcpu.rs index cfb64ace..349a872d 100644 --- a/src/macos/aarch64/vcpu.rs +++ b/src/macos/aarch64/vcpu.rs @@ -181,7 +181,7 @@ impl VirtualCPU for UhyveCPU { if let Some(hypercall) = self.address_to_hypercall(addr, data_addr as usize) { match hypercall { - Hypercall::SerialWrite(_buf) => { + Hypercall::SerialWriteByte(_char) => { let x8 = (self.vcpu.read_register(Register::X8)? & 0xFF) as u8; self.uart(&[x8]).unwrap(); diff --git a/src/macos/x86_64/vcpu.rs b/src/macos/x86_64/vcpu.rs index 88d599e1..33abd76a 100644 --- a/src/macos/x86_64/vcpu.rs +++ b/src/macos/x86_64/vcpu.rs @@ -761,7 +761,7 @@ impl VirtualCPU for UhyveCPU { self.write(syswrite).unwrap() } Hypercall::FileUnlink(sysunlink) => self.unlink(sysunlink), - Hypercall::SerialWrite(_buf) => { + Hypercall::SerialWriteByte(_char) => { // TODO Not sure why this call works different on macos... let al = (self.vcpu.read_register(&Register::RAX)? & 0xFF) as u8; self.uart(&[al]).unwrap(); diff --git a/src/vm.rs b/src/vm.rs index 69ba150c..f03cd83c 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -71,57 +71,48 @@ pub trait VirtualCPU { fn args(&self) -> &[OsString]; - /// addr is the address of the hypercall parameter in the guest's memory space. - fn address_to_hypercall(&self, addr: u16, data_addr: usize) -> Option> { + /// `addr` is the address of the hypercall parameter in the guest's memory space. `data` is the + /// parameter that was send to that address by the guest. + fn address_to_hypercall(&self, addr: u16, data: usize) -> Option> { if let Ok(hypercall_port) = HypercallAddress::try_from(addr) { Some(match hypercall_port { HypercallAddress::FileClose => { - let sysclose = - unsafe { &mut *(self.host_address(data_addr) as *mut CloseParams) }; + let sysclose = unsafe { &mut *(self.host_address(data) as *mut CloseParams) }; Hypercall::FileClose(sysclose) } HypercallAddress::FileLseek => { - let syslseek = - unsafe { &mut *(self.host_address(data_addr) as *mut LseekParams) }; + let syslseek = unsafe { &mut *(self.host_address(data) as *mut LseekParams) }; Hypercall::FileLseek(syslseek) } HypercallAddress::FileOpen => { - let sysopen = - unsafe { &mut *(self.host_address(data_addr) as *mut OpenParams) }; + let sysopen = unsafe { &mut *(self.host_address(data) as *mut OpenParams) }; Hypercall::FileOpen(sysopen) } HypercallAddress::FileRead => { - let sysread = unsafe { &mut *(self.host_address(data_addr) as *mut ReadPrams) }; + let sysread = unsafe { &mut *(self.host_address(data) as *mut ReadPrams) }; Hypercall::FileRead(sysread) } HypercallAddress::FileWrite => { - let syswrite = - unsafe { &*(self.host_address(data_addr) as *const WriteParams) }; + let syswrite = unsafe { &*(self.host_address(data) as *const WriteParams) }; Hypercall::FileWrite(syswrite) } HypercallAddress::FileUnlink => { - let sysunlink = - unsafe { &mut *(self.host_address(data_addr) as *mut UnlinkParams) }; + let sysunlink = unsafe { &mut *(self.host_address(data) as *mut UnlinkParams) }; Hypercall::FileUnlink(sysunlink) } HypercallAddress::Exit => { - let sysexit = unsafe { &*(self.host_address(data_addr) as *const ExitParams) }; + let sysexit = unsafe { &*(self.host_address(data) as *const ExitParams) }; Hypercall::Exit(sysexit) } HypercallAddress::Cmdsize => { - let syssize = - unsafe { &mut *(self.host_address(data_addr) as *mut CmdsizeParams) }; + let syssize = unsafe { &mut *(self.host_address(data) as *mut CmdsizeParams) }; Hypercall::Cmdsize(syssize) } HypercallAddress::Cmdval => { - let syscmdval = - unsafe { &*(self.host_address(data_addr) as *const CmdvalParams) }; + let syscmdval = unsafe { &*(self.host_address(data) as *const CmdvalParams) }; Hypercall::Cmdval(syscmdval) } - HypercallAddress::Uart => { - let buf = unsafe { &*(self.host_address(data_addr) as *const &[u8]) }; - Hypercall::SerialWrite(buf) - } + HypercallAddress::Uart => Hypercall::SerialWriteByte(data as u8), _ => unimplemented!(), }) } else { diff --git a/uhyve-interface/src/lib.rs b/uhyve-interface/src/lib.rs index 27d2d643..02c34c1a 100644 --- a/uhyve-interface/src/lib.rs +++ b/uhyve-interface/src/lib.rs @@ -70,7 +70,7 @@ impl From> for HypercallAddress { Hypercall::FileRead(_) => Self::FileRead, Hypercall::FileWrite(_) => Self::FileWrite, Hypercall::FileUnlink(_) => Self::FileUnlink, - Hypercall::SerialWrite(_) => Self::Uart, + Hypercall::SerialWriteByte(_) => Self::Uart, } } } @@ -93,8 +93,8 @@ pub enum Hypercall<'a> { FileRead(&'a mut ReadPrams), FileWrite(&'a WriteParams), FileUnlink(&'a mut UnlinkParams), - /// Write a buffer to the terminal. - SerialWrite(&'a [u8]), + /// Write a char to the terminal. + SerialWriteByte(u8), } impl<'a> Hypercall<'a> { /// Get a hypercall's port address. From d1f277ab11d6b7cfc59c6c1c3b32d234f3a9dac2 Mon Sep 17 00:00:00 2001 From: Jonathan Klimt Date: Wed, 11 Jan 2023 19:15:07 +0100 Subject: [PATCH 20/21] Made address_to_hypercall fn unsafe, as it actually is --- src/linux/vcpu.rs | 4 +++- src/macos/aarch64/vcpu.rs | 3 ++- src/macos/x86_64/vcpu.rs | 4 +++- src/vm.rs | 8 +++++++- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/linux/vcpu.rs b/src/linux/vcpu.rs index b5dbfd15..4f50f5fb 100755 --- a/src/linux/vcpu.rs +++ b/src/linux/vcpu.rs @@ -362,7 +362,9 @@ impl VirtualCPU for UhyveCPU { }, VcpuExit::IoOut(port, addr) => { let data_addr: usize = unsafe { (*(addr.as_ptr() as *const u32)) as usize }; - if let Some(hypercall) = self.address_to_hypercall(port, data_addr) { + if let Some(hypercall) = + unsafe { self.address_to_hypercall(port, data_addr) } + { match hypercall { Hypercall::Cmdsize(syssize) => self.cmdsize(syssize), Hypercall::Cmdval(syscmdval) => self.cmdval(syscmdval), diff --git a/src/macos/aarch64/vcpu.rs b/src/macos/aarch64/vcpu.rs index 349a872d..6758c26e 100644 --- a/src/macos/aarch64/vcpu.rs +++ b/src/macos/aarch64/vcpu.rs @@ -178,7 +178,8 @@ impl VirtualCPU for UhyveCPU { let pc = self.vcpu.read_register(Register::PC)?; let data_addr = self.vcpu.read_register(Register::X8)?; - if let Some(hypercall) = self.address_to_hypercall(addr, data_addr as usize) + if let Some(hypercall) = + unsafe { self.address_to_hypercall(port, data_addr as usize) } { match hypercall { Hypercall::SerialWriteByte(_char) => { diff --git a/src/macos/x86_64/vcpu.rs b/src/macos/x86_64/vcpu.rs index 33abd76a..a8cf937e 100644 --- a/src/macos/x86_64/vcpu.rs +++ b/src/macos/x86_64/vcpu.rs @@ -745,7 +745,9 @@ impl VirtualCPU for UhyveCPU { assert!(!input, "Invalid I/O operation"); let data_addr: u64 = self.vcpu.read_register(&Register::RAX)? & 0xFFFFFFFF; - if let Some(hypercall) = self.address_to_hypercall(port, data_addr as usize) { + if let Some(hypercall) = + unsafe { self.address_to_hypercall(port, data_addr as usize) } + { match hypercall { Hypercall::Cmdsize(syssize) => self.cmdsize(syssize), Hypercall::Cmdval(syscmdval) => self.cmdval(syscmdval), diff --git a/src/vm.rs b/src/vm.rs index f03cd83c..df6362bb 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -73,7 +73,13 @@ pub trait VirtualCPU { /// `addr` is the address of the hypercall parameter in the guest's memory space. `data` is the /// parameter that was send to that address by the guest. - fn address_to_hypercall(&self, addr: u16, data: usize) -> Option> { + /// + /// # Safety + /// + /// - `data` must be a valid pointer to the data attached to the hypercall. + /// - The return value is only valid, as long as the guest is halted. + /// - This fn must not be called multiple times on the same data, to avoid creating mutable aliasing. + unsafe fn address_to_hypercall(&self, addr: u16, data: usize) -> Option> { if let Ok(hypercall_port) = HypercallAddress::try_from(addr) { Some(match hypercall_port { HypercallAddress::FileClose => { From dc7da200753d7149639cf2032a88d6a92aebbcf7 Mon Sep 17 00:00:00 2001 From: Jonathan Klimt Date: Tue, 2 May 2023 11:52:40 +0200 Subject: [PATCH 21/21] Removed old and unused SHUTDOWN_PORT for macos --- src/macos/x86_64/vcpu.rs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/macos/x86_64/vcpu.rs b/src/macos/x86_64/vcpu.rs index a8cf937e..764b25e1 100644 --- a/src/macos/x86_64/vcpu.rs +++ b/src/macos/x86_64/vcpu.rs @@ -772,16 +772,8 @@ impl VirtualCPU for UhyveCPU { } self.vcpu.write_register(&Register::RIP, rip + len)?; } else { - match port { - // TODO: Deprecate (not used in Linux anyway) - SHUTDOWN_PORT => { - return Ok(VcpuStopReason::Exit(0)); - } - _ => { - error!("Receive unhandled output command at port 0x{:x}", port); - self.vcpu.write_register(&Register::RIP, rip + len)?; - } - } + error!("Receive unhandled output command at port 0x{:x}", port); + self.vcpu.write_register(&Register::RIP, rip + len)?; } } vmx_reason => {