blob: d707f485ce9921c1ef3b6baebeb461c9169ebab4 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
#include <stdbool.h>
#include <stdint.h>
#include "sw/device/lib/base/bitfield.h"
#include "sw/device/lib/dif/dif_sram_ctrl.h"
#include "sw/device/lib/runtime/log.h"
#include "sw/device/lib/testing/check.h"
#include "sw/device/lib/testing/lc_ctrl_testutils.h"
#include "sw/device/lib/testing/otp_ctrl_testutils.h"
#include "sw/device/lib/testing/sram_ctrl_testutils.h"
#include "sw/device/lib/testing/test_framework/test_main.h"
#include "sw/device/lib/testing/test_framework/test_status.h"
#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
const test_config_t kTestConfig;
static dif_sram_ctrl_t sram_ctrl;
static volatile const uint32_t kExecParam1 = 2;
static volatile const uint32_t kExecParam2 = 3;
/**
* Main SRAM start and end addresses (inclusive).
*/
static const uint32_t kRamStartAddr = TOP_EARLGREY_SRAM_CTRL_MAIN_RAM_BASE_ADDR;
static const uint32_t kRamEndAddr = TOP_EARLGREY_SRAM_CTRL_MAIN_RAM_BASE_ADDR +
TOP_EARLGREY_SRAM_CTRL_MAIN_RAM_SIZE_BYTES -
1;
/**
* OTP HW partition relative IFETCH offset in bytes.
*
* x = OTP_CTRL_PARAM_EN_SRAM_IFETCH_OFFSET (1728)
* y = OTP_CTRL_PARAM_HW_CFG_OFFSET (1664)
* IFETCH_OFFSET = (x - y) / 8 = 8
*/
static const uint32_t kOtpIfetchHwRelativeOffset = 8;
/**
* Buffer to hold instructions to be executed.
*
* Because this is the Main SRAM, we let the compiler allocate the buffer
* instead of using the hard-coded locations from the `sram_ctrl_testutil.c`,
* in order to prevent clobbering the data used by the program.
*
* This can be thought of as the implementation for the `ram_exec_function_t`.
*
* NOTE: We explicitly specify the `.data` section, otherwise due to the
* `const` qualifier buffer would end up in flash. The implicit
* alternative would be to drop the `const` qualifier, making the buffer
* go into the `.data` section.
*/
__attribute__((section(
".data"))) static volatile const uint32_t execution_test_instructions[2] = {
/*
* Abstract representation: add rd, rs1, rs2
* +-----------+-------+-------+-------+------+-----------+
* | 0000000 | rs2 | rs1 | 000 | rd | 0110011 |
* +-----------+-------+-------+-------+------+-----------+
* 31 25 24 20 19 15 14 12 11 7 6 0
*
* Custom representation: add a0, a1, a0
* +-----------+---------+---------+-------+--------+-----------+
* | 0000000 | 01010 | 01011 | 000 | 01010 | 0110011 |
* +-----------+---------+---------+-------+--------+-----------+
* 31 25 24 20 19 15 14 12 11 7 6 0
*/
0x00A58533,
/*
* Abstract representation: ret == jalr rd, offset(rs1)
* +----------------+-------+-------+------+-----------+
* | 000000000000 | rs1 | 000 | rd | 1100111 |
* +----------------+-------+-------+------+-----------+
* 31 20 19 15 14 12 11 7 6 0
*
* Custom representation: jalr zero, 0(ra)
* +----------------+---------+-------+---------+-----------+
* | 000000000000 | 00001 | 000 | 00000 | 1100111 |
* +----------------+---------+-------+---------+-----------+
* 31 20 19 15 14 12 11 7 6 0
*/
0x00008067,
};
static bool otp_ifetch_enabled(void) {
dif_otp_ctrl_t otp;
CHECK_DIF_OK(dif_otp_ctrl_init(
mmio_region_from_addr(TOP_EARLGREY_OTP_CTRL_CORE_BASE_ADDR), &otp));
dif_otp_ctrl_config_t config = {
.check_timeout = 100000,
.integrity_period_mask = 0x3ffff,
.consistency_period_mask = 0x3ffffff,
};
CHECK_DIF_OK(dif_otp_ctrl_configure(&otp, config));
CHECK_DIF_OK(dif_otp_ctrl_dai_read_start(&otp, kDifOtpCtrlPartitionHwCfg,
kOtpIfetchHwRelativeOffset));
otp_ctrl_testutils_wait_for_dai(&otp);
uint32_t value;
CHECK_DIF_OK(dif_otp_ctrl_dai_read32_end(&otp, &value));
// OTP stores IFETCH state in a single bit (enabled/disabled).
return bitfield_bit32_read(value, 0);
}
/**
* Performs SRAM execution test.
*
* `ram_exec_function_t` is "mapped" onto the `execution_test_instructions`
* buffer that holds hand-crafted machine instructions. Effectively, this
* buffer becomes the function body.
*
* According to the calling convention the parameters will be passed in the
* a0 and a1 registers, with a0 also treated as the return value register.
*/
static void sram_execution_test(void) {
// Map the function pointer onto the instruction buffer.
sram_ctrl_testutils_exec_function_t func =
(sram_ctrl_testutils_exec_function_t)execution_test_instructions;
uintptr_t func_address = (uintptr_t)func;
CHECK(func_address >= kRamStartAddr && func_address <= kRamEndAddr,
"Test code resides outside of the Main SRAM: function address = %x",
func_address);
uint32_t expected_result = kExecParam1 + kExecParam2;
uint32_t result = func(kExecParam1, kExecParam2);
CHECK(result == expected_result,
"SRAM Execution expected %d + %d = %d, got %d", kExecParam1,
kExecParam2, expected_result, result);
}
/**
* Performs the tests.
*
* When chip is in one of the lifecycle states where debug functions are
* enabled, execution from SRAM is enabled if the EN_SRAM_IFETCH
* (OTP) is disabled. When EN_SRAM_IFETCH (OTP) is enabled, EXEC CSR
* determines whether the execution from SRAM is enabled.
*/
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));
dif_lc_ctrl_t lc;
CHECK_DIF_OK(dif_lc_ctrl_init(
mmio_region_from_addr(TOP_EARLGREY_LC_CTRL_BASE_ADDR), &lc));
if (otp_ifetch_enabled()) {
dif_toggle_t state;
CHECK_DIF_OK(dif_sram_ctrl_exec_get_enabled(&sram_ctrl, &state));
if (state == kDifToggleDisabled) {
bool locked;
CHECK_DIF_OK(
dif_sram_ctrl_is_locked(&sram_ctrl, kDifSramCtrlLockExec, &locked));
CHECK(!locked,
"Execution is disabled and locked, cannot perform the test");
CHECK_DIF_OK(
dif_sram_ctrl_exec_set_enabled(&sram_ctrl, kDifToggleEnabled));
sram_execution_test();
}
} else if (lc_ctrl_testutils_debug_func_enabled(&lc)) {
sram_execution_test();
} else {
LOG_FATAL("Execution from SRAM cannot be enabled, cannot run the test");
}
return true;
}