]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[riscv] Provide a DMA API implementation for RISC-V bare-metal systems
authorMichael Brown <mcb30@ipxe.org>
Tue, 8 Jul 2025 13:56:47 +0000 (14:56 +0100)
committerMichael Brown <mcb30@ipxe.org>
Wed, 9 Jul 2025 10:07:37 +0000 (11:07 +0100)
Provide an implementation of dma_map() that performs cache clean or
invalidation as required, and an implementation of dma_alloc() that
returns virtual addresses within the coherent mapping of the 32-bit
physical address space.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/arch/riscv/core/riscv_dma.c [new file with mode: 0644]
src/arch/riscv/core/zicbom.c
src/arch/riscv/include/bits/dma.h [new file with mode: 0644]
src/arch/riscv/include/ipxe/riscv_dma.h [new file with mode: 0644]
src/arch/riscv/include/ipxe/svpage.h
src/arch/riscv/include/ipxe/zicbom.h
src/config/defaults/sbi.h
src/include/bits/dma.h [new file with mode: 0644]
src/include/ipxe/dma.h

diff --git a/src/arch/riscv/core/riscv_dma.c b/src/arch/riscv/core/riscv_dma.c
new file mode 100644 (file)
index 0000000..d215fba
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <assert.h>
+#include <ipxe/zicbom.h>
+#include <ipxe/iomap.h>
+#include <ipxe/dma.h>
+
+/** @file
+ *
+ * iPXE DMA API for RISC-V
+ *
+ */
+
+/**
+ * Map buffer for DMA
+ *
+ * @v dma              DMA device
+ * @v map              DMA mapping to fill in
+ * @v addr             Buffer address
+ * @v len              Length of buffer
+ * @v flags            Mapping flags
+ * @ret rc             Return status code
+ */
+static int riscv_dma_map ( struct dma_device *dma __unused,
+                          struct dma_mapping *map __unused,
+                          void *addr, size_t len, int flags ) {
+
+       /* Sanity check: we cannot support bidirectional mappings */
+       assert ( ! ( ( flags & DMA_TX ) & ( flags & DMA_RX ) ) );
+
+       /* Flush cached data to transmit buffers */
+       if ( flags & DMA_TX )
+               cache_clean ( addr, len );
+
+       /* Invalidate cached data in receive buffers */
+       if ( flags & DMA_RX )
+               cache_invalidate ( addr, len );
+
+       return 0;
+}
+
+/**
+ * Allocate and map DMA-coherent buffer
+ *
+ * @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 void * riscv_dma_alloc ( struct dma_device *dma,
+                               struct dma_mapping *map,
+                               size_t len, size_t align ) {
+       physaddr_t phys;
+       void *addr;
+       void *caddr;
+
+       /* Allocate from heap */
+       addr = malloc_phys ( len, align );
+       if ( ! addr )
+               return NULL;
+
+       /* Invalidate any existing cached data */
+       cache_invalidate ( addr, len );
+
+       /* Record mapping */
+       map->dma = dma;
+       map->token = addr;
+
+       /* Calculate coherently-mapped virtual address */
+       phys = virt_to_phys ( addr );
+       assert ( phys == ( ( uint32_t ) phys ) );
+       caddr = ( ( void * ) ( intptr_t ) ( phys + SVPAGE_DMA32 ) );
+       assert ( phys == virt_to_phys ( caddr ) );
+       DBGC ( dma, "DMA allocated [%#08lx,%#08lx) via %p\n",
+              phys, ( phys + len ), caddr );
+
+       return caddr;
+}
+
+/**
+ * Unmap and free DMA-coherent buffer
+ *
+ * @v dma              DMA device
+ * @v map              DMA mapping
+ * @v addr             Buffer address
+ * @v len              Length of buffer
+ */
+static void riscv_dma_free ( struct dma_mapping *map,
+                            void *addr, size_t len ) {
+
+       /* Sanity check */
+       assert ( virt_to_phys ( addr ) == virt_to_phys ( map->token ) );
+
+       /* Free original allocation */
+       free_phys ( map->token, len );
+
+       /* Clear mapping */
+       map->dma = NULL;
+       map->token = NULL;
+}
+
+PROVIDE_DMAAPI ( riscv, dma_map, riscv_dma_map );
+PROVIDE_DMAAPI_INLINE ( riscv, dma_unmap );
+PROVIDE_DMAAPI ( riscv, dma_alloc, riscv_dma_alloc );
+PROVIDE_DMAAPI ( riscv, dma_free, riscv_dma_free );
+PROVIDE_DMAAPI ( riscv, dma_umalloc, riscv_dma_alloc );
+PROVIDE_DMAAPI ( riscv, dma_ufree, riscv_dma_free );
+PROVIDE_DMAAPI_INLINE ( riscv, dma_set_mask );
+PROVIDE_DMAAPI_INLINE ( riscv, dma );
index 306b6c4597c11b9facbbe9d66f37856921dc764e..28ff62c22b81486a9d2ad482beb003ec3ba565fb 100644 (file)
@@ -36,7 +36,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 #include <stdint.h>
 #include <ipxe/hart.h>
 #include <ipxe/xthead.h>
-#include <ipxe/iobuf.h>
 #include <ipxe/zicbom.h>
 
 /** Minimum supported cacheline size
@@ -159,18 +158,18 @@ static struct cache_extension *cache_extension = &cache_auto;
  * @v start            Start address
  * @v len              Length
  */
-void cache_clean ( struct io_buffer *iobuf ) {
+void cache_clean ( const void *start, size_t len ) {
        const void *first;
        const void *last;
 
        /* Do nothing for zero-length buffers */
-       if ( ! iob_len ( iobuf ) )
+       if ( ! len )
                return;
 
        /* Construct address range */
        first = ( ( const void * )
-                 ( ( ( intptr_t ) iobuf->data ) & ~( CACHE_STRIDE - 1 ) ) );
-       last = ( iobuf->tail - 1 );
+                 ( ( ( intptr_t ) start ) & ~( CACHE_STRIDE - 1 ) ) );
+       last = ( start + len - 1 );
 
        /* Clean cache lines */
        cache_extension->clean ( first, last );
@@ -182,18 +181,18 @@ void cache_clean ( struct io_buffer *iobuf ) {
  * @v start            Start address
  * @v len              Length
  */
-void cache_invalidate ( struct io_buffer *iobuf ) {
+void cache_invalidate ( void *start, size_t len ) {
        void *first;
        void *last;
 
        /* Do nothing for zero-length buffers */
-       if ( ! iob_len ( iobuf ) )
+       if ( ! len )
                return;
 
        /* Construct address range */
        first = ( ( void * )
-                 ( ( ( intptr_t ) iobuf->data ) & ~( CACHE_STRIDE - 1 ) ) );
-       last = ( iobuf->tail - 1 );
+                 ( ( ( intptr_t ) start ) & ~( CACHE_STRIDE - 1 ) ) );
+       last = ( start + len - 1 );
 
        /* Invalidate cache lines */
        cache_extension->invalidate ( first, last );
diff --git a/src/arch/riscv/include/bits/dma.h b/src/arch/riscv/include/bits/dma.h
new file mode 100644 (file)
index 0000000..f7decd1
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef _BITS_DMA_H
+#define _BITS_DMA_H
+
+/** @file
+ *
+ * RISCV-specific DMA API implementations
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <ipxe/riscv_dma.h>
+
+#endif /* _BITS_DMA_H */
diff --git a/src/arch/riscv/include/ipxe/riscv_dma.h b/src/arch/riscv/include/ipxe/riscv_dma.h
new file mode 100644 (file)
index 0000000..568f28a
--- /dev/null
@@ -0,0 +1,56 @@
+#ifndef _IPXE_RISCV_DMA_H
+#define _IPXE_RISCV_DMA_H
+
+/** @file
+ *
+ * iPXE DMA API for RISC-V
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#ifdef DMAAPI_RISCV
+#define DMAAPI_PREFIX_riscv
+#else
+#define DMAAPI_PREFIX_riscv __riscv_
+#endif
+
+/**
+ * Unmap buffer
+ *
+ * @v map              DMA mapping
+ */
+static inline __always_inline void
+DMAAPI_INLINE ( riscv, dma_unmap ) ( struct dma_mapping *map __unused ) {
+
+       /* Nothing to do */
+}
+
+/**
+ * Set addressable space mask
+ *
+ * @v dma              DMA device
+ * @v mask             Addressable space mask
+ */
+static inline __always_inline void
+DMAAPI_INLINE ( riscv, dma_set_mask ) ( struct dma_device *dma __unused,
+                                       physaddr_t mask __unused ) {
+
+       /* Nothing to do */
+}
+
+/**
+ * Get DMA address from virtual address
+ *
+ * @v map              DMA mapping
+ * @v addr             Address within the mapped region
+ * @ret addr           Device-side DMA address
+ */
+static inline __always_inline physaddr_t
+DMAAPI_INLINE ( riscv, dma ) ( struct dma_mapping *map __unused, void *addr ) {
+
+       /* Use physical address as device address */
+       return virt_to_phys ( addr );
+}
+
+#endif /* _IPXE_RISCV_DMA_H */
index 374c607d9215e34ca54ef30dfea1fd4100a99d96..9a0d38b9f128bad44bd3ccf130cbe05f4194b4e6 100644 (file)
@@ -23,4 +23,11 @@ IOMAP_INLINE ( svpage, io_to_bus ) ( volatile const void *io_addr ) {
        return ( ( intptr_t ) io_addr );
 }
 
+/** Base virtual address for coherent DMA mappings
+ *
+ * The 64-bit page table includes an uncached mapping of the 32-bit
+ * address space at this virtual address.
+ */
+#define SVPAGE_DMA32 0xffffffc000000000ULL
+
 #endif /* _IPXE_SVPAGE_H */
index 0aeba1e68e115b278651da8e82b66aabf1b30b51..4ba165f3c86454da787ce07cacec0412c42f4c1d 100644 (file)
@@ -9,9 +9,9 @@
 
 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 
-#include <ipxe/iobuf.h>
+#include <stdint.h>
 
-extern void cache_clean ( struct io_buffer *iobuf );
-extern void cache_invalidate ( struct io_buffer *iobuf );
+extern void cache_clean ( const void *start, size_t len );
+extern void cache_invalidate ( void *start, size_t len );
 
 #endif /* _IPXE_ZICBOM_H */
index cf677409038af331745854348d4745855ffc55b8..de3c9ca790c0bba8ef09f2016cca8d7e22c1c7aa 100644 (file)
 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 
 #define IOAPI_RISCV
-#define DMAAPI_FLAT
 #define UACCESS_OFFSET
 #define TIMER_ZICNTR
 #define ENTROPY_ZKR
 
 #if __riscv_xlen == 64
 #define IOMAP_SVPAGE
+#define DMAAPI_RISCV
 #else
 #define IOMAP_VIRT
+#define DMAAPI_FLAT
 #endif
 
 #define CONSOLE_SBI
diff --git a/src/include/bits/dma.h b/src/include/bits/dma.h
new file mode 100644 (file)
index 0000000..e9cb849
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef _BITS_DMA_H
+#define _BITS_DMA_H
+
+/** @file
+ *
+ * Dummy architecture-specific DMA API implementations
+ *
+ * This file is included only if the architecture does not provide its
+ * own version of this file.
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#endif /* _BITS_DMA_H */
index d450ef523f65feb04c37ab59575f31c401d44224..b675df2123ce4c6ed8be5a485a2ab490863a3e22 100644 (file)
@@ -345,6 +345,9 @@ DMAAPI_INLINE ( op, dma ) ( struct dma_mapping *map, void *addr ) {
        return ( virt_to_phys ( addr ) + map->offset );
 }
 
+/* Include all architecture-dependent DMA API headers */
+#include <bits/dma.h>
+
 /**
  * Map buffer for DMA
  *