Extend the RISC-V PMP functionality
- Add test for mode support, which is architecture dependent - Add tests for address alignment and region size - Add option to query for access rights - The function goes through every PMP entry and tests if an address range from [base, base+size] has been configured for desired access rights. - If several PMP entries match the range and access rights, the information is combined - End result is either no access, a partial match was found, or a full match was found. Details about the partial match are not provided. The intent for testing access rights and not just blindly applying them is a case where they are already set in e.g. a bootloader. In this case, nothing should be done, unless the configuration does not match, in which case the software must not continue further.
This commit is contained in:
parent
8a4881c4e5
commit
81188d9c94
@ -101,6 +101,7 @@ config ARCH_CHIP_MPFS
|
||||
select ARCH_HAVE_RESET
|
||||
select ARCH_HAVE_SPI_CS_CONTROL
|
||||
select ARCH_HAVE_PWM_MULTICHAN
|
||||
select PMP_HAS_LIMITED_FEATURES
|
||||
---help---
|
||||
MicroChip Polarfire processor (RISC-V 64bit core with GCVX extensions).
|
||||
|
||||
@ -188,6 +189,29 @@ config ARCH_MMU_TYPE_SV39
|
||||
bool
|
||||
default n
|
||||
|
||||
# MPU has certain architecture dependent configurations, which are presented
|
||||
# here. Default is that the full RISC-V PMP specification is supported.
|
||||
|
||||
config PMP_HAS_LIMITED_FEATURES
|
||||
bool
|
||||
default n
|
||||
|
||||
config ARCH_MPU_MIN_BLOCK_SIZE
|
||||
int "Minimum MPU (PMP) block size"
|
||||
default 4 if !PMP_HAS_LIMITED_FEATURES
|
||||
|
||||
config ARCH_MPU_HAS_TOR
|
||||
bool "PMP supports TOR"
|
||||
default y if !PMP_HAS_LIMITED_FEATURES
|
||||
|
||||
config ARCH_MPU_HAS_NO4
|
||||
bool "PMP supports NO4"
|
||||
default y if !PMP_HAS_LIMITED_FEATURES
|
||||
|
||||
config ARCH_MPU_HAS_NAPOT
|
||||
bool "PMP supports NAPOT"
|
||||
default y if !PMP_HAS_LIMITED_FEATURES
|
||||
|
||||
source "arch/risc-v/src/opensbi/Kconfig"
|
||||
source "arch/risc-v/src/common/Kconfig"
|
||||
|
||||
|
@ -346,6 +346,7 @@
|
||||
#define PMPCFG_R (1 << 0) /* readable ? */
|
||||
#define PMPCFG_W (1 << 1) /* writeable ? */
|
||||
#define PMPCFG_X (1 << 2) /* excutable ? */
|
||||
#define PMPCFG_RWX_MASK (7 << 0) /* access rights mask */
|
||||
#define PMPCFG_A_OFF (0 << 3) /* null region (disabled) */
|
||||
#define PMPCFG_A_TOR (1 << 3) /* top of range */
|
||||
#define PMPCFG_A_NA4 (2 << 3) /* naturally aligned four-byte region */
|
||||
|
@ -91,6 +91,12 @@
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Return values from riscv_check_pmp_access */
|
||||
|
||||
#define PMP_ACCESS_OFF (0) /* Access for area not set */
|
||||
#define PMP_ACCESS_DENIED (-1) /* Access set and denied */
|
||||
#define PMP_ACCESS_FULL (1) /* Access set and allowed */
|
||||
|
||||
/****************************************************************************
|
||||
* Public Types
|
||||
****************************************************************************/
|
||||
@ -177,8 +183,12 @@ void riscv_restorefpu(const uintptr_t *regs);
|
||||
|
||||
/* RISC-V PMP Config ********************************************************/
|
||||
|
||||
void riscv_config_pmp_region(uintptr_t region, uintptr_t attr,
|
||||
uintptr_t base, uintptr_t size);
|
||||
int riscv_config_pmp_region(uintptr_t region, uintptr_t attr,
|
||||
uintptr_t base, uintptr_t size);
|
||||
|
||||
int riscv_check_pmp_access(uintptr_t attr, uintptr_t base, uintptr_t size);
|
||||
int riscv_configured_pmp_regions(void);
|
||||
int riscv_next_free_pmp_region(void);
|
||||
|
||||
/* Power management *********************************************************/
|
||||
|
||||
|
@ -22,6 +22,9 @@
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <nuttx/compiler.h>
|
||||
#include <nuttx/config.h>
|
||||
#include <nuttx/arch.h>
|
||||
#include <arch/csr.h>
|
||||
@ -32,6 +35,18 @@
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
/* Minimum supported block size */
|
||||
|
||||
#if !defined CONFIG_ARCH_MPU_MIN_BLOCK_SIZE
|
||||
#define MIN_BLOCK_SIZE (__riscv_xlen / 8)
|
||||
#else
|
||||
#define MIN_BLOCK_SIZE CONFIG_ARCH_MPU_MIN_BLOCK_SIZE
|
||||
#endif
|
||||
|
||||
/* Address and block size alignment mask */
|
||||
|
||||
#define BLOCK_ALIGN_MASK (MIN_BLOCK_SIZE - 1)
|
||||
|
||||
#define PMP_CFG_BITS_CNT (8)
|
||||
#define PMP_CFG_FLAG_MASK (0xFF)
|
||||
|
||||
@ -44,6 +59,361 @@
|
||||
reg |= attr << (offset * PMP_CFG_BITS_CNT); \
|
||||
} while(0);
|
||||
|
||||
#define PMP_READ_REGION_FROM_REG(region, reg) \
|
||||
({ \
|
||||
uintptr_t tmp = READ_CSR(reg); \
|
||||
tmp >>= ((region % PMP_CFG_CNT_IN_REG) * PMP_CFG_BITS_CNT); \
|
||||
tmp &= PMP_CFG_FLAG_MASK; \
|
||||
tmp; \
|
||||
})
|
||||
|
||||
#ifndef min
|
||||
#define min(a,b) ((a) < (b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
#ifndef max
|
||||
#define max(a,b) ((a) > (b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Private Types
|
||||
****************************************************************************/
|
||||
|
||||
/* Helper structure for handling a PMP entry */
|
||||
|
||||
struct pmp_entry_s
|
||||
{
|
||||
uintptr_t base; /* Base address of region */
|
||||
uintptr_t end; /* End address of region */
|
||||
uintptr_t size; /* Region size */
|
||||
uint8_t mode; /* Address matching mode */
|
||||
uint8_t rwx; /* Access rights */
|
||||
};
|
||||
|
||||
typedef struct pmp_entry_s pmp_entry_t;
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: pmp_check_addrmatch_type
|
||||
*
|
||||
* Description:
|
||||
* Test if an address matching type is supported by the architecture.
|
||||
*
|
||||
* Input Parameters:
|
||||
* type - The type to test.
|
||||
*
|
||||
* Returned Value:
|
||||
* true if it is, false otherwise.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static bool pmp_check_addrmatch_type(uintptr_t type)
|
||||
{
|
||||
/* Parameter is potentially unused */
|
||||
|
||||
UNUSED(type);
|
||||
#ifdef CONFIG_ARCH_MPU_HAS_TOR
|
||||
if (type == PMPCFG_A_TOR)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
#ifdef CONFIG_ARCH_MPU_HAS_NO4
|
||||
if (type == PMPCFG_A_NA4)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
#ifdef CONFIG_ARCH_MPU_HAS_NAPOT
|
||||
if (type == PMPCFG_A_NAPOT)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* None of the supported types match */
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: pmp_check_region_attrs
|
||||
*
|
||||
* Description:
|
||||
* Test if the base address and size of region meet alignment requirements.
|
||||
*
|
||||
* Input Parameters:
|
||||
* base - The base address of the region.
|
||||
* size - The memory length of the region.
|
||||
*
|
||||
* Returned Value:
|
||||
* true if it is, false otherwise.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static bool pmp_check_region_attrs(uintptr_t base, uintptr_t size)
|
||||
{
|
||||
/* Check that the size is not too small */
|
||||
|
||||
if (size < MIN_BLOCK_SIZE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check that the base address is aligned properly */
|
||||
|
||||
if ((base & BLOCK_ALIGN_MASK) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check that the size is aligned properly */
|
||||
|
||||
if ((size & BLOCK_ALIGN_MASK) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: pmp_read_region_cfg
|
||||
*
|
||||
* Description:
|
||||
* Read PMP configuration for region
|
||||
*
|
||||
* Input Parameters:
|
||||
* region - Region number.
|
||||
*
|
||||
* Returned Value:
|
||||
* Configuration value from pmpcfg+region
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static uintptr_t pmp_read_region_cfg(uintptr_t region)
|
||||
{
|
||||
# if (__riscv_xlen == 32)
|
||||
switch (region)
|
||||
{
|
||||
case 0 ... 3:
|
||||
return PMP_READ_REGION_FROM_REG(region, pmpcfg0);
|
||||
|
||||
case 4 ... 7:
|
||||
return PMP_READ_REGION_FROM_REG(region, pmpcfg1);
|
||||
|
||||
case 8 ... 11:
|
||||
return PMP_READ_REGION_FROM_REG(region, pmpcfg2);
|
||||
|
||||
case 12 ... 15:
|
||||
return PMP_READ_REGION_FROM_REG(region, pmpcfg3);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
# elif (__riscv_xlen == 64)
|
||||
switch (region)
|
||||
{
|
||||
case 0 ... 7:
|
||||
return PMP_READ_REGION_FROM_REG(region, pmpcfg0);
|
||||
|
||||
case 8 ... 15:
|
||||
return PMP_READ_REGION_FROM_REG(region, pmpcfg2);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Never executed */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: pmp_read_addr
|
||||
*
|
||||
* Description:
|
||||
* Read address for region
|
||||
*
|
||||
* Input Parameters:
|
||||
* region - Region number.
|
||||
*
|
||||
* Returned Value:
|
||||
* Address value from pmpcfg+region
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static uintptr_t pmp_read_addr(uintptr_t region)
|
||||
{
|
||||
switch (region)
|
||||
{
|
||||
case 0:
|
||||
return READ_CSR(pmpaddr0);
|
||||
|
||||
case 1:
|
||||
return READ_CSR(pmpaddr1);
|
||||
|
||||
case 2:
|
||||
return READ_CSR(pmpaddr2);
|
||||
|
||||
case 3:
|
||||
return READ_CSR(pmpaddr3);
|
||||
|
||||
case 4:
|
||||
return READ_CSR(pmpaddr4);
|
||||
|
||||
case 5:
|
||||
return READ_CSR(pmpaddr5);
|
||||
|
||||
case 6:
|
||||
return READ_CSR(pmpaddr6);
|
||||
|
||||
case 7:
|
||||
return READ_CSR(pmpaddr7);
|
||||
|
||||
case 8:
|
||||
return READ_CSR(pmpaddr8);
|
||||
|
||||
case 9:
|
||||
return READ_CSR(pmpaddr9);
|
||||
|
||||
case 10:
|
||||
return READ_CSR(pmpaddr10);
|
||||
|
||||
case 11:
|
||||
return READ_CSR(pmpaddr11);
|
||||
|
||||
case 12:
|
||||
return READ_CSR(pmpaddr12);
|
||||
|
||||
case 13:
|
||||
return READ_CSR(pmpaddr13);
|
||||
|
||||
case 14:
|
||||
return READ_CSR(pmpaddr14);
|
||||
|
||||
case 15:
|
||||
return READ_CSR(pmpaddr15);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Never executed */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: pmp_napot_decode
|
||||
*
|
||||
* Description:
|
||||
* Decode base and size from NAPOT value
|
||||
*
|
||||
* Input Parameters:
|
||||
* val - Value to decode.
|
||||
* base - Base out.
|
||||
* size - Size out.
|
||||
*
|
||||
* Returned Value:
|
||||
* None
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static void pmp_napot_decode(uintptr_t val, uintptr_t * base,
|
||||
uintptr_t * size)
|
||||
{
|
||||
uint64_t mask = (uint64_t)(-1) >> 1;
|
||||
uint64_t pot = __riscv_xlen + 2;
|
||||
|
||||
while (mask)
|
||||
{
|
||||
if ((val & mask) == mask)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
pot--;
|
||||
mask >>= 1;
|
||||
}
|
||||
|
||||
val &= ~mask;
|
||||
*base = (val << 2);
|
||||
*size = (1 << pot);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: pmp_read
|
||||
*
|
||||
* Description:
|
||||
* Read PMP region into PMP entry
|
||||
*
|
||||
* Input Parameters:
|
||||
* region - Region number.
|
||||
* entry - Entry out
|
||||
*
|
||||
* Returned Value:
|
||||
* None
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static void pmp_read(uintptr_t region, pmp_entry_t * entry)
|
||||
{
|
||||
uintptr_t addr = 0;
|
||||
uintptr_t size = 0;
|
||||
uintptr_t mode = 0;
|
||||
uintptr_t rwx = 0;
|
||||
uintptr_t cfg = 0;
|
||||
|
||||
addr = pmp_read_addr(region);
|
||||
cfg = pmp_read_region_cfg(region);
|
||||
mode = cfg & PMPCFG_A_MASK;
|
||||
rwx = cfg & PMPCFG_RWX_MASK;
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case PMPCFG_A_TOR:
|
||||
addr <<= 2;
|
||||
|
||||
/* TOR region, must peek into prior region for size */
|
||||
|
||||
if (region == 0)
|
||||
{
|
||||
size = addr;
|
||||
}
|
||||
else
|
||||
{
|
||||
size = addr - pmp_read_addr(region - 1);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case PMPCFG_A_NA4:
|
||||
addr <<= 2;
|
||||
size = 4;
|
||||
break;
|
||||
|
||||
case PMPCFG_A_NAPOT:
|
||||
pmp_napot_decode(addr, &addr, &size);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
entry->base = addr;
|
||||
entry->end = addr + size;
|
||||
entry->size = size;
|
||||
entry->rwx = rwx;
|
||||
entry->mode = mode;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
@ -63,17 +433,32 @@
|
||||
* and the size must be power-of-two according to the the PMP spec.
|
||||
*
|
||||
* Returned Value:
|
||||
* None.
|
||||
* 0 on succeess; negated error on failure
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void riscv_config_pmp_region(uintptr_t region, uintptr_t attr,
|
||||
uintptr_t base, uintptr_t size)
|
||||
int riscv_config_pmp_region(uintptr_t region, uintptr_t attr,
|
||||
uintptr_t base, uintptr_t size)
|
||||
{
|
||||
uintptr_t addr = 0;
|
||||
uintptr_t cfg = 0;
|
||||
uintptr_t addr = 0;
|
||||
uintptr_t cfg = 0;
|
||||
uintptr_t type = (attr & PMPCFG_A_MASK);
|
||||
|
||||
/* TODO: check the base address alignment and size */
|
||||
/* Check that the architecture supports address matching type */
|
||||
|
||||
if (pmp_check_addrmatch_type(type) == false)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Check the region attributes */
|
||||
|
||||
if (pmp_check_region_attrs(base, size))
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Calculate new base address from type */
|
||||
|
||||
addr = base >> 2;
|
||||
if (PMPCFG_A_NAPOT == (attr & PMPCFG_A_MASK))
|
||||
@ -81,6 +466,8 @@ void riscv_config_pmp_region(uintptr_t region, uintptr_t attr,
|
||||
addr |= (size - 1) >> 3;
|
||||
}
|
||||
|
||||
/* Set the address value */
|
||||
|
||||
switch (region)
|
||||
{
|
||||
case 0:
|
||||
@ -151,6 +538,8 @@ void riscv_config_pmp_region(uintptr_t region, uintptr_t attr,
|
||||
break;
|
||||
}
|
||||
|
||||
/* Set the configuration register value */
|
||||
|
||||
# if (__riscv_xlen == 32)
|
||||
switch (region)
|
||||
{
|
||||
@ -206,4 +595,165 @@ void riscv_config_pmp_region(uintptr_t region, uintptr_t attr,
|
||||
/* fence is needed when page-based virtual memory is implemented */
|
||||
|
||||
__asm volatile("sfence.vma x0, x0" : : : "memory");
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: riscv_check_pmp_access
|
||||
*
|
||||
* Description:
|
||||
* This function will set the specific PMP region with the desired cfg.
|
||||
*
|
||||
* Input Parameters:
|
||||
* attr - The region configurations.
|
||||
* base - The base address of the region.
|
||||
* size - The memory length of the region.
|
||||
* For the NAPOT mode, the base address must aligned to the size boundary,
|
||||
* and the size must be power-of-two according to the the PMP spec.
|
||||
*
|
||||
* Returned Value:
|
||||
* 0 if access rights are not set at all
|
||||
* < 0 if access rights are set and match match partially
|
||||
* > 0 if access rights are set and match fully
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int riscv_check_pmp_access(uintptr_t attr, uintptr_t base, uintptr_t size)
|
||||
{
|
||||
pmp_entry_t entry;
|
||||
uintptr_t end;
|
||||
uintptr_t orgsize;
|
||||
unsigned int region;
|
||||
|
||||
/* Go through every single configured region and test the attributes */
|
||||
|
||||
attr = (attr & PMPCFG_RWX_MASK);
|
||||
end = base + size;
|
||||
orgsize = size;
|
||||
|
||||
for (region = 0; region < 16 && size > 0; region++)
|
||||
{
|
||||
/* Find matching configuration first */
|
||||
|
||||
pmp_read(region, &entry);
|
||||
|
||||
/* Check if any configuration at all */
|
||||
|
||||
if (entry.mode == PMPCFG_A_OFF)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Does this address range match ? Take partial matches into account.
|
||||
*
|
||||
* There are four possibilities:
|
||||
* 1: Full match; region inside mapped area
|
||||
* 2: Partial match; mapped area inside region
|
||||
* 3: Partial match; base inside mapped region, end outside
|
||||
* 4: Partial match; base outside mapped region, end inside
|
||||
*/
|
||||
|
||||
if ((base >= entry.base && end <= entry.end) ||
|
||||
(base <= entry.base && end >= entry.end) ||
|
||||
(base >= entry.base && base <= entry.end) ||
|
||||
(end >= entry.base && end <= entry.end))
|
||||
{
|
||||
/* Found a matching splice, check rights */
|
||||
|
||||
if ((entry.rwx & attr) == attr)
|
||||
{
|
||||
/* Found matching region that allows access */
|
||||
|
||||
size -= min(end, entry.end) - max(base, entry.base);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Found matching region that does not allow access */
|
||||
|
||||
return PMP_ACCESS_DENIED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if nothing configured at all ? */
|
||||
|
||||
if (size == orgsize)
|
||||
{
|
||||
return PMP_ACCESS_OFF;
|
||||
}
|
||||
|
||||
/* If size is non-positive, the requested range is accessible */
|
||||
|
||||
if (size <= 0)
|
||||
{
|
||||
return PMP_ACCESS_FULL;
|
||||
}
|
||||
|
||||
/* The requested range is either fully or partially inaccessible */
|
||||
|
||||
return PMP_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: riscv_configured_pmp_regions
|
||||
*
|
||||
* Description:
|
||||
* Count amount of configured PMP regions, note: is not atomic
|
||||
*
|
||||
* Input Parameters:
|
||||
*
|
||||
* Returned Value:
|
||||
* Amount of configured PMP regions
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int riscv_configured_pmp_regions(void)
|
||||
{
|
||||
pmp_entry_t entry;
|
||||
unsigned int region;
|
||||
int ret = 0;
|
||||
|
||||
for (region = 0; region < 16; region++)
|
||||
{
|
||||
pmp_read(region, &entry);
|
||||
|
||||
if (entry.mode != PMPCFG_A_OFF)
|
||||
{
|
||||
ret++;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: riscv_next_free_pmp_region
|
||||
*
|
||||
* Description:
|
||||
* Returns next free PMP region, note: is not atomic
|
||||
*
|
||||
* Input Parameters:
|
||||
*
|
||||
* Returned Value:
|
||||
* Next free PMP region, or -1 if none found
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int riscv_next_free_pmp_region(void)
|
||||
{
|
||||
pmp_entry_t entry;
|
||||
unsigned int region;
|
||||
|
||||
for (region = 0; region < 16; region++)
|
||||
{
|
||||
pmp_read(region, &entry);
|
||||
|
||||
if (entry.mode == PMPCFG_A_OFF)
|
||||
{
|
||||
return region;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
@ -312,3 +312,17 @@ config MPFS_DMA
|
||||
menu "MPFS Others"
|
||||
|
||||
endmenu
|
||||
|
||||
# Override the default values for MPU / PMP parameters here
|
||||
|
||||
config ARCH_MPU_MIN_BLOCK_SIZE
|
||||
default 4096
|
||||
|
||||
config ARCH_MPU_HAS_TOR
|
||||
default n
|
||||
|
||||
config ARCH_MPU_HAS_NO4
|
||||
default n
|
||||
|
||||
config ARCH_MPU_HAS_NAPOT
|
||||
default y
|
||||
|
@ -39,11 +39,20 @@
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
#define PMP_UFLASH_FLAGS (PMPCFG_A_NAPOT | PMPCFG_X | PMPCFG_R)
|
||||
#define PMP_USRAM_FLAGS (PMPCFG_A_NAPOT | PMPCFG_W | PMPCFG_R)
|
||||
|
||||
#define UFLASH_START (uintptr_t)&__uflash_start
|
||||
#define UFLASH_SIZE (uintptr_t)&__uflash_size
|
||||
#define USRAM_START (uintptr_t)&__usram_start
|
||||
#define USRAM_SIZE (uintptr_t)&__usram_size
|
||||
|
||||
/* Physical and virtual addresses to page tables (vaddr = paddr mapping) */
|
||||
|
||||
#define PGT_BASE_PADDR (uint64_t)&m_l1_pgtable
|
||||
#define PGT_L1_PBASE (uint64_t)&m_l1_pgtable
|
||||
#define PGT_L2_PBASE (uint64_t)&m_l2_pgtable
|
||||
#define PGT_L3_PBASE (uint64_t)&m_l3_pgtable
|
||||
#define PGT_L1_VBASE PGT_L1_PBASE
|
||||
#define PGT_L2_VBASE PGT_L2_PBASE
|
||||
#define PGT_L3_VBASE PGT_L3_PBASE
|
||||
|
||||
@ -52,6 +61,11 @@
|
||||
#define MMU_UFLASH_FLAGS (PTE_R | PTE_X | PTE_U | PTE_G)
|
||||
#define MMU_USRAM_FLAGS (PTE_R | PTE_W | PTE_U | PTE_G)
|
||||
|
||||
/* Kernel RAM needs to be opened (the page tables) */
|
||||
|
||||
#define KSRAM_START (uintptr_t)&__ksram_start
|
||||
#define KSRAM_SIZE (uintptr_t)&__ksram_size
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
@ -178,9 +192,8 @@ static void configure_mpu(void)
|
||||
/* Configure the PMP to permit user-space access to its ROM and RAM.
|
||||
*
|
||||
* Note: PMP by default revokes access, thus if different privilege modes
|
||||
* are in use (mstatus.mprv is set), the the user space _must_ be granted
|
||||
* access here, otherwise an exception will fire when the user space task
|
||||
* is started.
|
||||
* are in use, the user space _must_ be granted access here, otherwise
|
||||
* an exception will fire when the user space task is started.
|
||||
*
|
||||
* Note: according to the Polarfire reference manual, address bits [1:0]
|
||||
* are not considered (due to 4 octet alignment), so strictly they don't
|
||||
@ -198,19 +211,46 @@ static void configure_mpu(void)
|
||||
*
|
||||
*/
|
||||
|
||||
riscv_config_pmp_region(0, PMPCFG_A_NAPOT | PMPCFG_X | PMPCFG_R,
|
||||
(uintptr_t)&__uflash_start,
|
||||
(uintptr_t)&__uflash_size);
|
||||
int ret;
|
||||
int idx;
|
||||
|
||||
riscv_config_pmp_region(1, PMPCFG_A_NAPOT | PMPCFG_W | PMPCFG_R,
|
||||
(uintptr_t)&__usram_start,
|
||||
(uintptr_t)&__usram_size);
|
||||
/* First, test access to user flash */
|
||||
|
||||
ret = riscv_check_pmp_access(PMP_UFLASH_FLAGS, UFLASH_START, UFLASH_SIZE);
|
||||
|
||||
/* No access or partial access means we must crash */
|
||||
|
||||
DEBUGASSERT(ret != PMP_ACCESS_DENIED);
|
||||
|
||||
if (ret == PMP_ACCESS_OFF)
|
||||
{
|
||||
idx = riscv_next_free_pmp_region();
|
||||
DEBUGASSERT(idx >= 0);
|
||||
riscv_config_pmp_region(idx, PMP_UFLASH_FLAGS, UFLASH_START,
|
||||
UFLASH_SIZE);
|
||||
}
|
||||
|
||||
/* Then, test access to user RAM */
|
||||
|
||||
ret = riscv_check_pmp_access(PMP_USRAM_FLAGS, USRAM_START, USRAM_SIZE);
|
||||
DEBUGASSERT(ret != PMP_ACCESS_DENIED);
|
||||
if (ret == PMP_ACCESS_OFF)
|
||||
{
|
||||
idx = riscv_next_free_pmp_region();
|
||||
DEBUGASSERT(idx >= 0);
|
||||
riscv_config_pmp_region(idx, PMP_USRAM_FLAGS, USRAM_START, USRAM_SIZE);
|
||||
}
|
||||
|
||||
/* The supervisor must have access to the page tables */
|
||||
|
||||
riscv_config_pmp_region(2, PMPCFG_A_NAPOT | PMPCFG_W | PMPCFG_R,
|
||||
(uintptr_t)&__ksram_start,
|
||||
(uintptr_t)&__ksram_size);
|
||||
ret = riscv_check_pmp_access(PMP_USRAM_FLAGS, KSRAM_START, KSRAM_SIZE);
|
||||
DEBUGASSERT(ret != PMP_ACCESS_DENIED);
|
||||
if (ret == PMP_ACCESS_OFF)
|
||||
{
|
||||
idx = riscv_next_free_pmp_region();
|
||||
DEBUGASSERT(idx >= 0);
|
||||
riscv_config_pmp_region(idx, PMP_USRAM_FLAGS, KSRAM_START, KSRAM_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
@ -228,24 +268,22 @@ static void configure_mmu(void)
|
||||
|
||||
/* Setup the L3 references for executable memory */
|
||||
|
||||
mmu_ln_map_region(3, PGT_L3_VBASE, (uintptr_t)&__uflash_start,
|
||||
(uintptr_t)&__uflash_start, (uintptr_t)&__uflash_size,
|
||||
MMU_UFLASH_FLAGS);
|
||||
mmu_ln_map_region(3, PGT_L3_VBASE, UFLASH_START, UFLASH_START,
|
||||
UFLASH_SIZE, MMU_UFLASH_FLAGS);
|
||||
|
||||
/* Setup the L3 references for data memory */
|
||||
|
||||
mmu_ln_map_region(3, PGT_L3_VBASE, (uintptr_t)&__usram_start,
|
||||
(uintptr_t)&__usram_start, (uintptr_t)&__usram_size,
|
||||
MMU_USRAM_FLAGS);
|
||||
mmu_ln_map_region(3, PGT_L3_VBASE, USRAM_START, USRAM_START,
|
||||
USRAM_SIZE, MMU_USRAM_FLAGS);
|
||||
|
||||
/* Setup the L2 and L1 references */
|
||||
|
||||
mmu_ln_setentry(2, PGT_L2_VBASE, PGT_L3_PBASE, PGT_L3_VBASE, PTE_G);
|
||||
mmu_ln_setentry(1, PGT_BASE_PADDR, PGT_L2_PBASE, PGT_L2_VBASE, PTE_G);
|
||||
mmu_ln_setentry(1, PGT_L1_VBASE, PGT_L2_PBASE, PGT_L2_VBASE, PTE_G);
|
||||
|
||||
/* Enable MMU */
|
||||
|
||||
mmu_enable(PGT_BASE_PADDR, 0);
|
||||
mmu_enable(PGT_L1_PBASE, 0);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_BUILD_PROTECTED */
|
||||
|
Loading…
Reference in New Issue
Block a user