test_rom: initial bancha support Changes in support of booting CHERIoT firmware images on a bancha system, This has been tested only with OTP_IS_RAM and requires the 2nd-level firmware image be loaded from SPI into RAM. NB: 2nd-level firmware images currently lack the expected manifest Specific changes: - many files forked to isolate / simplify adding CHERI support - memory layout is somewhat different per CHERIoT requirements (e.g. r/w data is collected in one ELF section for CGP-relative addressing) - remove PMP usage because CHERIoT does not support it and it's use can be done with CHERI caps - static_critical section size differs from non-CHERI - eflash, flash_ctrl, spi_flash, and bootstrap api's take capabilities for the MMIO regions (using mmio_region_t) instead of crafting pointers from raw addresses - tag the build target with "cheri" so we can filter it out for CI Bypass-Presubmit-Reason: verified as part of topic Change-Id: Iecbb7f9eaedc52a8988e7da9015d89e058f9d844
diff --git a/hw/top_matcha/sw/autogen/BUILD b/hw/top_matcha/sw/autogen/BUILD index cc139ca..29229ae 100644 --- a/hw/top_matcha/sw/autogen/BUILD +++ b/hw/top_matcha/sw/autogen/BUILD
@@ -22,6 +22,11 @@ includes = ["top_matcha_memory.ld"], ) +ld_library( + name = "top_matcha_memory_cheri", + includes = ["top_matcha_memory_cheri.ld"], +) + filegroup( name = "all_files", srcs = glob(["**"]),
diff --git a/hw/top_matcha/sw/autogen/top_matcha_memory_cheri.ld b/hw/top_matcha/sw/autogen/top_matcha_memory_cheri.ld new file mode 100644 index 0000000..d2fbb4b --- /dev/null +++ b/hw/top_matcha/sw/autogen/top_matcha_memory_cheri.ld
@@ -0,0 +1,41 @@ +/* Copyright lowRISC contributors. */ +/* Licensed under the Apache License, Version 2.0, see LICENSE for details. */ +/* SPDX-License-Identifier: Apache-2.0 */ + +/** + * Partial linker script for chip memory configuration with CHERIoT. + * NB: ROM is super-sized (for now) to deal with bloat + * NB: 2nd-level boot goes to RAM because eFLASH is untagged + * NB: RAM is partitioned into: + * ram_rom - memory used by the boot rom (e.g. data, bss, stack) + * ram_main - loaded 2nd-level firmware + */ +MEMORY { + ram_ret_aon(rwx) : ORIGIN = 0x40600000, LENGTH = 0x1000 + eflash(rx) : ORIGIN = 0x20000000, LENGTH = 0x100000 + ram_main(rwx) : ORIGIN = 0x10000000, LENGTH = 0x300000 + ram_rom(rw) : ORIGIN = 0x10300000, LENGTH = 0x100000 + rom(rx) : ORIGIN = 0x00008000, LENGTH = 0xb000 + ram_ml_dmem(rwx) : ORIGIN = 0x5A000000, LENGTH = 0x400000 +} + +/** + * Stack at the top of ram_rom. + */ +_stack_size = 16384; +_stack_end = ORIGIN(ram_rom) + LENGTH(ram_rom); +_stack_start = _stack_end - _stack_size; + +/** + * Size of the `.static_critical` section at the bottom of the main SRAM (in + * bytes). + * NB: non-CHERIoT is 8132 + */ +_static_critical_size = 8312; + +/** + * `.chip_info` at the top of ROM. + */ +_chip_info_size = 128; +_chip_info_end = ORIGIN(rom) + LENGTH(rom); +_chip_info_start = _chip_info_end - _chip_info_size;
diff --git a/sw/device/examples/hello_world_multicore/hello_world_multicore_sc_loaders_extflash.c b/sw/device/examples/hello_world_multicore/hello_world_multicore_sc_loaders_extflash.c index 50b2dd7..765e3f5 100644 --- a/sw/device/examples/hello_world_multicore/hello_world_multicore_sc_loaders_extflash.c +++ b/sw/device/examples/hello_world_multicore/hello_world_multicore_sc_loaders_extflash.c
@@ -32,4 +32,12 @@ (TOP_MATCHA_ML_TOP_DMEM_BASE_ADDR + TOP_MATCHA_RAM_ML_DMEM_SIZE_BYTES))); } -void load_init(void) { spi_flash_init(); } +void load_init(void) { + const mmio_region_t spi_host_addr = + mmio_region_from_addr(TOP_MATCHA_SPI_HOST0_BASE_ADDR); + const mmio_region_t eflash_addr = + mmio_region_from_addr(TOP_MATCHA_FLASH_CTRL_CORE_BASE_ADDR); + const mmio_region_t otp_addr = + mmio_region_from_addr(TOP_MATCHA_OTP_CTRL_CORE_BASE_ADDR); + spi_flash_init(spi_host_addr, eflash_addr, otp_addr); +}
diff --git a/sw/device/lib/eflash.c b/sw/device/lib/eflash.c index 579616a..ff07bc4 100644 --- a/sw/device/lib/eflash.c +++ b/sw/device/lib/eflash.c
@@ -25,10 +25,9 @@ static dif_flash_ctrl_state_t flash_ctrl; -dif_result_t eflash_init(void) { - CHECK_DIF_OK(dif_flash_ctrl_init_state( - &flash_ctrl, - mmio_region_from_addr(TOP_MATCHA_FLASH_CTRL_CORE_BASE_ADDR))); +dif_result_t eflash_init(mmio_region_t base_addr, mmio_region_t otp_addr) { + CHECK_DIF_OK(dif_flash_ctrl_init_state(&flash_ctrl, base_addr)); + flash_ctrl_init(base_addr, otp_addr); return kDifOk; }
diff --git a/sw/device/lib/eflash.h b/sw/device/lib/eflash.h index 0161629..e4c6a2a 100644 --- a/sw/device/lib/eflash.h +++ b/sw/device/lib/eflash.h
@@ -20,6 +20,7 @@ #include <stdint.h> #include "sw/device/lib/base/macros.h" +#include "sw/device/lib/base/mmio.h" #include "sw/device/lib/dif/dif_base.h" #define EFLASH_PAGE_SIZE (256) @@ -28,7 +29,8 @@ extern "C" { #endif -OT_WARN_UNUSED_RESULT dif_result_t eflash_init(void); +OT_WARN_UNUSED_RESULT dif_result_t eflash_init(mmio_region_t base_addr, + mmio_region_t otp_addr); OT_WARN_UNUSED_RESULT dif_result_t eflash_chip_erase(void); OT_WARN_UNUSED_RESULT dif_result_t eflash_write_page(const void* dst, uint8_t* src);
diff --git a/sw/device/lib/spi_flash.c b/sw/device/lib/spi_flash.c index 26eae0a..f80c508 100644 --- a/sw/device/lib/spi_flash.c +++ b/sw/device/lib/spi_flash.c
@@ -70,16 +70,16 @@ return kDifOk; } -dif_result_t spi_flash_init(void) { - CHECK_DIF_OK(dif_spi_host_init( - mmio_region_from_addr(TOP_MATCHA_SPI_HOST0_BASE_ADDR), &spi_host0)); +dif_result_t spi_flash_init(mmio_region_t spi_host_addr, + mmio_region_t eflash_addr, mmio_region_t otp_addr) { + CHECK_DIF_OK(dif_spi_host_init(spi_host_addr, &spi_host0)); dif_spi_host_config_t config = { .spi_clock = kClockFreqSpiFlashHz, .peripheral_clock_freq_hz = kClockFreqCpuHz, }; CHECK_DIF_OK(dif_spi_host_configure(&spi_host0, config)); CHECK_DIF_OK(dif_spi_host_output_set_enabled(&spi_host0, /*enabled=*/true)); - CHECK_DIF_OK(eflash_init()); + CHECK_DIF_OK(eflash_init(eflash_addr, otp_addr)); return kDifOk; }
diff --git a/sw/device/lib/spi_flash.h b/sw/device/lib/spi_flash.h index 90fd77d..e29c69f 100644 --- a/sw/device/lib/spi_flash.h +++ b/sw/device/lib/spi_flash.h
@@ -20,6 +20,7 @@ #include <stdint.h> +#include "sw/device/lib/base/mmio.h" #include "sw/device/lib/dif/dif_base.h" #define SPI_PAGE_SIZE (512) @@ -29,7 +30,8 @@ #endif /* General SPI flash related methods */ -dif_result_t spi_flash_init(void); +dif_result_t spi_flash_init(mmio_region_t spi_host_addr, + mmio_region_t eflash_addr, mmio_region_t otp_addr); dif_result_t spi_flash_read_page(uint32_t page, uint8_t* buf); /* Tar filesystem related methods */
diff --git a/sw/device/lib/testing/test_rom/BUILD b/sw/device/lib/testing/test_rom/BUILD index 83f3c27..4458399 100644 --- a/sw/device/lib/testing/test_rom/BUILD +++ b/sw/device/lib/testing/test_rom/BUILD
@@ -42,6 +42,7 @@ deps = [ ":linker_script", ":test_rom_lib", + "@lowrisc_opentitan//sw/device/lib/crt", "//sw/device/silicon_creator/rom:bootstrap_no_otp", ], ) @@ -54,6 +55,7 @@ ], deps = [ ":test_rom_lib", + "@lowrisc_opentitan//sw/device/lib/crt", "//sw/device/silicon_creator/rom:bootstrap", ], ) @@ -82,7 +84,6 @@ "@lowrisc_opentitan//sw/device/lib/base:abs_mmio", "@lowrisc_opentitan//sw/device/lib/base:bitfield", "@lowrisc_opentitan//sw/device/lib/base:mmio", - "@lowrisc_opentitan//sw/device/lib/crt", "@lowrisc_opentitan//sw/device/lib/dif:clkmgr", "@lowrisc_opentitan//sw/device/lib/dif:flash_ctrl", "@lowrisc_opentitan//sw/device/lib/dif:gpio", @@ -163,3 +164,52 @@ "@lowrisc_opentitan//sw/device/silicon_creator/lib/drivers:flash_ctrl", ], ) + +# Only build for CHERIoT targets. Requires that bazel be invoked with +# --config=cheriot-baremetal --copt=-D_CHERIOT_BAREMETAL_=1 +# so that all dependencies use those options. +opentitan_rom_binary( + name = "test_rom_no_otp_cheri", + visibility = ["//visibility:private"], + # NB: fpga_nexus is used for renode sims + per_device_deps = { + "fpga_nexus": [NEXUS_CORE_TARGETS.get("secure_core")], + }, + srcs = [ + "test_rom_cheri.c", + "test_rom_start_cheri.S", + # XXX move to opentitan/sw/device/lib/crt? + "crt_cheri.S", + ], + defines = ["OTP_IS_RAM"], + deps = [ + ":linker_script_cheri", + ":baremetal_lib", + ":test_rom_lib", + "//sw/device/silicon_creator/rom:bootstrap_no_otp", + ], + tags = [ + "cheri", + ], +) + +cc_library( + name = "baremetal_lib", + hdrs = [ + "cheriot-baremetal.h", + ], +) + +ld_library( + name = "linker_script_cheri", + visibility = ["//visibility:private"], + script = "test_rom_cheri.ld", + deps = [ + "//hw/top_matcha/sw/autogen:top_matcha_memory_cheri", + "@lowrisc_opentitan//sw/device:info_sections", + "@lowrisc_opentitan//sw/device/silicon_creator/lib/base:static_critical_sections", + ], + tags = [ + "cheri", + ], +)
diff --git a/sw/device/lib/testing/test_rom/cheriot-baremetal.h b/sw/device/lib/testing/test_rom/cheriot-baremetal.h new file mode 100644 index 0000000..38bed19 --- /dev/null +++ b/sw/device/lib/testing/test_rom/cheriot-baremetal.h
@@ -0,0 +1,90 @@ +/* + * Copyright 2024 Google LLC + * + * 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. + */ +#ifndef SW_DEVICE_LIB_TESTING_TEST_ROM_CHERIOT_BAREMETAL_H_ +#define SW_DEVICE_LIB_TESTING_TEST_ROM_CHERIOT_BAREMETAL_H_ + +// XXX just make this a noop? +#ifndef _CHERIOT_BAREMETAL_ +#error "This file is only useful when compiling for baremetal" +#endif + +#include <stddef.h> +#include <stdint.h> + +#define CHERI_PERM_GLOBAL (1U << 0) +#define CHERI_PERM_LOAD_GLOBAL (1U << 1) +#define CHERI_PERM_STORE (1U << 2) +#define CHERI_PERM_LOAD_MUTABLE (1U << 3) +#define CHERI_PERM_STORE_LOCAL (1U << 4) +#define CHERI_PERM_LOAD (1U << 5) +#define CHERI_PERM_LOAD_STORE_CAP (1U << 6) +#define CHERI_PERM_ACCESS_SYS (1U << 7) +#define CHERI_PERM_EXECUTE (1U << 8) +#define CHERI_PERM_UNSEAL (1U << 9) +#define CHERI_PERM_SEAL (1U << 10) +#define CHERI_PERM_USER0 (1U << 11) + +#define cgetlen(foo) __builtin_cheri_length_get(foo) +#define cgetperms(foo) __builtin_cheri_perms_get(foo) +#define cgettype(foo) __builtin_cheri_type_get(foo) +#define cgettag(foo) __builtin_cheri_tag_get(foo) +#define cgetoffset(foo) __builtin_cheri_offset_get(foo) +#define csetoffset(a, b) __builtin_cheri_offset_set((a), (b)) +#define cincoffset(a, b) __builtin_cheri_offset_increment((a), (b)) +#define cgetaddr(a) __builtin_cheri_address_get(a) +#define csetaddr(a, b) __builtin_cheri_address_set((a), (b)) +#define cgetbase(foo) __builtin_cheri_base_get(foo) +#define candperms(a, b) __builtin_cheri_perms_and((a), (b)) +#define cseal(a, b) __builtin_cheri_seal((a), (b)) +#define cunseal(a, b) __builtin_cheri_unseal((a), (b)) +#define csetbounds(a, b) __builtin_cheri_bounds_set((a), (b)) +#define csetboundsext(a, b) __builtin_cheri_bounds_set_exact((a), (b)) +#define ccheckperms(a, b) __builtin_cheri_perms_check((a), (b)) +#define cchecktype(a, b) __builtin_cheri_type_check((a), (b)) +#define cbuildcap(a, b) __builtin_cheri_cap_build((a), (b)) +#define ccopytype(a, b) __builtin_cheri_cap_type_copy((a), (b)) +#define ccseal(a, b) __builtin_cheri_conditional_seal((a), (b)) +#define cequalexact(a, b) __builtin_cheri_equal_exact((a), (b)) + +// Derives a capability from the root cap for doing MMIO to a +// device at a fixed address. +static inline uintptr_t cderivecap(const uintptr_t root_cap, + const uint32_t paddr, + const size_t size_bytes, + const uint32_t perms) { + return candperms( + csetbounds( + csetaddr(root_cap, paddr), + size_bytes), + perms); +} + +// Constructs a capability for invoking a function at the specified +// address using mtcc. This is intended only for passing control to +// the next stage firmware. +static inline uintptr_t cderivepcc(const uintptr_t paddr) { + volatile uintptr_t ret; + uint32_t paddr_temp; + __asm( + "cgetaddr %1, %2\n" + "cspecialr %0, mtcc\n" + "csetaddr %0, %0, %1\n" + : "=C"(ret), "=&r"(paddr_temp) + : "C"(paddr)); + return ret; +} + +#endif // SW_DEVICE_LIB_TESTING_TEST_ROM_CHERIOT_BAREMETAL_H_
diff --git a/sw/device/lib/testing/test_rom/crt_cheri.S b/sw/device/lib/testing/test_rom/crt_cheri.S new file mode 100644 index 0000000..fe908f9 --- /dev/null +++ b/sw/device/lib/testing/test_rom/crt_cheri.S
@@ -0,0 +1,143 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +/** + * CHERI CRT library + * + * Utility functions written in assembly that can be used before the C + * runtime has been initialized. These functions should not be used once + * the C runtime has been initialized. + * + * The name of this library is historical. In many toolchains, a file called + * "crt0.o" is linked into each executable, which does something similar to + * what these functions + */ + + // NOTE: The "ax" flag below is necessary to ensure that this section + // is allocated space in ROM by the linker. + .section .crt, "ax", @progbits + + /** + * Write zeros into the section bounded by the start and end pointers. + * The section must be word (4 byte) aligned. It is valid for the section + * to have a length of 0 (i.e. the start and end pointers may be equal). + * + * This function follows the standard ILP32 calling convention for arguments + * but does not require a valid stack pointer, thread pointer or global + * pointer. + * + * Clobbers a0 and t0. + * + * @param a0 pointer to start of section to clear (inclusive). + * @param a1 pointer to end of section to clear (exclusive). + */ + .balign 4 + .global crt_section_clear + .type crt_section_clear, @function +crt_section_clear: + + // Check that start is before end. + bgeu a0, a1, .L_clear_nothing + + // Check that start and end are word aligned. + or t0, a0, a1 + andi t0, t0, 0x3 + bnez t0, .L_clear_error + +.L_clear_loop: + // Write zero into section memory word-by-word. + // TODO: unroll and/or uses csc + csw zero, 0(ca0) +// addi a0, a0, 4 + cincoffsetimm ca0, ca0, 4 + bltu a0, a1, .L_clear_loop + cret + +.L_clear_nothing: + // If section length is 0 just return. Otherwise end is before start + // which is invalid so trigger an error. + bne a0, a1, .L_clear_error + cret + +.L_clear_error: + unimp + + // Set function size to allow disassembly. + .size crt_section_clear, .-crt_section_clear + +// ----------------------------------------------------------------------------- + + /** + * Copy data from the given source into the section bounded by the start and + * end pointers. Both the section and the source must be word (4 byte) aligned. + * It is valid for the section to have a length of 0 (i.e. the start and end + * pointers may be equal). The source is assumed to have the same length as the + * target section. + * + * The destination section and the source must not overlap. + * + * This function follows the standard ILP32 calling convention for arguments + * but does not require a valid stack pointer, thread pointer or global + * pointer. + * + * Clobbers a0, a2, t0 and t1. + * + * @param a0 pointer to start of destination section (inclusive). + * @param a1 pointer to end of destination section (exclusive). + * @param a2 pointer to source data (inclusive). + */ + .balign 4 + .global crt_section_copy + .type crt_section_copy, @function +crt_section_copy: + + // Check that start is before end. + bgeu a0, a1, .L_copy_nothing + + // Check that start, end and src are word aligned. + or t0, a0, a1 + or t0, t0, a2 + andi t0, t0, 0x3 + bnez t0, .L_copy_error + + // Check that source does not destructively overlap destination + // (assuming a forwards copy). + // + // src start end + // | | | + // +-------------+ | + // | source | | + // +-------------+ | + // +-------------+ + // | destination | + // +-------------+ + // | | + // start end + // + // TODO: disallow all overlap since it indicates API misuse? + sub t0, a0, a2 // (start - src) mod 2**32 + sub t1, a1, a0 // end - start + bltu t0, t1, .L_copy_error + +.L_copy_loop: + // Copy data from src into section word-by-word. + // TODO: unroll + clw t0, 0(ca2) +// addi a2, a2, 4 + cincoffsetimm ca2, ca2, 4 + csw t0, 0(ca0) +// addi a0, a0, 4 + cincoffsetimm ca0, ca0, 4 + bltu a0, a1, .L_copy_loop + cret + +.L_copy_nothing: + // If section length is 0 just return. Otherwise end is before start + // which is invalid so trigger an error. + bne a0, a1, .L_copy_error + cret + +.L_copy_error: + unimp + .size crt_section_copy, .-crt_section_copy
diff --git a/sw/device/lib/testing/test_rom/test_rom.c b/sw/device/lib/testing/test_rom/test_rom.c index 9c262ba..d95b6a5 100644 --- a/sw/device/lib/testing/test_rom/test_rom.c +++ b/sw/device/lib/testing/test_rom/test_rom.c
@@ -162,7 +162,8 @@ 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 = retention_sram_get(); + volatile retention_sram_t *ret_ram = + (volatile retention_sram_t *)TOP_MATCHA_RAM_RET_AON_BASE_ADDR; ret_ram->reset_reasons = reset_reasons; CHECK_DIF_OK(dif_rstmgr_reset_info_clear(&rstmgr)); @@ -216,12 +217,18 @@ CHECK_DIF_OK(dif_flash_ctrl_set_default_region_properties( &flash_ctrl, default_properties)); } - if (bootstrap_requested() == kHardenedBoolTrue) { + const mmio_region_t otp_addr = + mmio_region_from_addr(TOP_MATCHA_OTP_CTRL_CORE_BASE_ADDR); + const mmio_region_t gpio_addr = + mmio_region_from_addr(TOP_MATCHA_GPIO_BASE_ADDR); + 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"); - rom_error_t bootstrap_err = bootstrap(); + const mmio_region_t spi_device_addr = + mmio_region_from_addr(TOP_MATCHA_SPI_DEVICE_BASE_ADDR); + rom_error_t bootstrap_err = bootstrap(otp_addr, gpio_addr, spi_device_addr); if (bootstrap_err != kErrorOk) { LOG_ERROR("Bootstrap failed with status code: %08x", (uint32_t)bootstrap_err); @@ -240,7 +247,11 @@ entry_point > TOP_MATCHA_EFLASH_BASE_ADDR + TOP_MATCHA_EFLASH_SIZE_BYTES) { LOG_INFO("Attempting to load from external flash..."); - spi_flash_init(); + const mmio_region_t spi_host_addr = + mmio_region_from_addr(TOP_MATCHA_SPI_HOST0_BASE_ADDR); + const mmio_region_t eflash_addr = + mmio_region_from_addr(TOP_MATCHA_FLASH_CTRL_CORE_BASE_ADDR); + spi_flash_init(spi_host_addr, eflash_addr, otp_addr); dif_result_t load_result = load_file_from_tar( "matcha-tock-bundle.bin", (void *)TOP_MATCHA_EFLASH_BASE_ADDR, TOP_MATCHA_EFLASH_BASE_ADDR + TOP_MATCHA_EFLASH_SIZE_BYTES);
diff --git a/sw/device/lib/testing/test_rom/test_rom_cheri.c b/sw/device/lib/testing/test_rom/test_rom_cheri.c new file mode 100644 index 0000000..fe7e7bc --- /dev/null +++ b/sw/device/lib/testing/test_rom/test_rom_cheri.c
@@ -0,0 +1,328 @@ +/* + * 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 an mmio_region_t for 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(cderivecap(root_cap, base_addr, size_bytes, + CHERI_PERM_LOAD | CHERI_PERM_STORE)); +} + +// 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) { + // 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 = mmio_read32( + root_cap, TOP_MATCHA_OTP_CTRL_CORE_BASE_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 = + mmio_read32(root_cap, TOP_MATCHA_OTP_CTRL_CORE_BASE_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 = mmio_read32( + root_cap, + TOP_MATCHA_OTP_CTRL_CORE_BASE_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 mmio_region_t otp_addr = + get_mmio_region(root_cap, TOP_MATCHA_OTP_CTRL_CORE_BASE_ADDR, + TOP_MATCHA_OTP_CTRL_CORE_SIZE_BYTES); + const mmio_region_t gpio_addr = get_mmio_region( + 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 mmio_region_t spi_device_addr = + get_mmio_region(root_cap, TOP_MATCHA_SPI_DEVICE_BASE_ADDR, + TOP_MATCHA_SPI_DEVICE_BASE_ADDR); + rom_error_t bootstrap_err = bootstrap(otp_addr, gpio_addr, spi_device_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 mmio_region_t spi_host_addr = + get_mmio_region(root_cap, TOP_MATCHA_SPI_HOST0_BASE_ADDR, + TOP_MATCHA_SPI_HOST0_SIZE_BYTES); + const mmio_region_t eflash_addr = + get_mmio_region(root_cap, TOP_MATCHA_FLASH_CTRL_CORE_BASE_ADDR, + TOP_MATCHA_FLASH_CTRL_CORE_SIZE_BYTES); + spi_flash_init(spi_host_addr, eflash_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*) cderivecap(root_cap, + TOP_MATCHA_EFLASH_BASE_ADDR, TOP_MATCHA_EFLASH_SIZE_BYTES, + CHERI_PERM_LOAD | CHERI_PERM_STORE), + 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 memory configuration from SOT + (void *)cderivecap(root_cap, RAM_START, RAM_SIZE_BYTES, + CHERI_PERM_LOAD | CHERI_PERM_STORE), + 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(); +}
diff --git a/sw/device/lib/testing/test_rom/test_rom_cheri.ld b/sw/device/lib/testing/test_rom/test_rom_cheri.ld new file mode 100644 index 0000000..f55e26a --- /dev/null +++ b/sw/device/lib/testing/test_rom/test_rom_cheri.ld
@@ -0,0 +1,140 @@ +/* Copyright 2023 Google LLC. */ +/* Copyright lowRISC contributors. */ +/* Licensed under the Apache License, Version 2.0, see LICENSE for details. */ +/* SPDX-License-Identifier: Apache-2.0 */ + +/** + * Linker script for an OpenTitan (test) boot ROM on CHERIoT. + */ + +OUTPUT_ARCH(riscv) + +/** + * Indicate that there are no dynamic libraries, whatsoever. + */ +__DYNAMIC = 0; + +INCLUDE hw/top_matcha/sw/autogen/top_matcha_memory_cheri.ld + +/** + * The boot address, which indicates the location of the initial interrupt + * vector. + */ +_boot_address = ORIGIN(rom); + +/** + * Symbols to be used in the setup of the address translation for ROM_EXT. + */ + +_rom_digest_size = 32; +/*_chip_info_start = ORIGIN(rom) + LENGTH(rom) - _rom_digest_size - _chip_info_size;*/ + +/* DV Log offset (has to be different to other boot stages). */ +_dv_log_offset = 0x0; + +/** + * We define an entry point only for documentation purposes (and to stop LLD + * erroring). In reality, we don't use this information within the ROM image, as + * we start at a fixed offset. + */ +ENTRY(_reset_start); + +/** + * NOTE: We have to align each section to word boundaries as our current + * s19->slm conversion scripts are not able to handle non-word aligned sections. + */ +SECTIONS { + /** + * Ibex interrupt vector. See test_rom_start.S for more information. + * + * This has to be set up at the boot address, so that execution jumps to the + * reset handler correctly. + * XXX not used on CHERIoT where only direct mode is supported + */ + .vectors _boot_address : ALIGN(4) { + KEEP(*(.vectors)) + } > rom + /** + * Standard text section, containing program code. + */ + .text : ALIGN(4) { + *(.text) + *(.text.*) + *(.crt) /* currently in crt_cheri.S, could be merged */ + + /** + * Read-only data section, containing all large compile-time constants, like + * strings. Note this goes inside the output text segment to get pcc-relative + * addressing; otherwise it is considered global DATA and cgp-relative + * addressing is generated. + */ + /* Small read-only data comes before regular read-only data for the same + * reasons as in the data section */ + *(.srodata) + *(.srodata.*) + *(.rodata) + *(.rodata.*) + + /** + * Immutable chip_info data, containing build-time-recorded information. + */ + (*(.chip_info)) + } > rom + + /** + * Standard mutable data section, at the bottom of RAM. This is + * initialized from the .idata section at runtime by the CRT. + */ + .data : ALIGN(4) { + _data_start = .; + _data_init_start = LOADADDR(.data); + + /** + * Critical static data. + * NB: want only in RAM but this data needs to be in the same + * segment as other data to be addressable through cgp + */ + KEEP(*(.static_critical.boot_measurements)) + KEEP(*(.static_critical.epmp_state)) + KEEP(*(.static_critical.sec_mmio_ctx)) + + /* Small data should come before larger data. This helps to ensure small + * globals are within 2048 bytes of the value of `gp`, making their accesses + * hopefully only take one instruction. */ + *(.sdata) + *(.sdata.*) + + /* Other data will likely need multiple instructions to load, so we're less + * concerned about address materialisation taking more than one instruction. + */ + *(.data) + *(.data.*) + + /* Ensure section end is word-aligned. */ + . = ALIGN(4); + _data_end = .; + _data_init_end = LOADADDR(.data) + SIZEOF(.data); + + _bss_start = .; + /* Small BSS comes before regular BSS for the same reasons as in the data + * section */ + *(.sbss) + *(.sbss.*) + *(.bss) + *(.bss.*) + . = ALIGN(4); + _bss_end = .; + + /* This puts it in ram_main at runtime (for the VMA), but puts the section + * into rom for load time (for the LMA). This is why `_data_init_*` uses + * `LOADADDR`. */ + } > ram_rom AT> rom + + /** + * Discard capability relocation data. There are no global caps + * to be relocated. XXX need to undestand where these are coming from + */ + /DISCARD/ : { *(__cap_relocs) } + + INCLUDE external/lowrisc_opentitan/sw/device/info_sections.ld +}
diff --git a/sw/device/lib/testing/test_rom/test_rom_start_cheri.S b/sw/device/lib/testing/test_rom/test_rom_start_cheri.S new file mode 100644 index 0000000..4949f9e --- /dev/null +++ b/sw/device/lib/testing/test_rom/test_rom_start_cheri.S
@@ -0,0 +1,322 @@ +// 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. + +// CHERIoT version of test_rom_start. +// NB: no puppeteer support + +#include "hw/top_matcha/sw/autogen/top_matcha_memory.h" +#include "sw/device/lib/base/macros.h" +#include "sw/device/lib/base/multibits_asm.h" +#include "ast_regs.h" +#include "csrng_regs.h" +#include "edn_regs.h" +#include "entropy_src_regs.h" +#include "otp_ctrl_regs.h" +#include "sensor_ctrl_regs.h" +#include "sram_ctrl_regs.h" + +//.include "assembly-helpers.s" + +/// Load the absolute address of a symbol. +.macro la_abs reg, symbol + lui \reg, %hi(\symbol) + addi \reg, \reg, %lo(\symbol) +.endm + +/** + * Helper macro for zeroing a single register. + * Use c.li to guarantee it's 2 bytes and in the base ISA. + */ +.macro zeroOne reg + c.li \reg, 0 +.endm + +/** + * Helper macro for applying a macro to each argument in a list. Calls `m` + * once for each subsequent argument. + */ +.macro forall m rhead rtail:vararg + \m \rhead + .ifnb \rtail + forall \m \rtail + .endif +.endm + +/// Zero all of the registers in a list +.macro zeroRegisters reg1, regs:vararg + forall zeroOne, \reg1, \regs +.endm + + +/** + * Test ROM interrupt vectors. + * + * After reset all interrupts are disabled. Only exceptions (interrupt 0) and + * non-maskable interrupts (interrupt 31) are possible. For simplicity however + * we just set all interrupt handlers in the Test ROM to use the same handler, + * which loops forever. + * + * Interrupt vectors in Ibex have 32 entries for 32 possible interrupts. The + * vector must be 256-byte aligned, as Ibex's vectoring mechanism requires that. + * + * Note that the Ibex reset handler (entry point) immediately follows this + * interrupt vector and can be thought of as an extra entry. + * + * More information about Ibex's interrupts can be found here: + * https://ibex-core.readthedocs.io/en/latest/03_reference/exception_interrupts.html + */ + + // Push Test ROM interrupt vector options. + .option push + + // Disable RISC-V instruction compression: we need all instructions to + // be exactly word wide in the interrupt vector. + .option norvc + + // Disable RISC-V linker relaxation, as it can compress instructions at + // link-time, which we also really don't want. + .option norelax + + // NOTE: The "ax" flag below is necessary to ensure that this section + // is allocated executable space in ROM by the linker. + .section .vectors, "ax" + .balign 256 + .global _test_rom_interrupt_vector + .type _test_rom_interrupt_vector, @function +_test_rom_interrupt_vector: + + // Each jump instruction must be exactly 4 bytes in order to ensure that the + // entries are properly located. + .rept 32 + j _test_rom_irq_handler + .endr + + // Ibex reset vector, the initial entry point after reset. (This falls at IRQ + // handler 0x80.) + j _reset_start + + // Set size so this vector can be disassembled. + .size _test_rom_interrupt_vector, .-_test_rom_interrupt_vector + + // Pop ROM interrupt vector options. + // + // Re-enable compressed instructions, linker relaxation. + .option pop + +// ----------------------------------------------------------------------------- +/** + * Test ROM runtime initialization code. + */ + + // NOTE: The "ax" flag below is necessary to ensure that this section + // is allocated executable space in ROM by the linker. + .section .crt, "ax" + + /** + * Entry point after reset. This symbol is jumped to from the handler + * for IRQ 0x80. + */ + .balign 4 + .global _reset_start + .type _reset_start, @function + +_reset_start: + // Set up the trap vector, mostly for debugging + cspecialr ca2, mtcc + la_abs t0, _test_rom_irq_handler + csetaddr ca2, ca2, t0 + cspecialw mtcc, ca2 + + cspecialr ca4, mtdc // Keep the RW memory root in ca4 + + // Set up the stack at the end of RAM. XXX set proper perms + la_abs t0, _stack_start + csetaddr csp, ca4, t0 + la_abs t1, _stack_end + sub t1, t1, t0 + csetbounds csp, csp, t1 + cincoffset csp, csp, t1 + + // Set up the global pointer. This assumes .bss follows .data. + la_abs t0, _data_start + csetaddr cgp, ca4, t0 + la_abs t1, _bss_end + sub t1, t1, t0 + csetbounds cgp, cgp, t1 + srli t1, t1, 1 + cincoffset cgp, cgp, t1 + + // Clobber all writeable registers +// zeroRegisters x1, x2, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15 + + // Explicit fall-through to `_start`. + + .size _reset_start, .-_reset_start + +// ----------------------------------------------------------------------------- + + /** + * Callable entry point for the boot rom. + * + * Currently, this zeroes the `.bss` section, copies initial data to + * `.data`, and then jumps to the program entry point. + */ + .balign 4 + .global _start + .type _start, @function +_start: + cspecialr ca4, mtdc // Keep the RW memory root in ca4 + +#if !OT_IS_ENGLISH_BREAKFAST +#if !OTP_IS_RAM + // Check if AST initialization should be skipped. + li t0, (TOP_MATCHA_OTP_CTRL_CORE_BASE_ADDR + \ + OTP_CTRL_SW_CFG_WINDOW_REG_OFFSET) + cincoffset ca0, ca4, t0 + clw t0, OTP_CTRL_PARAM_CREATOR_SW_CFG_AST_INIT_EN_OFFSET(ca0) + li t1, MULTIBIT_ASM_BOOL4_TRUE + bne t0, t1, .L_ast_init_skip + + // Copy the AST configuration from OTP. + li t0, (TOP_MATCHA_AST_BASE_ADDR) + cincoffset ca0, ca4, t0 + li t0, (TOP_MATCHA_AST_BASE_ADDR + AST_REGAL_REG_OFFSET + 4) + cincoffset ca1, ca4, t0 + li t0, (TOP_MATCHA_OTP_CTRL_CORE_BASE_ADDR + \ + OTP_CTRL_SW_CFG_WINDOW_REG_OFFSET + \ + OTP_CTRL_PARAM_CREATOR_SW_CFG_AST_CFG_OFFSET) + cincoffset ca2, ca4, t0 + cjal crt_section_copy +#else // OTP_IS_RAM + j .L_ast_init_skip +#endif + + // Wait for AST initialization to complete. + li t0, TOP_MATCHA_SENSOR_CTRL_BASE_ADDR + cincoffset ca0, ca4, t0 +.L_ast_done_loop: + clw t0, SENSOR_CTRL_STATUS_REG_OFFSET(ca0) + srli t0, t0, SENSOR_CTRL_STATUS_AST_INIT_DONE_BIT // no-op as bit index is currently 0 + andi t0, t0, 0x1 + beqz t0, .L_ast_done_loop + +.L_ast_init_skip: + // The following sequence enables the minimum level of entropy required to + // initialize memory scrambling, as well as the entropy distribution network. + li t0, TOP_MATCHA_ENTROPY_SRC_BASE_ADDR + cincoffset ca0, ca4, t0 + + // Note for BOOT_ROM initialization the FIPS_ENABLE bit is set to kMultiBitBool4False + // to prevent the release of FIPS entropy until all the thresholds are set + li t0, (MULTIBIT_ASM_BOOL4_FALSE << ENTROPY_SRC_CONF_FIPS_ENABLE_OFFSET) | \ + (MULTIBIT_ASM_BOOL4_FALSE << ENTROPY_SRC_CONF_ENTROPY_DATA_REG_ENABLE_OFFSET) | \ + (MULTIBIT_ASM_BOOL4_FALSE << ENTROPY_SRC_CONF_THRESHOLD_SCOPE_OFFSET) | \ + (MULTIBIT_ASM_BOOL4_FALSE << ENTROPY_SRC_CONF_RNG_BIT_ENABLE_OFFSET) + csw t0, ENTROPY_SRC_CONF_REG_OFFSET(ca0) + + li t0, (MULTIBIT_ASM_BOOL4_TRUE << ENTROPY_SRC_MODULE_ENABLE_MODULE_ENABLE_OFFSET) + csw t0, ENTROPY_SRC_MODULE_ENABLE_REG_OFFSET(ca0) + + li t0, TOP_MATCHA_CSRNG_BASE_ADDR + cincoffset ca0, ca4, t0 + li t0, (MULTIBIT_ASM_BOOL4_TRUE << CSRNG_CTRL_ENABLE_OFFSET) | \ + (MULTIBIT_ASM_BOOL4_TRUE << CSRNG_CTRL_SW_APP_ENABLE_OFFSET) | \ + (MULTIBIT_ASM_BOOL4_TRUE << CSRNG_CTRL_READ_INT_STATE_OFFSET) + csw t0, CSRNG_CTRL_REG_OFFSET(ca0) + + li t0, TOP_MATCHA_EDN0_BASE_ADDR + cincoffset ca0, ca4, t0 + li t0, (MULTIBIT_ASM_BOOL4_TRUE << EDN_CTRL_EDN_ENABLE_OFFSET) | \ + (MULTIBIT_ASM_BOOL4_TRUE << EDN_CTRL_BOOT_REQ_MODE_OFFSET) | \ + (MULTIBIT_ASM_BOOL4_FALSE << EDN_CTRL_AUTO_REQ_MODE_OFFSET) | \ + (MULTIBIT_ASM_BOOL4_FALSE << EDN_CTRL_CMD_FIFO_RST_OFFSET) + csw t0, EDN_CTRL_REG_OFFSET(ca0) + +#if 0 + // Remove address space protections by configuring entry 15 as + // read-write-execute for the entire address space and then clearing + // all other entries. + // NOTE: This should happen before attemting to access any address outside + // the initial ePMP RX region at reset, e.g. `kDeviceType` which is in + // .rodata. + li t0, (0x9f << 24) // Locked NAPOT read-write-execute. + csrw pmpcfg3, t0 + li t0, 0x7fffffff // NAPOT encoded region covering entire 34-bit address space. + csrw pmpaddr15, t0 + csrw pmpcfg0, zero + csrw pmpcfg1, zero + csrw pmpcfg2, zero +#else + // No PMP support on CHERIoT. Could use caps to do the equivalent. +#endif +#endif // !OT_IS_ENGLISH_BREAKFAST + // Scramble and initialize main memory (main SRAM). + // Memory accesses will stall until initialization is complete. + // Skip SRAM initialization for DV sim device type, as the testbench handles + // this to optimize test run times. + la_abs a0, kDeviceType + csetaddr ca0, ca4, a0 + clw t0, (ca0) + beqz t0, .L_sram_init_skip + li t0, TOP_MATCHA_SRAM_CTRL_MAIN_REGS_BASE_ADDR + cincoffset ca0, ca4, t0 + li t0, (1 << SRAM_CTRL_CTRL_INIT_BIT) + csw t0, SRAM_CTRL_CTRL_REG_OFFSET(ca0) + +.L_sram_init_skip: + // Zero out the `.bss` segment. + la_abs a0, _bss_start + csetaddr ca0, ca4, a0 + la_abs a1, _bss_end + cjal crt_section_clear + + // Initialize the `.data` segment from the `.idata` segment. + la_abs a0, _data_start + csetaddr ca0, ca4, a0 // dst start + la_abs a1, _data_end + csetaddr ca1, ca4, a1 // dst end + la_abs a2, _data_init_start + csetaddr ca2, ca4, a2 // src start + cjal crt_section_copy + + // Clobber all temporary registers. + zeroRegisters t0, t1, t2 + cmove ca0, ca4 // Pass root cap along + // Clobber all argument registers. + zeroRegisters a1, a2, a3, a4, a5 + // XXX is stack zero'd? + // Jump into the C program entry point. + cjal _boot_start + + // Enter a wait for interrupt loop, the device should reset shortly. +.L_wfi_loop: + wfi + j .L_wfi_loop + + .size _start, .-_start + +// ----------------------------------------------------------------------------- + + /** + * Test ROM IRQ/exception handler; loops forever. + */ + .balign 4 + .section .text + .global _test_rom_irq_handler + .type _test_rom_irq_handler, @function +_test_rom_irq_handler: + wfi + j _test_rom_irq_handler + .size _test_rom_irq_handler, .-_test_rom_irq_handler
diff --git a/sw/device/silicon_creator/rom/bootstrap.c b/sw/device/silicon_creator/rom/bootstrap.c index da24b57..f31b154 100644 --- a/sw/device/silicon_creator/rom/bootstrap.c +++ b/sw/device/silicon_creator/rom/bootstrap.c
@@ -346,12 +346,13 @@ return error; } -hardened_bool_t bootstrap_requested(void) { +hardened_bool_t bootstrap_requested(mmio_region_t otp_addr, + mmio_region_t gpio_addr) { #if defined(OTP_IS_RAM) uint32_t res = kHardenedBoolTrue; #else uint32_t res = - otp_read32(OTP_CTRL_PARAM_OWNER_SW_CFG_ROM_BOOTSTRAP_EN_OFFSET); + otp_read32(otp_addr, OTP_CTRL_PARAM_OWNER_SW_CFG_ROM_BOOTSTRAP_EN_OFFSET); #endif if (launder32(res) != kHardenedBoolTrue) { return kHardenedBoolFalse; @@ -361,9 +362,7 @@ // A single read is sufficient since we expect strong pull-ups on the strap // pins. res ^= SW_STRAP_BOOTSTRAP; - res ^= - abs_mmio_read32(TOP_MATCHA_GPIO_BASE_ADDR + GPIO_DATA_IN_REG_OFFSET) & - SW_STRAP_MASK; + res ^= mmio_region_read32(gpio_addr, GPIO_DATA_IN_REG_OFFSET) & SW_STRAP_MASK; if (launder32(res) != kHardenedBoolTrue) { return kHardenedBoolFalse; } @@ -371,14 +370,16 @@ return res; } -rom_error_t bootstrap(void) { - hardened_bool_t requested = bootstrap_requested(); +// NB: assumes flash_ctrl_init is already called +rom_error_t bootstrap(mmio_region_t otp_addr, mmio_region_t gpio_addr, + mmio_region_t spi_device_addr) { + hardened_bool_t requested = bootstrap_requested(otp_addr, gpio_addr); if (launder32(requested) != kHardenedBoolTrue) { return kErrorBootstrapNotRequested; } HARDENED_CHECK_EQ(requested, kHardenedBoolTrue); - spi_device_init(); + spi_device_init(spi_device_addr); // Bootstrap event loop. bootstrap_state_t state = kBootstrapStateErase;
diff --git a/sw/device/silicon_creator/rom/bootstrap.h b/sw/device/silicon_creator/rom/bootstrap.h index 36b9667..758ae0b 100644 --- a/sw/device/silicon_creator/rom/bootstrap.h +++ b/sw/device/silicon_creator/rom/bootstrap.h
@@ -20,6 +20,7 @@ #define SW_DEVICE_SILICON_CREATOR_ROM_BOOTSTRAP_H_ #include "sw/device/lib/base/hardened.h" +#include "sw/device/lib/base/mmio.h" #include "sw/device/silicon_creator/lib/error.h" #ifdef __cplusplus @@ -34,7 +35,8 @@ * * @return Whether bootstrap is requested. */ -hardened_bool_t bootstrap_requested(void); +hardened_bool_t bootstrap_requested(mmio_region_t otp_addr, + mmio_region_t gpio_addr); /** * Bootstraps the data partition of the embedded flash with data received by the @@ -52,7 +54,8 @@ * * @return Result of the operation. */ -rom_error_t bootstrap(void); +rom_error_t bootstrap(mmio_region_t otp_addr, mmio_region_t gpio_addr, + mmio_region_t spi_device_addr); #ifdef __cplusplus }
diff --git a/sw/device/tests/kelvin/fpga_tests/kelvin_test_sc.c b/sw/device/tests/kelvin/fpga_tests/kelvin_test_sc.c index 92666f7..9a77e38 100644 --- a/sw/device/tests/kelvin/fpga_tests/kelvin_test_sc.c +++ b/sw/device/tests/kelvin/fpga_tests/kelvin_test_sc.c
@@ -61,7 +61,13 @@ test_status_set(kTestStatusInTest); init_uart(TOP_MATCHA_UART0_BASE_ADDR, &uart); LOG_INFO("kelvin_test_sc"); - spi_flash_init(); + const mmio_region_t spi_host_addr = + mmio_region_from_addr(TOP_MATCHA_SPI_HOST0_BASE_ADDR); + const mmio_region_t eflash_addr = + mmio_region_from_addr(TOP_MATCHA_FLASH_CTRL_CORE_BASE_ADDR); + const mmio_region_t otp_addr = + mmio_region_from_addr(TOP_MATCHA_OTP_CTRL_CORE_BASE_ADDR); + spi_flash_init(spi_host_addr, eflash_addr, otp_addr); // Copy binary to SMC RAM. CHECK_DIF_OK(load_file_from_tar(