diff --git a/hw/top_earlgrey/data/ip/chip_aes_testplan.hjson b/hw/top_earlgrey/data/ip/chip_aes_testplan.hjson index d12734ce83a24..0657677cacd4f 100644 --- a/hw/top_earlgrey/data/ip/chip_aes_testplan.hjson +++ b/hw/top_earlgrey/data/ip/chip_aes_testplan.hjson @@ -211,7 +211,7 @@ stage: V2 si_stage: SV3 lc_states: ["PROD"] - tests: ["chip_sw_aes_prng_reseed"] + tests: [] bazel: ["//sw/device/tests:aes_prng_reseed_test"] } { @@ -267,9 +267,54 @@ stage: V2 si_stage: SV3 lc_states: ["PROD"] - tests: ["chip_sw_aes_force_prng_reseed"] + tests: [] bazel: ["//sw/device/tests:aes_force_prng_reseed_test"] } + { + name: chip_sw_aes_stall_test + desc: '''Verify that the AES module's stall bit assertion + when multiple input blocks are loaded without reading + output data, and resumes encryption once the output data is read. + + Procedure: + - Initialize the AES. + - Configure the AES encryption operation with the following settings: + - Operation: Encrypt + - Mode: ECB + - Key Length: 128 bits + - Key Provider: Software-provided key + - Mask Reseeding: Reseed per block + - Manual Operation: Auto + - Reseed on Key Change: Enabled + - Force Masks: Enabled + - Generate an AES key and load it for encryption. + - Prepare input blocks with random plaintext data. + - Load multiple blocks into AES without reading output data to simulate + a stall condition. + - Verify that `kDifAesStatusStall` is set. + - Once the AES is stalled, read the output blocks one by one. + - Verify that reading the output clears the stall condition. + - Ensure the AES encryption operation completes successfully + and that the AES module reaches the idle state at the end of the test. + + Expected Outcome: + - AES enters a stall condition when multiple input blocks are loaded without + reading output. + - Reading the output clears the stall condition, allowing AES to resume + encryption. + - The test verifies correct handling of the stall condition and that + the AES operation completes as expected. + ''' + features: [ + "AES.MODE.ECB", + "AES.STATUS.STALL", + ] + stage: V3 + si_stage: SV3 + lc_states: ["PROD"] + tests: [] + bazel: ["//sw/device/tests:aes_stall_test"] + } { name: chip_sw_aes_idle desc: '''Verify AES idle signaling to clkmgr. diff --git a/sw/device/tests/BUILD b/sw/device/tests/BUILD index f9ba07c1f6bc4..724cc3d9f8dd0 100644 --- a/sw/device/tests/BUILD +++ b/sw/device/tests/BUILD @@ -243,6 +243,40 @@ opentitan_test( ], ) +opentitan_test( + name = "aes_stall_test", + srcs = ["aes_stall_test.c"], + exec_env = dicts.add( + EARLGREY_TEST_ENVS, + EARLGREY_SILICON_OWNER_ROM_EXT_ENVS, + { + "//hw/top_earlgrey:fpga_cw310_sival": None, + "//hw/top_earlgrey:sim_verilator": None, + "//hw/top_earlgrey:fpga_cw340_sival": None, + "//hw/top_earlgrey:silicon_creator": None, + }, + ), + verilator = verilator_params( + timeout = "long", + ), + deps = [ + "//hw/ip/aes:model", + "//hw/top_earlgrey/sw/autogen:top_earlgrey", + "//sw/device/lib/base:memory", + "//sw/device/lib/base:mmio", + "//sw/device/lib/dif:aes", + "//sw/device/lib/dif:edn", + "//sw/device/lib/dif:entropy_src", + "//sw/device/lib/runtime:log", + "//sw/device/lib/testing:aes_testutils", + "//sw/device/lib/testing:entropy_testutils", + "//sw/device/lib/testing/test_framework:check", + "//sw/device/lib/testing/test_framework:ottf_main", + "//sw/device/lib/testing/test_framework:ottf_utils", + "//sw/device/sca/lib:simple_serial", + ], +) + opentitan_test( name = "alert_handler_ping_timeout_test", srcs = ["alert_handler_ping_timeout_test.c"], diff --git a/sw/device/tests/aes_stall_test.c b/sw/device/tests/aes_stall_test.c new file mode 100644 index 0000000000000..f306e6d7260cd --- /dev/null +++ b/sw/device/tests/aes_stall_test.c @@ -0,0 +1,176 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#include "hw/ip/aes/model/aes_modes.h" +#include "sw/device/lib/base/memory.h" +#include "sw/device/lib/base/mmio.h" +#include "sw/device/lib/base/multibits.h" +#include "sw/device/lib/dif/dif_aes.h" +#include "sw/device/lib/dif/dif_entropy_src.h" +#include "sw/device/lib/dif/dif_rv_core_ibex.h" +#include "sw/device/lib/runtime/hart.h" +#include "sw/device/lib/runtime/log.h" +#include "sw/device/lib/testing/aes_testutils.h" +#include "sw/device/lib/testing/entropy_testutils.h" +#include "sw/device/lib/testing/rv_core_ibex_testutils.h" +#include "sw/device/lib/testing/test_framework/check.h" +#include "sw/device/lib/testing/test_framework/ottf_main.h" +#include "sw/device/sca/lib/simple_serial.h" + +#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h" + +OTTF_DEFINE_TEST_CONFIG(); + +static dif_aes_t aes; +dif_aes_key_share_t key; +enum { + kAesNumBlocks = 2, + // NumBytes in one block is 4x8 i.e. 32 bits in 1 block + kDifAesBlockNumBytes = 4, + kDisableEntropyAtStartEn = 1, +}; + +static const uint8_t kKeyShare1[16] = { + 0x0f, 0x1f, 0x2f, 0x3f, 0x4f, 0x5f, 0x6f, 0x7f, + 0x8f, 0x9f, 0xaf, 0xbf, 0xcf, 0xdf, 0xef, 0xff, +}; + +// Function to generate a new key based on provided index value +void generate_new_key(dif_aes_key_share_t *key, int index) { + uint8_t new_key_share0[sizeof(kAesModesKey128)]; + // Generate new key share0 by XOR-ing base key with kKeyShare1*index + for (size_t i = 0; i < sizeof(kAesModesKey128); ++i) { + new_key_share0[i] = kAesModesKey128[i] ^ kKeyShare1[i] * (uint8_t)(index); + } + // Updating the key shares + memcpy(key->share0, new_key_share0, sizeof(key->share0)); + memcpy(key->share1, kKeyShare1, sizeof(key->share1)); +} + +status_t execute_test(void) { + bool aes_idle = false; + bool input_ready = false; + bool output_valid = false; + + // Initialize AES + TRY(dif_aes_init(mmio_region_from_addr(TOP_EARLGREY_AES_BASE_ADDR), &aes)); + TRY(dif_aes_reset(&aes)); + + // Generate key with index 0 + generate_new_key(&key, 0); + + // Prepare transaction + dif_aes_transaction_t transaction = { + .operation = kDifAesOperationEncrypt, + .mode = kDifAesModeEcb, + .key_len = kDifAesKey128, + .key_provider = kDifAesKeySoftwareProvided, + .mask_reseeding = kDifAesReseedPerBlock, + .manual_operation = kDifAesManualOperationAuto, + .reseed_on_key_change = true, + .force_masks = true, + .ctrl_aux_lock = false, + }; + + dif_aes_data_t plain_text[kAesNumBlocks]; + dif_aes_data_t cipher_text[kAesNumBlocks]; + + // Initialize plaintext data dynamically + // Create plaintext with random data + dif_rv_core_ibex_t rv_core_ibex; + TRY(dif_rv_core_ibex_init( + mmio_region_from_addr(TOP_EARLGREY_RV_CORE_IBEX_CFG_BASE_ADDR), + &rv_core_ibex)); + for (uint32_t i = 0; i < ARRAYSIZE(plain_text); ++i) { + for (uint32_t j = 0; j < kDifAesBlockNumBytes; ++j) { + uint32_t rand; + TRY(rv_core_ibex_testutils_get_rnd_data(&rv_core_ibex, 2000, &rand)); + plain_text[i].data[j] = rand; + } + } + + // Start the AES operation + // Wait for AES to be idle before starting encryption + aes_idle = false; + do { + TRY(dif_aes_get_status(&aes, kDifAesStatusIdle, &aes_idle)); + } while (!aes_idle); + + TRY(dif_aes_start(&aes, &transaction, &key, NULL)); + + // Variables for status checks + bool stall = false; + uint32_t num_of_blocks_loaded = 0; + + // Load multiple input blocks without reading output data + // to generate STALL status bit assertion + LOG_INFO("Loading %d blocks without reading output to create back pressure", + kAesNumBlocks); + for (uint32_t i = 0; i < kAesNumBlocks; ++i) { + // Wait for input ready + input_ready = false; + do { + TRY(dif_aes_get_status(&aes, kDifAesStatusInputReady, &input_ready)); + } while (!input_ready); + + // Load data + TRY(dif_aes_load_data(&aes, plain_text[i])); + num_of_blocks_loaded++; + busy_spin_micros(1000); + + // Check for STALL condition + TRY(dif_aes_get_status(&aes, kDifAesStatusStall, &stall)); + if (i > 0) { + TRY_CHECK( + stall, + "ERROR: AES module not STALLED as expected after loading block %d", + i); + LOG_INFO("AES module is stalled as expected after loading block %d", i); + break; + } + } + + if (stall) { + // Read output data to clear stall + uint32_t num_of_blocks_read = 0; + + while (num_of_blocks_read < num_of_blocks_loaded) { + // Wait for output valid + output_valid = false; + do { + TRY(dif_aes_get_status(&aes, kDifAesStatusOutputValid, &output_valid)); + } while (!output_valid); + + // Read output data + TRY(dif_aes_read_output(&aes, &cipher_text[num_of_blocks_read])); + num_of_blocks_read++; + + // Check if STALL bit is cleared + TRY(dif_aes_get_status(&aes, kDifAesStatusStall, &stall)); + TRY_CHECK(!stall, "ERROR: Block %u not successfully read", + num_of_blocks_read - 1); + } + } else { + TRY_CHECK(stall, "ERROR:STALL condition did not occur..."); + } + + // Finish the AES encryption operation + LOG_INFO("End AES encryption operation"); + TRY(dif_aes_end(&aes)); + + // Wait for AES to be idle before starting decryption + aes_idle = false; + do { + TRY(dif_aes_get_status(&aes, kDifAesStatusIdle, &aes_idle)); + } while (!aes_idle); + LOG_INFO("AES module is idle"); + + return OK_STATUS(); +} + +bool test_main(void) { + LOG_INFO("Entering AES aes_stall_test Test"); + + return status_ok(execute_test()); +}