diff --git a/hw/ip/keymgr_dpe/data/keymgr_dpe.hjson b/hw/ip/keymgr_dpe/data/keymgr_dpe.hjson index 1530281902226..5d5a39d3b3f58 100644 --- a/hw/ip/keymgr_dpe/data/keymgr_dpe.hjson +++ b/hw/ip/keymgr_dpe/data/keymgr_dpe.hjson @@ -77,17 +77,29 @@ act: "req", package: "kmac_pkg", // Origin package (only needs for the requester) }, - { struct: "otp_keymgr_key", + { struct: "keymgr_dpe_creator_root_key", type: "uni", - name: "otp_key", + name: "creator_root_key", act: "rcv", - package: "otp_ctrl_pkg", + package: "keymgr_dpe_pkg", }, - { struct: "otp_device_id", + { struct: "keymgr_dpe_creator_seed", type: "uni", - name: "otp_device_id", + name: "creator_seed", act: "rcv", - package: "otp_ctrl_pkg", + package: "keymgr_dpe_pkg", + }, + { struct: "keymgr_dpe_owner_seed", + type: "uni", + name: "owner_seed", + act: "rcv", + package: "keymgr_dpe_pkg", + }, + { struct: "keymgr_dpe_device_id", + type: "uni", + name: "device_id", + act: "rcv", + package: "keymgr_dpe_pkg", }, { struct: "lc_tx", type: "uni", @@ -216,11 +228,33 @@ default: "1", local: "true" }, + { name: "NumMaxHwSlot", + desc: ''' + Number of maximum supported HW slots. If this value is changed then the + bit width in SLOT_SRC_SEL / SLOT_DST_SEL in the + "CONTROL_SHADOWED" register needs to be updated too. + ''', + type: "int", + default: "8", + local: "true" + }, + { name: "NumInstHwSlot", + desc: "Number of instantiated HW slots", + type: "int", + default: "4", + expose: "true" + }, + { name: "NumBootStages", + desc: "Number of available boot stage", + type: "int", + default: "3", + expose: "true" + }, { name: "NumRomDigestInputs", desc: "Number of digest inputs from ROM_CTRL", type: "int", - default: "2", - local: "true" + default: "1", + expose: "true" }, ], @@ -539,6 +573,9 @@ countermeasures: [ ''' } ], + // TODO(#30682): Remove this label as soon as the RTL raises an + // error instead of a assertion. + tags: ["excl:CsrAllTests:CsrExclWrite"] }, { name: "SIDELOAD_CLEAR", diff --git a/hw/ip/keymgr_dpe/doc/interfaces.md b/hw/ip/keymgr_dpe/doc/interfaces.md index 6c7abe5d444ac..17889e985cd93 100644 --- a/hw/ip/keymgr_dpe/doc/interfaces.md +++ b/hw/ip/keymgr_dpe/doc/interfaces.md @@ -10,20 +10,22 @@ Referring to the [Comportable guideline for peripheral device functionality](htt ## [Inter-Module Signals](https://opentitan.org/book/doc/contributing/hw/comportability/index.html#inter-signal-handling) -| Port Name | Package::Struct | Type | Act | Width | Description | -|:----------------|:-----------------------------|:--------|:------|--------:|:--------------| -| edn | edn_pkg::edn | req_rsp | req | 1 | | -| aes_key | keymgr_pkg::hw_key_req | uni | req | 1 | | -| kmac_key | keymgr_pkg::hw_key_req | uni | req | 1 | | -| otbn_key | keymgr_pkg::otbn_key_req | uni | req | 1 | | -| kmac_data | kmac_pkg::app | req_rsp | req | 1 | | -| otp_key | otp_ctrl_pkg::otp_keymgr_key | uni | rcv | 1 | | -| otp_device_id | otp_ctrl_pkg::otp_device_id | uni | rcv | 1 | | -| lc_keymgr_en | lc_ctrl_pkg::lc_tx | uni | rcv | 1 | | -| lc_keymgr_div | lc_ctrl_pkg::lc_keymgr_div | uni | rcv | 1 | | -| rom_digest | rom_ctrl_pkg::keymgr_data | uni | rcv | 2 | | -| kmac_en_masking | logic | uni | rcv | 1 | | -| tl | tlul_pkg::tl | req_rsp | rsp | 1 | | +| Port Name | Package::Struct | Type | Act | Width | Description | +|:-----------------|:--------------------------------------------|:--------|:------|--------:|:--------------| +| edn | edn_pkg::edn | req_rsp | req | 1 | | +| aes_key | keymgr_pkg::hw_key_req | uni | req | 1 | | +| kmac_key | keymgr_pkg::hw_key_req | uni | req | 1 | | +| otbn_key | keymgr_pkg::otbn_key_req | uni | req | 1 | | +| kmac_data | kmac_pkg::app | req_rsp | req | 1 | | +| creator_root_key | keymgr_dpe_pkg::keymgr_dpe_creator_root_key | uni | rcv | 1 | | +| creator_seed | keymgr_dpe_pkg::keymgr_dpe_creator_seed | uni | rcv | 1 | | +| owner_seed | keymgr_dpe_pkg::keymgr_dpe_owner_seed | uni | rcv | 1 | | +| device_id | keymgr_dpe_pkg::keymgr_dpe_device_id | uni | rcv | 1 | | +| lc_keymgr_en | lc_ctrl_pkg::lc_tx | uni | rcv | 1 | | +| lc_keymgr_div | lc_ctrl_pkg::lc_keymgr_div | uni | rcv | 1 | | +| rom_digest | rom_ctrl_pkg::keymgr_data | uni | rcv | 2 | | +| kmac_en_masking | logic | uni | rcv | 1 | | +| tl | tlul_pkg::tl | req_rsp | rsp | 1 | | ## Interrupts diff --git a/hw/ip/keymgr_dpe/dv/README.md b/hw/ip/keymgr_dpe/dv/README.md index 87c736908ee84..da9c7f643e8e3 100644 --- a/hw/ip/keymgr_dpe/dv/README.md +++ b/hw/ip/keymgr_dpe/dv/README.md @@ -102,6 +102,7 @@ Here's how to run a smoke test: ```console $ dvsim $REPO_TOP/hw/ip/keymgr_dpe/dv/keymgr_dpe_sim_cfg.hjson -i keymgr_dpe_smoke ``` +With the configurations `keymgr_dpe_earlgrey_sim_cfg.hjson` / `keymgr_dpe_darjeeling_sim_cfg.hjson` it is possible to test the top dependent default configurations. ## Testplan [Testplan](../data/keymgr_dpe_testplan.hjson) diff --git a/hw/ip/keymgr_dpe/dv/env/keymgr_dpe_ctrl_if.sv b/hw/ip/keymgr_dpe/dv/env/keymgr_dpe_ctrl_if.sv new file mode 100644 index 0000000000000..5216a5b1f313c --- /dev/null +++ b/hw/ip/keymgr_dpe/dv/env/keymgr_dpe_ctrl_if.sv @@ -0,0 +1,28 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// An interface that should be bound into an instance of keymgr_dpe_ctrl (and can access RTL signals +// through upwards hierarchical references). +interface keymgr_dpe_ctrl_if (input clk_i, input rst_ni); + import uvm_pkg::*; + + // Function to access the key of one slot via backdoor. + // Required to load the UDS after XORing with generated randomness. + function automatic keymgr_dpe_env_pkg::key_shares_t get_key_of_slot(int unsigned slot); + import keymgr_dpe_env_pkg::DvNumInstHwSlot; + + // Check that the slot index is in range. The key_slots_q array has length DvNumInstHwSlot, + // which is a global parameter. + if (slot >= DvNumInstHwSlot) begin + `uvm_error($sformatf("%m"), + $sformatf("Slot index out of range: index is %0d but DvNumInstHwSlot is %0d", + slot, DvNumInstHwSlot)) + return '0; + end + + // This is an upwards hierarchical reference through the keymgr_dpe_ctrl module (into an + // instance of which this interface is bound) + return keymgr_dpe_ctrl.key_slots_q[slot].key; + endfunction +endinterface diff --git a/hw/ip/keymgr_dpe/dv/env/keymgr_dpe_env.core b/hw/ip/keymgr_dpe/dv/env/keymgr_dpe_env.core index 2dd9f3df0ed38..dd798cee1a87b 100644 --- a/hw/ip/keymgr_dpe/dv/env/keymgr_dpe_env.core +++ b/hw/ip/keymgr_dpe/dv/env/keymgr_dpe_env.core @@ -14,6 +14,7 @@ filesets: - lowrisc:ip:kmac_pkg files: - keymgr_dpe_env_pkg.sv + - keymgr_dpe_ctrl_if.sv - keymgr_dpe_if.sv - keymgr_dpe_env_cfg.sv: {is_include_file: true} - keymgr_dpe_env_cov.sv: {is_include_file: true} diff --git a/hw/ip/keymgr_dpe/dv/env/keymgr_dpe_env.sv b/hw/ip/keymgr_dpe/dv/env/keymgr_dpe_env.sv index 811a07dd42032..15944a2e2b0e5 100644 --- a/hw/ip/keymgr_dpe/dv/env/keymgr_dpe_env.sv +++ b/hw/ip/keymgr_dpe/dv/env/keymgr_dpe_env.sv @@ -26,6 +26,10 @@ class keymgr_dpe_env extends cip_base_env #( `uvm_fatal(`gfn, "failed to get keymgr_dpe_vif from uvm_config_db") end cfg.scb = scoreboard; + if (!uvm_config_db#(virtual keymgr_dpe_ctrl_if)::get( + this, "", "keymgr_dpe_ctrl_vif", cfg.keymgr_dpe_ctrl_vif)) begin + `uvm_fatal(get_full_name(), "Could not get keymgr_dpe_ctrl_vif from uvm_config_db.") + end endfunction function void connect_phase(uvm_phase phase); diff --git a/hw/ip/keymgr_dpe/dv/env/keymgr_dpe_env_cfg.sv b/hw/ip/keymgr_dpe/dv/env/keymgr_dpe_env_cfg.sv index 120a17f9a49af..232f6e46cf8fd 100644 --- a/hw/ip/keymgr_dpe/dv/env/keymgr_dpe_env_cfg.sv +++ b/hw/ip/keymgr_dpe/dv/env/keymgr_dpe_env_cfg.sv @@ -15,6 +15,9 @@ class keymgr_dpe_env_cfg extends cip_base_env_cfg #(.RAL_T(keymgr_dpe_reg_block) `uvm_object_new + // Interface that is bound into the keymgr_dpe_ctrl instance + virtual keymgr_dpe_ctrl_if keymgr_dpe_ctrl_vif; + virtual function void initialize(); list_of_alerts = keymgr_dpe_env_pkg::LIST_OF_ALERTS; tl_intg_alert_name = "fatal_fault_err"; diff --git a/hw/ip/keymgr_dpe/dv/env/keymgr_dpe_env_pkg.sv b/hw/ip/keymgr_dpe/dv/env/keymgr_dpe_env_pkg.sv index 7603eeec987c3..aa6b702d31a17 100644 --- a/hw/ip/keymgr_dpe/dv/env/keymgr_dpe_env_pkg.sv +++ b/hw/ip/keymgr_dpe/dv/env/keymgr_dpe_env_pkg.sv @@ -20,6 +20,39 @@ package keymgr_dpe_env_pkg; `include "uvm_macros.svh" `include "dv_macros.svh" + // Define default values for all blocklevel dv parameter + `ifndef DEF_DV_BOOT_STAGES + `define DEF_DV_BOOT_STAGES 3 + `endif + `ifndef DEF_DV_DPE_NUM_SLOT + `define DEF_DV_DPE_NUM_SLOT 4 + `endif + `ifndef DEF_DV_NUM_ROM_DIGEST + `define DEF_DV_NUM_ROM_DIGEST 1 + `endif + + // Avoid using defines throughout the DV code by introducing the following + // parameters. + parameter int DvBootStages = `DEF_DV_BOOT_STAGES; + parameter int DvNumInstHwSlot = `DEF_DV_DPE_NUM_SLOT; + parameter int DvNumRomDigestInputs = `DEF_DV_NUM_ROM_DIGEST; + + // Advance width calculation + // When deriving from the UDS the following data are consumed (Not ordered): + // - Software binding + // - Revision seed + // - OTP device ID + // - LC keymgr diversification value + // - ROM digests + // - Creator seed (only if boot stage equals two) + parameter int DvDpeAdvDataWidth = keymgr_pkg::SwBindingWidth + + keymgr_pkg::KeyWidth + keymgr_pkg::KeyWidth * (DvBootStages == 2) + + lc_ctrl_pkg::LcKeymgrDivWidth + keymgr_dpe_pkg::DeviceIdWidth + + keymgr_pkg::KeyWidth * DvNumRomDigestInputs; + + localparam int DvNumInstHwSlotWidth = prim_util_pkg::vbits(DvNumInstHwSlot); + typedef logic [DvNumInstHwSlotWidth-1:0] dv_keymgr_dpe_slot_idx_e; + // parameters and types parameter uint NUM_ALERTS = 2; parameter string LIST_OF_ALERTS[NUM_ALERTS] = {"recov_operation_err", "fatal_fault_err"}; @@ -64,8 +97,8 @@ package keymgr_dpe_env_pkg; } keymgr_dpe_fault_inject_type_e; typedef struct{ - keymgr_dpe_pkg::keymgr_dpe_slot_idx_e src_slot; - keymgr_dpe_pkg::keymgr_dpe_slot_idx_e dst_slot; + dv_keymgr_dpe_slot_idx_e src_slot; + dv_keymgr_dpe_slot_idx_e dst_slot; } keymgr_dpe_key_slot_t; string msg_id = "keymgr_dpe_env_pkg"; diff --git a/hw/ip/keymgr_dpe/dv/env/keymgr_dpe_if.sv b/hw/ip/keymgr_dpe/dv/env/keymgr_dpe_if.sv index d1a8fc007eb63..00093bf506154 100644 --- a/hw/ip/keymgr_dpe/dv/env/keymgr_dpe_if.sv +++ b/hw/ip/keymgr_dpe/dv/env/keymgr_dpe_if.sv @@ -7,7 +7,6 @@ interface keymgr_dpe_if(input clk, input rst_n); import uvm_pkg::*; import keymgr_dpe_env_pkg::*; - import keymgr_dpe_reg_pkg::NumRomDigestInputs; // Represents the keymgr_dpe sideload state for each sideload interface. // @@ -16,11 +15,13 @@ interface keymgr_dpe_if(input clk, input rst_n); // Status can't be directly changed from SideLoadClear to SideLoadAvail. // When status is SideLoadClear due to SIDELOAD_CLEAR programmed, need to write CSR to 0 to reset // it so that status is changed to SideLoadNotAvail, then we may set it to SideLoadAvail again - lc_ctrl_pkg::lc_tx_t keymgr_dpe_en; - lc_ctrl_pkg::lc_keymgr_div_t keymgr_dpe_div; - otp_ctrl_pkg::otp_device_id_t otp_device_id; - otp_ctrl_pkg::otp_keymgr_key_t otp_key; - rom_ctrl_pkg::keymgr_data_t[NumRomDigestInputs-1:0] rom_digests; + lc_ctrl_pkg::lc_tx_t keymgr_dpe_en; + lc_ctrl_pkg::lc_keymgr_div_t keymgr_dpe_div; + keymgr_dpe_pkg::keymgr_dpe_device_id_t otp_device_id; + keymgr_dpe_pkg::keymgr_dpe_creator_root_key_t creator_root_key; + keymgr_dpe_pkg::keymgr_dpe_creator_seed_t creator_seed; + keymgr_dpe_pkg::keymgr_dpe_owner_seed_t owner_seed; + rom_ctrl_pkg::keymgr_data_t[DvNumRomDigestInputs-1:0] rom_digests; keymgr_pkg::hw_key_req_t kmac_key; keymgr_pkg::hw_key_req_t aes_key; @@ -101,38 +102,40 @@ interface keymgr_dpe_if(input clk, input rst_n); // assigned from the keymgr_dpe.keymgr_dpe_ctrl.key_slots_q signal, which should hold the // current value of the keyslots in the dut. - keymgr_dpe_pkg::keymgr_dpe_slot_t [keymgr_dpe_pkg::DpeNumSlots-1:0] internal_key_slots; + keymgr_dpe_pkg::keymgr_dpe_slot_t [DvNumInstHwSlot-1:0] internal_key_slots; task automatic init(bit rand_otp_key, bit invalid_otp_key); // Keymgr_dpe only latches OTP key once, so this scb does not support change OTP key on the // fly. Will write a direct sequence to cover otp key change on the fly. - otp_ctrl_pkg::otp_keymgr_key_t local_otp_key; + keymgr_dpe_pkg::keymgr_dpe_creator_root_key_t local_creator_root_key; // async delay as these signals are from different clock domain #($urandom_range(1000, 0) * 1ns); keymgr_dpe_en = lc_ctrl_pkg::On; keymgr_dpe_div = 64'h5CFBD765CE33F34E; otp_device_id = 'hF0F0; - otp_key = otp_ctrl_pkg::OTP_KEYMGR_KEY_DEFAULT; - for (int i = 0; i < NumRomDigestInputs; ++i) begin + for (int i = 0; i < DvNumRomDigestInputs; ++i) begin rom_digests[i].data = 256'hA20A046CF42E6EAC560A3F82BFA76285B5C1D4AEA7C915E49A32D1C89BE0F507; rom_digests[i].valid = '1; end + // Load the default value for all seed's + local_creator_root_key = keymgr_dpe_pkg::KEYMGR_DPE_CREATOR_ROOT_KEY_DEFAULT; + creator_seed = keymgr_dpe_pkg::KEYMGR_DPE_CREATOR_SEED_DEFAULT; + owner_seed = keymgr_dpe_pkg::KEYMGR_DPE_OWNER_SEED_DEFAULT; + // If requested randomize the creator_root_key if (rand_otp_key) begin - `DV_CHECK_STD_RANDOMIZE_WITH_FATAL(local_otp_key, - local_otp_key.creator_root_key_share0_valid == 1; - local_otp_key.creator_root_key_share1_valid == 1; - !(local_otp_key.creator_root_key_share0 inside {0, '1}); - !(local_otp_key.creator_root_key_share1 inside {0, '1}); + `DV_CHECK_STD_RANDOMIZE_WITH_FATAL(local_creator_root_key, + local_creator_root_key.share0_valid == 1; + local_creator_root_key.share1_valid == 1; + !(local_creator_root_key.share0 inside {0, '1}); + !(local_creator_root_key.share1 inside {0, '1}); , , msg_id) - end else begin - local_otp_key = otp_ctrl_pkg::OTP_KEYMGR_KEY_DEFAULT; end if (invalid_otp_key) begin - local_otp_key.creator_root_key_share0_valid = 0; - local_otp_key.creator_root_key_share1_valid = 0; + local_creator_root_key.share0_valid = 0; + local_creator_root_key.share1_valid = 0; end - otp_key = local_otp_key; + creator_root_key = local_creator_root_key; endtask // reset local exp variables when reset is issued @@ -189,14 +192,14 @@ interface keymgr_dpe_if(input clk, input rst_n); // as a set of flags / counters. bit bad_keymgr_dpe_div = 1'b0; bit bad_otp_device_id = 1'b0; - bit [NumRomDigestInputs-1:0] bad_rom_data = '0, bad_rom_valid = '0; + bit [DvNumRomDigestInputs-1:0] bad_rom_data = '0, bad_rom_valid = '0; repeat (num_invalid_input) begin randcase 1: bad_keymgr_dpe_div = 1'b1; 1: bad_otp_device_id = 1'b1; - 1: bad_rom_data[$urandom % NumRomDigestInputs] = 1'b1; - 1: bad_rom_valid[$urandom % NumRomDigestInputs] = 1'b1; + 1: bad_rom_data[$urandom % DvNumRomDigestInputs] = 1'b1; + 1: bad_rom_valid[$urandom % DvNumRomDigestInputs] = 1'b1; endcase end @@ -217,7 +220,7 @@ interface keymgr_dpe_if(input clk, input rst_n); // rom_digests begin - for (int i = 0; i < NumRomDigestInputs; i++) + for (int i = 0; i < DvNumRomDigestInputs; i++) fork automatic int local_i = i; #($urandom_range(1000, 0) * 1ns); @@ -235,8 +238,8 @@ interface keymgr_dpe_if(input clk, input rst_n); function automatic void compare_internal_key_slot( keymgr_dpe_pkg::keymgr_dpe_slot_t dst_key_slot, keymgr_dpe_pkg::keymgr_dpe_slot_t src_key_slot, - keymgr_dpe_pkg::keymgr_dpe_slot_idx_e dst_slot_index, - keymgr_dpe_pkg::keymgr_dpe_slot_idx_e src_slot_index, + dv_keymgr_dpe_slot_idx_e dst_slot_index, + dv_keymgr_dpe_slot_idx_e src_slot_index, bit check_parent_retained ); `DV_CHECK_EQ(dst_key_slot, internal_key_slots[dst_slot_index], diff --git a/hw/ip/keymgr_dpe/dv/env/keymgr_dpe_scoreboard.sv b/hw/ip/keymgr_dpe/dv/env/keymgr_dpe_scoreboard.sv index 0298495c0ce3c..bf73cc4f08004 100644 --- a/hw/ip/keymgr_dpe/dv/env/keymgr_dpe_scoreboard.sv +++ b/hw/ip/keymgr_dpe/dv/env/keymgr_dpe_scoreboard.sv @@ -11,7 +11,7 @@ class keymgr_dpe_scoreboard extends cip_base_scoreboard #( `define CREATE_CMP_STR(VAR) \ str = $sformatf("%0s\n %0s act: 0x%0h, exp: 0x%0h", str, `"VAR`", act.``VAR, exp.``VAR); - // if boot_stage == 0 + // if boot_stage == 0 with creator seed typedef struct packed { // SW_CDI_INPUT bit [keymgr_dpe_reg_pkg::NumSwBindingReg-1:0][TL_DW-1:0] SoftwareBinding; @@ -22,28 +22,53 @@ class keymgr_dpe_scoreboard extends cip_base_scoreboard #( // HEALTH_ST_MEASUREMENT bit [keymgr_pkg::HealthStateWidth-1:0] HealthMeasurement; // ROM_DESCRIPTORS - bit [keymgr_dpe_reg_pkg::NumRomDigestInputs-1:0][keymgr_pkg::KeyWidth-1:0] RomDigests; + bit [keymgr_dpe_env_pkg::DvNumRomDigestInputs-1:0][keymgr_pkg::KeyWidth-1:0] RomDigests; // CREATOR_SEED - bit [keymgr_pkg::KeyWidth-1:0] DiversificationKey; - } adv_creator_data_t; + bit [keymgr_pkg::KeyWidth-1:0] CreatorRootSecret; + } adv_creator_data_with_creator_seed_t; + + // if boot_stage == 0 without creator seed + typedef struct packed { + // SW_CDI_INPUT + bit [keymgr_dpe_reg_pkg::NumSwBindingReg-1:0][TL_DW-1:0] SoftwareBinding; + // DEVICE_IDENTIFIER + bit [keymgr_pkg::DevIdWidth-1:0] DeviceIdentifier; + // HEALTH_ST_MEASUREMENT + bit [keymgr_pkg::HealthStateWidth-1:0] HealthMeasurement; + // ROM_DESCRIPTORS + bit [keymgr_dpe_env_pkg::DvNumRomDigestInputs-1:0][keymgr_pkg::KeyWidth-1:0] RomDigests; + // HW_REVISION_SEED + bit [keymgr_pkg::KeyWidth-1:0] HardwareRevisionSecret; + } adv_creator_data_without_creator_seed_t; typedef struct packed { // some portions are unused, which are 0s - bit [keymgr_dpe_pkg::DpeAdvDataWidth-keymgr_pkg::KeyWidth-keymgr_pkg::SwBindingWidth-1:0] + bit [keymgr_dpe_env_pkg::DvDpeAdvDataWidth-keymgr_pkg::KeyWidth-keymgr_pkg::SwBindingWidth-1:0] unused; // SW_CDI_INPUT bit [keymgr_dpe_reg_pkg::NumSwBindingReg-1:0][TL_DW-1:0] SoftwareBinding; - // OWNER SEED - bit [keymgr_pkg::KeyWidth-1:0] OwnerRootSecret; + // CREATOR_SEED + bit [keymgr_pkg::KeyWidth-1:0] CreatorRootSecret; } adv_owner_int_data_t; typedef struct packed { // some portions are unused, which are 0s - bit [keymgr_dpe_pkg::DpeAdvDataWidth-keymgr_pkg::SwBindingWidth-1:0] unused; + bit [keymgr_dpe_env_pkg::DvDpeAdvDataWidth-keymgr_pkg::KeyWidth-keymgr_pkg::SwBindingWidth-1:0] + unused; // SW_CDI_INPUT bit [keymgr_dpe_reg_pkg::NumSwBindingReg-1:0][TL_DW-1:0] SoftwareBinding; + // OWNER SEED + bit [keymgr_pkg::KeyWidth-1:0] OwnerRootSecret; } adv_owner_data_t; + typedef struct packed { + // some portions are unused, which are 0s + bit [keymgr_dpe_env_pkg::DvDpeAdvDataWidth-keymgr_pkg::SwBindingWidth-1:0] + unused; + // SW_CDI_INPUT + bit [keymgr_dpe_reg_pkg::NumSwBindingReg-1:0][TL_DW-1:0] SoftwareBinding; + } adv_runtime_data_t; + typedef struct packed { bit [TL_DW-1:0] KeyVersion; bit [keymgr_dpe_reg_pkg::NumSaltReg-1:0][TL_DW-1:0] Salt; @@ -68,10 +93,16 @@ class keymgr_dpe_scoreboard extends cip_base_scoreboard #( bit is_kmac_rsp_err; bit is_kmac_invalid_data; bit is_sw_share_corrupted; + // Indicates if the UDS was fetched by the keymgr_dpe for the first time. + // The UDS needs to be xored with randomness to counter SCA, however the + // current dv environment cannot replicate this randomness. As a workaround + // the generated value (UDS xored with randomness) is loaded by a backdoor + // directly from the DUT. + bit load_uds_with_randomness; // HW internal key, used for OP in current state keymgr_dpe_env_pkg::keymgr_dpe_key_slot_t current_key_slot; - keymgr_dpe_pkg::keymgr_dpe_slot_t current_internal_key[keymgr_dpe_pkg::DpeNumSlots]; + keymgr_dpe_pkg::keymgr_dpe_slot_t current_internal_key[keymgr_dpe_env_pkg::DvNumInstHwSlot]; bit [keymgr_pkg::KeyWidth-1:0] old_key; // bit used to flag a comparison of key slot is required // it's set by the process_kmac_data_rsp() function, during an @@ -93,8 +124,8 @@ class keymgr_dpe_scoreboard extends cip_base_scoreboard #( // local queues to hold incoming packets pending comparison // store meaningful data, in non-working state, should not match to these data - bit [keymgr_dpe_pkg::DpeAdvDataWidth-1:0] adv_data_a_array[ - keymgr_dpe_pkg::DpeNumSlots][ + bit [keymgr_dpe_env_pkg::DvDpeAdvDataWidth-1:0] adv_data_a_array[ + keymgr_dpe_env_pkg::DvNumInstHwSlot][ keymgr_dpe_pkg::keymgr_dpe_exposed_working_state_e]; bit [keymgr_pkg::IdDataWidth-1:0] id_data_a_array[ keymgr_dpe_pkg::keymgr_dpe_exposed_working_state_e]; @@ -150,11 +181,14 @@ class keymgr_dpe_scoreboard extends cip_base_scoreboard #( virtual function void process_kmac_data_req(kmac_app_item item); keymgr_dpe_pkg::keymgr_dpe_ops_e op = get_operation(); bit is_err; + bit invalid_hw_input; + bit invalid_op; logic [keymgr_dpe_pkg::DpeBootStagesWidth-1:0] boot_stage = current_internal_key[current_key_slot.src_slot].boot_stage; - `uvm_info(`gfn, $sformatf("process_kmac_data_req: for op %s in state %s", - op.name, current_state.name), UVM_MEDIUM) + `uvm_info(`gfn, $sformatf({"process_kmac_data_req: for op %s in state %s", + "with boot_stage set to %0d"}, + op.name, current_state.name, boot_stage), UVM_MEDIUM) if (!cfg.keymgr_dpe_vif.get_keymgr_dpe_en()) begin compare_invalid_data(item.byte_data_q); @@ -168,25 +202,49 @@ class keymgr_dpe_scoreboard extends cip_base_scoreboard #( keymgr_dpe_pkg::OpDpeAdvance: begin // Invalid outputs and invalid operations should results in random data // for message data. - is_err = get_hw_invalid_input() || get_invalid_op(); - `uvm_info(`gfn, $sformatf("What is is_err: %d", is_err), UVM_MEDIUM) + invalid_hw_input = get_hw_invalid_input(); + invalid_op = get_invalid_op(); + is_err = invalid_hw_input || invalid_op; + `uvm_info(`gfn, $sformatf({"Invalid HW Input %0b || Invalid OP %0b", + " = is_err %0b"}, invalid_hw_input, invalid_op, is_err), UVM_MEDIUM) case (current_state) keymgr_dpe_pkg::StWorkDpeAvailable: begin if(boot_stage == keymgr_dpe_pkg::BootStageCreator) begin + if (DvBootStages == 2) begin + `uvm_info(`gfn, + $sformatf({"process_kmac_data_req: boot_stage %0d is_err %0d ", + "compare_boot_stage_0_data_with_creator_seed"}, + boot_stage, is_err), UVM_LOW) + compare_boot_stage_0_data_with_creator_seed( + .exp_match(!is_err), + .byte_data_q(item.byte_data_q) + ); + end else begin + `uvm_info(`gfn, + $sformatf({"process_kmac_data_req: boot_stage %0d is_err %0d ", + "compare_boot_stage_0_data_without_creator_seed"}, + boot_stage, is_err), UVM_LOW) + compare_boot_stage_0_data_without_creator_seed( + .exp_match(!is_err), + .byte_data_q(item.byte_data_q) + ); + end + + end else if (boot_stage == keymgr_dpe_pkg::BootStageOwnerInt) begin `uvm_info(`gfn, $sformatf({"process_kmac_data_req: boot_stage %0d is_err %0d", - "compare_boot_stage_0_data"}, + "compare_boot_stage_1_data"}, boot_stage, is_err), UVM_LOW) - compare_boot_stage_0_data( + compare_boot_stage_1_data( .exp_match(!is_err), .byte_data_q(item.byte_data_q) - ); + ); end else if (boot_stage == keymgr_dpe_pkg::BootStageOwner) begin `uvm_info(`gfn, $sformatf({"process_kmac_data_req: boot_stage %0d is_err %0d", - "compare_boot_stage_1_data"}, + "compare_boot_stage_2_data"}, boot_stage, is_err), UVM_LOW) - compare_boot_stage_1_data( + compare_boot_stage_2_data( .exp_match(!is_err), .byte_data_q(item.byte_data_q) ); @@ -264,6 +322,15 @@ class keymgr_dpe_scoreboard extends cip_base_scoreboard #( // runtime stage, we do not increment the boot stage. case(current_internal_key[current_key_slot.src_slot].boot_stage) keymgr_dpe_pkg::BootStageCreator: begin + if (DvBootStages == 2) begin + current_internal_key[current_key_slot.dst_slot].boot_stage = + keymgr_dpe_pkg::BootStageOwner; + end else begin + current_internal_key[current_key_slot.dst_slot].boot_stage = + keymgr_dpe_pkg::BootStageOwnerInt; + end + end + keymgr_dpe_pkg::BootStageOwnerInt: begin current_internal_key[current_key_slot.dst_slot].boot_stage = keymgr_dpe_pkg::BootStageOwner; end @@ -973,10 +1040,10 @@ class keymgr_dpe_scoreboard extends cip_base_scoreboard #( virtual function void latch_otp_key(); key_shares_t otp_key; - if (cfg.keymgr_dpe_vif.otp_key.creator_root_key_share0_valid && - cfg.keymgr_dpe_vif.otp_key.creator_root_key_share1_valid) begin - otp_key = {cfg.keymgr_dpe_vif.otp_key.creator_root_key_share1, - cfg.keymgr_dpe_vif.otp_key.creator_root_key_share0}; + if (cfg.keymgr_dpe_vif.creator_root_key.share0_valid && + cfg.keymgr_dpe_vif.creator_root_key.share1_valid) begin + otp_key = {cfg.keymgr_dpe_vif.creator_root_key.share1, + cfg.keymgr_dpe_vif.creator_root_key.share0}; end else begin if (cfg.en_cov) cov.invalid_hw_input_cg.sample(OtpRootKeyValidLow); `uvm_info(`gfn, "otp_key valid is low", UVM_LOW) @@ -984,9 +1051,15 @@ class keymgr_dpe_scoreboard extends cip_base_scoreboard #( current_internal_key[current_key_slot.dst_slot].valid = 1; current_internal_key[current_key_slot.dst_slot].boot_stage = keymgr_dpe_pkg::BootStageCreator; current_internal_key[current_key_slot.dst_slot].max_key_version = max_key_version; + // This call load the "true" UDS without the randomness present in the + // hw slot. The problem is that when this function is called (when writing + // the first time into the start register) the randomness in the hw slot + // is not yet generated. + // The current workaround is to backdoor load on the first advance call + // in the available state as the src_slot has the UDS loaded. current_internal_key[current_key_slot.dst_slot].key = otp_key; - current_internal_key[current_key_slot.dst_slot].key_policy = '0; - current_internal_key[current_key_slot.dst_slot].key_policy.allow_child = 1; + current_internal_key[current_key_slot.dst_slot].key_policy = + keymgr_dpe_pkg::DEFAULT_UDS_POLICY; `uvm_info(`gfn, $sformatf("latch_otp_key: key %p", current_internal_key[current_key_slot.dst_slot] @@ -998,6 +1071,15 @@ class keymgr_dpe_scoreboard extends cip_base_scoreboard #( ); endfunction + // Directly access the slot which holds the UDS with the xored randmoness + // Otherwise the scorebord would need to manually replicate the randomness + // generation. + virtual function void backdoor_load_uds(int slot); + `uvm_info(`gfn, "Load UDS with randomness via backdoor", UVM_MEDIUM) + current_internal_key[slot].key = + cfg.keymgr_dpe_ctrl_vif.get_key_of_slot(slot); + endfunction + virtual function bit [TL_DW-1:0] get_current_max_version( keymgr_dpe_pkg::keymgr_dpe_exposed_working_state_e state = current_state); // design change this to 0 if LC turns off keymgr_dpe. @@ -1071,11 +1153,10 @@ class keymgr_dpe_scoreboard extends cip_base_scoreboard #( key_policy : current_internal_key[current_key_slot.src_slot].key_policy; keymgr_dpe_pkg::keymgr_dpe_ops_e op = get_operation(); `uvm_info(`gfn, - $sformatf({"get_invalid_op: op %s current_state: %s, ", - "current_internal_key[%0d] = %p"}, - op.name, current_state.name, current_key_slot.src_slot, - current_internal_key[current_key_slot.src_slot]), - UVM_MEDIUM) + $sformatf({"get_invalid_op: op %s current_state: %s, src: %0d dst: %0d", + "current_internal_key = %p"}, op.name, current_state.name, + current_key_slot.src_slot, current_key_slot.dst_slot, + current_internal_key[current_key_slot.src_slot]), UVM_MEDIUM) case (current_state) keymgr_dpe_pkg::StWorkDpeReset : begin if (get_operation() != keymgr_dpe_pkg::OpDpeAdvance) begin @@ -1122,6 +1203,13 @@ class keymgr_dpe_scoreboard extends cip_base_scoreboard #( "src_slot valid == 0 err"}, op.name, current_state.name), UVM_MEDIUM) return 1; end + // Workaround to load the UDS with the xored randomness into the + // correct internal slot. The first (successful) advance call will + // use the UDS per default. + if (load_uds_with_randomness == 1'b0) begin + load_uds_with_randomness = 1'b1; + backdoor_load_uds(current_key_slot.src_slot); + end return 0; end keymgr_dpe_pkg::OpDpeGenSwOut, keymgr_dpe_pkg::OpDpeGenHwOut: begin @@ -1215,7 +1303,7 @@ class keymgr_dpe_scoreboard extends cip_base_scoreboard #( `uvm_info(`gfn, "HW invalid input on otp_device_id", UVM_LOW) end - for (int i = 0; i < keymgr_dpe_reg_pkg::NumRomDigestInputs; ++i) begin + for (int i = 0; i < keymgr_dpe_env_pkg::DvNumRomDigestInputs; ++i) begin if (cfg.keymgr_dpe_vif.rom_digests[i].data inside {0, '1}) begin invalid_hw_input_type = RomDigestInvalid; void'(ral.debug.invalid_digest.predict(1)); @@ -1265,18 +1353,23 @@ class keymgr_dpe_scoreboard extends cip_base_scoreboard #( return !(err_code[keymgr_pkg::ErrInvalidOp]) && !get_fault_err(); endfunction - virtual function void compare_boot_stage_0_data( + virtual function void compare_boot_stage_0_data_with_creator_seed( bit exp_match, const ref byte byte_data_q[$] ); - adv_creator_data_t exp, act; + adv_creator_data_with_creator_seed_t exp, act; string str = $sformatf("src_slot: %0d\n", current_key_slot.src_slot); - if (exp_match) `DV_CHECK_EQ(byte_data_q.size, keymgr_dpe_pkg::DpeAdvDataWidth / 8) + `uvm_info(`gfn, $sformatf({"Compare data for boot stage 0 including the", + " creator seed"}), UVM_MEDIUM) + + if (exp_match) begin + `DV_CHECK_EQ(byte_data_q.size, keymgr_dpe_env_pkg::DvDpeAdvDataWidth / 8) + end act = {<<8{byte_data_q}}; - exp.DiversificationKey = cfg.keymgr_dpe_vif.otp_key.creator_seed; - for (int i = 0; i < keymgr_dpe_reg_pkg::NumRomDigestInputs; ++i) begin + exp.CreatorRootSecret = cfg.keymgr_dpe_vif.creator_seed.seed; + for (int i = 0; i < keymgr_dpe_env_pkg::DvNumRomDigestInputs; ++i) begin exp.RomDigests[i] = cfg.keymgr_dpe_vif.rom_digests[i].data; end exp.HealthMeasurement = cfg.keymgr_dpe_vif.keymgr_dpe_div; @@ -1286,12 +1379,58 @@ class keymgr_dpe_scoreboard extends cip_base_scoreboard #( get_sw_binding_mirrored_value(exp.SoftwareBinding); // The order of the string creation must match the design - `CREATE_CMP_STR(DiversificationKey) - `CREATE_CMP_STR(RomDigests) + `CREATE_CMP_STR(CreatorRootSecret) + for (int i = 0; i < keymgr_dpe_env_pkg::DvNumRomDigestInputs; ++i) begin + `CREATE_CMP_STR(RomDigests[i]) + end `CREATE_CMP_STR(HealthMeasurement) `CREATE_CMP_STR(DeviceIdentifier) `CREATE_CMP_STR(HardwareRevisionSecret) - `CREATE_CMP_STR(SoftwareBinding) + for (int i = 0; i < keymgr_dpe_reg_pkg::NumSwBindingReg; i++) begin + `CREATE_CMP_STR(SoftwareBinding[i]) + end + + if (exp_match) begin + `DV_CHECK_EQ(act, exp, str) + end else begin + `DV_CHECK_NE(act, exp, str) + end + + if (exp_match) adv_data_a_array[current_key_slot.src_slot][current_state] = act; + endfunction + + virtual function void compare_boot_stage_0_data_without_creator_seed( + bit exp_match, + const ref byte byte_data_q[$] + ); + adv_creator_data_without_creator_seed_t exp, act; + string str = $sformatf("src_slot: %0d\n", current_key_slot.src_slot); + + `uvm_info(`gfn, $sformatf({"Compare data for boot stage 0 without the", + " creator seed"}), UVM_MEDIUM) + + if (exp_match) begin + `DV_CHECK_EQ(byte_data_q.size, keymgr_dpe_env_pkg::DvDpeAdvDataWidth / 8) + end + act = {<<8{byte_data_q}}; + + for (int i = 0; i < keymgr_dpe_env_pkg::DvNumRomDigestInputs; ++i) begin + exp.RomDigests[i] = cfg.keymgr_dpe_vif.rom_digests[i].data; + end + exp.HealthMeasurement = cfg.keymgr_dpe_vif.keymgr_dpe_div; + exp.DeviceIdentifier = cfg.keymgr_dpe_vif.otp_device_id; + exp.HardwareRevisionSecret = keymgr_pkg::RndCnstRevisionSeedDefault; + + get_sw_binding_mirrored_value(exp.SoftwareBinding); + + // The order of the string creation must match the design + `CREATE_CMP_STR(HardwareRevisionSecret) + `CREATE_CMP_STR(RomDigests) + `CREATE_CMP_STR(HealthMeasurement) + `CREATE_CMP_STR(DeviceIdentifier) + for (int i = 0; i < keymgr_dpe_reg_pkg::NumSwBindingReg; i++) begin + `CREATE_CMP_STR(SoftwareBinding[i]) + end if (exp_match) begin `DV_CHECK_EQ(act, exp, str) @@ -1309,8 +1448,36 @@ class keymgr_dpe_scoreboard extends cip_base_scoreboard #( adv_owner_int_data_t exp, act; string str = $sformatf("src_slot: %0d\n", current_key_slot.src_slot); + `uvm_info(`gfn, $sformatf("Compare data for boot stage 1"), UVM_MEDIUM) + + act = {<<8{byte_data_q}}; + exp.CreatorRootSecret = cfg.keymgr_dpe_vif.creator_seed.seed; + get_sw_binding_mirrored_value(exp.SoftwareBinding); + + `CREATE_CMP_STR(unused) + `CREATE_CMP_STR(CreatorRootSecret) + for (int i = 0; i < keymgr_dpe_reg_pkg::NumSwBindingReg; i++) begin + `CREATE_CMP_STR(SoftwareBinding[i]) + end + + if (exp_match) begin + `DV_CHECK_EQ(act, exp, str) + end else begin + `DV_CHECK_NE(act, exp, str) + end + + if (exp_match) adv_data_a_array[current_key_slot.src_slot][current_state] = act; + endfunction + + virtual function void compare_boot_stage_2_data( + bit exp_match, + const ref byte byte_data_q[$] + ); + adv_owner_data_t exp, act; + string str = $sformatf("src_slot: %0d\n", current_key_slot.src_slot); + act = {<<8{byte_data_q}}; - exp.OwnerRootSecret = cfg.keymgr_dpe_vif.otp_key.owner_seed; + exp.OwnerRootSecret = cfg.keymgr_dpe_vif.owner_seed.seed; get_sw_binding_mirrored_value(exp.SoftwareBinding); `CREATE_CMP_STR(unused) @@ -1334,7 +1501,7 @@ class keymgr_dpe_scoreboard extends cip_base_scoreboard #( bit exp_match, const ref byte byte_data_q[$] ); - adv_owner_data_t exp, act; + adv_runtime_data_t exp, act; string str = $sformatf("src_slot: %0d\n", current_key_slot.src_slot); act = {<<8{byte_data_q}}; @@ -1527,6 +1694,7 @@ class keymgr_dpe_scoreboard extends cip_base_scoreboard #( is_kmac_rsp_err = 0; is_kmac_invalid_data = 0; is_sw_share_corrupted = 0; + load_uds_with_randomness = 0; req_fifo.flush(); rsp_fifo.flush(); foreach (current_internal_key[slot]) begin @@ -1550,7 +1718,7 @@ class keymgr_dpe_scoreboard extends cip_base_scoreboard #( // post test checks - ensure that all local fifos and queues are empty `DV_EOT_PRINT_TLM_FIFO_CONTENTS(kmac_app_item, req_fifo) `DV_EOT_PRINT_TLM_FIFO_CONTENTS(kmac_app_item, rsp_fifo) - for (int slot = 0; slot < keymgr_dpe_pkg::DpeNumSlots; slot++) begin + for (int slot = 0; slot < keymgr_dpe_env_pkg::DvNumInstHwSlot; slot++) begin `DV_CHECK_EQ(cfg.keymgr_dpe_vif.internal_key_slots[slot].valid, current_internal_key[slot].valid) if (current_internal_key[slot].valid) begin diff --git a/hw/ip/keymgr_dpe/dv/env/seq_lib/keymgr_dpe_base_vseq.sv b/hw/ip/keymgr_dpe/dv/env/seq_lib/keymgr_dpe_base_vseq.sv index 66460a1ccc619..67c2cb593982c 100644 --- a/hw/ip/keymgr_dpe/dv/env/seq_lib/keymgr_dpe_base_vseq.sv +++ b/hw/ip/keymgr_dpe/dv/env/seq_lib/keymgr_dpe_base_vseq.sv @@ -30,8 +30,8 @@ class keymgr_dpe_base_vseq extends cip_base_vseq #( rand bit do_rand_otp_key; rand bit do_invalid_otp_key; rand keymgr_dpe_pkg::keymgr_dpe_policy_t policy; - rand keymgr_dpe_pkg::keymgr_dpe_slot_idx_e src_slot; - rand keymgr_dpe_pkg::keymgr_dpe_slot_idx_e dst_slot; + rand keymgr_dpe_env_pkg::dv_keymgr_dpe_slot_idx_e src_slot; + rand keymgr_dpe_env_pkg::dv_keymgr_dpe_slot_idx_e dst_slot; // save DUT returned current state here, rather than using it from RAL, // it's needed info to predict operation result in seq @@ -100,7 +100,14 @@ class keymgr_dpe_base_vseq extends cip_base_vseq #( end end - `uvm_info(`gfn, "Initializating key manager", UVM_MEDIUM) + `uvm_info(`gfn, "Initializing keymgr dpe", UVM_MEDIUM) + `uvm_info(`gfn, $sformatf({"Top dependent dv parameter > ", + "# Bootstages: %0d # HW slots: %0d # ROM Digest values: %0d", + " Size Adv Data: %0d"}, + keymgr_dpe_env_pkg::DvBootStages, + keymgr_dpe_env_pkg::DvNumInstHwSlot, + keymgr_dpe_env_pkg::DvNumRomDigestInputs, + keymgr_dpe_env_pkg::DvDpeAdvDataWidth), UVM_LOW) `DV_CHECK_RANDOMIZE_FATAL(ral.intr_enable) csr_update(.csr(ral.intr_enable)); @@ -118,7 +125,8 @@ class keymgr_dpe_base_vseq extends cip_base_vseq #( bit wait_done = 1 ); `uvm_info(`gfn, - $sformatf("Start keymgr_dpe_erase"), UVM_MEDIUM) + $sformatf("Start keymgr_dpe_erase"), + UVM_MEDIUM) ral.control_shadowed.operation.set(keymgr_dpe_pkg::OpDpeErase); ral.control_shadowed.slot_src_sel.set(src_slot); @@ -148,14 +156,19 @@ class keymgr_dpe_base_vseq extends cip_base_vseq #( end endtask : keymgr_dpe_erase + // Invoke the disable operation for the keymgr_dpe. This operation will + // the keymgr_dpe only if its in the available state, otherwise the DUT + // will ignore this operation. virtual task keymgr_dpe_disable( int num_gen_op = $urandom_range(1, 4), int num_adv_op = $urandom_range(1, 4), bit clr_output = $urandom_range(0, 1), bit wait_done = 1 ); + `uvm_info(`gfn, - $sformatf("Start keymgr_dpe_disable"), UVM_MEDIUM) + $sformatf("Start keymgr_dpe_disable"), + UVM_MEDIUM) ral.control_shadowed.operation.set(keymgr_dpe_pkg::OpDpeDisable); ral.control_shadowed.slot_src_sel.set(src_slot); @@ -166,8 +179,23 @@ class keymgr_dpe_base_vseq extends cip_base_vseq #( if (wait_done) wait_op_done(); - repeat (num_adv_op) begin - keymgr_dpe_advance(wait_done); + // Fix to make the smoketest pass again. The problem is that if + // the advance operation is invoked in the reset state, it is impossible + // to know how many times a valid advance operation is generated. However + // the current smoketest relies on knowing which slots are filled with + // valid entries. + // TODO(#323): Rewrite / Improve DV coverage of the keymgr_dpe + if (current_state == keymgr_dpe_pkg::StWorkDpeReset) begin + if (num_adv_op != 0) begin + `uvm_error(`gfn, + $sformatf({"Invoking the disable operation in the Reset ", + "State with the number of request advance call ", + "not set to 0 can lead to unexpected behavior."})) + end + end else begin + repeat (num_adv_op) begin + keymgr_dpe_advance(wait_done); + end end repeat (num_gen_op) begin @@ -186,8 +214,9 @@ class keymgr_dpe_base_vseq extends cip_base_vseq #( bit clr_output = $urandom_range(0, 1), bit wait_done = 1); `uvm_info(`gfn, - $sformatf("Start keymgr_dpe_operations num_gen_op %0d advance_state %0d", - num_gen_op, advance_state), UVM_MEDIUM) + $sformatf({"Start keymgr_dpe_operations num_gen_op %0d", + " advance_state %0d"}, num_gen_op, advance_state), + UVM_MEDIUM) if (advance_state) keymgr_dpe_advance(wait_done); @@ -253,8 +282,10 @@ class keymgr_dpe_base_vseq extends cip_base_vseq #( is_good_op &= !cfg.keymgr_dpe_vif.internal_key_slots[dst_slot].valid; end end - `uvm_info(`gfn, $sformatf("wait_op_done: current_state %s is_good_op %d", - current_state.name, is_good_op), UVM_MEDIUM) + `uvm_info(`gfn, + $sformatf("wait_op_done: current_state %s is_good_op %d", + current_state.name, is_good_op), + UVM_MEDIUM) end keymgr_dpe_pkg::OpDpeGenSwOut, keymgr_dpe_pkg::OpDpeGenHwOut: begin @@ -288,12 +319,13 @@ class keymgr_dpe_base_vseq extends cip_base_vseq #( end endcase - `uvm_info(`gfn, $sformatf({"Wait for operation done in state %0s, operation %0s, ", - "src_slot[%0d] = %p, dst_slot[%0d] = %p, good_op %0d"}, - current_state.name, cast_operation.name, - src_slot, cfg.keymgr_dpe_vif.internal_key_slots[src_slot], - dst_slot, cfg.keymgr_dpe_vif.internal_key_slots[dst_slot], - is_good_op), + `uvm_info(`gfn, + $sformatf({"Wait for operation done in state %0s, operation %0s, ", + "src_slot[%0d] = %p, dst_slot[%0d] = %p, good_op %0d"}, + current_state.name, cast_operation.name, + src_slot, cfg.keymgr_dpe_vif.internal_key_slots[src_slot], + dst_slot, cfg.keymgr_dpe_vif.internal_key_slots[dst_slot], + is_good_op), UVM_MEDIUM) // wait for status to get out of OpWip and check @@ -356,8 +388,13 @@ class keymgr_dpe_base_vseq extends cip_base_vseq #( keymgr_dpe_pkg::keymgr_dpe_exposed_working_state_e exp_next_state = get_next_state( current_state, keymgr_dpe_pkg::OpDpeAdvance); sema_update_control_csr.get(); - `uvm_info(`gfn, $sformatf("Advance key manager state from %0s slot %0d to %0d", - current_state.name, src_slot, dst_slot), UVM_MEDIUM) + `uvm_info(`gfn, + $sformatf({"Derive DPE context from %0d to %0d in state %s", + " - pol. parent: %b pol. child: %b, pol. export: %b"}, + src_slot, dst_slot, current_state.name, + policy.retain_parent, policy.allow_child, + policy.exportable), + UVM_LOW) ral.control_shadowed.operation.set(keymgr_dpe_pkg::OpDpeAdvance); ral.control_shadowed.slot_src_sel.set(src_slot); @@ -389,8 +426,9 @@ class keymgr_dpe_base_vseq extends cip_base_vseq #( ); sema_update_control_csr.get(); `uvm_info(`gfn, - $sformatf("Generate key manager output w/operation %s and dest %s", - operation.name, key_dest.name), UVM_MEDIUM) + $sformatf({"Generate key manager output w/operation %s and", + " dest %s"}, operation.name, key_dest.name), + UVM_MEDIUM) ral.control_shadowed.operation.set(int'(operation)); ral.control_shadowed.dest_sel.set(int'(key_dest)); diff --git a/hw/ip/keymgr_dpe/dv/env/seq_lib/keymgr_dpe_smoke_vseq.sv b/hw/ip/keymgr_dpe/dv/env/seq_lib/keymgr_dpe_smoke_vseq.sv index 6f3a63874fc7f..1e5ec3086e1f8 100644 --- a/hw/ip/keymgr_dpe/dv/env/seq_lib/keymgr_dpe_smoke_vseq.sv +++ b/hw/ip/keymgr_dpe/dv/env/seq_lib/keymgr_dpe_smoke_vseq.sv @@ -24,14 +24,16 @@ class keymgr_dpe_smoke_vseq extends keymgr_dpe_base_vseq; // for initial latch of OTP key src slot does not matter // it can be completely random constraint initial_slot_vals_c { - soft src_slot inside {[0:keymgr_dpe_pkg::DpeNumSlots-1]}; - (!otp_latched) -> {soft dst_slot == 0;} + soft src_slot < keymgr_dpe_env_pkg::DvNumInstHwSlot; + soft dst_slot < keymgr_dpe_env_pkg::DvNumInstHwSlot; + if (!otp_latched) { soft dst_slot == 0; } } task body(); // This test does the following: - // 1. While in reset state does a keymgr_dpe disable operation. - // - also does a random amount of advances and generations + // 0. The keymgr_dpe is in the reset state. + // 1. Invoke a disable operation which should not affect the keymgr_dpe + // at all as the disable operation is only valid in the available state! // 2. Fill key slots // - latches OTP key and fills all key slots // - each time doing a random amount of generations @@ -45,40 +47,64 @@ class keymgr_dpe_smoke_vseq extends keymgr_dpe_base_vseq; // Checks are done via the scoreboard, but this test does have some additional checks // in terms of valid bit for key slots for checking key slots are filled and empty - `uvm_info(`gfn, "Key Manager DPE Smoke Start", UVM_HIGH) + `uvm_info(`gfn, "Key Manager DPE smoke - disable test", UVM_LOW) // 1. - keymgr_dpe_disable(); + keymgr_dpe_disable(.num_adv_op(0)); // 2. - for (int iter = 0; iter 1 & iter < keymgr_dpe_pkg::DpeNumSlots) begin - src_slot = dst_slot; - dst_slot ++; - end - // Boot stages 0-3 are used for previous key slots. In the final key slot - // src_slot must differ from last iteration's dst slot. - // FIXME: Remove this constraint since DpeNumBootStages is removed. - else if (iter == keymgr_dpe_pkg::DpeNumSlots) begin - src_slot = $urandom_range(0,(keymgr_dpe_pkg::DpeNumSlots-1)-2); - dst_slot ++; - end + // unravel loop to both cover the retain_parent == 0/1 options + `uvm_info(`gfn, "Key Manager DPE smoke - advance test", UVM_LOW) + + // load the UDS into dst_slot with an advance call + dst_slot = 0; + `uvm_info(`gfn, + $sformatf("Key Manager DPE smoke load UDS into dst_slot %d", + dst_slot), + UVM_LOW) + + // The keymgr_dpe is advanced once (which loads the UDS into the predefined + // slot - by the constraint: initial_slot_vals_c). Afterwards the + // keymgr_dpe_base_vseq.sv generates between 5 and 10 random key generation + // operations for the keymgr_dpe. + keymgr_dpe_operations(.advance_state(1), .num_gen_op($urandom_range(5,10)), + .clr_output(1)); + + // Indicate the UDS is latched + otp_latched = 1'b1; + + // If the default policy retain_parent is clear then we add an extra + // advance call to overwrite the UDS. + if (keymgr_dpe_pkg::DEFAULT_UDS_POLICY.retain_parent == 1'b0) begin + dst_slot = 0; + src_slot = 0; + // Indicate which stage is derived `uvm_info(`gfn, - $sformatf("Key Manager DPE Smoke Iter %0d: src_slot %d dst_slot %d", - iter, src_slot ,dst_slot), UVM_HIGH) - keymgr_dpe_operations(.advance_state(1), .num_gen_op($urandom_range(5,10)), .clr_output(1)); + $sformatf({"Key Manager DPE smoke overwrite UDS when", + "retain_parent option is cleared:", + "src_slot %d dst_slot %d"}, src_slot, dst_slot), + UVM_LOW) + keymgr_dpe_operations(.advance_state(1), .num_gen_op($urandom_range(5,10)), + .clr_output(1)); + end + + // Iterate through all slots and fill them with DPE context + for (int iter = 0; iter<(keymgr_dpe_env_pkg::DvNumInstHwSlot - 1); iter++) begin + // set source and destination + src_slot = dst_slot; + dst_slot ++; + // Indicate which slot is derived + `uvm_info(`gfn, + $sformatf({"Key Manager DPE smoke iter %0d: src_slot %d", + "dst_slot %d"}, iter, src_slot, dst_slot), + UVM_MEDIUM) + keymgr_dpe_operations(.advance_state(1), .num_gen_op($urandom_range(5,10)), + .clr_output(1)); end // Check to make sure all key slots are valid after the advance operations // this is checked via the valid bit being set in the key slot - for (int slot = 0; slot < keymgr_dpe_pkg::DpeNumSlots; slot++) begin + for (int slot = 0; slot < keymgr_dpe_env_pkg::DvNumInstHwSlot; slot++) begin `DV_CHECK_EQ(cfg.keymgr_dpe_vif.internal_key_slots[slot].valid, 1) end @@ -88,7 +114,8 @@ class keymgr_dpe_smoke_vseq extends keymgr_dpe_base_vseq; // key slot that is still valid. The reason for picking slot 0 is that it should // only have boot_stage 1, which gives us enough boot_stages to advance and fill // key_slots 1-3 again. - for (int iter = 1; iter - $stable(keymgr_key_o.creator_root_key_share0_valid)) + $stable(keymgr_creator_root_key_o.share0_valid)) `OTP_ASSERT_WO_LC_ESC(OtpKeymgrValidStable1_A, pwr_otp_done_o |-> - $stable(keymgr_key_o.creator_root_key_share1_valid)) + $stable(keymgr_creator_root_key_o.share1_valid)) `OTP_ASSERT_WO_LC_ESC(OtpKeymgrValidStable2_A, pwr_otp_done_o |-> - $stable(keymgr_key_o.creator_seed_valid)) + $stable(keymgr_creator_seed_o.seed_valid)) `OTP_ASSERT_WO_LC_ESC(OtpKeymgrValidStable3_A, pwr_otp_done_o |-> - $stable(keymgr_key_o.owner_seed_valid)) + $stable(keymgr_owner_seed_o.seed_valid)) // During lc_prog_req, either otp_idle will be reset or lc_error is set `OTP_ASSERT_WO_LC_ESC(LcProgReq_A, $rose(lc_prog_req) |=> @@ -325,9 +327,9 @@ interface otp_ctrl_if(input clk_i, input rst_ni); `OTP_FATAL_ERR_ASSERT(LcDataRmaToken_A, lc_data_o.rma_token == RndCnstOtpCtrlPartInvDefault[RmaTokenOffset*8+:RmaTokenSize*8]) - `OTP_FATAL_ERR_ASSERT(KeymgrKeyData_A, keymgr_key_o.creator_root_key_share0 == + `OTP_FATAL_ERR_ASSERT(KeymgrKeyData_A, keymgr_creator_root_key_o.share0 == RndCnstOtpCtrlPartInvDefault[CreatorRootKeyShare0Offset*8+:CreatorRootKeyShare0Size*8] && - keymgr_key_o.creator_root_key_share1 == + keymgr_creator_root_key_o.share1 == RndCnstOtpCtrlPartInvDefault[CreatorRootKeyShare1Offset*8+:CreatorRootKeyShare1Size*8]) `OTP_FATAL_ERR_ASSERT(HwCfgOValid_A, otp_broadcast_o.valid == lc_ctrl_pkg::Off) diff --git a/hw/ip_templates/otp_ctrl/dv/env/otp_ctrl_scoreboard.sv.tpl b/hw/ip_templates/otp_ctrl/dv/env/otp_ctrl_scoreboard.sv.tpl index a50eb96099a9d..2e6f99cb1b250 100644 --- a/hw/ip_templates/otp_ctrl/dv/env/otp_ctrl_scoreboard.sv.tpl +++ b/hw/ip_templates/otp_ctrl/dv/env/otp_ctrl_scoreboard.sv.tpl @@ -151,9 +151,10 @@ class otp_ctrl_scoreboard #(type CFG_T = otp_ctrl_env_cfg) if (!cfg.under_reset && !cfg.otp_ctrl_vif.alert_reqs && cfg.en_scb) begin otp_ctrl_part_pkg::otp_hw_cfg0_data_t exp_hw_cfg0_data; otp_ctrl_part_pkg::otp_hw_cfg1_data_t exp_hw_cfg1_data; - otp_ctrl_pkg::otp_keymgr_key_t exp_keymgr_data; otp_ctrl_pkg::otp_lc_data_t exp_lc_data; - bit [otp_ctrl_pkg::KeyMgrKeyWidth-1:0] exp_keymgr_key0, exp_keymgr_key1; + keymgr_dpe_pkg::keymgr_dpe_creator_root_key_t exp_creator_root_key; + keymgr_dpe_pkg::keymgr_dpe_creator_seed_t exp_creator_seed; + keymgr_dpe_pkg::keymgr_dpe_owner_seed_t exp_owner_seed; if (PartInfo[dai_digest_ip].sw_digest || PartInfo[dai_digest_ip].hw_digest) begin bit [TL_DW-1:0] otp_addr = PART_OTP_DIGEST_ADDRS[dai_digest_ip]; @@ -229,7 +230,10 @@ class otp_ctrl_scoreboard #(type CFG_T = otp_ctrl_env_cfg) // ---------------------- Check keymgr_key_o output --------------------------------- // Otp_keymgr outputs creator and owner keys from secret partitions. // Depends on lc_seed_hw_rd_en_i, it will output the real keys or a constant - exp_keymgr_data = '0; + exp_creator_root_key = '0; + exp_creator_seed = '0; + exp_owner_seed = '0; + % for part in otp_mmap["partitions"]: <% part_name_camel = Name.to_camel_case(part["name"]) @@ -240,29 +244,43 @@ class otp_ctrl_scoreboard #(type CFG_T = otp_ctrl_env_cfg) item_name = Name.from_snake_case(item["name"]) item_name_camel = item_name.as_camel_case() %>\ - % if item["iskeymgr_creator"] or item["iskeymgr_owner"]: - exp_keymgr_data.${item["name"].lower()}_valid = get_otp_digest_val(${part_name_camel}Idx) != 0; + ## Generate the verification for both shares of the creator root key + % if item["name"] == 'CREATOR_ROOT_KEY_SHARE0' or item["name"] == 'CREATOR_ROOT_KEY_SHARE1': + // Fetch and verify the ${item["name"]} secret + exp_creator_root_key.${item["name"][-6:].lower()}_valid = get_otp_digest_val(${part_name_camel}Idx) != 0; if (cfg.otp_ctrl_vif.lc_seed_hw_rd_en_i == lc_ctrl_pkg::On) begin - exp_keymgr_data.${item["name"].lower()} = + exp_creator_root_key.${item["name"][-6:].lower()} = {<<32 {otp_a[${item_name_camel}Offset/4 +: ${item_name_camel}Size/4]}}; end else begin - exp_keymgr_data.${item["name"].lower()} = + exp_creator_root_key.${item["name"][-6:].lower()} = top_${topname}_rnd_cnst_pkg::RndCnstOtpCtrlPartInvDefault[${item_name_camel}Offset*8 +: ${item_name_camel}Size*8]; end - // Check otp_keymgr_key_t struct by item is easier to debug. - `DV_CHECK_EQ(cfg.otp_ctrl_vif.keymgr_key_o.${item["name"].lower()}_valid, - exp_keymgr_data.${item["name"].lower()}_valid) + `DV_CHECK_EQ(cfg.otp_ctrl_vif.keymgr_creator_root_key_o.${item["name"][-6:].lower()}_valid, + exp_creator_root_key.${item["name"][-6:].lower()}_valid) + `DV_CHECK_EQ(cfg.otp_ctrl_vif.keymgr_creator_root_key_o.${item["name"][-6:].lower()}, + exp_creator_root_key.${item["name"][-6:].lower()}) + + % endif + ## Generate the verification for the creator / owner seed + % if item["name"] == 'CREATOR_SEED' or item["name"] == 'OWNER_SEED': + // Fetch and verify the ${item["name"]} seed + exp_${item["name"].lower()}.seed_valid = get_otp_digest_val(${part_name_camel}Idx) != 0; + if (cfg.otp_ctrl_vif.lc_seed_hw_rd_en_i == lc_ctrl_pkg::On) begin + exp_${item["name"].lower()}.seed = + {<<32 {otp_a[${item_name_camel}Offset/4 +: ${item_name_camel}Size/4]}}; + end else begin + exp_${item["name"].lower()}.seed = + top_${topname}_rnd_cnst_pkg::RndCnstOtpCtrlPartInvDefault[${item_name_camel}Offset*8 +: ${item_name_camel}Size*8]; + end + `DV_CHECK_EQ(cfg.otp_ctrl_vif.keymgr_${item["name"].lower()}_o, exp_${item["name"].lower()}) + % endif % endfor % endif % endfor - - // Check otp_keymgr_key_t struct all together in case there is any missed item. - `DV_CHECK_EQ(cfg.otp_ctrl_vif.keymgr_key_o, exp_keymgr_data) - if (cfg.en_cov) begin cov.keymgr_o_cg.sample(cfg.otp_ctrl_vif.lc_seed_hw_rd_en_i == lc_ctrl_pkg::On, - exp_keymgr_data.creator_root_key_share0_valid); + exp_creator_root_key.share0_valid); end end end else if (cfg.otp_ctrl_vif.alert_reqs) begin diff --git a/hw/ip_templates/otp_ctrl/dv/tb.sv.tpl b/hw/ip_templates/otp_ctrl/dv/tb.sv.tpl index 8876d6c584584..6b2cbc02215a8 100644 --- a/hw/ip_templates/otp_ctrl/dv/tb.sv.tpl +++ b/hw/ip_templates/otp_ctrl/dv/tb.sv.tpl @@ -141,7 +141,9 @@ module tb; .lc_check_byp_en_i (otp_ctrl_if.lc_check_byp_en_i), .otp_lc_data_o (otp_ctrl_if.lc_data_o), // keymgr - .otp_keymgr_key_o (otp_ctrl_if.keymgr_key_o), + .keymgr_creator_root_key_o (otp_ctrl_if.keymgr_creator_root_key_o), + .keymgr_creator_seed_o (otp_ctrl_if.keymgr_creator_seed_o), + .keymgr_owner_seed_o (otp_ctrl_if.keymgr_owner_seed_o), % if enable_nvm_key: // nvm .nvm_otp_key_i (nvm_req), diff --git a/hw/ip_templates/otp_ctrl/rtl/otp_ctrl.sv.tpl b/hw/ip_templates/otp_ctrl/rtl/otp_ctrl.sv.tpl index 3e1ca78697f0f..778cf7dc55725 100644 --- a/hw/ip_templates/otp_ctrl/rtl/otp_ctrl.sv.tpl +++ b/hw/ip_templates/otp_ctrl/rtl/otp_ctrl.sv.tpl @@ -4,8 +4,8 @@ // // OTP Controller top. // -<% - import math +<% + import math from topgen.lib import Name %> `include "prim_assert.sv" @@ -77,8 +77,10 @@ module otp_ctrl input lc_ctrl_pkg::lc_tx_t lc_rma_state_i, // OTP broadcast outputs // SEC_CM: TOKEN_VALID.CTRL.MUBI - output otp_lc_data_t otp_lc_data_o, - output otp_keymgr_key_t otp_keymgr_key_o, + output otp_lc_data_t otp_lc_data_o, + output keymgr_dpe_pkg::keymgr_dpe_creator_root_key_t keymgr_creator_root_key_o, + output keymgr_dpe_pkg::keymgr_dpe_creator_seed_t keymgr_creator_seed_o, + output keymgr_dpe_pkg::keymgr_dpe_owner_seed_t keymgr_owner_seed_o, // Scrambling key requests % if enable_nvm_key: input nvm_otp_key_req_t nvm_otp_key_i, @@ -625,7 +627,7 @@ module otp_ctrl // Assign partition status % for r in range(int(math.ceil(len(otp_mmap["partitions"]) / 32))): % for k, part in enumerate(otp_mmap["partitions"][r*32 : (r+1)*32]): -<% +<% assignment_target = f"hw2reg.partition_status_{r}.{part['name'].lower()}_error.d" assignment_value = f"part_errors_reduced[{Name.from_snake_case(part['name']).as_camel_case()}Idx];" potential_length = 4 + len(assignment_target) + len(" = ") + len(assignment_value) @@ -1457,19 +1459,26 @@ end owner_seed_valid_q}) ); - always_comb begin : p_otp_keymgr_key_valid - // Valid reg inputs - creator_root_key_share0_valid_d = otp_keymgr_key.creator_root_key_share0_valid; - creator_root_key_share1_valid_d = otp_keymgr_key.creator_root_key_share1_valid; - creator_seed_valid_d = otp_keymgr_key.creator_seed_valid; - owner_seed_valid_d = otp_keymgr_key.owner_seed_valid; - // Output to keymgr - otp_keymgr_key_o = otp_keymgr_key; - otp_keymgr_key_o.creator_root_key_share0_valid = creator_root_key_share0_valid_q; - otp_keymgr_key_o.creator_root_key_share1_valid = creator_root_key_share1_valid_q; - otp_keymgr_key_o.creator_seed_valid = creator_seed_valid_q; - otp_keymgr_key_o.owner_seed_valid = owner_seed_valid_q; - end + assign creator_root_key_share0_valid_d = otp_keymgr_key.creator_root_key_share0_valid; + assign creator_root_key_share1_valid_d = otp_keymgr_key.creator_root_key_share1_valid; + assign keymgr_creator_root_key_o = '{ + share0: otp_keymgr_key.creator_root_key_share0, + share0_valid: creator_root_key_share0_valid_q, + share1: otp_keymgr_key.creator_root_key_share1, + share1_valid: creator_root_key_share1_valid_q + }; + + assign creator_seed_valid_d = otp_keymgr_key.creator_seed_valid; + assign keymgr_creator_seed_o = '{ + seed: otp_keymgr_key.creator_seed, + seed_valid: creator_seed_valid_q + }; + + assign owner_seed_valid_d = otp_keymgr_key.owner_seed_valid; + assign keymgr_owner_seed_o = '{ + seed: otp_keymgr_key.owner_seed, + seed_valid: owner_seed_valid_q + }; // Check that the lc_seed_hw_rd_en remains stable, once the key material is valid. `ASSERT(LcSeedHwRdEnStable0_A, @@ -1566,8 +1575,10 @@ end // Assertions // //////////////// - `ASSERT_INIT(CreatorRootKeyShare0Size_A, KeyMgrKeyWidth == CreatorRootKeyShare0Size * 8) - `ASSERT_INIT(CreatorRootKeyShare1Size_A, KeyMgrKeyWidth == CreatorRootKeyShare1Size * 8) + `ASSERT_INIT(CreatorRootKeyShare0Size_A, + keymgr_dpe_pkg::KeyMgrKeyWidth == CreatorRootKeyShare0Size * 8) + `ASSERT_INIT(CreatorRootKeyShare1Size_A, + keymgr_dpe_pkg::KeyMgrKeyWidth == CreatorRootKeyShare1Size * 8) % if enable_nvm_key: `ASSERT_INIT(NvmDataKeySeedSize_A, NvmKeySeedWidth == NvmDataKeySeedSize * 8) `ASSERT_INIT(NvmAddrKeySeedSize_A, NvmKeySeedWidth == NvmAddrKeySeedSize * 8) @@ -1587,7 +1598,9 @@ end `ASSERT_KNOWN(PwrOtpInitRspKnown_A, pwr_otp_o) `ASSERT_KNOWN(LcOtpProgramRspKnown_A, lc_otp_program_o) `ASSERT_KNOWN(OtpLcDataKnown_A, otp_lc_data_o) - `ASSERT_KNOWN(OtpKeymgrKeyKnown_A, otp_keymgr_key_o) + `ASSERT_KNOWN(KeymgrCreatorRootKey_A, keymgr_creator_root_key_o) + `ASSERT_KNOWN(KeymgrCreatorSeed_A, keymgr_creator_seed_o) + `ASSERT_KNOWN(KeymgrOwnerSeed_A, keymgr_owner_seed_o) % if enable_nvm_key: `ASSERT_KNOWN(NvmOtpKeyRspKnown_A, nvm_otp_key_o) % endif diff --git a/hw/top_darjeeling/data/autogen/top_darjeeling.gen.hjson b/hw/top_darjeeling/data/autogen/top_darjeeling.gen.hjson index 62daca2191790..dacccbf2d69c3 100644 --- a/hw/top_darjeeling/data/autogen/top_darjeeling.gen.hjson +++ b/hw/top_darjeeling/data/autogen/top_darjeeling.gen.hjson @@ -1268,10 +1268,40 @@ index: -1 } { - name: otp_keymgr_key + name: keymgr_creator_root_key desc: Key output to the key manager holding CREATOR_ROOT_KEY_SHARE0 and CREATOR_ROOT_KEY_SHARE1. - struct: otp_keymgr_key - package: otp_ctrl_pkg + struct: keymgr_dpe_creator_root_key + package: keymgr_dpe_pkg + type: uni + act: req + width: 1 + default: "'0" + inst_name: otp_ctrl + end_idx: -1 + top_type: broadcast + top_signame: otp_ctrl_keymgr_creator_root_key + index: -1 + } + { + name: keymgr_creator_seed + desc: Creator seed output to the key manager + struct: keymgr_dpe_creator_seed + package: keymgr_dpe_pkg + type: uni + act: req + width: 1 + default: "'0" + inst_name: otp_ctrl + end_idx: -1 + top_type: broadcast + top_signame: otp_ctrl_keymgr_creator_seed + index: -1 + } + { + name: keymgr_owner_seed + desc: Owner seed output to the key manager + struct: keymgr_dpe_owner_seed + package: keymgr_dpe_pkg type: uni act: req width: 1 @@ -1279,7 +1309,7 @@ inst_name: otp_ctrl end_idx: -1 top_type: broadcast - top_signame: otp_ctrl_otp_keymgr_key + top_signame: otp_ctrl_keymgr_owner_seed index: -1 } { @@ -5801,12 +5831,17 @@ domain: Main } } + param_decl: + { + NumInstHwSlot: "8" + NumBootStages: "2" + NumRomDigestInputs: "2" + } clock_connections: { clk_i: clkmgr_aon_clocks_i.clk_main_secure clk_edn_i: clkmgr_aon_clocks_i.clk_main_secure } - param_decl: {} memory: {} param_list: [ @@ -5819,6 +5854,33 @@ expose: "true" name_top: KeymgrDpeKmacEnMasking } + { + name: NumInstHwSlot + desc: Number of instantiated HW slots + type: int + default: "8" + local: "true" + expose: "true" + name_top: KeymgrDpeNumInstHwSlot + } + { + name: NumBootStages + desc: Number of available boot stage + type: int + default: "2" + local: "true" + expose: "true" + name_top: KeymgrDpeNumBootStages + } + { + name: NumRomDigestInputs + desc: Number of digest inputs from ROM_CTRL + type: int + default: "2" + local: "true" + expose: "true" + name_top: KeymgrDpeNumRomDigestInputs + } ] inter_signal_list: [ @@ -5891,28 +5953,54 @@ index: 0 } { - name: otp_key - struct: otp_keymgr_key - package: otp_ctrl_pkg + name: creator_root_key + struct: keymgr_dpe_creator_root_key + package: keymgr_dpe_pkg type: uni act: rcv width: 1 inst_name: keymgr_dpe default: "" domain: Main - top_signame: otp_ctrl_otp_keymgr_key + top_signame: otp_ctrl_keymgr_creator_root_key index: -1 } { - name: otp_device_id - struct: otp_device_id - package: otp_ctrl_pkg + name: creator_seed + struct: keymgr_dpe_creator_seed + package: keymgr_dpe_pkg type: uni act: rcv width: 1 inst_name: keymgr_dpe default: "" - top_signame: keymgr_dpe_otp_device_id + domain: Main + top_signame: otp_ctrl_keymgr_creator_seed + index: -1 + } + { + name: owner_seed + struct: keymgr_dpe_owner_seed + package: keymgr_dpe_pkg + type: uni + act: rcv + width: 1 + inst_name: keymgr_dpe + default: "" + domain: Main + top_signame: otp_ctrl_keymgr_owner_seed + index: -1 + } + { + name: device_id + struct: keymgr_dpe_device_id + package: keymgr_dpe_pkg + type: uni + act: rcv + width: 1 + inst_name: keymgr_dpe + default: "" + top_signame: keymgr_dpe_device_id index: -1 } { @@ -11373,9 +11461,17 @@ [ otbn.otbn_otp_key ] - otp_ctrl.otp_keymgr_key: + otp_ctrl.keymgr_creator_root_key: + [ + keymgr_dpe.creator_root_key + ] + otp_ctrl.keymgr_creator_seed: [ - keymgr_dpe.otp_key + keymgr_dpe.creator_seed + ] + otp_ctrl.keymgr_owner_seed: + [ + keymgr_dpe.owner_seed ] keymgr_dpe.aes_key: [ @@ -11937,7 +12033,7 @@ soc_dbg_ctrl.soc_dbg_state lc_ctrl.otp_device_id lc_ctrl.otp_manuf_state - keymgr_dpe.otp_device_id + keymgr_dpe.device_id sram_ctrl_main.otp_en_sram_ifetch rv_dm.otp_dis_rv_dm_late_debug ] @@ -22667,10 +22763,10 @@ index: -1 } { - name: otp_keymgr_key + name: keymgr_creator_root_key desc: Key output to the key manager holding CREATOR_ROOT_KEY_SHARE0 and CREATOR_ROOT_KEY_SHARE1. - struct: otp_keymgr_key - package: otp_ctrl_pkg + struct: keymgr_dpe_creator_root_key + package: keymgr_dpe_pkg type: uni act: req width: 1 @@ -22678,7 +22774,37 @@ inst_name: otp_ctrl end_idx: -1 top_type: broadcast - top_signame: otp_ctrl_otp_keymgr_key + top_signame: otp_ctrl_keymgr_creator_root_key + index: -1 + } + { + name: keymgr_creator_seed + desc: Creator seed output to the key manager + struct: keymgr_dpe_creator_seed + package: keymgr_dpe_pkg + type: uni + act: req + width: 1 + default: "'0" + inst_name: otp_ctrl + end_idx: -1 + top_type: broadcast + top_signame: otp_ctrl_keymgr_creator_seed + index: -1 + } + { + name: keymgr_owner_seed + desc: Owner seed output to the key manager + struct: keymgr_dpe_owner_seed + package: keymgr_dpe_pkg + type: uni + act: req + width: 1 + default: "'0" + inst_name: otp_ctrl + end_idx: -1 + top_type: broadcast + top_signame: otp_ctrl_keymgr_owner_seed index: -1 } { @@ -25686,28 +25812,54 @@ index: 0 } { - name: otp_key - struct: otp_keymgr_key - package: otp_ctrl_pkg + name: creator_root_key + struct: keymgr_dpe_creator_root_key + package: keymgr_dpe_pkg type: uni act: rcv width: 1 inst_name: keymgr_dpe default: "" domain: Main - top_signame: otp_ctrl_otp_keymgr_key + top_signame: otp_ctrl_keymgr_creator_root_key index: -1 } { - name: otp_device_id - struct: otp_device_id - package: otp_ctrl_pkg + name: creator_seed + struct: keymgr_dpe_creator_seed + package: keymgr_dpe_pkg + type: uni + act: rcv + width: 1 + inst_name: keymgr_dpe + default: "" + domain: Main + top_signame: otp_ctrl_keymgr_creator_seed + index: -1 + } + { + name: owner_seed + struct: keymgr_dpe_owner_seed + package: keymgr_dpe_pkg type: uni act: rcv width: 1 inst_name: keymgr_dpe default: "" - top_signame: keymgr_dpe_otp_device_id + domain: Main + top_signame: otp_ctrl_keymgr_owner_seed + index: -1 + } + { + name: device_id + struct: keymgr_dpe_device_id + package: keymgr_dpe_pkg + type: uni + act: rcv + width: 1 + inst_name: keymgr_dpe + default: "" + top_signame: keymgr_dpe_device_id index: -1 } { @@ -34240,10 +34392,34 @@ default: "" } { - package: otp_ctrl_pkg - struct: otp_keymgr_key + package: keymgr_dpe_pkg + struct: keymgr_dpe_creator_root_key domain: Main - signame: otp_ctrl_otp_keymgr_key + signame: otp_ctrl_keymgr_creator_root_key + width: 1 + type: uni + end_idx: -1 + act: req + suffix: "" + default: "'0" + } + { + package: keymgr_dpe_pkg + struct: keymgr_dpe_creator_seed + domain: Main + signame: otp_ctrl_keymgr_creator_seed + width: 1 + type: uni + end_idx: -1 + act: req + suffix: "" + default: "'0" + } + { + package: keymgr_dpe_pkg + struct: keymgr_dpe_owner_seed + domain: Main + signame: otp_ctrl_keymgr_owner_seed width: 1 type: uni end_idx: -1 @@ -36646,9 +36822,9 @@ default: "'0" } { - package: otp_ctrl_pkg - struct: otp_device_id - signame: keymgr_dpe_otp_device_id + package: keymgr_dpe_pkg + struct: keymgr_dpe_device_id + signame: keymgr_dpe_device_id domain: Main width: 1 type: uni diff --git a/hw/top_darjeeling/data/top_darjeeling.hjson b/hw/top_darjeeling/data/top_darjeeling.hjson index f0003d62b4413..aaba3cebe8e2a 100644 --- a/hw/top_darjeeling/data/top_darjeeling.hjson +++ b/hw/top_darjeeling/data/top_darjeeling.hjson @@ -732,6 +732,11 @@ base_addr: { hart: "0x21140000", }, + param_decl: { + NumInstHwSlot: "8", + NumBootStages: "2", + NumRomDigestInputs: "2", + }, }, { name: "csrng", type: "csrng", @@ -1224,10 +1229,12 @@ 'otp_ctrl.otbn_otp_key' : ['otbn.otbn_otp_key'], // KeyMgr Sideload & KDF function - 'otp_ctrl.otp_keymgr_key' : ['keymgr_dpe.otp_key'], - 'keymgr_dpe.aes_key' : ['aes.keymgr_key'], - 'keymgr_dpe.kmac_key' : ['kmac.keymgr_key'], - 'keymgr_dpe.otbn_key' : ['otbn.keymgr_key'], + 'otp_ctrl.keymgr_creator_root_key' : ['keymgr_dpe.creator_root_key'], + 'otp_ctrl.keymgr_creator_seed' : ['keymgr_dpe.creator_seed'], + 'otp_ctrl.keymgr_owner_seed' : ['keymgr_dpe.owner_seed'], + 'keymgr_dpe.aes_key' : ['aes.keymgr_key'], + 'keymgr_dpe.kmac_key' : ['kmac.keymgr_key'], + 'keymgr_dpe.otbn_key' : ['otbn.keymgr_key'], // KMAC Application Interface // Note that arbitration is fixed priority top to bottom. @@ -1362,7 +1369,7 @@ 'soc_dbg_ctrl.soc_dbg_state', 'lc_ctrl.otp_device_id', 'lc_ctrl.otp_manuf_state', - 'keymgr_dpe.otp_device_id', + 'keymgr_dpe.device_id', 'sram_ctrl_main.otp_en_sram_ifetch', # Unconditionally disable the late debug feature and enable early debug 'rv_dm.otp_dis_rv_dm_late_debug' diff --git a/hw/top_darjeeling/dv/env/seq_lib/chip_sw_keymgr_dpe_key_derivation_vseq.sv b/hw/top_darjeeling/dv/env/seq_lib/chip_sw_keymgr_dpe_key_derivation_vseq.sv index 66f24a6b39f7b..48842acae340f 100644 --- a/hw/top_darjeeling/dv/env/seq_lib/chip_sw_keymgr_dpe_key_derivation_vseq.sv +++ b/hw/top_darjeeling/dv/env/seq_lib/chip_sw_keymgr_dpe_key_derivation_vseq.sv @@ -54,14 +54,18 @@ class chip_sw_keymgr_dpe_key_derivation_vseq extends chip_sw_base_vseq; localparam int KmacDigestBytes = kmac_pkg::AppDigestW / 8; + int num_hw_slots; bit lc_at_prod; virtual task dut_init(string reset_kind = "HARD"); + string path = $sformatf("tb.dut.top_darjeeling.u_keymgr_dpe.NumInstHwSlot"); super.dut_init(reset_kind); void'($value$plusargs("lc_at_prod=%0d", lc_at_prod)); if (lc_at_prod) begin otp_write_lc_partition_state(cfg.mem_bkdr_util_h[Otp], LcStProd); end + // Backdoor load the number of hw slots available + `DV_CHECK_FATAL(uvm_hdl_read(path, num_hw_slots)) endtask virtual task body(); @@ -78,7 +82,7 @@ class chip_sw_keymgr_dpe_key_derivation_vseq extends chip_sw_base_vseq; // creator root key and be in boot stage 0. Verify that this holds. begin bit valid_found = 1'b0; - for (int i = 0; i < keymgr_dpe_pkg::DpeNumSlots; i++) begin + for (int i = 0; i < num_hw_slots; i++) begin keymgr_dpe_pkg::keymgr_dpe_slot_t slot = get_key_slot(i); if (slot.valid) begin `DV_CHECK_EQ(valid_found, 1'b0, "Expecting only one valid key slot") @@ -140,7 +144,7 @@ class chip_sw_keymgr_dpe_key_derivation_vseq extends chip_sw_base_vseq; // holds. begin bit key_found = 1'b0; - for (int i = 0; i < keymgr_dpe_pkg::DpeNumSlots; i++) begin + for (int i = 0; i < num_hw_slots; i++) begin keymgr_dpe_pkg::keymgr_dpe_slot_t slot = get_key_slot(i); if (slot.valid && slot.boot_stage == keymgr_dpe_pkg::BootStageOwner) begin `DV_CHECK_EQ(key_found, 1'b0, "Expecting only one boot stage 1 key") @@ -189,7 +193,7 @@ class chip_sw_keymgr_dpe_key_derivation_vseq extends chip_sw_base_vseq; // holds. begin bit key_found = 1'b0; - for (int i = 0; i < keymgr_dpe_pkg::DpeNumSlots; i++) begin + for (int i = 0; i < num_hw_slots; i++) begin keymgr_dpe_pkg::keymgr_dpe_slot_t slot = get_key_slot(i); if (slot.valid && slot.boot_stage == keymgr_dpe_pkg::BootStageRuntime) begin `DV_CHECK_EQ(key_found, 1'b0, "Expecting only one boot stage 2 key") @@ -237,7 +241,7 @@ class chip_sw_keymgr_dpe_key_derivation_vseq extends chip_sw_base_vseq; // holds. begin bit key_found = 1'b0; - for (int i = 0; i < keymgr_dpe_pkg::DpeNumSlots; i++) begin + for (int i = 0; i < num_hw_slots; i++) begin keymgr_dpe_pkg::keymgr_dpe_slot_t slot = get_key_slot(i); if (slot.valid && slot.boot_stage == keymgr_dpe_pkg::BootStageRuntime) begin `DV_CHECK_EQ(key_found, 1'b0, "Expecting only one boot stage 3 key") diff --git a/hw/top_darjeeling/dv/top_darjeeling_sim_cfgs.hjson b/hw/top_darjeeling/dv/top_darjeeling_sim_cfgs.hjson index 17161c3387ecd..72c1251614387 100644 --- a/hw/top_darjeeling/dv/top_darjeeling_sim_cfgs.hjson +++ b/hw/top_darjeeling/dv/top_darjeeling_sim_cfgs.hjson @@ -29,7 +29,7 @@ "{proj_root}/hw/ip/entropy_src/dv/entropy_src_rng16bits_sim_cfg.hjson", "{proj_root}/hw/ip/hmac/dv/hmac_sim_cfg.hjson", "{proj_root}/hw/ip/i2c/dv/i2c_sim_cfg.hjson", - "{proj_root}/hw/ip/keymgr_dpe/dv/keymgr_dpe_sim_cfg.hjson", + "{proj_root}/hw/ip/keymgr_dpe/dv/keymgr_dpe_darjeeling_sim_cfg.hjson", "{proj_root}/hw/ip/kmac/dv/kmac_masked_sim_cfg.hjson", "{proj_root}/hw/ip/kmac/dv/kmac_unmasked_sim_cfg.hjson", "{proj_root}/hw/ip/lc_ctrl/dv/lc_ctrl_dmi_volatile_unlock_disabled_sim_cfg.hjson", diff --git a/hw/top_darjeeling/ip_autogen/otp_ctrl/data/otp_ctrl.hjson b/hw/top_darjeeling/ip_autogen/otp_ctrl/data/otp_ctrl.hjson index 650cbb2cf83c3..9d54f948bf6f3 100644 --- a/hw/top_darjeeling/ip_autogen/otp_ctrl/data/otp_ctrl.hjson +++ b/hw/top_darjeeling/ip_autogen/otp_ctrl/data/otp_ctrl.hjson @@ -2045,14 +2045,30 @@ ''' } // Broadcast to Key Manager - { struct: "otp_keymgr_key" + { struct: "keymgr_dpe_creator_root_key" type: "uni" - name: "otp_keymgr_key" + name: "keymgr_creator_root_key" act: "req" default: "'0" - package: "otp_ctrl_pkg" + package: "keymgr_dpe_pkg" desc: "Key output to the key manager holding CREATOR_ROOT_KEY_SHARE0 and CREATOR_ROOT_KEY_SHARE1." } + { struct: "keymgr_dpe_creator_seed" + type: "uni" + name: "keymgr_creator_seed" + act: "req" + default: "'0" + package: "keymgr_dpe_pkg" + desc: "Creator seed output to the key manager" + } + { struct: "keymgr_dpe_owner_seed" + type: "uni" + name: "keymgr_owner_seed" + act: "req" + default: "'0" + package: "keymgr_dpe_pkg" + desc: "Owner seed output to the key manager" + } // Key request from SRAM scramblers { struct: "sram_otp_key" // TODO: would be nice if this could accept parameters. diff --git a/hw/top_darjeeling/ip_autogen/otp_ctrl/doc/interfaces.md b/hw/top_darjeeling/ip_autogen/otp_ctrl/doc/interfaces.md index 185ff3b156263..54338b3e6fa14 100644 --- a/hw/top_darjeeling/ip_autogen/otp_ctrl/doc/interfaces.md +++ b/hw/top_darjeeling/ip_autogen/otp_ctrl/doc/interfaces.md @@ -25,24 +25,26 @@ Referring to the [Comportable guideline for peripheral device functionality](htt ## [Inter-Module Signals](https://opentitan.org/book/doc/contributing/hw/comportability/index.html#inter-signal-handling) -| Port Name | Package::Struct | Type | Act | Width | Description | -|:-------------------------|:-----------------------------------|:--------|:------|--------:|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| edn | edn_pkg::edn | req_rsp | req | 1 | Entropy request to the entropy distribution network for LFSR reseeding and ephemeral key derivation. | -| pwr_otp | pwrmgr_pkg::pwr_otp | req_rsp | rsp | 1 | Initialization request/acknowledge from/to power manager. | -| lc_otp_program | otp_ctrl_pkg::lc_otp_program | req_rsp | rsp | 1 | Life cycle state transition interface. | -| otp_lc_data | otp_ctrl_pkg::otp_lc_data | uni | req | 1 | Life cycle state output holding the current life cycle state, the value of the transition counter and the tokens needed for life cycle transitions. | -| lc_escalate_en | lc_ctrl_pkg::lc_tx | uni | rcv | 1 | Life cycle escalation enable coming from life cycle controller. This signal moves all FSMs within OTP into the error state. | -| lc_creator_seed_sw_rw_en | lc_ctrl_pkg::lc_tx | uni | rcv | 1 | Provision enable qualifier coming from life cycle controller. This signal enables SW read / write access to the RMA_TOKEN and CREATOR_ROOT_KEY_SHARE0 and CREATOR_ROOT_KEY_SHARE1. | -| lc_owner_seed_sw_rw_en | lc_ctrl_pkg::lc_tx | uni | rcv | 1 | Provision enable qualifier coming from life cycle controller. This signal enables SW read / write access to the OWNER_SEED. | -| lc_seed_hw_rd_en | lc_ctrl_pkg::lc_tx | uni | rcv | 1 | Seed read enable coming from life cycle controller. This signal enables HW read access to the CREATOR_ROOT_KEY_SHARE0 and CREATOR_ROOT_KEY_SHARE1. | -| lc_rma_state | lc_ctrl_pkg::lc_tx | uni | rcv | 1 | This signal states whether the current life cycle is RMA. It is used to enable SW read access to (read-locked) partitions in the RMA state. | -| lc_check_byp_en | lc_ctrl_pkg::lc_tx | uni | rcv | 1 | Life cycle partition check bypass signal. This signal causes the life cycle partition to bypass consistency checks during life cycle state transitions in order to prevent spurious consistency check failures. | -| otp_keymgr_key | otp_ctrl_pkg::otp_keymgr_key | uni | req | 1 | Key output to the key manager holding CREATOR_ROOT_KEY_SHARE0 and CREATOR_ROOT_KEY_SHARE1. | -| sram_otp_key | otp_ctrl_pkg::sram_otp_key | req_rsp | rsp | 4 | Array with key derivation interfaces for SRAM scrambling devices. | -| otbn_otp_key | otp_ctrl_pkg::otbn_otp_key | req_rsp | rsp | 1 | Key derivation interface for OTBN scrambling devices. | -| otp_broadcast | otp_ctrl_part_pkg::otp_broadcast | uni | req | 1 | Output of the HW partitions with breakout data types. | -| otp_macro | otp_ctrl_macro_pkg::otp_ctrl_macro | req_rsp | req | 1 | Data interface for the OTP macro. | -| core_tl | tlul_pkg::tl | req_rsp | rsp | 1 | | +| Port Name | Package::Struct | Type | Act | Width | Description | +|:-------------------------|:--------------------------------------------|:--------|:------|--------:|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| edn | edn_pkg::edn | req_rsp | req | 1 | Entropy request to the entropy distribution network for LFSR reseeding and ephemeral key derivation. | +| pwr_otp | pwrmgr_pkg::pwr_otp | req_rsp | rsp | 1 | Initialization request/acknowledge from/to power manager. | +| lc_otp_program | otp_ctrl_pkg::lc_otp_program | req_rsp | rsp | 1 | Life cycle state transition interface. | +| otp_lc_data | otp_ctrl_pkg::otp_lc_data | uni | req | 1 | Life cycle state output holding the current life cycle state, the value of the transition counter and the tokens needed for life cycle transitions. | +| lc_escalate_en | lc_ctrl_pkg::lc_tx | uni | rcv | 1 | Life cycle escalation enable coming from life cycle controller. This signal moves all FSMs within OTP into the error state. | +| lc_creator_seed_sw_rw_en | lc_ctrl_pkg::lc_tx | uni | rcv | 1 | Provision enable qualifier coming from life cycle controller. This signal enables SW read / write access to the RMA_TOKEN and CREATOR_ROOT_KEY_SHARE0 and CREATOR_ROOT_KEY_SHARE1. | +| lc_owner_seed_sw_rw_en | lc_ctrl_pkg::lc_tx | uni | rcv | 1 | Provision enable qualifier coming from life cycle controller. This signal enables SW read / write access to the OWNER_SEED. | +| lc_seed_hw_rd_en | lc_ctrl_pkg::lc_tx | uni | rcv | 1 | Seed read enable coming from life cycle controller. This signal enables HW read access to the CREATOR_ROOT_KEY_SHARE0 and CREATOR_ROOT_KEY_SHARE1. | +| lc_rma_state | lc_ctrl_pkg::lc_tx | uni | rcv | 1 | This signal states whether the current life cycle is RMA. It is used to enable SW read access to (read-locked) partitions in the RMA state. | +| lc_check_byp_en | lc_ctrl_pkg::lc_tx | uni | rcv | 1 | Life cycle partition check bypass signal. This signal causes the life cycle partition to bypass consistency checks during life cycle state transitions in order to prevent spurious consistency check failures. | +| keymgr_creator_root_key | keymgr_dpe_pkg::keymgr_dpe_creator_root_key | uni | req | 1 | Key output to the key manager holding CREATOR_ROOT_KEY_SHARE0 and CREATOR_ROOT_KEY_SHARE1. | +| keymgr_creator_seed | keymgr_dpe_pkg::keymgr_dpe_creator_seed | uni | req | 1 | Creator seed output to the key manager | +| keymgr_owner_seed | keymgr_dpe_pkg::keymgr_dpe_owner_seed | uni | req | 1 | Owner seed output to the key manager | +| sram_otp_key | otp_ctrl_pkg::sram_otp_key | req_rsp | rsp | 4 | Array with key derivation interfaces for SRAM scrambling devices. | +| otbn_otp_key | otp_ctrl_pkg::otbn_otp_key | req_rsp | rsp | 1 | Key derivation interface for OTBN scrambling devices. | +| otp_broadcast | otp_ctrl_part_pkg::otp_broadcast | uni | req | 1 | Output of the HW partitions with breakout data types. | +| otp_macro | otp_ctrl_macro_pkg::otp_ctrl_macro | req_rsp | req | 1 | Data interface for the OTP macro. | +| core_tl | tlul_pkg::tl | req_rsp | rsp | 1 | | ## Interrupts @@ -208,10 +210,10 @@ In all other life cycle states this signal will be clamped to zero. ### Interface to Key Manager -The interface to the key manager is a simple struct that outputs the CREATOR_ROOT_KEY_SHARE0 and CREATOR_ROOT_KEY_SHARE1 keys via `otp_keymgr_key_o` if these secrets have been provisioned and locked (via CREATOR_KEY_LOCK). -Otherwise, this signal is tied to a random netlist constant. +Each individual secret key material (CREATOR_ROOT_KEY, OWNER_SEED, CREATOR_SEED) gets its own connection to the keymgr. +These secrets are provided to the keymgr if they have been provisioned and locked (via CREATOR_KEY_LOCK), otherwise these signals are tied to random netlist constants. -Since the key manager may run in a different clock domain, key manager is responsible for synchronizing the `otp_keymgr_key_o` signals. +Since the key manager may run in a different clock domain, key manager is responsible for synchronizing the `keymgr_creator_root_key_o`, `keymgr_creator_seed_o`, `keymgr_owner_seed_o` signals. ### Interfaces to SRAM and OTBN Scramblers diff --git a/hw/top_darjeeling/ip_autogen/otp_ctrl/dv/README.md b/hw/top_darjeeling/ip_autogen/otp_ctrl/dv/README.md index 50b1974b52ed5..aac6571002ea0 100644 --- a/hw/top_darjeeling/ip_autogen/otp_ctrl/dv/README.md +++ b/hw/top_darjeeling/ip_autogen/otp_ctrl/dv/README.md @@ -130,7 +130,7 @@ To avoid mismatches, scoreboard utilizes flags `dai_wr_ip` and `dai_digest_ip` t #### Assertions * TLUL assertions: The `tb/otp_ctrl_bind.sv` binds the `tlul_assert` [assertions](../../../../ip/tlul/doc/TlulProtocolChecker.md) to the IP to ensure TileLink interface protocol compliance. * Unknown checks on DUT outputs: The RTL has assertions to ensure all outputs are initialized to known values after coming out of reset. -* OTP_CTRL_IF assertions: This interface has assertions to ensure certain OTP_CTRL's outputs (such as: otp_broadcast_o, keymgr_key_o) are stable after OTP initialization. +* OTP_CTRL_IF assertions: This interface has assertions to ensure certain OTP_CTRL's outputs (such as: otp_broadcast_o, keymgr_creator_root_key_o, keymgr_creator_seed_o, keymgr_owner_seed_o) are stable after OTP initialization. ## Building and running tests The [dvsim](https://github.com/lowRISC/dvsim) tool is used for building and running our tests and regressions. diff --git a/hw/top_darjeeling/ip_autogen/otp_ctrl/dv/env/otp_ctrl_if.sv b/hw/top_darjeeling/ip_autogen/otp_ctrl/dv/env/otp_ctrl_if.sv index 44f17531cb942..55222eb81159a 100644 --- a/hw/top_darjeeling/ip_autogen/otp_ctrl/dv/env/otp_ctrl_if.sv +++ b/hw/top_darjeeling/ip_autogen/otp_ctrl/dv/env/otp_ctrl_if.sv @@ -44,7 +44,9 @@ interface otp_ctrl_if(input clk_i, input rst_ni); // Output from DUT otp_broadcast_t otp_broadcast_o; - otp_keymgr_key_t keymgr_key_o; + keymgr_dpe_pkg::keymgr_dpe_creator_root_key_t keymgr_creator_root_key_o; + keymgr_dpe_pkg::keymgr_dpe_creator_seed_t keymgr_creator_seed_o; + keymgr_dpe_pkg::keymgr_dpe_owner_seed_t keymgr_owner_seed_o; otp_lc_data_t lc_data_o; logic pwr_otp_done_o, pwr_otp_idle_o; @@ -408,13 +410,13 @@ interface otp_ctrl_if(input clk_i, input rst_ni); // Otp_keymgr valid is related to part_digest, should not be changed after otp_pwr_init `OTP_ASSERT_WO_LC_ESC(OtpKeymgrValidStable0_A, pwr_otp_done_o |-> - $stable(keymgr_key_o.creator_root_key_share0_valid)) + $stable(keymgr_creator_root_key_o.share0_valid)) `OTP_ASSERT_WO_LC_ESC(OtpKeymgrValidStable1_A, pwr_otp_done_o |-> - $stable(keymgr_key_o.creator_root_key_share1_valid)) + $stable(keymgr_creator_root_key_o.share1_valid)) `OTP_ASSERT_WO_LC_ESC(OtpKeymgrValidStable2_A, pwr_otp_done_o |-> - $stable(keymgr_key_o.creator_seed_valid)) + $stable(keymgr_creator_seed_o.seed_valid)) `OTP_ASSERT_WO_LC_ESC(OtpKeymgrValidStable3_A, pwr_otp_done_o |-> - $stable(keymgr_key_o.owner_seed_valid)) + $stable(keymgr_owner_seed_o.seed_valid)) // During lc_prog_req, either otp_idle will be reset or lc_error is set `OTP_ASSERT_WO_LC_ESC(LcProgReq_A, $rose(lc_prog_req) |=> @@ -437,9 +439,9 @@ interface otp_ctrl_if(input clk_i, input rst_ni); `OTP_FATAL_ERR_ASSERT(LcDataRmaToken_A, lc_data_o.rma_token == RndCnstOtpCtrlPartInvDefault[RmaTokenOffset*8+:RmaTokenSize*8]) - `OTP_FATAL_ERR_ASSERT(KeymgrKeyData_A, keymgr_key_o.creator_root_key_share0 == + `OTP_FATAL_ERR_ASSERT(KeymgrKeyData_A, keymgr_creator_root_key_o.share0 == RndCnstOtpCtrlPartInvDefault[CreatorRootKeyShare0Offset*8+:CreatorRootKeyShare0Size*8] && - keymgr_key_o.creator_root_key_share1 == + keymgr_creator_root_key_o.share1 == RndCnstOtpCtrlPartInvDefault[CreatorRootKeyShare1Offset*8+:CreatorRootKeyShare1Size*8]) `OTP_FATAL_ERR_ASSERT(HwCfgOValid_A, otp_broadcast_o.valid == lc_ctrl_pkg::Off) diff --git a/hw/top_darjeeling/ip_autogen/otp_ctrl/dv/env/otp_ctrl_scoreboard.sv b/hw/top_darjeeling/ip_autogen/otp_ctrl/dv/env/otp_ctrl_scoreboard.sv index 90e916793b3cb..9b2003b5ac8d5 100644 --- a/hw/top_darjeeling/ip_autogen/otp_ctrl/dv/env/otp_ctrl_scoreboard.sv +++ b/hw/top_darjeeling/ip_autogen/otp_ctrl/dv/env/otp_ctrl_scoreboard.sv @@ -146,9 +146,10 @@ class otp_ctrl_scoreboard #(type CFG_T = otp_ctrl_env_cfg) if (!cfg.under_reset && !cfg.otp_ctrl_vif.alert_reqs && cfg.en_scb) begin otp_ctrl_part_pkg::otp_hw_cfg0_data_t exp_hw_cfg0_data; otp_ctrl_part_pkg::otp_hw_cfg1_data_t exp_hw_cfg1_data; - otp_ctrl_pkg::otp_keymgr_key_t exp_keymgr_data; otp_ctrl_pkg::otp_lc_data_t exp_lc_data; - bit [otp_ctrl_pkg::KeyMgrKeyWidth-1:0] exp_keymgr_key0, exp_keymgr_key1; + keymgr_dpe_pkg::keymgr_dpe_creator_root_key_t exp_creator_root_key; + keymgr_dpe_pkg::keymgr_dpe_creator_seed_t exp_creator_seed; + keymgr_dpe_pkg::keymgr_dpe_owner_seed_t exp_owner_seed; if (PartInfo[dai_digest_ip].sw_digest || PartInfo[dai_digest_ip].hw_digest) begin bit [TL_DW-1:0] otp_addr = PART_OTP_DIGEST_ADDRS[dai_digest_ip]; @@ -224,58 +225,63 @@ class otp_ctrl_scoreboard #(type CFG_T = otp_ctrl_env_cfg) // ---------------------- Check keymgr_key_o output --------------------------------- // Otp_keymgr outputs creator and owner keys from secret partitions. // Depends on lc_seed_hw_rd_en_i, it will output the real keys or a constant - exp_keymgr_data = '0; - exp_keymgr_data.creator_root_key_share0_valid = get_otp_digest_val(Secret2Idx) != 0; + exp_creator_root_key = '0; + exp_creator_seed = '0; + exp_owner_seed = '0; + + // Fetch and verify the CREATOR_ROOT_KEY_SHARE0 secret + exp_creator_root_key.share0_valid = get_otp_digest_val(Secret2Idx) != 0; if (cfg.otp_ctrl_vif.lc_seed_hw_rd_en_i == lc_ctrl_pkg::On) begin - exp_keymgr_data.creator_root_key_share0 = + exp_creator_root_key.share0 = {<<32 {otp_a[CreatorRootKeyShare0Offset/4 +: CreatorRootKeyShare0Size/4]}}; end else begin - exp_keymgr_data.creator_root_key_share0 = + exp_creator_root_key.share0 = top_darjeeling_rnd_cnst_pkg::RndCnstOtpCtrlPartInvDefault[CreatorRootKeyShare0Offset*8 +: CreatorRootKeyShare0Size*8]; end - // Check otp_keymgr_key_t struct by item is easier to debug. - `DV_CHECK_EQ(cfg.otp_ctrl_vif.keymgr_key_o.creator_root_key_share0_valid, - exp_keymgr_data.creator_root_key_share0_valid) - exp_keymgr_data.creator_root_key_share1_valid = get_otp_digest_val(Secret2Idx) != 0; + `DV_CHECK_EQ(cfg.otp_ctrl_vif.keymgr_creator_root_key_o.share0_valid, + exp_creator_root_key.share0_valid) + `DV_CHECK_EQ(cfg.otp_ctrl_vif.keymgr_creator_root_key_o.share0, + exp_creator_root_key.share0) + + // Fetch and verify the CREATOR_ROOT_KEY_SHARE1 secret + exp_creator_root_key.share1_valid = get_otp_digest_val(Secret2Idx) != 0; if (cfg.otp_ctrl_vif.lc_seed_hw_rd_en_i == lc_ctrl_pkg::On) begin - exp_keymgr_data.creator_root_key_share1 = + exp_creator_root_key.share1 = {<<32 {otp_a[CreatorRootKeyShare1Offset/4 +: CreatorRootKeyShare1Size/4]}}; end else begin - exp_keymgr_data.creator_root_key_share1 = + exp_creator_root_key.share1 = top_darjeeling_rnd_cnst_pkg::RndCnstOtpCtrlPartInvDefault[CreatorRootKeyShare1Offset*8 +: CreatorRootKeyShare1Size*8]; end - // Check otp_keymgr_key_t struct by item is easier to debug. - `DV_CHECK_EQ(cfg.otp_ctrl_vif.keymgr_key_o.creator_root_key_share1_valid, - exp_keymgr_data.creator_root_key_share1_valid) - exp_keymgr_data.creator_seed_valid = get_otp_digest_val(Secret2Idx) != 0; + `DV_CHECK_EQ(cfg.otp_ctrl_vif.keymgr_creator_root_key_o.share1_valid, + exp_creator_root_key.share1_valid) + `DV_CHECK_EQ(cfg.otp_ctrl_vif.keymgr_creator_root_key_o.share1, + exp_creator_root_key.share1) + + // Fetch and verify the CREATOR_SEED seed + exp_creator_seed.seed_valid = get_otp_digest_val(Secret2Idx) != 0; if (cfg.otp_ctrl_vif.lc_seed_hw_rd_en_i == lc_ctrl_pkg::On) begin - exp_keymgr_data.creator_seed = + exp_creator_seed.seed = {<<32 {otp_a[CreatorSeedOffset/4 +: CreatorSeedSize/4]}}; end else begin - exp_keymgr_data.creator_seed = + exp_creator_seed.seed = top_darjeeling_rnd_cnst_pkg::RndCnstOtpCtrlPartInvDefault[CreatorSeedOffset*8 +: CreatorSeedSize*8]; end - // Check otp_keymgr_key_t struct by item is easier to debug. - `DV_CHECK_EQ(cfg.otp_ctrl_vif.keymgr_key_o.creator_seed_valid, - exp_keymgr_data.creator_seed_valid) - exp_keymgr_data.owner_seed_valid = get_otp_digest_val(Secret3Idx) != 0; + `DV_CHECK_EQ(cfg.otp_ctrl_vif.keymgr_creator_seed_o, exp_creator_seed) + + // Fetch and verify the OWNER_SEED seed + exp_owner_seed.seed_valid = get_otp_digest_val(Secret3Idx) != 0; if (cfg.otp_ctrl_vif.lc_seed_hw_rd_en_i == lc_ctrl_pkg::On) begin - exp_keymgr_data.owner_seed = + exp_owner_seed.seed = {<<32 {otp_a[OwnerSeedOffset/4 +: OwnerSeedSize/4]}}; end else begin - exp_keymgr_data.owner_seed = + exp_owner_seed.seed = top_darjeeling_rnd_cnst_pkg::RndCnstOtpCtrlPartInvDefault[OwnerSeedOffset*8 +: OwnerSeedSize*8]; end - // Check otp_keymgr_key_t struct by item is easier to debug. - `DV_CHECK_EQ(cfg.otp_ctrl_vif.keymgr_key_o.owner_seed_valid, - exp_keymgr_data.owner_seed_valid) - - // Check otp_keymgr_key_t struct all together in case there is any missed item. - `DV_CHECK_EQ(cfg.otp_ctrl_vif.keymgr_key_o, exp_keymgr_data) + `DV_CHECK_EQ(cfg.otp_ctrl_vif.keymgr_owner_seed_o, exp_owner_seed) if (cfg.en_cov) begin cov.keymgr_o_cg.sample(cfg.otp_ctrl_vif.lc_seed_hw_rd_en_i == lc_ctrl_pkg::On, - exp_keymgr_data.creator_root_key_share0_valid); + exp_creator_root_key.share0_valid); end end end else if (cfg.otp_ctrl_vif.alert_reqs) begin diff --git a/hw/top_darjeeling/ip_autogen/otp_ctrl/dv/tb.sv b/hw/top_darjeeling/ip_autogen/otp_ctrl/dv/tb.sv index 560f400af0482..aab615aa70f4a 100644 --- a/hw/top_darjeeling/ip_autogen/otp_ctrl/dv/tb.sv +++ b/hw/top_darjeeling/ip_autogen/otp_ctrl/dv/tb.sv @@ -129,7 +129,9 @@ module tb; .lc_check_byp_en_i (otp_ctrl_if.lc_check_byp_en_i), .otp_lc_data_o (otp_ctrl_if.lc_data_o), // keymgr - .otp_keymgr_key_o (otp_ctrl_if.keymgr_key_o), + .keymgr_creator_root_key_o (otp_ctrl_if.keymgr_creator_root_key_o), + .keymgr_creator_seed_o (otp_ctrl_if.keymgr_creator_seed_o), + .keymgr_owner_seed_o (otp_ctrl_if.keymgr_owner_seed_o), // sram .sram_otp_key_i (sram_req), .sram_otp_key_o (sram_rsp), diff --git a/hw/top_darjeeling/ip_autogen/otp_ctrl/rtl/otp_ctrl.sv b/hw/top_darjeeling/ip_autogen/otp_ctrl/rtl/otp_ctrl.sv index 0adfa3bfae5ba..de6ba49b67a39 100644 --- a/hw/top_darjeeling/ip_autogen/otp_ctrl/rtl/otp_ctrl.sv +++ b/hw/top_darjeeling/ip_autogen/otp_ctrl/rtl/otp_ctrl.sv @@ -72,8 +72,10 @@ module otp_ctrl input lc_ctrl_pkg::lc_tx_t lc_rma_state_i, // OTP broadcast outputs // SEC_CM: TOKEN_VALID.CTRL.MUBI - output otp_lc_data_t otp_lc_data_o, - output otp_keymgr_key_t otp_keymgr_key_o, + output otp_lc_data_t otp_lc_data_o, + output keymgr_dpe_pkg::keymgr_dpe_creator_root_key_t keymgr_creator_root_key_o, + output keymgr_dpe_pkg::keymgr_dpe_creator_seed_t keymgr_creator_seed_o, + output keymgr_dpe_pkg::keymgr_dpe_owner_seed_t keymgr_owner_seed_o, // Scrambling key requests input sram_otp_key_req_t [NumSramKeyReqSlots-1:0] sram_otp_key_i, output sram_otp_key_rsp_t [NumSramKeyReqSlots-1:0] sram_otp_key_o, @@ -1453,19 +1455,26 @@ end owner_seed_valid_q}) ); - always_comb begin : p_otp_keymgr_key_valid - // Valid reg inputs - creator_root_key_share0_valid_d = otp_keymgr_key.creator_root_key_share0_valid; - creator_root_key_share1_valid_d = otp_keymgr_key.creator_root_key_share1_valid; - creator_seed_valid_d = otp_keymgr_key.creator_seed_valid; - owner_seed_valid_d = otp_keymgr_key.owner_seed_valid; - // Output to keymgr - otp_keymgr_key_o = otp_keymgr_key; - otp_keymgr_key_o.creator_root_key_share0_valid = creator_root_key_share0_valid_q; - otp_keymgr_key_o.creator_root_key_share1_valid = creator_root_key_share1_valid_q; - otp_keymgr_key_o.creator_seed_valid = creator_seed_valid_q; - otp_keymgr_key_o.owner_seed_valid = owner_seed_valid_q; - end + assign creator_root_key_share0_valid_d = otp_keymgr_key.creator_root_key_share0_valid; + assign creator_root_key_share1_valid_d = otp_keymgr_key.creator_root_key_share1_valid; + assign keymgr_creator_root_key_o = '{ + share0: otp_keymgr_key.creator_root_key_share0, + share0_valid: creator_root_key_share0_valid_q, + share1: otp_keymgr_key.creator_root_key_share1, + share1_valid: creator_root_key_share1_valid_q + }; + + assign creator_seed_valid_d = otp_keymgr_key.creator_seed_valid; + assign keymgr_creator_seed_o = '{ + seed: otp_keymgr_key.creator_seed, + seed_valid: creator_seed_valid_q + }; + + assign owner_seed_valid_d = otp_keymgr_key.owner_seed_valid; + assign keymgr_owner_seed_o = '{ + seed: otp_keymgr_key.owner_seed, + seed_valid: owner_seed_valid_q + }; // Check that the lc_seed_hw_rd_en remains stable, once the key material is valid. `ASSERT(LcSeedHwRdEnStable0_A, @@ -1556,8 +1565,10 @@ end // Assertions // //////////////// - `ASSERT_INIT(CreatorRootKeyShare0Size_A, KeyMgrKeyWidth == CreatorRootKeyShare0Size * 8) - `ASSERT_INIT(CreatorRootKeyShare1Size_A, KeyMgrKeyWidth == CreatorRootKeyShare1Size * 8) + `ASSERT_INIT(CreatorRootKeyShare0Size_A, + keymgr_dpe_pkg::KeyMgrKeyWidth == CreatorRootKeyShare0Size * 8) + `ASSERT_INIT(CreatorRootKeyShare1Size_A, + keymgr_dpe_pkg::KeyMgrKeyWidth == CreatorRootKeyShare1Size * 8) `ASSERT_INIT(SramDataKeySeedSize_A, SramKeySeedWidth == SramDataKeySeedSize * 8) `ASSERT_INIT(RmaTokenSize_A, lc_ctrl_state_pkg::LcTokenWidth == RmaTokenSize * 8) @@ -1573,7 +1584,9 @@ end `ASSERT_KNOWN(PwrOtpInitRspKnown_A, pwr_otp_o) `ASSERT_KNOWN(LcOtpProgramRspKnown_A, lc_otp_program_o) `ASSERT_KNOWN(OtpLcDataKnown_A, otp_lc_data_o) - `ASSERT_KNOWN(OtpKeymgrKeyKnown_A, otp_keymgr_key_o) + `ASSERT_KNOWN(KeymgrCreatorRootKey_A, keymgr_creator_root_key_o) + `ASSERT_KNOWN(KeymgrCreatorSeed_A, keymgr_creator_seed_o) + `ASSERT_KNOWN(KeymgrOwnerSeed_A, keymgr_owner_seed_o) `ASSERT_KNOWN(OtpSramKeyKnown_A, sram_otp_key_o) `ASSERT_KNOWN(OtpOtgnKeyKnown_A, otbn_otp_key_o) `ASSERT_KNOWN(OtpBroadcastKnown_A, otp_broadcast_o) diff --git a/hw/top_darjeeling/rtl/autogen/top_darjeeling.sv b/hw/top_darjeeling/rtl/autogen/top_darjeeling.sv index 949658d7af224..13918ffbcc1fb 100644 --- a/hw/top_darjeeling/rtl/autogen/top_darjeeling.sv +++ b/hw/top_darjeeling/rtl/autogen/top_darjeeling.sv @@ -341,6 +341,10 @@ module top_darjeeling #( localparam int LcCtrlNumRmaAckSigs = 1; // local parameters for spi_host0 localparam int SpiHost0NumCS = 1; + // local parameters for keymgr_dpe + localparam int KeymgrDpeNumInstHwSlot = 8; + localparam int KeymgrDpeNumBootStages = 2; + localparam int KeymgrDpeNumRomDigestInputs = 2; // local parameters for entropy_src localparam int EntropySrcEsFifoDepth = 3; localparam int unsigned EntropySrcDistrFifoDepth = 11; @@ -579,7 +583,9 @@ module top_darjeeling #( edn_pkg::edn_rsp_t [Edn1NumEndPoints-1:0] edn1_edn_rsp; otp_ctrl_pkg::otbn_otp_key_req_t otp_ctrl_otbn_otp_key_req; otp_ctrl_pkg::otbn_otp_key_rsp_t otp_ctrl_otbn_otp_key_rsp; - otp_ctrl_pkg::otp_keymgr_key_t otp_ctrl_otp_keymgr_key; + keymgr_dpe_pkg::keymgr_dpe_creator_root_key_t otp_ctrl_keymgr_creator_root_key; + keymgr_dpe_pkg::keymgr_dpe_creator_seed_t otp_ctrl_keymgr_creator_seed; + keymgr_dpe_pkg::keymgr_dpe_owner_seed_t otp_ctrl_keymgr_owner_seed; keymgr_pkg::hw_key_req_t keymgr_dpe_aes_key; keymgr_pkg::hw_key_req_t keymgr_dpe_kmac_key; keymgr_pkg::otbn_key_req_t keymgr_dpe_otbn_key; @@ -772,7 +778,7 @@ module top_darjeeling #( lc_ctrl_state_pkg::soc_dbg_state_t soc_dbg_ctrl_soc_dbg_state; otp_ctrl_pkg::otp_device_id_t lc_ctrl_otp_device_id; otp_ctrl_pkg::otp_manuf_state_t lc_ctrl_otp_manuf_state; - otp_ctrl_pkg::otp_device_id_t keymgr_dpe_otp_device_id; + keymgr_dpe_pkg::keymgr_dpe_device_id_t keymgr_dpe_device_id; prim_mubi_pkg::mubi8_t sram_ctrl_main_otp_en_sram_ifetch; prim_mubi_pkg::mubi8_t rv_dm_otp_dis_rv_dm_late_debug; @@ -811,7 +817,7 @@ module top_darjeeling #( otp_ctrl_otp_broadcast.hw_cfg1_data.soc_dbg_state; assign lc_ctrl_otp_manuf_state = otp_ctrl_otp_broadcast.hw_cfg0_data.manuf_state; - assign keymgr_dpe_otp_device_id = + assign keymgr_dpe_device_id = otp_ctrl_otp_broadcast.hw_cfg0_data.device_id; logic unused_otp_broadcast_bits; @@ -1192,7 +1198,9 @@ module top_darjeeling #( .lc_seed_hw_rd_en_i(lc_ctrl_lc_seed_hw_rd_en), .lc_rma_state_i(lc_ctrl_lc_rma_state), .lc_check_byp_en_i(lc_ctrl_lc_check_byp_en), - .otp_keymgr_key_o(otp_ctrl_otp_keymgr_key), + .keymgr_creator_root_key_o(otp_ctrl_keymgr_creator_root_key), + .keymgr_creator_seed_o(otp_ctrl_keymgr_creator_seed), + .keymgr_owner_seed_o(otp_ctrl_keymgr_owner_seed), .sram_otp_key_i(otp_ctrl_sram_otp_key_req), .sram_otp_key_o(otp_ctrl_sram_otp_key_rsp), .otbn_otp_key_i(otp_ctrl_otbn_otp_key_req), @@ -1696,7 +1704,10 @@ module top_darjeeling #( .RndCnstAesSeed(RndCnstKeymgrDpeAesSeed), .RndCnstKmacSeed(RndCnstKeymgrDpeKmacSeed), .RndCnstOtbnSeed(RndCnstKeymgrDpeOtbnSeed), - .RndCnstNoneSeed(RndCnstKeymgrDpeNoneSeed) + .RndCnstNoneSeed(RndCnstKeymgrDpeNoneSeed), + .NumInstHwSlot(KeymgrDpeNumInstHwSlot), + .NumBootStages(KeymgrDpeNumBootStages), + .NumRomDigestInputs(KeymgrDpeNumRomDigestInputs) ) u_keymgr_dpe ( // Clock and reset connections .clk_i(clkmgr_aon_clocks_i.clk_main_secure), @@ -1721,8 +1732,10 @@ module top_darjeeling #( .otbn_key_o(keymgr_dpe_otbn_key), .kmac_data_o(kmac_app_req[0]), .kmac_data_i(kmac_app_rsp[0]), - .otp_key_i(otp_ctrl_otp_keymgr_key), - .otp_device_id_i(keymgr_dpe_otp_device_id), + .creator_root_key_i(otp_ctrl_keymgr_creator_root_key), + .creator_seed_i(otp_ctrl_keymgr_creator_seed), + .owner_seed_i(otp_ctrl_keymgr_owner_seed), + .device_id_i(keymgr_dpe_device_id), .lc_keymgr_en_i(lc_ctrl_lc_keymgr_en), .lc_keymgr_div_i(lc_ctrl_lc_keymgr_div), .rom_digest_i(keymgr_dpe_rom_digest), diff --git a/hw/top_darjeeling/templates/toplevel.sv.tpl b/hw/top_darjeeling/templates/toplevel.sv.tpl index 21d287bc889d9..03f8b6da9e2ff 100644 --- a/hw/top_darjeeling/templates/toplevel.sv.tpl +++ b/hw/top_darjeeling/templates/toplevel.sv.tpl @@ -52,7 +52,7 @@ module top_${top["name"]} #( otp_ctrl_otp_broadcast.hw_cfg0_data.manuf_state; % for mod in top["module"]: % if mod["type"] in ["keymgr", "keymgr_dpe"]: - assign ${mod["name"]}_otp_device_id = + assign ${mod["name"]}_device_id = otp_ctrl_otp_broadcast.hw_cfg0_data.device_id; % endif % endfor diff --git a/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson b/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson index c2ee4b7eca050..e6fce5edf765d 100644 --- a/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson +++ b/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson @@ -1948,18 +1948,42 @@ index: -1 } { - name: otp_keymgr_key + name: keymgr_creator_root_key desc: Key output to the key manager holding CREATOR_ROOT_KEY_SHARE0 and CREATOR_ROOT_KEY_SHARE1. - struct: otp_keymgr_key - package: otp_ctrl_pkg + struct: keymgr_dpe_creator_root_key + package: keymgr_dpe_pkg type: uni act: req width: 1 default: "'0" inst_name: otp_ctrl - end_idx: -1 - top_type: broadcast - top_signame: otp_ctrl_otp_keymgr_key + top_signame: otp_ctrl_keymgr_creator_root_key + index: -1 + } + { + name: keymgr_creator_seed + desc: Creator seed output to the key manager + struct: keymgr_dpe_creator_seed + package: keymgr_dpe_pkg + type: uni + act: req + width: 1 + default: "'0" + inst_name: otp_ctrl + top_signame: otp_ctrl_keymgr_creator_seed + index: -1 + } + { + name: keymgr_owner_seed + desc: Owner seed output to the key manager + struct: keymgr_dpe_owner_seed + package: keymgr_dpe_pkg + type: uni + act: req + width: 1 + default: "'0" + inst_name: otp_ctrl + top_signame: otp_ctrl_keymgr_owner_seed index: -1 } { @@ -7951,8 +7975,7 @@ width: 1 inst_name: keymgr default: "" - domain: Main - top_signame: otp_ctrl_otp_keymgr_key + top_signame: keymgr_otp_key index: -1 } { @@ -10238,10 +10261,6 @@ [ otbn.otbn_otp_key ] - otp_ctrl.otp_keymgr_key: - [ - keymgr.otp_key - ] keymgr.aes_key: [ aes.keymgr_key @@ -10665,6 +10684,10 @@ keymgr.otp_device_id sram_ctrl_main.otp_en_sram_ifetch rv_dm.otp_dis_rv_dm_late_debug + keymgr.otp_key + otp_ctrl.keymgr_creator_root_key + otp_ctrl.keymgr_creator_seed + otp_ctrl.keymgr_owner_seed ] external: { @@ -20977,18 +21000,42 @@ index: -1 } { - name: otp_keymgr_key + name: keymgr_creator_root_key desc: Key output to the key manager holding CREATOR_ROOT_KEY_SHARE0 and CREATOR_ROOT_KEY_SHARE1. - struct: otp_keymgr_key - package: otp_ctrl_pkg + struct: keymgr_dpe_creator_root_key + package: keymgr_dpe_pkg type: uni act: req width: 1 default: "'0" inst_name: otp_ctrl - end_idx: -1 - top_type: broadcast - top_signame: otp_ctrl_otp_keymgr_key + top_signame: otp_ctrl_keymgr_creator_root_key + index: -1 + } + { + name: keymgr_creator_seed + desc: Creator seed output to the key manager + struct: keymgr_dpe_creator_seed + package: keymgr_dpe_pkg + type: uni + act: req + width: 1 + default: "'0" + inst_name: otp_ctrl + top_signame: otp_ctrl_keymgr_creator_seed + index: -1 + } + { + name: keymgr_owner_seed + desc: Owner seed output to the key manager + struct: keymgr_dpe_owner_seed + package: keymgr_dpe_pkg + type: uni + act: req + width: 1 + default: "'0" + inst_name: otp_ctrl + top_signame: otp_ctrl_keymgr_owner_seed index: -1 } { @@ -24999,8 +25046,7 @@ width: 1 inst_name: keymgr default: "" - domain: Main - top_signame: otp_ctrl_otp_keymgr_key + top_signame: keymgr_otp_key index: -1 } { @@ -31258,18 +31304,6 @@ suffix: rsp default: "" } - { - package: otp_ctrl_pkg - struct: otp_keymgr_key - domain: Main - signame: otp_ctrl_otp_keymgr_key - width: 1 - type: uni - end_idx: -1 - act: req - suffix: "" - default: "'0" - } { package: keymgr_pkg struct: hw_key_req @@ -32974,6 +33008,46 @@ end_idx: -1 default: prim_mubi_pkg::MuBi8False } + { + package: otp_ctrl_pkg + struct: otp_keymgr_key + signame: keymgr_otp_key + domain: Main + width: 1 + type: uni + end_idx: -1 + default: "" + } + { + package: keymgr_dpe_pkg + struct: keymgr_dpe_creator_root_key + signame: otp_ctrl_keymgr_creator_root_key + domain: Main + width: 1 + type: uni + end_idx: -1 + default: "'0" + } + { + package: keymgr_dpe_pkg + struct: keymgr_dpe_creator_seed + signame: otp_ctrl_keymgr_creator_seed + domain: Main + width: 1 + type: uni + end_idx: -1 + default: "'0" + } + { + package: keymgr_dpe_pkg + struct: keymgr_dpe_owner_seed + signame: otp_ctrl_keymgr_owner_seed + domain: Main + width: 1 + type: uni + end_idx: -1 + default: "'0" + } ] } } diff --git a/hw/top_earlgrey/data/top_earlgrey.hjson b/hw/top_earlgrey/data/top_earlgrey.hjson index 4f5cf143b85b0..8efdc9129eb21 100644 --- a/hw/top_earlgrey/data/top_earlgrey.hjson +++ b/hw/top_earlgrey/data/top_earlgrey.hjson @@ -1194,7 +1194,6 @@ 'otp_ctrl.otbn_otp_key' : ['otbn.otbn_otp_key'], // KeyMgr Sideload & KDF function - 'otp_ctrl.otp_keymgr_key' : ['keymgr.otp_key'], 'keymgr.aes_key' : ['aes.keymgr_key'], 'keymgr.kmac_key' : ['kmac.keymgr_key'], 'keymgr.otbn_key' : ['otbn.keymgr_key'], @@ -1339,6 +1338,13 @@ 'keymgr.otp_device_id', 'sram_ctrl_main.otp_en_sram_ifetch', 'rv_dm.otp_dis_rv_dm_late_debug', + + // Connect the keymaterial from the OTP manually + // TODO: resolve this manual fix + 'keymgr.otp_key', + 'otp_ctrl.keymgr_creator_root_key', + 'otp_ctrl.keymgr_creator_seed', + 'otp_ctrl.keymgr_owner_seed', ], // ext is to create port in the top. diff --git a/hw/top_earlgrey/ip_autogen/otp_ctrl/data/otp_ctrl.hjson b/hw/top_earlgrey/ip_autogen/otp_ctrl/data/otp_ctrl.hjson index 5540c4866f734..ffc5672ec1b80 100644 --- a/hw/top_earlgrey/ip_autogen/otp_ctrl/data/otp_ctrl.hjson +++ b/hw/top_earlgrey/ip_autogen/otp_ctrl/data/otp_ctrl.hjson @@ -1895,14 +1895,30 @@ ''' } // Broadcast to Key Manager - { struct: "otp_keymgr_key" + { struct: "keymgr_dpe_creator_root_key" type: "uni" - name: "otp_keymgr_key" + name: "keymgr_creator_root_key" act: "req" default: "'0" - package: "otp_ctrl_pkg" + package: "keymgr_dpe_pkg" desc: "Key output to the key manager holding CREATOR_ROOT_KEY_SHARE0 and CREATOR_ROOT_KEY_SHARE1." } + { struct: "keymgr_dpe_creator_seed" + type: "uni" + name: "keymgr_creator_seed" + act: "req" + default: "'0" + package: "keymgr_dpe_pkg" + desc: "Creator seed output to the key manager" + } + { struct: "keymgr_dpe_owner_seed" + type: "uni" + name: "keymgr_owner_seed" + act: "req" + default: "'0" + package: "keymgr_dpe_pkg" + desc: "Owner seed output to the key manager" + } // Broadcast to Nvm Controller { struct: "nvm_otp_key" type: "req_rsp" diff --git a/hw/top_earlgrey/ip_autogen/otp_ctrl/doc/interfaces.md b/hw/top_earlgrey/ip_autogen/otp_ctrl/doc/interfaces.md index a56fa8d317829..766cccf68d12c 100644 --- a/hw/top_earlgrey/ip_autogen/otp_ctrl/doc/interfaces.md +++ b/hw/top_earlgrey/ip_autogen/otp_ctrl/doc/interfaces.md @@ -25,25 +25,27 @@ Referring to the [Comportable guideline for peripheral device functionality](htt ## [Inter-Module Signals](https://opentitan.org/book/doc/contributing/hw/comportability/index.html#inter-signal-handling) -| Port Name | Package::Struct | Type | Act | Width | Description | -|:-------------------------|:-----------------------------------|:--------|:------|--------:|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| edn | edn_pkg::edn | req_rsp | req | 1 | Entropy request to the entropy distribution network for LFSR reseeding and ephemeral key derivation. | -| pwr_otp | pwrmgr_pkg::pwr_otp | req_rsp | rsp | 1 | Initialization request/acknowledge from/to power manager. | -| lc_otp_program | otp_ctrl_pkg::lc_otp_program | req_rsp | rsp | 1 | Life cycle state transition interface. | -| otp_lc_data | otp_ctrl_pkg::otp_lc_data | uni | req | 1 | Life cycle state output holding the current life cycle state, the value of the transition counter and the tokens needed for life cycle transitions. | -| lc_escalate_en | lc_ctrl_pkg::lc_tx | uni | rcv | 1 | Life cycle escalation enable coming from life cycle controller. This signal moves all FSMs within OTP into the error state. | -| lc_creator_seed_sw_rw_en | lc_ctrl_pkg::lc_tx | uni | rcv | 1 | Provision enable qualifier coming from life cycle controller. This signal enables SW read / write access to the RMA_TOKEN and CREATOR_ROOT_KEY_SHARE0 and CREATOR_ROOT_KEY_SHARE1. | -| lc_owner_seed_sw_rw_en | lc_ctrl_pkg::lc_tx | uni | rcv | 1 | Provision enable qualifier coming from life cycle controller. This signal enables SW read / write access to the OWNER_SEED. | -| lc_seed_hw_rd_en | lc_ctrl_pkg::lc_tx | uni | rcv | 1 | Seed read enable coming from life cycle controller. This signal enables HW read access to the CREATOR_ROOT_KEY_SHARE0 and CREATOR_ROOT_KEY_SHARE1. | -| lc_rma_state | lc_ctrl_pkg::lc_tx | uni | rcv | 1 | This signal states whether the current life cycle is RMA. It is used to enable SW read access to (read-locked) partitions in the RMA state. | -| lc_check_byp_en | lc_ctrl_pkg::lc_tx | uni | rcv | 1 | Life cycle partition check bypass signal. This signal causes the life cycle partition to bypass consistency checks during life cycle state transitions in order to prevent spurious consistency check failures. | -| otp_keymgr_key | otp_ctrl_pkg::otp_keymgr_key | uni | req | 1 | Key output to the key manager holding CREATOR_ROOT_KEY_SHARE0 and CREATOR_ROOT_KEY_SHARE1. | -| nvm_otp_key | otp_ctrl_pkg::nvm_otp_key | req_rsp | rsp | 1 | Key derivation interface for NVM scrambling. | -| sram_otp_key | otp_ctrl_pkg::sram_otp_key | req_rsp | rsp | 4 | Array with key derivation interfaces for SRAM scrambling devices. | -| otbn_otp_key | otp_ctrl_pkg::otbn_otp_key | req_rsp | rsp | 1 | Key derivation interface for OTBN scrambling devices. | -| otp_broadcast | otp_ctrl_part_pkg::otp_broadcast | uni | req | 1 | Output of the HW partitions with breakout data types. | -| otp_macro | otp_ctrl_macro_pkg::otp_ctrl_macro | req_rsp | req | 1 | Data interface for the OTP macro. | -| core_tl | tlul_pkg::tl | req_rsp | rsp | 1 | | +| Port Name | Package::Struct | Type | Act | Width | Description | +|:-------------------------|:--------------------------------------------|:--------|:------|--------:|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| edn | edn_pkg::edn | req_rsp | req | 1 | Entropy request to the entropy distribution network for LFSR reseeding and ephemeral key derivation. | +| pwr_otp | pwrmgr_pkg::pwr_otp | req_rsp | rsp | 1 | Initialization request/acknowledge from/to power manager. | +| lc_otp_program | otp_ctrl_pkg::lc_otp_program | req_rsp | rsp | 1 | Life cycle state transition interface. | +| otp_lc_data | otp_ctrl_pkg::otp_lc_data | uni | req | 1 | Life cycle state output holding the current life cycle state, the value of the transition counter and the tokens needed for life cycle transitions. | +| lc_escalate_en | lc_ctrl_pkg::lc_tx | uni | rcv | 1 | Life cycle escalation enable coming from life cycle controller. This signal moves all FSMs within OTP into the error state. | +| lc_creator_seed_sw_rw_en | lc_ctrl_pkg::lc_tx | uni | rcv | 1 | Provision enable qualifier coming from life cycle controller. This signal enables SW read / write access to the RMA_TOKEN and CREATOR_ROOT_KEY_SHARE0 and CREATOR_ROOT_KEY_SHARE1. | +| lc_owner_seed_sw_rw_en | lc_ctrl_pkg::lc_tx | uni | rcv | 1 | Provision enable qualifier coming from life cycle controller. This signal enables SW read / write access to the OWNER_SEED. | +| lc_seed_hw_rd_en | lc_ctrl_pkg::lc_tx | uni | rcv | 1 | Seed read enable coming from life cycle controller. This signal enables HW read access to the CREATOR_ROOT_KEY_SHARE0 and CREATOR_ROOT_KEY_SHARE1. | +| lc_rma_state | lc_ctrl_pkg::lc_tx | uni | rcv | 1 | This signal states whether the current life cycle is RMA. It is used to enable SW read access to (read-locked) partitions in the RMA state. | +| lc_check_byp_en | lc_ctrl_pkg::lc_tx | uni | rcv | 1 | Life cycle partition check bypass signal. This signal causes the life cycle partition to bypass consistency checks during life cycle state transitions in order to prevent spurious consistency check failures. | +| keymgr_creator_root_key | keymgr_dpe_pkg::keymgr_dpe_creator_root_key | uni | req | 1 | Key output to the key manager holding CREATOR_ROOT_KEY_SHARE0 and CREATOR_ROOT_KEY_SHARE1. | +| keymgr_creator_seed | keymgr_dpe_pkg::keymgr_dpe_creator_seed | uni | req | 1 | Creator seed output to the key manager | +| keymgr_owner_seed | keymgr_dpe_pkg::keymgr_dpe_owner_seed | uni | req | 1 | Owner seed output to the key manager | +| nvm_otp_key | otp_ctrl_pkg::nvm_otp_key | req_rsp | rsp | 1 | Key derivation interface for NVM scrambling. | +| sram_otp_key | otp_ctrl_pkg::sram_otp_key | req_rsp | rsp | 4 | Array with key derivation interfaces for SRAM scrambling devices. | +| otbn_otp_key | otp_ctrl_pkg::otbn_otp_key | req_rsp | rsp | 1 | Key derivation interface for OTBN scrambling devices. | +| otp_broadcast | otp_ctrl_part_pkg::otp_broadcast | uni | req | 1 | Output of the HW partitions with breakout data types. | +| otp_macro | otp_ctrl_macro_pkg::otp_ctrl_macro | req_rsp | req | 1 | Data interface for the OTP macro. | +| core_tl | tlul_pkg::tl | req_rsp | rsp | 1 | | ## Interrupts @@ -209,10 +211,10 @@ In all other life cycle states this signal will be clamped to zero. ### Interface to Key Manager -The interface to the key manager is a simple struct that outputs the CREATOR_ROOT_KEY_SHARE0 and CREATOR_ROOT_KEY_SHARE1 keys via `otp_keymgr_key_o` if these secrets have been provisioned and locked (via CREATOR_KEY_LOCK). -Otherwise, this signal is tied to a random netlist constant. +Each individual secret key material (CREATOR_ROOT_KEY, OWNER_SEED, CREATOR_SEED) gets its own connection to the keymgr. +These secrets are provided to the keymgr if they have been provisioned and locked (via CREATOR_KEY_LOCK), otherwise these signals are tied to random netlist constants. -Since the key manager may run in a different clock domain, key manager is responsible for synchronizing the `otp_keymgr_key_o` signals. +Since the key manager may run in a different clock domain, key manager is responsible for synchronizing the `keymgr_creator_root_key_o`, `keymgr_creator_seed_o`, `keymgr_owner_seed_o` signals. ### Interfaces to SRAM and OTBN Scramblers diff --git a/hw/top_earlgrey/ip_autogen/otp_ctrl/dv/README.md b/hw/top_earlgrey/ip_autogen/otp_ctrl/dv/README.md index 93f49b9b1e66e..aa65f96a9d791 100644 --- a/hw/top_earlgrey/ip_autogen/otp_ctrl/dv/README.md +++ b/hw/top_earlgrey/ip_autogen/otp_ctrl/dv/README.md @@ -132,7 +132,7 @@ To avoid mismatches, scoreboard utilizes flags `dai_wr_ip` and `dai_digest_ip` t #### Assertions * TLUL assertions: The `tb/otp_ctrl_bind.sv` binds the `tlul_assert` [assertions](../../../../ip/tlul/doc/TlulProtocolChecker.md) to the IP to ensure TileLink interface protocol compliance. * Unknown checks on DUT outputs: The RTL has assertions to ensure all outputs are initialized to known values after coming out of reset. -* OTP_CTRL_IF assertions: This interface has assertions to ensure certain OTP_CTRL's outputs (such as: otp_broadcast_o, keymgr_key_o) are stable after OTP initialization. +* OTP_CTRL_IF assertions: This interface has assertions to ensure certain OTP_CTRL's outputs (such as: otp_broadcast_o, keymgr_creator_root_key_o, keymgr_creator_seed_o, keymgr_owner_seed_o) are stable after OTP initialization. ## Building and running tests The [dvsim](https://github.com/lowRISC/dvsim) tool is used for building and running our tests and regressions. diff --git a/hw/top_earlgrey/ip_autogen/otp_ctrl/dv/env/otp_ctrl_if.sv b/hw/top_earlgrey/ip_autogen/otp_ctrl/dv/env/otp_ctrl_if.sv index 5083c7ab0d3e6..d9cfe53484564 100644 --- a/hw/top_earlgrey/ip_autogen/otp_ctrl/dv/env/otp_ctrl_if.sv +++ b/hw/top_earlgrey/ip_autogen/otp_ctrl/dv/env/otp_ctrl_if.sv @@ -44,7 +44,9 @@ interface otp_ctrl_if(input clk_i, input rst_ni); // Output from DUT otp_broadcast_t otp_broadcast_o; - otp_keymgr_key_t keymgr_key_o; + keymgr_dpe_pkg::keymgr_dpe_creator_root_key_t keymgr_creator_root_key_o; + keymgr_dpe_pkg::keymgr_dpe_creator_seed_t keymgr_creator_seed_o; + keymgr_dpe_pkg::keymgr_dpe_owner_seed_t keymgr_owner_seed_o; otp_lc_data_t lc_data_o; logic pwr_otp_done_o, pwr_otp_idle_o; @@ -316,13 +318,13 @@ interface otp_ctrl_if(input clk_i, input rst_ni); // Otp_keymgr valid is related to part_digest, should not be changed after otp_pwr_init `OTP_ASSERT_WO_LC_ESC(OtpKeymgrValidStable0_A, pwr_otp_done_o |-> - $stable(keymgr_key_o.creator_root_key_share0_valid)) + $stable(keymgr_creator_root_key_o.share0_valid)) `OTP_ASSERT_WO_LC_ESC(OtpKeymgrValidStable1_A, pwr_otp_done_o |-> - $stable(keymgr_key_o.creator_root_key_share1_valid)) + $stable(keymgr_creator_root_key_o.share1_valid)) `OTP_ASSERT_WO_LC_ESC(OtpKeymgrValidStable2_A, pwr_otp_done_o |-> - $stable(keymgr_key_o.creator_seed_valid)) + $stable(keymgr_creator_seed_o.seed_valid)) `OTP_ASSERT_WO_LC_ESC(OtpKeymgrValidStable3_A, pwr_otp_done_o |-> - $stable(keymgr_key_o.owner_seed_valid)) + $stable(keymgr_owner_seed_o.seed_valid)) // During lc_prog_req, either otp_idle will be reset or lc_error is set `OTP_ASSERT_WO_LC_ESC(LcProgReq_A, $rose(lc_prog_req) |=> @@ -345,9 +347,9 @@ interface otp_ctrl_if(input clk_i, input rst_ni); `OTP_FATAL_ERR_ASSERT(LcDataRmaToken_A, lc_data_o.rma_token == RndCnstOtpCtrlPartInvDefault[RmaTokenOffset*8+:RmaTokenSize*8]) - `OTP_FATAL_ERR_ASSERT(KeymgrKeyData_A, keymgr_key_o.creator_root_key_share0 == + `OTP_FATAL_ERR_ASSERT(KeymgrKeyData_A, keymgr_creator_root_key_o.share0 == RndCnstOtpCtrlPartInvDefault[CreatorRootKeyShare0Offset*8+:CreatorRootKeyShare0Size*8] && - keymgr_key_o.creator_root_key_share1 == + keymgr_creator_root_key_o.share1 == RndCnstOtpCtrlPartInvDefault[CreatorRootKeyShare1Offset*8+:CreatorRootKeyShare1Size*8]) `OTP_FATAL_ERR_ASSERT(HwCfgOValid_A, otp_broadcast_o.valid == lc_ctrl_pkg::Off) diff --git a/hw/top_earlgrey/ip_autogen/otp_ctrl/dv/env/otp_ctrl_scoreboard.sv b/hw/top_earlgrey/ip_autogen/otp_ctrl/dv/env/otp_ctrl_scoreboard.sv index 9e15986be0c15..99a40c5c10f25 100644 --- a/hw/top_earlgrey/ip_autogen/otp_ctrl/dv/env/otp_ctrl_scoreboard.sv +++ b/hw/top_earlgrey/ip_autogen/otp_ctrl/dv/env/otp_ctrl_scoreboard.sv @@ -142,9 +142,10 @@ class otp_ctrl_scoreboard #(type CFG_T = otp_ctrl_env_cfg) if (!cfg.under_reset && !cfg.otp_ctrl_vif.alert_reqs && cfg.en_scb) begin otp_ctrl_part_pkg::otp_hw_cfg0_data_t exp_hw_cfg0_data; otp_ctrl_part_pkg::otp_hw_cfg1_data_t exp_hw_cfg1_data; - otp_ctrl_pkg::otp_keymgr_key_t exp_keymgr_data; otp_ctrl_pkg::otp_lc_data_t exp_lc_data; - bit [otp_ctrl_pkg::KeyMgrKeyWidth-1:0] exp_keymgr_key0, exp_keymgr_key1; + keymgr_dpe_pkg::keymgr_dpe_creator_root_key_t exp_creator_root_key; + keymgr_dpe_pkg::keymgr_dpe_creator_seed_t exp_creator_seed; + keymgr_dpe_pkg::keymgr_dpe_owner_seed_t exp_owner_seed; if (PartInfo[dai_digest_ip].sw_digest || PartInfo[dai_digest_ip].hw_digest) begin bit [TL_DW-1:0] otp_addr = PART_OTP_DIGEST_ADDRS[dai_digest_ip]; @@ -220,36 +221,41 @@ class otp_ctrl_scoreboard #(type CFG_T = otp_ctrl_env_cfg) // ---------------------- Check keymgr_key_o output --------------------------------- // Otp_keymgr outputs creator and owner keys from secret partitions. // Depends on lc_seed_hw_rd_en_i, it will output the real keys or a constant - exp_keymgr_data = '0; - exp_keymgr_data.creator_root_key_share0_valid = get_otp_digest_val(Secret2Idx) != 0; + exp_creator_root_key = '0; + exp_creator_seed = '0; + exp_owner_seed = '0; + + // Fetch and verify the CREATOR_ROOT_KEY_SHARE0 secret + exp_creator_root_key.share0_valid = get_otp_digest_val(Secret2Idx) != 0; if (cfg.otp_ctrl_vif.lc_seed_hw_rd_en_i == lc_ctrl_pkg::On) begin - exp_keymgr_data.creator_root_key_share0 = + exp_creator_root_key.share0 = {<<32 {otp_a[CreatorRootKeyShare0Offset/4 +: CreatorRootKeyShare0Size/4]}}; end else begin - exp_keymgr_data.creator_root_key_share0 = + exp_creator_root_key.share0 = top_earlgrey_rnd_cnst_pkg::RndCnstOtpCtrlPartInvDefault[CreatorRootKeyShare0Offset*8 +: CreatorRootKeyShare0Size*8]; end - // Check otp_keymgr_key_t struct by item is easier to debug. - `DV_CHECK_EQ(cfg.otp_ctrl_vif.keymgr_key_o.creator_root_key_share0_valid, - exp_keymgr_data.creator_root_key_share0_valid) - exp_keymgr_data.creator_root_key_share1_valid = get_otp_digest_val(Secret2Idx) != 0; + `DV_CHECK_EQ(cfg.otp_ctrl_vif.keymgr_creator_root_key_o.share0_valid, + exp_creator_root_key.share0_valid) + `DV_CHECK_EQ(cfg.otp_ctrl_vif.keymgr_creator_root_key_o.share0, + exp_creator_root_key.share0) + + // Fetch and verify the CREATOR_ROOT_KEY_SHARE1 secret + exp_creator_root_key.share1_valid = get_otp_digest_val(Secret2Idx) != 0; if (cfg.otp_ctrl_vif.lc_seed_hw_rd_en_i == lc_ctrl_pkg::On) begin - exp_keymgr_data.creator_root_key_share1 = + exp_creator_root_key.share1 = {<<32 {otp_a[CreatorRootKeyShare1Offset/4 +: CreatorRootKeyShare1Size/4]}}; end else begin - exp_keymgr_data.creator_root_key_share1 = + exp_creator_root_key.share1 = top_earlgrey_rnd_cnst_pkg::RndCnstOtpCtrlPartInvDefault[CreatorRootKeyShare1Offset*8 +: CreatorRootKeyShare1Size*8]; end - // Check otp_keymgr_key_t struct by item is easier to debug. - `DV_CHECK_EQ(cfg.otp_ctrl_vif.keymgr_key_o.creator_root_key_share1_valid, - exp_keymgr_data.creator_root_key_share1_valid) - - // Check otp_keymgr_key_t struct all together in case there is any missed item. - `DV_CHECK_EQ(cfg.otp_ctrl_vif.keymgr_key_o, exp_keymgr_data) + `DV_CHECK_EQ(cfg.otp_ctrl_vif.keymgr_creator_root_key_o.share1_valid, + exp_creator_root_key.share1_valid) + `DV_CHECK_EQ(cfg.otp_ctrl_vif.keymgr_creator_root_key_o.share1, + exp_creator_root_key.share1) if (cfg.en_cov) begin cov.keymgr_o_cg.sample(cfg.otp_ctrl_vif.lc_seed_hw_rd_en_i == lc_ctrl_pkg::On, - exp_keymgr_data.creator_root_key_share0_valid); + exp_creator_root_key.share0_valid); end end end else if (cfg.otp_ctrl_vif.alert_reqs) begin diff --git a/hw/top_earlgrey/ip_autogen/otp_ctrl/dv/tb.sv b/hw/top_earlgrey/ip_autogen/otp_ctrl/dv/tb.sv index 34f89cc5e1613..2ea810e0aae20 100644 --- a/hw/top_earlgrey/ip_autogen/otp_ctrl/dv/tb.sv +++ b/hw/top_earlgrey/ip_autogen/otp_ctrl/dv/tb.sv @@ -137,7 +137,9 @@ module tb; .lc_check_byp_en_i (otp_ctrl_if.lc_check_byp_en_i), .otp_lc_data_o (otp_ctrl_if.lc_data_o), // keymgr - .otp_keymgr_key_o (otp_ctrl_if.keymgr_key_o), + .keymgr_creator_root_key_o (otp_ctrl_if.keymgr_creator_root_key_o), + .keymgr_creator_seed_o (otp_ctrl_if.keymgr_creator_seed_o), + .keymgr_owner_seed_o (otp_ctrl_if.keymgr_owner_seed_o), // nvm .nvm_otp_key_i (nvm_req), .nvm_otp_key_o (nvm_rsp), diff --git a/hw/top_earlgrey/ip_autogen/otp_ctrl/rtl/otp_ctrl.sv b/hw/top_earlgrey/ip_autogen/otp_ctrl/rtl/otp_ctrl.sv index 312b211e9a1f8..1f25e81fef67b 100644 --- a/hw/top_earlgrey/ip_autogen/otp_ctrl/rtl/otp_ctrl.sv +++ b/hw/top_earlgrey/ip_autogen/otp_ctrl/rtl/otp_ctrl.sv @@ -75,8 +75,10 @@ module otp_ctrl input lc_ctrl_pkg::lc_tx_t lc_rma_state_i, // OTP broadcast outputs // SEC_CM: TOKEN_VALID.CTRL.MUBI - output otp_lc_data_t otp_lc_data_o, - output otp_keymgr_key_t otp_keymgr_key_o, + output otp_lc_data_t otp_lc_data_o, + output keymgr_dpe_pkg::keymgr_dpe_creator_root_key_t keymgr_creator_root_key_o, + output keymgr_dpe_pkg::keymgr_dpe_creator_seed_t keymgr_creator_seed_o, + output keymgr_dpe_pkg::keymgr_dpe_owner_seed_t keymgr_owner_seed_o, // Scrambling key requests input nvm_otp_key_req_t nvm_otp_key_i, output nvm_otp_key_rsp_t nvm_otp_key_o, @@ -1451,19 +1453,26 @@ end owner_seed_valid_q}) ); - always_comb begin : p_otp_keymgr_key_valid - // Valid reg inputs - creator_root_key_share0_valid_d = otp_keymgr_key.creator_root_key_share0_valid; - creator_root_key_share1_valid_d = otp_keymgr_key.creator_root_key_share1_valid; - creator_seed_valid_d = otp_keymgr_key.creator_seed_valid; - owner_seed_valid_d = otp_keymgr_key.owner_seed_valid; - // Output to keymgr - otp_keymgr_key_o = otp_keymgr_key; - otp_keymgr_key_o.creator_root_key_share0_valid = creator_root_key_share0_valid_q; - otp_keymgr_key_o.creator_root_key_share1_valid = creator_root_key_share1_valid_q; - otp_keymgr_key_o.creator_seed_valid = creator_seed_valid_q; - otp_keymgr_key_o.owner_seed_valid = owner_seed_valid_q; - end + assign creator_root_key_share0_valid_d = otp_keymgr_key.creator_root_key_share0_valid; + assign creator_root_key_share1_valid_d = otp_keymgr_key.creator_root_key_share1_valid; + assign keymgr_creator_root_key_o = '{ + share0: otp_keymgr_key.creator_root_key_share0, + share0_valid: creator_root_key_share0_valid_q, + share1: otp_keymgr_key.creator_root_key_share1, + share1_valid: creator_root_key_share1_valid_q + }; + + assign creator_seed_valid_d = otp_keymgr_key.creator_seed_valid; + assign keymgr_creator_seed_o = '{ + seed: otp_keymgr_key.creator_seed, + seed_valid: creator_seed_valid_q + }; + + assign owner_seed_valid_d = otp_keymgr_key.owner_seed_valid; + assign keymgr_owner_seed_o = '{ + seed: otp_keymgr_key.owner_seed, + seed_valid: owner_seed_valid_q + }; // Check that the lc_seed_hw_rd_en remains stable, once the key material is valid. `ASSERT(LcSeedHwRdEnStable0_A, @@ -1558,8 +1567,10 @@ end // Assertions // //////////////// - `ASSERT_INIT(CreatorRootKeyShare0Size_A, KeyMgrKeyWidth == CreatorRootKeyShare0Size * 8) - `ASSERT_INIT(CreatorRootKeyShare1Size_A, KeyMgrKeyWidth == CreatorRootKeyShare1Size * 8) + `ASSERT_INIT(CreatorRootKeyShare0Size_A, + keymgr_dpe_pkg::KeyMgrKeyWidth == CreatorRootKeyShare0Size * 8) + `ASSERT_INIT(CreatorRootKeyShare1Size_A, + keymgr_dpe_pkg::KeyMgrKeyWidth == CreatorRootKeyShare1Size * 8) `ASSERT_INIT(NvmDataKeySeedSize_A, NvmKeySeedWidth == NvmDataKeySeedSize * 8) `ASSERT_INIT(NvmAddrKeySeedSize_A, NvmKeySeedWidth == NvmAddrKeySeedSize * 8) `ASSERT_INIT(SramDataKeySeedSize_A, SramKeySeedWidth == SramDataKeySeedSize * 8) @@ -1577,7 +1588,9 @@ end `ASSERT_KNOWN(PwrOtpInitRspKnown_A, pwr_otp_o) `ASSERT_KNOWN(LcOtpProgramRspKnown_A, lc_otp_program_o) `ASSERT_KNOWN(OtpLcDataKnown_A, otp_lc_data_o) - `ASSERT_KNOWN(OtpKeymgrKeyKnown_A, otp_keymgr_key_o) + `ASSERT_KNOWN(KeymgrCreatorRootKey_A, keymgr_creator_root_key_o) + `ASSERT_KNOWN(KeymgrCreatorSeed_A, keymgr_creator_seed_o) + `ASSERT_KNOWN(KeymgrOwnerSeed_A, keymgr_owner_seed_o) `ASSERT_KNOWN(NvmOtpKeyRspKnown_A, nvm_otp_key_o) `ASSERT_KNOWN(OtpSramKeyKnown_A, sram_otp_key_o) `ASSERT_KNOWN(OtpOtgnKeyKnown_A, otbn_otp_key_o) diff --git a/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv b/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv index 077e0bb519256..14d0fc93454c3 100644 --- a/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv +++ b/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv @@ -627,7 +627,6 @@ module top_earlgrey #( edn_pkg::edn_rsp_t [Edn1NumEndPoints-1:0] edn1_edn_rsp; otp_ctrl_pkg::otbn_otp_key_req_t otp_ctrl_otbn_otp_key_req; otp_ctrl_pkg::otbn_otp_key_rsp_t otp_ctrl_otbn_otp_key_rsp; - otp_ctrl_pkg::otp_keymgr_key_t otp_ctrl_otp_keymgr_key; keymgr_pkg::hw_key_req_t keymgr_aes_key; keymgr_pkg::hw_key_req_t keymgr_kmac_key; keymgr_pkg::otbn_key_req_t keymgr_otbn_key; @@ -764,6 +763,10 @@ module top_earlgrey #( otp_ctrl_pkg::otp_device_id_t keymgr_otp_device_id; prim_mubi_pkg::mubi8_t sram_ctrl_main_otp_en_sram_ifetch; prim_mubi_pkg::mubi8_t rv_dm_otp_dis_rv_dm_late_debug; + otp_ctrl_pkg::otp_keymgr_key_t keymgr_otp_key; + keymgr_dpe_pkg::keymgr_dpe_creator_root_key_t otp_ctrl_keymgr_creator_root_key; + keymgr_dpe_pkg::keymgr_dpe_creator_seed_t otp_ctrl_keymgr_creator_seed; + keymgr_dpe_pkg::keymgr_dpe_owner_seed_t otp_ctrl_keymgr_owner_seed; // Create mixed connections to ports assign alert_handler_esc_rx[3] = alert_handler_esc_rx_i; @@ -815,6 +818,15 @@ module top_earlgrey #( otp_ctrl_otp_broadcast.hw_cfg1_data.unallocated }; + // Connect the keymaterial from the OTP manually + // TODO: resolve this manual fix + assign keymgr_otp_key = { + otp_ctrl_keymgr_creator_root_key, + otp_ctrl_keymgr_creator_seed, + otp_ctrl_keymgr_owner_seed + }; + + // Ibex-specific assignments // TODO: This should be further automated in the future. assign rv_core_ibex_irq_timer = intr_rv_timer_timer_expired_hart0_timer0; @@ -1480,7 +1492,9 @@ module top_earlgrey #( .lc_seed_hw_rd_en_i(lc_ctrl_lc_seed_hw_rd_en), .lc_rma_state_i(lc_ctrl_lc_rma_state), .lc_check_byp_en_i(lc_ctrl_lc_check_byp_en), - .otp_keymgr_key_o(otp_ctrl_otp_keymgr_key), + .keymgr_creator_root_key_o(otp_ctrl_keymgr_creator_root_key), + .keymgr_creator_seed_o(otp_ctrl_keymgr_creator_seed), + .keymgr_owner_seed_o(otp_ctrl_keymgr_owner_seed), .nvm_otp_key_i(flash_ctrl_otp_req), .nvm_otp_key_o(flash_ctrl_otp_rsp), .sram_otp_key_i(otp_ctrl_sram_otp_key_req), @@ -2231,7 +2245,7 @@ module top_earlgrey #( .otbn_key_o(keymgr_otbn_key), .kmac_data_o(kmac_app_req[0]), .kmac_data_i(kmac_app_rsp[0]), - .otp_key_i(otp_ctrl_otp_keymgr_key), + .otp_key_i(keymgr_otp_key), .otp_device_id_i(keymgr_otp_device_id), .flash_i(flash_ctrl_keymgr), .lc_keymgr_en_i(lc_ctrl_lc_keymgr_en), diff --git a/hw/top_earlgrey/templates/toplevel.sv.tpl b/hw/top_earlgrey/templates/toplevel.sv.tpl index e492c1e20ac2b..ec0e278a66e71 100644 --- a/hw/top_earlgrey/templates/toplevel.sv.tpl +++ b/hw/top_earlgrey/templates/toplevel.sv.tpl @@ -59,6 +59,15 @@ module top_${top["name"]} #( otp_ctrl_otp_broadcast.hw_cfg1_data.hw_cfg1_digest, otp_ctrl_otp_broadcast.hw_cfg1_data.unallocated }; + + // Connect the keymaterial from the OTP manually + // TODO: resolve this manual fix + assign keymgr_otp_key = { + otp_ctrl_keymgr_creator_root_key, + otp_ctrl_keymgr_creator_seed, + otp_ctrl_keymgr_owner_seed + }; + % endif % endfor diff --git a/sw/device/lib/dif/dif_keymgr_dpe.c b/sw/device/lib/dif/dif_keymgr_dpe.c index fd69fbf485b1f..ea9455298c715 100644 --- a/sw/device/lib/dif/dif_keymgr_dpe.c +++ b/sw/device/lib/dif/dif_keymgr_dpe.c @@ -166,6 +166,10 @@ dif_result_t dif_keymgr_dpe_initialize(const dif_keymgr_dpe_t *keymgr_dpe, return kDifLocked; } + // TODO(#30667): Verify if the max key version needs to be written here too! + // When loading the UDS the RTL fetches the max key version from + // the SW register. Verify that the lock is released when the + // version register is locked. uint32_t reg_control = bitfield_field32_write( KEYMGR_DPE_CONTROL_SHADOWED_REG_RESVAL, KEYMGR_DPE_CONTROL_SHADOWED_SLOT_DST_SEL_FIELD, slot_dst_sel); @@ -279,6 +283,28 @@ dif_result_t dif_keymgr_dpe_erase_slot( return kDifOk; } +dif_result_t dif_keymgr_dpe_disable(const dif_keymgr_dpe_t *keymgr_dpe) { + if (keymgr_dpe == NULL) { + return kDifBadArg; + } + + if (!is_ready(keymgr_dpe)) { + return kDifLocked; + } + + uint32_t reg_control = bitfield_field32_write( + KEYMGR_DPE_CONTROL_SHADOWED_REG_RESVAL, + KEYMGR_DPE_CONTROL_SHADOWED_OPERATION_FIELD, + KEYMGR_DPE_CONTROL_SHADOWED_OPERATION_VALUE_DISABLE); + mmio_region_write32_shadowed(keymgr_dpe->base_addr, + KEYMGR_DPE_CONTROL_SHADOWED_REG_OFFSET, + reg_control); + mmio_region_write32(keymgr_dpe->base_addr, KEYMGR_DPE_START_REG_OFFSET, + 1 << KEYMGR_DPE_START_EN_BIT); + + return kDifOk; +} + dif_result_t dif_keymgr_dpe_generate( const dif_keymgr_dpe_t *keymgr_dpe, const dif_keymgr_dpe_generate_params_t *params) { @@ -405,3 +431,40 @@ dif_result_t dif_keymgr_dpe_get_state(const dif_keymgr_dpe_t *keymgr_dpe, bitfield_field32_read(reg_state, KEYMGR_DPE_WORKING_STATE_STATE_FIELD); return kDifOk; } + +dif_result_t dif_keymgr_dpe_clear_sideload_key( + const dif_keymgr_dpe_t *keymgr_dpe, + dif_keymgr_dpe_sideload_clr_t clear_dest) { + if (keymgr_dpe == NULL) { + return kDifBadArg; + } + + mmio_region_write32(keymgr_dpe->base_addr, + KEYMGR_DPE_SIDELOAD_CLEAR_REG_OFFSET, clear_dest); + + return kDifOk; +} + +dif_result_t dif_keymgr_dpe_configure(const dif_keymgr_dpe_t *keymgr_dpe, + dif_keymgr_dpe_config_t config) { + if (keymgr_dpe == NULL) { + return kDifBadArg; + } + + // Verify if the register is unlocked + uint32_t reseed_regwen = mmio_region_read32( + keymgr_dpe->base_addr, KEYMGR_DPE_RESEED_INTERVAL_REGWEN_REG_OFFSET); + if (!bitfield_bit32_read(reseed_regwen, + KEYMGR_DPE_RESEED_INTERVAL_REGWEN_EN_BIT)) { + return kDifLocked; + } + + uint32_t reg_val = + bitfield_field32_write(0, KEYMGR_DPE_RESEED_INTERVAL_SHADOWED_VAL_FIELD, + config.entropy_reseed_interval); + mmio_region_write32_shadowed(keymgr_dpe->base_addr, + KEYMGR_DPE_RESEED_INTERVAL_SHADOWED_REG_OFFSET, + reg_val); + + return kDifOk; +} diff --git a/sw/device/lib/dif/dif_keymgr_dpe.h b/sw/device/lib/dif/dif_keymgr_dpe.h index b2daeda70a67a..281e0b94b8bf9 100644 --- a/sw/device/lib/dif/dif_keymgr_dpe.h +++ b/sw/device/lib/dif/dif_keymgr_dpe.h @@ -42,6 +42,33 @@ typedef enum dif_keymgr_dpe_state { kDifKeymgrDpeStateInvalid = 3 } dif_keymgr_dpe_state_t; +/** + * Enumeration for side load slot clearing. + */ +typedef enum dif_keymgr_sideload_clr { + kDifKeymgrDpeSideLoadClearNone = 0, + kDifKeymgrDpeSideLoadClearAes = 1, + kDifKeymgrDpeSideLoadClearKmac = 2, + kDifKeymgrDpeSideLoadClearOtbn = 3, + // Using different value than those enumerated above should clear all slots, + // so we can use the mask value of this field to denote ALL case. + kDifKeymgrDpeSideLoadClearAll = 7, +} dif_keymgr_dpe_sideload_clr_t; + +/** + * Runtime configuration for keymgr dpe. + */ +typedef struct dif_keymgr_dpe_config { + /** + * Number of keymgr_dpe cycles before the entropy is reseeded. + * + * Keymgr dpe uses random values generated by the entropy source for + * initializing its state and clearing sideload keys. This value determines + * the frequency at which this random value is updated. + */ + uint16_t entropy_reseed_interval; +} dif_keymgr_dpe_config_t; + /** * Input parameters for advancing a DPE context/slot. */ @@ -252,6 +279,20 @@ dif_result_t dif_keymgr_dpe_erase_slot( const dif_keymgr_dpe_t *keymgr_dpe, const dif_keymgr_dpe_erase_params_t *params); +/** + * Disables key manager dpe. + * + * This function disables keymgr dpe until the next power cycle by making + * it transition to the "disabled" state. The "disabled" state is a terminal + * state where the keymgr dpe is no longer operational and its secret value + * are wiped. + * + * @param keymgr_dpe A key manager handle. + * @return The result of the operation. + */ +OT_WARN_UNUSED_RESULT +dif_result_t dif_keymgr_dpe_disable(const dif_keymgr_dpe_t *keymgr_dpe); + /** * Generate a SW/HW key from a chosen keymgr_dpe slot. * @@ -288,7 +329,7 @@ dif_result_t dif_keymgr_dpe_get_status_codes( */ OT_WARN_UNUSED_RESULT dif_result_t dif_keymgr_dpe_get_state(const dif_keymgr_dpe_t *keymgr_dpe, - uint32_t *state); + dif_keymgr_dpe_state_t *state); /** * Read the value of SW generated key from its related CSR. It is the @@ -302,6 +343,33 @@ OT_WARN_UNUSED_RESULT dif_result_t dif_keymgr_dpe_read_output(const dif_keymgr_dpe_t *keymgr_dpe, dif_keymgr_dpe_output_t *output); +/** + * Starts or stops clearing of sideload keys. + * + * Calling this function on a set of output register causes keymgr dpe to clear + * sideload keys continuously using randomness. Callers must disable the + * clearing of sideload keys to resume normal sideload operation. + * + * @param keymgr_dpe A key manager handle. + * @param clear_dest Target sideload key. + * @return The result of the operation. + */ +OT_WARN_UNUSED_RESULT +dif_result_t dif_keymgr_dpe_clear_sideload_key( + const dif_keymgr_dpe_t *keymgr_dpe, + dif_keymgr_dpe_sideload_clr_t clear_dest); + +/** + * Runtime configuration for keymgr dpe. + * + * @param keymgr_dpe A key manager handle. + * @param config configuration for the keymgr dpe. + * @return The result of the operation. + */ +OT_WARN_UNUSED_RESULT +dif_result_t dif_keymgr_dpe_configure(const dif_keymgr_dpe_t *keymgr_dpe, + dif_keymgr_dpe_config_t config); + #ifdef __cplusplus } // extern "C" #endif // __cplusplus diff --git a/sw/device/lib/dif/dif_keymgr_dpe_unittest.cc b/sw/device/lib/dif/dif_keymgr_dpe_unittest.cc new file mode 100644 index 0000000000000..07f31ca951a75 --- /dev/null +++ b/sw/device/lib/dif/dif_keymgr_dpe_unittest.cc @@ -0,0 +1,23 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#include "sw/device/lib/dif/dif_keymgr_dpe.h" + +#include + +#include "gtest/gtest.h" +#include "sw/device/lib/base/mmio.h" +#include "sw/device/lib/base/mock_mmio.h" +#include "sw/device/lib/dif/dif_base.h" +#include "sw/device/lib/dif/dif_test_base.h" + +#include "hw/top/keymgr_dpe_regs.h" // Generated + +namespace dif_keymgr_dpe_unittest { +namespace { + +// TODO(#30609): Write these unittests + +} // namespace +} // namespace dif_keymgr_dpe_unittest diff --git a/sw/device/silicon_creator/lib/drivers/keymgr_dpe.c b/sw/device/silicon_creator/lib/drivers/keymgr_dpe.c new file mode 100644 index 0000000000000..90d402bac728e --- /dev/null +++ b/sw/device/silicon_creator/lib/drivers/keymgr_dpe.c @@ -0,0 +1,653 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#include "sw/device/silicon_creator/lib/drivers/keymgr_dpe.h" + +#include + +#include "hw/top/dt/keymgr_dpe.h" +#include "sw/device/lib/base/abs_mmio.h" +#include "sw/device/lib/base/hardened_memory.h" +#include "sw/device/lib/base/macros.h" +#include "sw/device/lib/runtime/hart.h" +#include "sw/device/silicon_creator/lib/base/sec_mmio.h" + +#include "hw/top/keymgr_dpe_regs.h" // Generated. + +#define KEYMGR_DPE_ASSERT(a, b) static_assert(a == b, "Bad value for " #a) +KEYMGR_DPE_ASSERT(kScKeymgrDPEStateReset, + KEYMGR_DPE_WORKING_STATE_STATE_VALUE_RESET); +KEYMGR_DPE_ASSERT(kScKeymgrDPEStateAvailable, + KEYMGR_DPE_WORKING_STATE_STATE_VALUE_AVAILABLE); +KEYMGR_DPE_ASSERT(kScKeymgrDPEStateDisabled, + KEYMGR_DPE_WORKING_STATE_STATE_VALUE_DISABLED); +KEYMGR_DPE_ASSERT(kScKeymgrDPEStateInvalid, + KEYMGR_DPE_WORKING_STATE_STATE_VALUE_INVALID); + +/** + * Internal keymgr dpe operation definition + */ +typedef enum sc_keymgr_dpe_operation { + kScKeymgrDPEOpsAdvance = 0, + kScKeymgrDPEOpsEraseSlot = 1, + kScKeymgrDPEOpsGenSwKey = 2, + kScKeymgrDPEOpsGenHwKey = 3, + kScKeymgrDPEOpsDisable = 4, + kScKeymgrDPEOpsLoadRootKey = 5, +} sc_keymgr_dpe_operation_t; + +/** + * Base address of the keymgr dpe registers. + */ +static inline uint32_t sc_keymgr_dpe_base(void) { + return dt_keymgr_dpe_reg_block(kDtKeymgrDpe, kDtKeymgrDpeRegBlockCore); +} + +/** + * Sets the context of the control register for the next command. + * + * @param exl_sw_binding Should additional hw bindings be used during the + * next advance call. Only impact DPE context generation if either the + * creator root key / owner int key / owner key is being generated. + * @param ops The type of operation to execute next. + * @param sel_src_slot Source slot when either advancing a DPE context inside + * the slot or if an HW / SW key is being generated. + * @param sel_dst_slot Destination slot in which the next advance call will + * load the newly generated DPE context. + * @param key_dest Determine the sideload port when deriving a HW key. + */ +void sc_keymgr_dpe_control_reg_set( + const sc_keymgr_dpe_sw_binding_t exl_sw_binding, const uint32_t ops, + const sc_keymgr_dpe_dest_t key_dest, uint32_t sel_src_slot, + uint32_t sel_dst_slot) { + // Build the control register + uint32_t ctrl = 0; + ctrl = bitfield_field32_write( + ctrl, KEYMGR_DPE_CONTROL_SHADOWED_OPERATION_FIELD, ops); + ctrl = bitfield_field32_write( + ctrl, KEYMGR_DPE_CONTROL_SHADOWED_DEST_SEL_FIELD, key_dest); + ctrl = bitfield_field32_write( + ctrl, KEYMGR_DPE_CONTROL_SHADOWED_SLOT_SRC_SEL_FIELD, sel_src_slot); + ctrl = bitfield_field32_write( + ctrl, KEYMGR_DPE_CONTROL_SHADOWED_SLOT_DST_SEL_FIELD, sel_dst_slot); + ctrl = bitfield_bit32_write( + ctrl, KEYMGR_DPE_CONTROL_SHADOWED_SW_BINDING_ONLY_BIT, exl_sw_binding); + + // Write the control register. + abs_mmio_write32_shadowed( + sc_keymgr_dpe_base() + KEYMGR_DPE_CONTROL_SHADOWED_REG_OFFSET, ctrl); +} + +/** + * Start the command. + */ +void sc_keymgr_dpe_start_operation(void) { + // Issue the start command. + abs_mmio_write32(sc_keymgr_dpe_base() + KEYMGR_DPE_START_REG_OFFSET, + 1 << KEYMGR_DPE_START_EN_BIT); +} + +/** + * Wait for the key manager dpe to finish an operation. + * + * Polls the key manager dpe until it is no longer busy. If the operation + * completed successfully, returns kErrorOk. If there was an error during the + * operation, reads and clears the error code and returns kErrorKeymgrInternal. + * + * @return `kErrorOk` if the status is either "idle" or the operation + * finished with "done_successfully", `kErrorKeymgrInternal`otherwise. + */ +OT_WARN_UNUSED_RESULT +rom_error_t sc_keymgr_dpe_wait_until_done(void) { + // Poll the OP_STATUS register until it is something other than "WIP". + uint32_t reg; + uint32_t status; + do { + // Read OP_STATUS and then clear by writing back the value we read. + reg = + abs_mmio_read32(sc_keymgr_dpe_base() + KEYMGR_DPE_OP_STATUS_REG_OFFSET); + abs_mmio_write32(sc_keymgr_dpe_base() + KEYMGR_DPE_OP_STATUS_REG_OFFSET, + reg); + status = bitfield_field32_read(reg, KEYMGR_DPE_OP_STATUS_STATUS_FIELD); + } while (status == KEYMGR_DPE_OP_STATUS_STATUS_VALUE_WIP); + + // Check if the keymgr_dpe reported errors. If it completed an operation + // successfully, return an OK status. A `WIP` status should not be possible + // because of the check above. + // The `IDLE` status is left unhandled because the keymgr_dpe should never + // be idle after an operation has been started by the caller. + switch (launder32(status)) { + case KEYMGR_DPE_OP_STATUS_STATUS_VALUE_DONE_SUCCESS: + HARDENED_CHECK_EQ(status, KEYMGR_DPE_OP_STATUS_STATUS_VALUE_DONE_SUCCESS); + return kErrorOk; + case KEYMGR_DPE_OP_STATUS_STATUS_VALUE_DONE_ERROR: { + // Clear the ERR_CODE register before returning. + uint32_t err_code = abs_mmio_read32(sc_keymgr_dpe_base() + + KEYMGR_DPE_ERR_CODE_REG_OFFSET); + abs_mmio_write32(sc_keymgr_dpe_base() + KEYMGR_DPE_ERR_CODE_REG_OFFSET, + err_code); + return kErrorKeymgrInternal; + } + default: + // Should be unreachable. + HARDENED_TRAP(); + return kErrorKeymgrInternal; + } +} + +/** + * Checks the key manager dpe `expected_state`. + * + * This function reads and clears the status and error code registers. + * + * @return `kErrorOk` if the key manager dpe is at the `expected_state` and the + * status is idle or success. + */ +OT_WARN_UNUSED_RESULT +static rom_error_t expected_state_check(uint32_t expected_state) { + // Read and clear the status register by writing back the read value, + // polling until the status is non-WIP. + uint32_t op_status; + uint32_t op_status_field; + do { + op_status = + abs_mmio_read32(sc_keymgr_dpe_base() + KEYMGR_DPE_OP_STATUS_REG_OFFSET); + abs_mmio_write32(sc_keymgr_dpe_base() + KEYMGR_DPE_OP_STATUS_REG_OFFSET, + op_status); + op_status_field = + bitfield_field32_read(op_status, KEYMGR_DPE_OP_STATUS_STATUS_FIELD); + } while (op_status_field == KEYMGR_DPE_OP_STATUS_STATUS_VALUE_WIP || + op_status_field == KEYMGR_DPE_OP_STATUS_STATUS_VALUE_DONE_SUCCESS); + + // Read and clear the error register by writing back the read value. + uint32_t error_code = + abs_mmio_read32(sc_keymgr_dpe_base() + KEYMGR_DPE_ERR_CODE_REG_OFFSET); + abs_mmio_write32(sc_keymgr_dpe_base() + KEYMGR_DPE_ERR_CODE_REG_OFFSET, + error_code); + + // Read the working state with sec_mmio so that we can check the expected + // value periodically. + // TODO(#30665): changed this towards abs_mmio_read32(...) instead of + // sec_mmio_read32(...) as otherwise several test fail. + // The test currently investigating is: 0.rom_e2e_asm_init_test_unlocked0! + uint32_t got_state = abs_mmio_read32(sc_keymgr_dpe_base() + + KEYMGR_DPE_WORKING_STATE_REG_OFFSET); + if (op_status_field == KEYMGR_DPE_OP_STATUS_STATUS_VALUE_IDLE && + error_code == 0u && got_state == expected_state) { + return kErrorOk; + } + return kErrorKeymgrInternal; +} + +/** + * Fails if the keymgr_dpe is not idle. + * + * @return OK if the key manager is idle, kErrorKeymgrInternal otherwise. + */ +OT_WARN_UNUSED_RESULT +static rom_error_t keymgr_dpe_is_idle(void) { + uint32_t reg = + abs_mmio_read32(sc_keymgr_dpe_base() + KEYMGR_DPE_OP_STATUS_REG_OFFSET); + uint32_t status = + bitfield_field32_read(reg, KEYMGR_DPE_OP_STATUS_STATUS_FIELD); + if (launder32(status) == KEYMGR_DPE_OP_STATUS_STATUS_VALUE_IDLE) { + HARDENED_CHECK_EQ(status, KEYMGR_DPE_OP_STATUS_STATUS_VALUE_IDLE); + return kErrorOk; + } + return kErrorKeymgrInternal; +} + +/** + * Wrapper for any advance call. + * + * All advance call will use this wrapper except for the first one as there + * the initial state is different. + * + * @param exl_sw_binding should the software binding be used exclusively + * @param adv_data all required data for a successful advance call + * + * @return `kErrorOk` if the advance call was successfully started + */ +OT_WARN_UNUSED_RESULT +static rom_error_t sc_keymgr_dpe_advance_wrapper( + const sc_keymgr_dpe_sw_binding_t exl_sw_binding, + sc_keymgr_dpe_advance_data_t adv_data) { + // Set the current key version + sc_keymgr_dpe_max_ver_set(adv_data.version); + + // Set the sw binding + sc_keymgr_dpe_sw_binding_set(adv_data.binding_value); + + // Set the policy for the DPE context + sc_keymgr_dpe_policy_set(adv_data.policy); + + // Set the control register entries + sc_keymgr_dpe_control_reg_set( + exl_sw_binding, KEYMGR_DPE_CONTROL_SHADOWED_OPERATION_VALUE_ADVANCE, + KEYMGR_DPE_CONTROL_SHADOWED_DEST_SEL_VALUE_NONE, adv_data.sel_src_slot, + adv_data.sel_dst_slot); + + // Start the advance operation + sc_keymgr_dpe_start_operation(); + return kErrorOk; +} + +/** + * Sets the entropy reseed interval of the key manager dpe. + */ +void sc_keymgr_dpe_entropy_reseed_interval_set(uint16_t reseed_interval) { + SEC_MMIO_ASSERT_WRITE_INCREMENT(kScKeymgrDPESecMmioReseedIntervalSet, 1); + uint32_t reg = bitfield_field32_write( + 0, KEYMGR_DPE_RESEED_INTERVAL_SHADOWED_VAL_FIELD, reseed_interval); + sec_mmio_write32_shadowed( + sc_keymgr_dpe_base() + KEYMGR_DPE_RESEED_INTERVAL_SHADOWED_REG_OFFSET, + reg); +} + +/** + * Sets the key manager dpe software binding input. + */ +void sc_keymgr_dpe_sw_binding_set(keymgr_dpe_binding_value_t *binding_value) { + SEC_MMIO_ASSERT_WRITE_INCREMENT(kScKeymgrDPESecMmioSwBindingSet, 8); + // Write and lock (rw0c) the software binding value. This register is + // unlocked by hardware upon a successful state transition. + for (size_t i = 0; i < ARRAYSIZE(binding_value->data); ++i) { + sec_mmio_write32(sc_keymgr_dpe_base() + KEYMGR_DPE_SW_BINDING_0_REG_OFFSET + + i * sizeof(uint32_t), + binding_value->data[i]); + } + // Lock (clear) the sw binding register here + // TODO(#30665): sec_mmio_writes currently does not work properly with + // auto reset register. The reason is if we start an advance operation + // then the HW will set this register afterwards. However, the + // sec_mmio_check_value() function still expects the written value of 0! + abs_mmio_write32( + sc_keymgr_dpe_base() + KEYMGR_DPE_SW_BINDING_REGWEN_REG_OFFSET, 0); +} + +/** + * Blocks until the software binding registers are unlocked. + */ +void sc_keymgr_dpe_sw_binding_unlock_wait(void) { + // wait until the sw binding register is unlocked + while (!abs_mmio_read32(sc_keymgr_dpe_base() + + KEYMGR_DPE_SW_BINDING_REGWEN_REG_OFFSET)) { + } + // Ignore the return value since this read is performed to check and update + // the expected value. + OT_DISCARD(sec_mmio_read32(sc_keymgr_dpe_base() + + KEYMGR_DPE_SW_BINDING_REGWEN_REG_OFFSET)); +} + +/** + * Sets the current max key version used to advance a DPE context + */ +void sc_keymgr_dpe_max_ver_set(uint32_t max_key_ver) { + SEC_MMIO_ASSERT_WRITE_INCREMENT(kScKeymgrDPESecMmioMaxVerSet, 1); + // Write and lock (rw0c) the max key version. + sec_mmio_write32_shadowed( + sc_keymgr_dpe_base() + KEYMGR_DPE_MAX_KEY_VER_SHADOWED_REG_OFFSET, + max_key_ver); + // TODO(#30665): sec_mmio_writes currently does not work > The reason is if + // we start an advance operation then the HW will set this register + // afterwards. However the sec_mmio_check_value() function still expects the + // written 0 value! + abs_mmio_write32( + sc_keymgr_dpe_base() + KEYMGR_DPE_MAX_KEY_VER_REGWEN_REG_OFFSET, 0); +} + +/** + * Sets the key version used to generate a key + */ +void sc_keymgr_dpe_key_ver_set(uint32_t key_ver) { + abs_mmio_write32(sc_keymgr_dpe_base() + KEYMGR_DPE_KEY_VERSION_REG_OFFSET, + key_ver); +} + +/** + * Sets all the slot policies for the next advance call. + */ +void sc_keymgr_dpe_policy_set(sc_keymgr_dpe_policies_t policy) { + SEC_MMIO_ASSERT_WRITE_INCREMENT(kScKeymgrDPESecMmioSlotPolicy, 1); + // Build, write and lock (rw0c) the policy field. + uint32_t reg = 0; + reg = bitfield_bit32_write(reg, KEYMGR_DPE_SLOT_POLICY_ALLOW_CHILD_BIT, + policy.child); + reg = bitfield_bit32_write(reg, KEYMGR_DPE_SLOT_POLICY_EXPORTABLE_BIT, + policy.expo); + reg = bitfield_bit32_write(reg, KEYMGR_DPE_SLOT_POLICY_RETAIN_PARENT_BIT, + policy.parent); + sec_mmio_write32(sc_keymgr_dpe_base() + KEYMGR_DPE_SLOT_POLICY_REG_OFFSET, + reg); + // TODO(#30665): sec_mmio_writes currently does not work > The reason is if + // we start an advance operation then the HW will set this register + // afterwards. However the sec_mmio_check_value() function still expects the + // written 0 value! + abs_mmio_write32( + sc_keymgr_dpe_base() + KEYMGR_DPE_SLOT_POLICY_REGWEN_REG_OFFSET, 0); +} + +/** + * Checks the state of the key manager and compares against the provided + * value. + */ +OT_WARN_UNUSED_RESULT +rom_error_t sc_keymgr_dpe_state_check(sc_keymgr_dpe_state_t expected_state) { + return expected_state_check(expected_state); +} + +/** + * Generate a key manager dpe key and sideload to the requested block. + */ +OT_WARN_UNUSED_RESULT +rom_error_t sc_keymgr_dpe_generate_key( + const sc_keymgr_dpe_dest_t destination, + sc_keymgr_dpe_diversification_t diversification) { + // Wait until the keymgr dpe has finished any previous operation + // Keys can only be generated in the Available state + HARDENED_RETURN_IF_ERROR(expected_state_check(kScKeymgrDPEStateAvailable)); + + // Determine if destination enforce either the HW or SW key + const uint32_t ops = + (destination == kScKeymgrDPEDestNone) + ? KEYMGR_DPE_CONTROL_SHADOWED_OPERATION_VALUE_GENERATE_SW_OUTPUT + : KEYMGR_DPE_CONTROL_SHADOWED_OPERATION_VALUE_GENERATE_HW_OUTPUT; + + // Set the current key version + sc_keymgr_dpe_key_ver_set(diversification.version); + + // Set the salt value for the key generation + for (size_t i = 0; i < kScKeymgrDPESaltNumWords; i++) { + abs_mmio_write32(sc_keymgr_dpe_base() + KEYMGR_DPE_SALT_0_REG_OFFSET + + i * sizeof(uint32_t), + diversification.salt[i]); + } + + // Set the control register entries + sc_keymgr_dpe_control_reg_set(kScKeymgrDPEUseAdditionalHwBinding, ops, + destination, diversification.sel_src_slot, 0); + + // Start the advance operation + sc_keymgr_dpe_start_operation(); + + // Blocks until the key is generated + return sc_keymgr_dpe_wait_until_done(); +} + +/** + * Read the generated sw key. + */ +OT_WARN_UNUSED_RESULT +rom_error_t sc_keymgr_dpe_read_key(uint32_t *share0, uint32_t *share1) { + // Return a error if the keymgr_dpe is not idle + HARDENED_RETURN_IF_ERROR(keymgr_dpe_is_idle()); + // To avoid side-channel leakage, first randomize the destination buffers + // using memshred and then read the key. + hardened_memshred(share0, kScKeymgrDPEKeyNumWords); + hardened_memcpy(share0, + (uint32_t *)(sc_keymgr_dpe_base() + + KEYMGR_DPE_SW_SHARE0_OUTPUT_0_REG_OFFSET), + kScKeymgrDPEKeyNumWords); + hardened_memshred(share1, kScKeymgrDPEKeyNumWords); + hardened_memcpy(share1, + (uint32_t *)(sc_keymgr_dpe_base() + + KEYMGR_DPE_SW_SHARE1_OUTPUT_0_REG_OFFSET), + kScKeymgrDPEKeyNumWords); + return kErrorOk; +} + +/** + * Clear the requested sideloaded key slot. + */ +OT_WARN_UNUSED_RESULT +rom_error_t sc_keymgr_dpe_clear_key(sc_keymgr_dpe_dest_t destination) { + uint32_t reg = 0; + // Wait until the keymgr dpe has finished any previous operation + HARDENED_RETURN_IF_ERROR(keymgr_dpe_is_idle()); + + // Set SIDELOAD_CLEAR to begin continuously clearing the requested slot. + reg = bitfield_field32_write(reg, KEYMGR_DPE_SIDELOAD_CLEAR_VAL_FIELD, + destination); + abs_mmio_write32(sc_keymgr_dpe_base() + KEYMGR_DPE_SIDELOAD_CLEAR_REG_OFFSET, + reg); + + // Read back the value (hardening measure). + uint32_t sideload_clear = abs_mmio_read32( + sc_keymgr_dpe_base() + KEYMGR_DPE_SIDELOAD_CLEAR_REG_OFFSET); + if (bitfield_field32_read( + sideload_clear, KEYMGR_DPE_SIDELOAD_CLEAR_VAL_FIELD) != destination) { + return kErrorKeymgrInternal; + } + + // Stop continuous clearing. + reg = 0; + reg = bitfield_field32_write(reg, KEYMGR_DPE_SIDELOAD_CLEAR_VAL_FIELD, + KEYMGR_DPE_SIDELOAD_CLEAR_VAL_VALUE_NONE); + abs_mmio_write32(sc_keymgr_dpe_base() + KEYMGR_DPE_SIDELOAD_CLEAR_REG_OFFSET, + reg); + + return kErrorOk; +} + +/** + * Enforces that only sw bindings are used for the advancement + */ +OT_WARN_UNUSED_RESULT +rom_error_t sc_keymgr_dpe_advance_dpe_context( + sc_keymgr_dpe_advance_data_t adv_data) { + HARDENED_RETURN_IF_ERROR(expected_state_check(kScKeymgrDPEStateAvailable)); + return sc_keymgr_dpe_advance_wrapper(kScKeymgrDPEUseExclusiveSwBinding, + adv_data); +} + +/** + * Write into the lock register for the UDS + */ +rom_error_t sc_keymgr_dpe_lock_uds(void) { + // Issue the start command. + abs_mmio_write32(sc_keymgr_dpe_base() + KEYMGR_DPE_LOAD_KEY_LOCK_REG_OFFSET, + 1 << KEYMGR_DPE_LOAD_KEY_LOCK_LOCK_BIT); + return kErrorOk; +} + +/** + * Load the UDS into the provided destination slot + */ +// TODO(#30667): Verify if the max key version needs to be written here too! +// When loading the UDS the RTL fetches the max key version from +// the SW register. Verify that the lock is released when the +// version register is locked. +rom_error_t sc_keymgr_dpe_load_uds(uint32_t sel_dst_slot) { + // Set the control register entries + sc_keymgr_dpe_control_reg_set( + kScKeymgrDPEUseAdditionalHwBinding, + KEYMGR_DPE_CONTROL_SHADOWED_OPERATION_VALUE_LOAD_ROOT_KEY, + KEYMGR_DPE_CONTROL_SHADOWED_DEST_SEL_VALUE_NONE, 0, sel_dst_slot); + + // Start the load operation + sc_keymgr_dpe_start_operation(); + return sc_keymgr_dpe_wait_until_done(); +} + +/** + * Executes the first advance call to load the UDS in the selected slot and + * sets the keymgr_dpe FSM to available. + */ +// TODO(#30667): Verify if the max key version needs to be written here too! +// When loading the UDS the RTL fetches the max key version from +// the SW register. Verify that the lock is released when the +// version register is locked. +OT_WARN_UNUSED_RESULT +rom_error_t sc_keymgr_dpe_advance_initial(const uint32_t sel_dst_slot_uds) { + // Verify the reset state + HARDENED_RETURN_IF_ERROR(expected_state_check(kScKeymgrDPEStateReset)); + + // Set the control register entries + sc_keymgr_dpe_control_reg_set( + kScKeymgrDPEUseAdditionalHwBinding, + KEYMGR_DPE_CONTROL_SHADOWED_OPERATION_VALUE_ADVANCE, + KEYMGR_DPE_CONTROL_SHADOWED_DEST_SEL_VALUE_NONE, 0, sel_dst_slot_uds); + + // Start the advance operation + sc_keymgr_dpe_start_operation(); + + // Wait until UDS is loaded + HARDENED_RETURN_IF_ERROR(sc_keymgr_dpe_wait_until_done()); + + // Verify the available state + HARDENED_RETURN_IF_ERROR(expected_state_check(kScKeymgrDPEStateAvailable)); + return kErrorOk; +} + +/** + * Sets the binding registers / key version registers and advances the + * UDS into the creator keys (sealing and attestation). + */ +OT_WARN_UNUSED_RESULT +rom_error_t sc_keymgr_dpe_advance_creator( + sc_keymgr_dpe_advance_data_t adv_data_sealing, + sc_keymgr_dpe_advance_data_t adv_data_attestation) { + // Sanity checks + if ((adv_data_sealing.policy.parent != kScKeymgrDPESlotPolEraseParent) || + (adv_data_attestation.policy.parent != kScKeymgrDPESlotPolEraseParent)) { + return kErrorKeymgrInternal; + } + + // Verify the keymgr_dpe is in the correct FSM state + HARDENED_RETURN_IF_ERROR(expected_state_check(kScKeymgrDPEStateAvailable)); + + // Advance the sealing key chain + HARDENED_RETURN_IF_ERROR(sc_keymgr_dpe_advance_wrapper( + kScKeymgrDPEUseAdditionalHwBinding, adv_data_sealing)); + HARDENED_RETURN_IF_ERROR(sc_keymgr_dpe_wait_until_done()); + + // Load the UDS into the attestation source slot + HARDENED_RETURN_IF_ERROR( + sc_keymgr_dpe_load_uds(adv_data_attestation.sel_src_slot)); + + // Advance the attestation key chain + HARDENED_RETURN_IF_ERROR(sc_keymgr_dpe_advance_wrapper( + kScKeymgrDPEUseAdditionalHwBinding, adv_data_attestation)); + HARDENED_RETURN_IF_ERROR(sc_keymgr_dpe_wait_until_done()); + + return kErrorOk; +} + +/** + * Sets the binding registers / key version registers and advances the + * creator keys into the owner int keys (sealing and attestation). + */ +OT_WARN_UNUSED_RESULT +rom_error_t sc_keymgr_dpe_advance_owner_int( + sc_keymgr_dpe_advance_data_t adv_data_sealing, + sc_keymgr_dpe_advance_data_t adv_data_attestation) { + // Sanity checks + if ((adv_data_sealing.sel_src_slot != adv_data_sealing.sel_dst_slot) || + (adv_data_attestation.sel_src_slot != + adv_data_attestation.sel_dst_slot) || + (adv_data_sealing.policy.parent != kScKeymgrDPESlotPolEraseParent) || + (adv_data_attestation.policy.parent != kScKeymgrDPESlotPolEraseParent)) { + return kErrorKeymgrInternal; + } + HARDENED_RETURN_IF_ERROR(expected_state_check(kScKeymgrDPEStateAvailable)); + + // Advance the sealing key + HARDENED_RETURN_IF_ERROR(sc_keymgr_dpe_advance_wrapper( + kScKeymgrDPEUseAdditionalHwBinding, adv_data_sealing)); + HARDENED_RETURN_IF_ERROR(sc_keymgr_dpe_wait_until_done()); + + // Advance the attestation key + HARDENED_RETURN_IF_ERROR(sc_keymgr_dpe_advance_wrapper( + kScKeymgrDPEUseAdditionalHwBinding, adv_data_attestation)); + HARDENED_RETURN_IF_ERROR(sc_keymgr_dpe_wait_until_done()); + + return kErrorOk; +} + +/** + * Sets the binding registers / key version registers and advances the + * owner int keys into the owner keys (sealing and attestation). + */ +OT_WARN_UNUSED_RESULT +rom_error_t sc_keymgr_dpe_advance_owner( + sc_keymgr_dpe_advance_data_t adv_data_sealing, + sc_keymgr_dpe_advance_data_t adv_data_attestation) { + // Sanity checks + if ((adv_data_sealing.sel_src_slot != adv_data_sealing.sel_dst_slot) || + (adv_data_attestation.sel_src_slot != + adv_data_attestation.sel_dst_slot)) { + return kErrorKeymgrInternal; + } + HARDENED_RETURN_IF_ERROR(expected_state_check(kScKeymgrDPEStateAvailable)); + + // Advance the sealing key + HARDENED_RETURN_IF_ERROR(sc_keymgr_dpe_advance_wrapper( + kScKeymgrDPEUseAdditionalHwBinding, adv_data_sealing)); + HARDENED_RETURN_IF_ERROR(sc_keymgr_dpe_wait_until_done()); + + // Advance the attestation key + HARDENED_RETURN_IF_ERROR(sc_keymgr_dpe_advance_wrapper( + kScKeymgrDPEUseAdditionalHwBinding, adv_data_attestation)); + HARDENED_RETURN_IF_ERROR(sc_keymgr_dpe_wait_until_done()); + + return kErrorOk; +} + +/** + * Erase the DPE context of any slot + */ +rom_error_t sc_keymgr_dpe_erase_slot(uint32_t sel_dst_slot) { + // Set the control register entries + sc_keymgr_dpe_control_reg_set( + kScKeymgrDPEUseAdditionalHwBinding, + KEYMGR_DPE_CONTROL_SHADOWED_OPERATION_VALUE_ERASE_SLOT, + KEYMGR_DPE_CONTROL_SHADOWED_DEST_SEL_VALUE_NONE, 0, sel_dst_slot); + + // Start the advance operation + sc_keymgr_dpe_start_operation(); + + // Wait until the erase operation has finished + HARDENED_RETURN_IF_ERROR(sc_keymgr_dpe_wait_until_done()); + return kErrorOk; +} + +/** + * Advances the keymgr dpe into the disable state. All keys store in the + * sideloaded interface are continuously scrambled. + */ +rom_error_t sc_keymgr_dpe_disable(void) { + // Check if we are in the available state (Stalls until all ops are done) + HARDENED_RETURN_IF_ERROR(expected_state_check(kScKeymgrDPEStateAvailable)); + + // Set the control register entries + sc_keymgr_dpe_control_reg_set( + kScKeymgrDPEUseExclusiveSwBinding, + KEYMGR_DPE_CONTROL_SHADOWED_OPERATION_VALUE_DISABLE, + KEYMGR_DPE_CONTROL_SHADOWED_DEST_SEL_VALUE_NONE, 0, 0); + + // Start the advance operation + sc_keymgr_dpe_start_operation(); + // Wait until keymgr_dpe is finished and verify if state if disabled + HARDENED_RETURN_IF_ERROR(expected_state_check(kScKeymgrDPEStateDisabled)); + // According to the documentation for the SIDELOAD_CLEAR register, an invalid + // destination will enable continuous clearing of all destinations. + abs_mmio_write32(sc_keymgr_dpe_base() + KEYMGR_DPE_SIDELOAD_CLEAR_REG_OFFSET, + UINT32_MAX); + return kErrorOk; +} + +/** + * Generate a key manager key and sideload to the OTBN block. + */ +rom_error_t sc_keymgr_dpe_generate_key_otbn( + sc_keymgr_dpe_diversification_t diversification) { + return sc_keymgr_dpe_generate_key(kScKeymgrDPEDestOtbn, diversification); +} + +/** + * Clear OTBN's sideloaded key slot. + */ +rom_error_t sc_keymgr_dpe_clear_sideload_key_otbn(void) { + return sc_keymgr_dpe_clear_key(kScKeymgrDPEDestOtbn); +} diff --git a/sw/device/silicon_creator/lib/drivers/keymgr_dpe.h b/sw/device/silicon_creator/lib/drivers/keymgr_dpe.h new file mode 100644 index 0000000000000..1252816676a34 --- /dev/null +++ b/sw/device/silicon_creator/lib/drivers/keymgr_dpe.h @@ -0,0 +1,517 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef OPENTITAN_SW_DEVICE_SILICON_CREATOR_LIB_DRIVERS_KEYMGR_DPE_H_ +#define OPENTITAN_SW_DEVICE_SILICON_CREATOR_LIB_DRIVERS_KEYMGR_DPE_H_ + +#include +#include + +#include "sw/device/silicon_creator/lib/error.h" +#include "sw/device/silicon_creator/lib/keymgr_dpe_binding_value.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Key Manager states. + */ +typedef enum sc_keymgr_dpe_state { + /** + * Key manager dpe control is still in reset. Please wait for initialization + * complete before issuing operations + */ + kScKeymgrDPEStateReset, + /** + * Key manager control has finished latching OTP root key and will + * now accept software commands. + */ + kScKeymgrDPEStateAvailable, + /** + * Key manager dpe currently disabled. Please reset the key manager. Sideload + * keys are still valid. + */ + kScKeymgrDPEStateDisabled, + /** + * Key manager dpe currently invalid. Please reset the key manager. Sideload + * keys are no longer valid. + */ + kScKeymgrDPEStateInvalid, + /** + * This is not a state - it is the total number of states. + */ + kScKeymgrDPEStateNumStates, +} sc_keymgr_dpe_state_t; + +/** + * Option to exclude the hw bindings values when deriving a DPE context + * (only applicable when deriving either a first, second or third generation + * DPE context in respect to the UDS). + */ +typedef enum sc_keymgr_dpe_sw_binding { + kScKeymgrDPEUseAdditionalHwBinding = 0, + kScKeymgrDPEUseExclusiveSwBinding = 1, +} sc_keymgr_dpe_sw_binding_t; + +/** + * Set whether further advance operations should overwrite the source slot. + */ +typedef enum sc_keymgr_dpe_policy_parent { + kScKeymgrDPESlotPolRetainParent = 1, + kScKeymgrDPESlotPolEraseParent = 0, +} sc_keymgr_dpe_policy_parent_t; + +/** + * Set whether the key for the target slot is exportable. + * Currently the export function is not implemented in RTL (Issue #30612)! + */ +typedef enum sc_keymgr_dpe_policy_export { + kScKeymgrDPESlotPolAllowExport = 1, + kScKeymgrDPESlotPolNoExport = 0, +} sc_keymgr_dpe_policy_export_t; + +/** + * Set whether this DPE context allows to derive further DPE context. + */ +typedef enum sc_keymgr_dpe_policy_children { + kScKeymgrDPESlotPolAllowChild = 1, + kScKeymgrDPESlotPolNoChild = 0, +} sc_keymgr_dpe_policy_children_t; + +/** + * Merges the three policies into a single struct. + */ +typedef struct sc_keymgr_dpe_policies { + sc_keymgr_dpe_policy_parent_t parent; + sc_keymgr_dpe_policy_children_t child; + sc_keymgr_dpe_policy_export_t expo; +} sc_keymgr_dpe_policies_t; + +enum { + /** + * Number of 32-bit words for the salt. + */ + kScKeymgrDPESaltNumWords = 8, + /** + * Number of 32-bit words for the key + */ + kScKeymgrDPEKeyNumWords = 8, +}; + +/** + * Data used to differentiate a generated keymgr key. + */ +typedef struct sc_keymgr_dpe_diversification { + /** + * Salt value to use for key generation. + */ + uint32_t salt[kScKeymgrDPESaltNumWords]; + /** + * The source slot to be used as parent DPE context. + */ + uint32_t sel_src_slot; + /** + * Version for key generation (anti-rollback protection). + */ + uint32_t version; +} sc_keymgr_dpe_diversification_t; + +/** + * Data used to advance the state of one DPE context + */ +typedef struct sc_keymgr_dpe_advance_data { + /** + * Binding values for the advance call. + */ + keymgr_dpe_binding_value_t *binding_value; + /** + * Policy for the newly created DPE context. + */ + sc_keymgr_dpe_policies_t policy; + /** + * The source slot to be used as parent DPE context. + */ + uint32_t sel_src_slot; + /** + * The destination slot for the derived DPE context. + */ + uint32_t sel_dst_slot; + /** + * Max version for key generation (anti-rollback protection). + */ + uint32_t version; +} sc_keymgr_dpe_advance_data_t; + +/** + * Destination for key generation. + */ +typedef enum sc_keymgr_dpe_dest { + kScKeymgrDPEDestNone = 0, + kScKeymgrDPEDestAes = 1, + kScKeymgrDPEDestKmac = 2, + kScKeymgrDPEDestOtbn = 3, +} sc_keymgr_dpe_dest_t; + +/** + * The following constants represent the expected number of sec_mmio register + * writes performed by functions provided in this module. See + * `SEC_MMIO_WRITE_INCREMENT()` for more details. + * + * Example: + * ``` + * sc_keymgr_sw_binding_set(); + * SEC_MMIO_WRITE_INCREMENT(kScKeymgrSecMmioSwBindingSet); + * ``` + */ +enum { + kScKeymgrDPESecMmioReseedIntervalSet = 1, + kScKeymgrDPESecMmioSwBindingSet = 8, + kScKeymgrDPESecMmioMaxVerSet = 1, + kScKeymgrDPESecMmioSlotPolicy = 1, +}; + +/** + * Keymgr DPE ECC key generation descriptor. + * + * The attestation or sealing key chain is directly mapped towards one of the + * keymgr_dpe slots thus removing the need to differentiate between the two + * of them. + * + * Attestation keys are derived from the system state, which changes when the + * ROM_EXT or the owner firmware is updated. Sealing keys remain stable as + * long as the device remains under the same ownership and hardware lifecycle + * state. + */ +typedef struct sc_keymgr_dpe_ecc_key { + /** + * Index into the kFlashCtrlInfoPageAttestationKeySeeds flash info page that + * holds a seed for generating the ECC key pair. + */ + uint32_t keygen_seed_idx; + /** + * Pointer to the keymgr dpe diversifier that is used when actuating the + * keymgr's "output-generate" function to generate another ECC keygen seed + * that will be sideloaded to OTBN. + */ + const sc_keymgr_dpe_diversification_t *keymgr_dpe_diversifier; + /** + * Currently there is no way to control if the correct key was already + * generated inside of keymgr_dpe slot. The program execution flow has to + * guarantee the correct DPE context! + */ + sc_keymgr_dpe_state_t required_keymgr_dpe_state; +} sc_keymgr_dpe_ecc_key_t; + +/** + * Wait for the key manager dpe to finish an operation. + * + * Polls the key manager dpe until it is no longer busy. If the operation + * completed successfully, returns kErrorOk. If there was an error during the + * operation, reads and clears the error code and returns kErrorKeymgrInternal. + * + * @return `kErrorOk` if the status is either "idle" or the operation + * finished with "done_successfully", `kErrorKeymgrInternal`otherwise. + */ +OT_WARN_UNUSED_RESULT +rom_error_t sc_keymgr_dpe_wait_until_done(void); + +/** + * Sets the context of the control register for the next command. + * + * @param exl_sw_binding Should additional hw bindings be used during the + * next advance call. Only impact DPE context generation if either the + * creator root key / owner int key / owner key is being generated. + * @param ops The type of operation to execute next. + * @param sel_src_slot Source slot when either advancing a DPE context inside + * the slot or if an HW / SW key is being generated. + * @param sel_dst_slot Destination slot in which the next advance call will + * load the newly generated DPE context. + * @param key_dest Determine the sideload port when deriving a HW key. + */ +void sc_keymgr_dpe_control_reg_set( + const sc_keymgr_dpe_sw_binding_t exl_sw_binding, const uint32_t ops, + const sc_keymgr_dpe_dest_t key_dest, uint32_t sel_src_slot, + uint32_t sel_dst_slot); + +/** + * Start the pre-programmed operation. + */ +void sc_keymgr_dpe_start_operation(void); + +/** + * Sets the entropy reseed interval of the key manager dpe. + * + * @param reseed_interval Number of key manager dpe cycles before the + * entropy is reseeded. + */ +void sc_keymgr_dpe_entropy_reseed_interval_set(uint16_t reseed_interval); + +/** + * Sets the key manager dpe software binding input. + * + * This function also clears and caches the value of the `SW_BINDING_REGWEN` + * register in the `sec_mmio` expectations table. This register is unlocked + * after a successful transaction. It is recommended to call + * `sc_keymgr_dpe_sw_binding_unlock_wait()` after initiating a transition + * to update its value in the `sec_mmio` expectations table. + * + * @param binding_value Software binding value + */ +void sc_keymgr_dpe_sw_binding_set(keymgr_dpe_binding_value_t *binding_value); + +/** + * Blocks until the software binding registers are unlocked. + * + * This function can be called after `sc_keymgr_dpe_advance_state()` to wait + * for the software binding registers to become available for writing and to + * update the cached value of `SW_BINDING_REGWEN` register in the `sec_mmio` + * expectations table. + */ +void sc_keymgr_dpe_sw_binding_unlock_wait(void); + +/** + * Sets the current max key version used to advance a DPE context + * + * This function sets the current max key version. The subfield + * max_key_version of each hardware slot is populated with this value + * during an advance call which target the corresponding slot. + * + * @param max_key_ver Maximum key version + */ +void sc_keymgr_dpe_max_ver_set(uint32_t max_key_ver); + +/** + * Sets the key version used to generate a key + * + * This version is compared against the max version stores in the subfield of + * the source slot. Only if this version is smaller or equal to the max version + * the key is generated. + * + * @param key_ver key version + */ +void sc_keymgr_dpe_key_ver_set(uint32_t key_ver); + +/** + * Sets all the slot policies for the next advance call. + * + * @param policy combined policy vector for the next advance call + */ +void sc_keymgr_dpe_policy_set(sc_keymgr_dpe_policies_t policy); + +/** + * Checks the state of the key manager and compares against the provided + * value. + * + * @param expected_state Expected key manager dpe state. + * @return `kErrorOk` if the key manager dpe is in `expected_state` and the + * status is idle or success; otherwise returns `kErrorKeymgrInternal`. + */ +OT_WARN_UNUSED_RESULT +rom_error_t sc_keymgr_dpe_state_check(sc_keymgr_dpe_state_t expected_state); + +/** + * Generate a key manager dpe key and sideload to the requested block. + * + * Calls the key manager dpe to sideload a key into the requested hardware block + * and waits until the operation is complete before returning. + * + * @param destination: Hardware destination for key material. + * @param diversification Diversification input for the key derivation. + * @return OK or error. + */ +OT_WARN_UNUSED_RESULT +rom_error_t sc_keymgr_dpe_generate_key( + const sc_keymgr_dpe_dest_t destination, + sc_keymgr_dpe_diversification_t diversification); + +/** + * Read the generated sw key. + * + * The caller ensure the pointer have sufficient space for the full + * key in each share and that a key was successfully generated first. + * + * @param share0 Pointer to store the first share of the generated key with + * the required size defined in kScKeymgrDPEKeyNumWords. + * @param share1 Pointer to store the second share of the generated key with + * the required size defined in kScKeymgrDPEKeyNumWords. + * @return OK or kErrorKeymgrInternal if keymgr_dpe is not idle + */ +OT_WARN_UNUSED_RESULT +rom_error_t sc_keymgr_dpe_read_key(uint32_t *share0, uint32_t *share1); + +/** + * Clear the requested sideloaded key slot. + * + * The entropy complex needs to be initialized before calling this function, so + * that keymgr dpe can use it to clear the slot. + * + * @param destination: Hardware block to clear key material + * @return OK or kErrorKeymgrInternal if keymgr_dpe is not idle + */ +OT_WARN_UNUSED_RESULT +rom_error_t sc_keymgr_dpe_clear_key(sc_keymgr_dpe_dest_t destination); + +/** + * Advances the DPE context of one slot inside the keymgr_dpe + * + * This function can only be called if the targeted key_slot has already + * advanced three times. If the function is called sooner it will generate + * not valid keys and the boot process is corrupted. The function stalls at + * the start if the keymgr dpe is not idle. + * + * The caller must ensure that this function finish successfully by calling + * `sc_keymgr_dpe_wait_until_done()`. + * + * @param adv_data All required data to advance the key of one DPE Context. + * @return The result of the advance call. + */ +OT_WARN_UNUSED_RESULT +rom_error_t sc_keymgr_dpe_advance_dpe_context( + sc_keymgr_dpe_advance_data_t adv_data); + +/** + * Locks the load uds operation until the next reset + * + * When this function is called then the function "sc_keymgr_dpe_load_uds" will + * generate an error as the uds is locked. This lock can only be released by + * resetting the device. + * + * @return kErrorOk if the operation finished with "done_successfully", + * kErrorKeymgrInternal otherwise. + * + */ +rom_error_t sc_keymgr_dpe_lock_uds(void); + +/** + * Load the UDS into an empty hw slot + * + * Load the UDS into the selected hw slot. If the selected hw slot is not + * empty then the keymgr_dpe will through an error. + * + * @param sel_dst_slot empty destination slot for the UDS + * @return kErrorOk + */ +rom_error_t sc_keymgr_dpe_load_uds(uint32_t sel_dst_slot); + +/** + * Executes the first advance call to load the UDS in the selected slot and + * sets the keymgr_dpe FSM to available. + * + * Precondition: keymgr_dpe has to be in the state kScKeymgrDPEStateReset + * + * @param sel_dst_slot DPE context slot for the UDS + * @return The result of the advance call. + */ +OT_WARN_UNUSED_RESULT +rom_error_t sc_keymgr_dpe_advance_initial(const uint32_t sel_dst_slot_uds); + +/** + * Sets the binding registers / key version registers and advances the + * UDS into the creator keys (sealing and attestation). + * + * First the sealing key is generated from the preloaded UDS while the UDS + * is manually loaded a second time to generated the attestation key. + * Additionally the retain parent policy bit must be cleared to avoid leaving + * the creator keys existing beyond its designated boot stage. This + * function is blocking until the advance operation was successfully. + * + * Precondition: keymgr_dpe has to be in the state kScKeymgrDPEStateAvailable + * + * @param adv_data_sealing All required data to advance the UDS into the + * sealing owner key. + * @param adv_data_attestation All required data to advance the UDS into the + * attestation owner key. + * @return The result of the advance call. + */ +OT_WARN_UNUSED_RESULT +rom_error_t sc_keymgr_dpe_advance_creator( + sc_keymgr_dpe_advance_data_t adv_data_sealing, + sc_keymgr_dpe_advance_data_t adv_data_attestation); + +/** + * Sets the binding registers / key version registers and advances the + * creator keys into the owner int keys (sealing and attestation). + * + * The retain parent policy bit must be cleared to avoid leaving the owner int + * keys existing beyond its designated boot stage. Due to the retain parent + * policy on the creator keys the source and destination slot have to be equal. + * This function is blocking until the advance operation was successfully. + * + * Precondition: keymgr_dpe has to be in the state kScKeymgrDPEStateAvailable + * + * @param adv_data_sealing All required data for the sealing key + * @param adv_data_attestation All required data for the attestation key + * @return The result of the advance call. + */ +OT_WARN_UNUSED_RESULT +rom_error_t sc_keymgr_dpe_advance_owner_int( + sc_keymgr_dpe_advance_data_t adv_data_sealing, + sc_keymgr_dpe_advance_data_t adv_data_attestation); + +/** + * Sets the binding registers / key version registers and advances the + * owner int keys into the owner keys (sealing and attestation). + * + * Due to the retain parent policy on the owner int the source and + * destination slot have to be equal. This function is blocking until the + * advance operation was successfully. + * + * Precondition: keymgr_dpe has to be in the state kScKeymgrDPEStateAvailable + * + * @param adv_data_sealing All required data for the sealing key + * @param adv_data_attestation All required data for the attestation key + * @return The result of the advance call. + */ +OT_WARN_UNUSED_RESULT +rom_error_t sc_keymgr_dpe_advance_owner( + sc_keymgr_dpe_advance_data_t adv_data_sealing, + sc_keymgr_dpe_advance_data_t adv_data_attestation); + +/** + * Erase the DPE context of any slot + * + * This function is blocking until the advance operation was successfully. + * + * @param sel_dst_slot selected DPE context to erase + * @return The result of the erase call. + */ +rom_error_t sc_keymgr_dpe_erase_slot(uint32_t sel_dst_slot); + +/** + * Advances the keymgr dpe into the disable state. + * + * All keys store in the sideloaded interface are continuously scrambled. + * + * @return OK or error. + */ +rom_error_t sc_keymgr_dpe_disable(void); + +/** + * Generate a key manager key and sideload to the OTBN block. + * + * Calls the key manager to sideload a key into the OTBN hardware block and + * waits until the operation is complete before returning. + * + * @param diversification Diversification input for the key derivation. + * @return OK or error. + */ +rom_error_t sc_keymgr_dpe_generate_key_otbn( + sc_keymgr_dpe_diversification_t diversification); + +/** + * Clear OTBN's sideloaded key slot. + * + * The entropy complex needs to be initialized before calling this function, so + * that keymgr dpe can use it to clear the slot. + * + * @return OK or error. + */ +rom_error_t sc_keymgr_dpe_clear_sideload_key_otbn(void); + +#ifdef __cplusplus +} +#endif + +#endif // OPENTITAN_SW_DEVICE_SILICON_CREATOR_LIB_DRIVERS_KEYMGR_DPE_H_ diff --git a/sw/device/silicon_creator/lib/drivers/keymgr_dpe_unittest.cc b/sw/device/silicon_creator/lib/drivers/keymgr_dpe_unittest.cc new file mode 100644 index 0000000000000..b377b8cc4dabb --- /dev/null +++ b/sw/device/silicon_creator/lib/drivers/keymgr_dpe_unittest.cc @@ -0,0 +1,24 @@ +// 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/top/dt/keymgr_dpe.h" + +#include +#include + +#include "gtest/gtest.h" +#include "sw/device/lib/base/mock_abs_mmio.h" +#include "sw/device/silicon_creator/lib/base/mock_sec_mmio.h" +#include "sw/device/silicon_creator/lib/drivers/keymgr_dpe.h" +#include "sw/device/silicon_creator/lib/error.h" + +#include "hw/top/keymgr_dpe_regs.h" // Generated. + +namespace keymgr_dpe_unittest { +namespace { + +// TODO(#30609): write this unittest + +} // namespace +} // namespace keymgr_dpe_unittest diff --git a/sw/device/silicon_creator/lib/keymgr_dpe_binding_value.h b/sw/device/silicon_creator/lib/keymgr_dpe_binding_value.h new file mode 100644 index 0000000000000..4a31411ef9cb3 --- /dev/null +++ b/sw/device/silicon_creator/lib/keymgr_dpe_binding_value.h @@ -0,0 +1,37 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef OPENTITAN_SW_DEVICE_SILICON_CREATOR_LIB_KEYMGR_DPE_BINDING_VALUE_H_ +#define OPENTITAN_SW_DEVICE_SILICON_CREATOR_LIB_KEYMGR_DPE_BINDING_VALUE_H_ + +#include + +#include "sw/device/lib/base/macros.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +/** + * Binding value used by key manager dpe to derive secret values. + * + * A change in this value changes the secret value of key manager dpe, and + * consequently, the versioned keys and identity seeds generated at subsequent + * boot stages. + * + * Note: The size of this value is an implementation detail of the key manager + * dpe hardware. + */ +typedef struct keymgr_dpe_binding_value { + uint32_t data[8]; +} keymgr_dpe_binding_value_t; + +OT_ASSERT_MEMBER_OFFSET(keymgr_dpe_binding_value_t, data, 0); +OT_ASSERT_SIZE(keymgr_dpe_binding_value_t, 32); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus + +#endif // OPENTITAN_SW_DEVICE_SILICON_CREATOR_LIB_KEYMGR_DPE_BINDING_VALUE_H_