/** @file | |
RISC-V specific functionality for cache. | |
Copyright (c) 2020, Hewlett Packard Enterprise Development LP. All rights reserved.<BR> | |
Copyright (c) 2023, Rivos Inc. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include <Base.h> | |
#include <Library/BaseLib.h> | |
#include <Library/DebugLib.h> | |
#include <Library/PcdLib.h> | |
// | |
// TODO: Grab cache block size and make Cache Management Operation | |
// enabling decision based on RISC-V CPU HOB in | |
// future when it is available and convert PcdRiscVFeatureOverride | |
// PCD to a pointer that contains pointer to bitmap structure | |
// which can be operated more elegantly. | |
// | |
#define RISCV_CACHE_BLOCK_SIZE 64 | |
#define RISCV_CPU_FEATURE_CMO_BITMASK 0x1 | |
typedef enum { | |
CacheOpClean, | |
CacheOpFlush, | |
CacheOpInvld, | |
} CACHE_OP; | |
/** | |
Verify CBOs are supported by this HW | |
TODO: Use RISC-V CPU HOB once available. | |
**/ | |
STATIC | |
BOOLEAN | |
RiscVIsCMOEnabled ( | |
VOID | |
) | |
{ | |
// If CMO is disabled in HW, skip Override check | |
// Otherwise this PCD can override settings | |
return ((PcdGet64 (PcdRiscVFeatureOverride) & RISCV_CPU_FEATURE_CMO_BITMASK) != 0); | |
} | |
/** | |
Performs required opeartion on cache lines in the cache coherency domain | |
of the calling CPU. If Address is not aligned on a cache line boundary, | |
then entire cache line containing Address is operated. If Address + Length | |
is not aligned on a cache line boundary, then the entire cache line | |
containing Address + Length -1 is operated. | |
If Length is greater than (MAX_ADDRESS - Address + 1), then ASSERT(). | |
@param Address The base address of the cache lines to | |
invalidate. | |
@param Length The number of bytes to invalidate from the instruction | |
cache. | |
@param Op Type of CMO operation to be performed | |
@return Address. | |
**/ | |
STATIC | |
VOID | |
CacheOpCacheRange ( | |
IN VOID *Address, | |
IN UINTN Length, | |
IN CACHE_OP Op | |
) | |
{ | |
UINTN CacheLineSize; | |
UINTN Start; | |
UINTN End; | |
if (Length == 0) { | |
return; | |
} | |
if ((Op != CacheOpInvld) && (Op != CacheOpFlush) && (Op != CacheOpClean)) { | |
return; | |
} | |
ASSERT ((Length - 1) <= (MAX_ADDRESS - (UINTN)Address)); | |
CacheLineSize = RISCV_CACHE_BLOCK_SIZE; | |
Start = (UINTN)Address; | |
// | |
// Calculate the cache line alignment | |
// | |
End = (Start + Length + (CacheLineSize - 1)) & ~(CacheLineSize - 1); | |
Start &= ~((UINTN)CacheLineSize - 1); | |
DEBUG ( | |
(DEBUG_VERBOSE, | |
"CacheOpCacheRange: Performing Cache Management Operation %d \n", Op) | |
); | |
do { | |
switch (Op) { | |
case CacheOpInvld: | |
RiscVCpuCacheInvalCmoAsm (Start); | |
break; | |
case CacheOpFlush: | |
RiscVCpuCacheFlushCmoAsm (Start); | |
break; | |
case CacheOpClean: | |
RiscVCpuCacheCleanCmoAsm (Start); | |
break; | |
default: | |
break; | |
} | |
Start = Start + CacheLineSize; | |
} while (Start != End); | |
} | |
/** | |
Invalidates the entire instruction cache in cache coherency domain of the | |
calling CPU. Risc-V does not have currently an CBO implementation which can | |
invalidate the entire I-cache. Hence using Fence instruction for now. P.S. | |
Fence instruction may or may not implement full I-cache invd functionality | |
on all implementations. | |
**/ | |
VOID | |
EFIAPI | |
InvalidateInstructionCache ( | |
VOID | |
) | |
{ | |
RiscVInvalidateInstCacheFenceAsm (); | |
} | |
/** | |
Invalidates a range of instruction cache lines in the cache coherency domain | |
of the calling CPU. | |
An operation from a CMO instruction is defined to operate only on the copies | |
of a cache block that are cached in the caches accessible by the explicit | |
memory accesses performed by the set of coherent agents.In other words CMO | |
operations are not applicable to instruction cache. Use fence.i instruction | |
instead to achieve the same purpose. | |
@param Address The base address of the instruction cache lines to | |
invalidate. If the CPU is in a physical addressing mode, then | |
Address is a physical address. If the CPU is in a virtual | |
addressing mode, then Address is a virtual address. | |
@param Length The number of bytes to invalidate from the instruction cache. | |
@return Address. | |
**/ | |
VOID * | |
EFIAPI | |
InvalidateInstructionCacheRange ( | |
IN VOID *Address, | |
IN UINTN Length | |
) | |
{ | |
DEBUG ( | |
(DEBUG_VERBOSE, | |
"InvalidateInstructionCacheRange: RISC-V unsupported function.\n" | |
"Invalidating the whole instruction cache instead.\n" | |
) | |
); | |
InvalidateInstructionCache (); | |
return Address; | |
} | |
/** | |
Writes back and invalidates the entire data cache in cache coherency domain | |
of the calling CPU. | |
Writes back and invalidates the entire data cache in cache coherency domain | |
of the calling CPU. This function guarantees that all dirty cache lines are | |
written back to system memory, and also invalidates all the data cache lines | |
in the cache coherency domain of the calling CPU. | |
**/ | |
VOID | |
EFIAPI | |
WriteBackInvalidateDataCache ( | |
VOID | |
) | |
{ | |
ASSERT (FALSE); | |
DEBUG (( | |
DEBUG_ERROR, | |
"WriteBackInvalidateDataCache: RISC-V unsupported function.\n" | |
)); | |
} | |
/** | |
Writes back and invalidates a range of data cache lines in the cache | |
coherency domain of the calling CPU. | |
Writes back and invalidates the data cache lines specified by Address and | |
Length. If Address is not aligned on a cache line boundary, then entire data | |
cache line containing Address is written back and invalidated. If Address + | |
Length is not aligned on a cache line boundary, then the entire data cache | |
line containing Address + Length -1 is written back and invalidated. This | |
function may choose to write back and invalidate the entire data cache if | |
that is more efficient than writing back and invalidating the specified | |
range. If Length is 0, then no data cache lines are written back and | |
invalidated. Address is returned. | |
If Length is greater than (MAX_ADDRESS - Address + 1), then ASSERT(). | |
@param Address The base address of the data cache lines to write back and | |
invalidate. If the CPU is in a physical addressing mode, then | |
Address is a physical address. If the CPU is in a virtual | |
addressing mode, then Address is a virtual address. | |
@param Length The number of bytes to write back and invalidate from the | |
data cache. | |
@return Address of cache invalidation. | |
**/ | |
VOID * | |
EFIAPI | |
WriteBackInvalidateDataCacheRange ( | |
IN VOID *Address, | |
IN UINTN Length | |
) | |
{ | |
if (RiscVIsCMOEnabled ()) { | |
CacheOpCacheRange (Address, Length, CacheOpFlush); | |
} else { | |
ASSERT (FALSE); | |
} | |
return Address; | |
} | |
/** | |
Writes back the entire data cache in cache coherency domain of the calling | |
CPU. | |
Writes back the entire data cache in cache coherency domain of the calling | |
CPU. This function guarantees that all dirty cache lines are written back to | |
system memory. This function may also invalidate all the data cache lines in | |
the cache coherency domain of the calling CPU. | |
**/ | |
VOID | |
EFIAPI | |
WriteBackDataCache ( | |
VOID | |
) | |
{ | |
ASSERT (FALSE); | |
} | |
/** | |
Writes back a range of data cache lines in the cache coherency domain of the | |
calling CPU. | |
Writes back the data cache lines specified by Address and Length. If Address | |
is not aligned on a cache line boundary, then entire data cache line | |
containing Address is written back. If Address + Length is not aligned on a | |
cache line boundary, then the entire data cache line containing Address + | |
Length -1 is written back. This function may choose to write back the entire | |
data cache if that is more efficient than writing back the specified range. | |
If Length is 0, then no data cache lines are written back. This function may | |
also invalidate all the data cache lines in the specified range of the cache | |
coherency domain of the calling CPU. Address is returned. | |
If Length is greater than (MAX_ADDRESS - Address + 1), then ASSERT(). | |
@param Address The base address of the data cache lines to write back. | |
@param Length The number of bytes to write back from the data cache. | |
@return Address of cache written in main memory. | |
**/ | |
VOID * | |
EFIAPI | |
WriteBackDataCacheRange ( | |
IN VOID *Address, | |
IN UINTN Length | |
) | |
{ | |
if (RiscVIsCMOEnabled ()) { | |
CacheOpCacheRange (Address, Length, CacheOpClean); | |
} else { | |
ASSERT (FALSE); | |
} | |
return Address; | |
} | |
/** | |
Invalidates the entire data cache in cache coherency domain of the calling | |
CPU. | |
Invalidates the entire data cache in cache coherency domain of the calling | |
CPU. This function must be used with care because dirty cache lines are not | |
written back to system memory. It is typically used for cache diagnostics. If | |
the CPU does not support invalidation of the entire data cache, then a write | |
back and invalidate operation should be performed on the entire data cache. | |
**/ | |
VOID | |
EFIAPI | |
InvalidateDataCache ( | |
VOID | |
) | |
{ | |
RiscVInvalidateDataCacheFenceAsm (); | |
} | |
/** | |
Invalidates a range of data cache lines in the cache coherency domain of the | |
calling CPU. | |
Invalidates the data cache lines specified by Address and Length. If Address | |
is not aligned on a cache line boundary, then entire data cache line | |
containing Address is invalidated. If Address + Length is not aligned on a | |
cache line boundary, then the entire data cache line containing Address + | |
Length -1 is invalidated. This function must never invalidate any cache lines | |
outside the specified range. If Length is 0, then no data cache lines are | |
invalidated. Address is returned. This function must be used with care | |
because dirty cache lines are not written back to system memory. It is | |
typically used for cache diagnostics. If the CPU does not support | |
invalidation of a data cache range, then a write back and invalidate | |
operation should be performed on the data cache range. | |
If Length is greater than (MAX_ADDRESS - Address + 1), then ASSERT(). | |
@param Address The base address of the data cache lines to invalidate. | |
@param Length The number of bytes to invalidate from the data cache. | |
@return Address. | |
**/ | |
VOID * | |
EFIAPI | |
InvalidateDataCacheRange ( | |
IN VOID *Address, | |
IN UINTN Length | |
) | |
{ | |
if (RiscVIsCMOEnabled ()) { | |
CacheOpCacheRange (Address, Length, CacheOpInvld); | |
} else { | |
DEBUG ( | |
(DEBUG_VERBOSE, | |
"InvalidateDataCacheRange: Zicbom not supported.\n" | |
"Invalidating the whole Data cache instead.\n") | |
); | |
InvalidateDataCache (); | |
} | |
return Address; | |
} |