// See LICENSE for license details.
#include <stdio.h>
#include <string.h>
#include "nuclei_sdk_soc.h"
/* different trigger condition */
#define INSTRUCTION_FETCH_EXCEPTION 0
#define LOAD_EXCEPTION 1
#define STORE_EXCEPTION 2
#define RUN_WITH_NO_PMP_CHECK 3
/* Just choose one from above defines to test PMP */
#define TRIGGER_PMP_VIOLATION_MODE INSTRUCTION_FETCH_EXCEPTION
volatile uint8_t protected_data[0x2000] __attribute__((aligned(0x2000))) = \
{0xaa, 0x1, 0x02, 0x03, 0x04, 0x05, 0x06, 0xaa};
#ifndef __ICCRISCV__
#define __PMP_PROTECT __attribute__((section (".text"), aligned(0x2000)))
#else
/* IAR CC currently don't support align function in section,
* so we provide a workaround using a customized iar_evalsoc_ilm.icf in this demo
* we define a block called PMPFUNC alignment set to 0x2000 */
#define __PMP_PROTECT __attribute__((section (".text.pmpfunc")))
#endif
static void __PMP_PROTECT protected_execute(void)
{
printf("----protected_execute succeed!----\r\n");
printf("Attempting to read protected_data[0]\r\n");
printf("protected_data[0]: 0x%0X succeed\r\n", protected_data[0]);
printf("Attempting to write protected_data[0]\r\n");
protected_data[0] = 0xFF;
printf("Won't run here if violates L R\\W\\X permission check!\r\n");
}
static void pmp_violation_fault_handler(unsigned long mcause, unsigned long sp)
{
EXC_Frame_Type *exc_frame = (EXC_Frame_Type *)sp;
switch (mcause & MCAUSE_CAUSE) {
case InsAccFault_EXCn:
printf("Instruction access fault occurs, cause: 0x%lx, epc: 0x%lx\r\n", exc_frame->cause, exc_frame->epc);
break;
case LdFault_EXCn:
printf("Load access fault occurs, cause: 0x%lx, epc: 0x%lx\r\n", exc_frame->cause, exc_frame->epc);
break;
case StAccessFault_EXCn:
printf("Store/AMO access fault occurs, cause: 0x%lx, epc: 0x%lx\r\n", exc_frame->cause, exc_frame->epc);
break;
default: break;
}
while(1);
}
typedef void(*__funcpt)(void);
int main(void)
{
/* Configuration of execution region*/
pmp_config pmp_config_x = {
/*
* Locked PMP entries remain locked until the hart is reset,
* the L bit also indicates whether the R/W/X permissions are enforced on M-mode accesses
*/
.protection = PMP_L | PMP_R | PMP_W | PMP_X,
/* Initial protected excutable address range is 2^12 = 4K bytes*/
.order = 12,
/* initial base address is 0, change it to your memory assignment */
.base_addr = 0,
};
/* configuration of read/write region*/
pmp_config pmp_config_rw = {
/*
* Locked PMP entries remain locked until the hart is reset,
* the L bit also indicates whether the R/W/X permissions are enforced on M-mode accesses
*/
.protection = PMP_L | PMP_R | PMP_W | PMP_X,
/* initial protected readable/writable address range is 2^12 = 4K bytes */
.order = 12,
/* initial base address is 0, change it to your memory assignment */
.base_addr = 0,
};
pmp_config_x.base_addr = (unsigned long)protected_execute;
pmp_config_rw.base_addr = (unsigned long)protected_data;
#if (INSTRUCTION_FETCH_EXCEPTION == TRIGGER_PMP_VIOLATION_MODE)
/* Remove X permission of protected_execute region */
pmp_config_x.protection = PMP_L | PMP_R | PMP_W;
pmp_config_rw.protection = PMP_L | PMP_R | PMP_W;
#elif (LOAD_EXCEPTION == TRIGGER_PMP_VIOLATION_MODE)
/* Retrive X permission of protected_execute region */
pmp_config_x.protection = PMP_L | PMP_R | PMP_W | PMP_X;
/* Remove R permission of protected_data region */
pmp_config_rw.protection = PMP_L | PMP_W;
#elif (STORE_EXCEPTION == TRIGGER_PMP_VIOLATION_MODE)
/* Retrive X permission of protected_execute region */
pmp_config_x.protection = PMP_L | PMP_R | PMP_W | PMP_X;
/* Remove W permission of protected_data region */
pmp_config_rw.protection = PMP_L | PMP_R ;
#elif (RUN_WITH_NO_PMP_CHECK == TRIGGER_PMP_VIOLATION_MODE)
/* Unset Locking bit, any M-mode access matching the PMP entry will succeed */
pmp_config_x.protection = PMP_R | PMP_W | PMP_X;
pmp_config_rw.protection = PMP_R | PMP_W ;
#endif
printf("------PMP demo with trigger condition %d------\r\n", TRIGGER_PMP_VIOLATION_MODE);
__set_PMPENTRYx(0, &pmp_config_x);
/* Verify the configuration takes effect */
memset(&pmp_config_x, 0, sizeof(pmp_config));
__get_PMPENTRYx(0, &pmp_config_x);
printf("Get pmp entry: index %d, prot_out: 0x%x, addr_out: 0x%lx, order_out: %lu\r\n", \
0, pmp_config_x.protection, pmp_config_x.base_addr, pmp_config_x.order);
__set_PMPENTRYx(1, &pmp_config_rw);
/* Verify the configuration takes effect */
memset(&pmp_config_rw, 0, sizeof(pmp_config));
__get_PMPENTRYx(1, &pmp_config_rw);
printf("Get pmp entry: index %d, prot_out: 0x%x, addr_out: 0x%lx, order_out: %lu\r\n", \
1, pmp_config_rw.protection, pmp_config_rw.base_addr, pmp_config_rw.order);
/* register corresponding exception */
Exception_Register_EXC(InsAccFault_EXCn, (unsigned long)pmp_violation_fault_handler);
Exception_Register_EXC(LdFault_EXCn, (unsigned long)pmp_violation_fault_handler);
Exception_Register_EXC(StAccessFault_EXCn, (unsigned long)pmp_violation_fault_handler);
/* In case compiler use inline optimization of protected_execute */
__funcpt fncptr = ((__funcpt)protected_execute);
printf("Attempting to fetch instruction from protected address\n");
fncptr();
while(1);
return 0;
}