From 9a763f4ff21acd4396f14b030b02e841d6fea887 Mon Sep 17 00:00:00 2001 From: Sachin Parekh Date: Wed, 18 May 2022 14:46:03 +0530 Subject: [PATCH] esp32c2: Enable IRAM/DRAM split using PMP --- .../port/esp32c2/cpu_util_esp32c2.c | 81 +++++++++++++++---- components/esp_system/Kconfig | 12 +++ .../esp32c2/include/soc/Kconfig.soc_caps.in | 4 + components/soc/esp32c2/include/soc/soc_caps.h | 2 + 4 files changed, 82 insertions(+), 17 deletions(-) diff --git a/components/esp_hw_support/port/esp32c2/cpu_util_esp32c2.c b/components/esp_hw_support/port/esp32c2/cpu_util_esp32c2.c index 92434e26ba..f5f0056b12 100644 --- a/components/esp_hw_support/port/esp32c2/cpu_util_esp32c2.c +++ b/components/esp_hw_support/port/esp32c2/cpu_util_esp32c2.c @@ -5,6 +5,29 @@ */ #include #include "esp_cpu.h" +#include "sdkconfig.h" + +#if CONFIG_ESP_SYSTEM_PMP_IDRAM_SPLIT && !BOOTLOADER_BUILD +extern int _iram_end; +extern int _data_start; +#define IRAM_END (int)&_iram_end +#define DRAM_START (int)&_data_start +#else +#define IRAM_END SOC_DIRAM_IRAM_HIGH +#define DRAM_START SOC_DIRAM_DRAM_LOW +#endif + +#ifdef BOOTLOADER_BUILD +// Without L bit set +#define CONDITIONAL_NONE 0x0 +#define CONDITIONAL_RX PMP_R | PMP_X +#define CONDITIONAL_RW PMP_R | PMP_W +#else +// With L bit set +#define CONDITIONAL_NONE NONE +#define CONDITIONAL_RX RX +#define CONDITIONAL_RW RW +#endif void esp_cpu_configure_region_protection(void) { @@ -19,47 +42,71 @@ void esp_cpu_configure_region_protection(void) * 3) 3-15 PMPADDR entries be hardcoded to fixed value, 0-2 PMPADDR be programmed to split ID SRAM * as IRAM/DRAM. All PMPCFG entryies be available. * + * 4) Ideally, PMPADDR 0-2 entries should be configured twice, once during bootloader startup and another during app startup. + * However, the CPU currently always executes in machine mode and to enforce these permissions in machine mode, we need + * to set the Lock (L) bit but if set once, it cannot be reconfigured. So, we only configure 0-2 PMPADDR during app startup. */ - const unsigned NONE = PMP_L ; + const unsigned NONE = PMP_L ; const unsigned R = PMP_L | PMP_R; const unsigned X = PMP_L | PMP_X; const unsigned RW = PMP_L | PMP_R | PMP_W; const unsigned RX = PMP_L | PMP_R | PMP_X; const unsigned RWX = PMP_L | PMP_R | PMP_W | PMP_X; + /* There are 3 configuration scenarios for PMPADDR 0-2 + * + * 1. Bootloader build: + * - We cannot set the lock bit as we need to reconfigure it again for the application. + * We configure PMPADDR 0-1 to cover entire valid IRAM range and PMPADDR 2-3 to cover entire valid DRAM range. + * + * 2. Application build with CONFIG_ESP_SYSTEM_PMP_IDRAM_SPLIT enabled + * - We split the SRAM into IRAM and DRAM such that IRAM region cannot be accessed via DBUS + * and DRAM region cannot be accessed via IBUS. We use _iram_end and _data_start markers to set the boundaries. + * We also lock these entries so the R/W/X permissions are enforced even for machine mode + * + * 3. Application build with CONFIG_ESP_SYSTEM_PMP_IDRAM_SPLIT disabled + * - The IRAM-DRAM split is not enabled so we just need to ensure that access to only valid address ranges are successful + * so for that we set PMPADDR 0-1 to cover entire valid IRAM range and PMPADDR 2-3 to cover entire DRAM region. + * We also lock these entries so the R/W/X permissions are enforced even for machine mode + * + * PMPADDR 3-15 are hard-coded and are appicable to both, bootloader and application. So we configure and lock + * these during BOOTLOADER build itself. During application build, reconfiguration of these PMPADDR entries + * are silently ignored by the CPU + */ + // 1. IRAM - PMP_ENTRY_SET(0,SOC_DIRAM_IRAM_LOW, NONE); - PMP_ENTRY_SET(1,SOC_DIRAM_IRAM_HIGH, PMP_TOR|RWX); //TODO IRAM/DRAM spilt address + PMP_ENTRY_SET(0, SOC_DIRAM_IRAM_LOW, CONDITIONAL_NONE); + PMP_ENTRY_SET(1, IRAM_END, PMP_TOR | CONDITIONAL_RX); // 2. DRAM - PMP_ENTRY_SET(2,SOC_DIRAM_DRAM_LOW, NONE); //TODO IRAM/DRAM spilt address - PMP_ENTRY_CFG_SET(3,PMP_TOR|RW); + PMP_ENTRY_SET(2, DRAM_START, CONDITIONAL_NONE); + PMP_ENTRY_CFG_SET(3, PMP_TOR | CONDITIONAL_RW); // 3. Debug region - PMP_ENTRY_CFG_SET(4,PMP_NAPOT|RWX); + PMP_ENTRY_CFG_SET(4, PMP_NAPOT | RWX); // 4. DROM (flash dcache) - PMP_ENTRY_CFG_SET(5,PMP_NAPOT|R); + PMP_ENTRY_CFG_SET(5, PMP_NAPOT | R); // 5. DROM_MASK - PMP_ENTRY_CFG_SET(6,NONE); - PMP_ENTRY_CFG_SET(7,PMP_TOR|R); + PMP_ENTRY_CFG_SET(6, NONE); + PMP_ENTRY_CFG_SET(7, PMP_TOR | R); // 6. IROM_MASK - PMP_ENTRY_CFG_SET(8,NONE); - PMP_ENTRY_CFG_SET(9,PMP_TOR|RX); + PMP_ENTRY_CFG_SET(8, NONE); + PMP_ENTRY_CFG_SET(9, PMP_TOR | RX); // 7. IROM (flash icache) - PMP_ENTRY_CFG_SET(10,PMP_NAPOT|RX); + PMP_ENTRY_CFG_SET(10, PMP_NAPOT | RX); // 8. Peripheral addresses - PMP_ENTRY_CFG_SET(11,PMP_NAPOT|RW); + PMP_ENTRY_CFG_SET(11, PMP_NAPOT | RW); // 9. SRAM (used as ICache) - PMP_ENTRY_CFG_SET(12,PMP_NAPOT|X); + PMP_ENTRY_CFG_SET(12, PMP_NAPOT | X); // 10. no access to any address below(0x0-0xFFFF_FFFF) - PMP_ENTRY_CFG_SET(13,PMP_NA4|NONE);// last 4 bytes(0xFFFFFFFC) - PMP_ENTRY_CFG_SET(14,NONE); - PMP_ENTRY_CFG_SET(15,PMP_TOR|NONE); + PMP_ENTRY_CFG_SET(13, PMP_NA4 | NONE);// last 4 bytes(0xFFFFFFFC) + PMP_ENTRY_CFG_SET(14, NONE); + PMP_ENTRY_CFG_SET(15, PMP_TOR | NONE); } diff --git a/components/esp_system/Kconfig b/components/esp_system/Kconfig index 7ff117390d..5cf117ebbb 100644 --- a/components/esp_system/Kconfig +++ b/components/esp_system/Kconfig @@ -117,6 +117,18 @@ menu "ESP System Settings" menu "Memory protection" + config ESP_SYSTEM_PMP_IDRAM_SPLIT + bool "Enable IRAM/DRAM split protection" + depends on SOC_CPU_IDRAM_SPLIT_USING_PMP + default "y" + help + If enabled, the CPU watches all the memory access and raises an exception in case + of any memory violation. This feature automatically splits + the SRAM memory, using PMP, into data and instruction segments and sets Read/Execute permissions + for the instruction part (below given splitting address) and Read/Write permissions + for the data part (above the splitting address). The memory protection is effective + on all access through the IRAM0 and DRAM0 buses. + config ESP_SYSTEM_MEMPROT_DEPCHECK bool default y if IDF_TARGET_ESP32S2 diff --git a/components/soc/esp32c2/include/soc/Kconfig.soc_caps.in b/components/soc/esp32c2/include/soc/Kconfig.soc_caps.in index e489b52c30..4e06c8853f 100644 --- a/components/soc/esp32c2/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32c2/include/soc/Kconfig.soc_caps.in @@ -151,6 +151,10 @@ config SOC_CPU_WATCHPOINT_SIZE hex default 0x80000000 +config SOC_CPU_IDRAM_SPLIT_USING_PMP + bool + default y + config SOC_GDMA_GROUPS int default 1 diff --git a/components/soc/esp32c2/include/soc/soc_caps.h b/components/soc/esp32c2/include/soc/soc_caps.h index 902b26da83..2350c6e882 100644 --- a/components/soc/esp32c2/include/soc/soc_caps.h +++ b/components/soc/esp32c2/include/soc/soc_caps.h @@ -80,6 +80,8 @@ #define SOC_CPU_WATCHPOINT_SIZE 0x80000000 // bytes +#define SOC_CPU_IDRAM_SPLIT_USING_PMP 1 + /*-------------------------- GDMA CAPS -------------------------------------*/ #define SOC_GDMA_GROUPS (1U) // Number of GDMA groups #define SOC_GDMA_PAIRS_PER_GROUP (1U) // Number of GDMA pairs in each group