This is an automated email from the ASF dual-hosted git repository.

xiaoxiang781216 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit 2e869ca85b730035daf6e49bbbef07c15f99d496
Author: Bowen Wang <[email protected]>
AuthorDate: Mon Jun 22 22:56:41 2026 +0800

    drivers/virtio/virtio-mmio: leave buffers unmapped except ADDRENV user ones
    
    The host (QEMU) can access arbitrary guest memory over MMIO, so buffers
    need no bouncing: leave map, unmap and free_largest NULL so
    virtio_map_buf returns the buffer as is (zero copy).
    
    With CONFIG_ARCH_ADDRENV a user buffer is only virtually contiguous,
    while the virtqueue translates its first byte and the device accesses
    len bytes from that physical address. Add virtio_addrenv_map/unmap/
    free_largest, which bounce a user address through the kernel heap and
    pass a kernel address through, and use them in that configuration.
    free_largest is a fixed 64 KiB so it never takes the heap lock, which
    fails in interrupt and panic context.
    
    Signed-off-by: Bowen Wang <[email protected]>
---
 drivers/virtio/virtio-mmio.c  |  19 ++++++-
 drivers/virtio/virtio.c       | 116 ++++++++++++++++++++++++++++++++++++++++++
 include/nuttx/virtio/virtio.h |  11 ++++
 3 files changed, 144 insertions(+), 2 deletions(-)

diff --git a/drivers/virtio/virtio-mmio.c b/drivers/virtio/virtio-mmio.c
index 46ea346dae2..cc64581401a 100644
--- a/drivers/virtio/virtio-mmio.c
+++ b/drivers/virtio/virtio-mmio.c
@@ -262,8 +262,23 @@ static const struct virtio_dispatch g_virtio_mmio_dispatch 
=
 
 static const struct virtio_memory_ops g_virtio_mmio_mmops =
 {
-  virtio_mmio_alloc_buf, /* Alloc */
-  virtio_mmio_free_buf,  /* Free */
+  .alloc        = virtio_mmio_alloc_buf,
+  .free         = virtio_mmio_free_buf,
+
+  /* The host (QEMU) can access arbitrary guest memory, so buffers are used
+   * as is (zero copy), except user buffers of a per-process address
+   * environment, which are not physically contiguous.
+   */
+
+#ifdef CONFIG_ARCH_ADDRENV
+  .free_largest = virtio_addrenv_free_largest,
+  .map          = virtio_addrenv_map,
+  .unmap        = virtio_addrenv_unmap,
+#else
+  .free_largest = NULL,
+  .map          = NULL,
+  .unmap        = NULL,
+#endif
 };
 
 /****************************************************************************
diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c
index d7623df5583..bc8682b3b4e 100644
--- a/drivers/virtio/virtio.c
+++ b/drivers/virtio/virtio.c
@@ -26,6 +26,9 @@
 
 #include <nuttx/debug.h>
 
+#include <string.h>
+
+#include <nuttx/arch.h>
 #include <nuttx/nuttx.h>
 #include <nuttx/mutex.h>
 #include <nuttx/kmalloc.h>
@@ -41,6 +44,14 @@
 #include "virtio-serial.h"
 #include "virtio-snd.h"
 
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Largest buffer virtio_addrenv_map() is asked to bounce at once */
+
+#define VIRTIO_ADDRENV_MAP_MAX (64 * 1024)
+
 /****************************************************************************
  * Private Types
  ****************************************************************************/
@@ -356,3 +367,108 @@ int virtio_unregister_device(FAR struct virtio_device 
*device)
   nxmutex_unlock(&g_virtio_bus.lock);
   return ret;
 }
+
+#ifdef CONFIG_ARCH_ADDRENV
+
+/****************************************************************************
+ * Name: virtio_addrenv_free_largest
+ *
+ * Description:
+ *   Return the largest buffer to pass to virtio_addrenv_map() at once, for
+ *   a transport whose device reaches guest physical memory directly.
+ *
+ * Input Parameters:
+ *   vdev - The virtio device
+ *
+ * Returned Value:
+ *   The largest mappable length in bytes.
+ *
+ ****************************************************************************/
+
+size_t virtio_addrenv_free_largest(FAR struct virtio_device *vdev)
+{
+  /* A fixed limit: walking the heap here would take the heap lock on
+   * every request, which fails in interrupt and panic context.
+   */
+
+  return VIRTIO_ADDRENV_MAP_MAX;
+}
+
+/****************************************************************************
+ * Name: virtio_addrenv_map
+ *
+ * Description:
+ *   Map a buffer for a transport whose device reaches guest physical memory
+ *   directly. A user address is only virtually contiguous, while the device
+ *   accesses len bytes from the physical address of its first byte, so it
+ *   is bounced through the kernel heap. A kernel address is linearly
+ *   mapped and used as is.
+ *
+ * Input Parameters:
+ *   vdev - The virtio device
+ *   buf  - The buffer to map
+ *   len  - The buffer length in bytes
+ *   dir  - The transfer direction
+ *
+ * Returned Value:
+ *   The address to give the device, or NULL when out of memory.
+ *
+ ****************************************************************************/
+
+FAR void *virtio_addrenv_map(FAR struct virtio_device *vdev, FAR void *buf,
+                             size_t len, enum virtio_dma_map dir)
+{
+  FAR void *bounce;
+
+  if (!up_addrenv_user_vaddr((uintptr_t)buf))
+    {
+      return buf;
+    }
+
+  bounce = kmm_malloc(len);
+  if (bounce != NULL && dir != VIRTIO_DMA_FROM_DEVICE)
+    {
+      memcpy(bounce, buf, len);
+    }
+
+  return bounce;
+}
+
+/****************************************************************************
+ * Name: virtio_addrenv_unmap
+ *
+ * Description:
+ *   Release a buffer mapped by virtio_addrenv_map(), copying the data the
+ *   device wrote back to the user buffer. Must run in the address
+ *   environment the buffer was mapped in.
+ *
+ * Input Parameters:
+ *   vdev   - The virtio device
+ *   buf    - The buffer passed to virtio_addrenv_map()
+ *   len    - The buffer length in bytes
+ *   dir    - The transfer direction passed to virtio_addrenv_map()
+ *   mapped - The address virtio_addrenv_map() returned
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+void virtio_addrenv_unmap(FAR struct virtio_device *vdev, FAR void *buf,
+                          size_t len, enum virtio_dma_map dir,
+                          FAR void *mapped)
+{
+  if (mapped == buf)
+    {
+      return;
+    }
+
+  if (dir != VIRTIO_DMA_TO_DEVICE)
+    {
+      memcpy(buf, mapped, len);
+    }
+
+  kmm_free(mapped);
+}
+
+#endif /* CONFIG_ARCH_ADDRENV */
diff --git a/include/nuttx/virtio/virtio.h b/include/nuttx/virtio/virtio.h
index abc768a29bc..0dc68531ba6 100644
--- a/include/nuttx/virtio/virtio.h
+++ b/include/nuttx/virtio/virtio.h
@@ -229,6 +229,17 @@ int virtio_unregister_device(FAR struct virtio_device 
*device);
 
 void virtio_register_drivers(void);
 
+/* Buffer map ops for MMIO/PCI with per-process address environments */
+
+#ifdef CONFIG_ARCH_ADDRENV
+size_t virtio_addrenv_free_largest(FAR struct virtio_device *vdev);
+FAR void *virtio_addrenv_map(FAR struct virtio_device *vdev, FAR void *buf,
+                             size_t len, enum virtio_dma_map dir);
+void virtio_addrenv_unmap(FAR struct virtio_device *vdev, FAR void *buf,
+                          size_t len, enum virtio_dma_map dir,
+                          FAR void *mapped);
+#endif
+
 #ifdef __cplusplus
 }
 #endif

Reply via email to