From 6519e1aed00bc04c2e786c42c50e8e66e1ecf2c3 Mon Sep 17 00:00:00 2001 From: Raphael Roth Date: Thu, 2 Jul 2026 14:06:14 +0200 Subject: [PATCH 1/9] [keymgr_dpe, otp_ctrl, rtl] Change otp keymgr_dpe interface Split the otp_keymgr_key interface into its respective components. Future version of the keymgr(_dpe) may receive the information from different IP's rather than combined from the otp_ctrl. To avoid a circular dependencies the DevIdWidth instantiated in keymgr_dpe_pkg replaces the otp_ctrl_pkg::DeviceIdWidth. As the keymgr still expects the combined message the earlgrey top manually combines the key material. This fix will be removed when the keymgr is replaced by the keymgr_dpe. Signed-off-by: Raphael Roth --- hw/ip/keymgr_dpe/data/keymgr_dpe.hjson | 24 +- hw/ip/keymgr_dpe/doc/interfaces.md | 30 +-- hw/ip/keymgr_dpe/dv/env/keymgr_dpe_if.sv | 6 +- hw/ip/keymgr_dpe/dv/keymgr_dpe_sim.core | 1 + hw/ip/keymgr_dpe/dv/tb.sv | 23 +- hw/ip/keymgr_dpe/keymgr_dpe.core | 1 - hw/ip/keymgr_dpe/rtl/keymgr_dpe.sv | 28 +-- hw/ip/keymgr_dpe/rtl/keymgr_dpe_pkg.sv | 44 +++- hw/ip/otp_ctrl/otp_ctrl_pkg.core | 1 + hw/ip/otp_ctrl/rtl/otp_ctrl_pkg.sv | 22 +- hw/ip/otp_macro/otp_macro.core | 1 + .../otp_ctrl/data/otp_ctrl.hjson.tpl | 22 +- .../otp_ctrl/doc/interfaces.md.tpl | 6 +- hw/ip_templates/otp_ctrl/dv/README.md.tpl | 2 +- hw/ip_templates/otp_ctrl/rtl/otp_ctrl.sv.tpl | 55 +++-- .../data/autogen/top_darjeeling.gen.hjson | 210 +++++++++++++++--- hw/top_darjeeling/data/top_darjeeling.hjson | 12 +- .../ip_autogen/otp_ctrl/data/otp_ctrl.hjson | 22 +- .../ip_autogen/otp_ctrl/doc/interfaces.md | 44 ++-- .../ip_autogen/otp_ctrl/dv/README.md | 2 +- .../ip_autogen/otp_ctrl/rtl/otp_ctrl.sv | 49 ++-- .../rtl/autogen/top_darjeeling.sv | 18 +- hw/top_darjeeling/templates/toplevel.sv.tpl | 2 +- .../data/autogen/top_earlgrey.gen.hjson | 138 +++++++++--- hw/top_earlgrey/data/top_earlgrey.hjson | 8 +- .../ip_autogen/otp_ctrl/data/otp_ctrl.hjson | 22 +- .../ip_autogen/otp_ctrl/doc/interfaces.md | 46 ++-- .../ip_autogen/otp_ctrl/dv/README.md | 2 +- .../ip_autogen/otp_ctrl/rtl/otp_ctrl.sv | 49 ++-- hw/top_earlgrey/rtl/autogen/top_earlgrey.sv | 20 +- hw/top_earlgrey/templates/toplevel.sv.tpl | 9 + 31 files changed, 675 insertions(+), 244 deletions(-) diff --git a/hw/ip/keymgr_dpe/data/keymgr_dpe.hjson b/hw/ip/keymgr_dpe/data/keymgr_dpe.hjson index 1530281902226..42953df157c81 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", 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/env/keymgr_dpe_if.sv b/hw/ip/keymgr_dpe/dv/env/keymgr_dpe_if.sv index d1a8fc007eb63..3f4ccb38886ac 100644 --- a/hw/ip/keymgr_dpe/dv/env/keymgr_dpe_if.sv +++ b/hw/ip/keymgr_dpe/dv/env/keymgr_dpe_if.sv @@ -18,8 +18,10 @@ interface keymgr_dpe_if(input clk, input rst_n); // 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; + 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[NumRomDigestInputs-1:0] rom_digests; keymgr_pkg::hw_key_req_t kmac_key; diff --git a/hw/ip/keymgr_dpe/dv/keymgr_dpe_sim.core b/hw/ip/keymgr_dpe/dv/keymgr_dpe_sim.core index f21f3ece92bbe..8ad2aaff2e16d 100644 --- a/hw/ip/keymgr_dpe/dv/keymgr_dpe_sim.core +++ b/hw/ip/keymgr_dpe/dv/keymgr_dpe_sim.core @@ -7,6 +7,7 @@ description: "KEYMGR_DPE DV sim target" filesets: files_rtl: depend: + - lowrisc:ip:otp_ctrl_pkg - lowrisc:ip:keymgr_dpe files_dv: diff --git a/hw/ip/keymgr_dpe/dv/tb.sv b/hw/ip/keymgr_dpe/dv/tb.sv index ac5c6167fb323..41f123b936393 100644 --- a/hw/ip/keymgr_dpe/dv/tb.sv +++ b/hw/ip/keymgr_dpe/dv/tb.sv @@ -41,6 +41,23 @@ module tb; assign keymgr_dpe_if.edn_ack = edn_if[0].ack; assign keymgr_dpe_if.lfsr_en = dut.lfsr_en; + // Quick fix to propagate the signal from the keymgr_dpe_if to the dut + 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; + assign creator_root_key.share0 = + keymgr_dpe_if.otp_key.creator_root_key_share0; + assign creator_root_key.share0_valid = + keymgr_dpe_if.otp_key.creator_root_key_share0_valid; + assign creator_root_key.share1 = + keymgr_dpe_if.otp_key.creator_root_key_share1; + assign creator_root_key.share1_valid = + keymgr_dpe_if.otp_key.creator_root_key_share1_valid; + assign creator_seed.seed = keymgr_dpe_if.otp_key.creator_seed; + assign creator_seed.seed_valid = keymgr_dpe_if.otp_key.creator_seed_valid; + assign owner_seed.seed = keymgr_dpe_if.otp_key.owner_seed; + assign owner_seed.seed_valid = keymgr_dpe_if.otp_key.owner_seed_valid; + // dut // TODO(opentitan-integrated/issues/332): // need to model the OTP seed input @@ -58,8 +75,10 @@ module tb; .kmac_en_masking_i (1'b1), .lc_keymgr_en_i (keymgr_dpe_if.keymgr_dpe_en), .lc_keymgr_div_i (keymgr_dpe_if.keymgr_dpe_div), - .otp_key_i (keymgr_dpe_if.otp_key), - .otp_device_id_i (keymgr_dpe_if.otp_device_id), + .creator_root_key_i (creator_root_key), + .creator_seed_i (creator_seed), + .owner_seed_i (owner_seed), + .device_id_i (keymgr_dpe_if.otp_device_id), .rom_digest_i (keymgr_dpe_if.rom_digests), .edn_o (edn_if[0].req), .edn_i ({edn_if[0].ack, edn_if[0].d_data}), diff --git a/hw/ip/keymgr_dpe/keymgr_dpe.core b/hw/ip/keymgr_dpe/keymgr_dpe.core index 67c4f1f6e5f9a..c0d0b574527c7 100644 --- a/hw/ip/keymgr_dpe/keymgr_dpe.core +++ b/hw/ip/keymgr_dpe/keymgr_dpe.core @@ -20,7 +20,6 @@ filesets: - lowrisc:prim:sparse_fsm - lowrisc:ip:keymgr_dpe_pkg - lowrisc:ip:kmac_pkg - - lowrisc:ip:otp_ctrl_pkg - lowrisc:ip:rom_ctrl_pkg - lowrisc:ip:tlul - lowrisc:ip:keymgr_common diff --git a/hw/ip/keymgr_dpe/rtl/keymgr_dpe.sv b/hw/ip/keymgr_dpe/rtl/keymgr_dpe.sv index 58ad10bbe1a80..2e303c1ff1f63 100644 --- a/hw/ip/keymgr_dpe/rtl/keymgr_dpe.sv +++ b/hw/ip/keymgr_dpe/rtl/keymgr_dpe.sv @@ -56,8 +56,10 @@ module keymgr_dpe // SEC_CM: LC_CTRL.INTERSIG.MUBI input lc_ctrl_pkg::lc_tx_t lc_keymgr_en_i, input lc_ctrl_pkg::lc_keymgr_div_t lc_keymgr_div_i, - input otp_ctrl_pkg::otp_keymgr_key_t otp_key_i, - input otp_ctrl_pkg::otp_device_id_t otp_device_id_i, + input keymgr_dpe_creator_root_key_t creator_root_key_i, + input keymgr_dpe_creator_seed_t creator_seed_i, + input keymgr_dpe_owner_seed_t owner_seed_i, + input keymgr_dpe_device_id_t device_id_i, // connection to edn output edn_pkg::edn_req_t edn_o, @@ -259,16 +261,16 @@ module keymgr_dpe end hw_key_req_t root_key; - assign root_key.key = '{otp_key_i.creator_root_key_share1, - otp_key_i.creator_root_key_share0}; + assign root_key.key = '{creator_root_key_i.share1, + creator_root_key_i.share0}; prim_flop_2sync # ( .Width(1) ) u_key_valid_sync ( .clk_i, .rst_ni, - .d_i(otp_key_i.creator_root_key_share0_valid & - otp_key_i.creator_root_key_share1_valid), + .d_i(creator_root_key_i.share0_valid & + creator_root_key_i.share1_valid), .q_o(root_key.valid) ); @@ -449,14 +451,14 @@ module keymgr_dpe // The values coming from otp_ctrl / lc_ctrl are treat as quasi-static for CDC purposes logic [KeyWidth-1:0] creator_seed; logic unused_creator_seed; - assign unused_creator_seed = ^{otp_key_i.creator_seed_valid}; - assign creator_seed = otp_key_i.creator_seed; + assign unused_creator_seed = ^{creator_seed_i.seed_valid}; + assign creator_seed = creator_seed_i.seed; // Advance to owner_intermediate_key logic [KeyWidth-1:0] owner_seed; logic unused_owner_seed; - assign unused_owner_seed = ^{otp_key_i.owner_seed_valid}; - assign owner_seed = otp_key_i.owner_seed; + assign unused_owner_seed = ^{owner_seed_i.seed_valid}; + assign owner_seed = owner_seed_i.seed; always_comb begin : gen_adv_matrix_all // One default only use SW binding @@ -467,7 +469,7 @@ module keymgr_dpe // For (0 = Creator) and (1 = OwnerInt), check seed validity adv_matrix[BootStageCreator] = DpeAdvDataWidth'({sw_binding, revision_seed, - otp_device_id_i, + device_id_i, lc_keymgr_div_i, rom_digests, creator_seed}); @@ -516,7 +518,7 @@ module keymgr_dpe .creator_seed_i(creator_seed), .owner_seed_i(owner_seed), .key_i(curr_active_key), - .devid_i(otp_device_id_i), + .devid_i(device_id_i), .health_state_i(HealthStateWidth'(lc_keymgr_div_i)), .creator_seed_vld_o(creator_seed_vld), .owner_seed_vld_o(owner_seed_vld), @@ -816,7 +818,7 @@ module keymgr_dpe assign unused_active_policy = active_key_slot.key_policy; assign unused_active_key_version = active_key_slot.max_key_version; - `ASSERT_INIT(KeyWidthEqualityCheck_A, otp_ctrl_pkg::KeyMgrKeyWidth == KeyWidth) + `ASSERT_INIT(KeyWidthEqualityCheck_A, KeyMgrKeyWidth == KeyWidth) `ASSERT_INIT_NET(KmacMaskCheck_A, KmacEnMasking == kmac_en_masking_i) diff --git a/hw/ip/keymgr_dpe/rtl/keymgr_dpe_pkg.sv b/hw/ip/keymgr_dpe/rtl/keymgr_dpe_pkg.sv index 929854cfb40eb..8d61865eb4f3e 100644 --- a/hw/ip/keymgr_dpe/rtl/keymgr_dpe_pkg.sv +++ b/hw/ip/keymgr_dpe/rtl/keymgr_dpe_pkg.sv @@ -10,6 +10,10 @@ package keymgr_dpe_pkg; parameter int DpeNumSlots = 8; parameter int DpeNumSlotsWidth = prim_util_pkg::vbits(DpeNumSlots); + // Chip Device ID + parameter int DeviceIdWidth = 256; + typedef logic [DeviceIdWidth-1:0] keymgr_dpe_device_id_t; + // keymgr and keymgr_dpe have different maximum KMAC input widths. The below widths correspond to // the following inputs to advance to the creator root key state: // - Software binding @@ -18,7 +22,7 @@ package keymgr_dpe_pkg; // - LC keymgr diversification value // - ROM digests // - Creator seed - parameter int DpeAdvDataWidth = SwBindingWidth + KeyWidth + otp_ctrl_pkg::DeviceIdWidth + + parameter int DpeAdvDataWidth = SwBindingWidth + KeyWidth + DeviceIdWidth + lc_ctrl_pkg::LcKeymgrDivWidth + KeyWidth*keymgr_dpe_reg_pkg::NumRomDigestInputs + KeyWidth; typedef logic [DpeNumSlotsWidth-1:0] keymgr_dpe_slot_idx_e; @@ -84,6 +88,44 @@ package keymgr_dpe_pkg; logic allow_child; } keymgr_dpe_policy_t; + // Parameter Key Width + parameter int KeyMgrKeyWidth = 256; + + // Input struct for secrets required by the keymgr dpe. + typedef struct packed { + logic [KeyMgrKeyWidth-1:0] share0; + logic share0_valid; + logic [KeyMgrKeyWidth-1:0] share1; + logic share1_valid; + } keymgr_dpe_creator_root_key_t; + + typedef struct packed { + logic [KeyMgrKeyWidth-1:0] seed; + logic seed_valid; + } keymgr_dpe_creator_seed_t; + + typedef struct packed { + logic [KeyMgrKeyWidth-1:0] seed; + logic seed_valid; + } keymgr_dpe_owner_seed_t; + + parameter keymgr_dpe_creator_root_key_t KEYMGR_DPE_CREATOR_ROOT_KEY_DEFAULT = '{ + share0 : 256'hefb7ea7ee90093cf4affd9aaa2d6c0ec446cfdf5f2d5a0bfd7e2d93edc63a102, + share0_valid : 1'b1, + share1 : 256'h56d24a00181de99e0f690b447a8dde2a1ffb8bc306707107aa6e2410f15cfc37, + share1_valid : 1'b1 + }; + + parameter keymgr_dpe_creator_seed_t KEYMGR_DPE_CREATOR_SEED_DEFAULT = '{ + seed : 256'hc7c50b38655cc87f821e5b07fed85d2c07e222a9e00bef308b3eccba0ba406fa, + seed_valid : 1'b1 + }; + + parameter keymgr_dpe_owner_seed_t KEYMGR_DPE_OWNER_SEED_DEFAULT = '{ + seed : 256'hf5052c0f14782d8b066be9f49c0b2000d3643ff3723ea7db972f69cd3e2e3e68, + seed_valid : 1'b1 + }; + // Enumeration for boot stage. In the BootStageRuntime stage, there is no limit on the number of // advance calls. parameter int DpeBootStagesWidth = 2; diff --git a/hw/ip/otp_ctrl/otp_ctrl_pkg.core b/hw/ip/otp_ctrl/otp_ctrl_pkg.core index b4173382e700a..5f2e5fd8b1d90 100644 --- a/hw/ip/otp_ctrl/otp_ctrl_pkg.core +++ b/hw/ip/otp_ctrl/otp_ctrl_pkg.core @@ -8,6 +8,7 @@ filesets: files_rtl: depend: - lowrisc:ip:lc_ctrl_pkg + - lowrisc:ip:keymgr_dpe_pkg files: - rtl/otp_ctrl_pkg.sv file_type: systemVerilogSource diff --git a/hw/ip/otp_ctrl/rtl/otp_ctrl_pkg.sv b/hw/ip/otp_ctrl/rtl/otp_ctrl_pkg.sv index b3e9ba245afef..ba2e06f8ee672 100644 --- a/hw/ip/otp_ctrl/rtl/otp_ctrl_pkg.sv +++ b/hw/ip/otp_ctrl/rtl/otp_ctrl_pkg.sv @@ -5,6 +5,8 @@ // This package can be imported by generic IPs: // - It does not import otp_ctrl_reg_pkg, which is generated and top-specific. +`include "prim_assert.sv" + package otp_ctrl_pkg; //////////////////////// @@ -13,6 +15,9 @@ package otp_ctrl_pkg; parameter int DeviceIdWidth = 256; typedef logic [DeviceIdWidth-1:0] otp_device_id_t; + // Device ID defined in the keymgr_dpe package must match + `ASSERT_STATIC_IN_PACKAGE(DeviceIdWidth_A, + DeviceIdWidth == keymgr_dpe_pkg::DeviceIdWidth) parameter int ManufStateWidth = 256; typedef logic [ManufStateWidth-1:0] otp_manuf_state_t; @@ -87,7 +92,6 @@ package otp_ctrl_pkg; parameter int NvmKeySeedWidth = 256; parameter int SramKeySeedWidth = 128; - parameter int KeyMgrKeyWidth = 256; parameter int NvmKeyWidth = 128; parameter int SramKeyWidth = 128; parameter int SramNonceWidth = 128; @@ -104,14 +108,14 @@ package otp_ctrl_pkg; localparam int SramNonceSel = SramNonceWidth / ScrmblBlockWidth; typedef struct packed { - logic [KeyMgrKeyWidth-1:0] creator_root_key_share0; - logic creator_root_key_share0_valid; - logic [KeyMgrKeyWidth-1:0] creator_root_key_share1; - logic creator_root_key_share1_valid; - logic [KeyMgrKeyWidth-1:0] creator_seed; - logic creator_seed_valid; - logic [KeyMgrKeyWidth-1:0] owner_seed; - logic owner_seed_valid; + logic [keymgr_dpe_pkg::KeyMgrKeyWidth-1:0] creator_root_key_share0; + logic creator_root_key_share0_valid; + logic [keymgr_dpe_pkg::KeyMgrKeyWidth-1:0] creator_root_key_share1; + logic creator_root_key_share1_valid; + logic [keymgr_dpe_pkg::KeyMgrKeyWidth-1:0] creator_seed; + logic creator_seed_valid; + logic [keymgr_dpe_pkg::KeyMgrKeyWidth-1:0] owner_seed; + logic owner_seed_valid; } otp_keymgr_key_t; parameter otp_keymgr_key_t OTP_KEYMGR_KEY_DEFAULT = '{ diff --git a/hw/ip/otp_macro/otp_macro.core b/hw/ip/otp_macro/otp_macro.core index 090a89b8d21e3..aa5ee03ca3765 100644 --- a/hw/ip/otp_macro/otp_macro.core +++ b/hw/ip/otp_macro/otp_macro.core @@ -16,6 +16,7 @@ filesets: - lowrisc:virtual_ip:otp_ctrl_macro_pkg - lowrisc:virtual_constants:top_racl_pkg - lowrisc:systems:ast_pkg + - lowrisc:virtual_ip:otp_ctrl_top_specific_pkg files: - rtl/otp_macro_reg_pkg.sv - rtl/otp_macro_prim_reg_top.sv diff --git a/hw/ip_templates/otp_ctrl/data/otp_ctrl.hjson.tpl b/hw/ip_templates/otp_ctrl/data/otp_ctrl.hjson.tpl index a73d1ca39a70f..40b0473b1be8e 100644 --- a/hw/ip_templates/otp_ctrl/data/otp_ctrl.hjson.tpl +++ b/hw/ip_templates/otp_ctrl/data/otp_ctrl.hjson.tpl @@ -376,14 +376,30 @@ otp_size_as_uint32 = otp_size_as_bytes // 4 ''' } // 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" + } % if enable_nvm_key: // Broadcast to Nvm Controller { struct: "nvm_otp_key" diff --git a/hw/ip_templates/otp_ctrl/doc/interfaces.md.tpl b/hw/ip_templates/otp_ctrl/doc/interfaces.md.tpl index 525039489f1dc..e1fae27aea5b4 100644 --- a/hw/ip_templates/otp_ctrl/doc/interfaces.md.tpl +++ b/hw/ip_templates/otp_ctrl/doc/interfaces.md.tpl @@ -115,10 +115,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/ip_templates/otp_ctrl/dv/README.md.tpl b/hw/ip_templates/otp_ctrl/dv/README.md.tpl index 71d503d61df2f..c25d4494dcb47 100644 --- a/hw/ip_templates/otp_ctrl/dv/README.md.tpl +++ b/hw/ip_templates/otp_ctrl/dv/README.md.tpl @@ -134,7 +134,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/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..48b4cdbd7a594 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 } { @@ -5891,28 +5921,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: "" + 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_otp_device_id + top_signame: keymgr_dpe_device_id index: -1 } { @@ -11373,9 +11429,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 +12001,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 +22731,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 +22742,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 +25780,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 +34360,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 +36790,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..3421007b8a8b5 100644 --- a/hw/top_darjeeling/data/top_darjeeling.hjson +++ b/hw/top_darjeeling/data/top_darjeeling.hjson @@ -1224,10 +1224,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 +1364,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/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/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..1353ad35c4292 100644 --- a/hw/top_darjeeling/rtl/autogen/top_darjeeling.sv +++ b/hw/top_darjeeling/rtl/autogen/top_darjeeling.sv @@ -579,7 +579,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 +774,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 +813,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 +1194,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), @@ -1721,8 +1725,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/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 From 478f6bffbde0eae11e74cac61796da88f6cc1709 Mon Sep 17 00:00:00 2001 From: Raphael Roth Date: Fri, 3 Jul 2026 13:28:38 +0200 Subject: [PATCH 2/9] [otp_ctrl, dv] Update otp_ctrl dv This commit updates the `otp_ctrl` dv to reflect the new interface between the `keymgr` and the `otp_ctrl`. The secret / seed verification inside the otp_ctrl scoreboard template are no longer dynamically generated. The reason is that the previously shared port signal has been split into its respective components, which does not interacts well with the dynamic generation anymore. However, adding a secret to the otp now results in a port-list change anyways the resulting overhead is deemed acceptable. Signed-off-by: Raphael Roth --- .../otp_ctrl/dv/env/otp_ctrl_if.sv.tpl | 16 +++-- .../dv/env/otp_ctrl_scoreboard.sv.tpl | 48 +++++++++---- hw/ip_templates/otp_ctrl/dv/tb.sv.tpl | 4 +- .../ip_autogen/otp_ctrl/dv/env/otp_ctrl_if.sv | 16 +++-- .../otp_ctrl/dv/env/otp_ctrl_scoreboard.sv | 68 ++++++++++--------- .../ip_autogen/otp_ctrl/dv/tb.sv | 4 +- .../ip_autogen/otp_ctrl/dv/env/otp_ctrl_if.sv | 16 +++-- .../otp_ctrl/dv/env/otp_ctrl_scoreboard.sv | 44 ++++++------ hw/top_earlgrey/ip_autogen/otp_ctrl/dv/tb.sv | 4 +- 9 files changed, 131 insertions(+), 89 deletions(-) diff --git a/hw/ip_templates/otp_ctrl/dv/env/otp_ctrl_if.sv.tpl b/hw/ip_templates/otp_ctrl/dv/env/otp_ctrl_if.sv.tpl index 26e0b963e55e6..de6fe7ab7bd4f 100644 --- a/hw/ip_templates/otp_ctrl/dv/env/otp_ctrl_if.sv.tpl +++ b/hw/ip_templates/otp_ctrl/dv/env/otp_ctrl_if.sv.tpl @@ -54,7 +54,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; @@ -296,13 +298,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) |=> @@ -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/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_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), From ea4d1ec56a0f803d639737a1fe7b9c3f5a4cc33e Mon Sep 17 00:00:00 2001 From: Raphael Roth Date: Wed, 29 Apr 2026 16:20:42 +0200 Subject: [PATCH 3/9] [keymgr_dpe, dv] Remove otp_ctrl dependency This commit removes all otp_ctrl dependency in the keymgr_dpe dv. This change ensures that the dv architecture accurately reflects the dependency in the RTL implementation. Signed-off-by: Raphael Roth --- hw/ip/keymgr_dpe/dv/env/keymgr_dpe_if.sv | 26 ++++++++++--------- .../dv/env/keymgr_dpe_scoreboard.sv | 12 ++++----- hw/ip/keymgr_dpe/dv/keymgr_dpe_sim.core | 2 +- hw/ip/keymgr_dpe/dv/tb.sv | 23 +++------------- 4 files changed, 24 insertions(+), 39 deletions(-) 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 3f4ccb38886ac..b6f228301374b 100644 --- a/hw/ip/keymgr_dpe/dv/env/keymgr_dpe_if.sv +++ b/hw/ip/keymgr_dpe/dv/env/keymgr_dpe_if.sv @@ -108,33 +108,35 @@ interface keymgr_dpe_if(input clk, input rst_n); 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 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 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..dde183f0c4bcf 100644 --- a/hw/ip/keymgr_dpe/dv/env/keymgr_dpe_scoreboard.sv +++ b/hw/ip/keymgr_dpe/dv/env/keymgr_dpe_scoreboard.sv @@ -973,10 +973,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) @@ -1274,7 +1274,7 @@ class keymgr_dpe_scoreboard extends cip_base_scoreboard #( if (exp_match) `DV_CHECK_EQ(byte_data_q.size, keymgr_dpe_pkg::DpeAdvDataWidth / 8) act = {<<8{byte_data_q}}; - exp.DiversificationKey = cfg.keymgr_dpe_vif.otp_key.creator_seed; + exp.DiversificationKey = cfg.keymgr_dpe_vif.creator_seed.seed; for (int i = 0; i < keymgr_dpe_reg_pkg::NumRomDigestInputs; ++i) begin exp.RomDigests[i] = cfg.keymgr_dpe_vif.rom_digests[i].data; @@ -1310,7 +1310,7 @@ class keymgr_dpe_scoreboard extends cip_base_scoreboard #( 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) diff --git a/hw/ip/keymgr_dpe/dv/keymgr_dpe_sim.core b/hw/ip/keymgr_dpe/dv/keymgr_dpe_sim.core index 8ad2aaff2e16d..3400e63bc3067 100644 --- a/hw/ip/keymgr_dpe/dv/keymgr_dpe_sim.core +++ b/hw/ip/keymgr_dpe/dv/keymgr_dpe_sim.core @@ -7,7 +7,7 @@ description: "KEYMGR_DPE DV sim target" filesets: files_rtl: depend: - - lowrisc:ip:otp_ctrl_pkg + - lowrisc:ip:keymgr_dpe_pkg - lowrisc:ip:keymgr_dpe files_dv: diff --git a/hw/ip/keymgr_dpe/dv/tb.sv b/hw/ip/keymgr_dpe/dv/tb.sv index 41f123b936393..43cf6d5463e23 100644 --- a/hw/ip/keymgr_dpe/dv/tb.sv +++ b/hw/ip/keymgr_dpe/dv/tb.sv @@ -41,23 +41,6 @@ module tb; assign keymgr_dpe_if.edn_ack = edn_if[0].ack; assign keymgr_dpe_if.lfsr_en = dut.lfsr_en; - // Quick fix to propagate the signal from the keymgr_dpe_if to the dut - 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; - assign creator_root_key.share0 = - keymgr_dpe_if.otp_key.creator_root_key_share0; - assign creator_root_key.share0_valid = - keymgr_dpe_if.otp_key.creator_root_key_share0_valid; - assign creator_root_key.share1 = - keymgr_dpe_if.otp_key.creator_root_key_share1; - assign creator_root_key.share1_valid = - keymgr_dpe_if.otp_key.creator_root_key_share1_valid; - assign creator_seed.seed = keymgr_dpe_if.otp_key.creator_seed; - assign creator_seed.seed_valid = keymgr_dpe_if.otp_key.creator_seed_valid; - assign owner_seed.seed = keymgr_dpe_if.otp_key.owner_seed; - assign owner_seed.seed_valid = keymgr_dpe_if.otp_key.owner_seed_valid; - // dut // TODO(opentitan-integrated/issues/332): // need to model the OTP seed input @@ -75,9 +58,9 @@ module tb; .kmac_en_masking_i (1'b1), .lc_keymgr_en_i (keymgr_dpe_if.keymgr_dpe_en), .lc_keymgr_div_i (keymgr_dpe_if.keymgr_dpe_div), - .creator_root_key_i (creator_root_key), - .creator_seed_i (creator_seed), - .owner_seed_i (owner_seed), + .creator_root_key_i (keymgr_dpe_if.creator_root_key), + .creator_seed_i (keymgr_dpe_if.creator_seed), + .owner_seed_i (keymgr_dpe_if.owner_seed), .device_id_i (keymgr_dpe_if.otp_device_id), .rom_digest_i (keymgr_dpe_if.rom_digests), .edn_o (edn_if[0].req), From c8fa723a69a7744e6ba555e13832027cbdbc87a1 Mon Sep 17 00:00:00 2001 From: Raphael Roth Date: Mon, 4 May 2026 17:55:30 +0200 Subject: [PATCH 4/9] [keymgr_dpe, dv] Improve keymgr_dpe block level dv This commit improves the block level dv by: - Introducing more verbose messages - Latching the correct DEFAULT_UDS_POLICY - Prevent ambiguous state after the disable operation by restricting any advance call Signed-off-by: Raphael Roth --- .../dv/env/keymgr_dpe_scoreboard.sv | 29 +++--- .../dv/env/seq_lib/keymgr_dpe_base_vseq.sv | 69 ++++++++++---- .../dv/env/seq_lib/keymgr_dpe_smoke_vseq.sv | 91 ++++++++++++------- 3 files changed, 127 insertions(+), 62 deletions(-) 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 dde183f0c4bcf..a9f96a1ecf37c 100644 --- a/hw/ip/keymgr_dpe/dv/env/keymgr_dpe_scoreboard.sv +++ b/hw/ip/keymgr_dpe/dv/env/keymgr_dpe_scoreboard.sv @@ -150,11 +150,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,8 +171,11 @@ 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 @@ -985,8 +991,8 @@ class keymgr_dpe_scoreboard extends cip_base_scoreboard #( 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; 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] @@ -1071,11 +1077,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 @@ -1272,6 +1277,8 @@ class keymgr_dpe_scoreboard extends cip_base_scoreboard #( adv_creator_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 0"), UVM_MEDIUM) + if (exp_match) `DV_CHECK_EQ(byte_data_q.size, keymgr_dpe_pkg::DpeAdvDataWidth / 8) act = {<<8{byte_data_q}}; exp.DiversificationKey = cfg.keymgr_dpe_vif.creator_seed.seed; 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..c8a589e6082a0 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 @@ -100,7 +100,7 @@ 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) `DV_CHECK_RANDOMIZE_FATAL(ral.intr_enable) csr_update(.csr(ral.intr_enable)); @@ -118,7 +118,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 +149,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 +172,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 +207,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 +275,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 +312,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 +381,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 +419,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..0a47f6de487c0 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_pkg::DpeNumSlots; + soft dst_slot < keymgr_dpe_pkg::DpeNumSlots; + 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,35 +47,59 @@ 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_pkg::DpeNumSlots - 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 @@ -88,6 +114,7 @@ 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. + `uvm_info(`gfn, "Key Manager DPE smoke - erase test", UVM_LOW) for (int iter = 1; iter Date: Mon, 4 May 2026 16:32:06 +0200 Subject: [PATCH 5/9] [keymgr_dpe, dv] Introduce backdoor load for UDS The scoreboard loads the UDS directly from the DUT via interface. The problem is the keymgr_dpe initializes the slot for the UDS with randomness. The initally loaded UDS is xored with the randomness already present in the destination slot as countermeasure for SCA. The scoreboard however can currently not recreate this randomness thus the backdoor load. Signed-off-by: Raphael Roth --- hw/ip/keymgr_dpe/dv/env/keymgr_dpe_ctrl_if.sv | 28 ++++++++++++++++++ hw/ip/keymgr_dpe/dv/env/keymgr_dpe_env.core | 1 + hw/ip/keymgr_dpe/dv/env/keymgr_dpe_env.sv | 4 +++ hw/ip/keymgr_dpe/dv/env/keymgr_dpe_env_cfg.sv | 3 ++ .../dv/env/keymgr_dpe_scoreboard.sv | 29 +++++++++++++++++++ hw/ip/keymgr_dpe/dv/tb.sv | 4 +++ 6 files changed, 69 insertions(+) create mode 100644 hw/ip/keymgr_dpe/dv/env/keymgr_dpe_ctrl_if.sv 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..80204e96baa1c --- /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_pkg::DpeNumSlots; + + // Check that the slot index is in range. The key_slots_q array has length DpeNumSlots, which is + // a global parameter. + if (slot >= DpeNumSlots) begin + `uvm_error($sformatf("%m"), + $sformatf("Slot index out of range: index is %0d but DpeNumSlots is %0d", + slot, DpeNumSlots)) + 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_scoreboard.sv b/hw/ip/keymgr_dpe/dv/env/keymgr_dpe_scoreboard.sv index a9f96a1ecf37c..1ef73d046e8c1 100644 --- a/hw/ip/keymgr_dpe/dv/env/keymgr_dpe_scoreboard.sv +++ b/hw/ip/keymgr_dpe/dv/env/keymgr_dpe_scoreboard.sv @@ -68,6 +68,12 @@ 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; @@ -990,6 +996,12 @@ 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 = keymgr_dpe_pkg::DEFAULT_UDS_POLICY; @@ -1004,6 +1016,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. @@ -1127,6 +1148,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 @@ -1534,6 +1562,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 diff --git a/hw/ip/keymgr_dpe/dv/tb.sv b/hw/ip/keymgr_dpe/dv/tb.sv index 43cf6d5463e23..44dd49e04070a 100644 --- a/hw/ip/keymgr_dpe/dv/tb.sv +++ b/hw/ip/keymgr_dpe/dv/tb.sv @@ -26,6 +26,8 @@ module tb; keymgr_dpe_if keymgr_dpe_if(.clk(clk), .rst_n(rst_n)); kmac_app_intf keymgr_dpe_kmac_intf(.clk(clk), .rst_n(rst_n)); + bind dut.u_ctrl keymgr_dpe_ctrl_if u_if (.clk_i, .rst_ni); + // connect KDF interface for assertion check assign keymgr_dpe_if.kmac_data_req = keymgr_dpe_kmac_intf.kmac_data_req; assign keymgr_dpe_if.kmac_data_rsp = keymgr_dpe_kmac_intf.kmac_data_rsp; @@ -83,6 +85,8 @@ module tb; uvm_config_db#(intr_vif)::set(null, "*.env", "intr_vif", intr_if); uvm_config_db#(virtual tl_if)::set(null, "*.env.m_tl_agent*", "vif", tl_if); uvm_config_db#(virtual keymgr_dpe_if)::set(null, "*.env", "keymgr_dpe_vif", keymgr_dpe_if); + uvm_config_db#(virtual keymgr_dpe_ctrl_if)::set(null, "*.env", "keymgr_dpe_ctrl_vif", + dut.u_ctrl.u_if); uvm_config_db#(virtual kmac_app_intf)::set(null, "*env.m_keymgr_dpe_kmac_agent*", "vif", keymgr_dpe_kmac_intf); $timeformat(-12, 0, " ps", 12); From d06e0d29c43c5321ce2684f719f7e37038c670eb Mon Sep 17 00:00:00 2001 From: Raphael Roth Date: Mon, 4 May 2026 17:56:45 +0200 Subject: [PATCH 6/9] [keymgr_dpe, rtl, dv] Introduce bootstage OwnerIntKey Introduction of the third bootstage defined in the opentitan identities-and-root-keys (https://opentitan.org/book/doc/security/specs/identities_and_root_keys/index.html) strategy. The `creator_seed` is now consumed by BootStageOwnerInt rather than by BootStageCreator. Signed-off-by: Raphael Roth --- .../dv/env/keymgr_dpe_scoreboard.sv | 70 ++++++++++++++++--- hw/ip/keymgr_dpe/rtl/keymgr_dpe.sv | 46 ++++++------ hw/ip/keymgr_dpe/rtl/keymgr_dpe_ctrl.sv | 4 +- hw/ip/keymgr_dpe/rtl/keymgr_dpe_pkg.sv | 9 +-- 4 files changed, 94 insertions(+), 35 deletions(-) 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 1ef73d046e8c1..227474d0e26bc 100644 --- a/hw/ip/keymgr_dpe/dv/env/keymgr_dpe_scoreboard.sv +++ b/hw/ip/keymgr_dpe/dv/env/keymgr_dpe_scoreboard.sv @@ -23,8 +23,6 @@ class keymgr_dpe_scoreboard extends cip_base_scoreboard #( bit [keymgr_pkg::HealthStateWidth-1:0] HealthMeasurement; // ROM_DESCRIPTORS bit [keymgr_dpe_reg_pkg::NumRomDigestInputs-1:0][keymgr_pkg::KeyWidth-1:0] RomDigests; - // CREATOR_SEED - bit [keymgr_pkg::KeyWidth-1:0] DiversificationKey; } adv_creator_data_t; typedef struct packed { @@ -33,17 +31,28 @@ class keymgr_dpe_scoreboard extends cip_base_scoreboard #( 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_pkg::DpeAdvDataWidth-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_pkg::DpeAdvDataWidth-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; @@ -193,7 +202,7 @@ class keymgr_dpe_scoreboard extends cip_base_scoreboard #( .exp_match(!is_err), .byte_data_q(item.byte_data_q) ); - end else if (boot_stage == keymgr_dpe_pkg::BootStageOwner) begin + 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_1_data"}, @@ -202,6 +211,15 @@ class keymgr_dpe_scoreboard extends cip_base_scoreboard #( .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_2_data"}, + boot_stage, is_err), UVM_LOW) + compare_boot_stage_2_data( + .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", @@ -276,6 +294,10 @@ 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 + current_internal_key[current_key_slot.dst_slot].boot_stage = + keymgr_dpe_pkg::BootStageOwnerInt; + end + keymgr_dpe_pkg::BootStageOwnerInt: begin current_internal_key[current_key_slot.dst_slot].boot_stage = keymgr_dpe_pkg::BootStageOwner; end @@ -1309,7 +1331,6 @@ class keymgr_dpe_scoreboard extends cip_base_scoreboard #( if (exp_match) `DV_CHECK_EQ(byte_data_q.size, keymgr_dpe_pkg::DpeAdvDataWidth / 8) act = {<<8{byte_data_q}}; - exp.DiversificationKey = cfg.keymgr_dpe_vif.creator_seed.seed; for (int i = 0; i < keymgr_dpe_reg_pkg::NumRomDigestInputs; ++i) begin exp.RomDigests[i] = cfg.keymgr_dpe_vif.rom_digests[i].data; @@ -1321,12 +1342,13 @@ 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(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) @@ -1344,6 +1366,34 @@ 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.owner_seed.seed; get_sw_binding_mirrored_value(exp.SoftwareBinding); @@ -1369,7 +1419,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}}; diff --git a/hw/ip/keymgr_dpe/rtl/keymgr_dpe.sv b/hw/ip/keymgr_dpe/rtl/keymgr_dpe.sv index 2e303c1ff1f63..aff20ef909f12 100644 --- a/hw/ip/keymgr_dpe/rtl/keymgr_dpe.sv +++ b/hw/ip/keymgr_dpe/rtl/keymgr_dpe.sv @@ -466,19 +466,21 @@ module keymgr_dpe adv_dvalid = {(2 ** DpeBootStagesWidth){1'b1}}; if (reg2hw.control_shadowed.sw_binding_only.q == 1'b0) begin - // For (0 = Creator) and (1 = OwnerInt), check seed validity - adv_matrix[BootStageCreator] = DpeAdvDataWidth'({sw_binding, - revision_seed, - device_id_i, - lc_keymgr_div_i, - rom_digests, - creator_seed}); - adv_dvalid[BootStageCreator] = creator_seed_vld & - devid_vld & - health_state_vld & - rom_digest_vld; - adv_matrix[BootStageOwner] = DpeAdvDataWidth'({sw_binding,owner_seed}); - adv_dvalid[BootStageOwner] = owner_seed_vld; + // For (0 = Creator) / (1 = OwnerInt) / (2 = Owner), check seed validity + adv_matrix[BootStageCreator] = DpeAdvDataWidth'({sw_binding, + revision_seed, + device_id_i, + lc_keymgr_div_i, + rom_digests}); + adv_dvalid[BootStageCreator] = devid_vld & + health_state_vld & + rom_digest_vld; + adv_matrix[BootStageOwnerInt] = DpeAdvDataWidth'({sw_binding, + creator_seed}); + adv_dvalid[BootStageOwnerInt] = creator_seed_vld; + adv_matrix[BootStageOwner] = DpeAdvDataWidth'({sw_binding, + owner_seed}); + adv_dvalid[BootStageOwner] = owner_seed_vld; end end @@ -541,18 +543,22 @@ module keymgr_dpe assign hw2reg.debug.inactive_lc_en.d = lc_tx_test_false_loose( lc_keymgr_en[KeymgrDpeEnDebug]); - // creator_seed, dev_id, health_state and rom digest are used when boot_stage is incremented from - // 0 (= Creator) to 1 (= OwnerInt), so only latch them during consumption. - logic is_creator_boot_stage, is_owner_boot_stage; - assign is_creator_boot_stage = active_key_slot.boot_stage == BootStageCreator; - assign is_owner_boot_stage = active_key_slot.boot_stage == BootStageOwner; + // Only latch required signals to advance the bootstage during consumption. + logic is_creator_boot_stage, is_owner_boot_stage, is_owner_int_boot_stage; + assign is_creator_boot_stage = active_key_slot.boot_stage == BootStageCreator; + assign is_owner_int_boot_stage = active_key_slot.boot_stage == BootStageOwnerInt; + assign is_owner_boot_stage = active_key_slot.boot_stage == BootStageOwner; - assign hw2reg.debug.invalid_creator_seed.de = adv_en & is_creator_boot_stage; + // dev_id, health_state and rom digest is used when boot_stage is incremented + // from 0 (= Creator) to 1 (= OwnerInt) assign hw2reg.debug.invalid_dev_id.de = adv_en & is_creator_boot_stage; assign hw2reg.debug.invalid_health_state.de = adv_en & is_creator_boot_stage; assign hw2reg.debug.invalid_digest.de = adv_en & is_creator_boot_stage; - // owner_seed is used when boot_stage is incremented from 1 (= OwnerInt) to 2 (= Owner). + // creator_seed is used when boot_stage is incremented from 1 (= OwnerInt) to 2 (= Owner). + assign hw2reg.debug.invalid_creator_seed.de = adv_en & is_owner_int_boot_stage; + + // owner_seed is used when boot_stage is incremented from 2 (= Owner) to 3 (= Runtime). assign hw2reg.debug.invalid_owner_seed.de = adv_en & is_owner_boot_stage; // key validity and versions are checked regardless of the boot stage, when there is an ongoing diff --git a/hw/ip/keymgr_dpe/rtl/keymgr_dpe_ctrl.sv b/hw/ip/keymgr_dpe/rtl/keymgr_dpe_ctrl.sv index 416eee816dd9e..c90b542db5b5d 100644 --- a/hw/ip/keymgr_dpe/rtl/keymgr_dpe_ctrl.sv +++ b/hw/ip/keymgr_dpe/rtl/keymgr_dpe_ctrl.sv @@ -321,7 +321,9 @@ module keymgr_dpe_ctrl key_slots_d[slot_dst_sel_i].key = kmac_data_i; key_slots_d[slot_dst_sel_i].max_key_version = max_key_version_i; key_slots_d[slot_dst_sel_i].boot_stage = - (active_key_slot_o.boot_stage == BootStageCreator) ? BootStageOwner : BootStageRuntime; + (active_key_slot_o.boot_stage == BootStageCreator) ? BootStageOwnerInt : + (active_key_slot_o.boot_stage == BootStageOwnerInt) ? BootStageOwner : + BootStageRuntime; key_slots_d[slot_dst_sel_i].key_policy = slot_policy_i; end diff --git a/hw/ip/keymgr_dpe/rtl/keymgr_dpe_pkg.sv b/hw/ip/keymgr_dpe/rtl/keymgr_dpe_pkg.sv index 8d61865eb4f3e..8664f95b50dab 100644 --- a/hw/ip/keymgr_dpe/rtl/keymgr_dpe_pkg.sv +++ b/hw/ip/keymgr_dpe/rtl/keymgr_dpe_pkg.sv @@ -23,7 +23,7 @@ package keymgr_dpe_pkg; // - ROM digests // - Creator seed parameter int DpeAdvDataWidth = SwBindingWidth + KeyWidth + DeviceIdWidth + - lc_ctrl_pkg::LcKeymgrDivWidth + KeyWidth*keymgr_dpe_reg_pkg::NumRomDigestInputs + KeyWidth; + lc_ctrl_pkg::LcKeymgrDivWidth + KeyWidth*keymgr_dpe_reg_pkg::NumRomDigestInputs; typedef logic [DpeNumSlotsWidth-1:0] keymgr_dpe_slot_idx_e; @@ -130,9 +130,10 @@ package keymgr_dpe_pkg; // advance calls. parameter int DpeBootStagesWidth = 2; typedef enum logic [DpeBootStagesWidth-1:0] { - BootStageCreator = 0, - BootStageOwner = 1, - BootStageRuntime = 2 + BootStageCreator = 0, + BootStageOwnerInt = 1, + BootStageOwner = 2, + BootStageRuntime = 3 } keymgr_dpe_boot_stage_e; // An internal secret key slot From 4d3ee5924fadfc525c25ae3948d0826ca1a99a42 Mon Sep 17 00:00:00 2001 From: Raphael Roth Date: Mon, 4 May 2026 18:17:01 +0200 Subject: [PATCH 7/9] [keymgr_dpe, rtl, dv] Support multiple top configurations This commit allows for a limited parametrization by the top without having a fully templated IP. The top can define the following: - Number of bootstages (either two or three) - Number of ROM digest values - Number of HW slots instanciated (up to 8 slots) The block level dv is extended to test two different configurations, called earlgrey and darjeeling. Signed-off-by: Raphael Roth --- hw/ip/keymgr_dpe/data/keymgr_dpe.hjson | 29 +++- hw/ip/keymgr_dpe/dv/README.md | 1 + hw/ip/keymgr_dpe/dv/env/keymgr_dpe_ctrl_if.sv | 12 +- hw/ip/keymgr_dpe/dv/env/keymgr_dpe_env_pkg.sv | 37 ++++- hw/ip/keymgr_dpe/dv/env/keymgr_dpe_if.sv | 31 ++-- .../dv/env/keymgr_dpe_scoreboard.sv | 136 ++++++++++++++---- .../dv/env/seq_lib/keymgr_dpe_base_vseq.sv | 11 +- .../dv/env/seq_lib/keymgr_dpe_smoke_vseq.sv | 20 +-- .../dv/keymgr_dpe_darjeeling_sim_cfg.hjson | 15 ++ .../dv/keymgr_dpe_earlgrey_sim_cfg.hjson | 15 ++ hw/ip/keymgr_dpe/dv/tb.sv | 6 +- hw/ip/keymgr_dpe/rtl/keymgr_dpe.sv | 136 +++++++++++++++--- hw/ip/keymgr_dpe/rtl/keymgr_dpe_ctrl.sv | 41 ++++-- hw/ip/keymgr_dpe/rtl/keymgr_dpe_pkg.sv | 16 --- hw/ip/keymgr_dpe/rtl/keymgr_dpe_reg_pkg.sv | 2 +- .../data/autogen/top_darjeeling.gen.hjson | 34 ++++- hw/top_darjeeling/data/top_darjeeling.hjson | 5 + .../chip_sw_keymgr_dpe_key_derivation_vseq.sv | 12 +- .../dv/top_darjeeling_sim_cfgs.hjson | 2 +- .../rtl/autogen/top_darjeeling.sv | 9 +- 20 files changed, 448 insertions(+), 122 deletions(-) create mode 100644 hw/ip/keymgr_dpe/dv/keymgr_dpe_darjeeling_sim_cfg.hjson create mode 100644 hw/ip/keymgr_dpe/dv/keymgr_dpe_earlgrey_sim_cfg.hjson diff --git a/hw/ip/keymgr_dpe/data/keymgr_dpe.hjson b/hw/ip/keymgr_dpe/data/keymgr_dpe.hjson index 42953df157c81..5d5a39d3b3f58 100644 --- a/hw/ip/keymgr_dpe/data/keymgr_dpe.hjson +++ b/hw/ip/keymgr_dpe/data/keymgr_dpe.hjson @@ -228,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" }, ], @@ -551,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/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 index 80204e96baa1c..5216a5b1f313c 100644 --- a/hw/ip/keymgr_dpe/dv/env/keymgr_dpe_ctrl_if.sv +++ b/hw/ip/keymgr_dpe/dv/env/keymgr_dpe_ctrl_if.sv @@ -10,14 +10,14 @@ interface keymgr_dpe_ctrl_if (input clk_i, input rst_ni); // 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_pkg::DpeNumSlots; + import keymgr_dpe_env_pkg::DvNumInstHwSlot; - // Check that the slot index is in range. The key_slots_q array has length DpeNumSlots, which is - // a global parameter. - if (slot >= DpeNumSlots) begin + // 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 DpeNumSlots is %0d", - slot, DpeNumSlots)) + $sformatf("Slot index out of range: index is %0d but DvNumInstHwSlot is %0d", + slot, DvNumInstHwSlot)) return '0; end 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 b6f228301374b..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,13 +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; - 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[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; @@ -103,7 +102,7 @@ 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 @@ -115,7 +114,7 @@ interface keymgr_dpe_if(input clk, input rst_n); keymgr_dpe_en = lc_ctrl_pkg::On; keymgr_dpe_div = 64'h5CFBD765CE33F34E; otp_device_id = 'hF0F0; - 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 @@ -193,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 @@ -221,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); @@ -239,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 227474d0e26bc..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,12 +22,28 @@ 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; - } adv_creator_data_t; + bit [keymgr_dpe_env_pkg::DvNumRomDigestInputs-1:0][keymgr_pkg::KeyWidth-1:0] RomDigests; + // CREATOR_SEED + 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; @@ -37,7 +53,7 @@ class keymgr_dpe_scoreboard extends cip_base_scoreboard #( 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; @@ -47,7 +63,7 @@ class keymgr_dpe_scoreboard extends cip_base_scoreboard #( typedef struct packed { // some portions are unused, which are 0s - bit [keymgr_dpe_pkg::DpeAdvDataWidth-keymgr_pkg::SwBindingWidth-1:0] + 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; @@ -86,7 +102,7 @@ class keymgr_dpe_scoreboard extends cip_base_scoreboard #( // 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 @@ -108,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]; @@ -194,14 +210,26 @@ class keymgr_dpe_scoreboard extends cip_base_scoreboard #( case (current_state) keymgr_dpe_pkg::StWorkDpeAvailable: begin if(boot_stage == keymgr_dpe_pkg::BootStageCreator) begin - `uvm_info(`gfn, - $sformatf({"process_kmac_data_req: boot_stage %0d is_err %0d", - "compare_boot_stage_0_data"}, - boot_stage, is_err), UVM_LOW) - compare_boot_stage_0_data( - .exp_match(!is_err), - .byte_data_q(item.byte_data_q) - ); + 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", @@ -294,8 +322,13 @@ 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 - current_internal_key[current_key_slot.dst_slot].boot_stage = - keymgr_dpe_pkg::BootStageOwnerInt; + 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 = @@ -1270,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)); @@ -1320,19 +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); - `uvm_info(`gfn, $sformatf("Compare data for boot stage 0"), UVM_MEDIUM) + `uvm_info(`gfn, $sformatf({"Compare data for boot stage 0 including the", + " creator seed"}), UVM_MEDIUM) - if (exp_match) `DV_CHECK_EQ(byte_data_q.size, keymgr_dpe_pkg::DpeAdvDataWidth / 8) + 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_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; @@ -1342,7 +1379,10 @@ 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(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) @@ -1359,6 +1399,48 @@ class keymgr_dpe_scoreboard extends cip_base_scoreboard #( 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) + 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_1_data( bit exp_match, const ref byte byte_data_q[$] @@ -1636,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 c8a589e6082a0..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 @@ -101,6 +101,13 @@ class keymgr_dpe_base_vseq extends cip_base_vseq #( end `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)); 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 0a47f6de487c0..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,8 +24,8 @@ 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 < keymgr_dpe_pkg::DpeNumSlots; - soft dst_slot < keymgr_dpe_pkg::DpeNumSlots; + soft src_slot < keymgr_dpe_env_pkg::DvNumInstHwSlot; + soft dst_slot < keymgr_dpe_env_pkg::DvNumInstHwSlot; if (!otp_latched) { soft dst_slot == 0; } } @@ -89,7 +89,7 @@ class keymgr_dpe_smoke_vseq extends keymgr_dpe_base_vseq; end // Iterate through all slots and fill them with DPE context - for (int iter = 0; iter<(keymgr_dpe_pkg::DpeNumSlots - 1); iter++) begin + for (int iter = 0; iter<(keymgr_dpe_env_pkg::DvNumInstHwSlot - 1); iter++) begin // set source and destination src_slot = dst_slot; dst_slot ++; @@ -104,7 +104,7 @@ class keymgr_dpe_smoke_vseq extends keymgr_dpe_base_vseq; // 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 @@ -115,7 +115,7 @@ class keymgr_dpe_smoke_vseq extends keymgr_dpe_base_vseq; // only have boot_stage 1, which gives us enough boot_stages to advance and fill // key_slots 1-3 again. `uvm_info(`gfn, "Key Manager DPE smoke - erase test", UVM_LOW) - for (int iter = 1; iter Date: Thu, 7 May 2026 11:47:14 +0200 Subject: [PATCH 8/9] [keymgr_dpe, dif] Add missing API calls Add three missing features to the dif of the keymgr_dpe. - The disable function to transition the keymgr_dpe into the disabled state - The configuration function to set the re-seeding interval of the keymgr_dpe - Any sideloaded key on the sideload interface can now be cleared by sw Add an empty `dif_keymgr_dpe_unittest.cc` file to avoid triggering error during the top integration, tracked in Issue #30609. Signed-off-by: Raphael Roth --- sw/device/lib/dif/dif_keymgr_dpe.c | 63 ++++++++++++++++++ sw/device/lib/dif/dif_keymgr_dpe.h | 70 +++++++++++++++++++- sw/device/lib/dif/dif_keymgr_dpe_unittest.cc | 23 +++++++ 3 files changed, 155 insertions(+), 1 deletion(-) create mode 100644 sw/device/lib/dif/dif_keymgr_dpe_unittest.cc 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 From 3cb89ab29394a65b7031e47573a5ce94d27d6980 Mon Sep 17 00:00:00 2001 From: Raphael Roth Date: Thu, 7 May 2026 12:07:27 +0200 Subject: [PATCH 9/9] [keymgr_dpe, sw] Add first version of the silicon driver This commit introduces a first version of the keymgr_dpe sw in the silicon creator lib. It covers the basic keymgr_dpe features. Add source / header file **without** linking them in the appropriate *BUILD* file as some dependencies do not exist yet! Add an empty keymgr_dpe_unittest.cc file to avoid triggering error during the top integration, tracked in Issue #30609. Signed-off-by: Raphael Roth --- .../silicon_creator/lib/drivers/keymgr_dpe.c | 653 ++++++++++++++++++ .../silicon_creator/lib/drivers/keymgr_dpe.h | 517 ++++++++++++++ .../lib/drivers/keymgr_dpe_unittest.cc | 24 + .../lib/keymgr_dpe_binding_value.h | 37 + 4 files changed, 1231 insertions(+) create mode 100644 sw/device/silicon_creator/lib/drivers/keymgr_dpe.c create mode 100644 sw/device/silicon_creator/lib/drivers/keymgr_dpe.h create mode 100644 sw/device/silicon_creator/lib/drivers/keymgr_dpe_unittest.cc create mode 100644 sw/device/silicon_creator/lib/keymgr_dpe_binding_value.h 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_