Skip to content

Commit

Permalink
[sival/aes] Introduce AES Stall Status Bit test
Browse files Browse the repository at this point in the history
This commit introduces the AES stall test (chip_sw_stall_test) to
 verify the stall bit getting asserted when scenario of back pressure
is created.

- Intial Checkin: sw/device/tests/aes_stall_test.c
- Updated File: sw/device/tests/BUILD
- Updated File: hw/top_earlgrey/data/ip/chip_aes_testplan.hjson
- Implemented changes as per review comments from engdoreis and nasahlpa
- Re-opening accidently closed PR#25082 did not work
- Attempt to create new PR
- Resolving conflit

Signed-off-by: Varunkumar Trivedi <[email protected]>
  • Loading branch information
github-gcontributor authored and engdoreis committed Dec 11, 2024
1 parent 6460129 commit 7fd8634
Show file tree
Hide file tree
Showing 3 changed files with 257 additions and 2 deletions.
49 changes: 47 additions & 2 deletions hw/top_earlgrey/data/ip/chip_aes_testplan.hjson
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
}
{
Expand Down Expand Up @@ -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.
Expand Down
34 changes: 34 additions & 0 deletions sw/device/tests/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -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"],
Expand Down
176 changes: 176 additions & 0 deletions sw/device/tests/aes_stall_test.c
Original file line number Diff line number Diff line change
@@ -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());
}

0 comments on commit 7fd8634

Please sign in to comment.