mirror of
https://github.com/ipxe/ipxe
synced 2025-12-13 23:41:45 +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 <stdint.h>
|
||||||
#include <ipxe/hart.h>
|
#include <ipxe/hart.h>
|
||||||
#include <ipxe/xthead.h>
|
#include <ipxe/xthead.h>
|
||||||
#include <ipxe/iobuf.h>
|
|
||||||
#include <ipxe/zicbom.h>
|
#include <ipxe/zicbom.h>
|
||||||
|
|
||||||
/** Minimum supported cacheline size
|
/** Minimum supported cacheline size
|
||||||
@@ -159,18 +158,18 @@ static struct cache_extension *cache_extension = &cache_auto;
|
|||||||
* @v start Start address
|
* @v start Start address
|
||||||
* @v len Length
|
* @v len Length
|
||||||
*/
|
*/
|
||||||
void cache_clean ( struct io_buffer *iobuf ) {
|
void cache_clean ( const void *start, size_t len ) {
|
||||||
const void *first;
|
const void *first;
|
||||||
const void *last;
|
const void *last;
|
||||||
|
|
||||||
/* Do nothing for zero-length buffers */
|
/* Do nothing for zero-length buffers */
|
||||||
if ( ! iob_len ( iobuf ) )
|
if ( ! len )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Construct address range */
|
/* Construct address range */
|
||||||
first = ( ( const void * )
|
first = ( ( const void * )
|
||||||
( ( ( intptr_t ) iobuf->data ) & ~( CACHE_STRIDE - 1 ) ) );
|
( ( ( intptr_t ) start ) & ~( CACHE_STRIDE - 1 ) ) );
|
||||||
last = ( iobuf->tail - 1 );
|
last = ( start + len - 1 );
|
||||||
|
|
||||||
/* Clean cache lines */
|
/* Clean cache lines */
|
||||||
cache_extension->clean ( first, last );
|
cache_extension->clean ( first, last );
|
||||||
@@ -182,18 +181,18 @@ void cache_clean ( struct io_buffer *iobuf ) {
|
|||||||
* @v start Start address
|
* @v start Start address
|
||||||
* @v len Length
|
* @v len Length
|
||||||
*/
|
*/
|
||||||
void cache_invalidate ( struct io_buffer *iobuf ) {
|
void cache_invalidate ( void *start, size_t len ) {
|
||||||
void *first;
|
void *first;
|
||||||
void *last;
|
void *last;
|
||||||
|
|
||||||
/* Do nothing for zero-length buffers */
|
/* Do nothing for zero-length buffers */
|
||||||
if ( ! iob_len ( iobuf ) )
|
if ( ! len )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Construct address range */
|
/* Construct address range */
|
||||||
first = ( ( void * )
|
first = ( ( void * )
|
||||||
( ( ( intptr_t ) iobuf->data ) & ~( CACHE_STRIDE - 1 ) ) );
|
( ( ( intptr_t ) start ) & ~( CACHE_STRIDE - 1 ) ) );
|
||||||
last = ( iobuf->tail - 1 );
|
last = ( start + len - 1 );
|
||||||
|
|
||||||
/* Invalidate cache lines */
|
/* Invalidate cache lines */
|
||||||
cache_extension->invalidate ( first, last );
|
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 );
|
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 */
|
#endif /* _IPXE_SVPAGE_H */
|
||||||
|
|||||||
@@ -9,9 +9,9 @@
|
|||||||
|
|
||||||
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
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_clean ( const void *start, size_t len );
|
||||||
extern void cache_invalidate ( struct io_buffer *iobuf );
|
extern void cache_invalidate ( void *start, size_t len );
|
||||||
|
|
||||||
#endif /* _IPXE_ZICBOM_H */
|
#endif /* _IPXE_ZICBOM_H */
|
||||||
|
|||||||
@@ -10,15 +10,16 @@
|
|||||||
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||||
|
|
||||||
#define IOAPI_RISCV
|
#define IOAPI_RISCV
|
||||||
#define DMAAPI_FLAT
|
|
||||||
#define UACCESS_OFFSET
|
#define UACCESS_OFFSET
|
||||||
#define TIMER_ZICNTR
|
#define TIMER_ZICNTR
|
||||||
#define ENTROPY_ZKR
|
#define ENTROPY_ZKR
|
||||||
|
|
||||||
#if __riscv_xlen == 64
|
#if __riscv_xlen == 64
|
||||||
#define IOMAP_SVPAGE
|
#define IOMAP_SVPAGE
|
||||||
|
#define DMAAPI_RISCV
|
||||||
#else
|
#else
|
||||||
#define IOMAP_VIRT
|
#define IOMAP_VIRT
|
||||||
|
#define DMAAPI_FLAT
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define CONSOLE_SBI
|
#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 );
|
return ( virt_to_phys ( addr ) + map->offset );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Include all architecture-dependent DMA API headers */
|
||||||
|
#include <bits/dma.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map buffer for DMA
|
* Map buffer for DMA
|
||||||
*
|
*
|
||||||
|
|||||||
Reference in New Issue
Block a user