[mask_rom] Add Memory Protection (ePMP) module implementation This is an implementation of the memory protection specification with most configuration done in assembly. A C API is also provided to make the ROM_EXT image executable. A shadow copy of the PMP configuration registers is maintained by the mask ROM and may be checked against the hardware register values using the `epmp_state_check` function. A note on test status: The ePMP configuration for the mask ROM prevents access to the DV address space. Tests write to the DV address space to signal their status. Therefore all tests booted from the mask ROM will fail unless they modify the ePMP configuration. For the time being this ePMP reconfiguration is performed in the ROM_EXT so that the tests continue to pass without modification. This is intended to be a temporary measure and this functionality should be absorbed into one of the test libraries. Signed-off-by: Michael Munday <mike.munday@lowrisc.org>
diff --git a/sw/device/silicon_creator/lib/manifest.h b/sw/device/silicon_creator/lib/manifest.h index 76700e6..90855b6 100644 --- a/sw/device/silicon_creator/lib/manifest.h +++ b/sw/device/silicon_creator/lib/manifest.h
@@ -8,7 +8,7 @@ #include <stddef.h> #include "sw/device/lib/base/macros.h" -#include "sw/device/lib/runtime/epmp.h" +#include "sw/device/silicon_creator/lib/epmp.h" #include "sw/device/silicon_creator/lib/error.h" #include "sw/device/silicon_creator/lib/keymgr_binding_value.h" #include "sw/device/silicon_creator/lib/manifest_size.h"
diff --git a/sw/device/silicon_creator/lib/manifest_unittest.cc b/sw/device/silicon_creator/lib/manifest_unittest.cc index 653b8ed..0901ad3 100644 --- a/sw/device/silicon_creator/lib/manifest_unittest.cc +++ b/sw/device/silicon_creator/lib/manifest_unittest.cc
@@ -5,7 +5,7 @@ #include "sw/device/silicon_creator/lib/manifest.h" #include "gtest/gtest.h" -#include "sw/device/lib/runtime/epmp.h" +#include "sw/device/silicon_creator/lib/epmp.h" namespace manifest_unittest { namespace {
diff --git a/sw/device/silicon_creator/mask_rom/mask_rom.c b/sw/device/silicon_creator/mask_rom/mask_rom.c index 34a4e9d..4dd7db5 100644 --- a/sw/device/silicon_creator/mask_rom/mask_rom.c +++ b/sw/device/silicon_creator/mask_rom/mask_rom.c
@@ -21,6 +21,7 @@ #include "sw/device/silicon_creator/lib/error.h" #include "sw/device/silicon_creator/lib/shutdown.h" #include "sw/device/silicon_creator/lib/sigverify.h" +#include "sw/device/silicon_creator/mask_rom/mask_rom_epmp.h" #include "sw/device/silicon_creator/mask_rom/romextimage.h" #include "sw/device/silicon_creator/mask_rom/sigverify_keys.h" @@ -61,6 +62,10 @@ lifecycle_state_t lc_state = lifecycle_state_get(); shutdown_init(lc_state); + // Initiaize shadow copy of the ePMP register configuration. + epmp_state_t epmp; + mask_rom_epmp_state_init(&epmp); + // Initialize pinmux configuration so we can use the UART. pinmux_init(); @@ -81,10 +86,6 @@ // See "Cleaning Device State" Below. // clean_device_state_part_2(boot_reason); // Chip-Specific Startup Module - // Enable Memory Protection - // - PMP Initial Region (if not done in power on) - // enable_memory_protection(); // Lockdown Module - // Chip-specific startup functionality (NOTE: we expect this portion of // initialization to be done in assembly before C runtime init. Delete // this segment of comments & pseudo-code when done). @@ -187,11 +188,18 @@ // if (!final_jump_to_rom_ext(current_rom_ext_manifest)) { // Hardened Jump // Module if (true) { + // Unlock execution of ROM_EXT executable code (text) sections. + RETURN_IF_ERROR(epmp_state_check(&epmp)); + epmp_region_t rom_ext_text; + RETURN_IF_ERROR(manifest_code_region_get(manifest, &rom_ext_text)); + mask_rom_epmp_unlock_rom_ext_rx(&epmp, rom_ext_text); + RETURN_IF_ERROR(epmp_state_check(&epmp)); + + // Jump to ROM_EXT entry point. uintptr_t entry_point; if (manifest_entry_point_get(manifest, &entry_point) != kErrorOk) { break; } - // Jump to ROM_EXT entry point. base_printf("rom_ext_entry: %p\r\n", entry_point); ((romextimage_entry_point *)entry_point)(); // NOTE: never expecting a return, but if something were to go wrong
diff --git a/sw/device/silicon_creator/mask_rom/mask_rom.ld b/sw/device/silicon_creator/mask_rom/mask_rom.ld index a03c973..ca98193 100644 --- a/sw/device/silicon_creator/mask_rom/mask_rom.ld +++ b/sw/device/silicon_creator/mask_rom/mask_rom.ld
@@ -37,6 +37,19 @@ /* DV Log offset (has to be different to other boot stages). */ _dv_log_offset = 0x0; +/** + * Physical Memory Protection (PMP) encoded address register values. + * + * Some addresses required for PMP entries are known only at link time. + * These addresses are encoded here so that no calculations need to be + * performed at runtime. + * + * See The RISC-V Instruction Set Manual Volume II: Privileged Architecture + * for more information about PMP address register encodings. + */ +_epmp_text_tor_lo = _text_start / 4; +_epmp_text_tor_hi = _text_end / 4; +_epmp_stack_guard_na4 = _stack_start / 4; ENTRY(_mask_rom_start_boot); @@ -52,6 +65,7 @@ * reset handler correctly. */ .vectors _mask_rom_boot_address : ALIGN(256) { + _text_start = .; KEEP(*(.vectors)) } > rom @@ -71,6 +85,10 @@ .text : ALIGN(4) { *(.text) *(.text.*) + + /* Ensure section end is word-aligned. */ + . = ALIGN(4); + _text_end = .; } > rom /**
diff --git a/sw/device/silicon_creator/mask_rom/mask_rom_epmp.S b/sw/device/silicon_creator/mask_rom/mask_rom_epmp.S new file mode 100644 index 0000000..d3d8322 --- /dev/null +++ b/sw/device/silicon_creator/mask_rom/mask_rom_epmp.S
@@ -0,0 +1,155 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#include "sw/device/silicon_creator/lib/epmp_defs.h" + +#include "hw/top_earlgrey/sw/autogen/top_earlgrey_memory.h" + +/** + * Helper macro to move v into the correct position to set the pmp{N*4+i}cfg + * field in the pmpcfg{N} register. + * + * Example: set value for pmp5cfg which is a field of pmpcfg1. + * + * li t0, CFG_INDEX(5 % 4, v) + * csrw pmpcfg1, t0 + */ +#define CFG_INDEX(i, v) ((v) << (i*8)) + +/** + * Encode address for Top-Of-Range addressing mode. + */ +#define TOR(addr) ((addr) >> 2) + +/** + * Encode address and length for Naturally-Aligned-Power-Of-Two addressing + * mode. + */ +#define NAPOT(addr, len) (((addr) >> 2) | (((len) - 1) >> 3)) + +/** + * The "ax" flag below is necessary to ensure that this section + * is allocated space in ROM by the linker. + */ +.section .crt, "ax", @progbits + +/** + * Configure the CPU's Enhanced Physical Memory Protection (ePMP) feature. + * + * The steps are: + * + * 1. Enable Rule Locking Bypass (RLB). RLB allows later boot stages to + * modify locked PMP entries. + * 2. Configure access permissions for each address space of interest. + * 3. Enable Machine Mode Whitelist Policy (MMWP). MMWP stops any access + * that does not match a PMP entry. + * + * This function follows the standard ILP32 calling convention but does not + * require a valid stack pointer, thread pointer or global pointer. + * + * May clobber temporary registers (t0-t6). + */ +mask_rom_epmp_init: + .globl mask_rom_epmp_init + .type mask_rom_epmp_init, @function + + // Enable Rule Locking Bypass (RLB). + csrsi EPMP_MSECCFG, EPMP_MSECCFG_RLB + + // Clear all PMP configuration registers. + csrw pmpcfg0, zero + csrw pmpcfg1, zero + csrw pmpcfg2, zero + csrw pmpcfg3, zero + + // Pre-encoded addresses defined in linker script. + .extern _epmp_text_tor_lo + .extern _epmp_text_tor_hi + .extern _epmp_stack_guard_na4 + + // Setup PMP address registers. + + // ROM TEXT + la t0, _epmp_text_tor_lo + csrw pmpaddr0, t0 + la t0, _epmp_text_tor_hi + csrw pmpaddr1, t0 + + // ROM + li t0, NAPOT(TOP_EARLGREY_ROM_BASE_ADDR, TOP_EARLGREY_ROM_SIZE_BYTES) + csrw pmpaddr2, t0 + + // ROM_EXT TEXT (configured after signature verification) + csrw pmpaddr3, zero // ROM_EXT TEXT low + csrw pmpaddr4, zero // ROM_EXT TEXT high + + // eFLASH + li t0, NAPOT(TOP_EARLGREY_EFLASH_BASE_ADDR, TOP_EARLGREY_EFLASH_SIZE_BYTES) + csrw pmpaddr5, t0 + + // Free entries + csrw pmpaddr6, zero + csrw pmpaddr7, zero + csrw pmpaddr8, zero + csrw pmpaddr9, zero + + // MMIO + li t0, TOR(0x40000000) // TODO(#7117): generate MMIO start address. + csrw pmpaddr10, t0 + li t0, TOR(0x50000000) // TODO(#7117): generate MMIO end address. + csrw pmpaddr11, t0 + + // Free entries + csrw pmpaddr12, zero + csrw pmpaddr13, zero + + // Stack guard + la t0, _epmp_stack_guard_na4 + csrw pmpaddr14, t0 + + // RAM + li t0, NAPOT(TOP_EARLGREY_RAM_MAIN_BASE_ADDR, TOP_EARLGREY_RAM_MAIN_SIZE_BYTES) + csrw pmpaddr15, t0 + + // Set PMP configuration registers. + li t0, CFG_INDEX(1 % 4, EPMP_CFG_A_TOR | EPMP_CFG_LRX) | /* ROM TEXT */ \ + CFG_INDEX(2 % 4, EPMP_CFG_A_NAPOT | EPMP_CFG_LR) /* ROM */ + li t1, CFG_INDEX(5 % 4, EPMP_CFG_A_NAPOT | EPMP_CFG_LR) /* eFLASH */ + li t2, CFG_INDEX(11 % 4, EPMP_CFG_A_TOR | EPMP_CFG_LRW) /* MMIO */ + li t3, CFG_INDEX(14 % 4, EPMP_CFG_A_NA4 | EPMP_CFG_LRW) | /* Stack Guard */ \ + CFG_INDEX(15 % 4, EPMP_CFG_A_NAPOT | EPMP_CFG_LRW) /* RAM */ + csrw pmpcfg0, t0 + csrw pmpcfg1, t1 + csrw pmpcfg2, t2 + csrw pmpcfg3, t3 + + // Enable Machine Mode Whitelist Policy (MMWP). + // TODO(#5653): Enable Machine Mode Lockdown (MML)? + csrsi EPMP_MSECCFG, EPMP_MSECCFG_MMWP + + ret + + // Set function size to allow disassembly. + .size mask_rom_epmp_init, .-mask_rom_epmp_init + +/** + * Disable all accesses to the stack guard word. + * + * This function follows the standard ILP32 calling convention but does not + * require a valid stack pointer, thread pointer or global pointer. + * + * Clobbers t0. + */ +mask_rom_epmp_stack_guard_init: + .globl mask_rom_epmp_stack_guard_init + .type mask_rom_epmp_stack_guard_init, @function + + // Remove all permissions from entry 14 (the stack guard). + li t0, CFG_INDEX(14 % 4, EPMP_CFG_R | EPMP_CFG_W | EPMP_CFG_X) + csrc pmpcfg3, t0 + + ret + + // Set function size to allow disassembly. + .size mask_rom_epmp_stack_guard_init, .-mask_rom_epmp_stack_guard_init
diff --git a/sw/device/silicon_creator/mask_rom/mask_rom_epmp.c b/sw/device/silicon_creator/mask_rom/mask_rom_epmp.c new file mode 100644 index 0000000..e4f7031 --- /dev/null +++ b/sw/device/silicon_creator/mask_rom/mask_rom_epmp.c
@@ -0,0 +1,73 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#include "sw/device/silicon_creator/mask_rom/mask_rom_epmp.h" + +#include "sw/device/lib/base/bitfield.h" +#include "sw/device/lib/base/csr.h" + +#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h" + +// Symbols defined in linker script. +extern char _stack_start[]; // Lowest stack address. +extern char _text_start[]; // Start of executable code. +extern char _text_end[]; // End of executable code. + +void mask_rom_epmp_state_init(epmp_state_t *state) { + // Address space definitions. + // + // Note that the stack guard is placed at _stack_start because the stack + // grows downward from _stack_end. + const epmp_region_t rom_text = {.start = (uintptr_t)_text_start, + .end = (uintptr_t)_text_end}; + const epmp_region_t rom = {.start = TOP_EARLGREY_ROM_CTRL_ROM_BASE_ADDR, + .end = TOP_EARLGREY_ROM_CTRL_ROM_BASE_ADDR + + TOP_EARLGREY_ROM_CTRL_ROM_SIZE_BYTES}; + const epmp_region_t eflash = { + .start = TOP_EARLGREY_EFLASH_BASE_ADDR, + .end = TOP_EARLGREY_EFLASH_BASE_ADDR + TOP_EARLGREY_EFLASH_SIZE_BYTES}; + // TODO(#7117): generate MMIO addresses. + const epmp_region_t mmio = {.start = 0x40000000, .end = 0x50000000}; + const epmp_region_t stack_guard = {.start = (uintptr_t)_stack_start, + .end = (uintptr_t)_stack_start + 4}; + const epmp_region_t ram = {.start = TOP_EARLGREY_RAM_MAIN_BASE_ADDR, + .end = TOP_EARLGREY_RAM_MAIN_BASE_ADDR + + TOP_EARLGREY_RAM_MAIN_SIZE_BYTES}; + + // Initialize in-memory copy of ePMP register state. + // + // The actual hardware configuration is performed separately, either by reset + // logic or in assembly. This code must be kept in sync with any changes + // to the hardware configuration. + *state = (epmp_state_t){0}; + epmp_state_configure_tor(state, 1, rom_text, kEpmpPermLockedReadExecute); + epmp_state_configure_napot(state, 2, rom, kEpmpPermLockedReadOnly); + epmp_state_configure_napot(state, 5, eflash, kEpmpPermLockedReadOnly); + epmp_state_configure_tor(state, 11, mmio, kEpmpPermLockedReadWrite); + epmp_state_configure_na4(state, 14, stack_guard, kEpmpPermLockedNoAccess); + epmp_state_configure_napot(state, 15, ram, kEpmpPermLockedReadWrite); + state->mseccfg = EPMP_MSECCFG_MMWP | EPMP_MSECCFG_RLB; +} + +void mask_rom_epmp_unlock_rom_ext_rx(epmp_state_t *state, epmp_region_t image) { + // Update the in-memory copy of ePMP register state. + const int kEntry = 4; + epmp_state_configure_tor(state, kEntry, image, kEpmpPermLockedReadExecute); + + // Update the hardware configuration (CSRs). + // + // Entry is hardcoded as 4. Make sure to modify hardcoded values if changing + // kEntry. + // + // The `pmp4cfg` configuration is the first field in `pmpcfg1`. + // + // 32 24 16 8 0 + // +-----------+-----------+-----------+-----------+ + // `pmpcfg1` = | `pmp7cfg` | `pmp6cfg` | `pmp5cfg` | `pmp4cfg` | + // +-----------+-----------+-----------+-----------+ + CSR_WRITE(CSR_REG_PMPADDR3, image.start >> 2); + CSR_WRITE(CSR_REG_PMPADDR4, image.end >> 2); + CSR_CLEAR_BITS(CSR_REG_PMPCFG1, 0xff); + CSR_SET_BITS(CSR_REG_PMPCFG1, kEpmpModeTor | kEpmpPermLockedReadExecute); +}
diff --git a/sw/device/silicon_creator/mask_rom/mask_rom_epmp.h b/sw/device/silicon_creator/mask_rom/mask_rom_epmp.h new file mode 100644 index 0000000..74dba69 --- /dev/null +++ b/sw/device/silicon_creator/mask_rom/mask_rom_epmp.h
@@ -0,0 +1,64 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef OPENTITAN_SW_DEVICE_SILICON_CREATOR_MASK_ROM_MASK_ROM_EPMP_H_ +#define OPENTITAN_SW_DEVICE_SILICON_CREATOR_MASK_ROM_MASK_ROM_EPMP_H_ + +#include <stdint.h> + +#include "sw/device/silicon_creator/lib/epmp.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +/** + * Mask ROM enhanced Physical Memory Protection (ePMP) library. + * + * The ePMP configuration is managed in two parts: + * + * 1. The actual hardware configuration held in CSRs + * 2. The in-memory copy of register values in `epmp_state_t` that is used + * to verify the CSRs + * + * Every time the hardware configuration is updated the in-memory copy + * must also be updated. The hardware configuration is usually interacted + * with directly using the CSR library or assembly whereas the in-memory + * copy of the state should normally be modified using configuration functions + * from the silicon creator ePMP library. + * + * This separation of concerns allows the hardware configuration to be + * updated efficiently as needed (including before the C runtime is + * initialized) with the in-memory copy of the state used to double check the + * configuration as required. + */ + +/** + * Initialise the ePMP in-memory copy of the register state to reflect the + * hardware configuration expected at entry to the mask ROM C code. + * + * The actual hardware configuration is performed separately, either by reset + * logic or in assembly. This code must be kept in sync with any changes + * to the hardware configuration. + * + * @param[out] state The shadow registers to configure with initial values. + */ +void mask_rom_epmp_state_init(epmp_state_t *state); + +/** + * Unlocks the provided ROM_EXT image region with read-execute permissions. + * + * The provided ePMP state is also updated to reflect the changes made to the + * hardware configuration. + * + * @param state The ePMP state to update. + * @param image Region for executable sections in ROM_EXT image. + */ +void mask_rom_epmp_unlock_rom_ext_rx(epmp_state_t *state, epmp_region_t image); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus + +#endif // OPENTITAN_SW_DEVICE_SILICON_CREATOR_MASK_ROM_MASK_ROM_EPMP_H_
diff --git a/sw/device/silicon_creator/mask_rom/mask_rom_start.S b/sw/device/silicon_creator/mask_rom/mask_rom_start.S index dd3dcd7..651b795 100644 --- a/sw/device/silicon_creator/mask_rom/mask_rom_start.S +++ b/sw/device/silicon_creator/mask_rom/mask_rom_start.S
@@ -224,6 +224,11 @@ .extern crt_section_clear .extern crt_section_copy + .extern mask_rom_epmp_init + .extern mask_rom_epmp_stack_guard_init + + // Must be called prior to any Main RAM access. + call mask_rom_epmp_init // TODO: Setup SRAM Scrambling // Temporarily: Zero out ram_main @@ -231,6 +236,10 @@ li a1, (TOP_EARLGREY_RAM_MAIN_BASE_ADDR + TOP_EARLGREY_RAM_MAIN_SIZE_BYTES) call crt_section_clear + // Must be called after RAM zeroing to avoid triggering stack overflow fault + // as it will write into the `_stack_start` address. + call mask_rom_epmp_stack_guard_init + /** * Setup C Runtime */
diff --git a/sw/device/silicon_creator/mask_rom/meson.build b/sw/device/silicon_creator/mask_rom/meson.build index 9ad384b..9894c6c 100644 --- a/sw/device/silicon_creator/mask_rom/meson.build +++ b/sw/device/silicon_creator/mask_rom/meson.build
@@ -6,6 +6,7 @@ # # See sw/device/exts/common/flash_link.ld for additional info about these # parameters. + rom_linkfile = files(['mask_rom.ld']) rom_link_args = [ '-Wl,-L,@0@'.format(meson.source_root()), @@ -36,6 +37,20 @@ ), ) +# Mask ROM ePMP library +sw_silicon_creator_mask_rom_epmp = declare_dependency( + link_with: static_library( + 'sw_silicon_creator_mask_rom_epmp', + sources: [ + 'mask_rom_epmp.S', + 'mask_rom_epmp.c', + ], + dependencies: [ + sw_silicon_creator_lib_epmp, + ], + ) +) + # MaskROM library. mask_rom_lib = declare_dependency( sources: [ @@ -54,6 +69,7 @@ sw_silicon_creator_lib_fake_deps, sw_silicon_creator_lib_manifest, sw_silicon_creator_lib_shutdown, + sw_silicon_creator_mask_rom_epmp, sw_silicon_creator_mask_rom_sigverify, sw_silicon_creator_mask_rom_romextimage, sw_lib_crt, @@ -63,7 +79,7 @@ link_with: static_library( 'mask_rom_lib', sources: [ - 'mask_rom.c' + 'mask_rom.c', ], link_depends: [rom_linkfile], )
diff --git a/sw/device/silicon_creator/rom_exts/meson.build b/sw/device/silicon_creator/rom_exts/meson.build index 0918377..28806bf 100644 --- a/sw/device/silicon_creator/rom_exts/meson.build +++ b/sw/device/silicon_creator/rom_exts/meson.build
@@ -52,10 +52,17 @@ sw_lib_runtime_hart, sw_lib_runtime_print, sw_silicon_creator_lib_manifest_section, + + # TODO: ePMP test status dependency should be removed from + # production ROM_EXT. Tests should unlock the test status + # address themselves (via a library or similar). + sw_silicon_creator_lib_epmp_test_unlock, ], link_with: static_library( slot + '_rom_ext_lib', - sources: ['rom_ext.c'], + sources: [ + 'rom_ext.c', + ], link_depends: [slot_link_args[1]], ) )
diff --git a/sw/device/silicon_creator/rom_exts/rom_ext.c b/sw/device/silicon_creator/rom_exts/rom_ext.c index f69cccf..806a5d8 100644 --- a/sw/device/silicon_creator/rom_exts/rom_ext.c +++ b/sw/device/silicon_creator/rom_exts/rom_ext.c
@@ -9,6 +9,7 @@ #include "sw/device/lib/dif/dif_uart.h" #include "sw/device/lib/runtime/hart.h" #include "sw/device/lib/runtime/print.h" +#include "sw/device/silicon_creator/lib/epmp_test_unlock.h" #include "hw/top_earlgrey/sw/autogen/top_earlgrey.h" // Generated. @@ -22,6 +23,12 @@ // this function will change to pass the relevant information from // the Mask ROM. void rom_ext_boot(void) { + // Unlock the DV address space so that test results can be written out. + // TODO: move to a test library. + if (!epmp_unlock_test_status(NULL)) { + abort(); + } + dif_uart_result_t init_result = dif_uart_init( (dif_uart_params_t){ .base_addr = mmio_region_from_addr(TOP_EARLGREY_UART0_BASE_ADDR),