[dif, kmac] Add function to read the kmac error
This fixes lowRISC/OpenTitan#6248
Signed-off-by: Douglas Reis <doreis@lowrisc.org>
diff --git a/sw/device/lib/dif/dif_kmac.c b/sw/device/lib/dif/dif_kmac.c
index 180f965..0f2a31c 100644
--- a/sw/device/lib/dif/dif_kmac.c
+++ b/sw/device/lib/dif/dif_kmac.c
@@ -105,7 +105,7 @@
*
* If the hardware is not idle then the `CFG` register is locked.
*
- * @param params Hardware parameters.
+ * @param kmac Handle.
* @returns Whether the hardware is currently idle or not.
*/
static bool is_state_idle(const dif_kmac_t *kmac) {
@@ -119,7 +119,7 @@
*
* Note that writes to the message FIFO may still block if it is full.
*
- * @param params Hardware parameters.
+ * @param kmac Handle.
* @returns Whether the hardware is currently absorbing or not.
*/
static bool is_state_absorb(const dif_kmac_t *kmac) {
@@ -131,7 +131,7 @@
* Report whether the hardware is currently in the squeeze state which means
* that the output state is valid and may be read by software.
*
- * @param params Hardware parameters.
+ * @param kmac Handle.
* @returns Whether the hardware is currently in the squeeze state or not.
*/
static bool is_state_squeeze(const dif_kmac_t *kmac) {
@@ -139,6 +139,38 @@
return bitfield_bit32_read(reg, KMAC_STATUS_SHA3_SQUEEZE_BIT);
}
+/**
+ * Report whether the hardware has indicated a error.
+ *
+ * @param kmac Handle.
+ * @returns True if an error occurred, False otherwise.
+ */
+static bool has_error_occurred(const dif_kmac_t *kmac) {
+ uint32_t reg =
+ mmio_region_read32(kmac->base_addr, KMAC_INTR_STATE_REG_OFFSET);
+ return bitfield_bit32_read(reg, KMAC_INTR_STATE_KMAC_ERR_BIT);
+}
+
+/**
+ * Poll until the status register is in the 'absorb' state or int state register
+ * has indicated an error.
+ *
+ * @param kmac
+ * @return dif_result
+ */
+static dif_result_t poll_state(const dif_kmac_t *kmac, uint32_t flag) {
+ while (true) {
+ uint32_t reg = mmio_region_read32(kmac->base_addr, KMAC_STATUS_REG_OFFSET);
+ if (bitfield_bit32_read(reg, flag)) {
+ break;
+ }
+ if (has_error_occurred(kmac)) {
+ return kDifError;
+ }
+ }
+ return kDifOk;
+}
+
dif_result_t dif_kmac_configure(dif_kmac_t *kmac, dif_kmac_config_t config) {
if (kmac == NULL) {
return kDifBadArg;
@@ -287,14 +319,7 @@
mmio_region_write32(kmac->base_addr, KMAC_CMD_REG_OFFSET, cmd_reg);
// Poll until the status register is in the 'absorb' state.
- while (true) {
- if (is_state_absorb(kmac)) {
- break;
- }
- // TODO(#6248): check for error.
- }
-
- return kDifOk;
+ return poll_state(kmac, KMAC_STATUS_SHA3_ABSORB_BIT);
}
dif_result_t dif_kmac_mode_shake_start(
@@ -343,15 +368,7 @@
bitfield_field32_write(0, KMAC_CMD_CMD_FIELD, KMAC_CMD_CMD_VALUE_START);
mmio_region_write32(kmac->base_addr, KMAC_CMD_REG_OFFSET, cmd_reg);
- // Poll until the status register is in the 'absorb' state.
- while (true) {
- if (is_state_absorb(kmac)) {
- break;
- }
- // TODO(#6248): check for error.
- }
-
- return kDifOk;
+ return poll_state(kmac, KMAC_STATUS_SHA3_ABSORB_BIT);
}
dif_result_t dif_kmac_mode_cshake_start(
@@ -451,15 +468,7 @@
bitfield_field32_write(0, KMAC_CMD_CMD_FIELD, KMAC_CMD_CMD_VALUE_START);
mmio_region_write32(kmac->base_addr, KMAC_CMD_REG_OFFSET, cmd_reg);
- // Poll until the status register is in the 'absorb' state.
- while (true) {
- if (is_state_absorb(kmac)) {
- break;
- }
- // TODO(#6248): check for error.
- }
-
- return kDifOk;
+ return poll_state(kmac, KMAC_STATUS_SHA3_ABSORB_BIT);
}
dif_result_t dif_kmac_mode_kmac_start(
@@ -574,13 +583,7 @@
bitfield_field32_write(0, KMAC_CMD_CMD_FIELD, KMAC_CMD_CMD_VALUE_START);
mmio_region_write32(kmac->base_addr, KMAC_CMD_REG_OFFSET, cmd_reg);
- // Poll until the status register is in the 'absorb' state.
- while (true) {
- if (is_state_absorb(kmac)) {
- break;
- }
- // TODO(#6248): check for error.
- }
+ return poll_state(kmac, KMAC_STATUS_SHA3_ABSORB_BIT);
return kDifOk;
}
@@ -805,3 +808,13 @@
return kDifOk;
}
+
+dif_result_t dif_kmac_get_error(const dif_kmac_t *kmac,
+ dif_kmac_error_t *error) {
+ if (kmac == NULL || error == NULL) {
+ return kDifBadArg;
+ }
+
+ *error = mmio_region_read32(kmac->base_addr, KMAC_ERR_CODE_REG_OFFSET);
+ return kDifOk;
+}
diff --git a/sw/device/lib/dif/dif_kmac.h b/sw/device/lib/dif/dif_kmac.h
index 89a96f8..12c9c18 100644
--- a/sw/device/lib/dif/dif_kmac.h
+++ b/sw/device/lib/dif/dif_kmac.h
@@ -666,7 +666,11 @@
dif_kmac_operation_state_t *operation_state);
/**
- * Get the current error code.
+ * Read the kmac error register to get the error code indicated the interrupt
+ * state.
+ *
+ * This function should be called in case of any of the `start` functions
+ * returns `kDifError`.
*
* @param kmac A KMAC handle.
* @param[out] error The current error code.
diff --git a/sw/device/lib/dif/dif_kmac_unittest.cc b/sw/device/lib/dif/dif_kmac_unittest.cc
index 3c77793..f56f6b5 100644
--- a/sw/device/lib/dif/dif_kmac_unittest.cc
+++ b/sw/device/lib/dif/dif_kmac_unittest.cc
@@ -717,4 +717,33 @@
EXPECT_DIF_BADARG(dif_kmac_get_status(&kmac_, nullptr));
}
+class KmacGetErrorTest : public KmacTest {
+ protected:
+ static constexpr std::array<dif_kmac_error_t, 7> kErrors = {
+ kDifErrorNone,
+ kDifErrorKeyNotValid,
+ kDifErrorSoftwarePushedMessageFifo,
+ kDifErrorSoftwarePushedWrongCommand,
+ kDifErrorEntropyWaitTimerExpired,
+ kDifErrorEntropyModeIncorrect,
+ kDifErrorUnknownError};
+ dif_kmac_error_t error_;
+ KmacGetErrorTest() { op_state_.squeezing = true; }
+};
+constexpr std::array<dif_kmac_error_t, 7> KmacGetErrorTest::kErrors;
+
+TEST_F(KmacGetErrorTest, Success) {
+ for (auto err : kErrors) {
+ EXPECT_READ32(KMAC_ERR_CODE_REG_OFFSET, err);
+ EXPECT_DIF_OK(dif_kmac_get_error(&kmac_, &error_));
+ EXPECT_EQ(error_, err);
+ }
+}
+
+TEST_F(KmacGetErrorTest, BadArg) {
+ EXPECT_DIF_BADARG(dif_kmac_get_error(nullptr, &error_));
+
+ EXPECT_DIF_BADARG(dif_kmac_get_error(&kmac_, nullptr));
+}
+
} // namespace dif_kmac_unittest