Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[aes,sw/dv] Test AES-GCM save&restore feature #26

Draft
wants to merge 2 commits into
base: aes-gcm-review
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions hw/ip/aes/data/aes_testplan.hjson
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,13 @@
stage: V2
tests: ["aes_nist_vectors_gcm"]
}
{
name: gcm_save_restore
desc: '''
Test whether an AES-GCM operation can be saved and restored.'''
stage: V2
tests: ["aes_gcm_save_restore"]
}
{
name: reset_recovery
desc: '''
Expand Down
5 changes: 5 additions & 0 deletions hw/ip/aes/dv/aes_base_sim_cfg.hjson
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,11 @@
uvm_test: aes_fi_test
uvm_test_seq: aes_readability_vseq
}
{
name: aes_gcm_save_restore
uvm_test: aes_gcm_save_restore_test
uvm_test_seq: aes_gcm_save_restore_vseq
}
// Regular tests for which the scoreboard is active.
{
name: aes_smoke
Expand Down
1 change: 1 addition & 0 deletions hw/ip/aes/dv/env/aes_env.core
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ filesets:
- seq_lib/aes_core_fi_vseq.sv: {is_include_file: true}
- seq_lib/aes_readability_vseq.sv: {is_include_file: true}
- seq_lib/aes_stress_all_vseq.sv: {is_include_file: true}
- seq_lib/aes_gcm_save_restore_vseq.sv: {is_include_file: true}
file_type: systemVerilogSource

generate:
Expand Down
12 changes: 7 additions & 5 deletions hw/ip/aes/dv/env/aes_env_pkg.sv
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,13 @@ package aes_env_pkg;
parameter uint NUM_EDN = 1;

typedef enum int {
AES_CFG = 0,
AES_DATA = 1,
AES_ERR_INJ = 2,
AES_GCM_AAD = 3,
AES_GCM_TAG = 4
AES_CFG = 0,
AES_DATA = 1,
AES_ERR_INJ = 2,
AES_GCM_AAD = 3,
AES_GCM_TAG = 4,
AES_GCM_SAVE = 5,
AES_GCM_RESTORE = 6
} aes_item_type_e;

typedef enum int {
Expand Down
2 changes: 1 addition & 1 deletion hw/ip/aes/dv/env/aes_message_item.sv
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ class aes_message_item extends uvm_sequence_item;
if (item.mode == AES_GCM) begin
if (item.item_type == AES_GCM_AAD) begin
add_aad_item(item);
end else begin
end else if (item.item_type == AES_DATA) begin
`uvm_info(`gfn, $sformatf("ADDING DATA ITEM TYPE IS %s", item.item_type.name()), UVM_LOW)
add_data_item(item);
end
Expand Down
10 changes: 6 additions & 4 deletions hw/ip/aes/dv/env/aes_scoreboard.sv
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,12 @@ class aes_scoreboard extends cip_base_scoreboard #(

function void on_ctrl_gcm_shadowed_write(logic [31:0] wdata);
case (get_field_val(ral.ctrl_gcm_shadowed.phase, wdata))
GCM_INIT: input_item.item_type = AES_CFG;
GCM_TEXT: input_item.item_type = AES_DATA;
GCM_AAD: input_item.item_type = AES_GCM_AAD;
GCM_TAG: input_item.item_type = AES_GCM_TAG;
GCM_INIT: input_item.item_type = AES_CFG;
GCM_TEXT: input_item.item_type = AES_DATA;
GCM_AAD: input_item.item_type = AES_GCM_AAD;
GCM_TAG: input_item.item_type = AES_GCM_TAG;
GCM_SAVE: input_item.item_type = AES_GCM_SAVE;
GCM_RESTORE: input_item.item_type = AES_GCM_RESTORE;
default: input_item.item_type = AES_CFG;
endcase

Expand Down
4 changes: 2 additions & 2 deletions hw/ip/aes/dv/env/seq_lib/aes_base_vseq.sv
Original file line number Diff line number Diff line change
Expand Up @@ -248,12 +248,12 @@ class aes_base_vseq extends cip_base_vseq #(
// Write the shadwoed CTRL register.
status_t status;
// Setup fields one by one (0) or all fields together (1).
bit setup_mode = 0;
bit setup_mode = 1;
// Trigger a control update error (1) or not (0). Only applicable if setup_mode = 1.
bit control_update_error = 0;
// Index of the field which shall trigger the control update error.
int idx_error_field = 0;
`DV_CHECK_STD_RANDOMIZE_FATAL(setup_mode)
//`DV_CHECK_STD_RANDOMIZE_FATAL(setup_mode)
Comment on lines +251 to +256
Copy link
Collaborator Author

@nasahlpa nasahlpa Feb 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be removed before merging this PR.

However, in the new aes_gcm_save_restore test I am encountering the following issue when setup_mode = 0.

Then, we are configuring the AES using the following functions:

set_operation(item.operation);
set_mode(item.aes_mode);
set_key_len(item.key_len);
set_manual_operation(item.manual_op);
set_prng_reseed_rate(prs_rate_e'(item.reseed_rate));
set_sideload(item.sideload_en);

Inside these functions, e.g., the set_operation() function, we first check whether the value we want to write is actually different to the value stored in the mirrored shadow register:

virtual task set_operation(bit [1:0] operation);
if (ral.ctrl_shadowed.operation.get_mirrored_value() != operation) begin
ral.ctrl_shadowed.operation.set(operation);
csr_update(.csr(ral.ctrl_shadowed), .en_shadow_wr(1'b1), .blocking(1));
end
endtask // set_operation

When I have the following two messages:

op: AES_DEC
op: AES_ENC

In the second message the operation field gets not updated and stays at AES_DEC instead of going to AES_ENC. The reason is because the result of the check in the function shown above is that
ral.ctrl_shadowed.operation.get_mirrored_value() == AES_ENC
for the second message.

As I have now spend a lot of time debugging this issue, could you please have a look @vogelpi?

Copy link
Collaborator Author

@nasahlpa nasahlpa Feb 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that this is independent of the save and restore - I have commented it out in the aes_gcm_save_restore_vseq.

To visualize the issue, I have expanded the set_operation() function as followed:

  virtual task set_operation(bit [1:0] operation);
    `uvm_info(`gfn, $sformatf("Trying to set op = %b", operation), UVM_LOW)
    if (ral.ctrl_shadowed.operation.get_mirrored_value() != operation) begin
      ral.ctrl_shadowed.operation.set(operation);
      csr_update(.csr(ral.ctrl_shadowed), .en_shadow_wr(1'b1), .blocking(1));
      `uvm_info(`gfn, $sformatf("Set op = %b", operation), UVM_LOW)
    end else begin
      `uvm_info(`gfn, $sformatf("Op is alreay %b", operation), UVM_LOW)
    end
  endtask // set_operation

When sending

op: AES_DEC - 10
op: AES_ENC - 01

I get the following output:

UVM_INFO @   7529006 ps: (aes_base_vseq.sv:133) uvm_test_top.env.virtual_sequencer [uvm_test_top.env.virtual_sequencer.aes_gcm_save_restore_vseq] Trying to set op = 10
UVM_INFO @   7689006 ps: (aes_base_vseq.sv:137) uvm_test_top.env.virtual_sequencer [uvm_test_top.env.virtual_sequencer.aes_gcm_save_restore_vseq] Set op = 10
...
UVM_INFO @  26889006 ps: (aes_base_vseq.sv:133) uvm_test_top.env.virtual_sequencer [uvm_test_top.env.virtual_sequencer.aes_gcm_save_restore_vseq] Trying to set op = 01
UVM_INFO @  26889006 ps: (aes_base_vseq.sv:139) uvm_test_top.env.virtual_sequencer [uvm_test_top.env.virtual_sequencer.aes_gcm_save_restore_vseq] Op is alreay 01

Screenshot from 2025-02-19 17-51-35

As shown in the waveform, the operation stays at AES_DEC although we want to switch to the AES_ENC mode for the second message.

if ($urandom_range(1, 100) > 95) control_update_error = 1;
idx_error_field = $urandom_range(0, 5);
csr_spinwait(.ptr(ral.status.idle), .exp_data(1'b1));
Expand Down
206 changes: 206 additions & 0 deletions hw/ip/aes/dv/env/seq_lib/aes_gcm_save_restore_vseq.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
// Copyright lowRISC contributors (OpenTitan project).
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

// Test processes several AES-GCM messages. During each message, at a random
// item, the GCM save and restore feature is tested. Here, before the new item
// is processed, the current state is saved, the registers are cleared, and the
// state is restored. Then, the test continues processing the item.

// scoreboard is disabled for this test
class aes_gcm_save_restore_vseq extends aes_base_vseq;
`uvm_object_utils(aes_gcm_save_restore_vseq)

`uvm_object_new
aes_message_item msg;
bit rst_set;
bit do_b2b = 0;

task body();
`uvm_info(`gfn, $sformatf("\n\n\t ----| STARTING AES-GCM SAVE & RESTORE SEQUENCE |----\n %s",
cfg.convert2string()), UVM_MEDIUM)

fork
// Start sideload (even if not used).
start_sideload_seq();
join_none

// Generate list of messages.
generate_message_queue();

// Process all messages.
while (message_queue.size() > 0 ) begin
bit new_aad = 1;
bit new_data = 1;
int item_cnt = 1;
int save_restore_item;
int crypto_res;
int aad_len, data_len;
bit operation;
bit [3:0][31:0] predicted_tag;
bit [3:0][31:0] saved_gcm_state, saved_iv;
aes_seq_item cfg_item = new();
aes_seq_item cfg_item_restored_iv = new();
aes_seq_item data_item = new();

`uvm_info(`gfn, $sformatf("Starting New Message - messages left %d",
message_queue.size() ), UVM_MEDIUM)

msg = message_queue.pop_back();
aad_len = msg.aad_length;
data_len = msg.message_length;
generate_aes_item_queue(msg);

cfg_item = aes_item_queue.pop_back();
msg.add_start_msg_item(cfg_item);

// Wait until the DUT is idle. This is required to start the configuration.
csr_spinwait(.ptr(ral.status.idle), .exp_data(1'b1));
// Configure the DUT. Depending on the configuration, this might trigger a PRNG reseed
// operation.
csr_spinwait(.ptr(ral.status.idle) , .exp_data(1'b1));
setup_dut(cfg_item);

// Wait until the DUT is idle. This is required to provide key and IV.
csr_spinwait(.ptr(ral.status.idle), .exp_data(1'b1));
// Provide key, IV and data.
write_data_key_iv(cfg_item, cfg_item, 1, cfg_item.manual_op, 0, 0, rst_set);
if (cfg_item.manual_op && !rst_set) trigger();
if (cfg_item.manual_op && !rst_set) trigger();

// At which item do we perform the save and restore?
// Do not perform before we have processed the first item and also when
// handling the tag.
save_restore_item = 0;//$urandom_range(2, aes_item_queue.size() - 1);

// Process all items.
while (aes_item_queue.size() > 0 ) begin
int valid_bytes;
// Wait until the DUT is idle.
csr_spinwait(.ptr(ral.status.idle), .exp_data(1'b1));

data_item = aes_item_queue.pop_back();
`uvm_info(`gfn, $sformatf("\n\t ----AES ITEM %s",
data_item.convert2string()), UVM_MEDIUM)

if (item_cnt == save_restore_item) begin
`uvm_info(`gfn, $sformatf("Saving AES-GCM state before processing item %d",
item_cnt), UVM_MEDIUM)
set_gcm_phase(GCM_SAVE, 16, 0);
if (cfg_item.manual_op) trigger();
// Save the current AES-GCM context.
csr_spinwait(.ptr(ral.status.output_valid), .exp_data(1'b1));
read_data(saved_gcm_state, do_b2b);
// Save the current IV.
csr_spinwait(.ptr(ral.status.idle), .exp_data(1'b1));
read_iv(saved_iv, do_b2b);
`downcast(cfg_item_restored_iv, cfg_item.clone());
cfg_item_restored_iv.iv = saved_iv;
cfg_item_restored_iv.iv[3] = '0; // Clear upper IV bits to restore the original IV.
// Clear the AES IV, data in, and data out registers.
csr_spinwait(.ptr(ral.status.idle), .exp_data(1'b1));
clear_regs(2'b11);
csr_spinwait(.ptr(ral.status.idle), .exp_data(1'b1));
`uvm_info(`gfn, $sformatf("Restoring AES-GCM state before processing item %d",
item_cnt), UVM_MEDIUM)
// Reconfigure the initial IV and the key.
write_data_key_iv(cfg_item_restored_iv, data_item, 1, cfg_item.manual_op,
0, 0, rst_set);
if (cfg_item.manual_op) trigger();
if (cfg_item.manual_op) trigger();

// Restore the saved AES-GCM context.
set_gcm_phase(GCM_RESTORE, 16, 1);
csr_spinwait(.ptr(ral.status.input_ready), .exp_data(1'b1));
add_data(saved_gcm_state, do_b2b);
// Restore the saved IV.
csr_spinwait(.ptr(ral.status.idle), .exp_data(1'b1));
write_iv(saved_iv, do_b2b);
if (cfg_item.manual_op) trigger();
`uvm_info(`gfn, $sformatf("Proceeding with item %d", item_cnt), UVM_MEDIUM)
csr_spinwait(.ptr(ral.status.idle), .exp_data(1'b1));
// Enforce setting the GCM phase for the next item.
new_aad = 1;
new_data = 1;
end

if (data_item.item_type == AES_GCM_AAD) begin
if (new_aad || data_item.data_len[3:0] != 4'd0) begin
// Configure AAD phase as this is either the first AAD block or a
// partial block.
valid_bytes = data_item.data_len == 0 ? 16 : data_item.data_len;
set_gcm_phase(GCM_AAD, valid_bytes, 0);
new_aad = 0;
end
csr_spinwait(.ptr(ral.status.input_ready), .exp_data(1'b1));
add_data(data_item.data_in, do_b2b);
if (cfg_item.manual_op) trigger();
// Add AAD to message.
msg.add_aad_item(data_item);
end else if (data_item.item_type == AES_DATA) begin
if (new_data || data_item.data_len[3:0] != 4'd0) begin
// Configure TEXT phase as this is either the first plaintext block or a
// partial block.
valid_bytes = data_item.data_len == 0 ? 16 : data_item.data_len;
set_gcm_phase(GCM_TEXT, valid_bytes, 0);
new_aad = 0;
end
csr_spinwait(.ptr(ral.status.input_ready), .exp_data(1'b1));
add_data(data_item.data_in, do_b2b);
if (cfg_item.manual_op) trigger();
csr_spinwait(.ptr(ral.status.output_valid), .exp_data(1'b1));
read_data(data_item.data_out, do_b2b);
// Add input and output data block to message.
msg.add_data_item(data_item);
end else if (data_item.item_type == AES_GCM_TAG) begin
set_gcm_phase(GCM_TAG, 16, 0);
csr_spinwait(.ptr(ral.status.input_ready), .exp_data(1'b1));
add_data(data_item.data_in, do_b2b);
if (cfg_item.manual_op) trigger();
csr_spinwait(.ptr(ral.status.output_valid), .exp_data(1'b1));
read_data(data_item.data_out, do_b2b);
// Add tag to message.
msg.add_tag_item(data_item);
end
item_cnt++;
end

// Check if we got the correct output.
operation = msg.aes_operation == AES_ENC ? 1'b0 :
msg.aes_operation == AES_DEC ? 1'b1 : 1'b0;
msg.alloc_predicted_msg();
c_dpi_aes_crypt_message(cfg.ref_model, operation, msg.aes_mode, msg.aes_iv,
msg.aes_keylen, msg.aes_key[0] ^ msg.aes_key[1],
data_len, aad_len, msg.input_msg,
msg.input_aad, msg.output_tag, msg.predicted_msg,
predicted_tag, crypto_res);

// Check the error code of the c_dpi library.
if (crypto_res < 0) begin
// The underlying c_dpi cyrpto lib returns an error code < 0 if something
// is wrong.
`uvm_fatal(`gfn, "c_dpi crypto lib error detected!\n")
end

// Check output data.
for (int n =0 ; n < data_len; n++) begin
if ((msg.output_msg[n] != msg.predicted_msg[n]) && ~msg.output_cleared[n]) begin
`uvm_fatal(`gfn, $sformatf(" output_msg[%d] (0x%x) != predicted_msg[%d] (0x%x) \n",
n, msg.output_msg[n], n, msg.predicted_msg[n]))
end
end

// Check tag.
if (msg.aes_operation == AES_ENC) begin
`uvm_info(`gfn, $sformatf("DOING TAG CHECK"), UVM_MEDIUM)
predicted_tag = aes_transpose(predicted_tag);
foreach(predicted_tag[n]) begin
if ((predicted_tag[n] != msg.output_tag[n]) && msg.output_tag_vld) begin
`uvm_fatal(`gfn, $sformatf(" predicted_tag[%d] (0x%x) != output_tag[%d] (0x%x) \n",
n, predicted_tag[n], n, msg.output_tag[n]))
end
end
end
end
endtask : body
endclass
1 change: 1 addition & 0 deletions hw/ip/aes/dv/env/seq_lib/aes_vseq_list.sv
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@
`include "aes_core_fi_vseq.sv"
`include "aes_readability_vseq.sv"
`include "aes_stress_all_vseq.sv"
`include "aes_gcm_save_restore_vseq.sv"
56 changes: 56 additions & 0 deletions hw/ip/aes/dv/tests/aes_gcm_save_restore_test.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Copyright lowRISC contributors (OpenTitan project).
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

class aes_gcm_save_restore_test extends aes_base_test;

`uvm_component_utils(aes_gcm_save_restore_test)
`uvm_component_new


virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
configure_env();
endfunction

function void configure_env();
super.configure_env();
// disable scoreboard for this directed test
cfg.en_scb = 0;
cfg.error_types = 0; // no errors
cfg.num_messages_min = 1;
cfg.num_messages_max = 5;
// message related knobs
cfg.ecb_weight = 0;
cfg.cbc_weight = 0;
cfg.ctr_weight = 0;
cfg.ofb_weight = 0;
cfg.cfb_weight = 0;
cfg.gcm_weight = 100;

cfg.message_len_min = 16; // one block (16bytes=128bits)
cfg.message_len_max = 123; //
cfg.aad_len_min = 16; //
cfg.aad_len_max = 123; //
cfg.manual_operation_pct = 50;
cfg.use_key_mask = 0;

cfg.fixed_data_en = 0;
cfg.fixed_key_en = 0;

cfg.fixed_operation_en = 0;
cfg.fixed_operation = aes_pkg::AES_DEC;

cfg.fixed_keylen_en = 0;
cfg.fixed_keylen = 3'b001;

cfg.fixed_iv_en = 0;
cfg.fixed_aad_en = 0;

cfg.random_data_key_iv_order = 0;
cfg.read_prob = 100;
cfg.write_prob = 100;

`DV_CHECK_RANDOMIZE_FATAL(cfg)
endfunction
endclass : aes_gcm_save_restore_test
1 change: 1 addition & 0 deletions hw/ip/aes/dv/tests/aes_test.core
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ filesets:
- aes_manual_config_err_test.sv: {is_include_file: true}
- aes_reseed_test.sv: {is_include_file: true}
- aes_fi_test.sv: {is_include_file: true}
- aes_gcm_save_restore_test.sv: {is_include_file: true}
file_type: systemVerilogSource

targets:
Expand Down
1 change: 1 addition & 0 deletions hw/ip/aes/dv/tests/aes_test_pkg.sv
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,5 @@ package aes_test_pkg;
`include "aes_manual_config_err_test.sv"
`include "aes_reseed_test.sv"
`include "aes_fi_test.sv"
`include "aes_gcm_save_restore_test.sv"
endpackage
14 changes: 14 additions & 0 deletions sw/device/lib/dif/dif_aes.c
Original file line number Diff line number Diff line change
Expand Up @@ -466,3 +466,17 @@ dif_result_t dif_aes_read_iv(const dif_aes_t *aes, dif_aes_iv_t *iv) {
}
return kDifOk;
}

dif_result_t dif_aes_load_iv(const dif_aes_t *aes, const dif_aes_iv_t iv) {
if (aes == NULL) {
return kDifBadArg;
}

if (!aes_idle(aes)) {
return kDifUnavailable;
}

aes_set_multireg(aes, &iv.iv[0], AES_IV_MULTIREG_COUNT, AES_IV_0_REG_OFFSET);

return kDifOk;
}
Loading
Loading