Skip to content

Commit d80a10e

Browse files
committed
feat(uhyve): use v2 interface
On x86_64, the data address will be stored in the rdi register to support 64-bit addresses. This also removes the usage of ExitArgs, as it is a single integer. We additionally introduce improved, more POSIX-compliant-ish error handling together, more debugging messages and replaced the if..else with matches for readability reasons. Co-authored-by: Ellen Εμιλία Άννα Zscheile <fogti+devel@ytrizja.de> Helped-by: Jonathan Klimt <jonathan.klimt@eonerc.rwth-aachen.de> Replace register, improve error handling, assert UhyveStdout/UhyveStderr return value, use matches fix abs bug
1 parent 15411f4 commit d80a10e

File tree

5 files changed

+119
-75
lines changed

5 files changed

+119
-75
lines changed

Cargo.lock

Lines changed: 3 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,7 @@ thiserror = { version = "2", default-features = false }
347347
time = { version = "0.3", default-features = false }
348348
volatile = "0.6"
349349
zerocopy = { version = "0.8", default-features = false }
350-
uhyve-interface = "0.1.4"
350+
uhyve-interface = "0.2.0"
351351

352352
[dependencies.smoltcp]
353353
version = "0.12"
@@ -416,6 +416,7 @@ exclude = [
416416
]
417417

418418
[patch.crates-io]
419+
uhyve-interface = { git = "https://github.com/hermit-os/uhyve" }
419420
safe-mmio = { git = "https://github.com/hermit-os/safe-mmio", branch = "be" }
420421

421422
[profile.profiling]

src/fd/stdio.rs

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@ use core::future;
22
use core::task::Poll;
33

44
use embedded_io::{Read, ReadReady, Write};
5-
use uhyve_interface::parameters::WriteParams;
6-
use uhyve_interface::{GuestVirtAddr, Hypercall};
5+
use memory_addresses::VirtAddr;
6+
use uhyve_interface::GuestPhysAddr;
7+
use uhyve_interface::v2::Hypercall;
8+
use uhyve_interface::v2::parameters::WriteParams;
79

10+
use crate::arch::mm::paging;
811
use crate::console::{CONSOLE, CONSOLE_WAKER};
912
use crate::fd::{
1013
AccessPermission, FileAttr, ObjectInterface, PollEvent, STDERR_FILENO, STDOUT_FILENO,
@@ -152,14 +155,20 @@ impl ObjectInterface for UhyveStdout {
152155
}
153156

154157
async fn write(&self, buf: &[u8]) -> io::Result<usize> {
155-
let write_params = WriteParams {
158+
let mut write_params = WriteParams {
156159
fd: STDOUT_FILENO,
157-
buf: GuestVirtAddr::from_ptr(buf.as_ptr()),
158-
len: buf.len(),
160+
buf: GuestPhysAddr::new(
161+
paging::virtual_to_physical(VirtAddr::from_ptr(buf.as_ptr()))
162+
.unwrap()
163+
.as_u64(),
164+
),
165+
len: buf.len().try_into().unwrap(),
166+
ret: 0i64,
159167
};
160-
uhyve_hypercall(Hypercall::FileWrite(&write_params));
161-
162-
Ok(write_params.len)
168+
uhyve_hypercall(Hypercall::FileWrite(&mut write_params));
169+
// Assumption: Uhyve will write to stdout and manually override the ret.
170+
debug_assert_eq!(write_params.len, write_params.ret as u64);
171+
Ok(write_params.ret.try_into().unwrap())
163172
}
164173

165174
async fn isatty(&self) -> io::Result<bool> {
@@ -190,14 +199,20 @@ impl ObjectInterface for UhyveStderr {
190199
}
191200

192201
async fn write(&self, buf: &[u8]) -> io::Result<usize> {
193-
let write_params = WriteParams {
202+
let mut write_params = WriteParams {
194203
fd: STDERR_FILENO,
195-
buf: GuestVirtAddr::from_ptr(buf.as_ptr()),
196-
len: buf.len(),
204+
buf: GuestPhysAddr::new(
205+
paging::virtual_to_physical(VirtAddr::from_ptr(buf.as_ptr()))
206+
.unwrap()
207+
.as_u64(),
208+
),
209+
len: buf.len().try_into().unwrap(),
210+
ret: 0i64,
197211
};
198-
uhyve_hypercall(Hypercall::FileWrite(&write_params));
199-
200-
Ok(write_params.len)
212+
uhyve_hypercall(Hypercall::FileWrite(&mut write_params));
213+
// Assumption: Uhyve will write to stdout and manually override the ret.
214+
debug_assert_eq!(write_params.len, write_params.ret as u64);
215+
Ok(write_params.ret.try_into().unwrap())
201216
}
202217

203218
async fn isatty(&self) -> io::Result<bool> {

src/fs/uhyve.rs

Lines changed: 55 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ use alloc::sync::Arc;
77
use async_lock::Mutex;
88
use embedded_io::{ErrorType, Read, Write};
99
use memory_addresses::VirtAddr;
10-
use uhyve_interface::parameters::{
10+
use uhyve_interface::GuestPhysAddr;
11+
use uhyve_interface::v2::Hypercall;
12+
use uhyve_interface::v2::parameters::{
1113
CloseParams, LseekParams, OpenParams, ReadParams, UnlinkParams, WriteParams,
1214
};
13-
use uhyve_interface::{GuestPhysAddr, GuestVirtAddr, Hypercall};
1415

1516
use crate::arch::mm::paging;
1617
use crate::env::fdt;
@@ -34,16 +35,20 @@ impl UhyveFileHandleInner {
3435
fn lseek(&self, offset: isize, whence: SeekWhence) -> io::Result<isize> {
3536
let mut lseek_params = LseekParams {
3637
fd: self.0,
37-
offset,
38+
offset: offset.try_into().unwrap(),
3839
whence: u8::from(whence).into(),
3940
};
4041
uhyve_hypercall(Hypercall::FileLseek(&mut lseek_params));
41-
42-
if lseek_params.offset < 0 {
43-
return Err(Errno::Inval);
42+
// TODO: Although we can generally assume that what Uhyve delivers should be
43+
// correct for now, it might make sense to build in checks (or at least debug_assert's)
44+
match lseek_params.offset {
45+
offset if offset >= 0 => Ok(offset.try_into().unwrap()),
46+
errno if errno < 0 => Err((errno as i32).abs().try_into().unwrap()),
47+
_ => {
48+
debug!("Uhyve lseek hypercall yielded a zero.");
49+
Err(Errno::Inval)
50+
}
4451
}
45-
46-
Ok(lseek_params.offset)
4752
}
4853
}
4954

@@ -55,30 +60,47 @@ impl Read for UhyveFileHandleInner {
5560
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
5661
let mut read_params = ReadParams {
5762
fd: self.0,
58-
buf: GuestVirtAddr::from_ptr(buf.as_mut_ptr()),
59-
len: buf.len(),
60-
ret: 0,
63+
buf: GuestPhysAddr::new(
64+
paging::virtual_to_physical(VirtAddr::from_ptr(buf.as_mut_ptr()))
65+
.unwrap()
66+
.as_u64(),
67+
),
68+
len: buf.len().try_into().unwrap(),
69+
ret: 0i64,
6170
};
6271
uhyve_hypercall(Hypercall::FileRead(&mut read_params));
63-
64-
if read_params.ret < 0 {
65-
return Err(Errno::Io);
72+
match read_params.ret {
73+
ret if ret >= 0 => Ok(ret.try_into().unwrap()),
74+
_ => Err((read_params.ret as i32).abs().try_into().unwrap()),
6675
}
67-
68-
Ok(read_params.ret.try_into().unwrap())
6976
}
7077
}
7178

7279
impl Write for UhyveFileHandleInner {
7380
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
74-
let write_params = WriteParams {
81+
let mut write_params = WriteParams {
7582
fd: self.0,
76-
buf: GuestVirtAddr::from_ptr(buf.as_ptr()),
77-
len: buf.len(),
83+
buf: GuestPhysAddr::new(
84+
paging::virtual_to_physical(VirtAddr::from_ptr(buf.as_ptr()))
85+
.unwrap()
86+
.as_u64(),
87+
),
88+
len: buf.len().try_into().unwrap(),
89+
ret: 0i64,
7890
};
79-
uhyve_hypercall(Hypercall::FileWrite(&write_params));
80-
81-
Ok(write_params.len)
91+
// fd refers to a regular file
92+
uhyve_hypercall(Hypercall::FileWrite(&mut write_params));
93+
match write_params.ret {
94+
// Assumption: fd is a regular file, a zero is only valid if the len
95+
// (aka. "count") is also zero. Otherwise, however, we assume that something
96+
// is wrong in Hermit<>Uhyve communication.
97+
ret if ret > 0 || (ret == 0 && write_params.len == 0) => Ok(ret.try_into().unwrap()),
98+
errno if errno < 0 => Err((errno as i32).abs().try_into().unwrap()),
99+
_ => {
100+
debug!("Uhyve write hypercall yielded a zero.");
101+
Err(Errno::Inval)
102+
}
103+
}
82104
}
83105

84106
fn flush(&mut self) -> Result<(), Self::Error> {
@@ -180,14 +202,14 @@ impl VfsNode for UhyveDirectory {
180202
ret: -1,
181203
};
182204
uhyve_hypercall(Hypercall::FileOpen(&mut open_params));
183-
184-
if open_params.ret <= 0 {
185-
return Err(Errno::Io);
205+
let ret = open_params.ret; // circumvent packed field access
206+
match ret {
207+
// Assumption: Uhyve will never return a standard stream.
208+
ret if ret >= 0 => Ok(Arc::new(async_lock::RwLock::new(
209+
UhyveFileHandle::new(ret).into(),
210+
))),
211+
_ => Err(ret.abs().try_into().unwrap()),
186212
}
187-
188-
Ok(Arc::new(async_lock::RwLock::new(
189-
UhyveFileHandle::new(open_params.ret).into(),
190-
)))
191213
}
192214

193215
fn traverse_unlink(&self, path: &str) -> io::Result<()> {
@@ -202,12 +224,11 @@ impl VfsNode for UhyveDirectory {
202224
ret: -1,
203225
};
204226
uhyve_hypercall(Hypercall::FileUnlink(&mut unlink_params));
205-
206-
if unlink_params.ret != 0 {
207-
return Err(Errno::Io);
227+
let ret = unlink_params.ret; // circumvent packed field access
228+
match ret {
229+
0 => Ok(()),
230+
_ => Err(unlink_params.ret.abs().try_into().unwrap()),
208231
}
209-
210-
Ok(())
211232
}
212233

213234
fn traverse_rmdir(&self, _path: &str) -> io::Result<()> {

src/syscalls/interfaces/uhyve.rs

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,26 @@
11
use core::ptr;
22

33
use memory_addresses::VirtAddr;
4-
use uhyve_interface::parameters::{ExitParams, SerialWriteBufferParams};
5-
use uhyve_interface::{Hypercall, HypercallAddress};
4+
use uhyve_interface::GuestPhysAddr;
5+
use uhyve_interface::v2::parameters::SerialWriteBufferParams;
6+
use uhyve_interface::v2::{Hypercall, HypercallAddress};
67

78
use crate::arch;
89
use crate::arch::mm::paging::{self, virtual_to_physical};
910
use crate::syscalls::interfaces::SyscallInterface;
1011

11-
/// Perform a SerialWriteBuffer hypercall with `buf` as payload.
12+
/// perform a SerialWriteBuffer hypercall with `buf` as payload
1213
#[inline]
1314
#[cfg_attr(target_arch = "riscv64", expect(dead_code))]
1415
pub(crate) fn serial_buf_hypercall(buf: &[u8]) {
15-
let len = buf.len();
16-
let buf = virtual_to_physical(VirtAddr::from_ptr(ptr::from_ref::<[u8]>(buf)))
17-
.unwrap()
18-
.as_u64()
19-
.into();
20-
let p = SerialWriteBufferParams { buf, len };
16+
let p = SerialWriteBufferParams {
17+
buf: GuestPhysAddr::new(
18+
virtual_to_physical(VirtAddr::from_ptr(ptr::from_ref::<[u8]>(buf)))
19+
.unwrap()
20+
.as_u64(),
21+
),
22+
len: buf.len() as u64,
23+
};
2124
uhyve_hypercall(Hypercall::SerialWriteBuffer(&p));
2225
}
2326

@@ -33,9 +36,11 @@ fn data_addr<T>(data: &T) -> u64 {
3336
#[inline]
3437
fn hypercall_data(hypercall: &Hypercall<'_>) -> u64 {
3538
match hypercall {
36-
Hypercall::Cmdsize(data) => data_addr(*data),
37-
Hypercall::Cmdval(data) => data_addr(*data),
38-
Hypercall::Exit(data) => data_addr(*data),
39+
// As we are encoding an exit code (max 32 bits) into "an
40+
// address", and memory_addresses complains if an address
41+
// has any bits above the 48th one set to 1, we encode
42+
// potential negative numbers into a u32, then a u64.
43+
Hypercall::Exit(exit_code) => u64::from((*exit_code) as u32),
3944
Hypercall::FileClose(data) => data_addr(*data),
4045
Hypercall::FileLseek(data) => data_addr(*data),
4146
Hypercall::FileOpen(data) => data_addr(*data),
@@ -56,20 +61,25 @@ pub(crate) fn uhyve_hypercall(hypercall: Hypercall<'_>) {
5661
let data = hypercall_data(&hypercall);
5762

5863
#[cfg(target_arch = "x86_64")]
59-
unsafe {
60-
use x86_64::instructions::port::Port;
61-
62-
let data =
63-
u32::try_from(data).expect("Hypercall data must lie in the first 4GiB of memory");
64-
Port::new(ptr).write(data);
64+
{
65+
unsafe {
66+
use core::arch::asm;
67+
asm!(
68+
"out dx, eax",
69+
in("dx") ptr,
70+
in("eax") 0x1234u32,
71+
in("rdi") data,
72+
options(nostack, preserves_flags)
73+
);
74+
}
6575
}
6676

6777
#[cfg(target_arch = "aarch64")]
6878
unsafe {
6979
use core::arch::asm;
7080
asm!(
7181
"str x8, [{ptr}]",
72-
ptr = in(reg) u64::from(ptr),
82+
ptr = in(reg) ptr,
7383
in("x8") data,
7484
options(nostack),
7585
);
@@ -83,8 +93,7 @@ pub struct Uhyve;
8393

8494
impl SyscallInterface for Uhyve {
8595
fn shutdown(&self, error_code: i32) -> ! {
86-
let sysexit = ExitParams { arg: error_code };
87-
uhyve_hypercall(Hypercall::Exit(&sysexit));
96+
uhyve_hypercall(Hypercall::Exit(error_code));
8897

8998
loop {
9099
arch::processor::halt();

0 commit comments

Comments
 (0)