[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 <mcb30@ipxe.org>
This commit is contained in:
Michael Brown
2025-07-08 14:56:47 +01:00
parent 22de0c4edf
commit 101ef74a6e
9 changed files with 241 additions and 13 deletions

View File

@@ -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 );

View 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 );

View File

@@ -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 */

View File

@@ -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 */

View 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 */

View 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 */

View File

@@ -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

15
src/include/bits/dma.h Normal file
View File

@@ -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 */

View 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
*