hermit/syscalls/interfaces/
uhyve.rs

1use core::ptr;
2
3use memory_addresses::VirtAddr;
4use uhyve_interface::parameters::{ExitParams, SerialWriteBufferParams};
5use uhyve_interface::{Hypercall, HypercallAddress};
6
7use crate::arch;
8use crate::arch::mm::paging::{self, virtual_to_physical};
9use crate::syscalls::interfaces::SyscallInterface;
10
11/// perform a SerialWriteBuffer hypercall with `buf` as payload.
12#[inline]
13#[cfg_attr(target_arch = "riscv64", expect(dead_code))]
14pub(crate) fn serial_buf_hypercall(buf: &[u8]) {
15	let p = SerialWriteBufferParams {
16		buf: virtual_to_physical(VirtAddr::from_ptr(core::ptr::from_ref::<[u8]>(buf))).unwrap(),
17		len: buf.len(),
18	};
19	uhyve_hypercall(Hypercall::SerialWriteBuffer(&p));
20}
21
22/// calculates the physical address of the struct passed as reference.
23#[inline]
24fn data_addr<T>(data: &T) -> u64 {
25	paging::virtual_to_physical(VirtAddr::from_ptr(ptr::from_ref(data)))
26		.unwrap()
27		.as_u64()
28}
29
30/// calculates the hypercall data argument
31#[inline]
32fn hypercall_data(hypercall: &Hypercall<'_>) -> u64 {
33	match hypercall {
34		Hypercall::Cmdsize(data) => data_addr(*data),
35		Hypercall::Cmdval(data) => data_addr(*data),
36		Hypercall::Exit(data) => data_addr(*data),
37		Hypercall::FileClose(data) => data_addr(*data),
38		Hypercall::FileLseek(data) => data_addr(*data),
39		Hypercall::FileOpen(data) => data_addr(*data),
40		Hypercall::FileRead(data) => data_addr(*data),
41		Hypercall::FileUnlink(data) => data_addr(*data),
42		Hypercall::FileWrite(data) => data_addr(*data),
43		Hypercall::SerialWriteBuffer(data) => data_addr(*data),
44		Hypercall::SerialWriteByte(byte) => u64::from(*byte),
45		h => todo!("unimplemented hypercall {h:?}"),
46	}
47}
48
49/// Perform a hypercall to the uhyve hypervisor
50#[inline]
51#[allow(unused_variables)] // until riscv64 is implemented
52pub(crate) fn uhyve_hypercall(hypercall: Hypercall<'_>) {
53	let ptr = HypercallAddress::from(&hypercall) as u16;
54	let data = hypercall_data(&hypercall);
55
56	#[cfg(target_arch = "x86_64")]
57	unsafe {
58		use x86_64::instructions::port::Port;
59
60		let data =
61			u32::try_from(data).expect("Hypercall data must lie in the first 4GiB of memory");
62		Port::new(ptr).write(data);
63	}
64
65	#[cfg(target_arch = "aarch64")]
66	unsafe {
67		use core::arch::asm;
68		asm!(
69			"str x8, [{ptr}]",
70			ptr = in(reg) u64::from(ptr),
71			in("x8") data,
72			options(nostack),
73		);
74	}
75
76	#[cfg(target_arch = "riscv64")]
77	todo!()
78}
79
80pub struct Uhyve;
81
82impl SyscallInterface for Uhyve {
83	fn shutdown(&self, error_code: i32) -> ! {
84		let sysexit = ExitParams { arg: error_code };
85		uhyve_hypercall(Hypercall::Exit(&sysexit));
86
87		loop {
88			arch::processor::halt();
89		}
90	}
91}