diff --git a/sw/device/silicon_creator/imm_rom_ext/BUILD b/sw/device/silicon_creator/imm_rom_ext/BUILD index faa572d3b35fa..537baf64ed969 100644 --- a/sw/device/silicon_creator/imm_rom_ext/BUILD +++ b/sw/device/silicon_creator/imm_rom_ext/BUILD @@ -14,6 +14,7 @@ cc_library( srcs = ["imm_rom_ext.c"], hdrs = ["imm_rom_ext.h"], deps = [ + ":imm_rom_ext_epmp", "//hw/top_earlgrey/ip_autogen/flash_ctrl:flash_ctrl_c_regs", "//sw/device/lib/arch:device", "//sw/device/lib/base:macros", @@ -34,6 +35,22 @@ cc_library( ], ) +cc_library( + name = "imm_rom_ext_epmp", + srcs = ["imm_rom_ext_epmp.c"], + hdrs = ["imm_rom_ext_epmp.h"], + deps = [ + "//hw/top_earlgrey/sw/autogen:top_earlgrey", + "//sw/device/lib/base:csr", + "//sw/device/lib/base:macros", + "//sw/device/silicon_creator/lib:epmp_state", + "//sw/device/silicon_creator/lib:error", + "//sw/device/silicon_creator/lib:manifest", + "//sw/device/silicon_creator/lib/drivers:epmp", + "//sw/device/silicon_creator/lib/drivers:lifecycle", + ], +) + ld_library( name = "ld_hello_world", script = "hello_world.ld", diff --git a/sw/device/silicon_creator/imm_rom_ext/imm_rom_ext.c b/sw/device/silicon_creator/imm_rom_ext/imm_rom_ext.c index fb6b32dd8279a..de4e896f067f4 100644 --- a/sw/device/silicon_creator/imm_rom_ext/imm_rom_ext.c +++ b/sw/device/silicon_creator/imm_rom_ext/imm_rom_ext.c @@ -6,6 +6,7 @@ #include "sw/device/lib/arch/device.h" #include "sw/device/lib/base/macros.h" +#include "sw/device/silicon_creator/imm_rom_ext/imm_rom_ext_epmp.h" #include "sw/device/silicon_creator/lib/base/boot_measurements.h" #include "sw/device/silicon_creator/lib/base/sec_mmio.h" #include "sw/device/silicon_creator/lib/cert/dice_chain.h" @@ -31,6 +32,8 @@ static rom_error_t imm_rom_ext_start(void) { // Initialize Immutable ROM EXT. sec_mmio_next_stage_init(); + HARDENED_RETURN_IF_ERROR(imm_rom_ext_epmp_reconfigure()); + // Configure UART0 as stdout. pinmux_init_uart0_tx(); uart_init(kUartNCOValue); @@ -52,6 +55,9 @@ static rom_error_t imm_rom_ext_start(void) { // Write the DICE certs to flash if they have been updated. HARDENED_RETURN_IF_ERROR(dice_chain_flush_flash()); + // Make mutable part executable. + HARDENED_RETURN_IF_ERROR(imm_rom_ext_epmp_mutable_rx(rom_ext)); + return kErrorOk; } diff --git a/sw/device/silicon_creator/imm_rom_ext/imm_rom_ext_epmp.c b/sw/device/silicon_creator/imm_rom_ext/imm_rom_ext_epmp.c new file mode 100644 index 0000000000000..4f25738e0c684 --- /dev/null +++ b/sw/device/silicon_creator/imm_rom_ext/imm_rom_ext_epmp.c @@ -0,0 +1,115 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#include "sw/device/silicon_creator/imm_rom_ext/imm_rom_ext_epmp.h" + +#include "sw/device/lib/base/csr.h" +#include "sw/device/lib/base/macros.h" +#include "sw/device/silicon_creator/lib/drivers/epmp.h" +#include "sw/device/silicon_creator/lib/drivers/lifecycle.h" +#include "sw/device/silicon_creator/lib/epmp_state.h" +#include "sw/device/silicon_creator/lib/error.h" +#include "sw/device/silicon_creator/lib/manifest.h" + +#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h" // Generated. + +// Address populated by the linker. +extern char _rom_ext_immutable_start[]; +extern char _rom_ext_immutable_end[]; +extern char _text_end[]; +extern char _stack_start[]; // Lowest stack address. + +static const epmp_region_t kMmioRegion = { + .start = TOP_EARLGREY_MMIO_BASE_ADDR, + .end = TOP_EARLGREY_MMIO_BASE_ADDR + TOP_EARLGREY_MMIO_SIZE_BYTES, +}; + +static const epmp_region_t kFlashRegion = { + .start = TOP_EARLGREY_EFLASH_BASE_ADDR, + .end = TOP_EARLGREY_EFLASH_BASE_ADDR + TOP_EARLGREY_EFLASH_SIZE_BYTES, +}; + +static const epmp_region_t kRvDmRegion = { + .start = TOP_EARLGREY_RV_DM_MEM_BASE_ADDR, + .end = TOP_EARLGREY_RV_DM_MEM_BASE_ADDR + TOP_EARLGREY_RV_DM_MEM_SIZE_BYTES, +}; + +static const epmp_region_t kStackGuard = {.start = (uintptr_t)_stack_start, + .end = (uintptr_t)_stack_start + 4}; + +static const epmp_region_t kImmTextRegion = { + .start = (uintptr_t)_rom_ext_immutable_start, + .end = (uintptr_t)_text_end, +}; + +rom_error_t imm_rom_ext_epmp_reconfigure(void) { + lifecycle_state_t lc_state = lifecycle_state_get(); + + // ePMP region 15 gives read/write access to RAM. + // Leave it unchanged. + + // Reconfigure the ePMP MMIO region to be NAPOT region 14, thus freeing + // up an ePMP entry for use elsewhere. + epmp_set_napot(14, kMmioRegion, kEpmpPermLockedReadWrite); + + // ePMP region 11 protects the stack from overflow. + // This stack guard was in ePMP region 14. + epmp_set_napot(11, kStackGuard, kEpmpPermLockedNoAccess); + + // ePMP region 12 allows RvDM access. + // This RvDM region was in ePMP region 13. + if (lc_state == kLcStateProd || lc_state == kLcStateProdEnd) { + // No RvDM access in Prod states, so we can clear the entry. + epmp_clear(12); + } else { + epmp_set_napot(12, kRvDmRegion, kEpmpPermLockedReadWriteExecute); + } + + // ePMP region 13 gives read access to all of flash for both M and U modes. + // This flash region was in ePMP region 5. + epmp_set_napot(13, kFlashRegion, kEpmpPermLockedReadOnly); + + // Move the ROM_EXT virtual region from entry 6 to 10. + uint32_t virtual_napot; + CSR_READ(CSR_REG_PMPADDR6, &virtual_napot); + epmp_clear(10); + if (virtual_napot) { + epmp_set_napot(10, epmp_decode_napot(virtual_napot), + kEpmpPermLockedReadOnly); + } + + // Clear mutable ROM_EXT entries (8 & 9). + epmp_clear(9); + epmp_clear(8); + + // Immutable ROM_EXT TOR (6 & 7). + epmp_set_tor(6, kImmTextRegion, kEpmpPermLockedReadExecute); + + // Clear entries from 5 ~ 3. + epmp_clear(5); + epmp_clear(4); + epmp_clear(3); + + // 3 ~ 0 are ROM ePMP entries. + // Leave them unchanged. + + HARDENED_RETURN_IF_ERROR(epmp_state_check()); + + return kErrorOk; +} + +rom_error_t imm_rom_ext_epmp_mutable_rx(const manifest_t *manifest) { + // Immutable ROM_EXT TOR (8 & 9). + epmp_region_t mutable_code_region = manifest_code_region_get(manifest); + + // Manifest code_region includes immutable data segment. Move the start + // address to exclude. + mutable_code_region.start = (uintptr_t)_rom_ext_immutable_end; + + epmp_set_tor(8, mutable_code_region, kEpmpPermLockedReadExecute); + + HARDENED_RETURN_IF_ERROR(epmp_state_check()); + + return kErrorOk; +} diff --git a/sw/device/silicon_creator/imm_rom_ext/imm_rom_ext_epmp.h b/sw/device/silicon_creator/imm_rom_ext/imm_rom_ext_epmp.h new file mode 100644 index 0000000000000..974f5be59fed0 --- /dev/null +++ b/sw/device/silicon_creator/imm_rom_ext/imm_rom_ext_epmp.h @@ -0,0 +1,66 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef OPENTITAN_SW_DEVICE_SILICON_CREATOR_IMM_ROM_EXT_IMM_ROM_EXT_EPMP_H_ +#define OPENTITAN_SW_DEVICE_SILICON_CREATOR_IMM_ROM_EXT_IMM_ROM_EXT_EPMP_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "sw/device/silicon_creator/lib/error.h" +#include "sw/device/silicon_creator/lib/manifest.h" + +/** + * Reconfigure ePMP entries to lower priority. + * + * ePMP will be reconfigured to: + * 0: ROM ----- ---- + * 1: ROM TOR LX-R + * 2: ROM NAPOT L--R + * 3: ------- ----- ---- + * 4: ------- ----- ---- + * 5: ------- ----- ---- + * 6: IM_EXT ----- ---- + * 7: IM_EXT TOR LX-R + * 8:[MU_EXT ----- ----] + * 9:[MU_EXT TOR LX-R] + * 10: VIRTUAL NAPOT L--R + * 11: STACK NA4 L--- + * 12: RvDM NAPOT LXWR + * 13: FLASH NAPOT L--R + * 14: MMIO NAPOT L-WR + * 15: RAM NAPOT L-WR + * + * Mutable ROM_EXT segment (8 & 9) won't be configured by this function. + * `imm_rom_ext_epmp_mutable_rx` will configure them when we are ready to + * jump back to ROM. + * + * Entries 6~12 can be recycled in Owner SW stage. + * + * @return The result of the operation. + */ +OT_WARN_UNUSED_RESULT +rom_error_t imm_rom_ext_epmp_reconfigure(void); + +/** + * Configure the Mutable ROM_EXT text segment with read-execute permissions. + * + * 8: MU_EXT ----- ---- + * 9: MU_EXT TOR LX-R + * + * Note: When address translation is enabled, the manifest argument should + * point to the one in the virtual space. + * + * @param manifest Pointer to the rom_ext manifest. + * @return The result of the operation. + */ +OT_WARN_UNUSED_RESULT +rom_error_t imm_rom_ext_epmp_mutable_rx(const manifest_t *manifest); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // OPENTITAN_SW_DEVICE_SILICON_CREATOR_IMM_ROM_EXT_IMM_ROM_EXT_EPMP_H_ diff --git a/sw/device/silicon_creator/lib/drivers/BUILD b/sw/device/silicon_creator/lib/drivers/BUILD index a55827a9d21d9..02599aee58315 100644 --- a/sw/device/silicon_creator/lib/drivers/BUILD +++ b/sw/device/silicon_creator/lib/drivers/BUILD @@ -893,3 +893,13 @@ cc_library( "//sw/device/silicon_creator/lib:epmp_state", ], ) + +cc_test( + name = "epmp_unittest", + srcs = ["epmp_unittest.cc"], + deps = [ + ":epmp", + "//sw/device/silicon_creator/testing:rom_test", + "@googletest//:gtest_main", + ], +) diff --git a/sw/device/silicon_creator/lib/drivers/epmp.c b/sw/device/silicon_creator/lib/drivers/epmp.c index a11f9fb1bb6c1..306df514ad608 100644 --- a/sw/device/silicon_creator/lib/drivers/epmp.c +++ b/sw/device/silicon_creator/lib/drivers/epmp.c @@ -16,7 +16,7 @@ CSR_WRITE(CSR_REG_PMPADDR##addr_reg, pmpaddr); \ CSR_SET_BITS(CSR_REG_PMPCFG##cfg_reg, cfg); -static void epmp_set(uint8_t entry, uint32_t pmpcfg, uint32_t pmpaddr) { +void epmp_set(uint8_t entry, uint32_t pmpcfg, uint32_t pmpaddr) { uint32_t shift = 8 * (entry % 4); uint32_t mask = 0xFFu << shift; uint32_t cfg = (pmpcfg & 0xFFu) << shift; @@ -51,16 +51,41 @@ static void epmp_set(uint8_t entry, uint32_t pmpcfg, uint32_t pmpaddr) { void epmp_clear(uint8_t entry) { epmp_set(entry, kEpmpModeOff, 0); } -void epmp_set_napot(uint8_t entry, epmp_region_t region, epmp_perm_t perm) { - uint32_t length = region.end - region.start; +void epmp_clear_lock_bits(void) { + const uint32_t mask = + ((uint32_t)EPMP_CFG_L << 0 * 8) | ((uint32_t)EPMP_CFG_L << 1 * 8) | + ((uint32_t)EPMP_CFG_L << 2 * 8) | ((uint32_t)EPMP_CFG_L << 3 * 8); + CSR_CLEAR_BITS(CSR_REG_PMPCFG0, mask); + CSR_CLEAR_BITS(CSR_REG_PMPCFG1, mask); + CSR_CLEAR_BITS(CSR_REG_PMPCFG2, mask); + CSR_CLEAR_BITS(CSR_REG_PMPCFG3, mask); + for (int cfgent = 0; cfgent < 4; ++cfgent) { + epmp_state.pmpcfg[cfgent] &= ~mask; + } +} + +uint32_t epmp_encode_napot(epmp_region_t region) { + const uint32_t length = region.end - region.start; // The length must be 4 or more. HARDENED_CHECK_GE(length, 4); // The length must be a power of 2. HARDENED_CHECK_EQ(bitfield_popcount32(length), 1); // The start address must be naturally aligned with length. HARDENED_CHECK_EQ(region.start & (length - 1), 0); - epmp_mode_t mode = length == 4 ? kEpmpModeNa4 : kEpmpModeNapot; - uint32_t addr = (region.start >> 2) | ((length - 1) >> 3); + return (region.start >> 2) | ((length - 1) >> 3); +} + +epmp_region_t epmp_decode_napot(uint32_t pmpaddr) { + uint32_t size = 1 << bitfield_count_trailing_zeroes32(~pmpaddr); + pmpaddr = (pmpaddr & ~(size - 1)) << 2; + size <<= 3; + return (epmp_region_t){.start = pmpaddr, .end = pmpaddr + size}; +} + +void epmp_set_napot(uint8_t entry, epmp_region_t region, epmp_perm_t perm) { + uint32_t addr = epmp_encode_napot(region); + epmp_mode_t mode = + region.end - region.start == 4 ? kEpmpModeNa4 : kEpmpModeNapot; epmp_set(entry, (uint32_t)mode | (uint32_t)perm, addr); } diff --git a/sw/device/silicon_creator/lib/drivers/epmp.h b/sw/device/silicon_creator/lib/drivers/epmp.h index bdabb32ac85d1..25cc3a35eca65 100644 --- a/sw/device/silicon_creator/lib/drivers/epmp.h +++ b/sw/device/silicon_creator/lib/drivers/epmp.h @@ -43,6 +43,31 @@ extern "C" { */ void epmp_clear(uint8_t entry); +/** + * Clear the lock bit in all ePMP entries. + */ +void epmp_clear_lock_bits(void); + +/** + * Encode a start/end address pair to NAPOT address. + * + * The region start must have an alignment consistend with the region size. The + * region size must be a power of two. If either of these conditions is not + * met, this function will fault. + * + * @param region The address region to configure. + * @return The encoded NAPOT address. + */ +uint32_t epmp_encode_napot(epmp_region_t region); + +/** + * Decode a NAPOT address back to start/end address pair. + * + * @param pmpaddr The encoded NAPOT address. + * @return region The decoded start/end address pair. + */ +epmp_region_t epmp_decode_napot(uint32_t pmpaddr); + /** * Configures an ePMP entry for a NAPOT or NA4 region. * diff --git a/sw/device/silicon_creator/lib/drivers/epmp_unittest.cc b/sw/device/silicon_creator/lib/drivers/epmp_unittest.cc new file mode 100644 index 0000000000000..9f6989c8ae04d --- /dev/null +++ b/sw/device/silicon_creator/lib/drivers/epmp_unittest.cc @@ -0,0 +1,64 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#include "sw/device/silicon_creator/lib/drivers/epmp.h" + +#include "gtest/gtest.h" +#include "sw/device/silicon_creator/testing/rom_test.h" + +namespace epmp_unittest { +namespace { + +struct NapotCase { + /** + * Start / end of the NAPOT region. + */ + uint32_t start; + uint32_t end; + /** + * Encoded NAPOT address. + */ + uint32_t encoded; +}; + +class NapotTest : public rom_test::RomTest, + public testing::WithParamInterface {}; + +TEST_P(NapotTest, Codec) { + epmp_region_t region = { + .start = (uintptr_t)GetParam().start, + .end = (uintptr_t)GetParam().end, + }; + EXPECT_EQ(epmp_encode_napot(region), GetParam().encoded); + + epmp_region_t decoded = epmp_decode_napot(GetParam().encoded); + EXPECT_EQ(decoded.start, GetParam().start); + EXPECT_EQ(decoded.end, GetParam().end); +} + +INSTANTIATE_TEST_SUITE_P(AllCases, NapotTest, + testing::Values( + NapotCase{ + .start = 0b1000010100100100101010100000, + .end = 0b1000010100100100101011000000, + .encoded = 0b10000101001001001010101011, + }, + NapotCase{ + .start = 0b101000001101000011100000000000, + .end = 0b101000001101000011100100000000, + .encoded = 0b1010000011010000111000011111, + }, + NapotCase{ + .start = 0b10111111111111111111111111111000, + .end = 0b11000000000000000000000000000000, + .encoded = 0b101111111111111111111111111110, + }, + NapotCase{ + .start = 0b00000000000000000000000000000000, + .end = 0b10000000000000000000000000000000, + .encoded = 0b001111111111111111111111111111, + })); + +} // namespace +} // namespace epmp_unittest diff --git a/sw/device/silicon_creator/rom_ext/rom_ext.c b/sw/device/silicon_creator/rom_ext/rom_ext.c index b50ba4ff08b88..212fe2a797e6f 100644 --- a/sw/device/silicon_creator/rom_ext/rom_ext.c +++ b/sw/device/silicon_creator/rom_ext/rom_ext.c @@ -67,27 +67,6 @@ owner_config_t owner_config; // Owner application keys. owner_application_keyring_t keyring; -// ePMP regions for important address spaces. -const epmp_region_t kRamRegion = { - .start = TOP_EARLGREY_RAM_MAIN_BASE_ADDR, - .end = TOP_EARLGREY_RAM_MAIN_BASE_ADDR + TOP_EARLGREY_RAM_MAIN_SIZE_BYTES, -}; - -const epmp_region_t kMmioRegion = { - .start = TOP_EARLGREY_MMIO_BASE_ADDR, - .end = TOP_EARLGREY_MMIO_BASE_ADDR + TOP_EARLGREY_MMIO_SIZE_BYTES, -}; - -const epmp_region_t kRvDmRegion = { - .start = TOP_EARLGREY_RV_DM_MEM_BASE_ADDR, - .end = TOP_EARLGREY_RV_DM_MEM_BASE_ADDR + TOP_EARLGREY_RV_DM_MEM_SIZE_BYTES, -}; - -const epmp_region_t kFlashRegion = { - .start = TOP_EARLGREY_EFLASH_BASE_ADDR, - .end = TOP_EARLGREY_EFLASH_BASE_ADDR + TOP_EARLGREY_EFLASH_SIZE_BYTES, -}; - OT_WARN_UNUSED_RESULT static rom_error_t rom_ext_irq_error(void) { uint32_t mcause; @@ -158,7 +137,11 @@ static rom_error_t rom_ext_init(boot_data_t *boot_data) { // Configure UART0 as stdout. uart_init(kUartNCOValue); - // TODO: Verify ePMP expectations from ROM. + // Reclaim entries 0 ~ 7 from ROM and IMM_ROM_EXT. + for (int8_t i = 7; i >= 0; --i) { + epmp_clear((uint8_t)i); + } + HARDENED_RETURN_IF_ERROR(epmp_state_check()); // Conditionally patch AST and check that it is in the expected state. HARDENED_RETURN_IF_ERROR(ast_patch(lc_state)); @@ -292,55 +275,8 @@ static rom_error_t rom_ext_boot(const manifest_t *manifest) { SEC_MMIO_WRITE_INCREMENT(kFlashCtrlSecMmioCreatorInfoPagesLockdown + kOtpSecMmioCreatorSwCfgLockDown); - // ePMP region 15 gives read/write access to RAM. - epmp_set_napot(15, kRamRegion, kEpmpPermReadWrite); + epmp_clear_lock_bits(); - // Reconfigure the ePMP MMIO region to be NAPOT region 14, thus freeing - // up an ePMP entry for use elsewhere. - epmp_set_napot(14, kMmioRegion, kEpmpPermReadWrite); - - // ePMP region 13 allows RvDM access. - if (lc_state == kLcStateProd || lc_state == kLcStateProdEnd) { - // No RvDM access in Prod states, so we can clear the entry. - epmp_clear(13); - } else { - epmp_set_napot(13, kRvDmRegion, kEpmpPermReadWriteExecute); - } - - // ePMP region 12 gives read access to all of flash for both M and U modes. - // The flash access was in ePMP region 5. Clear it so it doesn't take - // priority over 12. - epmp_set_napot(12, kFlashRegion, kEpmpPermReadOnly); - epmp_clear(5); - - // Move the ROM_EXT TOR region from entries 3/4/6 to 9/10/11. - // If the ROM_EXT is located in the virtual window, the ROM will have - // configured ePMP entry 6 as the read-only region over the entire - // window. - // - // If not using the virtual window, we move the ROM_EXT TOR region to - // ePMP entries 10/11. - // If using the virtual window, we move the ROM_EXT read-only region to - // ePMP entry 11 and move the TOR region to 9/10. - uint32_t start, end, vwindow; - CSR_READ(CSR_REG_PMPADDR3, &start); - CSR_READ(CSR_REG_PMPADDR4, &end); - CSR_READ(CSR_REG_PMPADDR6, &vwindow); - uint8_t rxindex = 10; - if (vwindow) { - rxindex = 9; - uint32_t size = 1 << bitfield_count_trailing_zeroes32(~vwindow); - vwindow = (vwindow & ~(size - 1)) << 2; - size <<= 3; - - epmp_set_napot(11, (epmp_region_t){.start = vwindow, .end = vwindow + size}, - kEpmpPermReadOnly); - } - epmp_set_tor(rxindex, (epmp_region_t){.start = start << 2, .end = end << 2}, - kEpmpPermReadExecute); - for (int8_t i = (int8_t)rxindex - 1; i >= 0; --i) { - epmp_clear((uint8_t)i); - } HARDENED_RETURN_IF_ERROR(epmp_state_check()); // Configure address translation, compute the epmp regions and the entry