diff --git a/src/hypercall.rs b/src/hypercall.rs index 0205419c..1abc741a 100644 --- a/src/hypercall.rs +++ b/src/hypercall.rs @@ -64,6 +64,10 @@ pub unsafe fn address_to_hypercall( Hypercall::Cmdval(syscmdval) } HypercallAddress::Uart => Hypercall::SerialWriteByte(data.as_u64() as u8), + HypercallAddress::SerialBufferWrite => { + let sysserialwrite = mem.get_ref_mut(data).unwrap(); + Hypercall::SerialWriteBuffer(sysserialwrite) + } _ => unimplemented!(), }) } else { @@ -158,6 +162,15 @@ pub fn uart(buf: &[u8]) -> io::Result<()> { io::stdout().write_all(buf) } +/// Handles a UART syscall by contructing a buffer from parameter +pub fn uart_buffer(sysuart: &SerialWriteBufferParams, mem: &MmapMemory) { + let buf = unsafe { + mem.slice_at(sysuart.buf, sysuart.len) + .expect("Systemcall parameters for SerialWriteBuffer are invalid") + }; + io::stdout().write_all(buf).unwrap() +} + /// Copies the arguments of the application into the VM's memory to the destinations specified in `syscmdval`. pub fn copy_argv(path: &OsStr, argv: &[OsString], syscmdval: &CmdvalParams, mem: &MmapMemory) { // copy kernel path as first argument diff --git a/src/linux/x86_64/kvm_cpu.rs b/src/linux/x86_64/kvm_cpu.rs index 8ef03734..e6110884 100644 --- a/src/linux/x86_64/kvm_cpu.rs +++ b/src/linux/x86_64/kvm_cpu.rs @@ -432,6 +432,9 @@ impl VirtualCPU for KvmCpu { hypercall::unlink(&self.parent_vm.mem, sysunlink) } Hypercall::SerialWriteByte(buf) => hypercall::uart(&[buf])?, + Hypercall::SerialWriteBuffer(sysserialwrite) => { + hypercall::uart_buffer(sysserialwrite, &self.parent_vm.mem) + } _ => panic!("Got unknown hypercall {:?}", hypercall), }; } else { diff --git a/src/macos/aarch64/vcpu.rs b/src/macos/aarch64/vcpu.rs index 35d86869..a252903f 100644 --- a/src/macos/aarch64/vcpu.rs +++ b/src/macos/aarch64/vcpu.rs @@ -171,6 +171,9 @@ impl VirtualCPU for XhyveCpu { hypercall::uart(&[x8]).unwrap(); } + Hypercall::SerialWriteBuffer(sysserialwrite) => { + hypercall::uart_buffer(sysserialwrite, &self.parent_vm.mem) + } Hypercall::Exit(sysexit) => { return Ok(VcpuStopReason::Exit(sysexit.arg)); } diff --git a/src/macos/x86_64/vcpu.rs b/src/macos/x86_64/vcpu.rs index b3799ba3..bda07415 100644 --- a/src/macos/x86_64/vcpu.rs +++ b/src/macos/x86_64/vcpu.rs @@ -750,6 +750,9 @@ impl VirtualCPU for XhyveCpu { hypercall::unlink(&self.parent_vm.mem, sysunlink) } Hypercall::SerialWriteByte(buf) => hypercall::uart(&[buf]).unwrap(), + Hypercall::SerialWriteBuffer(sysserialwrite) => { + hypercall::uart_buffer(sysserialwrite, &self.parent_vm.mem)? + } _ => panic!("Got unknown hypercall {:?}", hypercall), } self.vcpu.write_register(&Register::RIP, rip + len)?; diff --git a/tests/serial.rs b/tests/serial.rs new file mode 100644 index 00000000..b764fdea --- /dev/null +++ b/tests/serial.rs @@ -0,0 +1,10 @@ +mod common; + +use common::{build_hermit_bin, run_simple_vm}; + +#[test] +fn serial_buffer_test() { + // TODO: Check the output once https://github.com/hermit-os/uhyve/issues/528 is resolved + let bin_path = build_hermit_bin("serial"); + run_simple_vm(bin_path); +} diff --git a/tests/test-kernels/Cargo.lock b/tests/test-kernels/Cargo.lock index 39bc3701..56f7cbdb 100644 --- a/tests/test-kernels/Cargo.lock +++ b/tests/test-kernels/Cargo.lock @@ -2,6 +2,25 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aarch64" +version = "0.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0adf345d8b4e2861016511db094993ee8a9f74195f55ccf62d1305d35ab91bfa" +dependencies = [ + "aarch64-cpu", + "tock-registers", +] + +[[package]] +name = "aarch64-cpu" +version = "9.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac42a04a61c19fc8196dd728022a784baecc5d63d7e256c01ad1b3fbfab26287" +dependencies = [ + "tock-registers", +] + [[package]] name = "adler" version = "1.0.2" @@ -14,6 +33,12 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "bit_field" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" + [[package]] name = "bitflags" version = "1.3.2" @@ -147,6 +172,26 @@ dependencies = [ "adler", ] +[[package]] +name = "num_enum" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "once_cell" version = "1.19.0" @@ -159,6 +204,24 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + [[package]] name = "redox_syscall" version = "0.4.1" @@ -227,6 +290,12 @@ dependencies = [ "untrusted", ] +[[package]] +name = "rustversion" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" + [[package]] name = "spin" version = "0.9.8" @@ -239,6 +308,17 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +[[package]] +name = "syn" +version = "2.0.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "tar" version = "0.4.41" @@ -265,11 +345,28 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "tock-registers" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "696941a0aee7e276a165a978b37918fd5d22c55c3d6bda197813070ca9c0f21c" + +[[package]] +name = "uhyve-interface" +version = "0.1.1" +dependencies = [ + "aarch64", + "num_enum", + "x86_64", +] + [[package]] name = "uhyve-test-kernels" version = "0.1.0" dependencies = [ "hermit", + "uhyve-interface", + "x86_64", ] [[package]] @@ -278,6 +375,12 @@ version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + [[package]] name = "unicode-normalization" version = "0.1.23" @@ -321,6 +424,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "volatile" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "442887c63f2c839b346c192d047a7c87e73d0689c9157b00b53dcc27dd5ea793" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -409,6 +518,18 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +[[package]] +name = "x86_64" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bc79523af8abf92fb1a970c3e086c5a343f6bcc1a0eb890f575cbb3b45743df" +dependencies = [ + "bit_field", + "bitflags 2.5.0", + "rustversion", + "volatile", +] + [[package]] name = "xattr" version = "1.3.1" diff --git a/tests/test-kernels/Cargo.toml b/tests/test-kernels/Cargo.toml index 6252ce3c..d452a458 100644 --- a/tests/test-kernels/Cargo.toml +++ b/tests/test-kernels/Cargo.toml @@ -7,3 +7,11 @@ publish = false [target.'cfg(target_os = "hermit")'.dependencies] hermit = "0.9" + +[target.'cfg(target_arch = "x86_64")'.dependencies] +x86_64 = { version = "0.15", default-features = false, features = [ + "instructions", +] } + +[dependencies] +uhyve-interface = { path = "../../uhyve-interface" } diff --git a/tests/test-kernels/src/bin/serial.rs b/tests/test-kernels/src/bin/serial.rs new file mode 100644 index 00000000..dd1af2af --- /dev/null +++ b/tests/test-kernels/src/bin/serial.rs @@ -0,0 +1,52 @@ +#[cfg(target_os = "hermit")] +use hermit as _; +use uhyve_interface::{parameters::SerialWriteBufferParams, GuestPhysAddr, HypercallAddress}; +#[cfg(target_arch = "x86_64")] +use x86_64::{ + instructions::port::Port, + structures::paging::{PageTable, RecursivePageTable}, + VirtAddr, +}; + +unsafe fn get_page_table() -> RecursivePageTable<'static> { + let level_4_table_addr: u64 = 0xFFFF_FFFF_FFFF_F000; + unsafe { + let level_4_table = &mut *(level_4_table_addr as *mut PageTable); + RecursivePageTable::new(level_4_table).unwrap() + } +} + +/// Translate a virtual memory address to a physical one. +pub fn virtual_to_physical(virtual_address: VirtAddr) -> Option { + use x86_64::structures::paging::mapper::Translate; + + let page_table = unsafe { get_page_table() }; + page_table + .translate_addr(virtual_address) + .map(|addr| GuestPhysAddr::new(addr.as_u64())) +} + +fn main() { + println!("Test"); + + let mut serial_byte_port = Port::new(HypercallAddress::Uart as u16); + for c in "ABCD\n".bytes() { + unsafe { serial_byte_port.write(c) }; + } + + let mut serial_buf_port = Port::new(HypercallAddress::SerialBufferWrite as u16); + let testtext = "1234ASDF!@#$\n"; + let serial_write_params = SerialWriteBufferParams { + buf: virtual_to_physical(VirtAddr::new( + core::ptr::addr_of!(*testtext) as *const u8 as u64 + )) + .unwrap(), + len: testtext.bytes().len(), + }; + let params_addr = virtual_to_physical(VirtAddr::new( + core::ptr::addr_of!(serial_write_params) as u64 + )) + .unwrap(); + + unsafe { serial_buf_port.write(params_addr.as_u64() as u32) }; +} diff --git a/uhyve-interface/src/lib.rs b/uhyve-interface/src/lib.rs index be833362..9fb3f298 100644 --- a/uhyve-interface/src/lib.rs +++ b/uhyve-interface/src/lib.rs @@ -69,6 +69,8 @@ pub enum HypercallAddress { Uart = 0x800, /// Port address = `0x840` FileUnlink = 0x840, + /// Port address = `0x880` + SerialBufferWrite = 0x880, } impl From> for HypercallAddress { fn from(value: Hypercall) -> Self { @@ -83,6 +85,7 @@ impl From> for HypercallAddress { Hypercall::FileWrite(_) => Self::FileWrite, Hypercall::FileUnlink(_) => Self::FileUnlink, Hypercall::SerialWriteByte(_) => Self::Uart, + Hypercall::SerialWriteBuffer(_) => Self::SerialBufferWrite, } } } @@ -107,6 +110,8 @@ pub enum Hypercall<'a> { FileUnlink(&'a mut UnlinkParams), /// Write a char to the terminal. SerialWriteByte(u8), + /// Write a buffer to the terminal. + SerialWriteBuffer(&'a SerialWriteBufferParams), } impl<'a> Hypercall<'a> { /// Get a hypercall's port address. diff --git a/uhyve-interface/src/parameters.rs b/uhyve-interface/src/parameters.rs index 5e5db513..905bf74c 100644 --- a/uhyve-interface/src/parameters.rs +++ b/uhyve-interface/src/parameters.rs @@ -136,3 +136,11 @@ pub struct LseekParams { /// `whence` value of the lseek call. pub whence: i32, } + +/// Parameters for a [`SerialWriteBuffer`](crate::Hypercall::SerialWriteBuffer) hypercall. +#[repr(C, packed)] +#[derive(Debug, Copy, Clone)] +pub struct SerialWriteBufferParams { + pub buf: GuestPhysAddr, + pub len: usize, +}