[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 ///////////////////////////////////////////////////////////////////