|
| 1 | +// Copyright lowRISC contributors (OpenTitan project). |
| 2 | +// Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| 3 | +// SPDX-License-Identifier: Apache-2.0 |
| 4 | + |
| 5 | +// Test processes several AES-GCM messages. During each message, at a random |
| 6 | +// item, the GCM save and restore feature is tested. Here, before the new item |
| 7 | +// is processed, the current state is saved, the registers are cleared, and the |
| 8 | +// state is restored. Then, the test continues processing the item. |
| 9 | + |
| 10 | +// scoreboard is disabled for this test |
| 11 | +class aes_gcm_save_restore_vseq extends aes_base_vseq; |
| 12 | + `uvm_object_utils(aes_gcm_save_restore_vseq) |
| 13 | + |
| 14 | + `uvm_object_new |
| 15 | + aes_message_item msg; |
| 16 | + bit rst_set; |
| 17 | + bit do_b2b = 0; |
| 18 | + |
| 19 | + task body(); |
| 20 | + `uvm_info(`gfn, $sformatf("\n\n\t ----| STARTING AES-GCM SAVE & RESTORE SEQUENCE |----\n %s", |
| 21 | + cfg.convert2string()), UVM_MEDIUM) |
| 22 | + |
| 23 | + fork |
| 24 | + // Start sideload (even if not used). |
| 25 | + start_sideload_seq(); |
| 26 | + join_none |
| 27 | + |
| 28 | + // Generate list of messages. |
| 29 | + generate_message_queue(); |
| 30 | + |
| 31 | + // Process all messages. |
| 32 | + while (message_queue.size() > 0 ) begin |
| 33 | + bit new_aad = 1; |
| 34 | + bit new_data = 1; |
| 35 | + int item_cnt = 1; |
| 36 | + int save_restore_item; |
| 37 | + int crypto_res; |
| 38 | + int aad_len, data_len; |
| 39 | + bit operation; |
| 40 | + bit [3:0][31:0] predicted_tag; |
| 41 | + bit [3:0][31:0] saved_gcm_state, saved_iv; |
| 42 | + aes_seq_item cfg_item = new(); |
| 43 | + aes_seq_item cfg_item_restored_iv = new(); |
| 44 | + aes_seq_item data_item = new(); |
| 45 | + |
| 46 | + `uvm_info(`gfn, $sformatf("Starting New Message - messages left %d", |
| 47 | + message_queue.size() ), UVM_MEDIUM) |
| 48 | + |
| 49 | + msg = message_queue.pop_back(); |
| 50 | + aad_len = msg.aad_length; |
| 51 | + data_len = msg.message_length; |
| 52 | + generate_aes_item_queue(msg); |
| 53 | + |
| 54 | + cfg_item = aes_item_queue.pop_back(); |
| 55 | + msg.add_start_msg_item(cfg_item); |
| 56 | + |
| 57 | + // Wait until the DUT is idle. This is required to start the configuration. |
| 58 | + csr_spinwait(.ptr(ral.status.idle), .exp_data(1'b1)); |
| 59 | + // Configure the DUT. Depending on the configuration, this might trigger a PRNG reseed |
| 60 | + // operation. |
| 61 | + csr_spinwait(.ptr(ral.status.idle) , .exp_data(1'b1)); |
| 62 | + setup_dut(cfg_item); |
| 63 | + |
| 64 | + // Wait until the DUT is idle. This is required to provide key and IV. |
| 65 | + csr_spinwait(.ptr(ral.status.idle), .exp_data(1'b1)); |
| 66 | + // Provide key, IV and data. |
| 67 | + write_data_key_iv(cfg_item, cfg_item, 1, cfg_item.manual_op, 0, 0, rst_set); |
| 68 | + if (cfg_item.manual_op && !rst_set) trigger(); |
| 69 | + if (cfg_item.manual_op && !rst_set) trigger(); |
| 70 | + |
| 71 | + // At which item do we perform the save and restore? |
| 72 | + // Do not perform before we have processed the first item and also when |
| 73 | + // handling the tag. |
| 74 | + save_restore_item = 0;//$urandom_range(2, aes_item_queue.size() - 1); |
| 75 | + |
| 76 | + // Process all items. |
| 77 | + while (aes_item_queue.size() > 0 ) begin |
| 78 | + int valid_bytes; |
| 79 | + // Wait until the DUT is idle. |
| 80 | + csr_spinwait(.ptr(ral.status.idle), .exp_data(1'b1)); |
| 81 | + |
| 82 | + data_item = aes_item_queue.pop_back(); |
| 83 | + `uvm_info(`gfn, $sformatf("\n\t ----AES ITEM %s", |
| 84 | + data_item.convert2string()), UVM_MEDIUM) |
| 85 | + |
| 86 | + if (item_cnt == save_restore_item) begin |
| 87 | + `uvm_info(`gfn, $sformatf("Saving AES-GCM state before processing item %d", |
| 88 | + item_cnt), UVM_MEDIUM) |
| 89 | + set_gcm_phase(GCM_SAVE, 16, 0); |
| 90 | + if (cfg_item.manual_op) trigger(); |
| 91 | + // Save the current AES-GCM context. |
| 92 | + csr_spinwait(.ptr(ral.status.output_valid), .exp_data(1'b1)); |
| 93 | + read_data(saved_gcm_state, do_b2b); |
| 94 | + // Save the current IV. |
| 95 | + csr_spinwait(.ptr(ral.status.idle), .exp_data(1'b1)); |
| 96 | + read_iv(saved_iv, do_b2b); |
| 97 | + `downcast(cfg_item_restored_iv, cfg_item.clone()); |
| 98 | + cfg_item_restored_iv.iv = saved_iv; |
| 99 | + cfg_item_restored_iv.iv[3] = '0; // Clear upper IV bits to restore the original IV. |
| 100 | + // Clear the AES IV, data in, and data out registers. |
| 101 | + csr_spinwait(.ptr(ral.status.idle), .exp_data(1'b1)); |
| 102 | + clear_regs(2'b11); |
| 103 | + csr_spinwait(.ptr(ral.status.idle), .exp_data(1'b1)); |
| 104 | + `uvm_info(`gfn, $sformatf("Restoring AES-GCM state before processing item %d", |
| 105 | + item_cnt), UVM_MEDIUM) |
| 106 | + // Reconfigure the initial IV and the key. |
| 107 | + write_data_key_iv(cfg_item_restored_iv, data_item, 1, cfg_item.manual_op, |
| 108 | + 0, 0, rst_set); |
| 109 | + if (cfg_item.manual_op) trigger(); |
| 110 | + if (cfg_item.manual_op) trigger(); |
| 111 | + |
| 112 | + // Restore the saved AES-GCM context. |
| 113 | + set_gcm_phase(GCM_RESTORE, 16, 1); |
| 114 | + csr_spinwait(.ptr(ral.status.input_ready), .exp_data(1'b1)); |
| 115 | + add_data(saved_gcm_state, do_b2b); |
| 116 | + // Restore the saved IV. |
| 117 | + csr_spinwait(.ptr(ral.status.idle), .exp_data(1'b1)); |
| 118 | + write_iv(saved_iv, do_b2b); |
| 119 | + if (cfg_item.manual_op) trigger(); |
| 120 | + `uvm_info(`gfn, $sformatf("Proceeding with item %d", item_cnt), UVM_MEDIUM) |
| 121 | + csr_spinwait(.ptr(ral.status.idle), .exp_data(1'b1)); |
| 122 | + // Enforce setting the GCM phase for the next item. |
| 123 | + new_aad = 1; |
| 124 | + new_data = 1; |
| 125 | + end |
| 126 | + |
| 127 | + if (data_item.item_type == AES_GCM_AAD) begin |
| 128 | + if (new_aad || data_item.data_len[3:0] != 4'd0) begin |
| 129 | + // Configure AAD phase as this is either the first AAD block or a |
| 130 | + // partial block. |
| 131 | + valid_bytes = data_item.data_len == 0 ? 16 : data_item.data_len; |
| 132 | + set_gcm_phase(GCM_AAD, valid_bytes, 0); |
| 133 | + new_aad = 0; |
| 134 | + end |
| 135 | + csr_spinwait(.ptr(ral.status.input_ready), .exp_data(1'b1)); |
| 136 | + add_data(data_item.data_in, do_b2b); |
| 137 | + if (cfg_item.manual_op) trigger(); |
| 138 | + // Add AAD to message. |
| 139 | + msg.add_aad_item(data_item); |
| 140 | + end else if (data_item.item_type == AES_DATA) begin |
| 141 | + if (new_data || data_item.data_len[3:0] != 4'd0) begin |
| 142 | + // Configure TEXT phase as this is either the first plaintext block or a |
| 143 | + // partial block. |
| 144 | + valid_bytes = data_item.data_len == 0 ? 16 : data_item.data_len; |
| 145 | + set_gcm_phase(GCM_TEXT, valid_bytes, 0); |
| 146 | + new_aad = 0; |
| 147 | + end |
| 148 | + csr_spinwait(.ptr(ral.status.input_ready), .exp_data(1'b1)); |
| 149 | + add_data(data_item.data_in, do_b2b); |
| 150 | + if (cfg_item.manual_op) trigger(); |
| 151 | + csr_spinwait(.ptr(ral.status.output_valid), .exp_data(1'b1)); |
| 152 | + read_data(data_item.data_out, do_b2b); |
| 153 | + // Add input and output data block to message. |
| 154 | + msg.add_data_item(data_item); |
| 155 | + end else if (data_item.item_type == AES_GCM_TAG) begin |
| 156 | + set_gcm_phase(GCM_TAG, 16, 0); |
| 157 | + csr_spinwait(.ptr(ral.status.input_ready), .exp_data(1'b1)); |
| 158 | + add_data(data_item.data_in, do_b2b); |
| 159 | + if (cfg_item.manual_op) trigger(); |
| 160 | + csr_spinwait(.ptr(ral.status.output_valid), .exp_data(1'b1)); |
| 161 | + read_data(data_item.data_out, do_b2b); |
| 162 | + // Add tag to message. |
| 163 | + msg.add_tag_item(data_item); |
| 164 | + end |
| 165 | + item_cnt++; |
| 166 | + end |
| 167 | + |
| 168 | + // Check if we got the correct output. |
| 169 | + operation = msg.aes_operation == AES_ENC ? 1'b0 : |
| 170 | + msg.aes_operation == AES_DEC ? 1'b1 : 1'b0; |
| 171 | + msg.alloc_predicted_msg(); |
| 172 | + c_dpi_aes_crypt_message(cfg.ref_model, operation, msg.aes_mode, msg.aes_iv, |
| 173 | + msg.aes_keylen, msg.aes_key[0] ^ msg.aes_key[1], |
| 174 | + data_len, aad_len, msg.input_msg, |
| 175 | + msg.input_aad, msg.output_tag, msg.predicted_msg, |
| 176 | + predicted_tag, crypto_res); |
| 177 | + |
| 178 | + // Check the error code of the c_dpi library. |
| 179 | + if (crypto_res < 0) begin |
| 180 | + // The underlying c_dpi cyrpto lib returns an error code < 0 if something |
| 181 | + // is wrong. |
| 182 | + `uvm_fatal(`gfn, "c_dpi crypto lib error detected!\n") |
| 183 | + end |
| 184 | + |
| 185 | + // Check output data. |
| 186 | + for (int n =0 ; n < data_len; n++) begin |
| 187 | + if ((msg.output_msg[n] != msg.predicted_msg[n]) && ~msg.output_cleared[n]) begin |
| 188 | + `uvm_fatal(`gfn, $sformatf(" output_msg[%d] (0x%x) != predicted_msg[%d] (0x%x) \n", |
| 189 | + n, msg.output_msg[n], n, msg.predicted_msg[n])) |
| 190 | + end |
| 191 | + end |
| 192 | + |
| 193 | + // Check tag. |
| 194 | + if (msg.aes_operation == AES_ENC) begin |
| 195 | + `uvm_info(`gfn, $sformatf("DOING TAG CHECK"), UVM_MEDIUM) |
| 196 | + predicted_tag = aes_transpose(predicted_tag); |
| 197 | + foreach(predicted_tag[n]) begin |
| 198 | + if ((predicted_tag[n] != msg.output_tag[n]) && msg.output_tag_vld) begin |
| 199 | + `uvm_fatal(`gfn, $sformatf(" predicted_tag[%d] (0x%x) != output_tag[%d] (0x%x) \n", |
| 200 | + n, predicted_tag[n], n, msg.output_tag[n])) |
| 201 | + end |
| 202 | + end |
| 203 | + end |
| 204 | + end |
| 205 | + endtask : body |
| 206 | +endclass |
0 commit comments