-
Notifications
You must be signed in to change notification settings - Fork 31
Introduce uhyve-interface v2 and MmioWrite-based hypercalls on x86_64 #1145
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1145 +/- ##
==========================================
- Coverage 76.70% 75.26% -1.45%
==========================================
Files 29 31 +2
Lines 3490 3626 +136
==========================================
+ Hits 2677 2729 +52
- Misses 813 897 +84 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
| let sysunlink = unsafe { mem.get_ref_mut(data).unwrap() }; | ||
| Hypercall::FileUnlink(sysunlink) | ||
| } | ||
| HypercallAddress::Exit => Hypercall::Exit(data.as_u64() as i32), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
... I frankly forgot why I just decided to pass the integer instead of dereferencing a pointer, but perhaps it might be worth thinking about?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if I understand.
Just passing the exit code directly is fine here - no further information is necessary when exiting.
src/arch/x86_64/paging/mod.rs
Outdated
| }; | ||
| } | ||
|
|
||
| let _ = unsafe { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A comment explaining this is likely needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A comment explaining this is likely needed.
Agreed; it's just that things do actually break if we use a non-relocatable image as of now, so I can't really justify that it's safe using a // SAFETY: comment just yet :D
src/arch/x86_64/paging/mod.rs
Outdated
|
|
||
| let _ = unsafe { | ||
| pagetable_mapping.identity_map( | ||
| PhysFrame::<Size2MiB>::from_start_address(PhysAddr::new(0)).unwrap(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it also work when only mapping a 4KiB Page? Would that impact performance?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would that impact performance?
You mentioned you wanted measurements for the MMIO-based hypercalls, is this in-scope?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be interesting to check the difference. Mapping a single 4KiB page is better from a memory point of view, but 2MiB pages are faster to access. Just a note here for later measurements.
520e639 to
3aa5f14
Compare
src/hypercall.rs
Outdated
| return peripherals.serial.output(bytes); | ||
| } else if !file_map.fdmap.is_fd_present(syswrite.fd.into_raw_fd()) { | ||
| // We don't write anything if the file descriptor is not available, | ||
| // but this is OK for now, as we have no means of returning an error code |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given that this is a new interface version that isn't currently deployed, wouldn't it make sense to adjust the write hypercall params to also have a way of returning non-fatal errors?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you envision?
I'm all open for extra fields here — the reasoning behind the current params is to be close to Linux/POSIX write. Since that is the de-facto interface to all applications as well as the file system interface on the Uhyve side, we save transformations when keeping things close.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm solely complaining about the lack of being able to emit non-fatal write errors back to the guest (currently all errors from writes afaik terminate the guest).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I discussed this with @jounathaen last week and was mildly unsure as to what type of errors we could report here; it's not like we can use POSIX as a reference, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not? Like, we could pick any arbitrary standard, we just have to make sure that it fits the constants defined in hermit-abi and isn't extremely cumbersome to use from the kernel side.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The main errors that would make sense to forward are EPIPE, ECONNRESET (for host-side IPC stuff, i.e. anything that uses pipes or sockets on host-side) and ENOSPC (we can't expect the guest to know how much disk space it has available).
| /// The discriminants of this enum are the respective addresses, so one can get the code by calling | ||
| /// e.g., `HypercallAddress::Exit as u64`. | ||
| #[non_exhaustive] | ||
| #[repr(u64)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would this (e.g. HypercallAddress::Exit as u64) also work with #[repr(u16)] or such?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, yes. But as this is intended to represent a GuestPhysAddr, I've opted for an u64 to reduce as 64 statements in the call.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it makes a difference, and subsequently #[repr(u16)] should be preferred: https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=610243c9a13c895b8140a3a22bddc5d4
|
Given that this PR depends upon another PR that is currently marked as a draft, it makes to sense to mark this one as non-draft, yet. |
|
tbqh, d1d002c took me way too long, but I think it does massively cut down on code duplication. |
d1d002c to
3d13bb4
Compare
|
Appears to work on linux now 🎉 |
fogti
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Apart from fixing the MacOS / Darwin part, this looks good now.
The (P.S. I'm aware of the suggestion regarding that exit still) |
|
We also aren't making the paging conditional - there are some corner cases (particularly when disabling ASLR) where this breaks, I think. So we're not quite done yet. |
3b25ec9 to
7287905
Compare
|
Okay, now that the "abuse the macOS CI because I don't have a machine" strat worked, I should probably fix the mess I made trying to frantically rebase this, check whether I wiped some work during rebasing accidentally, squash and continue. |
b4da38a to
2c77b78
Compare
fogti
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall LGTM, a few weirdnesses remain.
| if let Ok(hypercall_port) = HypercallAddress::try_from(addr as u64) { | ||
| Some(match hypercall_port { | ||
| HypercallAddress::FileClose => { | ||
| let sysclose = unsafe { mem.get_ref_mut::<CloseParams>(data).unwrap() }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we sometimes have to explicitly denote the parameter type (e.g. ::<CloseParams> and sometimes not?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this was answered yet...
This globally enforces a suggestion by Ellen in: hermit-os#1145 Helped-by: Ellen Emilia Anna Zscheile <[email protected]>
This globally enforces a suggestion by Ellen in: hermit-os#1145 Helped-by: Ellen Emilia Anna Zscheile <[email protected]>
This is based on a suggestion by Ellen: hermit-os#1145 (comment) Helped-by: Ellen Emilia Anna Zscheile <[email protected]>
| if bytes_read >= 0 { | ||
| bytes_read as i64 | ||
| } else { | ||
| -1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't we use some errno number here?
src/vm.rs
Outdated
| use std::{ | ||
| env, fmt, fs, io, | ||
| num::NonZeroU32, | ||
| num::{NonZero, NonZeroU32}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could probably get rid of the NonZeroU32, too.
| /// Number of bytes to read into the buffer. | ||
| pub len: usize, | ||
| pub len: u64, | ||
| /// Number of bytes read on success. `-1` on failure. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about -EFAULT?
This is based on a suggestion by Ellen: hermit-os#1145 (comment) Helped-by: Ellen Emilia Anna Zscheile <[email protected]>
This globally enforces a suggestion by Ellen in: #1145 Helped-by: Ellen Emilia Anna Zscheile <[email protected]>
This is based on a suggestion by Ellen: #1145 (comment) Helped-by: Ellen Emilia Anna Zscheile <[email protected]>
a1aa479 to
175d06f
Compare
Co-authored-by: Panagiotis "Ivory" Vasilopoulos <[email protected]>
- bump uhyve-interface to 0.2.0 - perform virtaddr -> physaddr conversions in hermit for read, write. - use adjust serial_port based on version of uhyve-interface used - Exit hypercalls should encode the exit code instead of a pointer to the integer. ExitArgs has been removed. - v2: add v2 support in src/stats.rs, futureproof - v2: use u64, i64 instead of usize, isize - v2 (x86_64): allocate page for MmioExits uhyve-interface (v2) should now use MmioWrite to invoke hypercalls, so as to allow for using pointers to 64-bit addresses (ports restrict us to 32-bit). This restriction should not apply for serial writes, although it also remains possible for the kernel to print out messages. Co-authored-by: Ellen Emilia Anna Zscheile <[email protected]> Co-authored-by: Jonathan Klimt <[email protected]>
175d06f to
954e7e1
Compare
Experimental branch/pull request built on top of #754