Skip to content

Commit 3b8c59b

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 af77eb8 commit 3b8c59b

5 files changed

Lines changed: 113 additions & 75 deletions

File tree

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
@@ -141,7 +141,7 @@ thiserror = { version = "2", default-features = false }
141141
time = { version = "0.3", default-features = false }
142142
volatile = "0.6"
143143
zerocopy = { version = "0.8", default-features = false }
144-
uhyve-interface = "0.1.4"
144+
uhyve-interface = "0.2.0"
145145

146146
[dependencies.smoltcp]
147147
version = "0.12"
@@ -210,6 +210,7 @@ exclude = [
210210
]
211211

212212
[patch.crates-io]
213+
uhyve-interface = { git = "https://github.com/n0toose/uhyve", branch = "uhyve-if-v2_io-plus-rcx" }
213214
safe-mmio = { git = "https://github.com/hermit-os/safe-mmio", branch = "be" }
214215

215216
[profile.profiling]

src/fd/stdio.rs

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

55
use async_trait::async_trait;
66
use embedded_io::{Read, ReadReady, Write};
7-
use uhyve_interface::parameters::WriteParams;
8-
use uhyve_interface::{GuestVirtAddr, Hypercall};
7+
use memory_addresses::VirtAddr;
8+
use uhyve_interface::GuestPhysAddr;
9+
use uhyve_interface::v2::Hypercall;
10+
use uhyve_interface::v2::parameters::WriteParams;
911

12+
use crate::arch::mm::paging;
1013
use crate::console::{CONSOLE, CONSOLE_WAKER};
1114
use crate::fd::{
1215
AccessPermission, FileAttr, ObjectInterface, PollEvent, STDERR_FILENO, STDOUT_FILENO,
@@ -159,14 +162,20 @@ impl ObjectInterface for UhyveStdout {
159162
}
160163

161164
async fn write(&self, buf: &[u8]) -> io::Result<usize> {
162-
let write_params = WriteParams {
165+
let mut write_params = WriteParams {
163166
fd: STDOUT_FILENO,
164-
buf: GuestVirtAddr::from_ptr(buf.as_ptr()),
165-
len: buf.len(),
167+
buf: GuestPhysAddr::new(
168+
paging::virtual_to_physical(VirtAddr::from_ptr(buf.as_ptr()))
169+
.unwrap()
170+
.as_u64(),
171+
),
172+
len: buf.len().try_into().unwrap(),
173+
ret: 0i64,
166174
};
167-
uhyve_hypercall(Hypercall::FileWrite(&write_params));
168-
169-
Ok(write_params.len)
175+
uhyve_hypercall(Hypercall::FileWrite(&mut write_params));
176+
// Assumption: Uhyve will write to stdout and manually override the ret.
177+
debug_assert_eq!(write_params.len, write_params.ret as u64);
178+
Ok(write_params.ret.try_into().unwrap())
170179
}
171180

172181
async fn isatty(&self) -> io::Result<bool> {
@@ -198,14 +207,20 @@ impl ObjectInterface for UhyveStderr {
198207
}
199208

200209
async fn write(&self, buf: &[u8]) -> io::Result<usize> {
201-
let write_params = WriteParams {
210+
let mut write_params = WriteParams {
202211
fd: STDERR_FILENO,
203-
buf: GuestVirtAddr::from_ptr(buf.as_ptr()),
204-
len: buf.len(),
212+
buf: GuestPhysAddr::new(
213+
paging::virtual_to_physical(VirtAddr::from_ptr(buf.as_ptr()))
214+
.unwrap()
215+
.as_u64(),
216+
),
217+
len: buf.len().try_into().unwrap(),
218+
ret: 0i64,
205219
};
206-
uhyve_hypercall(Hypercall::FileWrite(&write_params));
207-
208-
Ok(write_params.len)
220+
uhyve_hypercall(Hypercall::FileWrite(&mut write_params));
221+
// Assumption: Uhyve will write to stdout and manually override the ret.
222+
debug_assert_eq!(write_params.len, write_params.ret as u64);
223+
Ok(write_params.ret.try_into().unwrap())
209224
}
210225

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

src/fs/uhyve.rs

Lines changed: 53 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@ use async_lock::Mutex;
99
use async_trait::async_trait;
1010
use embedded_io::{ErrorType, Read, Write};
1111
use memory_addresses::VirtAddr;
12-
use uhyve_interface::parameters::{
12+
use uhyve_interface::GuestPhysAddr;
13+
use uhyve_interface::v2::Hypercall;
14+
use uhyve_interface::v2::parameters::{
1315
CloseParams, LseekParams, OpenParams, ReadParams, UnlinkParams, WriteParams,
1416
};
15-
use uhyve_interface::{GuestPhysAddr, GuestVirtAddr, Hypercall};
1617

1718
use crate::arch::mm::paging;
1819
use crate::env::fdt;
@@ -35,15 +36,19 @@ impl UhyveFileHandleInner {
3536
fn lseek(&self, offset: isize, whence: SeekWhence) -> io::Result<isize> {
3637
let mut lseek_params = LseekParams {
3738
fd: self.0,
38-
offset,
39+
offset: offset.try_into().unwrap(),
3940
whence: u8::from(whence).into(),
4041
};
4142
uhyve_hypercall(Hypercall::FileLseek(&mut lseek_params));
42-
43-
if lseek_params.offset >= 0 {
44-
Ok(lseek_params.offset)
45-
} else {
46-
Err(Errno::Inval)
43+
// TODO: Although we can generally assume that what Uhyve delivers should be
44+
// correct for now, it might make sense to build in checks (or at least debug_assert's)
45+
match lseek_params.offset {
46+
offset if offset >= 0 => Ok(offset.try_into().unwrap()),
47+
errno if errno < 0 => Err((errno as i32).abs().try_into().unwrap()),
48+
_ => {
49+
debug!("Uhyve lseek hypercall yielded a zero.");
50+
Err(Errno::Inval)
51+
}
4752
}
4853
}
4954
}
@@ -56,30 +61,47 @@ impl Read for UhyveFileHandleInner {
5661
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
5762
let mut read_params = ReadParams {
5863
fd: self.0,
59-
buf: GuestVirtAddr::from_ptr(buf.as_mut_ptr()),
60-
len: buf.len(),
61-
ret: 0,
64+
buf: GuestPhysAddr::new(
65+
paging::virtual_to_physical(VirtAddr::from_ptr(buf.as_mut_ptr()))
66+
.unwrap()
67+
.as_u64(),
68+
),
69+
len: buf.len().try_into().unwrap(),
70+
ret: 0i64,
6271
};
6372
uhyve_hypercall(Hypercall::FileRead(&mut read_params));
64-
65-
if read_params.ret >= 0 {
66-
Ok(read_params.ret.try_into().unwrap())
67-
} else {
68-
Err(Errno::Io)
73+
match read_params.ret {
74+
ret if ret >= 0 => Ok(ret.try_into().unwrap()),
75+
_ => Err((read_params.ret as i32).abs().try_into().unwrap()),
6976
}
7077
}
7178
}
7279

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

85107
fn flush(&mut self) -> Result<(), Self::Error> {
@@ -186,13 +208,11 @@ impl VfsNode for UhyveDirectory {
186208
ret: -1,
187209
};
188210
uhyve_hypercall(Hypercall::FileOpen(&mut open_params));
189-
190-
if open_params.ret > 0 {
191-
Ok(Arc::new(async_lock::RwLock::new(UhyveFileHandle::new(
192-
open_params.ret,
193-
))))
194-
} else {
195-
Err(Errno::Io)
211+
let ret = open_params.ret; // circumvent packed field access
212+
match ret {
213+
// Assumption: Uhyve will never return a standard stream.
214+
ret if ret >= 0 => Ok(Arc::new(async_lock::RwLock::new(UhyveFileHandle::new(ret)))),
215+
_ => Err(ret.abs().try_into().unwrap()),
196216
}
197217
}
198218

@@ -208,11 +228,10 @@ impl VfsNode for UhyveDirectory {
208228
ret: -1,
209229
};
210230
uhyve_hypercall(Hypercall::FileUnlink(&mut unlink_params));
211-
212-
if unlink_params.ret == 0 {
213-
Ok(())
214-
} else {
215-
Err(Errno::Io)
231+
let ret = unlink_params.ret; // circumvent packed field access
232+
match ret {
233+
0 => Ok(()),
234+
_ => Err(unlink_params.ret.abs().try_into().unwrap()),
216235
}
217236
}
218237

src/syscalls/interfaces/uhyve.rs

Lines changed: 26 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(core::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(core::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,21 @@ 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", in("dx") ptr, in("eax") 0x1234u32, in("rdi") data, options(nostack, preserves_flags)
69+
);
70+
}
6571
}
6672

6773
#[cfg(target_arch = "aarch64")]
6874
unsafe {
6975
use core::arch::asm;
7076
asm!(
7177
"str x8, [{ptr}]",
72-
ptr = in(reg) u64::from(ptr),
78+
ptr = in(reg) ptr,
7379
in("x8") data,
7480
options(nostack),
7581
);
@@ -83,8 +89,7 @@ pub struct Uhyve;
8389

8490
impl SyscallInterface for Uhyve {
8591
fn shutdown(&self, error_code: i32) -> ! {
86-
let sysexit = ExitParams { arg: error_code };
87-
uhyve_hypercall(Hypercall::Exit(&sysexit));
92+
uhyve_hypercall(Hypercall::Exit(error_code));
8893

8994
loop {
9095
arch::processor::halt();

0 commit comments

Comments
 (0)