-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Volatile reads and writes on aarch64 sometimes generate instructions not suitable for MMIO in protected VMs #131894
Comments
Does clang make this work correctly with C |
I suppose we sometimes have a reproducer then? |
I wouldn't be so quick to assume this is not feasible or preferable, because if you want a very specific instruction, this might in fact be the preferred solution. The reason why is this ain't gonna lower to a single ptr.cast::<[u8; 1024]>().write_volatile(bytes); It can't. That is why I am very interested in seeing what Rust source code actually causes this to be a problem. |
From @Noratrieb
We have observed this eissue with From @workingjubilee
The issue is not that it should lower to a single I see a few possible solutions to this:
|
I mean, the Rust compiler could be the one emitting the inline asm when it monomorphizes a word-sized-or-less |
Given that clang has the same issue and rustc just compiles it to |
If LLVM doesn't do it, is this pattern even blessed by anyone? Does GCC guarantee that volatile writes are virtualizable? |
Well, the C language's specification for As for gcc, its C frontend actually specifically requires you to write The Linux Kernel advises against using volatile, even for MMIO, but advises using specific accessor functions... so, equivalent to some kind of |
core::ptr::write_volatile
andcore::ptr::read_volatile
are documented as being intended to act on I/O memory, i.e. for MMIO. These are indeed widely used by many crates providing drivers for MMIO devices across the ecosystem.When running in a virtual machine, MMIO must be emulated by the hypervisor. This is done (on aarch64 at least) by having the MMIO region unmapped in the stage 2 page table, which results in a data abort to the hypervisor when the VM attempts to read or write the MMIO region. The hypervisor then decodes the exception syndrome register (
esr_el2
) and uses the fault address register (far_el2
) to determine which MMIO address is being accessed and perform the appropriate operation in response.Unfortunately, rustc sometimes compiles
core::ptr::write_volatile
on aarch64 to something likestr w9, [x0], #4
. We've seen this happen particularly since Rust 1.78, but it may be possible with earlier Rust versions too. The problem with this is that this post-addressing mode is performing register writeback (in this case, incrementingx0
by 4), and so doesn't set the exception syndrome register. This prevents the hypervisor from emulating the MMIO access, as it has no way of decoding the instruction syndrome or finding the faulting address.In an unprotected VM (e.g. regular KVM), it is possible for the VMM to work around this by reading the guest VM's memory to find the relevant instruction, decoding the instruction manually, and finding the MMIO address that way. This has a performance overhead and adds extra complexity. In the case of a protected VM where the host doesn't have access to the guest VM's memory (e.g. protected KVM), this is not possible as the VMM is not able to read the guest VM's memory and so cannot do instruction decoding. There is thus no way to emulate these attempted MMIO accesses in a protected VM on aarch64.
The net result of this is that instructions which perform register writeback (e.g. post-increment addressing modes) are not suitable for MMIO in aarch64 VMs. This is arguably a flaw in the aarch64 architecture, but as that's not feasible to fix at this point it must be fixed in the compiler instead. rustc should therefore avoid generating such instructions for
volatile_read
andvolatile_write
calls.The only alternative I can see to fixing this in rustc is for every crate which performs MMIO to use inline assembly rather than
volatile_read
/volatile_write
, but that is not a very feasible or scalable solution.The text was updated successfully, but these errors were encountered: