mirror of
https://github.com/ipxe/ipxe
synced 2026-01-24 04:59:13 +03:00
The data cache must be invalidated twice for RX DMA buffers: once before passing ownership to the DMA device (in case the cache happens to contain dirty data that will be written back at an undefined future point), and once after receiving ownership from the DMA device (in case the CPU happens to have speculatively accessed data in the buffer while it was owned by the hardware). Only the used portion of the buffer needs to be invalidated after completion, since we do not care about data within the unused portion. Update the DMA API to include the used length as an additional parameter to dma_unmap(), and add the necessary second cache invalidation pass to the RISC-V DMA API implementation. Signed-off-by: Michael Brown <mcb30@ipxe.org>
180 lines
4.7 KiB
C
180 lines
4.7 KiB
C
/*
|
|
* Copyright (C) 2020 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 (at your option) 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 <errno.h>
|
|
#include <ipxe/dma.h>
|
|
|
|
/** @file
|
|
*
|
|
* DMA mappings
|
|
*
|
|
*/
|
|
|
|
/******************************************************************************
|
|
*
|
|
* Flat address space DMA API
|
|
*
|
|
******************************************************************************
|
|
*/
|
|
|
|
PROVIDE_DMAAPI_INLINE ( flat, dma_map );
|
|
PROVIDE_DMAAPI_INLINE ( flat, dma_unmap );
|
|
PROVIDE_DMAAPI_INLINE ( flat, dma_alloc );
|
|
PROVIDE_DMAAPI_INLINE ( flat, dma_free );
|
|
PROVIDE_DMAAPI_INLINE ( flat, dma_umalloc );
|
|
PROVIDE_DMAAPI_INLINE ( flat, dma_ufree );
|
|
PROVIDE_DMAAPI_INLINE ( flat, dma_set_mask );
|
|
PROVIDE_DMAAPI_INLINE ( flat, dma );
|
|
|
|
/******************************************************************************
|
|
*
|
|
* Operations-based DMA API
|
|
*
|
|
******************************************************************************
|
|
*/
|
|
|
|
/**
|
|
* 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 dma_op_map ( struct dma_device *dma, struct dma_mapping *map,
|
|
void *addr, size_t len, int flags ) {
|
|
struct dma_operations *op = dma->op;
|
|
|
|
if ( ! op )
|
|
return -ENODEV;
|
|
return op->map ( dma, map, addr, len, flags );
|
|
}
|
|
|
|
/**
|
|
* Unmap buffer
|
|
*
|
|
* @v map DMA mapping
|
|
* @v len Used length
|
|
*/
|
|
static void dma_op_unmap ( struct dma_mapping *map, size_t len ) {
|
|
struct dma_device *dma = map->dma;
|
|
|
|
assert ( dma != NULL );
|
|
assert ( dma->op != NULL );
|
|
dma->op->unmap ( dma, map, len );
|
|
}
|
|
|
|
/**
|
|
* 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 * dma_op_alloc ( struct dma_device *dma, struct dma_mapping *map,
|
|
size_t len, size_t align ) {
|
|
struct dma_operations *op = dma->op;
|
|
|
|
if ( ! op )
|
|
return NULL;
|
|
return op->alloc ( dma, map, len, align );
|
|
}
|
|
|
|
/**
|
|
* Unmap and free DMA-coherent buffer
|
|
*
|
|
* @v map DMA mapping
|
|
* @v addr Buffer address
|
|
* @v len Length of buffer
|
|
*/
|
|
static void dma_op_free ( struct dma_mapping *map, void *addr, size_t len ) {
|
|
struct dma_device *dma = map->dma;
|
|
|
|
assert ( dma != NULL );
|
|
assert ( dma->op != NULL );
|
|
dma->op->free ( dma, map, addr, len );
|
|
}
|
|
|
|
/**
|
|
* Allocate and map DMA-coherent buffer from external (user) memory
|
|
*
|
|
* @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 * dma_op_umalloc ( struct dma_device *dma,
|
|
struct dma_mapping *map,
|
|
size_t len, size_t align ) {
|
|
struct dma_operations *op = dma->op;
|
|
|
|
if ( ! op )
|
|
return NULL;
|
|
return op->umalloc ( dma, map, len, align );
|
|
}
|
|
|
|
/**
|
|
* Unmap and free DMA-coherent buffer from external (user) memory
|
|
*
|
|
* @v map DMA mapping
|
|
* @v addr Buffer address
|
|
* @v len Length of buffer
|
|
*/
|
|
static void dma_op_ufree ( struct dma_mapping *map, void *addr, size_t len ) {
|
|
struct dma_device *dma = map->dma;
|
|
|
|
assert ( dma != NULL );
|
|
assert ( dma->op != NULL );
|
|
dma->op->ufree ( dma, map, addr, len );
|
|
}
|
|
|
|
/**
|
|
* Set addressable space mask
|
|
*
|
|
* @v dma DMA device
|
|
* @v mask Addressable space mask
|
|
*/
|
|
static void dma_op_set_mask ( struct dma_device *dma, physaddr_t mask ) {
|
|
struct dma_operations *op = dma->op;
|
|
|
|
if ( op )
|
|
op->set_mask ( dma, mask );
|
|
}
|
|
|
|
PROVIDE_DMAAPI ( op, dma_map, dma_op_map );
|
|
PROVIDE_DMAAPI ( op, dma_unmap, dma_op_unmap );
|
|
PROVIDE_DMAAPI ( op, dma_alloc, dma_op_alloc );
|
|
PROVIDE_DMAAPI ( op, dma_free, dma_op_free );
|
|
PROVIDE_DMAAPI ( op, dma_umalloc, dma_op_umalloc );
|
|
PROVIDE_DMAAPI ( op, dma_ufree, dma_op_ufree );
|
|
PROVIDE_DMAAPI ( op, dma_set_mask, dma_op_set_mask );
|
|
PROVIDE_DMAAPI_INLINE ( op, dma );
|