mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Merge #19712
19712: cpu/riscv: Add PMP driver r=MrKevinWeiss a=Teufelchen1
### Contribution description
Hi! 🐘
this adds a basic RISC-V physical memory protection (PMP) driver to RIOT. Well, 'driver' might be a stretched, feels more like a little utility :)
EDIT: Also added a no-execute RAM option for the hifive & a corresponding test
Since I only have an Hifive rev b, it's only enabled on this board / cpu. I also tested the code on an ESP32-C but the feature can't be enabled there, as `cpu/riscv_common/` is not used by the ESP32...
### Testing procedure
* Grab a hifive rev b
* go to `examples/hello-world`
* Add `USEMODULES += periph_pmp` to the `Makefile`
* Include `pmp.h` in `main.c`
* Add code e.g. `print_pmpcfg(0);`
* compile & flash & term
You should see something like this:
```
# Hello World!
# You are running RIOT on a(n) hifive1b board.
# This board features a(n) fe310 MCU.
# pmp00cfg: - R-X OFF 0x00000000 - 0x00000000
```
Co-authored-by: Teufelchen1 <bennet.blischke@outlook.com>
This commit is contained in:
commit
755442fe27
@ -33,6 +33,10 @@ ifneq (,$(filter mpu_noexec_ram,$(USEMODULE)))
|
|||||||
FEATURES_REQUIRED += cortexm_mpu
|
FEATURES_REQUIRED += cortexm_mpu
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifneq (,$(filter pmp_noexec_ram,$(USEMODULE)))
|
||||||
|
FEATURES_REQUIRED += periph_pmp
|
||||||
|
endif
|
||||||
|
|
||||||
ifneq (,$(filter lwip_%,$(USEMODULE)))
|
ifneq (,$(filter lwip_%,$(USEMODULE)))
|
||||||
USEPKG += lwip
|
USEPKG += lwip
|
||||||
endif
|
endif
|
||||||
|
@ -37,13 +37,13 @@ typedef enum {
|
|||||||
PANIC_HARD_REBOOT,
|
PANIC_HARD_REBOOT,
|
||||||
PANIC_ASSERT_FAIL,
|
PANIC_ASSERT_FAIL,
|
||||||
PANIC_EXPECT_FAIL,
|
PANIC_EXPECT_FAIL,
|
||||||
|
PANIC_MEM_MANAGE, /**< memory management fault */
|
||||||
#ifdef MODULE_CORTEXM_COMMON
|
#ifdef MODULE_CORTEXM_COMMON
|
||||||
PANIC_NMI_HANDLER, /**< non maskable interrupt */
|
PANIC_NMI_HANDLER, /**< non maskable interrupt */
|
||||||
PANIC_HARD_FAULT, /**< hard fault */
|
PANIC_HARD_FAULT, /**< hard fault */
|
||||||
#if defined(CPU_CORE_CORTEX_M3) || defined(CPU_CORE_CORTEX_M33) || \
|
#if defined(CPU_CORE_CORTEX_M3) || defined(CPU_CORE_CORTEX_M33) || \
|
||||||
defined(CPU_CORE_CORTEX_M4) || defined(CPU_CORE_CORTEX_M4F) || \
|
defined(CPU_CORE_CORTEX_M4) || defined(CPU_CORE_CORTEX_M4F) || \
|
||||||
defined(CPU_CORE_CORTEX_M7)
|
defined(CPU_CORE_CORTEX_M7)
|
||||||
PANIC_MEM_MANAGE, /**< memory controller interrupt */
|
|
||||||
PANIC_BUS_FAULT, /**< bus fault */
|
PANIC_BUS_FAULT, /**< bus fault */
|
||||||
PANIC_USAGE_FAULT, /**< undefined instruction or unaligned access */
|
PANIC_USAGE_FAULT, /**< undefined instruction or unaligned access */
|
||||||
PANIC_DEBUG_MON, /**< debug interrupt */
|
PANIC_DEBUG_MON, /**< debug interrupt */
|
||||||
|
@ -13,6 +13,7 @@ config CPU_FAM_FE310
|
|||||||
select HAS_PERIPH_GPIO
|
select HAS_PERIPH_GPIO
|
||||||
select HAS_PERIPH_GPIO_IRQ
|
select HAS_PERIPH_GPIO_IRQ
|
||||||
select HAS_PERIPH_PM
|
select HAS_PERIPH_PM
|
||||||
|
select HAS_PERIPH_PMP
|
||||||
select HAS_PERIPH_PLIC
|
select HAS_PERIPH_PLIC
|
||||||
select HAS_PERIPH_RTT_OVERFLOW
|
select HAS_PERIPH_RTT_OVERFLOW
|
||||||
select HAS_PERIPH_RTT_SET_COUNTER
|
select HAS_PERIPH_RTT_SET_COUNTER
|
||||||
|
@ -3,6 +3,7 @@ CPU_CORE := rv32imac
|
|||||||
FEATURES_PROVIDED += periph_cpuid
|
FEATURES_PROVIDED += periph_cpuid
|
||||||
FEATURES_PROVIDED += periph_gpio periph_gpio_irq
|
FEATURES_PROVIDED += periph_gpio periph_gpio_irq
|
||||||
FEATURES_PROVIDED += periph_pm
|
FEATURES_PROVIDED += periph_pm
|
||||||
|
FEATURES_PROVIDED += periph_pmp
|
||||||
FEATURES_PROVIDED += periph_plic
|
FEATURES_PROVIDED += periph_plic
|
||||||
FEATURES_PROVIDED += periph_rtt_overflow
|
FEATURES_PROVIDED += periph_rtt_overflow
|
||||||
FEATURES_PROVIDED += periph_rtt_set_counter
|
FEATURES_PROVIDED += periph_rtt_set_counter
|
||||||
|
@ -36,6 +36,12 @@ extern "C" {
|
|||||||
*/
|
*/
|
||||||
#define PLIC_BASE_ADDR (PLIC_CTRL_ADDR)
|
#define PLIC_BASE_ADDR (PLIC_CTRL_ADDR)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Number of available PMP regions
|
||||||
|
* Note, the upper 8 regions are hardwired to zero!
|
||||||
|
*/
|
||||||
|
#define NUM_PMP_ENTRIES 16
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
125
cpu/riscv_common/include/pmp.h
Normal file
125
cpu/riscv_common/include/pmp.h
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 Bennet Blischke <bennet.blischke@haw-hamburg.de>
|
||||||
|
*
|
||||||
|
* This file is subject to the terms and conditions of the GNU Lesser General
|
||||||
|
* Public License v2.1. See the file LICENSE in the top level directory for more
|
||||||
|
* details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ingroup cpu_riscv_common
|
||||||
|
* @{
|
||||||
|
*
|
||||||
|
* @file
|
||||||
|
* @brief RISC-V PMP configuration options
|
||||||
|
*
|
||||||
|
* RISCV implementations using this peripheral must define the `NUM_PMP_ENTRIES`
|
||||||
|
* `NUM_PMP_ENTRIES` must be 16 or 64.
|
||||||
|
*
|
||||||
|
* @author Bennet Blischke
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PMP_H
|
||||||
|
#define PMP_H
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Bit masks for the PMP configuration register
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
#define PMP_NONE 0x00 /**< No access allowed at all */
|
||||||
|
#define PMP_R 0x01 /**< Allow read access */
|
||||||
|
#define PMP_W 0x02 /**< Allow write access */
|
||||||
|
#define PMP_X 0x04 /**< Allow execution */
|
||||||
|
|
||||||
|
#define PMP_A 0x18 /**< Addressing mode mask */
|
||||||
|
#define PMP_OFF 0x00 /**< Disable this pmp entry */
|
||||||
|
#define PMP_TOR 0x08 /**< Top-of-range addressing mode */
|
||||||
|
#define PMP_NA4 0x10 /**< Naturally aligned four-byte region */
|
||||||
|
#define PMP_NAPOT 0x18 /**< Naturally aligned power-of-two region, ≥8 bytes */
|
||||||
|
|
||||||
|
#define PMP_L 0x80 /**< Lock; read-only config & applies to machine-mode */
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create a NAPOT formatted address
|
||||||
|
*
|
||||||
|
* @param addr Base address, must be aligned to the size of the region
|
||||||
|
* @param size Size of the region in bytes
|
||||||
|
*/
|
||||||
|
static inline uint32_t make_napot(uint32_t addr, uint32_t size)
|
||||||
|
{
|
||||||
|
assert(addr % size == 0);
|
||||||
|
return addr | ((size - 1) >> 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Writes a complete pmpcfg register
|
||||||
|
*
|
||||||
|
* @param reg_num Register number
|
||||||
|
* @param value Value to write
|
||||||
|
*/
|
||||||
|
void write_pmpcfg(uint8_t reg_num, uint32_t value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read a complete pmpcfg register
|
||||||
|
*
|
||||||
|
* @param[in] reg_num Register number
|
||||||
|
*
|
||||||
|
* @return Contents of the specified register
|
||||||
|
*/
|
||||||
|
uint32_t read_pmpcfg(uint8_t reg_num);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Writes a complete pmpaddr register
|
||||||
|
*
|
||||||
|
* @param reg_num Register number
|
||||||
|
* @param value Value to write
|
||||||
|
*/
|
||||||
|
void write_pmpaddr(uint8_t reg_num, uint32_t value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read a complete pmpaddr register
|
||||||
|
*
|
||||||
|
* @param[in] reg_num Register number
|
||||||
|
*
|
||||||
|
* @return Contents of the specified register
|
||||||
|
*/
|
||||||
|
uint32_t read_pmpaddr(uint8_t reg_num);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read a single pmpcfg sub-register
|
||||||
|
*
|
||||||
|
* @param[in] entry Sub-register number
|
||||||
|
*
|
||||||
|
* @return Contents of the specified sub-register
|
||||||
|
*/
|
||||||
|
uint8_t get_pmpcfg(uint8_t entry);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set's a single pmpcfg sub-register
|
||||||
|
*
|
||||||
|
* @param entry Sub-register number
|
||||||
|
* @param value Value to write
|
||||||
|
*/
|
||||||
|
void set_pmpcfg(uint8_t entry, uint8_t value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Prints a single pmpcfg sub-register human readable
|
||||||
|
*
|
||||||
|
* @param entry Register number to print
|
||||||
|
*/
|
||||||
|
void print_pmpcfg(uint8_t entry);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* PMP_H */
|
||||||
|
/** @} */
|
3
cpu/riscv_common/include/vendor/riscv_csr.h
vendored
3
cpu/riscv_common/include/vendor/riscv_csr.h
vendored
@ -171,6 +171,9 @@
|
|||||||
|
|
||||||
#define PTE_TABLE(PTE) (((PTE) & (PTE_V | PTE_R | PTE_W | PTE_X)) == PTE_V)
|
#define PTE_TABLE(PTE) (((PTE) & (PTE_V | PTE_R | PTE_W | PTE_X)) == PTE_V)
|
||||||
|
|
||||||
|
#define CSR_PMPCFG0 0x3a0 // PMP configuration base register
|
||||||
|
#define CSR_PMPADDR0 0x3b0 // PMP address base register
|
||||||
|
|
||||||
#ifdef __riscv
|
#ifdef __riscv
|
||||||
|
|
||||||
#ifdef __riscv64
|
#ifdef __riscv64
|
||||||
|
@ -134,6 +134,14 @@ static void handle_trap(uword_t mcause)
|
|||||||
write_csr(mepc, return_pc + 4);
|
write_csr(mepc, return_pc + 4);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
#ifdef MODULE_PERIPH_PMP
|
||||||
|
case CAUSE_FAULT_FETCH:
|
||||||
|
core_panic(PANIC_MEM_MANAGE, "MEM MANAGE HANDLER (fetch)");
|
||||||
|
case CAUSE_FAULT_LOAD:
|
||||||
|
core_panic(PANIC_MEM_MANAGE, "MEM MANAGE HANDLER (load)");
|
||||||
|
case CAUSE_FAULT_STORE:
|
||||||
|
core_panic(PANIC_MEM_MANAGE, "MEM MANAGE HANDLER (store)");
|
||||||
|
#endif
|
||||||
default:
|
default:
|
||||||
#ifdef DEVELHELP
|
#ifdef DEVELHELP
|
||||||
printf("Unhandled trap:\n");
|
printf("Unhandled trap:\n");
|
||||||
|
@ -32,4 +32,10 @@ config MODULE_PERIPH_PLIC
|
|||||||
help
|
help
|
||||||
Platform-Level interrupt controller driver.
|
Platform-Level interrupt controller driver.
|
||||||
|
|
||||||
|
config MODULE_PERIPH_PMP
|
||||||
|
bool
|
||||||
|
depends on HAS_PERIPH_PMP
|
||||||
|
help
|
||||||
|
Physical memory protection driver.
|
||||||
|
|
||||||
endif # MODULE_RISCV_COMMON_PERIPH
|
endif # MODULE_RISCV_COMMON_PERIPH
|
||||||
|
300
cpu/riscv_common/periph/pmp.c
Normal file
300
cpu/riscv_common/periph/pmp.c
Normal file
@ -0,0 +1,300 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 Bennet Blischke <bennet.blischke@haw-hamburg.de>
|
||||||
|
*
|
||||||
|
* This file is subject to the terms and conditions of the GNU Lesser General
|
||||||
|
* Public License v2.1. See the file LICENSE in the top level directory for more
|
||||||
|
* details.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @ingroup cpu_riscv_common
|
||||||
|
* @{
|
||||||
|
*
|
||||||
|
* @file
|
||||||
|
* @brief RISCV PMP implementation
|
||||||
|
*
|
||||||
|
* @author Bennet Blischke
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "cpu_conf.h"
|
||||||
|
#include "vendor/riscv_csr.h"
|
||||||
|
#include "pmp.h"
|
||||||
|
|
||||||
|
#if NUM_PMP_ENTRIES != 16 && NUM_PMP_ENTRIES != 64
|
||||||
|
#error "Only 16 or 64 PMP entries allowed."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define _WRITE_PMPCFG(REG, VALUE) write_csr(REG, VALUE)
|
||||||
|
#define _READ_PMPCFG(REG) read_csr(REG)
|
||||||
|
#define WRITE_PMPCFG(REG, VALUE) _WRITE_PMPCFG(CSR_PMPCFG0 + REG, VALUE)
|
||||||
|
#define READ_PMPCFG(REG) _READ_PMPCFG(CSR_PMPCFG0 + REG)
|
||||||
|
|
||||||
|
/* These shifts are needed as the PMP Address registers stores
|
||||||
|
* 34 Bit addresses with 4 byte alignment in a 32 bit register
|
||||||
|
*/
|
||||||
|
#define _WRITE_PMPADDR(REG, VALUE) write_csr(REG, VALUE >> 2)
|
||||||
|
#define _READ_PMPADDR(REG) (read_csr(REG) << 2)
|
||||||
|
#define WRITE_PMPADDR(REG, VALUE) _WRITE_PMPADDR(CSR_PMPADDR0 + REG, VALUE)
|
||||||
|
#define READ_PMPADDR(REG) _READ_PMPADDR(CSR_PMPADDR0 + REG)
|
||||||
|
|
||||||
|
#define _NAPOT_base(addr) (addr & (addr + 1))
|
||||||
|
#define _NAPOT_end(addr) (addr | (addr + 1))
|
||||||
|
|
||||||
|
/* The assembly instructions for reading riscv CSRs, encode the CSR-address
|
||||||
|
* in the immediate field which is not changeable at run-time.
|
||||||
|
* Hence it is needed to generate all possible register accesses at build-time.
|
||||||
|
* Settled for switch-case as they are easiest to read, understand and maintain.
|
||||||
|
*/
|
||||||
|
void write_pmpcfg(uint8_t reg_num, uint32_t value)
|
||||||
|
{
|
||||||
|
assert(reg_num < NUM_PMP_ENTRIES / 4);
|
||||||
|
switch (reg_num) {
|
||||||
|
case 0: WRITE_PMPCFG(0, value); break;
|
||||||
|
case 1: WRITE_PMPCFG(1, value); break;
|
||||||
|
case 2: WRITE_PMPCFG(2, value); break;
|
||||||
|
case 3: WRITE_PMPCFG(3, value); break;
|
||||||
|
#if NUM_PMP_ENTRIES == 64
|
||||||
|
case 4: WRITE_PMPCFG(4, value); break;
|
||||||
|
case 5: WRITE_PMPCFG(5, value); break;
|
||||||
|
case 6: WRITE_PMPCFG(6, value); break;
|
||||||
|
case 7: WRITE_PMPCFG(7, value); break;
|
||||||
|
case 8: WRITE_PMPCFG(8, value); break;
|
||||||
|
case 9: WRITE_PMPCFG(9, value); break;
|
||||||
|
case 10: WRITE_PMPCFG(10, value); break;
|
||||||
|
case 11: WRITE_PMPCFG(11, value); break;
|
||||||
|
case 12: WRITE_PMPCFG(12, value); break;
|
||||||
|
case 13: WRITE_PMPCFG(13, value); break;
|
||||||
|
case 14: WRITE_PMPCFG(14, value); break;
|
||||||
|
case 15: WRITE_PMPCFG(15, value); break;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t read_pmpcfg(uint8_t reg_num)
|
||||||
|
{
|
||||||
|
assert(reg_num < NUM_PMP_ENTRIES / 4);
|
||||||
|
switch (reg_num) {
|
||||||
|
case 0: return READ_PMPCFG(0);
|
||||||
|
case 1: return READ_PMPCFG(1);
|
||||||
|
case 2: return READ_PMPCFG(2);
|
||||||
|
case 3: return READ_PMPCFG(3);
|
||||||
|
#if NUM_PMP_ENTRIES == 64
|
||||||
|
case 4: return READ_PMPCFG(4);
|
||||||
|
case 5: return READ_PMPCFG(5);
|
||||||
|
case 6: return READ_PMPCFG(6);
|
||||||
|
case 7: return READ_PMPCFG(7);
|
||||||
|
case 8: return READ_PMPCFG(8);
|
||||||
|
case 9: return READ_PMPCFG(9);
|
||||||
|
case 10: return READ_PMPCFG(10);
|
||||||
|
case 11: return READ_PMPCFG(11);
|
||||||
|
case 12: return READ_PMPCFG(12);
|
||||||
|
case 13: return READ_PMPCFG(13);
|
||||||
|
case 14: return READ_PMPCFG(14);
|
||||||
|
case 15: return READ_PMPCFG(15);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_pmpaddr(uint8_t reg_num, uint32_t value)
|
||||||
|
{
|
||||||
|
assert(reg_num < NUM_PMP_ENTRIES);
|
||||||
|
switch (reg_num) {
|
||||||
|
case 0: WRITE_PMPADDR(0, value); break;
|
||||||
|
case 1: WRITE_PMPADDR(1, value); break;
|
||||||
|
case 2: WRITE_PMPADDR(2, value); break;
|
||||||
|
case 3: WRITE_PMPADDR(3, value); break;
|
||||||
|
case 4: WRITE_PMPADDR(4, value); break;
|
||||||
|
case 5: WRITE_PMPADDR(5, value); break;
|
||||||
|
case 6: WRITE_PMPADDR(6, value); break;
|
||||||
|
case 7: WRITE_PMPADDR(7, value); break;
|
||||||
|
case 8: WRITE_PMPADDR(8, value); break;
|
||||||
|
case 9: WRITE_PMPADDR(9, value); break;
|
||||||
|
case 10: WRITE_PMPADDR(10, value); break;
|
||||||
|
case 11: WRITE_PMPADDR(11, value); break;
|
||||||
|
case 12: WRITE_PMPADDR(12, value); break;
|
||||||
|
case 13: WRITE_PMPADDR(13, value); break;
|
||||||
|
case 14: WRITE_PMPADDR(14, value); break;
|
||||||
|
case 15: WRITE_PMPADDR(15, value); break;
|
||||||
|
#if NUM_PMP_ENTRIES == 64
|
||||||
|
case 16: WRITE_PMPADDR(16, value); break;
|
||||||
|
case 17: WRITE_PMPADDR(17, value); break;
|
||||||
|
case 18: WRITE_PMPADDR(18, value); break;
|
||||||
|
case 19: WRITE_PMPADDR(19, value); break;
|
||||||
|
case 20: WRITE_PMPADDR(20, value); break;
|
||||||
|
case 21: WRITE_PMPADDR(21, value); break;
|
||||||
|
case 22: WRITE_PMPADDR(22, value); break;
|
||||||
|
case 23: WRITE_PMPADDR(23, value); break;
|
||||||
|
case 24: WRITE_PMPADDR(24, value); break;
|
||||||
|
case 25: WRITE_PMPADDR(25, value); break;
|
||||||
|
case 26: WRITE_PMPADDR(26, value); break;
|
||||||
|
case 27: WRITE_PMPADDR(27, value); break;
|
||||||
|
case 28: WRITE_PMPADDR(28, value); break;
|
||||||
|
case 29: WRITE_PMPADDR(29, value); break;
|
||||||
|
case 30: WRITE_PMPADDR(30, value); break;
|
||||||
|
case 31: WRITE_PMPADDR(31, value); break;
|
||||||
|
case 32: WRITE_PMPADDR(32, value); break;
|
||||||
|
case 33: WRITE_PMPADDR(33, value); break;
|
||||||
|
case 34: WRITE_PMPADDR(34, value); break;
|
||||||
|
case 35: WRITE_PMPADDR(35, value); break;
|
||||||
|
case 36: WRITE_PMPADDR(36, value); break;
|
||||||
|
case 37: WRITE_PMPADDR(37, value); break;
|
||||||
|
case 38: WRITE_PMPADDR(38, value); break;
|
||||||
|
case 39: WRITE_PMPADDR(39, value); break;
|
||||||
|
case 40: WRITE_PMPADDR(40, value); break;
|
||||||
|
case 41: WRITE_PMPADDR(41, value); break;
|
||||||
|
case 42: WRITE_PMPADDR(42, value); break;
|
||||||
|
case 43: WRITE_PMPADDR(43, value); break;
|
||||||
|
case 44: WRITE_PMPADDR(44, value); break;
|
||||||
|
case 45: WRITE_PMPADDR(45, value); break;
|
||||||
|
case 46: WRITE_PMPADDR(46, value); break;
|
||||||
|
case 47: WRITE_PMPADDR(47, value); break;
|
||||||
|
case 48: WRITE_PMPADDR(48, value); break;
|
||||||
|
case 49: WRITE_PMPADDR(49, value); break;
|
||||||
|
case 50: WRITE_PMPADDR(50, value); break;
|
||||||
|
case 51: WRITE_PMPADDR(51, value); break;
|
||||||
|
case 52: WRITE_PMPADDR(52, value); break;
|
||||||
|
case 53: WRITE_PMPADDR(53, value); break;
|
||||||
|
case 54: WRITE_PMPADDR(54, value); break;
|
||||||
|
case 55: WRITE_PMPADDR(55, value); break;
|
||||||
|
case 56: WRITE_PMPADDR(56, value); break;
|
||||||
|
case 57: WRITE_PMPADDR(57, value); break;
|
||||||
|
case 58: WRITE_PMPADDR(58, value); break;
|
||||||
|
case 59: WRITE_PMPADDR(59, value); break;
|
||||||
|
case 60: WRITE_PMPADDR(60, value); break;
|
||||||
|
case 61: WRITE_PMPADDR(61, value); break;
|
||||||
|
case 62: WRITE_PMPADDR(62, value); break;
|
||||||
|
case 63: WRITE_PMPADDR(63, value); break;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t read_pmpaddr(uint8_t reg_num)
|
||||||
|
{
|
||||||
|
assert(reg_num < NUM_PMP_ENTRIES);
|
||||||
|
switch (reg_num) {
|
||||||
|
case 0: return READ_PMPADDR(0);
|
||||||
|
case 1: return READ_PMPADDR(1);
|
||||||
|
case 2: return READ_PMPADDR(2);
|
||||||
|
case 3: return READ_PMPADDR(3);
|
||||||
|
case 4: return READ_PMPADDR(4);
|
||||||
|
case 5: return READ_PMPADDR(5);
|
||||||
|
case 6: return READ_PMPADDR(6);
|
||||||
|
case 7: return READ_PMPADDR(7);
|
||||||
|
case 8: return READ_PMPADDR(8);
|
||||||
|
case 9: return READ_PMPADDR(9);
|
||||||
|
case 10: return READ_PMPADDR(10);
|
||||||
|
case 11: return READ_PMPADDR(11);
|
||||||
|
case 12: return READ_PMPADDR(12);
|
||||||
|
case 13: return READ_PMPADDR(13);
|
||||||
|
case 14: return READ_PMPADDR(14);
|
||||||
|
case 15: return READ_PMPADDR(15);
|
||||||
|
#if NUM_PMP_ENTRIES == 64
|
||||||
|
case 16: return READ_PMPADDR(16);
|
||||||
|
case 17: return READ_PMPADDR(17);
|
||||||
|
case 18: return READ_PMPADDR(18);
|
||||||
|
case 19: return READ_PMPADDR(19);
|
||||||
|
case 20: return READ_PMPADDR(20);
|
||||||
|
case 21: return READ_PMPADDR(21);
|
||||||
|
case 22: return READ_PMPADDR(22);
|
||||||
|
case 23: return READ_PMPADDR(23);
|
||||||
|
case 24: return READ_PMPADDR(24);
|
||||||
|
case 25: return READ_PMPADDR(25);
|
||||||
|
case 26: return READ_PMPADDR(26);
|
||||||
|
case 27: return READ_PMPADDR(27);
|
||||||
|
case 28: return READ_PMPADDR(28);
|
||||||
|
case 29: return READ_PMPADDR(29);
|
||||||
|
case 30: return READ_PMPADDR(30);
|
||||||
|
case 31: return READ_PMPADDR(31);
|
||||||
|
case 32: return READ_PMPADDR(32);
|
||||||
|
case 33: return READ_PMPADDR(33);
|
||||||
|
case 34: return READ_PMPADDR(34);
|
||||||
|
case 35: return READ_PMPADDR(35);
|
||||||
|
case 36: return READ_PMPADDR(36);
|
||||||
|
case 37: return READ_PMPADDR(37);
|
||||||
|
case 38: return READ_PMPADDR(38);
|
||||||
|
case 39: return READ_PMPADDR(39);
|
||||||
|
case 40: return READ_PMPADDR(40);
|
||||||
|
case 41: return READ_PMPADDR(41);
|
||||||
|
case 42: return READ_PMPADDR(42);
|
||||||
|
case 43: return READ_PMPADDR(43);
|
||||||
|
case 44: return READ_PMPADDR(44);
|
||||||
|
case 45: return READ_PMPADDR(45);
|
||||||
|
case 46: return READ_PMPADDR(46);
|
||||||
|
case 47: return READ_PMPADDR(47);
|
||||||
|
case 48: return READ_PMPADDR(48);
|
||||||
|
case 49: return READ_PMPADDR(49);
|
||||||
|
case 50: return READ_PMPADDR(50);
|
||||||
|
case 51: return READ_PMPADDR(51);
|
||||||
|
case 52: return READ_PMPADDR(52);
|
||||||
|
case 53: return READ_PMPADDR(53);
|
||||||
|
case 54: return READ_PMPADDR(54);
|
||||||
|
case 55: return READ_PMPADDR(55);
|
||||||
|
case 56: return READ_PMPADDR(56);
|
||||||
|
case 57: return READ_PMPADDR(57);
|
||||||
|
case 58: return READ_PMPADDR(58);
|
||||||
|
case 59: return READ_PMPADDR(59);
|
||||||
|
case 60: return READ_PMPADDR(60);
|
||||||
|
case 61: return READ_PMPADDR(61);
|
||||||
|
case 62: return READ_PMPADDR(62);
|
||||||
|
case 63: return READ_PMPADDR(63);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t get_pmpcfg(uint8_t entry)
|
||||||
|
{
|
||||||
|
return (read_pmpcfg(entry / 4) >> (entry % 4) * 8) & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_pmpcfg(uint8_t entry, uint8_t value)
|
||||||
|
{
|
||||||
|
uint32_t cur_value = read_pmpcfg(entry / 4);
|
||||||
|
uint32_t mask = 0xff << (entry % 4) * 8;
|
||||||
|
|
||||||
|
cur_value &= ~mask;
|
||||||
|
cur_value |= value << (entry % 4) * 8;
|
||||||
|
write_pmpcfg(entry / 4, cur_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_pmpcfg(uint8_t entry)
|
||||||
|
{
|
||||||
|
uint8_t cfg = get_pmpcfg(entry);
|
||||||
|
uint32_t start = 0;
|
||||||
|
uint32_t stop = 0;
|
||||||
|
char *mode = "OFF ";
|
||||||
|
|
||||||
|
switch (cfg & PMP_A) {
|
||||||
|
case PMP_TOR:
|
||||||
|
mode = "TOR ";
|
||||||
|
start = (entry > 0) ? read_pmpaddr(entry - 1) : 0;
|
||||||
|
stop = read_pmpaddr(entry);
|
||||||
|
break;
|
||||||
|
case PMP_NA4:
|
||||||
|
mode = "NA4 ";
|
||||||
|
start = read_pmpaddr(entry);
|
||||||
|
stop = start + 3;
|
||||||
|
break;
|
||||||
|
case PMP_NAPOT:
|
||||||
|
mode = "NAPOT";
|
||||||
|
/* Flipp last two bits in NAPOT mode */
|
||||||
|
uint32_t _tmp = read_pmpaddr(entry) | 0x03;
|
||||||
|
start = _NAPOT_base(_tmp);
|
||||||
|
stop = _NAPOT_end(_tmp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
printf("pmp%02dcfg: %c %c%c%c %s 0x%08x - 0x%08x\n",
|
||||||
|
entry,
|
||||||
|
(cfg & PMP_L) ? 'L' : '-',
|
||||||
|
(cfg & PMP_R) ? 'R' : '-',
|
||||||
|
(cfg & PMP_W) ? 'W' : '-',
|
||||||
|
(cfg & PMP_X) ? 'X' : '-',
|
||||||
|
mode,
|
||||||
|
(unsigned int)start,
|
||||||
|
(unsigned int)stop
|
||||||
|
);
|
||||||
|
}
|
@ -21,6 +21,10 @@
|
|||||||
#include "cpu_conf_common.h"
|
#include "cpu_conf_common.h"
|
||||||
#include "periph_cpu_common.h"
|
#include "periph_cpu_common.h"
|
||||||
|
|
||||||
|
#ifdef MODULE_PMP_NOEXEC_RAM
|
||||||
|
#include "pmp.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef MODULE_PUF_SRAM
|
#ifdef MODULE_PUF_SRAM
|
||||||
#include "puf_sram.h"
|
#include "puf_sram.h"
|
||||||
|
|
||||||
@ -45,4 +49,13 @@ void riscv_init(void)
|
|||||||
{
|
{
|
||||||
riscv_fpu_init();
|
riscv_fpu_init();
|
||||||
riscv_irq_init();
|
riscv_irq_init();
|
||||||
|
#ifdef MODULE_PMP_NOEXEC_RAM
|
||||||
|
/* This marks the (main) RAM region as non
|
||||||
|
* executable. Using PMP entry 0.
|
||||||
|
*/
|
||||||
|
write_pmpaddr(0, make_napot(CPU_RAM_BASE, CPU_RAM_SIZE));
|
||||||
|
|
||||||
|
/* Lock & select NAPOT, only allow write and read */
|
||||||
|
set_pmpcfg(0, PMP_L | PMP_NAPOT | PMP_W | PMP_R);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -344,6 +344,11 @@ config HAS_PERIPH_PLIC
|
|||||||
help
|
help
|
||||||
Indicates that a RISC-V Platform-local Interrupt Controller (PLIC) peripheral is present.
|
Indicates that a RISC-V Platform-local Interrupt Controller (PLIC) peripheral is present.
|
||||||
|
|
||||||
|
config HAS_PERIPH_PMP
|
||||||
|
bool
|
||||||
|
help
|
||||||
|
Indicates that a RISC-V physical memory protection (PMP) peripheral is present.
|
||||||
|
|
||||||
config HAS_PERIPH_PM
|
config HAS_PERIPH_PM
|
||||||
bool
|
bool
|
||||||
help
|
help
|
||||||
|
@ -322,6 +322,15 @@ PSEUDOMODULES += mpu_stack_guard
|
|||||||
PSEUDOMODULES += mpu_noexec_ram
|
PSEUDOMODULES += mpu_noexec_ram
|
||||||
## @}
|
## @}
|
||||||
|
|
||||||
|
## @defgroup pseudomodule_pmp_noexec_ram pmp_noexec_ram
|
||||||
|
## @{
|
||||||
|
## @brief Mark RAM as non-executable using the PMP
|
||||||
|
##
|
||||||
|
## Mark the RAM non executable.
|
||||||
|
## This is a protection mechanism which makes exploitation of buffer overflows significantly harder.
|
||||||
|
PSEUDOMODULES += pmp_noexec_ram
|
||||||
|
## @}
|
||||||
|
|
||||||
## @defgroup pseudomodule_md5sum md5sum
|
## @defgroup pseudomodule_md5sum md5sum
|
||||||
## @ingroup sys_shell_commands
|
## @ingroup sys_shell_commands
|
||||||
## @{
|
## @{
|
||||||
|
7
tests/cpu/pmp_noexec_ram/Makefile
Normal file
7
tests/cpu/pmp_noexec_ram/Makefile
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
BOARD ?= hifive1b
|
||||||
|
|
||||||
|
include ../Makefile.cpu_common
|
||||||
|
|
||||||
|
USEMODULE += pmp_noexec_ram
|
||||||
|
|
||||||
|
include $(RIOTBASE)/Makefile.include
|
11
tests/cpu/pmp_noexec_ram/README.md
Normal file
11
tests/cpu/pmp_noexec_ram/README.md
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# mpu_noexec_ram
|
||||||
|
|
||||||
|
Tests for the `pmp_noexec_ram` pseudomodule.
|
||||||
|
Only supported on RISC-V devices with PMP.
|
||||||
|
|
||||||
|
## Output
|
||||||
|
|
||||||
|
With `USEMODULE += pmp_noexec_ram` in `Makefile` this application should
|
||||||
|
execute a kernel panic, stating `Instruction access fault` (0x01) in the
|
||||||
|
`mcause` register. Without this pseudomodule activated, the hard fault
|
||||||
|
will be triggered by `Illegal instruction` (0x02) instead.
|
43
tests/cpu/pmp_noexec_ram/main.c
Normal file
43
tests/cpu/pmp_noexec_ram/main.c
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 Bennet Blischke <bennet.blischke@haw-hamburg.de>
|
||||||
|
*
|
||||||
|
* This file is subject to the terms and conditions of the GNU Lesser
|
||||||
|
* General Public License v2.1. See the file LICENSE in the top level
|
||||||
|
* directory for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ingroup tests
|
||||||
|
* @{
|
||||||
|
*
|
||||||
|
* @file
|
||||||
|
* @brief Test application for the pmp_noexec_ram pseudo-module
|
||||||
|
*
|
||||||
|
* @author Sören Tempel <tempel@uni-bremen.de>
|
||||||
|
* @author Bennet Blischke <bennet.blischke@haw-hamburg.de>
|
||||||
|
*
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "cpu.h"
|
||||||
|
#include "pmp.h"
|
||||||
|
|
||||||
|
#define JMPBUF_SIZE 3
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
uint32_t buf[JMPBUF_SIZE];
|
||||||
|
|
||||||
|
/* Fill the buffer with invalid instructions */
|
||||||
|
for (unsigned i = 0; i < JMPBUF_SIZE; i++) {
|
||||||
|
buf[i] = UINT32_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
puts("Attempting to jump to stack buffer ...\n");
|
||||||
|
__asm__ volatile ("jr %0" :: "r" ((uint8_t*)&buf));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
18
tests/cpu/pmp_noexec_ram/tests/01-run.py
Executable file
18
tests/cpu/pmp_noexec_ram/tests/01-run.py
Executable file
@ -0,0 +1,18 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# Copyright (C) 2020 Sören Tempel <tempel@uni-bremen.de>
|
||||||
|
#
|
||||||
|
# This file is subject to the terms and conditions of the GNU Lesser
|
||||||
|
# General Public License v2.1. See the file LICENSE in the top level
|
||||||
|
# directory for more details.
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from testrunner import run
|
||||||
|
|
||||||
|
|
||||||
|
def testfunc(child):
|
||||||
|
child.expect_exact("MEM MANAGE HANDLER (fetch)\r\n")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(run(testfunc, timeout=10))
|
Loading…
Reference in New Issue
Block a user