Hi,
This RFC series explores integrating the vm-memory API into QEMU's
rust/memory bindings.
Thanks to Paolo and Manos's many suggestions and feedback, I have
resolved many issues over the past few months, but there are still
some open issues that I would like to discuss.
This series finally provides the following safe interfaces in Rust:
* AddressSpace::write in Rust <=> address_space_write in C
- **but only** supports MEMTXATTRS_UNSPECIFIED
* AddressSpace::read in Rust <=> address_space_read_full in C
- **but only** supports MEMTXATTRS_UNSPECIFIED.
* AddressSpace::store in Rust <=> address_space_st{size} in C
- **but only** supports MEMTXATTRS_UNSPECIFIED and native endian.
* AddressSpace::load in Rust <=> address_space_ld{size} in C
- **but only** supports MEMTXATTRS_UNSPECIFIED and native endian.
And this series involves changes mainly to these three parts:
* NEW QEMU memory APIs wrapper at C side.
* Extra changes for vm-memory (downstream for now).
* NEW QEMU memory bindings/APIs based on vm-memory at Rust side.
Although the number of line changes appears to be significant, more
than half of them are documentation and comments.
(Note: the latest vm-memory v0.16.2 crate didn't contain Paolo's
commit 5f59e29c3d30 ("guest_memory: let multiple regions slice one
global bitmap"), so I have to pull the vm-memory from github directly.)
Thanks for your feedback!
Background
==========
About vm-memory crate, it's design documentation said:
"The vm-memory crate focuses on defining consumer side interfaces to
access the physical memory of the VM. It does not define how the
underlying VM memory provider is implemented. Lightweight VMMs like
CrosVM and Firecracker can make assumptions about the structure of VM's
physical memory and implement a lightweight backend to access it. For
VMMs like Qemu, a high performance and full functionality backend may
be implemented with less assumptions."
At present, in addition to the memory model abstractions (including
GuestMemoryRegion, GuestMemory, and AddressSpace) it provides, it also
implements a simple memory management backend based on mmap for RAM
access.
However, for QEMU, the backend implementation based on vm-memory is more
complex, as QEMU not only needs to consider MMIO/IOMMU, but also complex
situations such as different endian/memory attributes.
This series tries to be simple as much as possible, and leaves different
endian/memory attributes support as the open issues.
But... wait, why vm-memory is necessary?
QEMU needs the safe Rust bindings for memory access. Whatever vm-memory
is used or not, there'll be the similar wrappers over AddressSpace/
FlatView/MemorySection, and there'll be the safe bindings for translation/
memory store, load, read and write.
Even if we don't use vm-memory, we will likely end up creating something
similar to vm-memory.
Furthermore, many components in vm-memory are also inspiring for
enhancements on the QEMU Rust side.
So, why not have a try?
Introduction
============
The core idea of this series is simple:
* Implement vm_memory::GuestMemoryRegion trait for MemoryRegionSection
to represent (non-overlapping) memory region.
vm_memory::GuestMemoryRegion trait itself doesn't provide any
interface to access memory. So vm_memory::Bytes trait is also
necessary for MemoryRegionSection to access memory region.
* Implement vm_memory::GuestMemory trait for FlatView to manage
Guest memory regions (that's MemoryRegionSection).
Similarly, vm_memory::Bytes trait is also needed for FlatView
to provide methods to write/read/store/load memory.
* Implement vm_memory::AddressSpace tarit for AddressSpace, to
provide a safe address space abstraction at Rust side.
For the above three parts, the most critical stuff is related to
MemoryRegionSection.
Currently QEMU's memory API is built around MemoryRegion, and
MemoryRegionSection is only for internal use at the most time.
But vm_memory::GuestMemoryRegion trait requires us to wrap unsafe C
bindings based on MemoryRegionSection. So it's necessary to expose
some C memory APIs based MemoryRegionSection. This is the following
section:
NEW QEMU memory APIs wrappers at C side
=======================================
Around MemoryRegionSection, this series provides these interfaces:
* Some straightforward wrappers over original C interfaces:
- section_access_allowed
- section_covers_region_addr,
- section_fuzz_dma_read
- section_get_host_addr
* Critical wrappers for intermediate memroy read/write (they're still
simple wrapper over C functions):
- section_rust_read_continue_step
- section_rust_write_continue_step
* And, special C helpers for memory load/store:
- section_rust_load
MemTxResult section_rust_load(MemoryRegionSection *section,
hwaddr mr_offset, uint8_t *buf,
MemTxAttrs attrs, hwaddr len);
- section_rust_store
MemTxResult section_rust_store(MemoryRegionSection *section,
hwaddr mr_offset, const uint8_t *buf,
MemTxAttrs attrs, hwaddr len);
These 2 load/store helpers are so different: comparing with the
detail implementations of address_space_ld{size}/
address_space_st{size}, these 2 functions aren't bound with specific
type (l, q, or w), and transfer the value via byte array.
This is because of the AtomicAccess bound in vm_memory::Bytes, which
makes it difficult to convert AtomicAccess type to u64! (For more
details, please refer the comments of Bytes::store/Bytes::load in
patch 22 "rust/memory: Implement vm_memory::GuestMemoryRegion for
MemoryRegionSection").
Of course, some other wrappers are also needed for FlatView and
AddressSpace:
* For FlatView:
- flatview_ref
- flatview_translate_section
- flatview_unref
* For AddressSpace:
- address_space_lookup_section
- address_space_memory
- address_space_to_flatview
They're all simple wrappers.
But, though QEMU's native C memory API could support complex conditions,
such as different endian formats or different memory attributes,
especially for memory write/read/store/load inferfaces. For now, Rust
side can only limit the support to only native endian and only
MEMTXATTRS_UNSPECIFIED.
This issue is related with why and how to adjust vm-memory's API for
QEMU:
Extra changes for vm-memory (downstream for now)
================================================
(All the patches for vm-memory can be found at patch 10 "subprojects/
vm-memory: Patch vm-memory for QEMU memory".)
As a minimum requirement, vm-memory still needs at least two changes:
* the 0001.diff file under subprojects/packagefiles/vm-memory-0.16-rs:
guest_memory: Add a marker tarit to implement Bytes<GuestAddress> for
GuestMemory
- This patch allows QEMU to customize its own Bytes trait
implementation for FlatView, which makes it possible to have the
memory write/read process in Rust similar to
flatview_write/flatview_read in C side.
So this patch is straightforward with low risk.
* the 0002.diff file under subprojects/packagefiles/vm-memory-0.16-rs:
guest_memory: Add is_write argument for GuestMemory::try_access()
- This patch is related with how to extend vm-memory to support more
complex cases.
Paolo suggested to implement Bytes<(GuestAddress, MemTxAttrs)> for
FlatView, but I found it's tricky since memory read/write will
finally depend on GuestMemory::try_access() to do iteration. Then
try_access() should konw more information.
1) One option is like the 0002.diff, just to add more arguments,
but this option is not very flexible. It is difficult to extend
for MemTxAttrs because vm-memory does not have MemTxAttrs at all.
(But perhaps vm-memory could support more memory attributes?)
2) Another option is to move try_access() under Bytes trait, then
try_access could accepts the tuple like (GuestAddress, MemTxAttrs)
or (GuestAddress, bool, MemTxAttrs) - boolean is is_write. But
my concern is I'm not sure whether it's proper to let Bytes have
try_access() method, especially there's no other need expect
GuestMemory.
Therefore, this is the issue that blocks us to provide more flexible
(or more complex) memory access interfaces at Rust side.
NEW QEMU memory bindings/APIs based on vm-memory at Rust side
=============================================================
At least, now we could have the safe bindings for the most basic (and
minimum) memory access in Rust side - no special memory attributs, and
only naive endian.
Speaking endian, this is another open issue. The current implementation
only supports native endian, i.e., endian that is consistent with the
target. Users can obtain the current endian and adjust it, for example,
```
use qemu_api::memory::{ADDRESS_SPACE_MEMORY, target_is_big_endian};
let addr = GuestAddress(0x123438000);
let val: u32 = 5;
let val_end = if target_is_big_endian() {
val.to_be()
} else {
val.to_le()
};
assert!(ADDRESS_SPACE_MEMORY.store(addr, val_end).is_ok());
```
It can work, But idealy, for Rust, type itself could tell enough
information.
But unfortunately, the Bytes::store/load accepts AtomicAccess, it doesn't
provide any information about endian (though vm-memory has other endian
type, e.g., Le32/be32).
For more endian support, I think there would be 2 options:
1) Implement Bytes<(GuestAddress, bool, MemTxAttrs, DeviceEndian)>, and
ask C side to handle endian issues.
2) Consider to add endian information in AtomicAccess.
Option 1 seems easier, but option 2 seems more reasonable? Because only
the Bytes::store/load care about endian, and no other methods requires
it. Considering endian for the entire Bytes seems a bit overkill.
Open issues
===========
Alright, the open issues are talked in the above sections. But let me
make a summary:
* About how to support more MemTxAttrs, please see the section "Extra
changes for vm-memory (downstream for now)".
* About how to support more endian formats, please see the section
"NEW QEMU memory bindings/APIs based on vm-memory at Rust side".
Thanks and Best Regards,
Zhao
---
Zhao Liu (26):
rust/hpet: Fix the error caused by vm-memory
rust/cargo: Add the support for vm-memory
subprojects: Add thiserror-impl crate
subprojects: Add thiserror crate
subprojects: Add winapi-i686-pc-windows-gnu crate
subprojects: Add winapi-x86_64-pc-windows-gnu crate
subprojects: Add winapi crate
subprojects: Add vm-memory crate
rust: Add vm-memory in meson
subprojects/vm-memory: Patch vm-memory for QEMU memory backend
rust/cargo: Specify the patched vm-memory crate
rcu: Make rcu_read_lock & rcu_read_unlock not inline
rust: Add RCU bindings
memory: Expose interfaces about Flatview reference count to Rust side
memory: Rename address_space_lookup_region and expose it to Rust side
memory: Make flatview_do_translate() return a pointer to
MemoryRegionSection
memory: Add a translation helper to return MemoryRegionSection
memory: Rename flatview_access_allowed() to
memory_region_access_allowed()
memory: Add MemoryRegionSection based misc helpers
memory: Add wrappers of intermediate steps for read/write
memory: Add store/load interfaces for Rust side
rust/memory: Implement vm_memory::GuestMemoryRegion for
MemoryRegionSection
rust/memory: Implement vm_memory::GuestMemory for FlatView
rust/memory: Provide AddressSpace bindings
rust/memory: Add binding to check target endian
rust/hpet: Use safe binding to access address space
include/qemu/rcu.h | 45 +-
include/system/memory.h | 313 +++++-
rust/Cargo.lock | 51 +
rust/Cargo.toml | 3 +
rust/hw/timer/hpet/src/device.rs | 29 +-
rust/meson.build | 2 +
rust/qemu-api/Cargo.toml | 11 +
rust/qemu-api/meson.build | 5 +-
rust/qemu-api/src/lib.rs | 1 +
rust/qemu-api/src/memory.rs | 971 +++++++++++++++++-
rust/qemu-api/src/rcu.rs | 26 +
rust/qemu-api/wrapper.h | 2 +
scripts/archive-source.sh | 4 +-
scripts/make-release | 4 +-
subprojects/.gitignore | 6 +
.../packagefiles/thiserror-1-rs/meson.build | 23 +
.../thiserror-impl-1-rs/meson.build | 41 +
.../packagefiles/vm-memory-0.16-rs/0001.diff | 81 ++
.../packagefiles/vm-memory-0.16-rs/0002.diff | 111 ++
.../vm-memory-0.16-rs/meson.build | 35 +
.../packagefiles/winapi-0.3-rs/meson.build | 46 +
.../meson.build | 20 +
.../meson.build | 20 +
subprojects/thiserror-1-rs.wrap | 10 +
subprojects/thiserror-impl-1-rs.wrap | 10 +
subprojects/vm-memory-0.16-rs.wrap | 14 +
subprojects/winapi-0.3-rs.wrap | 10 +
.../winapi-i686-pc-windows-gnu-0.4-rs.wrap | 10 +
.../winapi-x86_64-pc-windows-gnu-0.4-rs.wrap | 10 +
system/memory-internal.h | 1 -
system/memory.c | 7 +-
system/physmem.c | 200 +++-
util/rcu.c | 43 +
33 files changed, 2040 insertions(+), 125 deletions(-)
create mode 100644 rust/qemu-api/src/rcu.rs
create mode 100644 subprojects/packagefiles/thiserror-1-rs/meson.build
create mode 100644 subprojects/packagefiles/thiserror-impl-1-rs/meson.build
create mode 100644 subprojects/packagefiles/vm-memory-0.16-rs/0001.diff
create mode 100644 subprojects/packagefiles/vm-memory-0.16-rs/0002.diff
create mode 100644 subprojects/packagefiles/vm-memory-0.16-rs/meson.build
create mode 100644 subprojects/packagefiles/winapi-0.3-rs/meson.build
create mode 100644 subprojects/packagefiles/winapi-i686-pc-windows-gnu-0.4-rs/meson.build
create mode 100644 subprojects/packagefiles/winapi-x86_64-pc-windows-gnu-0.4-rs/meson.build
create mode 100644 subprojects/thiserror-1-rs.wrap
create mode 100644 subprojects/thiserror-impl-1-rs.wrap
create mode 100644 subprojects/vm-memory-0.16-rs.wrap
create mode 100644 subprojects/winapi-0.3-rs.wrap
create mode 100644 subprojects/winapi-i686-pc-windows-gnu-0.4-rs.wrap
create mode 100644 subprojects/winapi-x86_64-pc-windows-gnu-0.4-rs.wrap
--
2.34.1