From 101ef74a6e7ed29af42f9d5432504b437e75374d Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 8 Jul 2025 14:56:47 +0100 Subject: [PATCH] [riscv] Provide a DMA API implementation for RISC-V bare-metal systems 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 --- src/arch/riscv/core/riscv_dma.c | 133 ++++++++++++++++++++++++ src/arch/riscv/core/zicbom.c | 17 ++- src/arch/riscv/include/bits/dma.h | 14 +++ src/arch/riscv/include/ipxe/riscv_dma.h | 56 ++++++++++ src/arch/riscv/include/ipxe/svpage.h | 7 ++ src/arch/riscv/include/ipxe/zicbom.h | 6 +- src/config/defaults/sbi.h | 3 +- src/include/bits/dma.h | 15 +++ src/include/ipxe/dma.h | 3 + 9 files changed, 241 insertions(+), 13 deletions(-) create mode 100644 src/arch/riscv/core/riscv_dma.c create mode 100644 src/arch/riscv/include/bits/dma.h create mode 100644 src/arch/riscv/include/ipxe/riscv_dma.h create mode 100644 src/include/bits/dma.h diff --git a/src/arch/riscv/core/riscv_dma.c b/src/arch/riscv/core/riscv_dma.c new file mode 100644 index 000000000..d215fba2c --- /dev/null +++ b/src/arch/riscv/core/riscv_dma.c @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2025 Michael Brown . + * + * 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 +#include +#include +#include + +/** @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 ); diff --git a/src/arch/riscv/core/zicbom.c b/src/arch/riscv/core/zicbom.c index 306b6c459..28ff62c22 100644 --- a/src/arch/riscv/core/zicbom.c +++ b/src/arch/riscv/core/zicbom.c @@ -36,7 +36,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include -#include #include /** 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 index 000000000..f7decd14c --- /dev/null +++ b/src/arch/riscv/include/bits/dma.h @@ -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 + +#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 index 000000000..568f28afe --- /dev/null +++ b/src/arch/riscv/include/ipxe/riscv_dma.h @@ -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 */ diff --git a/src/arch/riscv/include/ipxe/svpage.h b/src/arch/riscv/include/ipxe/svpage.h index 374c607d9..9a0d38b9f 100644 --- a/src/arch/riscv/include/ipxe/svpage.h +++ b/src/arch/riscv/include/ipxe/svpage.h @@ -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 */ diff --git a/src/arch/riscv/include/ipxe/zicbom.h b/src/arch/riscv/include/ipxe/zicbom.h index 0aeba1e68..4ba165f3c 100644 --- a/src/arch/riscv/include/ipxe/zicbom.h +++ b/src/arch/riscv/include/ipxe/zicbom.h @@ -9,9 +9,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); -#include +#include -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 */ diff --git a/src/config/defaults/sbi.h b/src/config/defaults/sbi.h index cf6774090..de3c9ca79 100644 --- a/src/config/defaults/sbi.h +++ b/src/config/defaults/sbi.h @@ -10,15 +10,16 @@ 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 index 000000000..e9cb84942 --- /dev/null +++ b/src/include/bits/dma.h @@ -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 */ diff --git a/src/include/ipxe/dma.h b/src/include/ipxe/dma.h index d450ef523..b675df212 100644 --- a/src/include/ipxe/dma.h +++ b/src/include/ipxe/dma.h @@ -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 + /** * Map buffer for DMA *