[dif/alert_manager] Add a header for dif_alert_handler

Signed-off-by: Miguel Young de la Sota <mcyoung@google.com>
diff --git a/sw/device/lib/dif/dif_alert_handler.h b/sw/device/lib/dif/dif_alert_handler.h
new file mode 100644
index 0000000..816f416
--- /dev/null
+++ b/sw/device/lib/dif/dif_alert_handler.h
@@ -0,0 +1,699 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#ifndef OPENTITAN_SW_DEVICE_LIB_DIF_DIF_ALERT_HANDLER_H_
+#define OPENTITAN_SW_DEVICE_LIB_DIF_DIF_ALERT_HANDLER_H_
+
+/**
+ * @file
+ * @brief <a href="/hw/ip/alert_handler/doc/">Alert handler</a> Device Interface
+ * Functions
+ */
+
+#include <stdint.h>
+
+#include "sw/device/lib/base/mmio.h"
+#include "sw/device/lib/dif/dif_warn_unused_result.h"
+
+// Header Extern Guard (so header can be used from C and C++)
+#ifdef __cplusplus
+extern "C" {
+#endif  // __cplusplus
+
+/**
+ * A toggle state: enabled, or disabled.
+ *
+ * This enum may be used instead of a `bool` when describing an enabled/disabled
+ * state.
+ */
+typedef enum dif_alert_handler_toggle {
+  /**
+   * The "enabled" state.
+   */
+  kDifAlertHandlerToggleEnabled,
+  /**
+   * The "disabled" state.
+   */
+  kDifAlertHandlerToggleDisabled,
+} dif_alert_handler_toggle_t;
+
+/**
+ * Hardware instantiation parameters for the alert handler.
+ *
+ * This struct describes information about the underlying hardware that is
+ * not determined until the hardware design is used as part of a top-level
+ * design.
+ */
+typedef struct dif_alert_handler_params {
+  /**
+   * The base address for the alert handler hardware registers.
+   */
+  mmio_region_t base_addr;
+  /**
+   * The configured number of alerts.
+   *
+   * This value is fixed by the hardware, but not known to this library.
+   */
+  uint32_t alert_count;
+  /**
+   * The configured number of escalation signals.
+   *
+   * This value is fixed by the hardware, but not known to this library.
+   */
+  // It's actually fixed at 4 right now but this is likely to become
+  // configurable too.
+  uint32_t escalation_signal_count;
+} dif_alert_handler_params_t;
+
+/**
+ * A handle to alert handler.
+ *
+ * This type should be treated as opaque by users.
+ */
+typedef struct dif_alert_handler {
+  dif_alert_handler_params_t params;
+} dif_alert_handler_t;
+
+/**
+ * The result of an alert handler operation.
+ */
+typedef enum dif_alert_handler_result {
+  /**
+   * Indicates that the operation succeeded.
+   */
+  kDifAlertHandlerOk = 0,
+  /**
+   * Indicates some unspecified failure.
+   */
+  kDifAlertHandlerError = 1,
+  /**
+   * Indicates that some parameter passed into a function failed a
+   * precondition.
+   *
+   * When this value is returned, no hardware operations occured.
+   */
+  kDifAlertHandlerBadArg = 2,
+} dif_alert_handler_result_t;
+
+/**
+ * The result of an alert handler configuration operation.
+ */
+typedef enum dif_alert_handler_config_result {
+  /**
+   * Indicates that the operation succeeded.
+   */
+  kDifAlertHandlerConfigOk = kDifAlertHandlerOk,
+  /**
+   * Indicates some unspecified failure.
+   */
+  kDifAlertHandlerConfigError = kDifAlertHandlerError,
+  /**
+   * Indicates that some parameter passed into a function failed a
+   * precondition.
+   *
+   * When this value is returned, no hardware operations occured.
+   */
+  kDifAlertHandlerConfigBadArg = kDifAlertHandlerBadArg,
+  /**
+   * Indicates that this operation has been locked out, and can never
+   * succeed until hardware reset.
+   */
+  kDifAlertHandlerConfigLocked = 3,
+} dif_alert_handler_config_result_t;
+
+/**
+ * An alert class.
+ *
+ * An alert class roughly specifies how to deal with an alert. The class
+ * determines which interrupt handler is fired for an alert, as well as the
+ * fine-grained details of the escalation policy, for when the processor
+ * fails to respond to an alert quickly enough.
+ *
+ * Alert classes serve as the alert handler's IRQ types. There is one IRQ for
+ * each class. Whenever an alert fires, the corresponding class's IRQ is
+ * serviced by the processor (if enabled).
+ */
+typedef enum dif_alert_handler_class {
+  /**
+   * Alert class "A".
+   */
+  kDifAlertHandlerClassA = 0,
+  /**
+   * Alert class "B".
+   */
+  kDifAlertHandlerClassB = 1,
+  /**
+   * Alert class "C".
+   */
+  kDifAlertHandlerClassC = 2,
+  /**
+   * Alert class "D".
+   */
+  kDifAlertHandlerClassD = 3,
+} dif_alert_handler_class_t;
+
+/**
+ * A snapshot of the enablement state of the interrupts for alert handler.
+ *
+ * This is an opaque type, to be used with the
+ * `dif_alert_handler_irq_disable_all()` and
+ * `dif_alert_handler_irq_restore_all()` functions.
+ */
+typedef uint32_t dif_alert_handler_irq_snapshot_t;
+
+/**
+ * An alert, identified by a numeric id.
+ *
+ * Alerts are hardware-level events indicating that something catastrophic
+ * has happened. An alert handler handle.consumes alerts, classifies them to a
+ * particular `dif_alert_handler_class_t`, and uses policy information attached
+ * to that class to handle it.
+ *
+ * The number of alerts is configurable at hardware-synthesis time, and is
+ * specified by the software when initializing a `dif_alert_handler_t`.
+ */
+typedef uint32_t dif_alert_handler_alert_t;
+
+/**
+ * A local alert originating from within the alert handler itself.
+ *
+ * A local alert is exactly the same as a normal `dif_alert_handler_alert_t`,
+ * except that they use different functions for setting up classification and
+ * for getting causes.
+ */
+typedef enum dif_alert_handler_local_alert {
+  kDifAlertHandlerLocalAlertAlertPingFail,
+  kDifAlertHandlerLocalAlertEscalationPingFail,
+  kDifAlertHandlerLocalAlertAlertIntegrityFail,
+  kDifAlertHandlerLocalAlertEscalationIntegrityFail,
+} dif_alert_handler_local_alert_t;
+
+/**
+ * An escalation signal, identified by a numeric id.
+ *
+ * An escalation signal is a generic "response" to failing to handle a
+ * catastrophic event. What each signal means is determined by the chip.
+ *
+ * A `dif_alert_handler_class_t` can be configured to raise escalation signals
+ * as part of its policy.
+ */
+typedef uint32_t dif_alert_handler_signal_t;
+
+/**
+ * An alert class state.
+ *
+ * This enum describes the sequence of states in the *escalation protocol*,
+ * which triggers under two different conditions:
+ * - If too many alerts of a particular class accumualte.
+ * - If the software IRQ handler for that class times out.
+ *
+ * When either of these conditions is reached, phase 0 begins. This may trigger
+ * an escalation signal, and after a configured duration, proceed to phase 1.
+ * This process repeats until phase 3 ends, at which point the class enters a
+ * "bricked" terminal state, which cannot be exited except by reset.
+ *
+ * At any point, software may end the escalation protocol by calling
+ * `dif_alert_handler_escalation_clear()` (unless clearing is disabled).
+ * Successfully calling this function, or clearing the IRQ on time, will reset
+ * back to the idle state. Note that this function cannot clear the terminal
+ * state; that state can only be cleared by resetting the chip.
+ */
+typedef enum dif_alert_handler_class_state {
+  /**
+   * The initial, idle state.
+   */
+  kDifAlertHandlerClassStateIdle,
+  /**
+   * The "timeout" state, that is, the IRQ has been fired and the clock is
+   * ticking for the processor to handle the alert.
+   */
+  kDifAlertHandlerClassStateTimeout,
+
+  /**
+   * The zeroth escalation phase.
+   */
+  kDifAlertHandlerClassStatePhase0,
+  /**
+   * The first escalation phase.
+   */
+  kDifAlertHandlerClassStatePhase1,
+  /**
+   * The second escalation phase.
+   */
+  kDifAlertHandlerClassStatePhase2,
+  /**
+   * The third escalation phase.
+   */
+  kDifAlertHandlerClassStatePhase3,
+
+  /**
+   * The terminal state. Most configurations will never reach this state, since
+   * one of the previous phases will use an escalation signal to reset the
+   * device.
+   */
+  kDifAlertHandlerClassStateTerminal,
+} dif_alert_handler_class_state_t;
+
+/**
+ * Runtime configuration for responding to a given escalation phase.
+ *
+ * See `dif_alert_manager_class_config_t`.
+ */
+typedef struct dif_alert_handler_class_phase_signal {
+  /**
+   * The phase this configuration describes.
+   *
+   * It is an error for this to not be one of the `Phase` constants in
+   * `dif_alert_handler_class_state_t`.
+   */
+  dif_alert_handler_class_state_t phase;
+  /**
+   * The signal that should be triggered when this phase begins.
+   */
+  dif_alert_handler_signal_t signal;
+} dif_alert_handler_class_phase_signal_t;
+
+/**
+ * Runtime configuration for the duration of an escalation phase.
+ *
+ * See `dif_alert_manager_class_config_t`.
+ */
+typedef struct dif_alert_handler_class_phase_duration {
+  /**
+   * The phase this configuration describes.
+   *
+   * It is an error for this to not be one of the `Phase` constants in
+   * `dif_alert_handler_class_state_t`.
+   */
+  dif_alert_handler_class_state_t phase;
+  /**
+   * The duration of this phase, in cycles.
+   */
+  uint32_t cycles;
+} dif_alert_handler_class_phase_duration_t;
+
+/**
+ * Runtime configuration for a particular alert class.
+ *
+ * This struct describes how a particular alert class should be configured,
+ * such as which signals are associated with it, and what options are turned
+ * on for the escalation protocol.
+ *
+ * For each of the pointer/length field pairs below, if the length is zero, the
+ * pointer may be `NULL`.
+ */
+typedef struct dif_alert_handler_class_config {
+  /**
+   * The class this configuration describes.
+   */
+  dif_alert_handler_class_t alert_class;
+
+  /**
+   * A list of alerts that should be classified as having this class.
+   *
+   * Each alert in this list will additionally be set as enabled.
+   */
+  const dif_alert_handler_alert_t *alerts;
+  /**
+   * The length of the array `alerts`.
+   */
+  size_t alerts_len;
+
+  /**
+   * A list of local that should be classified as having this class.
+   *
+   * Each local alert in this list will additionally be set as enabled.
+   */
+  const dif_alert_handler_local_alert_t *local_alerts;
+  /**
+   * The length of the array `local_alerts`.
+   */
+  size_t local_alerts_len;
+
+  /**
+   * Whether the escalation protocol should be used for this class
+   * (i.e., accumulator and timeout based escalation).
+   *
+   * Class IRQs will still fire regardless of this setting.
+   */
+  dif_alert_handler_toggle_t use_escalation_protocol;
+
+  /**
+   * Whether automatic escalation locking should be used for this class.
+   *
+   * When enabled, upon beginning the escalation protocol, the hardware will
+   * lock
+   * the escalation clear bit, so that software cannot stop escalation once it
+   * has begun.
+   */
+  dif_alert_handler_toggle_t automatic_locking;
+
+  /**
+   * The threshold for the class accmulator which, when reached, will
+   * automatically
+   * trigger escalation.
+   */
+  uint16_t accumulator_threshold;
+
+  /**
+   * The number of cycles this class's associated IRQ handler has to acknowledge
+   * the IRQ before escalation is triggered.
+   *
+   * A value of zero disables the timeout.
+   */
+  uint32_t irq_deadline_cycles;
+
+  /**
+   * Signals to associate to each escalation phase.
+   *
+   * Each escalation phase signal in this list will additionally be set as
+   * enabled; phases not listed will have their escalation signals disabled.
+   */
+  const dif_alert_handler_class_phase_signal_t *phase_signals;
+  /**
+   * The length of the array `phase_signals`.
+   */
+  size_t phase_signals_len;
+
+  /**
+   * Durations, in cycles, of each escalation phase.
+   */
+  const dif_alert_handler_class_phase_duration_t *phase_durations;
+  /**
+   * The length of the array `phase_durations`.
+   */
+  size_t phase_durations_len;
+} dif_alert_handler_class_config_t;
+
+/**
+ * Runtime configuration for alert handler.
+ *
+ * This struct describes runtime information for one-time configuration of the
+ * hardware.
+ */
+typedef struct dif_alert_handler_config {
+  /**
+   * The alert ping timeout, in cycles.
+   *
+   * An alert handler handle.will regularly, at random intervals, ping alert
+   * sources.
+   * If a source fails to respond, a local alert will be raised.
+   *
+   * The appropriate value will be dependent on all of the clocks involved on
+   * a chip.
+   *
+   * Note that the ping timer won't start until `dif_alert_handler_lock()` is
+   * successfully called.
+   */
+  uint32_t ping_timeout;
+
+  /**
+   * A list of alert classes to configure.
+   */
+  const dif_alert_handler_class_config_t *classes;
+  /**
+   * The length of the array `classes`.
+   */
+  size_t classes_len;
+} dif_alert_handler_config_t;
+
+/**
+ * Creates a new handle for alert handler.
+ *
+ * This function does not actuate the hardware.
+ *
+ * @param params Hardware instantiation parameters.
+ * @param handler Out param for the initialized handle.
+ * @return The result of the operation.
+ */
+DIF_WARN_UNUSED_RESULT
+dif_alert_handler_result_t dif_alert_handler_init(
+    dif_alert_handler_params_t params, dif_alert_handler_t *handler);
+
+/**
+ * Configures alert handler with runtime information.
+ *
+ * This function should need to be called once for the lifetime of `handle`.
+ *
+ * This operation is lock-protected.
+ *
+ * @param handler An alert handler handle.
+ * @param config Runtime configuration parameters.
+ * @return The result of the operation.
+ */
+DIF_WARN_UNUSED_RESULT
+dif_alert_handler_config_result_t dif_alert_handler_configure(
+    const dif_alert_handler_t *handler, dif_alert_handler_config_t config);
+/**
+ * Locks out alert handler configuration functionality.
+ *
+ * Once locked, `dif_alert_handler_configure()` will return
+ * `kDifAlertHandlerConfigLocked`.
+ *
+ * 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 `kDifAlertHandlerOk`.
+ *
+ * @param handler An alert handler handle.
+ * @return The result of the operation.
+ */
+DIF_WARN_UNUSED_RESULT
+dif_alert_handler_result_t dif_alert_handler_lock(
+    const dif_alert_handler_t *handler);
+
+/**
+ * Checks whether this alert handler is locked.
+ *
+ * See `dif_alert_handler_lock()`.
+ *
+ * @param handler An alert handler handle.
+ * @param is_locked Out-param for the locked state.
+ * @return The result of the operation.
+ */
+DIF_WARN_UNUSED_RESULT
+dif_alert_handler_result_t dif_alert_handler_is_locked(
+    const dif_alert_handler_t *handler, bool *is_locked);
+
+/**
+ * Returns whether a particular interrupt is currently pending.
+ *
+ * @param handler An alert handler handle.
+ * @param alert_class An alert class.
+ * @param is_pending Out-param for whether the interrupt is pending.
+ * @return The result of the operation.
+ */
+DIF_WARN_UNUSED_RESULT
+dif_alert_handler_result_t dif_alert_handler_irq_is_pending(
+    const dif_alert_handler_t *handler, dif_alert_handler_class_t alert_class,
+    bool *is_pending);
+
+/**
+ * Acknowledges a particular interrupt, indicating to the hardware that it has
+ * been successfully serviced.
+ *
+ * @param handler An alert handler handle.
+ * @param alert_class An alert class.
+ * @return The result of the operation.
+ */
+DIF_WARN_UNUSED_RESULT
+dif_alert_handler_result_t dif_alert_handler_irq_acknowledge(
+    const dif_alert_handler_t *handler, dif_alert_handler_class_t alert_class);
+
+/**
+ * Checks whether a particular interrupt is currently enabled or disabled.
+ *
+ * @param handler An alert handler handle.
+ * @param alert_class An alert class.
+ * @param state Out-param toggle state of the interrupt.
+ * @return The result of the operation.
+ */
+DIF_WARN_UNUSED_RESULT
+dif_alert_handler_result_t dif_alert_handler_irq_get_enabled(
+    const dif_alert_handler_t *handler, dif_alert_handler_class_t alert_class,
+    dif_alert_handler_toggle_t *state);
+
+/**
+ * Sets whether a particular interrupt is currently enabled or disabled.
+ *
+ * @param handler An alert handler handle.
+ * @param alert_class An alert class.
+ * @param state The new toggle state for the interrupt.
+ * @return The result of the operation.
+ */
+DIF_WARN_UNUSED_RESULT
+dif_alert_handler_result_t dif_alert_handler_irq_set_enabled(
+    const dif_alert_handler_t *handler, dif_alert_handler_class_t alert_class,
+    dif_alert_handler_toggle_t state);
+
+/**
+ * Forces a particular interrupt, causing it to be serviced as if hardware had
+ * asserted it.
+ *
+ * @param handler An alert handler handle.
+ * @param alert_class An alert class.
+ * @return The result of the operation.
+ */
+DIF_WARN_UNUSED_RESULT
+dif_alert_handler_result_t dif_alert_handler_irq_force(
+    const dif_alert_handler_t *handler, dif_alert_handler_class_t alert_class);
+
+/**
+ * Disables all interrupts, optionally snapshotting all toggle state for later
+ * restoration.
+ *
+ * @param handler An alert handler handle.
+ * @param snapshot Out-param for the snapshot; may be `NULL`.
+ * @return The result of the operation.
+ */
+DIF_WARN_UNUSED_RESULT
+dif_alert_handler_result_t dif_alert_handler_irq_disable_all(
+    const dif_alert_handler_t *handler,
+    dif_alert_handler_irq_snapshot_t *snapshot);
+
+/**
+ * Restores interrupts from the given snapshot.
+ *
+ * This function can be used with `dif_alert_handler_irq_disable_all()` to
+ * temporary
+ * interrupt save-and-restore.
+ *
+ * @param handler An alert handler handle.
+ * @param snapshot A snapshot to restore from.
+ * @return The result of the operation.
+ */
+DIF_WARN_UNUSED_RESULT
+dif_alert_handler_result_t dif_alert_handler_irq_restore_all(
+    const dif_alert_handler_t *handler,
+    const dif_alert_handler_irq_snapshot_t *snapshot);
+
+/**
+ * Checks whether an alert is one of the causes for an alert IRQ.
+ *
+ * @param handler An alert handler handle.
+ * @param alert The alert to check.
+ * @param is_cause Out-param for whether this alert is a cause.
+ * @return The result of the operation.
+ */
+DIF_WARN_UNUSED_RESULT
+dif_alert_handler_result_t dif_alert_handler_alert_is_cause(
+    const dif_alert_handler_t *handler, dif_alert_handler_alert_t alert,
+    bool *is_cause);
+
+/**
+ * Clears an alert from the cause vector, similar to an IRQ acknowledgement.
+ *
+ * @param handler An alert handler handle.
+ * @param alert The alert to acknowledge.
+ * @return The result of the operation.
+ */
+DIF_WARN_UNUSED_RESULT
+dif_alert_handler_result_t dif_alert_handler_alert_acknowledge(
+    const dif_alert_handler_t *handler, dif_alert_handler_alert_t alert);
+
+/**
+ * Checks whether software can clear escalations for this class.
+ *
+ * If `automatic_locking` has been set in a class's configuration, this
+ * function may suddenly begin returning `false` instead of `true` without
+ * software invervention, if escalation has been triggered.
+ *
+ * @param handler An alert handler handle.
+ * @param alert_class The class to check.
+ * @param can_clear Out-param for the clear enablement state.
+ * @return The result of the operation.
+ */
+DIF_WARN_UNUSED_RESULT
+dif_alert_handler_result_t dif_alert_handler_escalation_can_clear(
+    const dif_alert_handler_t *handler, dif_alert_handler_class_t alert_class,
+    bool *can_clear);
+
+/**
+ * Disables escalation clearing for this class.
+ *
+ * This operation is similar to locking in that it cannot be undone.
+ *
+ * @param handler An alert handler handle.
+ * @param alert_class The class to disable clearing for.
+ * @return The result of the operation.
+ */
+DIF_WARN_UNUSED_RESULT
+dif_alert_handler_result_t dif_alert_handler_escalation_disable_clearing(
+    const dif_alert_handler_t *handler, dif_alert_handler_class_t alert_class);
+
+/**
+ * Clears an on-going escalation, as well as the class accumulator.
+ *
+ * This operation can be disabled with
+ * `dif_alert_handler_escalation_disable_clearing()`.
+ *
+ * @param handler An alert handler handle.
+ * @param alert_class The class to clear an escalation for.
+ * @return The result of the operation.
+ */
+DIF_WARN_UNUSED_RESULT
+dif_alert_handler_result_t dif_alert_handler_escalation_clear(
+    const dif_alert_handler_t *handler, dif_alert_handler_class_t alert_class);
+
+/**
+ * Gets the accumulator value for this class.
+ *
+ * This value is the number of alerts of this class that have been logged so
+ * far (more or less, since multiple alerts on the same cycle will be merged
+ * into one). Once this value equals the configured threshold, any followup
+ * alerts will immediately trigger the escalation protocol.
+ *
+ * This value is cleared as a side-effect of
+ * `dif_alert_handler_escalation_clear()`.
+ *
+ * @param handler An alert handler handle.
+ * @param alert_class The class to get the accumulator for.
+ * @param alerts Out-param for the accumulator.
+ * @return The result of the operation.
+ */
+DIF_WARN_UNUSED_RESULT
+dif_alert_handler_result_t dif_alert_handler_get_accumulator(
+    const dif_alert_handler_t *handler, dif_alert_handler_class_t alert_class,
+    uint16_t *alerts);
+
+/**
+ * Gets the current value of the "escalation counter".
+ *
+ * The interpretation of this value depends on the value returned by
+ * `dif_alert_handler_class_state_get()`. If it is in the timeout state,
+ * it returns the number of cycles counted towards that cycle so far.
+ * If in an escalation phase, it returns the number of cycles that phase
+ * has been active for.
+ *
+ * @param handler An alert handler handle.
+ * @param alert_class The class to set the counter for.
+ * @param cycles Out-param for the counter.
+ * @return The result of the operation.
+ */
+DIF_WARN_UNUSED_RESULT
+dif_alert_handler_result_t dif_alert_handler_get_escalation_counter(
+    const dif_alert_handler_t *handler, dif_alert_handler_class_t alert_class,
+    uint32_t *cycles);
+
+/**
+ * Gets the current state of this class.
+ *
+ * See `dif_alert_handler_class_state_t` for potential states.
+ *
+ * @param handler An alert handler handle.
+ * @param alert_class The class to get the state of
+ * @param state Out-param for the class state.
+ * @return The result of the operation.
+ */
+DIF_WARN_UNUSED_RESULT
+dif_alert_handler_result_t dif_alert_handler_get_class_state(
+    const dif_alert_handler_t *handler, dif_alert_handler_class_t alert_class,
+    dif_alert_handler_class_state_t *state);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif  // __cplusplus
+
+#endif  // OPENTITAN_SW_DEVICE_LIB_DIF_DIF_ALERT_HANDLER_H_