blob: d12d04fabc503a8c78f2e4fdf363871210a32e6d [file] [log] [blame]
/*
* Copyright 2023 Google LLC
* Copyright lowRISC contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "cheriot-baremetal.h"
#include "flash_ctrl_regs.h"
#include "hw/top_matcha/sw/autogen/top_matcha.h" // Generated.
#include "otp_ctrl_regs.h"
#include "sw/device/lib/arch/device.h"
#include "sw/device/lib/base/abs_mmio.h"
#include "sw/device/lib/base/bitfield.h"
#include "sw/device/lib/base/csr.h"
#include "sw/device/lib/base/mmio.h"
#include "sw/device/lib/dif/dif_base.h"
#include "sw/device/lib/dif/dif_clkmgr.h"
#include "sw/device/lib/dif/dif_flash_ctrl.h"
#include "sw/device/lib/dif/dif_gpio.h"
#include "sw/device/lib/dif/dif_pinmux.h"
#include "sw/device/lib/dif/dif_rstmgr.h"
#include "sw/device/lib/dif/dif_rv_core_ibex.h"
#include "sw/device/lib/dif/dif_uart.h"
#include "sw/device/lib/runtime/hart.h"
#include "sw/device/lib/runtime/log.h"
#include "sw/device/lib/runtime/print.h"
#include "sw/device/lib/spi_flash.h"
#include "sw/device/lib/testing/flash_ctrl_testutils.h"
#include "sw/device/lib/testing/pinmux_testutils.h"
#include "sw/device/lib/testing/test_framework/check.h"
#include "sw/device/lib/testing/test_framework/status.h"
#include "sw/device/lib/testing/test_rom/chip_info.h" // Generated.
#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/retention_sram.h"
#include "sw/device/silicon_creator/lib/manifest.h"
#include "sw/device/silicon_creator/rom/bootstrap.h"
// 2nd-level firmware filename loaded from SPI
#define CHERIOT_FIRMWARE "cheriot-firmware.bin"
// RAM configuration where 2nd-level firmware is loaded
// TODO(sleffler): needs to come from SOT
#define RAM_START 0x10000000
#define RAM_SIZE_BYTES 0x300000
/**
* Type alias for the OTTF entry point.
*
* The entry point address obtained from the OTTF manifest must be cast to a
* pointer to this type before being called.
*/
typedef void ottf_entry_point(void);
static dif_clkmgr_t clkmgr;
static dif_flash_ctrl_state_t flash_ctrl;
static dif_pinmux_t pinmux;
static dif_rstmgr_t rstmgr;
static dif_uart_t uart0;
static dif_rv_core_ibex_t ibex;
// Helper to craft a uintptr_t for a fixed address.
static inline uintptr_t get_mmio_ptr(const uintptr_t root_cap,
const uint32_t base_addr,
const uint32_t size_bytes) {
return cderivecap(root_cap, base_addr, size_bytes,
CHERI_PERM_LOAD | CHERI_PERM_STORE);
}
// Helper to craft an mmio_region_t for a fixed address.
static inline mmio_region_t get_mmio_region(const uintptr_t root_cap,
const uint32_t base_addr,
const uint32_t size_bytes) {
return mmio_region_from_addr(get_mmio_ptr(root_cap, base_addr, size_bytes));
}
// Helper to read a 32-bit value from a fixed address.
static inline uint32_t mmio_read32(const uintptr_t root_cap,
const uint32_t base_addr) {
return abs_mmio_read32(
cderivecap(root_cap, base_addr, sizeof(uint32_t), CHERI_PERM_LOAD));
}
// `test_in_rom = True` tests can override this symbol to provide their own
// rom tests. By default, it simply jumps into the OTTF's flash.
OT_WEAK
bool rom_test_main(const uintptr_t root_cap) {
const uintptr_t otp_addr =
get_mmio_ptr(root_cap, TOP_MATCHA_OTP_CTRL_CORE_BASE_ADDR,
TOP_MATCHA_OTP_CTRL_CORE_SIZE_BYTES);
// Check the otp to see if execute should start
// val: 0xFFFFFFFF
#if defined(OTP_IS_RAM)
uint32_t otp_val = 0xffffffff;
#else
uint32_t otp_val = abs_mmio_read32(otp_addr +
OTP_CTRL_SW_CFG_WINDOW_REG_OFFSET +
OTP_CTRL_PARAM_CREATOR_SW_CFG_ROM_EXEC_EN_OFFSET);
#endif
if (otp_val == 0) {
test_status_set(kTestStatusInBootRomHalt);
// Abort simply forever loops on a wait_for_interrupt;
abort();
}
// Initialize Ibex cpuctrl (contains icache / security feature enablements).
uint32_t cpuctrl_csr;
CSR_READ(CSR_REG_CPUCTRL, &cpuctrl_csr);
// val: 0x1
#if defined(OTP_IS_RAM)
uint32_t cpuctrl_otp_val = 0x1;
#else
uint32_t cpuctrl_otp_val =
abs_mmio_read32(otp_addr +
OTP_CTRL_SW_CFG_WINDOW_REG_OFFSET +
OTP_CTRL_PARAM_CREATOR_SW_CFG_CPUCTRL_OFFSET);
#endif
cpuctrl_csr = bitfield_field32_write(
cpuctrl_csr, (bitfield_field32_t){.mask = 0x3f, .index = 0},
cpuctrl_otp_val);
CSR_WRITE(CSR_REG_CPUCTRL, cpuctrl_csr);
// Initial sec_mmio, required by bootstrap and its dependencies.
sec_mmio_init();
// Configure the pinmux.
CHECK_DIF_OK(
dif_pinmux_init(get_mmio_region(root_cap, TOP_MATCHA_PINMUX_AON_BASE_ADDR,
TOP_MATCHA_PINMUX_AON_SIZE_BYTES),
&pinmux));
pinmux_testutils_init(&pinmux);
CHECK_DIF_OK(
dif_rstmgr_init(get_mmio_region(root_cap, TOP_MATCHA_RSTMGR_AON_BASE_ADDR,
TOP_MATCHA_RSTMGR_AON_SIZE_BYTES),
&rstmgr));
// Initialize the flash.
CHECK_DIF_OK(dif_flash_ctrl_init_state(
&flash_ctrl,
get_mmio_region(root_cap, TOP_MATCHA_FLASH_CTRL_CORE_BASE_ADDR,
TOP_MATCHA_FLASH_CTRL_CORE_SIZE_BYTES)));
CHECK_DIF_OK(dif_flash_ctrl_start_controller_init(&flash_ctrl));
flash_ctrl_testutils_wait_for_init(&flash_ctrl);
CHECK_DIF_OK(
dif_flash_ctrl_set_flash_enablement(&flash_ctrl, kDifToggleEnabled));
// Setup the UART for printing messages to the console.
if (kDeviceType != kDeviceSimDV) {
CHECK_DIF_OK(
dif_uart_init(get_mmio_region(root_cap, TOP_MATCHA_UART0_BASE_ADDR,
TOP_MATCHA_UART0_SIZE_BYTES),
&uart0));
CHECK_DIF_OK(
dif_uart_configure(&uart0, (dif_uart_config_t){
.baudrate = kUartBaudrate,
.clk_freq_hz = kClockFreqPeripheralHz,
.parity_enable = kDifToggleDisabled,
.parity = kDifUartParityEven,
.tx_enable = kDifToggleEnabled,
.rx_enable = kDifToggleEnabled,
}));
base_uart_stdout(&uart0);
}
// Print the chip version information
LOG_INFO("%s", chip_info);
// Skip sram_init for test_rom
dif_rstmgr_reset_info_bitfield_t reset_reasons;
CHECK_DIF_OK(dif_rstmgr_reset_info_get(&rstmgr, &reset_reasons));
// Store the reset reason in retention RAM and clear the register.
volatile retention_sram_t *ret_ram = (volatile retention_sram_t *)cderivecap(
root_cap, TOP_MATCHA_RAM_RET_AON_BASE_ADDR,
TOP_MATCHA_RAM_RET_AON_SIZE_BYTES, CHERI_PERM_STORE);
ret_ram->reset_reasons = reset_reasons;
CHECK_DIF_OK(dif_rstmgr_reset_info_clear(&rstmgr));
// Write 0x54534554 (ASCII: TEST) to the end of the retention SRAM creator
// area to be able to determine the type of ROM in tests.
volatile uint32_t *creator_last_word =
&ret_ram->reserved_creator[ARRAYSIZE(ret_ram->reserved_creator) - 1];
*creator_last_word = TEST_ROM_IDENTIFIER;
// Print the FPGA version-id.
// This is guaranteed to be zero on all non-FPGA implementations.
dif_rv_core_ibex_fpga_info_t fpga;
CHECK_DIF_OK(dif_rv_core_ibex_init(
get_mmio_region(root_cap, TOP_MATCHA_RV_CORE_IBEX_SEC_CFG_BASE_ADDR,
TOP_MATCHA_RV_CORE_IBEX_SEC_CFG_SIZE_BYTES),
&ibex));
CHECK_DIF_OK(dif_rv_core_ibex_read_fpga_info(&ibex, &fpga));
if (fpga != 0) {
LOG_INFO("TestROM:%08x", fpga);
}
// Enable clock jitter if requested.
// The kJitterEnabled symbol defaults to false across all hardware platforms.
// However, in DV simulation, it may be overridden via a backdoor write with
// the plusarg: `+en_jitter=1`.
if (kJitterEnabled) {
CHECK_DIF_OK(dif_clkmgr_init(
get_mmio_region(root_cap, TOP_MATCHA_CLKMGR_AON_BASE_ADDR,
TOP_MATCHA_CLKMGR_AON_SIZE_BYTES),
&clkmgr));
CHECK_DIF_OK(dif_clkmgr_jitter_set_enabled(&clkmgr, kDifToggleEnabled));
LOG_INFO("Jitter is enabled");
}
// Check the otp to see if flash scramble should be enabled.
// val: 0x0
#if defined(OTP_IS_RAM)
otp_val = 0;
#else
otp_val = abs_mmio_read32(otp_addr +
OTP_CTRL_SW_CFG_WINDOW_REG_OFFSET +
OTP_CTRL_PARAM_CREATOR_SW_CFG_FLASH_DATA_DEFAULT_CFG_OFFSET);
#endif
if (otp_val != 0) {
dif_flash_ctrl_region_properties_t default_properties;
CHECK_DIF_OK(dif_flash_ctrl_get_default_region_properties(
&flash_ctrl, &default_properties));
default_properties.scramble_en =
bitfield_field32_read(otp_val, FLASH_CTRL_OTP_FIELD_SCRAMBLING);
default_properties.ecc_en =
bitfield_field32_read(otp_val, FLASH_CTRL_OTP_FIELD_ECC);
default_properties.high_endurance_en =
bitfield_field32_read(otp_val, FLASH_CTRL_OTP_FIELD_HE);
CHECK_DIF_OK(dif_flash_ctrl_set_default_region_properties(
&flash_ctrl, default_properties));
}
// TODO(sleffler): cannot run from eFLASH so this is pointless
const uintptr_t gpio_addr =
get_mmio_ptr(root_cap, TOP_MATCHA_GPIO_BASE_ADDR,
TOP_MATCHA_GPIO_SIZE_BYTES);
if (bootstrap_requested(otp_addr, gpio_addr) == kHardenedBoolTrue) {
// This log statement is used to synchronize the rom and DV testbench
// for specific test cases.
LOG_INFO("Boot strap requested");
const uintptr_t flash_ctrl_addr =
get_mmio_ptr(root_cap, TOP_MATCHA_FLASH_CTRL_CORE_BASE_ADDR,
TOP_MATCHA_FLASH_CTRL_CORE_SIZE_BYTES);
const uintptr_t flash_mem_addr =
get_mmio_ptr(root_cap, TOP_MATCHA_FLASH_CTRL_MEM_BASE_ADDR,
TOP_MATCHA_FLASH_CTRL_MEM_SIZE_BYTES);
const uintptr_t spi_device_addr =
get_mmio_ptr(root_cap, TOP_MATCHA_SPI_DEVICE_BASE_ADDR,
TOP_MATCHA_SPI_DEVICE_BASE_ADDR);
rom_error_t bootstrap_err = bootstrap(flash_ctrl_addr, flash_mem_addr,
spi_device_addr, otp_addr, gpio_addr);
if (bootstrap_err != kErrorOk) {
LOG_ERROR("Bootstrap failed with status code: %08x",
(uint32_t)bootstrap_err);
// Currently the only way to recover is by a hard reset.
test_status_set(kTestStatusFailed);
}
}
CHECK_DIF_OK(
dif_flash_ctrl_set_exec_enablement(&flash_ctrl, kDifToggleEnabled));
// Always select slot A (NB: there is no hw address translation so ignore the
// manifest).
const manifest_t *manifest =
(const manifest_t *)cderivecap(root_cap, TOP_MATCHA_EFLASH_BASE_ADDR,
sizeof(manifest_t), CHERI_PERM_LOAD);
uintptr_t entry_point = manifest_entry_point_get(manifest);
if (entry_point < TOP_MATCHA_EFLASH_BASE_ADDR + sizeof(manifest_t) ||
entry_point >
TOP_MATCHA_EFLASH_BASE_ADDR + TOP_MATCHA_EFLASH_SIZE_BYTES) {
LOG_INFO("Attempting to load from external flash...");
const uintptr_t spi_host_addr =
get_mmio_ptr(root_cap, TOP_MATCHA_SPI_HOST0_BASE_ADDR,
TOP_MATCHA_SPI_HOST0_SIZE_BYTES);
const uintptr_t flash_ctrl_addr =
get_mmio_ptr(root_cap, TOP_MATCHA_FLASH_CTRL_CORE_BASE_ADDR,
TOP_MATCHA_FLASH_CTRL_CORE_SIZE_BYTES);
spi_flash_init(spi_host_addr, flash_ctrl_addr, otp_addr);
// XXX max_mem_addr should not include the stack
#if 0
// NB: cannot run from eFLASH because global caps cannot be represented
// without tagged memory; this is left here in case something changes.
dif_result_t load_result = load_file_from_tar(
CHERIOT_FIRMWARE,
(void*) get_mmio_ptr(root_cap,
TOP_MATCHA_EFLASH_BASE_ADDR, TOP_MATCHA_EFLASH_SIZE_BYTES),
TOP_MATCHA_EFLASH_BASE_ADDR + TOP_MATCHA_EFLASH_SIZE_BYTES);
if (load_result == kDifOk) {
entry_point = manifest_entry_point_get(manifest);
#else
// Load from SPI to RAM.
dif_result_t load_result = load_file_from_tar(
CHERIOT_FIRMWARE,
// TODO(sleffler): get RAM configuration from SOT
(void *)get_mmio_ptr(root_cap, RAM_START, RAM_SIZE_BYTES),
RAM_START + RAM_SIZE_BYTES);
if (load_result == kDifOk) {
// TODO(sleffler): get entry point from manifest
entry_point = 0x10000000;
#endif
} else {
LOG_FATAL("Failed to load program from SPI flash!");
abort();
}
}
// Jump to the OTTF in flash. Within the flash binary, it is the
// responsibily of the OTTF to set up its own stack, and to never return.
// TODO(sleffler): sanitize environment before jump
LOG_INFO("Test ROM complete, jumping to flash (addr: %x)!", entry_point);
((ottf_entry_point *)cderivepcc(entry_point))();
// If the flash image returns, we should abort anyway.
abort();
}
void _boot_start(const uintptr_t root_cap) {
test_status_set(kTestStatusInBootRom);
test_status_set(rom_test_main(root_cap) ? kTestStatusPassed
: kTestStatusFailed);
abort();
}