mirror of
https://github.com/ipxe/ipxe
synced 2025-12-13 15:31:42 +03:00
[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:
133
src/arch/riscv/core/riscv_dma.c
Normal file
133
src/arch/riscv/core/riscv_dma.c
Normal 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 );
|
||||
@@ -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 );
|
||||
|
||||
14
src/arch/riscv/include/bits/dma.h
Normal file
14
src/arch/riscv/include/bits/dma.h
Normal 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 */
|
||||
56
src/arch/riscv/include/ipxe/riscv_dma.h
Normal file
56
src/arch/riscv/include/ipxe/riscv_dma.h
Normal 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 */
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
15
src/include/bits/dma.h
Normal 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 */
|
||||
@@ -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
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user