[otp_ctrl] Add multiple RW access checks to DAI FSM
Signed-off-by: Michael Schaffner <msf@opentitan.org>
diff --git a/hw/ip/otp_ctrl/rtl/otp_ctrl_dai.sv b/hw/ip/otp_ctrl/rtl/otp_ctrl_dai.sv
index ff2118f..b8f863b 100644
--- a/hw/ip/otp_ctrl/rtl/otp_ctrl_dai.sv
+++ b/hw/ip/otp_ctrl/rtl/otp_ctrl_dai.sv
@@ -289,24 +289,33 @@
// transaction fails, latch the OTP error code, and jump to
// terminal error state.
ReadWaitSt: begin
- if (otp_rvalid_i) begin
- // Check OTP return code.
- if ((!(otp_err_i inside {NoError, MacroEccCorrError}))) begin
- state_d = ErrorSt;
- error_d = otp_err_i;
- end else begin
- data_en = 1'b1;
- if (PartInfo[part_idx].secret) begin
- state_d = DescrSt;
- end else begin
- state_d = IdleSt;
- dai_cmd_done_o = 1'b1;
- end
- // Signal soft ECC errors, but do not go into terminal error state.
- if (otp_err_i == MacroEccCorrError) begin
+ // Continuously check read access and bail out if this is not consistent.
+ if (part_access_i[part_idx].read_lock == Unlocked) begin
+ if (otp_rvalid_i) begin
+ // Check OTP return code.
+ if ((!(otp_err_i inside {NoError, MacroEccCorrError}))) begin
+ state_d = ErrorSt;
error_d = otp_err_i;
+ end else begin
+ data_en = 1'b1;
+ if (PartInfo[part_idx].secret) begin
+ state_d = DescrSt;
+ end else begin
+ state_d = IdleSt;
+ dai_cmd_done_o = 1'b1;
+ end
+ // Signal soft ECC errors, but do not go into terminal error state.
+ if (otp_err_i == MacroEccCorrError) begin
+ error_d = otp_err_i;
+ end
end
end
+ // At this point, this check MUST succeed - otherwise this means that
+ // there was a tampering attempt. Hence we go into a terminal error state
+ // when this check fails.
+ end else begin
+ state_d = ErrorSt;
+ error_d = FsmStateError;
end
end
///////////////////////////////////////////////////////////////////
@@ -369,19 +378,37 @@
// OTP transaction fails, latch the OTP error code, and jump to
// terminal error state.
WriteWaitSt: begin
- if (otp_rvalid_i) begin
- // Check OTP return code. Note that non-blank errors are recoverable.
- if ((!(otp_err_i inside {NoError, MacroWriteBlankError}))) begin
- state_d = ErrorSt;
- error_d = otp_err_i;
- end else begin
- state_d = IdleSt;
- dai_cmd_done_o = 1'b1;
- // Signal non-blank state, but do not go to terminal error state.
- if (otp_err_i == MacroWriteBlankError) begin
+ // Continuously check write access and bail out if this is not consistent.
+ if (part_access_i[part_idx].write_lock == Unlocked &&
+ // If this is a HW digest write to a buffered partition.
+ ((PartInfo[part_idx].variant == Buffered && PartInfo[part_idx].hw_digest &&
+ base_sel_q == PartOffset && otp_addr_o == digest_addr_lut[part_idx]) ||
+ // If this is a non HW digest write to a buffered partition.
+ (PartInfo[part_idx].variant == Buffered && PartInfo[part_idx].hw_digest &&
+ base_sel_q == DaiOffset && otp_addr_o < digest_addr_lut[part_idx]) ||
+ // If this is a write to an unbuffered partition
+ (PartInfo[part_idx].variant != Buffered && base_sel_q == DaiOffset))) begin
+
+ if (otp_rvalid_i) begin
+ // Check OTP return code. Note that non-blank errors are recoverable.
+ if ((!(otp_err_i inside {NoError, MacroWriteBlankError}))) begin
+ state_d = ErrorSt;
error_d = otp_err_i;
+ end else begin
+ state_d = IdleSt;
+ dai_cmd_done_o = 1'b1;
+ // Signal non-blank state, but do not go to terminal error state.
+ if (otp_err_i == MacroWriteBlankError) begin
+ error_d = otp_err_i;
+ end
end
end
+ // At this point, this check MUST succeed - otherwise this means that
+ // there was a tampering attempt. Hence we go into a terminal error state
+ // when this check fails.
+ end else begin
+ state_d = ErrorSt;
+ error_d = FsmStateError;
end
end
///////////////////////////////////////////////////////////////////
@@ -391,11 +418,23 @@
// the mutex by deasserting scrmbl_mtx_req_o.
ScrSt: begin
scrmbl_mtx_req_o = 1'b1;
- scrmbl_valid_o = 1'b1;
- scrmbl_cmd_o = Encrypt;
- scrmbl_sel_o = PartInfo[part_idx].key_sel;
- if (scrmbl_mtx_gnt_i && scrmbl_ready_i) begin
- state_d = ScrWaitSt;
+ // Check write access and bail out if this is not consistent.
+ if (part_access_i[part_idx].write_lock == Unlocked &&
+ // If this is a non HW digest write to a buffered partition.
+ (PartInfo[part_idx].variant == Buffered && PartInfo[part_idx].secret &&
+ PartInfo[part_idx].hw_digest && base_sel_q == DaiOffset &&
+ otp_addr_o < digest_addr_lut[part_idx])) begin
+
+ scrmbl_valid_o = 1'b1;
+ scrmbl_cmd_o = Encrypt;
+ scrmbl_sel_o = PartInfo[part_idx].key_sel;
+ if (scrmbl_mtx_gnt_i && scrmbl_ready_i) begin
+ state_d = ScrWaitSt;
+ end
+ end else begin
+ state_d = IdleSt;
+ error_d = AccessError; // Signal this error, but do not go into terminal error state.
+ dai_cmd_done_o = 1'b1;
end
end
///////////////////////////////////////////////////////////////////
@@ -403,10 +442,23 @@
// the mutex lock upon leaving this state.
ScrWaitSt: begin
scrmbl_mtx_req_o = 1'b1;
- data_sel = ScrmblData;
- if (scrmbl_valid_i) begin
- state_d = WriteSt;
- data_en = 1'b1;
+ // Continously check write access and bail out if this is not consistent.
+ if (part_access_i[part_idx].write_lock == Unlocked &&
+ // If this is a non HW digest write to a buffered partition.
+ (PartInfo[part_idx].variant == Buffered && PartInfo[part_idx].secret &&
+ PartInfo[part_idx].hw_digest && base_sel_q == DaiOffset &&
+ otp_addr_o < digest_addr_lut[part_idx])) begin
+ data_sel = ScrmblData;
+ if (scrmbl_valid_i) begin
+ state_d = WriteSt;
+ data_en = 1'b1;
+ end
+ // At this point, this check MUST succeed - otherwise this means that
+ // there was a tampering attempt. Hence we go into a terminal error state
+ // when this check fails.
+ end else begin
+ state_d = ErrorSt;
+ error_d = FsmStateError;
end
end
///////////////////////////////////////////////////////////////////