]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[dma] Provide dma_umalloc() for allocating large DMA-coherent buffers
authorMichael Brown <mcb30@ipxe.org>
Sun, 29 Nov 2020 10:55:14 +0000 (10:55 +0000)
committerMichael Brown <mcb30@ipxe.org>
Sun, 29 Nov 2020 11:25:40 +0000 (11:25 +0000)
Some devices (e.g. xHCI USB host controllers) may require the use of
large areas of host memory for private use by the device.  These
allocations cannot be satisfied from iPXE's limited heap space, and so
are currently allocated using umalloc() which will allocate external
system memory (and alter the system memory map as needed).

Provide dma_umalloc() to provide such allocations as part of the DMA
API, since there is otherwise no way to guarantee that the allocated
regions are usable for coherent DMA.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/core/dma.c
src/include/ipxe/dma.h
src/interface/efi/efi_pci.c

index e5fa3f3232cd0fc01feb6245f8a1629193fe231b..5d686821682254f646d3456060ac08dc7f1b75c8 100644 (file)
@@ -44,6 +44,8 @@ PROVIDE_DMAAPI_INLINE ( flat, dma_map );
 PROVIDE_DMAAPI_INLINE ( flat, dma_unmap );
 PROVIDE_DMAAPI_INLINE ( flat, dma_alloc );
 PROVIDE_DMAAPI_INLINE ( flat, dma_free );
+PROVIDE_DMAAPI_INLINE ( flat, dma_umalloc );
+PROVIDE_DMAAPI_INLINE ( flat, dma_ufree );
 PROVIDE_DMAAPI_INLINE ( flat, dma_set_mask );
 PROVIDE_DMAAPI_INLINE ( flat, dma_phys );
 
@@ -119,6 +121,41 @@ static void dma_op_free ( struct dma_mapping *map, void *addr, size_t len ) {
        dma->op->free ( dma, map, addr, len );
 }
 
+/**
+ * Allocate and map DMA-coherent buffer from external (user) memory
+ *
+ * @v dma              DMA device
+ * @v map              DMA mapping to fill in
+ * @v len              Length of buffer
+ * @v align            Physical alignment
+ * @ret addr           Buffer address, or NULL on error
+ */
+static userptr_t dma_op_umalloc ( struct dma_device *dma,
+                                 struct dma_mapping *map,
+                                 size_t len, size_t align ) {
+       struct dma_operations *op = dma->op;
+
+       if ( ! op )
+               return UNULL;
+       return op->umalloc ( dma, map, len, align );
+}
+
+/**
+ * Unmap and free DMA-coherent buffer from external (user) memory
+ *
+ * @v map              DMA mapping
+ * @v addr             Buffer address
+ * @v len              Length of buffer
+ */
+static void dma_op_ufree ( struct dma_mapping *map, userptr_t addr,
+                          size_t len ) {
+       struct dma_device *dma = map->dma;
+
+       assert ( dma != NULL );
+       assert ( dma->op != NULL );
+       dma->op->ufree ( dma, map, addr, len );
+}
+
 /**
  * Set addressable space mask
  *
@@ -136,5 +173,7 @@ PROVIDE_DMAAPI ( op, dma_map, dma_op_map );
 PROVIDE_DMAAPI ( op, dma_unmap, dma_op_unmap );
 PROVIDE_DMAAPI ( op, dma_alloc, dma_op_alloc );
 PROVIDE_DMAAPI ( op, dma_free, dma_op_free );
+PROVIDE_DMAAPI ( op, dma_umalloc, dma_op_umalloc );
+PROVIDE_DMAAPI ( op, dma_ufree, dma_op_ufree );
 PROVIDE_DMAAPI ( op, dma_set_mask, dma_op_set_mask );
 PROVIDE_DMAAPI_INLINE ( op, dma_phys );
index b3fa24e4768c410aa55cb0c71b3221a536672fce..385e4baf761df94aefa848558e3f4228cc894379 100644 (file)
@@ -13,6 +13,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 #include <ipxe/api.h>
 #include <ipxe/io.h>
 #include <ipxe/malloc.h>
+#include <ipxe/umalloc.h>
 #include <config/ioapi.h>
 
 #ifdef DMAAPI_OP
@@ -96,6 +97,28 @@ struct dma_operations {
         */
        void ( * free ) ( struct dma_device *dma, struct dma_mapping *map,
                          void *addr, size_t len );
+       /**
+        * Allocate and map DMA-coherent buffer from external (user) memory
+        *
+        * @v dma               DMA device
+        * @v map               DMA mapping to fill in
+        * @v len               Length of buffer
+        * @v align             Physical alignment
+        * @ret addr            Buffer address, or NULL on error
+        */
+       userptr_t ( * umalloc ) ( struct dma_device *dma,
+                                 struct dma_mapping *map,
+                                 size_t len, size_t align );
+       /**
+        * Unmap and free DMA-coherent buffer from external (user) memory
+        *
+        * @v dma               DMA device
+        * @v map               DMA mapping
+        * @v addr              Buffer address
+        * @v len               Length of buffer
+        */
+       void ( * ufree ) ( struct dma_device *dma, struct dma_mapping *map,
+                          userptr_t addr, size_t len );
        /**
         * Set addressable space mask
         *
@@ -233,6 +256,55 @@ DMAAPI_INLINE ( flat, dma_free ) ( struct dma_mapping *map,
        }
 }
 
+/**
+ * Allocate and map DMA-coherent buffer from external (user) memory
+ *
+ * @v dma              DMA device
+ * @v map              DMA mapping to fill in
+ * @v len              Length of buffer
+ * @v align            Physical alignment
+ * @ret addr           Buffer address, or NULL on error
+ */
+static inline __always_inline userptr_t
+DMAAPI_INLINE ( flat, dma_umalloc ) ( struct dma_device *dma,
+                                     struct dma_mapping *map,
+                                     size_t len, size_t align __unused ) {
+       userptr_t addr;
+
+       /* Allocate buffer */
+       addr = umalloc ( len );
+
+       /* Increment mapping count (for debugging) */
+       if ( DBG_LOG && addr ) {
+               map->dma = dma;
+               dma->mapped++;
+       }
+
+       return addr;
+}
+
+/**
+ * Unmap and free DMA-coherent buffer from external (user) memory
+ *
+ * @v map              DMA mapping
+ * @v addr             Buffer address
+ * @v len              Length of buffer
+ */
+static inline __always_inline void
+DMAAPI_INLINE ( flat, dma_ufree ) ( struct dma_mapping *map,
+                                   userptr_t addr, size_t len __unused ) {
+
+       /* Free buffer */
+       ufree ( addr );
+
+       /* Decrement mapping count (for debugging) */
+       if ( DBG_LOG ) {
+               assert ( map->dma != NULL );
+               map->dma->mapped--;
+               map->dma = NULL;
+       }
+}
+
 /**
  * Set addressable space mask
  *
@@ -316,6 +388,27 @@ void * dma_alloc ( struct dma_device *dma, struct dma_mapping *map,
  */
 void dma_free ( struct dma_mapping *map, void *addr, size_t len );
 
+/**
+ * Allocate and map DMA-coherent buffer from external (user) memory
+ *
+ * @v dma              DMA device
+ * @v map              DMA mapping to fill in
+ * @v len              Length of buffer
+ * @v align            Physical alignment
+ * @ret addr           Buffer address, or NULL on error
+ */
+userptr_t dma_umalloc ( struct dma_device *dma, struct dma_mapping *map,
+                       size_t len, size_t align );
+
+/**
+ * Unmap and free DMA-coherent buffer from external (user) memory
+ *
+ * @v map              DMA mapping
+ * @v addr             Buffer address
+ * @v len              Length of buffer
+ */
+void dma_ufree ( struct dma_mapping *map, userptr_t addr, size_t len );
+
 /**
  * Set addressable space mask
  *
index 9f6bf952bde30be85be79ad91d9b4aa14acf23ec..6b32fd6a9596d432bf97c1bae2420a057aedfd6e 100644 (file)
@@ -504,6 +504,38 @@ static void efipci_dma_free ( struct dma_device *dma, struct dma_mapping *map,
                dma->allocated--;
 }
 
+/**
+ * Allocate and map DMA-coherent buffer from external (user) memory
+ *
+ * @v dma              DMA device
+ * @v map              DMA mapping to fill in
+ * @v len              Length of buffer
+ * @v align            Physical alignment
+ * @ret addr           Buffer address, or NULL on error
+ */
+static userptr_t efipci_dma_umalloc ( struct dma_device *dma,
+                                     struct dma_mapping *map,
+                                     size_t len, size_t align ) {
+       void *addr;
+
+       addr = efipci_dma_alloc ( dma, map, len, align );
+       return virt_to_user ( addr );
+}
+
+/**
+ * Unmap and free DMA-coherent buffer from external (user) memory
+ *
+ * @v dma              DMA device
+ * @v map              DMA mapping
+ * @v addr             Buffer address
+ * @v len              Length of buffer
+ */
+static void efipci_dma_ufree ( struct dma_device *dma, struct dma_mapping *map,
+                              userptr_t addr, size_t len ) {
+
+       efipci_dma_free ( dma, map, user_to_virt ( addr, 0 ), len );
+}
+
 /**
  * Set addressable space mask
  *
@@ -542,6 +574,8 @@ static struct dma_operations efipci_dma_operations = {
        .unmap = efipci_dma_unmap,
        .alloc = efipci_dma_alloc,
        .free = efipci_dma_free,
+       .umalloc = efipci_dma_umalloc,
+       .ufree = efipci_dma_ufree,
        .set_mask = efipci_dma_set_mask,
 };