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(