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


Reply via email to