blob: 9e4166f94c74297d39c04bbacf8eb5f4277c42f2 [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 "sw/device/lib/dif/dif_base.h"
#include "sw/device/lib/dif/dif_otbn.h"
#include "sw/device/lib/runtime/log.h"
#include "sw/device/lib/runtime/otbn.h"
#include "sw/device/lib/testing/test_framework/check.h"
#include "sw/device/lib/testing/test_framework/ottf_main.h"
#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
OTBN_DECLARE_APP_SYMBOLS(randomness);
static const otbn_app_t kOtbnAppCfiTest = OTBN_APP_T_INIT(randomness);
const test_config_t kTestConfig;
static volatile bool has_exception_fired;
/**
* This overrides the default OTTF load/store fault exception handler.
*/
void ottf_load_store_fault_handler(void) { has_exception_fired = true; }
typedef dif_result_t (*otbn_read_t)(const dif_otbn_t *otbn,
uint32_t offset_bytes, void *dest,
size_t len_bytes);
/**
* Check whether the contents of an otbn memory match the reference data pointed
* at `addr`.
*
* @param ctx The otbn context object.
* @param addr The address of the reference data.
* @param mem_size The size of the reference data.
* @param match_expected Indicates whether the checking is expected to match.
* @param read_function Pointer to the function to read the data. It can be
* either `dif_otbn_imem_read` or `dif_otbn_dmem_read`.
*/
static void otbn_check_mem(otbn_t *ctx, const uint8_t *addr, size_t mem_size,
bool match_expected, otbn_read_t otbn_read) {
uint8_t local_buf[256];
size_t offset = 0;
do {
size_t remainder = mem_size - offset;
if (remainder > ARRAYSIZE(local_buf)) {
remainder = ARRAYSIZE(local_buf);
}
// If the memory has been scrambled we will expect to receive an exception,
// otherwise we compare the memory value.
has_exception_fired = false;
CHECK_DIF_OK(otbn_read(&ctx->dif, offset, local_buf, remainder));
if (match_expected) {
CHECK(!has_exception_fired, "Unexpected exception");
CHECK_BUFFER_EQ(addr + offset, local_buf, remainder);
} else {
CHECK(has_exception_fired, "Expected exception haven't fired");
break;
}
offset += remainder;
} while (offset < mem_size);
}
/**
* Check that the application is loaded correctly to the IMEM and DMEM.
*
* @param ctx The otbn context object.
* @param app The application to match with OTBN memory.
* @param match_expected Indicates whether the checking is expected to match.
*/
static void otbn_check_app(otbn_t *ctx, const otbn_app_t app,
bool match_expected) {
const size_t imem_size = app.imem_end - app.imem_start;
const size_t data_size = app.dmem_data_end - app.dmem_data_start;
const uint8_t *imem_start = app.imem_start;
const uint8_t *dmem_start = app.dmem_data_start;
// Memory images and offsets must be multiples of 32b words.
CHECK(imem_size % sizeof(uint32_t) == 0);
// Check the IMEM content.
otbn_check_mem(ctx, imem_start, imem_size, match_expected,
dif_otbn_imem_read);
// Check the DMEM content.
otbn_check_mem(ctx, dmem_start, data_size, match_expected,
dif_otbn_dmem_read);
}
bool test_main() {
otbn_t otbn_ctx;
mmio_region_t addr = mmio_region_from_addr(TOP_EARLGREY_OTBN_BASE_ADDR);
CHECK(otbn_init(&otbn_ctx, addr) == kOtbnOk);
// Write and read-check OTBN and IMEM for consistency.
CHECK(otbn_load_app(&otbn_ctx, kOtbnAppCfiTest) == kOtbnOk);
otbn_check_app(&otbn_ctx, kOtbnAppCfiTest, /*match_expected=*/true);
// Fetch a new key from the OTP_CTRL and ensure that previous contents in the
// IMEM and DMEM cannot be read anymore.
CHECK_DIF_OK(dif_otbn_write_cmd(&otbn_ctx.dif, kDifOtbnCmdSecWipeImem));
CHECK(otbn_busy_wait_for_done(&otbn_ctx) == kOtbnOk);
CHECK_DIF_OK(dif_otbn_write_cmd(&otbn_ctx.dif, kDifOtbnCmdSecWipeDmem));
CHECK(otbn_busy_wait_for_done(&otbn_ctx) == kOtbnOk);
otbn_check_app(&otbn_ctx, kOtbnAppCfiTest, /*match_expected=*/false);
return true;
}