mcu_hi3321_watch/kernel/freertos/FreeRTOS/Arch/RISC-V/pmp.c
2025-05-26 20:15:20 +08:00

635 lines
20 KiB
C

/*
* Copyright (c) CompanyNameMagicTag 2021-2022. All rights reserved.
*/
#include "FreeRTOS.h"
#include "print.h"
#include "hwi.h"
#include "encoding.h"
#include "print.h"
#include "pmp.h"
#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif /* __cplusplus */
#endif /* __cplusplus */
static inline uint32_t Ctz32(uint32_t val)
{
if (val == 0)
{
return 32; /* The number of preamble 0 of zero is 32. */
}
return __builtin_ctz(val);
}
/**
* Related instructions of registers are specified in register_config.h and they are not explained here.
*/
#define PMP_ADDR_ALIGN_SIZE 4
#define PMP_TOR_MIN_SIZE 4
#define PMP_NAPOT_MIN_SIZE 8
#define GET_PMPREG_NUM(pmpIdx) ((uint8_t)((uint8_t)(pmpIdx) & 0x3) << 3)
#define CLEAR_PMPCFG_BIT(pmpIdx, cfgValue) ((uint32_t)(cfgValue) & \
~((0xff << GET_PMPREG_NUM(pmpIdx)) & UINT32_CUT_MASK))
#define GET_PMPCFG_VALID_VALUE(pmpIdx, cfgOldValue, cfgValue) (((uint32_t)(cfgValue) << GET_PMPREG_NUM(pmpIdx)) | \
CLEAR_PMPCFG_BIT(pmpIdx, cfgOldValue))
#define GET_PMPCFG_NUM(pmpIdx) ((uint8_t)(pmpIdx) >> 2)
#define GET_SECTL_PMP_NUM(secIndx) ((uint8_t)(secIndx) << 1)
#define CLEAR_SECTL_BIT(secIdx) (~((0x3U << GET_SECTL_PMP_NUM(secIdx)) & UINT32_CUT_MASK))
#define GET_MEMATTRREG_NUM(memAttrIndx) ((uint8_t)(memAttrIndx) >> 3)
#define GET_MEMATTR_PMP_NUM(memAttrIndx) (((uint8_t)(memAttrIndx) & 0x7) << 2)
#define CLEAR_MEMATTR_BIT(memAttrIndx) (~((0xfU << GET_MEMATTR_PMP_NUM(memAttrIndx)) & UINT32_CUT_MASK))
#define GET_PMP_NAPOT_SIZE(value) (((uint32_t)(value) >> 1) - 1)
#define SET_PMP_PROT_ADDR(value) ((uint32_t)(value) >> 2)
#define GET_PMP_PROT_ADDR(value) (((uint32_t)(value) << 2) & UINT32_CUT_MASK)
#define GET_NAPOT_PMP_REG_PROT_SIZE(value) (1U << (Ctz32((value) + 1) + 3))
#define IS_ALIGN(addr, boundary) ((uint32_t)(addr) & ((uint32_t)(boundary) - 1))
uint64_t g_pmpMaxAddr = PMP_MAX_ADDRESS;
uint64_t g_pmpMinAddr = PMP_MIN_ADDRESS;
static BaseType_t g_pmpTopFlag = pdFALSE;
/*****************************************************************************
Function : PmpCfgRead
Description: Read pmp config register.
Input : number -- pmp config serial number.
Return : Pmp config register value.
*****************************************************************************/
static uint8_t PmpCfgRead(uint8_t number)
{
uint32_t pmpCfgRegVal = 0;
switch (GET_PMPCFG_NUM(number))
{
case 0: /* PMP memory R/W/X property sets register number register 0 */
pmpCfgRegVal = read_csr(pmpcfg0);
break;
case 1: /* PMP memory R/W/X property sets register number register 1 */
pmpCfgRegVal = read_csr(pmpcfg1);
break;
case 2: /* PMP memory R/W/X property sets register number register 2 */
pmpCfgRegVal = read_csr(pmpcfg2);
break;
case 3: /* PMP memory R/W/X property sets register number register 3 */
pmpCfgRegVal = read_csr(pmpcfg3);
break;
default:
break;
}
return (uint8_t)(pmpCfgRegVal >> GET_PMPREG_NUM(number)) & UINT8_CUT_MASK;
}
static uint32_t PmpIsLocked(uint8_t number)
{
uint8_t pmpCfg;
pmpCfg = PmpCfgRead(number);
if (pmpCfg & PMP_RGN_LOCK)
{
return pdTRUE;
}
return pdFALSE;
}
static uint32_t PmpAddrRead(uint8_t pmpAddrIdx)
{
switch (pmpAddrIdx)
{
case 0: /* Pmpaddr registers numbered 0 */
return read_csr(pmpaddr0);
case 1: /* Pmpaddr registers numbered 1 */
return read_csr(pmpaddr1);
case 2: /* Pmpaddr registers numbered 2 */
return read_csr(pmpaddr2);
case 3: /* Pmpaddr registers numbered 3 */
return read_csr(pmpaddr3);
case 4: /* Pmpaddr registers numbered 4 */
return read_csr(pmpaddr4);
case 5: /* Pmpaddr registers numbered 5 */
return read_csr(pmpaddr5);
case 6: /* Pmpaddr registers numbered 6 */
return read_csr(pmpaddr6);
case 7: /* Pmpaddr registers numbered 7 */
return read_csr(pmpaddr7);
case 8: /* Pmpaddr registers numbered 8 */
return read_csr(pmpaddr8);
case 9: /* Pmpaddr registers numbered 9 */
return read_csr(pmpaddr9);
case 10: /* Pmpaddr registers numbered 10 */
return read_csr(pmpaddr10);
case 11: /* Pmpaddr registers numbered 11 */
return read_csr(pmpaddr11);
case 12: /* Pmpaddr registers numbered 12 */
return read_csr(pmpaddr12);
case 13: /* Pmpaddr registers numbered 13 */
return read_csr(pmpaddr13);
case 14: /* Pmpaddr registers numbered 14 */
return read_csr(pmpaddr14);
case 15: /* Pmpaddr registers numbered 15 */
return read_csr(pmpaddr15);
default:
return 0;
}
}
static void PmpAddrWrite(uint8_t pmpAddrIdx, uint32_t pmpAddrVal)
{
switch (pmpAddrIdx)
{
case 0: /* Pmpaddr registers numbered 0 */
write_csr(pmpaddr0, pmpAddrVal);
break;
case 1: /* Pmpaddr registers numbered 1 */
write_csr(pmpaddr1, pmpAddrVal);
break;
case 2: /* Pmpaddr registers numbered 2 */
write_csr(pmpaddr2, pmpAddrVal);
break;
case 3: /* Pmpaddr registers numbered 3 */
write_csr(pmpaddr3, pmpAddrVal);
break;
case 4: /* Pmpaddr registers numbered 4 */
write_csr(pmpaddr4, pmpAddrVal);
break;
case 5: /* Pmpaddr registers numbered 5 */
write_csr(pmpaddr5, pmpAddrVal);
break;
case 6: /* Pmpaddr registers numbered 6 */
write_csr(pmpaddr6, pmpAddrVal);
break;
case 7: /* Pmpaddr registers numbered 7 */
write_csr(pmpaddr7, pmpAddrVal);
break;
case 8: /* Pmpaddr registers numbered 8 */
write_csr(pmpaddr8, pmpAddrVal);
break;
case 9: /* Pmpaddr registers numbered 9 */
write_csr(pmpaddr9, pmpAddrVal);
break;
case 10: /* Pmpaddr registers numbered 10 */
write_csr(pmpaddr10, pmpAddrVal);
break;
case 11: /* Pmpaddr registers numbered 11 */
write_csr(pmpaddr11, pmpAddrVal);
break;
case 12: /* Pmpaddr registers numbered 12 */
write_csr(pmpaddr12, pmpAddrVal);
break;
case 13: /* Pmpaddr registers numbered 13 */
write_csr(pmpaddr13, pmpAddrVal);
break;
case 14: /* Pmpaddr registers numbered 14 */
write_csr(pmpaddr14, pmpAddrVal);
break;
case 15: /* Pmpaddr registers numbered 15 */
write_csr(pmpaddr15, pmpAddrVal);
break;
default:
break;
}
}
static uint32_t PmpTorLowerBoundRead(uint8_t pmpAddrIdx)
{
uint8_t pmpAddrMatch;
uint32_t pmpPreAddrVal;
uint32_t pmpPreSize;
uint32_t pmpPreBaseAddr;
uint32_t napotSize;
if (pmpAddrIdx == 0)
{
return 0;
}
pmpAddrMatch = PmpCfgRead(pmpAddrIdx - 1) & PMP_RGN_ADDR_MATCH_MASK;
switch (pmpAddrMatch)
{
case PMP_RGN_ADDR_MATCH_OFF:
/* FALLTHROUGH */
case PMP_RGN_ADDR_MATCH_TOR:
pmpPreAddrVal = PmpAddrRead(pmpAddrIdx - 1);
return GET_PMP_PROT_ADDR(pmpPreAddrVal);
case PMP_RGN_ADDR_MATCH_NAPOT:
{
pmpPreAddrVal = PmpAddrRead(pmpAddrIdx - 1);
PRINTK(ePrintkErr, "pre region addr value = 0x%x\n", pmpPreAddrVal);
pmpPreSize = GET_NAPOT_PMP_REG_PROT_SIZE(pmpPreAddrVal);
PRINTK(ePrintkErr, "pre region size = 0x%x\n", pmpPreSize);
napotSize = GET_PMP_NAPOT_SIZE(pmpPreSize); /* get the pre-region size */
pmpPreBaseAddr = GET_PMP_PROT_ADDR(pmpPreAddrVal) & (~napotSize); /* get the pre-region base address */
PRINTK(ePrintkErr, "pre region base address = 0x%x\n", pmpPreBaseAddr);
/* get the pre-region end address, i.e. the lower bound of this TOR region */
return (pmpPreBaseAddr + pmpPreSize);
}
default:
return 0;
}
}
static uint32_t PmpCheckNextPmpEntry(const PMP_ENTRY_INFO *pmpInfo)
{
uint8_t pmpcfgMode = PmpCfgRead(pmpInfo->ucNumber) & PMP_RGN_ADDR_MATCH_MASK;
if ((pmpcfgMode == PMP_RGN_ADDR_MATCH_OFF) || (pmpcfgMode == PMP_RGN_ADDR_MATCH_TOR))
{
if (pmpInfo->ucAddressMatch == PMP_RGN_ADDR_MATCH_NAPOT)
{
return errPMP_TOR_INVALID_MATCH_MODE;
}
}
else if (pmpcfgMode == PMP_RGN_ADDR_MATCH_NAPOT)
{
if (pmpInfo->ucAddressMatch != PMP_RGN_ADDR_MATCH_NAPOT)
{
return errPMP_NAPOT_INVALID_MATCH_MODE;
}
}
return pdPASS;
}
static uint32_t PmpBaseAddrAndRegionSizeCheck(uint8_t number,
uint8_t addrMatch,
const char baseAddr,
uint32_t regionSize )
{
uint32_t prevAddr;
/* Base address is not in the range of [PMP_MIN_ADDRESS, PMP_MAX_ADDRESS] */
if ((baseAddr < g_pmpMinAddr) || (baseAddr > g_pmpMaxAddr))
{
return errPMP_BASE_ADDRESS_NOT_IN_RANGE;
}
/* the PMP region start address and end address must be 4 aligned. */
if (IS_ALIGN(baseAddr, PMP_ADDR_ALIGN_SIZE) != 0)
{
return errPMP_INVALID_BASE_ADDRESS;
}
if (addrMatch == PMP_RGN_ADDR_MATCH_NAPOT)
{
if ((regionSize < PMP_NAPOT_MIN_SIZE) && ((baseAddr + regionSize) > g_pmpMaxAddr))
{
return errPMP_NAPOT_ENCODING_NON_MATCHED;
}
/* check the naturally aligned power-of-2 regions (NAPOT) size */
if (((regionSize - 1) & regionSize) != 0)
{
return errPMP_INVALID_CAPACITY;
}
/* the NAPOT type and size should be matched in NAPOT range encoding. */
if (((regionSize - 1) & baseAddr) != 0)
{
return errPMP_NAPOT_ENCODING_NON_MATCHED;
}
}
/* check TOR base address equals to the pre-region end address */
if (addrMatch == PMP_RGN_ADDR_MATCH_TOR)
{
prevAddr = PmpTorLowerBoundRead(number);
if (baseAddr < (prevAddr + PMP_TOR_MIN_SIZE))
{
return errPMP_TOR_LOWER_BOUND_NONMATCHED;
}
}
return pdPASS;
}
/*****************************************************************************
Function : PmpCheckParame
Description: check parame
Input : pmpInfo -- PMP info
Return : Error Information or pdPASS
*****************************************************************************/
static uint32_t PmpAttrAndSectlCheck(PMP_ENTRY_INFO *pmpInfo)
{
if ((pmpInfo->memAttr != MEM_ATTR_DEV_NON_BUF) && (pmpInfo->memAttr != MEM_ATTR_NORM_NON_CA_NON_BUF) &&
(pmpInfo->memAttr != MEM_ATTR_NORM_NON_CA_BUF) && (pmpInfo->memAttr != MEM_ATTR_WB_RD_ALLOC) &&
(pmpInfo->memAttr != MEM_ATTR_WB_WR_ALLOC) && (pmpInfo->memAttr != MEM_ATTR_WB_RW_ALLOC))
{
return errPMP_ATTR_INVALID;
}
if ((pmpInfo->seCtl != SEC_CONTRLO_SECURE_NOMMU) && (pmpInfo->seCtl != SEC_CONTRLO_NOSECURE_NOMMU) &&
(pmpInfo->seCtl != SEC_CONTRLO_SECURE_MMU) && (pmpInfo->seCtl != SEC_CONTRLO_NOSECURE_MMU))
{
return errPMP_SECTL_INVALID;
}
return pdPASS;
}
static uint32_t PmpCheckParame(PMP_ENTRY_INFO *pmpInfo)
{
uint32_t ret;
uint8_t pmpcfgNext;
if (pmpInfo == NULL)
{
return errPMP_PTR_NULL;
}
if (pmpInfo->ucAddressMatch == PMP_RGN_ADDR_MATCH_NA4)
{
return errPMP_INVALID_MATCH;
}
/* Number exceeds the maximum value supported by PMO */
if (pmpInfo->ucNumber >= PMP_MAX_SUPPORT)
{
return errPMP_INVALID_NUMBER;
}
/* Check the entry whether locked or not */
if (PmpIsLocked(pmpInfo->ucNumber) == pdTRUE)
{
return errPMP_ENTRY_IS_LOCKED;
}
g_pmpTopFlag = pdFALSE;
/* If address matching mode equals to TOR, check the next PMP entry */
if ((pmpInfo->ucNumber + 1) < PMP_MAX_SUPPORT)
{
pmpcfgNext = PmpCfgRead(pmpInfo->ucNumber + 1);
if ((pmpcfgNext & PMP_RGN_LOCK) && ((pmpcfgNext & PMP_RGN_ADDR_MATCH_MASK) == PMP_RGN_ADDR_MATCH_TOR))
{
ret = PmpCheckNextPmpEntry(pmpInfo);
if (ret != pdPASS)
{
return ret;
}
g_pmpTopFlag = pdTRUE;
}
}
if (g_pmpTopFlag != pdTRUE)
{
ret = PmpBaseAddrAndRegionSizeCheck(pmpInfo->ucNumber, pmpInfo->ucAddressMatch,
pmpInfo->uwBaseAddress, pmpInfo->uwRegionSize);
if (ret != pdPASS)
{
return ret;
}
}
if (pmpInfo->ucAddressMatch != PMP_RGN_ADDR_MATCH_OFF)
{
return PmpAttrAndSectlCheck(pmpInfo);
}
else
{
pmpInfo->memAttr = MEM_ATTR_DEV_NON_BUF;
pmpInfo->seCtl = SEC_CONTRLO_SECURE_NOMMU;
}
return pdPASS;
}
static uint8_t PmpAccpGet(PmpAccInfo accPermission)
{
uint8_t pmpCfgAcc = 0;
if (accPermission.readAcc == E_MEM_RD_ACC_RD)
{
pmpCfgAcc |= (1 << PMPCFG_R_BIT);
}
if (accPermission.writeAcc == E_MEM_WR_ACC_WR)
{
pmpCfgAcc |= (1 << PMPCFG_W_BIT);
}
if (accPermission.excuteAcc == E_MEM_EX_ACC_EX)
{
pmpCfgAcc |= (1 << PMPCFG_X_BIT);
}
return pmpCfgAcc;
}
static void PmpCfgWrite(uint8_t pmpCfgIdx, uint8_t pmpCfgVal)
{
uint32_t cfgVal;
switch (GET_PMPCFG_NUM(pmpCfgIdx))
{
case 0: /* PMP memory R/W/X property sets register number register 0 */
cfgVal = read_csr(pmpcfg0);
cfgVal = GET_PMPCFG_VALID_VALUE(pmpCfgIdx, cfgVal, pmpCfgVal);
write_csr(pmpcfg0, cfgVal);
break;
case 1: /* PMP memory R/W/X property sets register number register 1 */
cfgVal = read_csr(pmpcfg1);
cfgVal = GET_PMPCFG_VALID_VALUE(pmpCfgIdx, cfgVal, pmpCfgVal);
write_csr(pmpcfg1, cfgVal);
break;
case 2: /* PMP memory R/W/X property sets register number register 2 */
cfgVal = read_csr(pmpcfg2);
cfgVal = GET_PMPCFG_VALID_VALUE(pmpCfgIdx, cfgVal, pmpCfgVal);
write_csr(pmpcfg2, cfgVal);
break;
case 3: /* PMP memory R/W/X property sets register number register 3 */
cfgVal = read_csr(pmpcfg3);
cfgVal = GET_PMPCFG_VALID_VALUE(pmpCfgIdx, cfgVal, pmpCfgVal);
write_csr(pmpcfg3, cfgVal);
break;
default:
break;
}
}
/*****************************************************************************
Function : PmpNapotAddrGet
Description : get the pmp address (NAPOT) of the physical memory protection entry
Input : base -- base addr
Input : size -- protection size
Output : pmp address (NAPOT)
Return : OS_SUCCESS
*****************************************************************************/
static uint32_t PmpNapotAddrGet(uint32_t base, uint32_t size)
{
return (uint32_t)SET_PMP_PROT_ADDR(base + GET_PMP_NAPOT_SIZE(size));
}
static void PmpMemattrWrite(uint8_t pmpMemAttrIdx, PmpMemAttrInfo pmpMemAttrVal)
{
uint32_t memAttrVal;
if (GET_MEMATTRREG_NUM(pmpMemAttrIdx) == 0)
{
memAttrVal = read_custom_csr(MEMATTRL);
memAttrVal &= CLEAR_MEMATTR_BIT(pmpMemAttrIdx);
memAttrVal |= ((uint32_t)pmpMemAttrVal << GET_MEMATTR_PMP_NUM(pmpMemAttrIdx));
write_custom_csr(MEMATTRL, memAttrVal);
}
else
{
memAttrVal = read_custom_csr(MEMATTRH);
memAttrVal &= CLEAR_MEMATTR_BIT(pmpMemAttrIdx);
memAttrVal |= ((uint32_t)pmpMemAttrVal << GET_MEMATTR_PMP_NUM(pmpMemAttrIdx));
write_custom_csr(MEMATTRH, memAttrVal);
}
}
static void PmpSectlWrite(uint8_t pmpSecIdx, uint8_t pmpCfgVal)
{
uint32_t sectl;
sectl = read_custom_csr(SECTL);
sectl &= CLEAR_SECTL_BIT(pmpSecIdx);
sectl |= (pmpCfgVal << GET_SECTL_PMP_NUM(pmpSecIdx));
write_custom_csr(SECTL, sectl);
}
/*****************************************************************************
Function : ulArchRegionDisable
Description: disable region by number
Input : number -- mpu region number want to disable
Return : pdPASS or Error Information
*****************************************************************************/
uint32_t ulArchRegionDisable(uint8_t number)
{
uint32_t intSave;
if (number >= PMP_MAX_SUPPORT)
{
return errPMP_INVALID_NUMBER;
}
if (PmpIsLocked(number) == pdTRUE)
{
return errPMP_ENTRY_IS_LOCKED;
}
intSave = uxHwiLock();
PmpSectlWrite(number, SEC_CONTRLO_SECURE_NOMMU);
PmpCfgWrite(number, PMP_RGN_ADDR_MATCH_OFF);
vHwiRestore(intSave);
return pdPASS;
}
static void PmpRegionConfig(const PMP_ENTRY_INFO *pmpInfo)
{
uint8_t pmpXCfg = 0;
uint32_t pmpXAddr = UINT32_CUT_MASK;
switch ((pmpInfo->ucAddressMatch & PMP_RGN_ADDR_MATCH_MASK))
{
case PMP_RGN_ADDR_MATCH_OFF:
pmpXAddr = SET_PMP_PROT_ADDR(pmpInfo->uwBaseAddress);
break;
case PMP_RGN_ADDR_MATCH_NAPOT:
pmpXAddr = PmpNapotAddrGet(pmpInfo->uwBaseAddress, pmpInfo->uwRegionSize);
break;
case PMP_RGN_ADDR_MATCH_TOR:
pmpXAddr = SET_PMP_PROT_ADDR(pmpInfo->uwBaseAddress);
break;
case PMP_RGN_ADDR_MATCH_NA4:
/* The PMP entry size granularity must be the multiple of cache line size, not support yet! */
return;
default:
break;
}
/* write pmp address register */
if (g_pmpTopFlag != pdTRUE)
{
PmpAddrWrite(pmpInfo->ucNumber, pmpXAddr);
}
/* write pmp memattr/memattrh register */
PmpMemattrWrite(pmpInfo->ucNumber, pmpInfo->memAttr);
/* write pmp sectl register */
PmpSectlWrite(pmpInfo->ucNumber, pmpInfo->seCtl);
/* write pmp cfg register */
if (pmpInfo->ucAddressMatch != PMP_RGN_ADDR_MATCH_OFF)
{
pmpXCfg |= (pmpInfo->ucAddressMatch) | (PmpAccpGet(pmpInfo->accPermission));
}
if (pmpInfo->bLocked)
{
pmpXCfg |= PMP_RGN_LOCK;
}
PmpCfgWrite(pmpInfo->ucNumber, pmpXCfg);
}
/*****************************************************************************
Function : ulArchGetSectl
Description: Get Sectl region
Input : None
Return : Sectl register value
*****************************************************************************/
uint32_t ulArchGetSectl(void)
{
return read_custom_csr(SECTL);
}
/*****************************************************************************
Function : ulArchProtectionRegionSet
Description: set protection region
Input : pmpInfo --- PMP parameters to be set.
The base address must be in the range of RAM
Return : pdPASS or the index of pmpInfo which set up failed
*****************************************************************************/
uint32_t ulArchProtectionRegionSet(PMP_ENTRY_INFO *pmpInfo)
{
uint32_t ret;
uint32_t intSave;
PMP_ENTRY_INFO *pmpConfig = NULL;
intSave = uxHwiLock();
pmpConfig = pmpInfo;
ret = PmpCheckParame(pmpConfig);
if (ret != pdPASS)
{
PRINTK(ePrintkErr, "set protection region error, ret: [0x%x]\n", ret);
vHwiRestore(intSave);
return ret;
}
PmpRegionConfig(pmpConfig);
vHwiRestore(intSave);
return pdPASS;
}
uint32_t ulArchProtectionUnitInit(uint64_t minAddr, uint64_t maxAddr)
{
uint32_t intSave;
intSave = uxHwiLock();
g_pmpMinAddr = minAddr;
g_pmpMaxAddr = maxAddr;
vHwiRestore(intSave);
return pdPASS;
}
void vArchPmpAddrWrite(uint8_t pmpAddrIdx, uint32_t pmpAddrVal)
{
PmpAddrWrite(pmpAddrIdx, SET_PMP_PROT_ADDR(pmpAddrVal));
}
#ifdef __cplusplus
#if __cplusplus
}
#endif /* __cplusplus */
#endif /* __cplusplus */