Skip to content

Commit

Permalink
[otp_ctrl] Differentiate between owner and creator keys
Browse files Browse the repository at this point in the history
This updates the templating mechanism so that keymaterial
can be associated either with the creator or the owner.

This is needed since the life cycle controller outputs
different enablement signals for creator and owner seeds.

Note that because key material is stored in secret partitions,
the key material will be inaccessible by SW after locking the
associated partition.

Outputing the key material to the key manager requires all
partitions holding keymaterial to be locked.

This addresses #21044.

Signed-off-by: Michael Schaffner <[email protected]>
  • Loading branch information
msfschaffner committed Feb 1, 2024
1 parent aff3fbd commit 3705405
Show file tree
Hide file tree
Showing 19 changed files with 334 additions and 166 deletions.
11 changes: 11 additions & 0 deletions hw/ip/otp_ctrl/data/otp_ctrl.hjson
Original file line number Diff line number Diff line change
Expand Up @@ -1286,6 +1286,17 @@
This signal enables SW read / write access to the RMA_TOKEN and CREATOR_ROOT_KEY_SHARE0 and CREATOR_ROOT_KEY_SHARE1.
'''
}
{ struct: "lc_tx"
type: "uni"
name: "lc_owner_seed_sw_rw_en"
act: "rcv"
default: "lc_ctrl_pkg::Off"
package: "lc_ctrl_pkg"
desc: '''
Provision enable qualifier coming from life cycle controller.
This signal enables SW read / write access to the OWNER_SEED.
'''
}
{ struct: "lc_tx"
type: "uni"
name: "lc_seed_hw_rd_en"
Expand Down
11 changes: 11 additions & 0 deletions hw/ip/otp_ctrl/data/otp_ctrl.hjson.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,17 @@ otp_size_as_uint32 = otp_size_as_bytes // 4
This signal enables SW read / write access to the RMA_TOKEN and CREATOR_ROOT_KEY_SHARE0 and CREATOR_ROOT_KEY_SHARE1.
'''
}
{ struct: "lc_tx"
type: "uni"
name: "lc_owner_seed_sw_rw_en"
act: "rcv"
default: "lc_ctrl_pkg::Off"
package: "lc_ctrl_pkg"
desc: '''
Provision enable qualifier coming from life cycle controller.
This signal enables SW read / write access to the OWNER_SEED.
'''
}
{ struct: "lc_tx"
type: "uni"
name: "lc_seed_hw_rd_en"
Expand Down
1 change: 1 addition & 0 deletions hw/ip/otp_ctrl/data/otp_ctrl_base_vseq.sv.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ class otp_ctrl_base_vseq extends cip_base_vseq #(

virtual task otp_ctrl_vif_init();
cfg.otp_ctrl_vif.drive_lc_creator_seed_sw_rw_en(lc_ctrl_pkg::On);
cfg.otp_ctrl_vif.drive_lc_owner_seed_sw_rw_en(lc_ctrl_pkg::On);
cfg.otp_ctrl_vif.drive_lc_seed_hw_rd_en(get_rand_lc_tx_val());
cfg.otp_ctrl_vif.drive_lc_dft_en(get_rand_lc_tx_val(.t_weight(0)));
cfg.otp_ctrl_vif.drive_lc_escalate_en(lc_ctrl_pkg::Off);
Expand Down
3 changes: 3 additions & 0 deletions hw/ip/otp_ctrl/data/otp_ctrl_dai_lock_vseq.sv.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ class otp_ctrl_dai_lock_vseq extends otp_ctrl_smoke_vseq;
if ($urandom_range(0, 1)) begin
cfg.otp_ctrl_vif.drive_lc_creator_seed_sw_rw_en(get_rand_lc_tx_val(.t_weight(0)));
end
if ($urandom_range(0, 1)) begin
cfg.otp_ctrl_vif.drive_lc_owner_seed_sw_rw_en(get_rand_lc_tx_val(.t_weight(0)));
end
endtask

endclass
Expand Down
7 changes: 6 additions & 1 deletion hw/ip/otp_ctrl/data/otp_ctrl_if.sv.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ interface otp_ctrl_if(input clk_i, input rst_ni);
// Inputs to DUT
logic pwr_otp_init_i, scan_en_i, scan_rst_ni, ext_voltage_h_io;
lc_ctrl_pkg::lc_tx_t lc_dft_en_i, lc_escalate_en_i, lc_check_byp_en_i,
lc_creator_seed_sw_rw_en_i, lc_seed_hw_rd_en_i;
lc_creator_seed_sw_rw_en_i, lc_owner_seed_sw_rw_en_i,
lc_seed_hw_rd_en_i;
prim_mubi_pkg::mubi4_t scanmode_i;
otp_ast_rsp_t otp_ast_pwr_seq_h_i;
ast_pkg::ast_obs_ctrl_t obs_ctrl_i;
Expand Down Expand Up @@ -142,6 +143,10 @@ interface otp_ctrl_if(input clk_i, input rst_ni);
lc_creator_seed_sw_rw_en_i = val;
endfunction

function automatic void drive_lc_owner_seed_sw_rw_en(lc_ctrl_pkg::lc_tx_t val);
lc_owner_seed_sw_rw_en_i = val;
endfunction

function automatic void drive_lc_dft_en(lc_ctrl_pkg::lc_tx_t val);
lc_dft_en_i = val;
endfunction
Expand Down
4 changes: 2 additions & 2 deletions hw/ip/otp_ctrl/data/otp_ctrl_mmap.hjson
Original file line number Diff line number Diff line change
Expand Up @@ -490,13 +490,13 @@
name: "CREATOR_ROOT_KEY_SHARE0",
inv_default: "<random>",
size: "32",
iskeymgr: true
iskeymgr_creator: true
}
{
name: "CREATOR_ROOT_KEY_SHARE1",
inv_default: "<random>",
size: "32"
iskeymgr: true
iskeymgr_creator: true
}
],
desc: '''Secret partition 2.
Expand Down
65 changes: 34 additions & 31 deletions hw/ip/otp_ctrl/data/otp_ctrl_part_pkg.sv.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -89,27 +89,29 @@ package otp_ctrl_part_pkg;
// Key index to use for scrambling.
key_sel_e key_sel;
// Attributes
logic secret; // Whether the partition is secret (and hence scrambled)
logic sw_digest; // Whether the partition has a software digest
logic hw_digest; // Whether the partition has a hardware digest
logic write_lock; // Whether the partition is write lockable (via digest)
logic read_lock; // Whether the partition is read lockable (via digest)
logic integrity; // Whether the partition is integrity protected
logic iskeymgr; // Whether the partition has any key material
logic secret; // Whether the partition is secret (and hence scrambled)
logic sw_digest; // Whether the partition has a software digest
logic hw_digest; // Whether the partition has a hardware digest
logic write_lock; // Whether the partition is write lockable (via digest)
logic read_lock; // Whether the partition is read lockable (via digest)
logic integrity; // Whether the partition is integrity protected
logic iskeymgr_creator; // Whether the partition has any creator key material
logic iskeymgr_owner; // Whether the partition has any owner key material
} part_info_t;

parameter part_info_t PartInfoDefault = '{
variant: Unbuffered,
offset: '0,
size: OtpByteAddrWidth'('hFF),
key_sel: key_sel_e'('0),
secret: 1'b0,
sw_digest: 1'b0,
hw_digest: 1'b0,
write_lock: 1'b0,
read_lock: 1'b0,
integrity: 1'b0,
iskeymgr: 1'b0
variant: Unbuffered,
offset: '0,
size: OtpByteAddrWidth'('hFF),
key_sel: key_sel_e'('0),
secret: 1'b0,
sw_digest: 1'b0,
hw_digest: 1'b0,
write_lock: 1'b0,
read_lock: 1'b0,
integrity: 1'b0,
iskeymgr_creator: 1'b0,
iskeymgr_owner: 1'b0
};
////////////////////////
Expand All @@ -120,17 +122,18 @@ package otp_ctrl_part_pkg;
% for part in otp_mmap.config["partitions"]:
// ${part["name"]}
'{
variant: ${part["variant"]},
offset: ${otp_mmap.config["otp"]["byte_addr_width"]}'d${part["offset"]},
size: ${part["size"]},
key_sel: ${part["key_sel"] if part["key_sel"] != "NoKey" else "key_sel_e'('0)"},
secret: 1'b${"1" if part["secret"] else "0"},
sw_digest: 1'b${"1" if part["sw_digest"] else "0"},
hw_digest: 1'b${"1" if part["hw_digest"] else "0"},
write_lock: 1'b${"1" if part["write_lock"].lower() == "digest" else "0"},
read_lock: 1'b${"1" if part["read_lock"].lower() == "digest" else "0"},
integrity: 1'b${"1" if part["integrity"] else "0"},
iskeymgr: 1'b${"1" if part["iskeymgr"] else "0"}
variant: ${part["variant"]},
offset: ${otp_mmap.config["otp"]["byte_addr_width"]}'d${part["offset"]},
size: ${part["size"]},
key_sel: ${part["key_sel"] if part["key_sel"] != "NoKey" else "key_sel_e'('0)"},
secret: 1'b${"1" if part["secret"] else "0"},
sw_digest: 1'b${"1" if part["sw_digest"] else "0"},
hw_digest: 1'b${"1" if part["hw_digest"] else "0"},
write_lock: 1'b${"1" if part["write_lock"].lower() == "digest" else "0"},
read_lock: 1'b${"1" if part["read_lock"].lower() == "digest" else "0"},
integrity: 1'b${"1" if part["integrity"] else "0"},
iskeymgr_creator: 1'b${"1" if part["iskeymgr_creator"] else "0"},
iskeymgr_owner: 1'b${"1" if part["iskeymgr_owner"] else "0"}
}${"" if loop.last else ","}
% endfor
};
Expand Down Expand Up @@ -305,14 +308,14 @@ package otp_ctrl_part_pkg;
part_name = Name.from_snake_case(part["name"])
part_name_camel = part_name.as_camel_case()
%>\
% if part["iskeymgr"]:
% if part["iskeymgr_creator"] or part["iskeymgr_owner"]:
valid = (part_digest[${part_name_camel}Idx] != 0);
% for item in part["items"]:
<%
item_name = Name.from_snake_case(item["name"])
item_name_camel = item_name.as_camel_case()
%>\
% if item["iskeymgr"]:
% if item["iskeymgr_creator"] or item["iskeymgr_owner"]:
otp_keymgr_key.${item["name"].lower()}_valid = valid;
if (lc_ctrl_pkg::lc_tx_test_true_strict(lc_seed_hw_rd_en)) begin
otp_keymgr_key.${item["name"].lower()} =
Expand Down
38 changes: 29 additions & 9 deletions hw/ip/otp_ctrl/data/otp_ctrl_scoreboard.sv.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -210,21 +210,21 @@ class otp_ctrl_scoreboard #(type CFG_T = otp_ctrl_env_cfg)
`DV_CHECK_EQ(cfg.otp_ctrl_vif.lc_data_o, exp_lc_data)

// ---------------------- Check keymgr_key_o output ---------------------------------
// Otp_keymgr outputs creator root key shares from the secret2 partition.
// 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;
% for part in otp_mmap.config["partitions"]:
<%
part_name = Name.from_snake_case(part["name"])
part_name_camel = part_name.as_camel_case()
%>\
% if part["iskeymgr"]:
% if part["iskeymgr_creator"] or part["iskeymgr_owner"]:
% for item in part["items"]:
<%
item_name = Name.from_snake_case(item["name"])
item_name_camel = item_name.as_camel_case()
%>\
% if item["iskeymgr"]:
% if item["iskeymgr_creator"] or item["iskeymgr_owner"]:
exp_keymgr_data.${item["name"].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()} =
Expand Down Expand Up @@ -745,12 +745,23 @@ class otp_ctrl_scoreboard #(type CFG_T = otp_ctrl_env_cfg)
// However, digest is always readable except SW partitions (Issue #5752).
(is_secret(dai_addr) && get_digest_reg_val(part_idx) != 0 &&
!is_digest(dai_addr)) ||
// If the partition has key material and lc_creator_seed_sw_rw is disable, then
// return access error.
(PartInfo[part_idx].iskeymgr && !is_digest(dai_addr) &&
// If the partition has creator key material and lc_creator_seed_sw_rw is
// disable, then return access error.
(PartInfo[part_idx].iskeymgr_creator && !is_digest(dai_addr) &&
cfg.otp_ctrl_vif.lc_creator_seed_sw_rw_en_i != lc_ctrl_pkg::On)) begin
predict_err(OtpDaiErrIdx, OtpAccessError);
predict_rdata(is_secret(dai_addr) || is_digest(dai_addr), 0, 0);
end else if (sw_read_lock ||
// Secret partitions cal digest can also lock read access.
// However, digest is always readable except SW partitions (Issue #5752).
(is_secret(dai_addr) && get_digest_reg_val(part_idx) != 0 &&
!is_digest(dai_addr)) ||
// If the partition has owner key material and lc_owner_seed_sw_rw is disable,
// then return access error.
(PartInfo[part_idx].iskeymgr_owner && !is_digest(dai_addr) &&
cfg.otp_ctrl_vif.lc_owner_seed_sw_rw_en_i != lc_ctrl_pkg::On)) begin
predict_err(OtpDaiErrIdx, OtpAccessError);
predict_rdata(is_secret(dai_addr) || is_digest(dai_addr), 0, 0);

end else begin
bit [TL_DW-1:0] read_out0, read_out1;
Expand Down Expand Up @@ -805,8 +816,13 @@ class otp_ctrl_scoreboard #(type CFG_T = otp_ctrl_env_cfg)
is_write_locked = 0;
end

if (is_write_locked || (PartInfo[part_idx].iskeymgr && !is_digest(dai_addr) &&
cfg.otp_ctrl_vif.lc_creator_seed_sw_rw_en_i != lc_ctrl_pkg::On)) begin
if (is_write_locked || (PartInfo[part_idx].iskeymgr_creator &&
!is_digest(dai_addr) &&
cfg.otp_ctrl_vif.lc_creator_seed_sw_rw_en_i != lc_ctrl_pkg::On)) begin
predict_err(OtpDaiErrIdx, OtpAccessError);
end else if (is_write_locked || (PartInfo[part_idx].iskeymgr_owner &&
!is_digest(dai_addr) &&
cfg.otp_ctrl_vif.lc_owner_seed_sw_rw_en_i != lc_ctrl_pkg::On)) begin
predict_err(OtpDaiErrIdx, OtpAccessError);
end else begin
predict_no_err(OtpDaiErrIdx);
Expand Down Expand Up @@ -1182,10 +1198,14 @@ class otp_ctrl_scoreboard #(type CFG_T = otp_ctrl_env_cfg)
if (!part_has_hw_digest(part_idx) || get_digest_reg_val(part_idx) != 0) begin
predict_err(OtpDaiErrIdx, OtpAccessError);
return;
end else if (PartInfo[part_idx].iskeymgr &&
end else if (PartInfo[part_idx].iskeymgr_creator &&
cfg.otp_ctrl_vif.lc_creator_seed_sw_rw_en_i != lc_ctrl_pkg::On) begin
predict_err(OtpDaiErrIdx, OtpAccessError);
return;
end else if (PartInfo[part_idx].iskeymgr_owner &&
cfg.otp_ctrl_vif.lc_owner_seed_sw_rw_en_i != lc_ctrl_pkg::On) begin
predict_err(OtpDaiErrIdx, OtpAccessError);
return;
end else begin
predict_no_err(OtpDaiErrIdx);
dai_digest_ip = part_idx;
Expand Down
1 change: 1 addition & 0 deletions hw/ip/otp_ctrl/doc/interfaces.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ Referring to the [Comportable guideline for peripheral device functionality](htt
| 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_dft_en | lc_ctrl_pkg::lc_tx | uni | rcv | 1 | Test enable qualifier coming from life cycle controller. This signals enables the TL-UL access port to the proprietary OTP IP. |
| 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. |
Expand Down
7 changes: 6 additions & 1 deletion hw/ip/otp_ctrl/dv/env/otp_ctrl_if.sv
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ interface otp_ctrl_if(input clk_i, input rst_ni);
// Inputs to DUT
logic pwr_otp_init_i, scan_en_i, scan_rst_ni, ext_voltage_h_io;
lc_ctrl_pkg::lc_tx_t lc_dft_en_i, lc_escalate_en_i, lc_check_byp_en_i,
lc_creator_seed_sw_rw_en_i, lc_seed_hw_rd_en_i;
lc_creator_seed_sw_rw_en_i, lc_owner_seed_sw_rw_en_i,
lc_seed_hw_rd_en_i;
prim_mubi_pkg::mubi4_t scanmode_i;
otp_ast_rsp_t otp_ast_pwr_seq_h_i;
ast_pkg::ast_obs_ctrl_t obs_ctrl_i;
Expand Down Expand Up @@ -134,6 +135,10 @@ interface otp_ctrl_if(input clk_i, input rst_ni);
lc_creator_seed_sw_rw_en_i = val;
endfunction

function automatic void drive_lc_owner_seed_sw_rw_en(lc_ctrl_pkg::lc_tx_t val);
lc_owner_seed_sw_rw_en_i = val;
endfunction

function automatic void drive_lc_dft_en(lc_ctrl_pkg::lc_tx_t val);
lc_dft_en_i = val;
endfunction
Expand Down
Loading

0 comments on commit 3705405

Please sign in to comment.