Currently, there are many store/load variants defined in
memory_ldst.c.inc.

However, the vm-memory crate's Bytes::store() and Bytes::load() methods
are bound by the AtomicAccess trait, making it almost impossible to
select  the proper interface (e.g., for l, w, or q) based on a specific
type.

Therefore, it is necessary to provide interfaces that hide these type
details as much as possible. Compared to the
address_space_st{size}_{endian} and address_space_ld{size}_{endian}
functions, the differences include:

 * No address translation; they only perform memory access.

 * They treat the byte array as raw bytes.  RAM accesses copy the bytes
   unchanged.  For MMIO accesses, the Rust caller must use the correct
   endian ordering for the target device (via LeNN/BeNN), just as the C
   side must select the appropriate address_space_st{size}_le() or _be()
   variants.

 * They use byte arrays instead of a single uint64_t for the value
   being written or read, eliminating the need for the Rust side to
   convert generic types to u64.
   - However, this incurs an extra cost for MMIO accesses, as it
     requires conversion between byte arrays and uint64_t internally.

 * They do not handle cross-region MMIO accesses. The Rust side will be
   responsible for handling such edge cases.

Signed-off-by: Zhao Liu <[email protected]>
---
Changes since v1:
 * Drop the native endian assumption and require the caller to use the
   target device's endianness.
---
 include/system/memory.h | 50 ++++++++++++++++++++++++++++
 system/physmem.c        | 72 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 122 insertions(+)

diff --git a/include/system/memory.h b/include/system/memory.h
index 87006ce79d47..32399c111f48 100644
--- a/include/system/memory.h
+++ b/include/system/memory.h
@@ -2970,6 +2970,56 @@ MemTxResult 
rust_section_write_continue_step(MemoryRegionSection *section,
 MemTxResult rust_section_read_continue_step(MemoryRegionSection *section,
     MemTxAttrs attrs, uint8_t *buf, hwaddr len, hwaddr mr_addr, hwaddr *l);
 
+/**
+ * rust_section_store: Store data to a #MemoryRegionSection.
+ *
+ * Note: This function should only be used by the Rust side, and callers
+ * should not invoke it directly!
+ *
+ * This function does a single-access store without translation. @buf provides
+ * raw bytes: RAM uses direct memory copy; MMIO expects data in device
+ * endianness.
+ *
+ * Must be called within an RCU critical section.
+ *
+ * @section: The #MemoryRegionSection to be accessed.
+ * @mr_offset: The address within that memory region.
+ * @buf: Buffer containing the data to be written.
+ * @attrs: Memory transaction attributes.
+ * @len: The number of bytes to write.
+ *
+ * Returns:
+ * A MemTxResult indicating whether the operation succeeded or failed.
+ */
+MemTxResult rust_section_store(MemoryRegionSection *section,
+                               hwaddr mr_offset, const uint8_t *buf,
+                               MemTxAttrs attrs, hwaddr len);
+
+/**
+ * rust_section_load: Load data from a #MemoryRegionSection.
+ *
+ * Note: This function should only be used by the Rust side, and callers
+ * should not invoke it directly!
+ *
+ * This function does a single-access load without translation. @buf receives
+ * raw bytes: RAM uses direct memory copy; MMIO returns data in device
+ * endianness.
+ *
+ * Must be called within an RCU critical section.
+ *
+ * @section: The #MemoryRegionSection to be accessed.
+ * @mr_offset: The address within that memory region.
+ * @buf: The output buffer to store the read data.
+ * @attrs: Memory transaction attributes.
+ * @len: The expected number of bytes to read.
+ *
+ * Returns:
+ * A MemTxResult indicating whether the operation succeeded or failed.
+ */
+MemTxResult rust_section_load(MemoryRegionSection *section,
+                              hwaddr mr_offset, uint8_t *buf,
+                              MemTxAttrs attrs, hwaddr len);
+
 /* Coalesced MMIO regions are areas where write operations can be reordered.
  * This usually implies that write operations are side-effect free.  This 
allows
  * batching which can make a major impact on performance when using
diff --git a/system/physmem.c b/system/physmem.c
index f19709be59e5..51abb6865b96 100644
--- a/system/physmem.c
+++ b/system/physmem.c
@@ -3518,6 +3518,78 @@ void physical_memory_read(hwaddr addr, void *buf, hwaddr 
len)
                        MEMTXATTRS_UNSPECIFIED, buf, len);
 }
 
+MemTxResult rust_section_store(MemoryRegionSection *section,
+                               hwaddr mr_offset, const uint8_t *buf,
+                               MemTxAttrs attrs, hwaddr len)
+{
+    MemoryRegion *mr = section->mr;
+    MemTxResult r;
+
+    /*
+     * Rust encodes the requested endian order in @buf, via LeNN or BeNN.
+     *
+     * For RAM, store the bytes unchanged.
+     * For MMIO, @buf is read into a host-order temporary value, then
+     * adjust_endianness() converts it to the device's endianness later.
+     *
+     * Note that there is no guarantee that the endian order encoded by Rust
+     * will match the target device's endianness. Caller should use the right
+     * LeNN or BeNN.
+     */
+    if (!memory_access_is_direct(mr, true, attrs)) {
+        bool release_lock = false;
+        uint64_t val = ldn_he_p(buf, len);
+
+        release_lock |= prepare_mmio_access(mr);
+        r = memory_region_dispatch_write(mr, mr_offset, val,
+                                         size_memop(len), attrs);
+        if (release_lock) {
+            bql_unlock();
+        }
+    } else {
+        uint8_t *ptr = qemu_map_ram_ptr(mr->ram_block, mr_offset);
+
+        memcpy(ptr, buf, len);
+        invalidate_and_set_dirty(mr, mr_offset, len);
+        r = MEMTX_OK;
+    }
+
+    return r;
+}
+
+MemTxResult rust_section_load(MemoryRegionSection *section,
+                              hwaddr mr_offset, uint8_t *buf,
+                              MemTxAttrs attrs, hwaddr len)
+{
+    MemoryRegion *mr = section->mr;
+    MemTxResult r;
+
+    /*
+     * Similar to rust_section_store():
+     * For RAM, load the bytes unchanged, but for MMIO, it uses a host-order
+     * temporary value to help with conversion.
+     */
+    if (!memory_access_is_direct(mr, false, attrs)) {
+        bool release_lock = false;
+        uint64_t val;
+
+        release_lock |= prepare_mmio_access(mr);
+        r = memory_region_dispatch_read(mr, mr_offset, &val,
+                                        size_memop(len), attrs);
+        stn_he_p(buf, len, val);
+        if (release_lock) {
+            bql_unlock();
+        }
+    } else {
+        uint8_t *ptr = qemu_map_ram_ptr(mr->ram_block, mr_offset);
+
+        memcpy(buf, ptr, len);
+        r = MEMTX_OK;
+    }
+
+    return r;
+}
+
 void physical_memory_write(hwaddr addr, const void *buf, hwaddr len)
 {
     address_space_write(&address_space_memory, addr,
-- 
2.34.1


Reply via email to