blob: e9fa2007a0708a535332f8a58542162e2b1f8030 [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/base/mmio.h"
#include "sw/device/lib/dif/dif_hmac.h"
#include "sw/device/lib/dif/dif_rv_plic.h"
#include "sw/device/lib/irq.h"
#include "sw/device/lib/runtime/ibex.h"
#include "sw/device/lib/runtime/log.h"
#include "sw/device/lib/testing/hmac_testutils.h"
#include "sw/device/lib/testing/rv_plic_testutils.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"
#include "sw/device/lib/testing/autogen/isr_testutils.h"
/**
* HMAC done 50uS timeout, which should be enough even for a 100kHz device.
*
* https://docs.opentitan.org/hw/ip/hmac/doc/
* Final hash calculation takes 360 cycles, which consists of one
* block compression and extra HMAC computation.
*/
const uint32_t kHmacEncFinishTimeoutUsec = 50;
/**
* FIFO empty 10uS timeout, which should be enough even for a 100kHz device.
*
* https://docs.opentitan.org/hw/ip/hmac/doc/
* Single HMAC block compression takes 80 cycles.
*/
const uint32_t kHmacEncEmptyTimeoutUsec = 10;
static plic_isr_ctx_t plic_ctx = {
.hart_id = kTopEarlgreyPlicTargetIbex0,
};
static dif_hmac_t hmac;
static top_earlgrey_plic_peripheral_t peripheral_serviced;
static dif_hmac_irq_t irq_serviced;
static hmac_isr_ctx_t hmac_ctx = {
.hmac = &hmac,
.plic_hmac_start_irq_id = kTopEarlgreyPlicIrqIdHmacHmacDone,
.is_only_irq = false,
};
const test_config_t kTestConfig;
static const dif_hmac_transaction_t kHmacTransactionConfig = {
.digest_endianness = kDifHmacEndiannessLittle,
.message_endianness = kDifHmacEndiannessLittle,
};
/**
* External ISR.
*
* Handles all peripheral interrupts on Ibex. PLIC asserts an external interrupt
* line to the CPU, which results in a call to this OTTF ISR. This ISR
* overrides the default OTTF implementation.
*/
void ottf_external_isr(void) {
isr_testutils_hmac_isr(plic_ctx, hmac_ctx, &peripheral_serviced,
&irq_serviced);
}
/**
* Enables interrupts required by this test.
*/
static void enable_irqs(void) {
mmio_region_t base_addr =
mmio_region_from_addr(TOP_EARLGREY_RV_PLIC_BASE_ADDR);
CHECK_DIF_OK(dif_rv_plic_init(base_addr, plic_ctx.rv_plic));
// Enable interrupts in HMAC IP.
CHECK_DIF_OK(dif_hmac_irq_set_enabled(hmac_ctx.hmac, kDifHmacIrqHmacDone,
kDifToggleEnabled));
CHECK_DIF_OK(dif_hmac_irq_set_enabled(hmac_ctx.hmac, kDifHmacIrqFifoEmpty,
kDifToggleEnabled));
// Enable interrupts in PLIC.
rv_plic_testutils_irq_range_enable(plic_ctx.rv_plic, plic_ctx.hart_id,
kTopEarlgreyPlicIrqIdHmacHmacDone,
kTopEarlgreyPlicIrqIdHmacFifoEmpty);
// Enable interrupts in Ibex.
irq_global_ctrl(true);
irq_external_ctrl(true);
}
bool test_main() {
mmio_region_t base_addr = mmio_region_from_addr(TOP_EARLGREY_HMAC_BASE_ADDR);
CHECK_DIF_OK(dif_hmac_init(base_addr, &hmac));
enable_irqs();
// The purpose of this test is to ensure that HMAC empty and done interrupts
// assert when the conditions are met. Digest is not verified by this
// test, which means that a "dummy" data will suffice.
size_t sent_bytes;
char data[4] = {0xaa, 0xbb, 0xcc, 0xdd};
CHECK_DIF_OK(dif_hmac_mode_sha256_start(&hmac, kHmacTransactionConfig));
hmac_ctx.expected_irq = kDifHmacIrqFifoEmpty;
CHECK_DIF_OK(
dif_hmac_fifo_push(&hmac, &data[0], ARRAYSIZE(data), &sent_bytes));
hmac_testutils_check_message_length(&hmac, 32);
// Spin waiting for the "empty" interrupt.
IBEX_SPIN_FOR(irq_serviced == hmac_ctx.expected_irq,
kHmacEncEmptyTimeoutUsec);
// Race conditions could result in a stale value due to `hmac_empty_irq`
// being set in the ISR, however, in practice that does not matter.
hmac_ctx.expected_irq = kDifHmacIrqHmacDone;
CHECK_DIF_OK(dif_hmac_process(&hmac));
LOG_INFO("Waiting for HMAC to finish");
// Spin waiting for the "done" interrupt.
IBEX_SPIN_FOR(irq_serviced == hmac_ctx.expected_irq,
kHmacEncFinishTimeoutUsec);
// Finish the HMAC operation.
dif_hmac_digest_t dummy_digest;
CHECK_DIF_OK(dif_hmac_finish(&hmac, &dummy_digest));
return true;
}