-
Notifications
You must be signed in to change notification settings - Fork 104
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
Converting AtomicAccess to u64 #187
Comments
In C++ this would be solved by specializing template functions, but this is not possible in Rust: struct U64Converter {
value: u64,
}
macro_rules! as_plain_integer_or_fail {
($T:ty) => {
impl From<$T> for U64Converter {
fn from(val: $T) -> Self {
Self { value: val as u64 }
}
}
};
}
as_plain_integer_or_fail!(u8);
as_plain_integer_or_fail!(u16);
// ...
impl<T> From<T> for U64Converter { // Error: Conflicting implementations
fn from(_val: T) -> Self {
panic!("Trying to convert unsupported type to u64");
}
} |
So this solution gets somewhat closer: unsafe fn any_int_to_u64<T>(val: T) -> u64 {
use std::mem::{size_of_val, transmute};
match size_of_val(&val) {
1 => transmute::<T, u8>(val) as u64,
2 => transmute::<T, u16>(val) as u64,
4 => transmute::<T, u16>(val) as u64,
8 => transmute::<T, u16>(val) as u64,
unsupported => unimplemented!("Integer size {} cannot be converted to u64", unsupported),
}
} but fails due to:
See also rust-lang/rust#61956. |
What does this means?
When storing an AtomicU8 value to an address, the u8 value will be converted to u64 internally? |
When we've designed our internal memory bus we considered using generics, but abandoned the idea, because Rust has no good support for generic programming over integer types so far. So we ended up with a trait fn read(&self, req: Request) -> u64;
fn write(&self, req: Request, value: u64);
let value = some_device.read(Request::new(addr, RequestSize::Size1)) Yes, this value is let a_byte: u8 = 17;
some_device.write(Request::new(addr, RequestSize::Size1), a_byte.into()); But as for reading, there can just be helpers for We make the distinction between "immediate" accesses (1,2,4,8 byte writes that happen "atomically") and "bulk" accesses where you don't really care about whether data written as bytes or words. So there are additional methods for bulk memory access. This is loosely modelled around how PCI works. The rationale is if people manage to implement it in hardware, it is sufficient to implement device models. ;) So while there is probably a very interesting discussion about the merits of each approach, it seems that at least the I have a working (but still extremely sad and unsafe) workaround for now: fn atomic_int_to_u64<T: AtomicAccess>(val: T) -> u64 {
use std::mem::{size_of_val, transmute};
let val: <<T as AtomicAccess>::A as AtomicInteger>::V = val.into();
unsafe {
// We cannot transmute unknown types directly due to
// https://github.com/rust-lang/rust/issues/61956. The workaround is to transmute pointer
// types and dereference them.
match size_of_val(&val) {
1 => *transmute::<*const _, *const u8>(&val) as u64,
2 => *transmute::<*const _, *const u16>(&val) as u64,
4 => *transmute::<*const _, *const u32>(&val) as u64,
8 => *transmute::<*const _, *const u64>(&val) as u64,
unsupported => {
unimplemented!("Integer size {} cannot be converted to u64", unsupported)
}
}
}
} |
It seems the way from any concrete integer type to |
Maybe a concrete problem. Consider this method from the fn load<T: AtomicAccess>(
&self,
addr: MemoryRegionAddress,
order: std::sync::atomic::Ordering,
) How would you implement a device that always returns 23? The only way seems to somehow restrict the type of |
Still unsafe, but might be prettier than using transmute: Edit: I think the call to |
Interesting! I wonder whether the compiler can then still remove all the cruft. The "nice" thing about the transmute solution is that the ugliness is completely optimized away. Will try that out. Is there any example code of tests that implement MMIO devices or memory chunks that interact with these interfaces? It would be interesting to see how that is solved. |
Some examples are available in vm-virtio:
We're not using |
@andreeaflorescu Thanks for the pointers. Indeed trying to implement Thanks for the rubber ducking here. 👍 |
Maybe one additional thought for the guest memory abstraction: Besides converting our RAM abstraction to |
I'm trying to implement the
Bytes
trait for the abstractions of our hypervisor and I'm really struggling. My current problem is thatBytes
has a store method that takes the value to store as anAtomicAccess
. Now our internal abstractions really want anu64
to store data. That didn't seem much of an issue until I tried converting it:So the initially innocent looking problem of getting an actual integer I can use out of
AtomicAccess
appears really complex now. Am I missing something obvious? How do others solve this?@bonzini
The text was updated successfully, but these errors were encountered: