[dif/alert_handler] Add class locking DIFs.

This commit adds two DIFs to lock the configuration CSRs of a specific
alert class, and check if a specific alert class' CSRs are locked. This
gives users fine grained control over the locking of a specific alert
class' configuration.

This addresses a task in #9899.

Signed-off-by: Timothy Trippel <ttrippel@google.com>
diff --git a/sw/device/lib/dif/dif_alert_handler.c b/sw/device/lib/dif/dif_alert_handler.c
index 62433c1..2266287 100644
--- a/sw/device/lib/dif/dif_alert_handler.c
+++ b/sw/device/lib/dif/dif_alert_handler.c
@@ -20,13 +20,21 @@
               "Expected seven local alerts!");
 
 /**
- * Macro for generating the case statements for local alert lock CSRS.
+ * Macro for generating the case statements for local alert lock CSRs.
  */
 #define LOC_ALERT_REGWENS_CASE_(loc_alert_, value_)                       \
   case loc_alert_:                                                        \
     regwen_offset = ALERT_HANDLER_LOC_ALERT_REGWEN_##value_##_REG_OFFSET; \
     break;
 
+/**
+ * Macro for generating the case statements for class lock CSRs.
+ */
+#define ALERT_CLASS_REGWENS_CASE_(class_, value_)                    \
+  case kDifAlertHandlerClass##class_:                                \
+    regwen_offset = ALERT_HANDLER_CLASS##class_##_REGWEN_REG_OFFSET; \
+    break;
+
 // TODO(#9899): add static assert for accumulator_threshold field size
 
 /**
@@ -552,6 +560,44 @@
   return kDifOk;
 }
 
+dif_result_t dif_alert_handler_lock_class(
+    const dif_alert_handler_t *alert_handler,
+    dif_alert_handler_class_t alert_class) {
+  if (alert_handler == NULL) {
+    return kDifBadArg;
+  }
+
+  ptrdiff_t regwen_offset;
+  switch (alert_class) {
+    LIST_OF_CLASSES(ALERT_CLASS_REGWENS_CASE_)
+    default:
+      return kDifBadArg;
+  }
+
+  mmio_region_write32(alert_handler->base_addr, regwen_offset, 0);
+
+  return kDifOk;
+}
+
+dif_result_t dif_alert_handler_is_class_locked(
+    const dif_alert_handler_t *alert_handler,
+    dif_alert_handler_class_t alert_class, bool *is_locked) {
+  if (alert_handler == NULL || is_locked == NULL) {
+    return kDifBadArg;
+  }
+
+  ptrdiff_t regwen_offset;
+  switch (alert_class) {
+    LIST_OF_CLASSES(ALERT_CLASS_REGWENS_CASE_)
+    default:
+      return kDifBadArg;
+  }
+
+  *is_locked = !mmio_region_read32(alert_handler->base_addr, regwen_offset);
+
+  return kDifOk;
+}
+
 dif_result_t dif_alert_handler_lock_ping_timer(
     const dif_alert_handler_t *alert_handler) {
   if (alert_handler == NULL) {
@@ -947,3 +993,4 @@
 }
 
 #undef LOC_ALERT_REGWENS_CASE_
+#undef ALERT_CLASS_REGWENS_CASE_
diff --git a/sw/device/lib/dif/dif_alert_handler.h b/sw/device/lib/dif/dif_alert_handler.h
index 4ad1656..8116c5f 100644
--- a/sw/device/lib/dif/dif_alert_handler.h
+++ b/sw/device/lib/dif/dif_alert_handler.h
@@ -493,6 +493,37 @@
     dif_alert_handler_local_alert_t local_alert, bool *is_locked);
 
 /**
+ * Locks out an alert handler class configuration.
+ *
+ * This operation cannot be undone, and should be performed at the end of
+ * configuring the alert handler in early boot.
+ *
+ * This function is reentrant: calling it while functionality is locked will
+ * have no effect and return `kDifOk`.
+ *
+ * @param alert_handler An alert handler handle.
+ * @param alert_class The alert class to lock.
+ * @return The result of the operation.
+ */
+OT_WARN_UNUSED_RESULT
+dif_result_t dif_alert_handler_lock_class(
+    const dif_alert_handler_t *alert_handler,
+    dif_alert_handler_class_t alert_class);
+
+/**
+ * Checks whether an alert handler's class is locked.
+ *
+ * @param alert_handler An alert handler handle.
+ * @param alert_class The alert class to check is locked.
+ * @param is_locked Out-param for the locked state.
+ * @return The result of the operation.
+ */
+OT_WARN_UNUSED_RESULT
+dif_result_t dif_alert_handler_is_class_locked(
+    const dif_alert_handler_t *alert_handler,
+    dif_alert_handler_class_t alert_class, bool *is_locked);
+
+/**
  * Locks out alert handler ping timer configuration.
  *
  * This operation cannot be undone, and should be performed at the end of
diff --git a/sw/device/lib/dif/dif_alert_handler_unittest.cc b/sw/device/lib/dif/dif_alert_handler_unittest.cc
index 371934f..6a08682 100644
--- a/sw/device/lib/dif/dif_alert_handler_unittest.cc
+++ b/sw/device/lib/dif/dif_alert_handler_unittest.cc
@@ -7,6 +7,8 @@
 #include <cstring>
 #include <limits>
 #include <ostream>
+#include <tuple>
+#include <vector>
 
 #include "gtest/gtest.h"
 #include "sw/device/lib/base/mmio.h"
@@ -731,6 +733,75 @@
             kDifBadArg);
 }
 
+class ClassLockTest : public AlertHandlerTest,
+                      public testing::WithParamInterface<
+                          std::tuple<dif_alert_handler_class_t, uint32_t>> {};
+
+static std::vector<std::tuple<dif_alert_handler_class_t, uint32_t>>
+    class_regwen_pairs{
+        std::tuple<dif_alert_handler_class_t, uint32_t>{
+            kDifAlertHandlerClassA, ALERT_HANDLER_CLASSA_REGWEN_REG_OFFSET},
+        std::tuple<dif_alert_handler_class_t, uint32_t>{
+            kDifAlertHandlerClassB, ALERT_HANDLER_CLASSB_REGWEN_REG_OFFSET},
+        std::tuple<dif_alert_handler_class_t, uint32_t>{
+            kDifAlertHandlerClassC, ALERT_HANDLER_CLASSC_REGWEN_REG_OFFSET},
+        std::tuple<dif_alert_handler_class_t, uint32_t>{
+            kDifAlertHandlerClassD, ALERT_HANDLER_CLASSD_REGWEN_REG_OFFSET}};
+
+TEST_P(ClassLockTest, IsClassLocked) {
+  dif_alert_handler_class_t alert_class = std::get<0>(GetParam());
+  uint32_t regwen_offset = std::get<1>(GetParam());
+  bool is_locked = true;
+
+  EXPECT_READ32(regwen_offset, ALERT_HANDLER_CLASSA_REGWEN_REG_RESVAL);
+  EXPECT_EQ(dif_alert_handler_is_class_locked(&alert_handler_, alert_class,
+                                              &is_locked),
+            kDifOk);
+  EXPECT_FALSE(is_locked);
+
+  is_locked = false;
+  EXPECT_READ32(regwen_offset, 0);
+  EXPECT_EQ(dif_alert_handler_is_class_locked(&alert_handler_, alert_class,
+                                              &is_locked),
+            kDifOk);
+  EXPECT_TRUE(is_locked);
+}
+
+INSTANTIATE_TEST_SUITE_P(AllClassesLockedAndUnlocked, ClassLockTest,
+                         testing::ValuesIn(class_regwen_pairs));
+
+TEST_P(ClassLockTest, LockClass) {
+  dif_alert_handler_class_t alert_class = std::get<0>(GetParam());
+  uint32_t regwen_offset = std::get<1>(GetParam());
+
+  EXPECT_WRITE32(regwen_offset, 0);
+  EXPECT_EQ(dif_alert_handler_lock_class(&alert_handler_, alert_class), kDifOk);
+}
+
+INSTANTIATE_TEST_SUITE_P(LockAllClasses, ClassLockTest,
+                         testing::ValuesIn(class_regwen_pairs));
+
+TEST_F(ClassLockTest, BadArgs) {
+  bool is_locked;
+  EXPECT_EQ(dif_alert_handler_is_class_locked(nullptr, kDifAlertHandlerClassA,
+                                              &is_locked),
+            kDifBadArg);
+  EXPECT_EQ(dif_alert_handler_is_class_locked(
+                &alert_handler_,
+                static_cast<dif_alert_handler_class_t>(kClasses), &is_locked),
+            kDifBadArg);
+  EXPECT_EQ(dif_alert_handler_is_class_locked(&alert_handler_,
+                                              kDifAlertHandlerClassD, nullptr),
+            kDifBadArg);
+
+  EXPECT_EQ(dif_alert_handler_lock_class(nullptr, kDifAlertHandlerClassA),
+            kDifBadArg);
+  EXPECT_EQ(
+      dif_alert_handler_lock_class(
+          &alert_handler_, static_cast<dif_alert_handler_class_t>(kClasses)),
+      kDifBadArg);
+}
+
 class PingTimerLockTest : public AlertHandlerTest {};
 
 TEST_F(PingTimerLockTest, IsPingTimerLocked) {