blob: 83123924a9c79c17f43309de1c2a08f82ca6753c [file] [log] [blame]
// 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/rom_ext/rom_ext.h"
#include "sw/device/lib/arch/device.h"
#include "sw/device/lib/base/csr.h"
#include "sw/device/lib/base/macros.h"
#include "sw/device/lib/base/stdasm.h"
#include "sw/device/lib/runtime/hart.h"
#include "sw/device/silicon_creator/lib/base/chip.h"
#include "sw/device/silicon_creator/lib/base/sec_mmio.h"
#include "sw/device/silicon_creator/lib/drivers/flash_ctrl.h"
#include "sw/device/silicon_creator/lib/drivers/hmac.h"
#include "sw/device/silicon_creator/lib/drivers/ibex.h"
#include "sw/device/silicon_creator/lib/drivers/lifecycle.h"
#include "sw/device/silicon_creator/lib/drivers/otp.h"
#include "sw/device/silicon_creator/lib/drivers/pinmux.h"
#include "sw/device/silicon_creator/lib/drivers/uart.h"
#include "sw/device/silicon_creator/lib/epmp_state.h"
#include "sw/device/silicon_creator/lib/manifest.h"
#include "sw/device/silicon_creator/lib/manifest_def.h"
#include "sw/device/silicon_creator/lib/rom_print.h"
#include "sw/device/silicon_creator/lib/shutdown.h"
#include "sw/device/silicon_creator/lib/sigverify/sigverify.h"
#include "sw/device/silicon_creator/rom_ext/rom_ext_boot_policy.h"
#include "sw/device/silicon_creator/rom_ext/rom_ext_epmp.h"
#include "sw/device/silicon_creator/rom_ext/sigverify_keys.h"
#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h" // Generated.
// Life cycle state of the chip.
lifecycle_state_t lc_state = kLcStateProd;
static rom_error_t rom_ext_irq_error(void) {
uint32_t mcause;
CSR_READ(CSR_REG_MCAUSE, &mcause);
// Shuffle the mcause bits into the uppermost byte of the word and report
// the cause as kErrorInterrupt.
// Based on the ibex verilog, it appears that the most significant bit
// indicates whether the cause is an exception (0) or external interrupt (1),
// and the 5 least significant bits indicate which exception/interrupt.
//
// Preserve the MSB and shift the 7 LSBs into the upper byte.
// (we preserve 7 instead of 5 because the verilog hardcodes the unused bits
// as zero and those would be the next bits used should the number of
// interrupt causes increase).
mcause = (mcause & 0x80000000) | ((mcause & 0x7f) << 24);
return kErrorInterrupt + mcause;
}
void rom_ext_init(void) {
sec_mmio_next_stage_init();
lc_state = lifecycle_state_get();
// TODO: Verify ePMP expectations from ROM.
pinmux_init();
// Configure UART0 as stdout.
uart_init(kUartNCOValue);
}
static rom_error_t rom_ext_verify(const manifest_t *manifest) {
RETURN_IF_ERROR(rom_ext_boot_policy_manifest_check(manifest));
const sigverify_rsa_key_t *key;
RETURN_IF_ERROR(sigverify_rsa_key_get(
sigverify_rsa_key_id_get(&manifest->modulus), lc_state, &key));
hmac_sha256_init();
// Hash usage constraints.
manifest_usage_constraints_t usage_constraints_from_hw;
sigverify_usage_constraints_get(manifest->usage_constraints.selector_bits,
&usage_constraints_from_hw);
hmac_sha256_update(&usage_constraints_from_hw,
sizeof(usage_constraints_from_hw));
// Hash the remaining part of the image.
manifest_digest_region_t digest_region = manifest_digest_region_get(manifest);
hmac_sha256_update(digest_region.start, digest_region.length);
// Verify signature
hmac_digest_t act_digest;
hmac_sha256_final(&act_digest);
uint32_t flash_exec = 0;
return sigverify_rsa_verify(&manifest->signature, key, &act_digest, lc_state,
&flash_exec);
}
/* These symbols are defined in
* `opentitan/sw/device/silicon_creator/rom_ext/rom_ext.ld`, and describe the
* location of the flash header.
*/
extern char _owner_virtual_start_address[];
extern char _owner_virtual_size[];
/**
* Compute the virtual address corresponding to the physical address `lma_addr`.
*
* @param manifest Pointer to the current manifest.
* @param lma_addr Load address or physical address.
* @return the computed virtual address.
*/
static uintptr_t owner_vma_get(const manifest_t *manifest, uintptr_t lma_addr) {
return (lma_addr - (uintptr_t)manifest +
(uintptr_t)_owner_virtual_start_address + CHIP_ROM_EXT_SIZE_MAX);
}
static rom_error_t rom_ext_boot(const manifest_t *manifest) {
// Disable access to silicon creator info pages and OTP partitions until next
// reset.
flash_ctrl_creator_info_pages_lockdown();
otp_creator_sw_cfg_lockdown();
SEC_MMIO_WRITE_INCREMENT(kFlashCtrlSecMmioCreatorInfoPagesLockdown +
kOtpSecMmioCreatorSwCfgLockDown);
// 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.
epmp_region_t text_region = manifest_code_region_get(manifest);
uintptr_t entry_point = manifest_entry_point_get(manifest);
switch (launder32(manifest->address_translation)) {
case kHardenedBoolTrue:
HARDENED_CHECK_EQ(manifest->address_translation, kHardenedBoolTrue);
ibex_addr_remap_1_set((uintptr_t)_owner_virtual_start_address,
(uintptr_t)TOP_EARLGREY_EFLASH_BASE_ADDR,
(size_t)_owner_virtual_size);
SEC_MMIO_WRITE_INCREMENT(kAddressTranslationSecMmioConfigure);
// Unlock read-only for the whole rom_ext virtual memory.
HARDENED_RETURN_IF_ERROR(epmp_state_check());
rom_ext_epmp_unlock_owner_stage_r(
(epmp_region_t){.start = (uintptr_t)_owner_virtual_start_address,
.end = (uintptr_t)_owner_virtual_start_address +
(uintptr_t)_owner_virtual_size});
HARDENED_RETURN_IF_ERROR(epmp_state_check());
// Move the ROM_EXT execution section from the load address to the virtual
// address.
// TODO(#13513): Harden these calculations.
text_region.start = owner_vma_get(manifest, text_region.start);
text_region.end = owner_vma_get(manifest, text_region.end);
entry_point = owner_vma_get(manifest, entry_point);
break;
case kHardenedBoolFalse:
HARDENED_CHECK_EQ(manifest->address_translation, kHardenedBoolFalse);
break;
default:
HARDENED_UNREACHABLE();
}
// Unlock execution of owner stage executable code (text) sections.
HARDENED_RETURN_IF_ERROR(epmp_state_check());
rom_ext_epmp_unlock_owner_stage_rx(text_region);
HARDENED_RETURN_IF_ERROR(epmp_state_check());
// Jump to OWNER entry point.
rom_printf("entry: 0x%x\r\n", (unsigned int)entry_point);
((owner_stage_entry_point *)entry_point)();
return kErrorRomBootFailed;
}
static rom_error_t rom_ext_try_boot(void) {
rom_ext_boot_policy_manifests_t manifests =
rom_ext_boot_policy_manifests_get();
rom_error_t error = kErrorRomBootFailed;
for (size_t i = 0; i < ARRAYSIZE(manifests.ordered); ++i) {
error = rom_ext_verify(manifests.ordered[i]);
if (error != kErrorOk) {
continue;
}
// Boot fails if a verified ROM_EXT cannot be booted.
RETURN_IF_ERROR(rom_ext_boot(manifests.ordered[i]));
// `rom_ext_boot()` should never return `kErrorOk`, but if it does
// we must shut down the chip instead of trying the next ROM_EXT.
return kErrorRomBootFailed;
}
return error;
}
void rom_ext_main(void) {
rom_ext_init();
rom_printf("Starting ROM_EXT\r\n");
shutdown_finalize(rom_ext_try_boot());
}
void rom_ext_interrupt_handler(void) { shutdown_finalize(rom_ext_irq_error()); }
// We only need a single handler for all ROM_EXT interrupts, but we want to
// keep distinct symbols to make writing tests easier. In the ROM_EXT,
// alias all interrupt handler symbols to the single handler.
OT_ALIAS("rom_ext_interrupt_handler")
void rom_ext_exception_handler(void);
OT_ALIAS("rom_ext_interrupt_handler")
void rom_ext_nmi_handler(void);