| // Copyright lowRISC contributors. | |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. | |
| // SPDX-License-Identifier: Apache-2.0 | |
| #include <stdbool.h> | |
| #include <stdint.h> | |
| #include "sw/device/examples/sram_program/sram_program.h" | |
| #include "sw/device/lib/base/csr.h" | |
| #include "sw/device/lib/base/macros.h" | |
| #include "sw/device/lib/dif/dif_sram_ctrl.h" | |
| #include "sw/device/lib/runtime/ibex.h" | |
| #include "sw/device/lib/runtime/log.h" | |
| #include "sw/device/lib/testing/test_framework/check.h" | |
| #include "sw/device/lib/testing/test_framework/ottf_isrs.h" | |
| #include "sw/device/lib/testing/test_framework/ottf_macros.h" | |
| #include "sw/device/lib/testing/test_framework/ottf_main.h" | |
| #include "sw/device/silicon_creator/lib/epmp_state.h" | |
| #include "hw/top_earlgrey/sw/autogen/top_earlgrey.h" | |
| OTTF_DEFINE_TEST_CONFIG(); | |
| static dif_sram_ctrl_t sram_ctrl; | |
| enum { | |
| kSramStart = TOP_EARLGREY_SRAM_CTRL_MAIN_RAM_BASE_ADDR, | |
| kSramEnd = TOP_EARLGREY_SRAM_CTRL_MAIN_RAM_BASE_ADDR + | |
| TOP_EARLGREY_SRAM_CTRL_MAIN_RAM_SIZE_BYTES, | |
| kSramRetStart = TOP_EARLGREY_SRAM_CTRL_RET_AON_RAM_BASE_ADDR, | |
| }; | |
| OT_SECTION(".data.sram_function_test") | |
| OT_NOINLINE | |
| static void sram_function_test(void) { | |
| uint32_t pc = 0; | |
| asm("auipc %[pc], 0;" : [pc] "=r"(pc)); | |
| LOG_INFO("PC: %p, SRAM: [%p, %p)", pc, kSramStart, kSramEnd); | |
| CHECK(pc >= kSramStart && pc < kSramEnd, "PC is outside the main SRAM"); | |
| } | |
| // Redeclaration of the addressable symbol in `sram_ret_neg_test()` to be used | |
| // in `ottf_exception_handler()`. | |
| extern const char kSramRetNegTestReturn[]; | |
| void ottf_exception_handler(void) { | |
| // The frame address is the address of the stack location that holds the | |
| // `mepc`, since the OTTF exception handler entry code saves the `mepc` to | |
| // the top of the stack before transferring control flow to the exception | |
| // handler function (which is overridden here). See the `handler_exception` | |
| // subroutine in `sw/device/lib/testing/testing/ottf_isrs.S` for more details. | |
| uintptr_t *mepc_stack_addr = (uintptr_t *)OT_FRAME_ADDR(); | |
| ibex_exc_t exception = ibex_mcause_read(); | |
| switch (exception) { | |
| case kIbexExcInstrAccessFault: | |
| LOG_INFO("Detected instruction access fault"); | |
| *mepc_stack_addr = (uintptr_t)kSramRetNegTestReturn; | |
| break; | |
| default: | |
| LOG_FATAL("Unexpected exception id = 0x%x", exception); | |
| abort(); | |
| } | |
| } | |
| static void sram_ret_neg_test(void) { | |
| memset((void *)kSramRetStart, 0, sizeof(uint32_t)); | |
| ((void (*)(void))kSramRetStart)(); | |
| CHECK(false, "Should not be here"); | |
| OT_ADDRESSABLE_LABEL(kSramRetNegTestReturn); | |
| } | |
| // See the `bin_to_archive` rule in `opentitan.bzl` for the definition of this | |
| // symbol. | |
| extern const char _sram_program_start[]; | |
| static sram_program_entrypoint *sram_main = | |
| (sram_program_entrypoint *)_sram_program_start; | |
| static void sram_program_test(void) { | |
| LOG_INFO("Jumping to the program in SRAM: %p", sram_main); | |
| sram_main(); | |
| LOG_INFO("Returned from the program in SRAM"); | |
| } | |
| bool test_main(void) { | |
| CHECK_DIF_OK(dif_sram_ctrl_init( | |
| mmio_region_from_addr(TOP_EARLGREY_SRAM_CTRL_MAIN_REGS_BASE_ADDR), | |
| &sram_ctrl)); | |
| // Unlock the entire address space for RWX so that we can run this test with | |
| // both rom and test_rom. | |
| CSR_SET_BITS(CSR_REG_PMPADDR15, 0x7fffffff); | |
| CSR_SET_BITS(CSR_REG_PMPCFG3, kEpmpPermLockedReadWriteExecute << 24); | |
| CSR_WRITE(CSR_REG_PMPCFG2, 0); | |
| CSR_WRITE(CSR_REG_PMPCFG1, 0); | |
| CSR_WRITE(CSR_REG_PMPCFG0, 0); | |
| // Note: We can test the negative case only using the retention SRAM since | |
| // execution is unconditionally enabled for the main SRAM in the RMA life | |
| // cycle state. | |
| sram_ret_neg_test(); | |
| sram_function_test(); | |
| sram_program_test(); | |
| return true; | |
| } |