Skip to content

Commit

Permalink
[imm_rom_ext] Add immutable ROM_EXT ePMP reconfiguration
Browse files Browse the repository at this point in the history
Reconfigure ePMP in immutable ROM_EXT to remove executable permission on the
immutable data segment.

Now, ePMP will be reconfigured to the following layout before jumping to the
mutable ROM_EXT:
```
 *    6: MU_EXT   ----- ----
 *    7: MU_EXT     TOR LX-R
 *    8: IM_EXT   ----- ----
 *    9: IM_EXT     TOR LX-R
 *   10: VIRTUAL  NAPOT L--R
 *   11: STACK      NA4 L---
```

MU_EXT stands for mutable part of rom_ext, and IM_EXT is the immutable part.

Slot 10 will be empty if address translation is not enabled, and stack guard
is added back to slot 11.

Before jumping to Owner SW, all lock bits will be cleared, so all entries can
be recycled by Owner SW.

Owner SW will be placed at the same entries as before (2, 3, 4).

Change-Id: Id9b6cd68cc24a5816d3e3dc8374b5533a7c6eb75
Signed-off-by: Yi-Hsuan Deng <[email protected]>
  • Loading branch information
sasdf committed Dec 17, 2024
1 parent da96d61 commit 9d1f432
Show file tree
Hide file tree
Showing 9 changed files with 339 additions and 75 deletions.
17 changes: 17 additions & 0 deletions sw/device/silicon_creator/imm_rom_ext/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,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",
Expand All @@ -28,3 +29,19 @@ cc_library(
"//sw/device/silicon_creator/rom_ext:rom_ext_manifest",
],
)

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",
],
)
6 changes: 6 additions & 0 deletions sw/device/silicon_creator/imm_rom_ext/imm_rom_ext.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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);
Expand All @@ -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;
}

Expand Down
115 changes: 115 additions & 0 deletions sw/device/silicon_creator/imm_rom_ext/imm_rom_ext_epmp.c
Original file line number Diff line number Diff line change
@@ -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;
}
66 changes: 66 additions & 0 deletions sw/device/silicon_creator/imm_rom_ext/imm_rom_ext_epmp.h
Original file line number Diff line number Diff line change
@@ -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_
10 changes: 10 additions & 0 deletions sw/device/silicon_creator/lib/drivers/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -914,3 +914,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",
],
)
35 changes: 30 additions & 5 deletions sw/device/silicon_creator/lib/drivers/epmp.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}

Expand Down
25 changes: 25 additions & 0 deletions sw/device/silicon_creator/lib/drivers/epmp.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down
Loading

0 comments on commit 9d1f432

Please sign in to comment.