Skip to content

Commit

Permalink
[rom_ext] Update the ROM_EXT ePMP configuration
Browse files Browse the repository at this point in the history
To provide owner applications with a more flexible memory protection
configuration, I have re-arranged the ePMP settings.

Notably:
- The lockouts are moved to regions 0 & 1 to have the highest priority.
- The full FLASH/MMIO/RAM windows have been moved to the end to allow
  finer grained controls to be applied in the middle entries.
- The ROM_EXT region is moved and unlocked so owners can reclaim the
  ROM_EXT entries for their own use.
- The owner code regions are left unlocked so owners can select their
  own preferred arrangement.

We jump to owner code with MML=0, allowing the owner to reconfigure the
unlocked entries.  The owner should set MML=1 if they need machine/user
mode isolation.

Signed-off-by: Chris Frantz <[email protected]>
  • Loading branch information
cfrantz committed Jan 18, 2024
1 parent 9fe2120 commit 0f45b38
Show file tree
Hide file tree
Showing 8 changed files with 219 additions and 248 deletions.
24 changes: 22 additions & 2 deletions sw/device/silicon_creator/lib/epmp_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,36 @@ enum {
* W | 1
* X | 2
*
* Combinations not exposed by this enum should not be used. The 'unlocked'
* zero value should only be used for entries that are configured as OFF.
* NOTE: Because the chip is configured with MMWP=1 and MML=0, the ePMP bit
* patterns can sometimes have counterintuitive meanings.
*
* NOTE: After setting MML=1, the meanings of some of the bit patterns will
* change. See section 2.2 of the "PMP Enhancements for memory access and
* execution prevention on Machine mode (Smepmp)" document
* (https://github.com/riscv/riscv-tee/blob/main/Smepmp/Smepmp.pdf).
*/
typedef enum epmp_perm {
kEpmpPermUnlocked = 0,
/** M mode: no access. U mode: no access. */
kEpmpPermLockedNoAccess = EPMP_CFG_L,

/** M mode: read only. U mode: no access. */
kEpmpPermLockedReadOnly = EPMP_CFG_LR,

/** M mode: read/write. U mode: no access. */
kEpmpPermLockedReadWrite = EPMP_CFG_LRW,

/** M mode: read/execute. U mode: no access. */
kEpmpPermLockedReadExecute = EPMP_CFG_LRX,

/** M mode: read/execute. U mode: read/execute. */
kEpmpPermLockedReadWriteExecute = EPMP_CFG_LRWX,

/** M mode: read/write/execute. U mode: read only. */
kEpmpPermReadOnly = EPMP_CFG_R,

/** M mode: read/write/execute. U mode: read/execute. */
kEpmpPermReadExecute = EPMP_CFG_R | EPMP_CFG_X,
} epmp_perm_t;

/**
Expand Down
1 change: 1 addition & 0 deletions sw/device/silicon_creator/rom_ext/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ cc_library(
"//hw/top_earlgrey/sw/autogen:top_earlgrey",
"//sw/device/lib/base:bitfield",
"//sw/device/lib/base:csr",
"//sw/device/lib/base:hardened",
"//sw/device/silicon_creator/lib:epmp_state",
],
)
Expand Down
33 changes: 21 additions & 12 deletions sw/device/silicon_creator/rom_ext/doc/si_val.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,25 +17,34 @@ The ePMP will allow access to the FLASH, to the MMIO region and to SRAM.
An example of how the SiVal `ROM_EXT` will configure the ePMP:

```console
0: 20010400 ----- ---- sz=00000000 ; OWNER code start.
1: 20013d24 TOR LX-R sz=00003924 ; OWNER code end.
2: 00008000 NAPOT L--R sz=00008000 ; OWNER data (if using remap window, else ROM data region)
3: 20000400 ----- ---- sz=00000000 ; ROM_EXT code start.
4: 20004fe8 TOR LX-R sz=00004be8 ; ROM_EXT code end.
5: 20000000 NAPOT L--R sz=00100000 ; FLASH data (1 MB).
6: 40130000 NAPOT L--- sz=00001000 ; OTP MMIO lockout.
7: 40480000 NAPOT L--- sz=00000400 ; AST MMIO lockout.
0: 40130000 NAPOT L--- sz=00001000 ; OTP MMIO lockout.
1: 40480000 NAPOT L--- sz=00000400 ; AST MMIO lockout.
2: 20010400 ----- ---- sz=00000000 ; OWNER code start.
3: 20013cac TOR -X-R sz=000038ac ; OWNER code end.
4: 00000000 ----- ---- sz=00000000 ; OWNER data (if using remap window, else unused).
5: 00000000 ----- ---- sz=00000000
6: 00000000 ----- ---- sz=00000000
7: 00000000 ----- ---- sz=00000000
8: 00000000 ----- ---- sz=00000000
9: 00000000 ----- ---- sz=00000000
10: 00000000 ----- ---- sz=00000000
11: 40000000 NAPOT L-WR sz=10000000 ; MMIO region.
12: 00000000 ----- ---- sz=00000000
10: 20000400 ----- ---- sz=00000000 ; ROM_EXT code start.
11: 20005bc8 TOR -X-R sz=000057c8 ; ROM_EXT code end.
12: 20000000 NAPOT L--R sz=00100000 ; FLASH data (1 MB).
13: 00010000 NAPOT LXWR sz=00001000 ; RvDM region (not PROD, RMA/DEV only).
14: 1001c000 NA4 L--- sz=00000004 ; Stack guard.
14: 40000000 NAPOT L-WR sz=10000000 ; MMIO region.
15: 10000000 NAPOT L-WR sz=00020000 ; RAM region.
mseccfg = 00000002 ; RLB=0, MMWP=1, MML=0.
```

In this configuration, the owner stage can re-arrange ePMP entries
2-11 as needed. Applications that require machine/user mode isolation can set
the MML bit after arranging the ePMP to the preferred configuration.

NOTE: if the ROM\_EXT stage is booted in the virtual slot, then ePMP entries
9/10/11 will be used to map the virtual window's code/data segments. Since
these entries are unlocked _and_ since the ROM\_EXT is no longer requred
after owner code boots, these entries can be reclaimed for owner use.

### OTP

- The OTP creator partition memory window is locked and is unreadable.
Expand Down
8 changes: 0 additions & 8 deletions sw/device/silicon_creator/rom_ext/e2e/lockdown/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,6 @@ opentitan_test(
opentitan_test(
name = "epmp_rlb_lockdown",
srcs = ["epmp_rlb_lockdown.c"],
cw310 = cw310_params(
exit_failure = "(PASS|FAIL|BFV|FAULT).*\r\n",
# Make sure the test program failed to modify the ePMP.
exit_success = (
"6: 40130000 NAPOT L--- sz=00001000\r\n" +
"7: 40480000 NAPOT L--- sz=00000400\r\n"
),
),
exec_env = {
"//hw/top_earlgrey:fpga_cw310_rom_ext": None,
},
Expand Down
16 changes: 10 additions & 6 deletions sw/device/silicon_creator/rom_ext/e2e/lockdown/epmp_rlb_lockdown.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "sw/device/lib/base/csr.h"
#include "sw/device/lib/base/status.h"
#include "sw/device/lib/runtime/log.h"
#include "sw/device/lib/testing/test_framework/check.h"
#include "sw/device/lib/testing/test_framework/ottf_main.h"
#include "sw/device/silicon_creator/lib/dbg_print.h"

Expand All @@ -13,13 +14,16 @@
OTTF_DEFINE_TEST_CONFIG();

bool test_main(void) {
// Try to overwrite ePMP entry 6 (ie: the otp lockout) by clearing its config
// Try to overwrite ePMP entry 0 (ie: the otp lockout) by clearing its config
// bits.
CSR_CLEAR_BITS(CSR_REG_PMPCFG1, 0xff << 16);
CSR_WRITE(CSR_REG_PMPADDR6, 0);
CSR_CLEAR_BITS(CSR_REG_PMPCFG0, 0xff);
CSR_WRITE(CSR_REG_PMPADDR0, 0);

// The test rule in the BUILD file will look for the expected configuration
// for ePMP entry 10.
dbg_print_epmp();
// Read back ePMP entry 0 and make sure it hasn't changed to zero.
uint32_t pmpcfg0, pmpaddr0;
CSR_READ(CSR_REG_PMPCFG0, &pmpcfg0);
CSR_READ(CSR_REG_PMPADDR0, &pmpaddr0);
CHECK((pmpcfg0 & 0xFF) != 0);
CHECK(pmpaddr0 != 0);
return true;
}
95 changes: 83 additions & 12 deletions sw/device/silicon_creator/rom_ext/rom_ext.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,29 @@ extern const char _chip_info_start[];
// Life cycle state of the chip.
lifecycle_state_t lc_state = kLcStateProd;

// ePMP regions for important address spaces.
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 kFlashRegion = {
.start = TOP_EARLGREY_EFLASH_BASE_ADDR,
.end = TOP_EARLGREY_EFLASH_BASE_ADDR + TOP_EARLGREY_EFLASH_SIZE_BYTES,
};

const epmp_region_t kAstRegion = {
.start = TOP_EARLGREY_AST_BASE_ADDR,
.end = TOP_EARLGREY_AST_BASE_ADDR + TOP_EARLGREY_AST_SIZE_BYTES,
};

const epmp_region_t kOtpRegion = {
// We only want to lock out the register, not the memory mapped interface.
// The size of the register space is 0x1000 bytes.
.start = TOP_EARLGREY_OTP_CTRL_CORE_BASE_ADDR,
.end = TOP_EARLGREY_OTP_CTRL_CORE_BASE_ADDR + 0x1000,
};

OT_WARN_UNUSED_RESULT
static rom_error_t rom_ext_irq_error(void) {
uint32_t mcause;
Expand Down Expand Up @@ -286,25 +309,72 @@ static rom_error_t rom_ext_boot(const manifest_t *manifest) {
SEC_MMIO_WRITE_INCREMENT(kFlashCtrlSecMmioCreatorInfoPagesLockdown +
kOtpSecMmioCreatorSwCfgLockDown);

// Reconfigure the ePMP MMIO region to be a NAPOT region, thus freeing
// up a region for OTP DAI lockout.
rom_ext_epmp_mmio_adjust();
// ePMP region 15 gives access to RAM and is already configured correctly.

// Reconfigure the ePMP MMIO region to be NAPOT region 14, thus freeing
// up an ePMP entry for use elsewhere.
rom_ext_epmp_set_napot(14, kMmioRegion, kEpmpPermLockedReadWrite);

// ePMP region 13 allows RvDM access and is already configured correctly.

// 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.
rom_ext_epmp_set_napot(12, kFlashRegion, kEpmpPermLockedReadOnly);
rom_ext_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;

rom_ext_epmp_set_napot(
11, (epmp_region_t){.start = vwindow, .end = vwindow + size},
kEpmpPermReadOnly);
}
rom_ext_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) {
rom_ext_epmp_clear((uint8_t)i);
}

// Use the ePMP to forbid access to the OTP DAI interface.
// TODO(cfrantz): This lockout is for silicon validation testing.
// We want to prevent accidental OTP programming by test programs before we
// commit to a finalized OTP configuration on test chips. Since the OTP
// controller doesn't have a per-boot register lockout, we'll use the ePMP to
// disable access to the programming interface.
rom_ext_epmp_otp_dai_lockout();
rom_ext_epmp_set_napot(0, kOtpRegion, kEpmpPermLockedNoAccess);

// TODO(cfrantz): This lockout is for silicon validation testing.
// We want to prevent access to the AST by test programs before we commit
// to a finalized AST configuration set in OTP.
rom_ext_epmp_ast_lockout();
rom_ext_epmp_set_napot(1, kAstRegion, kEpmpPermLockedNoAccess);
HARDENED_RETURN_IF_ERROR(epmp_state_check());

// Configure address translation, compute the epmp regions and the entry
// point for the virtual address in case the address translation is enabled.
// Otherwise, compute the epmp regions and the entry point for the load
// address.
//
// We'll map the owner code TOR region as ePMP entries 2/3. If using address
// translation, we'll configure ePMP entry 4 as the read-only region.
epmp_region_t text_region = manifest_code_region_get(manifest);
uintptr_t entry_point = manifest_entry_point_get(manifest);
switch (launder32(manifest->address_translation)) {
Expand All @@ -316,10 +386,12 @@ static rom_error_t rom_ext_boot(const manifest_t *manifest) {

// Unlock read-only for the whole rom_ext virtual memory.
HARDENED_RETURN_IF_ERROR(epmp_state_check());
rom_ext_epmp_unlock_owner_stage_r(
rom_ext_epmp_set_napot(
4,
(epmp_region_t){.start = (uintptr_t)_owner_virtual_start_address,
.end = (uintptr_t)_owner_virtual_start_address +
(uintptr_t)_owner_virtual_size});
(uintptr_t)_owner_virtual_size},
kEpmpPermReadOnly);
HARDENED_RETURN_IF_ERROR(epmp_state_check());

// Move the ROM_EXT execution section from the load address to the virtual
Expand All @@ -345,11 +417,10 @@ static rom_error_t rom_ext_boot(const manifest_t *manifest) {
// unlock the ROM_EXT code regions so the next stage can re-use those
// entries and clear RLB to prevent further changes to locked ePMP regions.
HARDENED_RETURN_IF_ERROR(epmp_state_check());
rom_ext_epmp_unlock_owner_stage_rx(text_region);
// We'd normally want to unlock the ROM_EXT regions so they can be re-used by
// OWNER code, however we need to prevent OWNER code from overriding the OTP
// lockout in entry 6.
// rom_ext_epmp_final_cleanup();
rom_ext_epmp_set_tor(2, text_region, kEpmpPermReadExecute);

// Now that we're done reconfiguring the ePMP, we'll clear the RLB bit to
// prevent any modification to locked entries.
rom_ext_epmp_clear_rlb();
HARDENED_RETURN_IF_ERROR(epmp_state_check());

Expand Down
Loading

0 comments on commit 0f45b38

Please sign in to comment.