[sw,tests] Enter RMA LC_STATE and check flash access and wipe
For tests:
chip_sw_flash_rma_unlocked
chip_sw_flash_creator_seed_wipe_on_rma
The system is initially started in Dev LC_STATE and data is written to all flash partitions.
The system is then set to enter RMA mode. After entry the flash is read again and checked
that the flash is accessible and that the contents have been wiped and differ from the
previously written values.
Signed-off-by: Dave Williams <dave.williams@ensilica.com>
diff --git a/hw/dv/sv/mem_bkdr_util/mem_bkdr_util__otp.sv b/hw/dv/sv/mem_bkdr_util/mem_bkdr_util__otp.sv
index d26b8b6..630f840 100644
--- a/hw/dv/sv/mem_bkdr_util/mem_bkdr_util__otp.sv
+++ b/hw/dv/sv/mem_bkdr_util/mem_bkdr_util__otp.sv
@@ -53,6 +53,37 @@
write64(Secret0DigestOffset, digest);
endfunction
+virtual function void otp_write_secret2_partition(bit [RmaTokenSize*8-1:0] rma_unlock_token,
+ bit [CreatorRootKeyShare0Size*8-1:0] creator_root_key0,
+ bit [CreatorRootKeyShare1Size*8-1:0] creator_root_key1
+);
+
+ bit [Secret2DigestSize*8-1:0] digest;
+
+ bit [RmaTokenSize*8-1:0] scrambled_unlock_token;
+ bit [CreatorRootKeyShare0Size*8-1:0] scrambled_root_key0;
+ bit [CreatorRootKeyShare1Size*8-1:0] scrambled_root_key1;
+ bit [bus_params_pkg::BUS_DW-1:0] secret_data[$];
+
+ for (int i = 0; i < RmaTokenSize; i+=8) begin
+ scrambled_unlock_token[i*8+:64] = scramble_data(rma_unlock_token[i*8+:64], Secret2Idx);
+ write64(i + RmaTokenOffset, scrambled_unlock_token[i*8+:64]);
+ end
+ for (int i = 0; i < CreatorRootKeyShare0Size; i+=8) begin
+ scrambled_root_key0[i*8+:64] = scramble_data(creator_root_key0[i*8+:64], Secret2Idx);
+ write64(i + CreatorRootKeyShare0Offset, scrambled_root_key0[i*8+:64]);
+ end
+ for (int i = 0; i < CreatorRootKeyShare1Size; i+=8) begin
+ scrambled_root_key1[i*8+:64] = scramble_data(creator_root_key1[i*8+:64], Secret2Idx);
+ write64(i + CreatorRootKeyShare1Offset, scrambled_root_key1[i*8+:64]);
+ end
+
+ secret_data = {<<32 {scrambled_root_key1, scrambled_root_key0, scrambled_unlock_token}};
+ digest = cal_digest(Secret2Idx, secret_data);
+
+ write64(Secret2DigestOffset, digest);
+endfunction
+
virtual function void otp_write_hw_cfg_partition(
bit [DeviceIdSize*8-1:0] device_id, bit [ManufStateSize*8-1:0] manuf_state,
bit [EnSramIfetchSize*8-1:0] en_sram_ifetch,
diff --git a/hw/top_earlgrey/data/chip_testplan.hjson b/hw/top_earlgrey/data/chip_testplan.hjson
index 3ac1c17..aa9d99e 100644
--- a/hw/top_earlgrey/data/chip_testplan.hjson
+++ b/hw/top_earlgrey/data/chip_testplan.hjson
@@ -2279,7 +2279,7 @@
interface to flash ctrl is actually for the closed src.
'''
milestone: V2
- tests: []
+ tests: ["chip_sw_flash_rma_unlocked"]
}
{
name: chip_conn_flash_jtag
@@ -2347,8 +2347,7 @@
desc: '''Verify that the creator seed is wiped by the flash ctrl on RMA entry.
'''
milestone: V2
- tests: []
-
+ tests: ["chip_sw_flash_rma_unlocked"]
}
{
name: chip_sw_flash_lc_owner_seed_sw_rw_en
diff --git a/hw/top_earlgrey/dv/chip_sim_cfg.hjson b/hw/top_earlgrey/dv/chip_sim_cfg.hjson
index 757a417..ed47f3d 100644
--- a/hw/top_earlgrey/dv/chip_sim_cfg.hjson
+++ b/hw/top_earlgrey/dv/chip_sim_cfg.hjson
@@ -346,6 +346,13 @@
en_run_modes: ["sw_test_mode_test_rom"]
}
{
+ name: chip_sw_flash_rma_unlocked
+ uvm_test_seq: chip_sw_flash_rma_unlocked_vseq
+ sw_images: ["sw/device/tests/flash_rma_unlocked_test:0"]
+ en_run_modes: ["sw_test_mode_common"]
+ run_opts: ["+sw_test_timeout_ns=200000000"]
+ }
+ {
name: chip_sw_lc_ctrl_otp_hw_cfg
uvm_test_seq: chip_sw_base_vseq
sw_images: ["sw/device/tests/lc_ctrl_otp_hw_cfg_test:1"]
diff --git a/hw/top_earlgrey/dv/env/chip_env.core b/hw/top_earlgrey/dv/env/chip_env.core
index c00a106..e8d93b5 100644
--- a/hw/top_earlgrey/dv/env/chip_env.core
+++ b/hw/top_earlgrey/dv/env/chip_env.core
@@ -48,6 +48,7 @@
- seq_lib/chip_sw_gpio_smoke_vseq.sv: {is_include_file: true}
- seq_lib/chip_sw_gpio_vseq.sv: {is_include_file: true}
- seq_lib/chip_sw_flash_ctrl_lc_rw_en_vseq.sv: {is_include_file: true}
+ - seq_lib/chip_sw_flash_rma_unlocked_vseq.sv: {is_include_file: true}
- seq_lib/chip_sw_lc_ctrl_transition_vseq.sv: {is_include_file: true}
- seq_lib/chip_sw_spi_tx_rx_vseq.sv: {is_include_file: true}
- seq_lib/chip_sw_rom_ctrl_integrity_check_vseq.sv: {is_include_file: true}
diff --git a/hw/top_earlgrey/dv/env/seq_lib/chip_sw_flash_rma_unlocked_vseq.sv b/hw/top_earlgrey/dv/env/seq_lib/chip_sw_flash_rma_unlocked_vseq.sv
new file mode 100644
index 0000000..a97bf38
--- /dev/null
+++ b/hw/top_earlgrey/dv/env/seq_lib/chip_sw_flash_rma_unlocked_vseq.sv
@@ -0,0 +1,152 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+class chip_sw_flash_rma_unlocked_vseq extends chip_sw_base_vseq;
+ `uvm_object_utils(chip_sw_flash_rma_unlocked_vseq)
+
+ `uvm_object_new
+
+ // LC sends two 64-bit msg as input token.
+ localparam uint TokenWidthBit = kmac_pkg::MsgWidth * 2;
+ localparam uint TokenWidthByte = TokenWidthBit / 8;
+ localparam uint KeyWidthBit = otp_ctrl_reg_pkg::CreatorRootKeyShare0Size * 8;
+ localparam uint KeyWidthByte = KeyWidthBit / 8;
+
+ localparam string LC_CTRL_TRANS_SUCCESS_PATH =
+ "tb.dut.top_earlgrey.u_lc_ctrl.u_lc_ctrl_fsm.trans_success_o";
+
+ // TODO(lowRISC/opentitan:#11795): replace with SW symbol backdoor write
+ // when this is fixed for ROM.
+ // Currently using retention SRAM to pass test_phase to program.
+ localparam string SRAM_CTRL_RET_HDL_PATH = "tb.dut.top_earlgrey.u_sram_ctrl_ret_aon";
+ localparam string SRAM_CTRL_RET_NONCE_PATH = {SRAM_CTRL_RET_HDL_PATH, ".nonce_q"};
+ localparam string SRAM_CTRL_RET_KEY_PATH = {SRAM_CTRL_RET_HDL_PATH, ".key_q"};
+
+ bit [sram_scrambler_pkg::SRAM_BLOCK_WIDTH-1:0] sram_ret_nonce;
+ bit [sram_scrambler_pkg::SRAM_KEY_WIDTH-1:0] sram_ret_key;
+
+ // TODO(lowRISC/opentitan:#11795): replace with SW symbol backdoor write
+ // when this is fixed for ROM.
+ // Currently a fixed unlock token which will match in software.
+ bit [7:0] rma_unlock_token[TokenWidthByte] = '{
+ 8'h00,
+ 8'h01,
+ 8'h02,
+ 8'h03,
+ 8'h04,
+ 8'h05,
+ 8'h06,
+ 8'h07,
+ 8'h08,
+ 8'h09,
+ 8'h0a,
+ 8'h0b,
+ 8'h0c,
+ 8'h0d,
+ 8'h0e,
+ 8'h0f
+ };
+
+ rand bit [7:0] creator_root_key0[KeyWidthByte];
+ rand bit [7:0] creator_root_key1[KeyWidthByte];
+
+ // This function takes the token value from LC_CTRL token CSRs, then runs through cshake128 to
+ // get a 768-bit XORed token output.
+ // The first 128 bits of the decoded token should match the OTP's secret2 paritition's
+ // descrambled tokens value.
+ virtual function bit [TokenWidthBit-1:0] get_otp_token(bit [7:0] token_in[TokenWidthByte]);
+ bit [7:0] dpi_digest[kmac_pkg::AppDigestW/8];
+ bit [kmac_pkg::AppDigestW-1:0] digest_bits;
+
+ digestpp_dpi_pkg::c_dpi_cshake128(token_in, "", "LC_CTRL", TokenWidthByte,
+ kmac_pkg::AppDigestW / 8, dpi_digest);
+
+ digest_bits = {<<byte{dpi_digest}};
+ return (digest_bits[TokenWidthBit-1:0]);
+ endfunction
+
+ virtual function bit [KeyWidthBit-1:0] get_otp_key(bit [7:0] key_in[KeyWidthByte]);
+ bit [kmac_pkg::AppDigestW-1:0] digest_bits;
+ bit [7:0] dpi_digest[kmac_pkg::AppDigestW/8];
+
+ digestpp_dpi_pkg::c_dpi_cshake128(key_in, "", "LC_CTRL", KeyWidthByte, kmac_pkg::AppDigestW / 8,
+ dpi_digest);
+
+ digest_bits = {<<byte{dpi_digest}};
+ return (digest_bits[KeyWidthBit-1:0]);
+ endfunction
+
+ // When the RMA transition has been completed the CPU will be disabled
+ // and therefore it cannot be detected in SW. Detect this transition here
+ // to allow the CPU to be reset.
+ virtual task wait_for_transition();
+ int retval;
+ int transition_success = 0;
+ retval = uvm_hdl_check_path(LC_CTRL_TRANS_SUCCESS_PATH);
+ `DV_CHECK_EQ_FATAL(retval, 1, $sformatf(
+ "Hierarchical path %0s appears to be invalid.", LC_CTRL_TRANS_SUCCESS_PATH))
+ while (transition_success == 0) begin
+ retval = uvm_hdl_read(LC_CTRL_TRANS_SUCCESS_PATH, transition_success);
+ `DV_CHECK_EQ(retval, 1, $sformatf("uvm_hdl_read failed for %0s", LC_CTRL_TRANS_SUCCESS_PATH))
+ cfg.clk_rst_vif.wait_clks(1);
+ end
+ endtask
+
+ // TODO(lowRISC/opentitan:#11795): replace with SW symbol backdoor write
+ // when this is fixed for ROM.
+ // Currently using retention SRAM to pass test_phase to program.
+ // Initial write to retention SRAM with a value of zero for
+ // test phase.
+ virtual task ret_backdoor_write();
+ int retval;
+ retval = uvm_hdl_read(SRAM_CTRL_RET_NONCE_PATH, sram_ret_nonce);
+ retval = uvm_hdl_read(SRAM_CTRL_RET_KEY_PATH, sram_ret_key);
+ ret_sram_bkdr_write32(0, 0, sram_ret_key, sram_ret_nonce);
+ endtask
+
+ virtual task dut_init(string reset_kind = "HARD");
+ super.dut_init(reset_kind);
+ // Override the LC partition to Dev state.
+ cfg.mem_bkdr_util_h[Otp].otp_write_lc_partition_state(LcStDev);
+ // TODO(lowRISC/opentitan:#11795): replace with SW symbol backdoor write
+ // when this is fixed for ROM.
+ // The following overwrite does not yet work.
+ sw_symbol_backdoor_overwrite("kLcRmaUnlockToken", rma_unlock_token, Rom, SwTypeRom);
+ endtask
+
+ virtual task body();
+ // First Boot.
+ super.body();
+
+ // TODO(lowRISC/opentitan:#11795): replace with SW symbol backdoor write
+ // when this is fixed for ROM.
+ // Disable SRAM data integrity checks and do SRAM write.
+ cfg.disable_d_user_data_intg_check_for_passthru_mem = 1;
+ cfg.en_scb_mem_chk = 0;
+ ret_backdoor_write();
+
+ wait(cfg.sw_test_status_vif.sw_test_status == SwTestStatusInTest);
+ wait(cfg.sw_test_status_vif.sw_test_status == SwTestStatusInWfi);
+ apply_reset();
+
+ // Second Boot.
+ // Override the rma unlock token to match SW test's input token.
+ cfg.mem_bkdr_util_h[Otp].otp_write_secret2_partition(
+ .rma_unlock_token(get_otp_token(rma_unlock_token)),
+ .creator_root_key0(get_otp_key(creator_root_key0)),
+ .creator_root_key1(get_otp_key(creator_root_key1)));
+
+ wait(cfg.sw_test_status_vif.sw_test_status == SwTestStatusInTest);
+ wait(cfg.sw_test_status_vif.sw_test_status == SwTestStatusInWfi);
+
+ wait_for_transition();
+ cfg.clk_rst_vif.wait_clks(1000);
+ apply_reset();
+
+ // Third Boot.
+ wait(cfg.sw_test_status_vif.sw_test_status == SwTestStatusInTest);
+
+ endtask
+
+endclass
diff --git a/hw/top_earlgrey/dv/env/seq_lib/chip_vseq_list.sv b/hw/top_earlgrey/dv/env/seq_lib/chip_vseq_list.sv
index 3b38043..d38c1ea 100644
--- a/hw/top_earlgrey/dv/env/seq_lib/chip_vseq_list.sv
+++ b/hw/top_earlgrey/dv/env/seq_lib/chip_vseq_list.sv
@@ -16,6 +16,7 @@
`include "chip_sw_gpio_smoke_vseq.sv"
`include "chip_sw_gpio_vseq.sv"
`include "chip_sw_flash_ctrl_lc_rw_en_vseq.sv"
+`include "chip_sw_flash_rma_unlocked_vseq.sv"
`include "chip_sw_lc_ctrl_transition_vseq.sv"
`include "chip_sw_spi_tx_rx_vseq.sv"
`include "chip_sw_rom_ctrl_integrity_check_vseq.sv"
diff --git a/sw/device/lib/testing/check.h b/sw/device/lib/testing/check.h
index 7b2ea16..6beb65e 100644
--- a/sw/device/lib/testing/check.h
+++ b/sw/device/lib/testing/check.h
@@ -85,6 +85,44 @@
} while (false)
/**
+ * Compare `num_items_` of `actual_` against `not_expected_` buffer.
+ *
+ * Prints matches between `actual_` and `not_expected_` before logging an error.
+ * Note, the matches between the actual and not_expected buffer values are
+ * logged via LOG_INFO _before_ the error is logged with LOG_ERROR, since by
+ * default DV simulations are configured to terminate upon the first error.
+ *
+ * @param actual_ Buffer containing actual values.
+ * @param not_expected_ Buffer containing not expected values.
+ * @param num_items_ Number of items to compare.
+ * @param ... Arguments to a LOG_* macro, which are evaluated if the check.
+ */
+#define CHECK_BUFFER_NOT_EQ(actual_, not_expected_, num_items_, ...) \
+ do { \
+ /* `sizeof(actual_[0])` is used to determine the size of each item in the \
+ * buffer. */ \
+ if (memcmp((actual_), (not_expected_), \
+ num_items_ * sizeof((actual_)[0])) == 0) { \
+ for (size_t i = 0; i < num_items_; ++i) { \
+ LOG_INFO("[%d] actual = 0x%x; not expected = 0x%x", i, (actual_)[i], \
+ (not_expected_)[i]); \
+ } \
+ if (OT_VA_ARGS_COUNT(_, ##__VA_ARGS__) == 0) { \
+ LOG_ERROR("CHECK-BUFFER_NOT_EQ-fail: " #actual_ \
+ " matches " #not_expected_); \
+ } else { \
+ LOG_ERROR("CHECK-BUFFER_NOT_EQ-fail: " __VA_ARGS__); \
+ } \
+ /* Currently, this macro will call into \
+ the test failure code, which logs \
+ "FAIL" and aborts. In the future, \
+ we will try to condition on whether \
+ or not this is a test.*/ \
+ test_status_set(kTestStatusFailed); \
+ } \
+ } while (false)
+
+/**
* Checks that the given DIF call returns kDifOk. If the DIF call returns a
* different dif_result_t value (defined in sw/device/lib/dif/dif_base.h), this
* function logs and then aborts.
diff --git a/sw/device/lib/testing/flash_ctrl_testutils.c b/sw/device/lib/testing/flash_ctrl_testutils.c
index 66a8ab2..7121b17 100644
--- a/sw/device/lib/testing/flash_ctrl_testutils.c
+++ b/sw/device/lib/testing/flash_ctrl_testutils.c
@@ -31,17 +31,10 @@
return output.operation_error;
}
-uint32_t flash_ctrl_testutils_data_region_setup(
+uint32_t flash_ctrl_testutils_data_region_setup_properties(
dif_flash_ctrl_state_t *flash_state, uint32_t base_page_index,
- uint32_t data_region, uint32_t region_size) {
- dif_flash_ctrl_region_properties_t region_properties = {
- .ecc_en = true,
- .high_endurance_en = false,
- .erase_en = true,
- .prog_en = true,
- .rd_en = true,
- .scramble_en = false};
-
+ uint32_t data_region, uint32_t region_size,
+ dif_flash_ctrl_region_properties_t region_properties) {
dif_flash_ctrl_data_region_properties_t data_region_properties = {
.base = base_page_index,
.properties = region_properties,
@@ -56,9 +49,9 @@
return (base_page_index * device_info.bytes_per_page);
}
-uint32_t flash_ctrl_testutils_info_region_setup(
- dif_flash_ctrl_state_t *flash_state, uint32_t page_id, uint32_t bank,
- uint32_t partition_id) {
+uint32_t flash_ctrl_testutils_data_region_setup(
+ dif_flash_ctrl_state_t *flash_state, uint32_t base_page_index,
+ uint32_t data_region, uint32_t region_size) {
dif_flash_ctrl_region_properties_t region_properties = {
.ecc_en = true,
.high_endurance_en = false,
@@ -66,7 +59,30 @@
.prog_en = true,
.rd_en = true,
.scramble_en = false};
+ return flash_ctrl_testutils_data_region_setup_properties(
+ flash_state, base_page_index, data_region, region_size,
+ region_properties);
+}
+uint32_t flash_ctrl_testutils_data_region_scrambled_setup(
+ dif_flash_ctrl_state_t *flash_state, uint32_t base_page_index,
+ uint32_t data_region, uint32_t region_size) {
+ dif_flash_ctrl_region_properties_t region_properties = {
+ .ecc_en = true,
+ .high_endurance_en = false,
+ .erase_en = true,
+ .prog_en = true,
+ .rd_en = true,
+ .scramble_en = true};
+ return flash_ctrl_testutils_data_region_setup_properties(
+ flash_state, base_page_index, data_region, region_size,
+ region_properties);
+}
+
+uint32_t flash_ctrl_testutils_info_region_setup_properties(
+ dif_flash_ctrl_state_t *flash_state, uint32_t page_id, uint32_t bank,
+ uint32_t partition_id,
+ dif_flash_ctrl_region_properties_t region_properties) {
dif_flash_ctrl_info_region_t info_region = {
.bank = bank, .page = page_id, .partition_id = partition_id};
@@ -79,6 +95,34 @@
return (page_id * device_info.bytes_per_page);
}
+uint32_t flash_ctrl_testutils_info_region_setup(
+ dif_flash_ctrl_state_t *flash_state, uint32_t page_id, uint32_t bank,
+ uint32_t partition_id) {
+ dif_flash_ctrl_region_properties_t region_properties = {
+ .ecc_en = true,
+ .high_endurance_en = false,
+ .erase_en = true,
+ .prog_en = true,
+ .rd_en = true,
+ .scramble_en = false};
+ return flash_ctrl_testutils_info_region_setup_properties(
+ flash_state, page_id, bank, partition_id, region_properties);
+}
+
+uint32_t flash_ctrl_testutils_info_region_scrambled_setup(
+ dif_flash_ctrl_state_t *flash_state, uint32_t page_id, uint32_t bank,
+ uint32_t partition_id) {
+ dif_flash_ctrl_region_properties_t region_properties = {
+ .ecc_en = true,
+ .high_endurance_en = false,
+ .erase_en = true,
+ .prog_en = true,
+ .rd_en = true,
+ .scramble_en = true};
+ return flash_ctrl_testutils_info_region_setup_properties(
+ flash_state, page_id, bank, partition_id, region_properties);
+}
+
bool flash_ctrl_testutils_erase_page(
dif_flash_ctrl_state_t *flash_state, uint32_t byte_address,
uint32_t partition_id, dif_flash_ctrl_partition_type_t partition_type) {
diff --git a/sw/device/lib/testing/flash_ctrl_testutils.h b/sw/device/lib/testing/flash_ctrl_testutils.h
index 289ee9a..78b8707 100644
--- a/sw/device/lib/testing/flash_ctrl_testutils.h
+++ b/sw/device/lib/testing/flash_ctrl_testutils.h
@@ -25,8 +25,22 @@
dif_flash_ctrl_state_t *flash_state);
/**
- * Setup and enable for a data region.
- * Returns the address offset of the region.
+ * Setup and enable for a data region taking region properties as a parameter.
+ *
+ * @param flash_state A flash_ctrl state handle.
+ * @param base_page_index The region base page index.
+ * @param data_region The region index.
+ * @param region_size The region size (in number of pages).
+ * @param region_properties The properties for the data region.
+ * @return The byte address offset of the region.
+ */
+uint32_t flash_ctrl_testutils_data_region_setup_properties(
+ dif_flash_ctrl_state_t *flash_state, uint32_t base_page_index,
+ uint32_t data_region, uint32_t region_size,
+ dif_flash_ctrl_region_properties_t region_properties);
+
+/**
+ * Setup and enable for a data region with scrambling disabled.
*
* @param flash_state A flash_ctrl state handle.
* @param base_page_index The region base page index.
@@ -39,8 +53,35 @@
uint32_t data_region, uint32_t region_size);
/**
- * Setup and enable for an info region.
- * Returns the address offset of the region.
+ * Setup and enable for a data region with scrambling enabled.
+ *
+ * @param flash_state A flash_ctrl state handle.
+ * @param base_page_index The region base page index.
+ * @param data_region The region index.
+ * @param region_size The region size (in number of pages).
+ * @return The byte address offset of the region.
+ */
+uint32_t flash_ctrl_testutils_data_region_scrambled_setup(
+ dif_flash_ctrl_state_t *flash_state, uint32_t base_page_index,
+ uint32_t data_region, uint32_t region_size);
+
+/**
+ * Setup and enable for an info region taking region properties as a parameter.
+ *
+ * @param flash_state A flash_ctrl state handle.
+ * @param page_id Region page index.
+ * @param bank The required bank.
+ * @param paritiion_id The partition index.
+ * @param region_properties The properties for the info region.
+ * @return The byte address offset of the region.
+ */
+uint32_t flash_ctrl_testutils_info_region_setup_properties(
+ dif_flash_ctrl_state_t *flash_state, uint32_t page_id, uint32_t bank,
+ uint32_t partition_id,
+ dif_flash_ctrl_region_properties_t region_properties);
+
+/**
+ * Setup and enable for an info region with scrambling disabled.
*
* @param flash_state A flash_ctrl state handle.
* @param page_id Region page index.
@@ -53,6 +94,19 @@
uint32_t partition_id);
/**
+ * Setup and enable for an info region with scrambling enabled.
+ *
+ * @param flash_state A flash_ctrl state handle.
+ * @param page_id Region page index.
+ * @param bank The required bank.
+ * @param paritiion_id The partition index.
+ * @return The byte address offset of the region.
+ */
+uint32_t flash_ctrl_testutils_info_region_scrambled_setup(
+ dif_flash_ctrl_state_t *flash_state, uint32_t page_id, uint32_t bank,
+ uint32_t partition_id);
+
+/**
* Erases the page at byte_address.
* Returns the result of transaction_end.
*
diff --git a/sw/device/tests/sim_dv/flash_rma_unlocked_test.c b/sw/device/tests/sim_dv/flash_rma_unlocked_test.c
new file mode 100644
index 0000000..fe3ed03
--- /dev/null
+++ b/sw/device/tests/sim_dv/flash_rma_unlocked_test.c
@@ -0,0 +1,311 @@
+// 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/arch/device.h"
+#include "sw/device/lib/base/mmio.h"
+#include "sw/device/lib/dif/dif_flash_ctrl.h"
+#include "sw/device/lib/dif/dif_lc_ctrl.h"
+#include "sw/device/lib/dif/dif_otp_ctrl.h"
+#include "sw/device/lib/dif/dif_uart.h"
+#include "sw/device/lib/pinmux.h"
+#include "sw/device/lib/runtime/hart.h"
+#include "sw/device/lib/runtime/log.h"
+#include "sw/device/lib/runtime/print.h"
+#include "sw/device/lib/testing/check.h"
+#include "sw/device/lib/testing/flash_ctrl_testutils.h"
+#include "sw/device/lib/testing/test_framework/test_status.h"
+
+#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h" // Generated.
+
+#define LC_TOKEN_SIZE 16
+
+static dif_uart_t uart0;
+static dif_flash_ctrl_state_t flash_state;
+static dif_lc_ctrl_t lc;
+
+// TODO(lowRISC/opentitan:#11795): when the sw_symbol_backdoor_overwrite
+// is fixed for ROM, this can be overriden by the testbench as a SW variable.
+static mmio_region_t sram_region_ret_base_addr;
+
+enum {
+ kFlashInfoPageIdCreatorSecret = 1,
+ kFlashInfoPageIdOwnerSecret = 2,
+ kFlashInfoPageIdIsoPart = 3,
+ kFlashInfoBank = 0,
+ kRegionBaseBank0Page0Index = 0,
+ kRegionBaseBank0Page255Index = 255,
+ kRegionBaseBank1Page0Index = 256,
+ kRegionBaseBank1Page255Index = 511,
+ kFlashBank0Page0DataRegion = 0,
+ kFlashBank0Page255DataRegion = 1,
+ kFlashBank1Page0DataRegion = 2,
+ kFlashBank1Page255DataRegion = 3,
+ kPartitionId = 0,
+ kRegionSize = 1,
+ kDataSize = 16,
+ kPageSize = 2048,
+};
+
+enum {
+ kTestPhaseWriteData = 0,
+ kTestPhaseEnterRMA = 1,
+ kTestPhaseCheckWipe = 2,
+};
+
+static const uint32_t kRandomData[7][kDataSize] = {
+ {0x27af716e, 0xdd493905, 0x4d674f07, 0x1e876023, 0x555477e5, 0x8c079501,
+ 0xfa0aed05, 0x1091a5e4, 0xe94119be, 0xe0bed120, 0xb4611217, 0x1fa02d2a,
+ 0x5252583f, 0xc2a5083b, 0xc43409c3, 0xdb348c5c},
+ {0x55c88b8c, 0x5cb3c5eb, 0xc942d714, 0x7df99821, 0x151101b2, 0x739749a1,
+ 0x82bab3ef, 0xeb96c062, 0x99ac3750, 0xb31bb5e2, 0x83f979fb, 0x2e89575f,
+ 0x0ff9081f, 0x81d627e9, 0x8d0bdbfb, 0x69349b84},
+ {0xabd3622b, 0xd5d03fbb, 0x7c3bf1df, 0x697b82fb, 0xa96e5282, 0xf6dda27e,
+ 0x9bb834db, 0x207e2f9f, 0xe7249768, 0x24e91f04, 0x0c86716b, 0x38f956fe,
+ 0x246ac305, 0xee607b20, 0xb20bb6ef, 0xe95c3687},
+ {0xaf2afb37, 0x646de1b1, 0x092b67d9, 0x1aaaebc3, 0x0dab6bd2, 0x47c83a55,
+ 0xbdd1009f, 0xcd9f4c7a, 0xabe6e3a3, 0xb56e9d22, 0xacb955ce, 0x2d05fe1b,
+ 0x7e748f0c, 0xc7f4a59d, 0x7cac8713, 0x568ba3c9},
+ {0x95f9fe5a, 0xb5173233, 0x7055dc6e, 0x9ffdbec7, 0x98d5a883, 0xe81e5751,
+ 0x62bbd34e, 0x4bd22ca9, 0x58aac56e, 0xf9d87d26, 0xb2e7eba5, 0x6bcb1c4e,
+ 0xec7421c7, 0x8c49708a, 0xbf7932b2, 0x353b6a75},
+ {0xf8ff57b9, 0x444ff4a9, 0xf3574d3c, 0xf6682a84, 0x67455a38, 0x9c138df2,
+ 0x4054e3e5, 0xde4b1a26, 0x047cc121, 0x42cbd0ae, 0x3eec418f, 0x454323c4,
+ 0xf21bee28, 0x28dd24b7, 0x29dd06a6, 0xf83e419f},
+ {0x0eb23677, 0x58c97854, 0x284e1a8f, 0x9b460e99, 0x339b0fe6, 0x80778f39,
+ 0x9dbc2981, 0xb4bdc15f, 0x3abbdeb2, 0xab39dd53, 0x96bb2c4a, 0x9b2d1795,
+ 0x733bf534, 0xc4914b4b, 0x64487458, 0x9d0fa332}};
+
+// RMA unlock token value for LC state transition.
+// TODO(lowRISC/opentitan:#11795): when the sw_symbol_backdoor_overwrite
+// is fixed for ROM, this can be overriden by the testbench as a SW variable.
+// Currently hardcoded to match the token written in the testbench.
+static volatile const uint8_t kLcRmaUnlockToken[LC_TOKEN_SIZE] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+};
+
+static void write_info_page_scrambled(uint32_t page_index,
+ const uint32_t *data) {
+ uint32_t address = flash_ctrl_testutils_info_region_scrambled_setup(
+ &flash_state, page_index, kFlashInfoBank, kPartitionId);
+ CHECK(!flash_ctrl_testutils_erase_and_write_page(
+ &flash_state, address, kPartitionId, data, kDifFlashCtrlPartitionTypeInfo,
+ kDataSize));
+}
+
+static void write_data_page_scrambled(uint32_t page_index, uint32_t region,
+ const uint32_t *data) {
+ uint32_t address = flash_ctrl_testutils_data_region_scrambled_setup(
+ &flash_state, page_index, region, kRegionSize);
+ CHECK(!flash_ctrl_testutils_erase_and_write_page(
+ &flash_state, address, kPartitionId, data, kDifFlashCtrlPartitionTypeData,
+ kDataSize));
+}
+
+static void read_and_check_info_page_scrambled(bool is_equal,
+ uint32_t page_index,
+ const uint32_t *data) {
+ uint32_t readback_data[kDataSize];
+ uint32_t address = flash_ctrl_testutils_info_region_scrambled_setup(
+ &flash_state, page_index, kFlashInfoBank, kPartitionId);
+
+ CHECK(!flash_ctrl_testutils_read_page(
+ &flash_state, address, kPartitionId, readback_data,
+ kDifFlashCtrlPartitionTypeInfo, kDataSize, 0));
+ if (is_equal) {
+ CHECK_BUFFER(readback_data, data, kDataSize);
+ } else {
+ CHECK_BUFFER_NOT_EQ(readback_data, data, kDataSize);
+ }
+}
+
+static void read_and_check_data_page_scrambled(bool is_equal,
+ uint32_t page_index,
+ uint32_t region,
+ const uint32_t *data) {
+ uint32_t readback_data[kDataSize];
+ uint32_t address = flash_ctrl_testutils_data_region_scrambled_setup(
+ &flash_state, page_index, region, kRegionSize);
+
+ CHECK(!flash_ctrl_testutils_read_page(
+ &flash_state, address, kPartitionId, readback_data,
+ kDifFlashCtrlPartitionTypeData, kDataSize, 0));
+ if (is_equal) {
+ CHECK_BUFFER(readback_data, data, kDataSize);
+ } else {
+ CHECK_BUFFER_NOT_EQ(readback_data, data, kDataSize);
+ }
+}
+
+// Function for the first boot, the flash is written to with known data.
+// All partitions can be written to when the LC_STATE is Dev and
+// the OTP secret 2 partition has not been provisioned.
+static void write_data_test_phase(void) {
+ dif_lc_ctrl_state_t curr_state;
+ CHECK_DIF_OK(dif_lc_ctrl_get_state(&lc, &curr_state));
+ CHECK(curr_state == kDifLcCtrlStateDev);
+
+ write_info_page_scrambled(kFlashInfoPageIdCreatorSecret, kRandomData[0]);
+ write_info_page_scrambled(kFlashInfoPageIdOwnerSecret, kRandomData[1]);
+ write_info_page_scrambled(kFlashInfoPageIdIsoPart, kRandomData[2]);
+ write_data_page_scrambled(kRegionBaseBank0Page0Index,
+ kFlashBank0Page0DataRegion, kRandomData[3]);
+ write_data_page_scrambled(kRegionBaseBank0Page255Index,
+ kFlashBank0Page255DataRegion, kRandomData[4]);
+ write_data_page_scrambled(kRegionBaseBank1Page0Index,
+ kFlashBank1Page0DataRegion, kRandomData[5]);
+ write_data_page_scrambled(kRegionBaseBank1Page255Index,
+ kFlashBank1Page255DataRegion, kRandomData[6]);
+
+ // Advancing the test_phase so on the next boot the next branch can
+ // be taken. Going into WFI which will be detected by the testbench
+ // and an OTP write and reset can be triggered.
+ mmio_region_write32(sram_region_ret_base_addr, 0, kTestPhaseEnterRMA);
+ test_status_set(kTestStatusInWfi);
+ wait_for_interrupt();
+}
+
+// Function for the second boot, the LC_STATE is still Dev but the OTP secret 2
+// partition has now been provisioned by the testbench. Reading back
+// from the flash pages that are still accessible as a sanity check.
+// Creator Secret is not accessible because of OTP secret 2 provisioning.
+// Isolation Partition cannot be read in LC_STATE of Dev.
+// All others are accessible.
+// Once readback is complete setup and entering RMA LC_STATE.
+static void enter_rma_test_phase(void) {
+ dif_lc_ctrl_state_t curr_state;
+ CHECK_DIF_OK(dif_lc_ctrl_get_state(&lc, &curr_state));
+ CHECK(curr_state == kDifLcCtrlStateDev);
+
+ read_and_check_info_page_scrambled(true, kFlashInfoPageIdOwnerSecret,
+ kRandomData[1]);
+ read_and_check_data_page_scrambled(true, kRegionBaseBank0Page0Index,
+ kFlashBank0Page0DataRegion,
+ kRandomData[3]);
+ read_and_check_data_page_scrambled(true, kRegionBaseBank0Page255Index,
+ kFlashBank0Page255DataRegion,
+ kRandomData[4]);
+ read_and_check_data_page_scrambled(true, kRegionBaseBank1Page0Index,
+ kFlashBank1Page0DataRegion,
+ kRandomData[5]);
+ read_and_check_data_page_scrambled(true, kRegionBaseBank1Page255Index,
+ kFlashBank1Page255DataRegion,
+ kRandomData[6]);
+
+ // Advance the test_phase.
+ mmio_region_write32(sram_region_ret_base_addr, 0, kTestPhaseCheckWipe);
+
+ // Setting up lc_ctrl for an RMA transition.
+ dif_lc_ctrl_token_t token;
+ for (int i = 0; i < LC_TOKEN_SIZE; ++i) {
+ token.data[i] = kLcRmaUnlockToken[i];
+ }
+ CHECK_DIF_OK(dif_lc_ctrl_mutex_try_acquire(&lc));
+
+ dif_lc_ctrl_settings_t settings = {.clock_select = kDifLcCtrlInternalClockEn};
+ CHECK_DIF_OK(
+ dif_lc_ctrl_transition(&lc, kDifLcCtrlStateRma, &token, &settings));
+
+ // Enter WFI for detection in the testbench.
+ test_status_set(kTestStatusInWfi);
+ wait_for_interrupt();
+}
+
+// Function for the third boot, all flash partitions are readable as the
+// LC_STATE is now RMA. Check that the data read from these partitions does
+// *not* match the original data and therefore has been successfully wiped.
+static void check_wipe_test_phase(void) {
+ dif_lc_ctrl_state_t curr_state;
+ CHECK_DIF_OK(dif_lc_ctrl_get_state(&lc, &curr_state));
+ CHECK(curr_state == kDifLcCtrlStateRma);
+
+ read_and_check_info_page_scrambled(false, kFlashInfoPageIdCreatorSecret,
+ kRandomData[0]);
+ read_and_check_info_page_scrambled(false, kFlashInfoPageIdOwnerSecret,
+ kRandomData[1]);
+ read_and_check_info_page_scrambled(false, kFlashInfoPageIdIsoPart,
+ kRandomData[2]);
+ read_and_check_data_page_scrambled(false, kRegionBaseBank0Page0Index,
+ kFlashBank0Page0DataRegion,
+ kRandomData[3]);
+ read_and_check_data_page_scrambled(false, kRegionBaseBank0Page255Index,
+ kFlashBank0Page255DataRegion,
+ kRandomData[4]);
+ read_and_check_data_page_scrambled(false, kRegionBaseBank1Page0Index,
+ kFlashBank1Page0DataRegion,
+ kRandomData[5]);
+ read_and_check_data_page_scrambled(false, kRegionBaseBank1Page255Index,
+ kFlashBank1Page255DataRegion,
+ kRandomData[6]);
+}
+
+void _boot_start(void) {
+ // We need to set the test status as "in test" to indicate to the test code
+ // has been reached, even though this test is also in the "boot ROM".
+ test_status_set(kTestStatusInTest);
+ pinmux_init();
+
+ // We need to initialize the UART regardless if we LOG any messages, since
+ // Verilator and FPGA platforms use the UART to communicate the test results.
+ if (kDeviceType != kDeviceSimDV) {
+ CHECK_DIF_OK(dif_uart_init(
+ mmio_region_from_addr(TOP_EARLGREY_UART0_BASE_ADDR), &uart0));
+ CHECK_DIF_OK(
+ dif_uart_configure(&uart0, (dif_uart_config_t){
+ .baudrate = kUartBaudrate,
+ .clk_freq_hz = kClockFreqPeripheralHz,
+ .parity_enable = kDifToggleDisabled,
+ .parity = kDifUartParityEven,
+ }));
+ base_uart_stdout(&uart0);
+ }
+
+ // Start of the test specific code.
+ CHECK_DIF_OK(dif_flash_ctrl_init_state(
+ &flash_state,
+ mmio_region_from_addr(TOP_EARLGREY_FLASH_CTRL_CORE_BASE_ADDR)));
+ CHECK_DIF_OK(dif_lc_ctrl_init(
+ mmio_region_from_addr(TOP_EARLGREY_LC_CTRL_BASE_ADDR), &lc));
+
+ dif_otp_ctrl_t otp;
+ CHECK_DIF_OK(dif_otp_ctrl_init(
+ mmio_region_from_addr(TOP_EARLGREY_OTP_CTRL_CORE_BASE_ADDR), &otp));
+
+ dif_otp_ctrl_config_t otp_config = {
+ .check_timeout = 100000,
+ .integrity_period_mask = 0x0,
+ .consistency_period_mask = 0x0,
+ };
+ CHECK_DIF_OK(dif_otp_ctrl_configure(&otp, otp_config));
+
+ // TODO(lowRISC/opentitan:#11795): when the sw_symbol_backdoor_overwrite
+ // is fixed for ROM, this can be overriden by the testbench as a SW variable.
+ // Currently using retention SRAM to record the test_phase.
+ sram_region_ret_base_addr =
+ mmio_region_from_addr(TOP_EARLGREY_SRAM_CTRL_RET_AON_RAM_BASE_ADDR);
+
+ uint32_t test_phase = mmio_region_read32(sram_region_ret_base_addr, 0);
+
+ switch (test_phase) {
+ case kTestPhaseWriteData:
+ write_data_test_phase();
+ break;
+ case kTestPhaseEnterRMA:
+ enter_rma_test_phase();
+ break;
+ case kTestPhaseCheckWipe:
+ check_wipe_test_phase();
+ break;
+ default:
+ LOG_FATAL("Unexpected Test Phase");
+ break;
+ }
+
+ // If this point is reached everything was successful.
+ // Report test status as passed.
+ test_status_set(kTestStatusPassed);
+
+ // Unreachable.
+ abort();
+}
diff --git a/sw/device/tests/sim_dv/meson.build b/sw/device/tests/sim_dv/meson.build
index 516eaf5..08dd5e2 100644
--- a/sw/device/tests/sim_dv/meson.build
+++ b/sw/device/tests/sim_dv/meson.build
@@ -192,6 +192,34 @@
}
}
+flash_rma_unlocked_test_lib = declare_dependency(
+ link_with: static_library(
+ 'flash_rma_unlocked_test_lib',
+ sources: [
+ 'flash_rma_unlocked_test.c',
+ ],
+ dependencies: [
+ sw_lib_dif_flash_ctrl,
+ sw_lib_dif_lc_ctrl,
+ sw_lib_dif_otp_ctrl,
+ sw_lib_dif_uart,
+ sw_lib_mmio,
+ sw_lib_pinmux,
+ sw_lib_runtime_hart,
+ sw_lib_runtime_print,
+ sw_lib_runtime_log,
+ sw_lib_testing_flash_ctrl_testutils,
+ sw_lib_testing_test_status,
+ ],
+ ),
+)
+sw_tests += {
+ 'flash_rma_unlocked_test': {
+ 'library': flash_rma_unlocked_test_lib,
+ 'run_at_rom_stage': true,
+ }
+}
+
rom_ctrl_integrity_check_test_lib = declare_dependency(
link_with: static_library(
'rom_ctrl_integrity_check_test_lib',