blob: f541c0208828a49b567a6a753d6a4b0eb462223c [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/runtime/otbn.h"
#include "sw/device/lib/base/mmio.h"
#include "sw/device/lib/dif/dif_base.h"
#include "sw/device/lib/dif/dif_otbn.h"
#include "sw/device/lib/runtime/log.h"
/**
* Data width of big number subset, in bytes.
*/
const int kOtbnWlenBytes = 256 / 8;
otbn_result_t otbn_busy_wait_for_done(otbn_t *ctx) {
bool busy = true;
while (busy) {
dif_otbn_status_t status;
if (dif_otbn_get_status(&ctx->dif, &status) != kDifOk) {
return kOtbnError;
}
busy = status != kDifOtbnStatusIdle && status != kDifOtbnStatusLocked;
}
dif_otbn_err_bits_t err_bits;
if (dif_otbn_get_err_bits(&ctx->dif, &err_bits) != kDifOk) {
return kOtbnError;
}
if (err_bits != kDifOtbnErrBitsNoError) {
return kOtbnOperationFailed;
}
return kOtbnOk;
}
otbn_result_t otbn_init(otbn_t *ctx, mmio_region_t base_addr) {
if (ctx == NULL) {
return kOtbnBadArg;
}
ctx->app_is_loaded = false;
if (dif_otbn_init(base_addr, &ctx->dif) != kDifOk) {
return kOtbnError;
}
return kOtbnOk;
}
/**
* Checks if the OTBN application's IMEM and DMEM address parameters are valid.
*
* IMEM and DMEM ranges must not be "backwards" in memory, with the end address
* coming before the start address, and the IMEM range must additionally be
* non-empty. Finally, separate sections in DMEM must not overlap each other
* when converted to DMEM address space.
*
* @param app the OTBN application to check
* @return true if the addresses are valid, otherwise false.
*/
bool check_app_address_ranges(const otbn_app_t *app) {
// IMEM must have a strictly positive range (cannot be backwards or empty)
if (app->imem_end <= app->imem_start) {
return false;
}
// Initialised DMEM section must not be backwards
if (app->dmem_data_end < app->dmem_data_start) {
return false;
}
return true;
}
otbn_result_t otbn_load_app(otbn_t *ctx, const otbn_app_t app) {
if (!check_app_address_ranges(&app)) {
return kOtbnBadArg;
}
const size_t imem_size = app.imem_end - app.imem_start;
const size_t data_size = app.dmem_data_end - app.dmem_data_start;
// Memory images and offsets must be multiples of 32b words.
if (imem_size % sizeof(uint32_t) != 0 || data_size % sizeof(uint32_t) != 0) {
return kOtbnBadArg;
}
ctx->app_is_loaded = false;
ctx->app = app;
if (dif_otbn_imem_write(&ctx->dif, 0, ctx->app.imem_start, imem_size) !=
kDifOk) {
return kOtbnError;
}
// Zero all of DMEM
otbn_result_t err = otbn_zero_data_memory(ctx);
if (err != kOtbnOk) {
return err;
}
// Write initialized data
if (data_size > 0) {
if (dif_otbn_dmem_write(&ctx->dif, 0, ctx->app.dmem_data_start,
data_size) != kDifOk) {
return kOtbnError;
}
}
ctx->app_is_loaded = true;
return kOtbnOk;
}
otbn_result_t otbn_execute(otbn_t *ctx) {
if (ctx == NULL || !ctx->app_is_loaded) {
return kOtbnBadArg;
}
if (dif_otbn_write_cmd(&ctx->dif, kDifOtbnCmdExecute) != kDifOk) {
return kOtbnError;
}
return kOtbnOk;
}
otbn_result_t otbn_copy_data_to_otbn(otbn_t *ctx, size_t len_bytes,
const void *src, otbn_addr_t dest) {
if (ctx == NULL) {
return kOtbnBadArg;
}
if (dif_otbn_dmem_write(&ctx->dif, dest, src, len_bytes) != kDifOk) {
return kOtbnError;
}
return kOtbnOk;
}
otbn_result_t otbn_copy_data_from_otbn(otbn_t *ctx, size_t len_bytes,
otbn_addr_t src, void *dest) {
if (ctx == NULL || dest == NULL) {
return kOtbnBadArg;
}
if (dif_otbn_dmem_read(&ctx->dif, src, dest, len_bytes) != kDifOk) {
return kOtbnError;
}
return kOtbnOk;
}
otbn_result_t otbn_zero_data_memory(otbn_t *ctx) {
if (ctx == NULL) {
return kOtbnBadArg;
}
size_t dmem_size_words =
dif_otbn_get_dmem_size_bytes(&ctx->dif) / sizeof(uint32_t);
bool retval = kOtbnOk;
for (size_t i = 0; i < dmem_size_words; ++i) {
const uint32_t zero = 0;
// Continue the process even if a single write fails to try to clear as much
// memory as possible.
if (dif_otbn_dmem_write(&ctx->dif, i * sizeof(uint32_t), &zero,
sizeof(zero)) != kDifOk) {
retval = kOtbnError;
}
}
return retval;
}
otbn_result_t otbn_dump_dmem(const otbn_t *ctx, uint32_t max_addr) {
if (ctx == NULL || max_addr % kOtbnWlenBytes != 0 ||
max_addr > dif_otbn_get_dmem_size_bytes(&ctx->dif)) {
return kOtbnBadArg;
}
if (max_addr == 0) {
max_addr = dif_otbn_get_dmem_size_bytes(&ctx->dif);
}
for (int i = 0; i < max_addr; i += kOtbnWlenBytes) {
uint32_t data[kOtbnWlenBytes / sizeof(uint32_t)];
dif_otbn_dmem_read(&ctx->dif, i, data, kOtbnWlenBytes);
LOG_INFO("DMEM @%04d: 0x%08x%08x%08x%08x%08x%08x%08x%08x",
i / kOtbnWlenBytes, data[7], data[6], data[5], data[4], data[3],
data[2], data[1], data[0]);
}
return kOtbnOk;
}