arch: riscv: add pmp support

- Set some helper function to write/clear/print PMP config registers.
- Add support for different PMP slot size function to core/board.

Signed-off-by: Alexandre Mergnat <amergnat@baylibre.com>
This commit is contained in:
Alexandre Mergnat 2020-07-21 15:59:23 +02:00 committed by Anas Nashif
commit 18962e4ab8
7 changed files with 553 additions and 0 deletions

View file

@ -25,6 +25,7 @@ menu "RISCV Processor Options"
config CORE_E31
bool "Use E31 core"
select RISCV_PMP
select ARCH_HAS_USERSPACE
select ARCH_HAS_STACK_PROTECTION if PMP_STACK_GUARD
default n
@ -112,6 +113,19 @@ config GEN_IRQ_VECTOR_TABLE
config NUM_IRQS
int
menuconfig RISCV_PMP
bool "RISC-V PMP Support"
default n
select THREAD_STACK_INFO
select MEMORY_PROTECTION
select ARCH_MEM_DOMAIN_SYNCHRONOUS_API if USERSPACE
help
MCU implements Physical Memory Protection.
if RISCV_PMP
source "arch/riscv/core/pmp/Kconfig"
endif #RISCV_PMP
endmenu
endmenu

View file

@ -1,5 +1,7 @@
# SPDX-License-Identifier: Apache-2.0
add_subdirectory_ifdef(CONFIG_RISCV_PMP pmp)
zephyr_library()
zephyr_library_sources(

View file

@ -0,0 +1,14 @@
#
# Copyright (c) 2020 BayLibre, SAS
#
# SPDX-License-Identifier: Apache-2.0
#
zephyr_library()
zephyr_library_sources_ifdef(CONFIG_RISCV_PMP core_pmp.c)
zephyr_library_include_directories(
.
../../include
)

View file

@ -0,0 +1,11 @@
# Physical Memory Protection (PMP) configuration options
# Copyright (c) 2020 BayLibre, SAS
# SPDX-License-Identifier: Apache-2.0
config PMP_SLOT
int "Number of PMP slot"
default 8
help
Depend of the arch/board. Take care to don't put value higher
than the Hardware allow you.

View file

@ -0,0 +1,221 @@
/*
* Copyright (c) 2020 BayLibre, SAS
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <kernel.h>
#include <kernel_internal.h>
#include "core_pmp.h"
#include <arch/riscv/csr.h>
#include <stdio.h>
#define PMP_SLOT_NUMBER CONFIG_PMP_SLOT
#ifdef CONFIG_USERSPACE
extern ulong_t is_user_mode;
#endif
enum {
CSR_PMPCFG0,
CSR_PMPCFG1,
CSR_PMPCFG2,
CSR_PMPCFG3,
CSR_PMPADDR0,
CSR_PMPADDR1,
CSR_PMPADDR2,
CSR_PMPADDR3,
CSR_PMPADDR4,
CSR_PMPADDR5,
CSR_PMPADDR6,
CSR_PMPADDR7,
CSR_PMPADDR8,
CSR_PMPADDR9,
CSR_PMPADDR10,
CSR_PMPADDR11,
CSR_PMPADDR12,
CSR_PMPADDR13,
CSR_PMPADDR14,
CSR_PMPADDR15
};
ulong_t csr_read_enum(int pmp_csr_enum)
{
ulong_t res = -1;
switch (pmp_csr_enum) {
case CSR_PMPCFG0:
res = csr_read(0x3A0); break;
case CSR_PMPCFG1:
res = csr_read(0x3A1); break;
case CSR_PMPCFG2:
res = csr_read(0x3A2); break;
case CSR_PMPCFG3:
res = csr_read(0x3A3); break;
case CSR_PMPADDR0:
res = csr_read(0x3B0); break;
case CSR_PMPADDR1:
res = csr_read(0x3B1); break;
case CSR_PMPADDR2:
res = csr_read(0x3B2); break;
case CSR_PMPADDR3:
res = csr_read(0x3B3); break;
case CSR_PMPADDR4:
res = csr_read(0x3B4); break;
case CSR_PMPADDR5:
res = csr_read(0x3B5); break;
case CSR_PMPADDR6:
res = csr_read(0x3B6); break;
case CSR_PMPADDR7:
res = csr_read(0x3B7); break;
case CSR_PMPADDR8:
res = csr_read(0x3B8); break;
case CSR_PMPADDR9:
res = csr_read(0x3B9); break;
case CSR_PMPADDR10:
res = csr_read(0x3BA); break;
case CSR_PMPADDR11:
res = csr_read(0x3BB); break;
case CSR_PMPADDR12:
res = csr_read(0x3BC); break;
case CSR_PMPADDR13:
res = csr_read(0x3BD); break;
case CSR_PMPADDR14:
res = csr_read(0x3BE); break;
case CSR_PMPADDR15:
res = csr_read(0x3BF); break;
default:
break;
}
return res;
}
void csr_write_enum(int pmp_csr_enum, ulong_t value)
{
switch (pmp_csr_enum) {
case CSR_PMPCFG0:
csr_write(0x3A0, value); break;
case CSR_PMPCFG1:
csr_write(0x3A1, value); break;
case CSR_PMPCFG2:
csr_write(0x3A2, value); break;
case CSR_PMPCFG3:
csr_write(0x3A3, value); break;
case CSR_PMPADDR0:
csr_write(0x3B0, value); break;
case CSR_PMPADDR1:
csr_write(0x3B1, value); break;
case CSR_PMPADDR2:
csr_write(0x3B2, value); break;
case CSR_PMPADDR3:
csr_write(0x3B3, value); break;
case CSR_PMPADDR4:
csr_write(0x3B4, value); break;
case CSR_PMPADDR5:
csr_write(0x3B5, value); break;
case CSR_PMPADDR6:
csr_write(0x3B6, value); break;
case CSR_PMPADDR7:
csr_write(0x3B7, value); break;
case CSR_PMPADDR8:
csr_write(0x3B8, value); break;
case CSR_PMPADDR9:
csr_write(0x3B9, value); break;
case CSR_PMPADDR10:
csr_write(0x3BA, value); break;
case CSR_PMPADDR11:
csr_write(0x3BB, value); break;
case CSR_PMPADDR12:
csr_write(0x3BC, value); break;
case CSR_PMPADDR13:
csr_write(0x3BD, value); break;
case CSR_PMPADDR14:
csr_write(0x3BE, value); break;
case CSR_PMPADDR15:
csr_write(0x3BF, value); break;
default:
break;
}
}
int z_riscv_pmp_set(unsigned int index, ulong_t cfg_val, ulong_t addr_val)
{
ulong_t reg_val;
ulong_t shift, mask;
int pmpcfg_csr;
int pmpaddr_csr;
if ((index >= PMP_SLOT_NUMBER) | (index < 0))
return -1;
/* Calculate PMP config/addr register, shift and mask */
#ifdef CONFIG_64BIT
pmpcfg_csr = CSR_PMPCFG0 + ((index >> 3) << 1);
shift = (index & 0x7) << 3;
#else
pmpcfg_csr = CSR_PMPCFG0 + (index >> 2);
shift = (index & 0x3) << 3;
#endif /* CONFIG_64BIT */
pmpaddr_csr = CSR_PMPADDR0 + index;
/* Mask = 0x000000FF<<((index%4)*8) */
mask = 0x000000FF << shift;
cfg_val = cfg_val << shift;
addr_val = TO_PMP_ADDR(addr_val);
reg_val = csr_read_enum(pmpcfg_csr);
reg_val = reg_val & ~mask;
reg_val = reg_val | cfg_val;
csr_write_enum(pmpaddr_csr, addr_val);
csr_write_enum(pmpcfg_csr, reg_val);
return 0;
}
int pmp_get(unsigned int index, ulong_t *cfg_val, ulong_t *addr_val)
{
ulong_t shift;
int pmpcfg_csr;
int pmpaddr_csr;
if ((index >= PMP_SLOT_NUMBER) | (index < 0))
return -1;
/* Calculate PMP config/addr register and shift */
#ifdef CONFIG_64BIT
pmpcfg_csr = CSR_PMPCFG0 + (index >> 4);
shift = (index & 0x0007) << 3;
#else
pmpcfg_csr = CSR_PMPCFG0 + (index >> 2);
shift = (index & 0x0003) << 3;
#endif /* CONFIG_64BIT */
pmpaddr_csr = CSR_PMPADDR0 + index;
*cfg_val = (csr_read_enum(pmpcfg_csr) >> shift) & 0xFF;
*addr_val = FROM_PMP_ADDR(csr_read_enum(pmpaddr_csr));
return 0;
}
void z_riscv_pmp_clear_config(void)
{
for (unsigned int i = 0; i < RISCV_PMP_CFG_NUM; i++)
csr_write_enum(CSR_PMPCFG0 + i, 0);
}
/* Function to help debug */
void z_riscv_pmp_print(unsigned int index)
{
ulong_t cfg_val;
ulong_t addr_val;
if (pmp_get(index, &cfg_val, &addr_val))
return;
#ifdef CONFIG_64BIT
printf("PMP[%d] :\t%02lX %16lX\n", index, cfg_val, addr_val);
#else
printf("PMP[%d] :\t%02lX %08lX\n", index, cfg_val, addr_val);
#endif /* CONFIG_64BIT */
}

View file

@ -0,0 +1,57 @@
/*
* Copyright (c) 2020 BayLibre, SAS
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef CORE_PMP_H_
#define CORE_PMP_H_
/* Configuration register flags (cfg_val)*/
#define PMP_R 0x01 /* Allow read */
#define PMP_W 0x02 /* Allow write */
#define PMP_X 0x04 /* Allow execute */
#define PMP_A 0x18 /* Address-matching mode field */
#define PMP_L 0x80 /* PMP entry is locked */
#define PMP_OFF 0x00 /* Null region */
#define PMP_TOR 0x08 /* Top of range */
#define PMP_NA4 0x10 /* Naturally aligned four-byte region */
#define PMP_NAPOT 0x18 /* Naturally aligned power-of-two region */
#define PMP_SHIFT_ADDR 2
#define PMP_TYPE_MASK 0x18
#define TO_PMP_ADDR(addr) ((addr) >> PMP_SHIFT_ADDR)
#define FROM_PMP_ADDR(addr) ((addr) << PMP_SHIFT_ADDR)
#define TO_NAPOT_RANGE(size) (((size) - 1) >> 1)
#define TO_PMP_NAPOT(addr, size) TO_PMP_ADDR(addr | \
TO_NAPOT_RANGE(size))
#ifdef CONFIG_RISCV_PMP
/*
* @brief Set a Physical Memory Protection slot
*
* Configure a memory region to be secured by one of the 16 PMP entries.
*
* @param index Number of the targeted PMP entrie (0 to 15 only).
* @param cfg_val Configuration value (cf datasheet or defined flags)
* @param addr_val Address register value
*
* This function shall only be called from Secure state.
*
* @return -1 if bad argument, 0 otherwise.
*/
int z_riscv_pmp_set(unsigned int index, ulong_t cfg_val, ulong_t addr_val);
/*
* @brief Reset to 0 all PMP setup registers
*/
void z_riscv_pmp_clear_config(void);
/*
* @brief Print PMP setup register for info/debug
*/
void z_riscv_pmp_print(unsigned int index);
#endif /* CONFIG_RISCV_PMP */
#endif /* CORE_PMP_H_ */

234
include/arch/riscv/csr.h Normal file
View file

@ -0,0 +1,234 @@
/*
* Copyright (c) 2020 BayLibre, SAS
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef CSR_H_
#define CSR_H_
#define MSTATUS_UIE 0x00000001
#define MSTATUS_SIE 0x00000002
#define MSTATUS_HIE 0x00000004
#define MSTATUS_MIE 0x00000008
#define MSTATUS_UPIE 0x00000010
#define MSTATUS_SPIE 0x00000020
#define MSTATUS_HPIE 0x00000040
#define MSTATUS_MPIE 0x00000080
#define MSTATUS_SPP 0x00000100
#define MSTATUS_HPP 0x00000600
#define MSTATUS_MPP 0x00001800
#define MSTATUS_FS 0x00006000
#define MSTATUS_XS 0x00018000
#define MSTATUS_MPRV 0x00020000
#define MSTATUS_SUM 0x00040000
#define MSTATUS_MXR 0x00080000
#define MSTATUS_TVM 0x00100000
#define MSTATUS_TW 0x00200000
#define MSTATUS_TSR 0x00400000
#define MSTATUS32_SD 0x80000000
#define MSTATUS_UXL 0x0000000300000000
#define MSTATUS_SXL 0x0000000C00000000
#define MSTATUS64_SD 0x8000000000000000
#define SSTATUS_UIE 0x00000001
#define SSTATUS_SIE 0x00000002
#define SSTATUS_UPIE 0x00000010
#define SSTATUS_SPIE 0x00000020
#define SSTATUS_SPP 0x00000100
#define SSTATUS_FS 0x00006000
#define SSTATUS_XS 0x00018000
#define SSTATUS_SUM 0x00040000
#define SSTATUS_MXR 0x00080000
#define SSTATUS32_SD 0x80000000
#define SSTATUS_UXL 0x0000000300000000
#define SSTATUS64_SD 0x8000000000000000
#define DCSR_XDEBUGVER (3U<<30)
#define DCSR_NDRESET (1<<29)
#define DCSR_FULLRESET (1<<28)
#define DCSR_EBREAKM (1<<15)
#define DCSR_EBREAKH (1<<14)
#define DCSR_EBREAKS (1<<13)
#define DCSR_EBREAKU (1<<12)
#define DCSR_STOPCYCLE (1<<10)
#define DCSR_STOPTIME (1<<9)
#define DCSR_CAUSE (7<<6)
#define DCSR_DEBUGINT (1<<5)
#define DCSR_HALT (1<<3)
#define DCSR_STEP (1<<2)
#define DCSR_PRV (3<<0)
#define DCSR_CAUSE_NONE 0
#define DCSR_CAUSE_SWBP 1
#define DCSR_CAUSE_HWBP 2
#define DCSR_CAUSE_DEBUGINT 3
#define DCSR_CAUSE_STEP 4
#define DCSR_CAUSE_HALT 5
#define MCONTROL_TYPE(xlen) (0xfULL<<((xlen)-4))
#define MCONTROL_DMODE(xlen) (1ULL<<((xlen)-5))
#define MCONTROL_MASKMAX(xlen) (0x3fULL<<((xlen)-11))
#define MCONTROL_SELECT (1<<19)
#define MCONTROL_TIMING (1<<18)
#define MCONTROL_ACTION (0x3f<<12)
#define MCONTROL_CHAIN (1<<11)
#define MCONTROL_MATCH (0xf<<7)
#define MCONTROL_M (1<<6)
#define MCONTROL_H (1<<5)
#define MCONTROL_S (1<<4)
#define MCONTROL_U (1<<3)
#define MCONTROL_EXECUTE (1<<2)
#define MCONTROL_STORE (1<<1)
#define MCONTROL_LOAD (1<<0)
#define MCONTROL_TYPE_NONE 0
#define MCONTROL_TYPE_MATCH 2
#define MCONTROL_ACTION_DEBUG_EXCEPTION 0
#define MCONTROL_ACTION_DEBUG_MODE 1
#define MCONTROL_ACTION_TRACE_START 2
#define MCONTROL_ACTION_TRACE_STOP 3
#define MCONTROL_ACTION_TRACE_EMIT 4
#define MCONTROL_MATCH_EQUAL 0
#define MCONTROL_MATCH_NAPOT 1
#define MCONTROL_MATCH_GE 2
#define MCONTROL_MATCH_LT 3
#define MCONTROL_MATCH_MASK_LOW 4
#define MCONTROL_MATCH_MASK_HIGH 5
#define MIP_SSIP (1 << IRQ_S_SOFT)
#define MIP_HSIP (1 << IRQ_H_SOFT)
#define MIP_MSIP (1 << IRQ_M_SOFT)
#define MIP_STIP (1 << IRQ_S_TIMER)
#define MIP_HTIP (1 << IRQ_H_TIMER)
#define MIP_MTIP (1 << IRQ_M_TIMER)
#define MIP_SEIP (1 << IRQ_S_EXT)
#define MIP_HEIP (1 << IRQ_H_EXT)
#define MIP_MEIP (1 << IRQ_M_EXT)
#define SIP_SSIP MIP_SSIP
#define SIP_STIP MIP_STIP
#define PRV_U 0
#define PRV_S 1
#define PRV_H 2
#define PRV_M 3
#define SATP32_MODE 0x80000000
#define SATP32_ASID 0x7FC00000
#define SATP32_PPN 0x003FFFFF
#define SATP64_MODE 0xF000000000000000
#define SATP64_ASID 0x0FFFF00000000000
#define SATP64_PPN 0x00000FFFFFFFFFFF
#define SATP_MODE_OFF 0
#define SATP_MODE_SV32 1
#define SATP_MODE_SV39 8
#define SATP_MODE_SV48 9
#define SATP_MODE_SV57 10
#define SATP_MODE_SV64 11
#define PMP_R 0x01
#define PMP_W 0x02
#define PMP_X 0x04
#define PMP_A 0x18
#define PMP_L 0x80
#define PMP_SHIFT 2
#define PMP_TOR 0x08
#define PMP_NA4 0x10
#define PMP_NAPOT 0x18
#define IRQ_S_SOFT 1
#define IRQ_H_SOFT 2
#define IRQ_M_SOFT 3
#define IRQ_S_TIMER 5
#define IRQ_H_TIMER 6
#define IRQ_M_TIMER 7
#define IRQ_S_EXT 9
#define IRQ_H_EXT 10
#define IRQ_M_EXT 11
#define IRQ_COP 12
#define IRQ_HOST 13
#define DEFAULT_RSTVEC 0x00001000
#define CLINT_BASE 0x02000000
#define CLINT_SIZE 0x000c0000
#define EXT_IO_BASE 0x40000000
#define DRAM_BASE 0x80000000
/* page table entry (PTE) fields */
#define PTE_V 0x001 /* Valid */
#define PTE_R 0x002 /* Read */
#define PTE_W 0x004 /* Write */
#define PTE_X 0x008 /* Execute */
#define PTE_U 0x010 /* User */
#define PTE_G 0x020 /* Global */
#define PTE_A 0x040 /* Accessed */
#define PTE_D 0x080 /* Dirty */
#define PTE_SOFT 0x300 /* Reserved for Software */
#define PTE_PPN_SHIFT 10
#define PTE_TABLE(PTE) (((PTE) & (PTE_V | PTE_R | PTE_W | PTE_X)) == PTE_V)
#define INSERT_FIELD(val, which, fieldval) \
( \
((val) & ~(which)) | ((fieldval) * ((which) & ~((which)-1))) \
) \
#define csr_read(csr) \
({ \
register unsigned long __v; \
__asm__ volatile ("csrr %0, " #csr \
: "=r" (__v)); \
__v; \
})
#define csr_write(csr, val) \
({ \
unsigned long __v = (unsigned long)(val); \
__asm__ volatile ("csrw " #csr ", %0" \
: : "rK" (__v) \
: "memory"); \
})
#define csr_read_set(csr, val) \
({ \
unsigned long __v = (unsigned long)(val); \
__asm__ volatile ("csrrs %0, " #csr ", %1" \
: "=r" (__v) : "rK" (__v) \
: "memory"); \
__v; \
})
#define csr_set(csr, val) \
({ \
unsigned long __v = (unsigned long)(val); \
__asm__ volatile ("csrs " #csr ", %0" \
: : "rK" (__v) \
: "memory"); \
})
#define csr_read_clear(csr, val) \
({ \
unsigned long __v = (unsigned long)(val); \
__asm__ volatile ("csrrc %0, " #csr ", %1" \
: "=r" (__v) : "rK" (__v) \
: "memory"); \
__v; \
})
#define csr_clear(csr, val) \
({ \
unsigned long __v = (unsigned long)(val); \
__asm__ volatile ("csrc " #csr ", %0" \
: : "rK" (__v) \
: "memory"); \
})
#endif /* CSR_H_ */