opentitanlib/otp/
alert_handler.rs

1// Copyright lowRISC contributors (OpenTitan project).
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
5use crate::otp::alert_handler_regs::*;
6use crate::otp::lc_state::LcStateVal;
7use crate::otp::otp_img::OtpRead;
8
9use anyhow::{Context, Result, bail};
10use crc::{Crc, Digest};
11
12/// ALERT_HANDLER_ALERT_CLASS related register values.
13#[derive(Clone, Copy, Debug, PartialEq)]
14struct AlertClassRegs {
15    regwen: u32,
16    ctrl: u32,
17    accum_thresh: u32,
18    timeout_cyc: u32,
19    phase_cycs: [u32; ALERT_HANDLER_PARAM_N_PHASES as usize],
20}
21
22/// Register values for alert_handler used in CRC32 calculation.
23#[derive(Debug, PartialEq)]
24pub struct AlertRegs {
25    /// ALERT_HANDLER_LOC_ALERT_REGWEN
26    regwen: [u32; ALERT_HANDLER_ALERT_REGWEN_MULTIREG_COUNT as usize],
27    /// ALERT_HANDLER_ALERT_EN_SHADOWED
28    en: [u32; ALERT_HANDLER_ALERT_EN_SHADOWED_MULTIREG_COUNT as usize],
29    /// ALERT_HANDLER_ALERT_CLASS_SHADOWED
30    class: [u32; ALERT_HANDLER_ALERT_CLASS_SHADOWED_MULTIREG_COUNT as usize],
31    /// ALERT_HANDLER_LOC_ALERT_REGWEN
32    loc_regwen: [u32; ALERT_HANDLER_LOC_ALERT_REGWEN_MULTIREG_COUNT as usize],
33    /// ALERT_HANDLER_LOC_ALERT_EN_SHADOWED
34    loc_en: [u32; ALERT_HANDLER_LOC_ALERT_EN_SHADOWED_MULTIREG_COUNT as usize],
35    /// ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED
36    loc_class: [u32; ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED_MULTIREG_COUNT as usize],
37    /// Alert handler class registers
38    class_regs: [AlertClassRegs; ALERT_HANDLER_PARAM_N_CLASSES as usize],
39}
40
41// TODO: Use bindgen to produce the following enum definitions.
42/// Alert classification values.
43///
44/// Based on values generated by sparse-fsm-encode.py and defined in
45/// sw/device/silicon_creator/lib/drivers/alert.h as alert_class_t.
46#[derive(strum::FromRepr)]
47#[repr(u8)]
48enum AlertClass {
49    X = 0x94,
50    A = 0xee,
51    B = 0x64,
52    C = 0xa7,
53    D = 0x32,
54}
55
56impl AlertClass {
57    fn index(&self) -> usize {
58        match self {
59            AlertClass::A => 0,
60            AlertClass::B => 1,
61            AlertClass::C => 2,
62            AlertClass::D => 3,
63            AlertClass::X => 0,
64        }
65    }
66
67    fn from_index(index: usize) -> Self {
68        match index {
69            0 => AlertClass::A,
70            1 => AlertClass::B,
71            2 => AlertClass::C,
72            3 => AlertClass::D,
73            _ => AlertClass::X,
74        }
75    }
76}
77
78#[derive(strum::FromRepr)]
79#[repr(u8)]
80enum AlertEnable {
81    None = 0xa9,
82    Enabled = 0x07,
83    Locked = 0xd2,
84}
85
86#[derive(strum::FromRepr)]
87#[repr(u8)]
88enum AlertEscalate {
89    None = 0xd1,
90    Phase0 = 0xb9,
91    Phase1 = 0xcb,
92    Phase2 = 0x25,
93    Phase3 = 0x76,
94}
95
96struct AlertClassConfig {
97    enabled: AlertEnable,
98    escalate: AlertEscalate,
99    accum_thresh: u32,
100    timeout_cyc: u32,
101    phase_cycs: [u32; ALERT_HANDLER_PARAM_N_PHASES as usize],
102}
103
104impl Default for AlertClassRegs {
105    fn default() -> Self {
106        AlertClassRegs {
107            regwen: 1,
108            ctrl: 0,
109            accum_thresh: 0,
110            timeout_cyc: 0,
111            phase_cycs: [0; ALERT_HANDLER_PARAM_N_PHASES as usize],
112        }
113    }
114}
115
116impl Default for AlertRegs {
117    fn default() -> Self {
118        AlertRegs {
119            regwen: [1; ALERT_HANDLER_ALERT_REGWEN_MULTIREG_COUNT as usize],
120            loc_regwen: [1; ALERT_HANDLER_LOC_ALERT_REGWEN_MULTIREG_COUNT as usize],
121            en: [0; ALERT_HANDLER_ALERT_EN_SHADOWED_MULTIREG_COUNT as usize],
122            class: [0; ALERT_HANDLER_ALERT_CLASS_SHADOWED_MULTIREG_COUNT as usize],
123            loc_en: [0; ALERT_HANDLER_LOC_ALERT_EN_SHADOWED_MULTIREG_COUNT as usize],
124            loc_class: [0; ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED_MULTIREG_COUNT as usize],
125            class_regs: [Default::default(); ALERT_HANDLER_PARAM_N_CLASSES as usize],
126        }
127    }
128}
129
130impl AlertRegs {
131    /// Compute the CRC32 of the internal register values to match the value produced by
132    /// `sw/device/silicon_creator/lib/drivers/alert.h:alert_config_crc32`.
133    pub fn crc32(self) -> u32 {
134        let crc = new_crc();
135        let mut digest = crc.digest();
136        self.crc32_add(&mut digest);
137        digest.finalize()
138    }
139
140    /// Create the set of alert_handler register values from a given lifecycle state and OTP.
141    ///
142    /// The internal fields of `AlertRegs` should match those produced on the device after
143    /// alert_handler is configured in `sw/device/silicon_creator/lib/shutdown.h:shutdown_init`.
144    pub fn try_new<T: OtpRead>(lc_state: LcStateVal, otp: &T) -> Result<Self> {
145        let mut alert = AlertRegs::default();
146
147        let lc_shift = match lc_state {
148            LcStateVal::Prod => 0,
149            LcStateVal::ProdEnd => 1,
150            LcStateVal::Dev => 2,
151            LcStateVal::Rma => 3,
152            LcStateVal::Test => return Ok(alert),
153        };
154
155        let class_enable = otp.read32("OWNER_SW_CFG_ROM_ALERT_CLASS_EN")?;
156        let class_escalate = otp.read32("OWNER_SW_CFG_ROM_ALERT_ESCALATION")?;
157
158        for i in 0..ALERT_HANDLER_ALERT_CLASS_SHADOWED_MULTIREG_COUNT as usize {
159            let value = otp.read32_offset("OWNER_SW_CFG_ROM_ALERT_CLASSIFICATION", i * 4)?;
160            let cls = AlertClass::from_repr(value.to_le_bytes()[lc_shift])
161                .with_context(|| format!("invalid alert class value {value:#010x}"))?;
162            let enable = AlertEnable::from_repr(class_enable.to_le_bytes()[cls.index()])
163                .with_context(|| format!("invalid class enable value {value:#010x}"))?;
164            alert.configure(i, cls, enable)?;
165        }
166
167        for i in 0..ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED_MULTIREG_COUNT as usize {
168            let value = otp.read32_offset("OWNER_SW_CFG_ROM_LOCAL_ALERT_CLASSIFICATION", i * 4)?;
169            let cls = AlertClass::from_repr(value.to_le_bytes()[lc_shift])
170                .with_context(|| format!("invalid alert class value {value:#010x}"))?;
171            let enable = AlertEnable::from_repr(class_enable.to_le_bytes()[cls.index()])
172                .with_context(|| format!("invalid class enable value {value:#010x}"))?;
173            alert.local_configure(i, cls, enable)?;
174        }
175
176        for i in 0..ALERT_HANDLER_PARAM_N_CLASSES as usize {
177            let mut phase_cycs = [0; ALERT_HANDLER_PARAM_N_PHASES as usize];
178            for phase in 0..ALERT_HANDLER_PARAM_N_PHASES as usize {
179                phase_cycs[phase] = otp.read32_offset(
180                    "OWNER_SW_CFG_ROM_ALERT_PHASE_CYCLES",
181                    (i * phase_cycs.len() + phase) * 4,
182                )?;
183            }
184            let config = AlertClassConfig {
185                enabled: AlertEnable::from_repr(class_enable.to_le_bytes()[i])
186                    .with_context(|| format!("invalid class enable value {class_enable:#010x}"))?,
187                escalate: AlertEscalate::from_repr(class_escalate.to_le_bytes()[i]).with_context(
188                    || format!("invalid class escalate value {class_escalate:#010x}"),
189                )?,
190                accum_thresh: otp.read32_offset("OWNER_SW_CFG_ROM_ALERT_ACCUM_THRESH", i * 4)?,
191                timeout_cyc: otp.read32_offset("OWNER_SW_CFG_ROM_ALERT_TIMEOUT_CYCLES", i * 4)?,
192                phase_cycs,
193            };
194            alert.class_configure(AlertClass::from_index(i), &config)?;
195        }
196
197        Ok(alert)
198    }
199
200    fn configure(&mut self, index: usize, cls: AlertClass, enabled: AlertEnable) -> Result<()> {
201        if index >= ALERT_HANDLER_ALERT_CLASS_SHADOWED_MULTIREG_COUNT as usize {
202            bail!("Bad alert index {}", index);
203        }
204
205        self.class[index] = match cls {
206            AlertClass::A => ALERT_HANDLER_ALERT_CLASS_SHADOWED_0_CLASS_A_0_VALUE_CLASSA,
207            AlertClass::B => ALERT_HANDLER_ALERT_CLASS_SHADOWED_0_CLASS_A_0_VALUE_CLASSB,
208            AlertClass::C => ALERT_HANDLER_ALERT_CLASS_SHADOWED_0_CLASS_A_0_VALUE_CLASSC,
209            AlertClass::D => ALERT_HANDLER_ALERT_CLASS_SHADOWED_0_CLASS_A_0_VALUE_CLASSD,
210            AlertClass::X => return Ok(()),
211        };
212
213        match enabled {
214            AlertEnable::None => {}
215            AlertEnable::Enabled => self.en[index] = 1,
216            AlertEnable::Locked => {
217                self.en[index] = 1;
218                self.regwen[index] = 0;
219            }
220        };
221
222        Ok(())
223    }
224
225    fn local_configure(
226        &mut self,
227        index: usize,
228        cls: AlertClass,
229        enabled: AlertEnable,
230    ) -> Result<()> {
231        if index >= ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED_MULTIREG_COUNT as usize {
232            bail!("Bad local alert index {}", index);
233        }
234
235        self.loc_class[index] = match cls {
236            AlertClass::A => ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED_0_CLASS_LA_0_VALUE_CLASSA,
237            AlertClass::B => ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED_0_CLASS_LA_0_VALUE_CLASSB,
238            AlertClass::C => ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED_0_CLASS_LA_0_VALUE_CLASSC,
239            AlertClass::D => ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED_0_CLASS_LA_0_VALUE_CLASSD,
240            AlertClass::X => return Ok(()),
241        };
242
243        match enabled {
244            AlertEnable::None => {}
245            AlertEnable::Enabled => self.loc_en[index] = 1,
246            AlertEnable::Locked => {
247                self.loc_en[index] = 1;
248                self.loc_regwen[index] = 0;
249            }
250        };
251
252        Ok(())
253    }
254
255    fn class_configure(&mut self, cls: AlertClass, config: &AlertClassConfig) -> Result<()> {
256        let index = match cls {
257            AlertClass::A => 0,
258            AlertClass::B => 1,
259            AlertClass::C => 2,
260            AlertClass::D => 3,
261            AlertClass::X => bail!("Bad class"),
262        };
263
264        let mut reg = 0_u32;
265
266        // TODO(lowRISC/opentitan#15443): Fix this lint (clippy::erasing_op):
267        //reg |= (0 & ALERT_HANDLER_CLASSA_CTRL_SHADOWED_MAP_E0_MASK)
268        //    << ALERT_HANDLER_CLASSA_CTRL_SHADOWED_MAP_E0_OFFSET;
269        reg |= (1 & ALERT_HANDLER_CLASSA_CTRL_SHADOWED_MAP_E1_MASK)
270            << ALERT_HANDLER_CLASSA_CTRL_SHADOWED_MAP_E1_OFFSET;
271        reg |= (2 & ALERT_HANDLER_CLASSA_CTRL_SHADOWED_MAP_E2_MASK)
272            << ALERT_HANDLER_CLASSA_CTRL_SHADOWED_MAP_E2_OFFSET;
273        reg |= (3 & ALERT_HANDLER_CLASSA_CTRL_SHADOWED_MAP_E3_MASK)
274            << ALERT_HANDLER_CLASSA_CTRL_SHADOWED_MAP_E3_OFFSET;
275
276        match config.enabled {
277            AlertEnable::None => {}
278            AlertEnable::Enabled => {
279                reg |= 1 << ALERT_HANDLER_CLASSA_CTRL_SHADOWED_EN_BIT;
280            }
281            AlertEnable::Locked => {
282                reg |= 1 << ALERT_HANDLER_CLASSA_CTRL_SHADOWED_LOCK_BIT;
283                reg |= 1 << ALERT_HANDLER_CLASSA_CTRL_SHADOWED_EN_BIT;
284            }
285        }
286
287        match config.escalate {
288            AlertEscalate::Phase0 => {
289                reg |= 1 << ALERT_HANDLER_CLASSA_CTRL_SHADOWED_EN_E0_BIT;
290            }
291            AlertEscalate::Phase1 => {
292                reg |= 1 << ALERT_HANDLER_CLASSA_CTRL_SHADOWED_EN_E0_BIT;
293                reg |= 1 << ALERT_HANDLER_CLASSA_CTRL_SHADOWED_EN_E1_BIT;
294            }
295            AlertEscalate::Phase2 => {
296                reg |= 1 << ALERT_HANDLER_CLASSA_CTRL_SHADOWED_EN_E0_BIT;
297                reg |= 1 << ALERT_HANDLER_CLASSA_CTRL_SHADOWED_EN_E1_BIT;
298                reg |= 1 << ALERT_HANDLER_CLASSA_CTRL_SHADOWED_EN_E2_BIT;
299            }
300            AlertEscalate::Phase3 => {
301                reg |= 1 << ALERT_HANDLER_CLASSA_CTRL_SHADOWED_EN_E0_BIT;
302                reg |= 1 << ALERT_HANDLER_CLASSA_CTRL_SHADOWED_EN_E1_BIT;
303                reg |= 1 << ALERT_HANDLER_CLASSA_CTRL_SHADOWED_EN_E2_BIT;
304                reg |= 1 << ALERT_HANDLER_CLASSA_CTRL_SHADOWED_EN_E3_BIT;
305            }
306            AlertEscalate::None => {}
307        }
308
309        self.class_regs[index].ctrl = reg;
310        self.class_regs[index].accum_thresh = config.accum_thresh;
311        self.class_regs[index].timeout_cyc = config.timeout_cyc;
312        self.class_regs[index].phase_cycs = config.phase_cycs;
313
314        Ok(())
315    }
316}
317
318trait Crc32Add {
319    fn crc32_add(self, diegst: &mut Digest<u32>);
320}
321
322impl Crc32Add for u32 {
323    fn crc32_add(self, digest: &mut Digest<u32>) {
324        digest.update(self.to_le_bytes().as_slice())
325    }
326}
327
328impl<T: Crc32Add, const N: usize> Crc32Add for [T; N] {
329    fn crc32_add(self, digest: &mut Digest<u32>) {
330        self.into_iter().for_each(|v| v.crc32_add(digest));
331    }
332}
333
334impl Crc32Add for AlertClassRegs {
335    fn crc32_add(self, digest: &mut Digest<u32>) {
336        self.regwen.crc32_add(digest);
337        self.ctrl.crc32_add(digest);
338        self.accum_thresh.crc32_add(digest);
339        self.timeout_cyc.crc32_add(digest);
340        self.phase_cycs.crc32_add(digest);
341    }
342}
343
344impl Crc32Add for AlertRegs {
345    fn crc32_add(self, digest: &mut Digest<u32>) {
346        self.regwen.crc32_add(digest);
347        self.en.crc32_add(digest);
348        self.class.crc32_add(digest);
349        self.loc_regwen.crc32_add(digest);
350        self.loc_en.crc32_add(digest);
351        self.loc_class.crc32_add(digest);
352        self.class_regs.crc32_add(digest);
353    }
354}
355
356fn new_crc() -> Crc<u32> {
357    Crc::<u32>::new(&crc::CRC_32_ISO_HDLC)
358}
359
360#[cfg(test)]
361mod test {
362    use super::*;
363
364    // Register values dumped from device after alert_handler initialization.
365    const TEST_REGS: AlertRegs = AlertRegs {
366        regwen: [
367            0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001,
368            0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001,
369            0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001,
370            0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001,
371            0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001,
372            0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001,
373            0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001,
374            0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001,
375            0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001,
376            0x00000001, 0x00000001,
377        ],
378        en: [
379            0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
380            0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
381            0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
382            0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
383            0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
384            0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
385            0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
386            0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
387            0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
388            0x00000000, 0x00000000,
389        ],
390        class: [
391            0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
392            0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
393            0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
394            0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
395            0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
396            0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
397            0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
398            0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
399            0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
400            0x00000000, 0x00000000,
401        ],
402        loc_regwen: [
403            0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001,
404        ],
405        loc_en: [
406            0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
407        ],
408        loc_class: [
409            0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
410        ],
411        class_regs: [
412            AlertClassRegs {
413                regwen: 0x00000001,
414                ctrl: 0x00003900,
415                accum_thresh: 0x00000000,
416                timeout_cyc: 0x00000000,
417                phase_cycs: [0x00000000, 0x0000000a, 0x0000000a, 0xffffffff],
418            },
419            AlertClassRegs {
420                regwen: 0x00000001,
421                ctrl: 0x00003900,
422                accum_thresh: 0x00000000,
423                timeout_cyc: 0x00000000,
424                phase_cycs: [0x00000000, 0x0000000a, 0x0000000a, 0xffffffff],
425            },
426            AlertClassRegs {
427                regwen: 0x00000001,
428                ctrl: 0x00003900,
429                accum_thresh: 0x00000000,
430                timeout_cyc: 0x00000000,
431                phase_cycs: [0x00000000, 0x00000000, 0x00000000, 0x00000000],
432            },
433            AlertClassRegs {
434                regwen: 0x00000001,
435                ctrl: 0x00003900,
436                accum_thresh: 0x00000000,
437                timeout_cyc: 0x00000000,
438                phase_cycs: [0x00000000, 0x00000000, 0x00000000, 0x00000000],
439            },
440        ],
441    };
442
443    struct TestOtpAlertsDisabled {}
444
445    // OTP values that corrispond to the above `TEST_REG` values.
446    impl OtpRead for TestOtpAlertsDisabled {
447        fn read32_offset(&self, name: &str, offset: usize) -> Result<u32> {
448            Ok(match name {
449                "OWNER_SW_CFG_ROM_ALERT_CLASS_EN" => 0xa9a9a9a9,
450                "OWNER_SW_CFG_ROM_ALERT_ESCALATION" => 0xd1d1d1d1,
451                "OWNER_SW_CFG_ROM_ALERT_CLASSIFICATION"
452                | "OWNER_SW_CFG_ROM_LOCAL_ALERT_CLASSIFICATION" => 0x94949494,
453                "OWNER_SW_CFG_ROM_ALERT_PHASE_CYCLES" => [
454                    0x00000000, 0x0000000a, 0x0000000a, 0xffffffff, // Class 0
455                    0x00000000, 0x0000000a, 0x0000000a, 0xffffffff, // Class 1
456                    0x00000000, 0x00000000, 0x00000000, 0x00000000, // Class 2
457                    0x00000000, 0x00000000, 0x00000000, 0x00000000, // Class 3
458                ][offset / 4],
459                "OWNER_SW_CFG_ROM_ALERT_ACCUM_THRESH" | "OWNER_SW_CFG_ROM_ALERT_TIMEOUT_CYCLES" => {
460                    0x00000000
461                }
462                _ => panic!("No such OTP value {}", name),
463            })
464        }
465    }
466
467    struct TestOtpAlertsEnabled {}
468
469    // OTP values with `*_CLASS_EN` vales set to `kAlertEnableEnabled`
470    impl OtpRead for TestOtpAlertsEnabled {
471        fn read32_offset(&self, name: &str, offset: usize) -> Result<u32> {
472            Ok(match name {
473                "OWNER_SW_CFG_ROM_ALERT_CLASS_EN" => 0x07070707,
474                "OWNER_SW_CFG_ROM_ALERT_ESCALATION" => 0xd1d1d1d1,
475                "OWNER_SW_CFG_ROM_ALERT_CLASSIFICATION"
476                | "OWNER_SW_CFG_ROM_LOCAL_ALERT_CLASSIFICATION" => 0x94949494,
477                "OWNER_SW_CFG_ROM_ALERT_PHASE_CYCLES" => [
478                    0x00000000, 0x0000000a, 0x0000000a, 0xffffffff, // Class 0
479                    0x00000000, 0x0000000a, 0x0000000a, 0xffffffff, // Class 1
480                    0x00000000, 0x00000000, 0x00000000, 0x00000000, // Class 2
481                    0x00000000, 0x00000000, 0x00000000, 0x00000000, // Class 3
482                ][offset / 4],
483                "OWNER_SW_CFG_ROM_ALERT_ACCUM_THRESH" | "OWNER_SW_CFG_ROM_ALERT_TIMEOUT_CYCLES" => {
484                    0x00000000
485                }
486                _ => panic!("No such OTP value {}", name),
487            })
488        }
489    }
490
491    // A sanity test to make sure the correct CRC algorithm is being used.
492    //
493    // These values are taken from the CRC32 unit tests in
494    // `sw/device/lib/base/crc32_unittest.cc`.
495    #[test]
496    fn test_new_crc() {
497        let crc = new_crc();
498        let mut digest = crc.digest();
499        digest.update(b"123456789");
500        assert_eq!(digest.finalize(), 0xcbf43926);
501
502        let crc = new_crc();
503        let mut digest = crc.digest();
504        digest.update(b"The quick brown fox jumps over the lazy dog");
505        assert_eq!(digest.finalize(), 0x414fa339);
506
507        let crc = new_crc();
508        let mut digest = crc.digest();
509        digest.update(b"\xfe\xca\xfe\xca\x02\xb0\xad\x1b");
510        assert_eq!(digest.finalize(), 0x9508ac14);
511    }
512
513    #[test]
514    fn test_crc_from_regs() {
515        assert_eq!(TEST_REGS.crc32(), 0xf9616122);
516    }
517
518    #[test]
519    fn test_regs_from_otp() {
520        assert_eq!(
521            TEST_REGS,
522            AlertRegs::try_new(LcStateVal::Dev, &TestOtpAlertsDisabled {}).unwrap()
523        );
524    }
525
526    #[test]
527    fn test_crc_disabled() {
528        assert_eq!(
529            AlertRegs::try_new(LcStateVal::Dev, &TestOtpAlertsDisabled {})
530                .unwrap()
531                .crc32(),
532            0xf9616122
533        );
534    }
535
536    #[test]
537    fn test_crc_enabled() {
538        assert_eq!(
539            AlertRegs::try_new(LcStateVal::Dev, &TestOtpAlertsEnabled {})
540                .unwrap()
541                .crc32(),
542            0x561bcb14
543        );
544    }
545}