1use 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#[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#[derive(Debug, PartialEq)]
24pub struct AlertRegs {
25 regwen: [u32; ALERT_HANDLER_ALERT_REGWEN_MULTIREG_COUNT as usize],
27 en: [u32; ALERT_HANDLER_ALERT_EN_SHADOWED_MULTIREG_COUNT as usize],
29 class: [u32; ALERT_HANDLER_ALERT_CLASS_SHADOWED_MULTIREG_COUNT as usize],
31 loc_regwen: [u32; ALERT_HANDLER_LOC_ALERT_REGWEN_MULTIREG_COUNT as usize],
33 loc_en: [u32; ALERT_HANDLER_LOC_ALERT_EN_SHADOWED_MULTIREG_COUNT as usize],
35 loc_class: [u32; ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED_MULTIREG_COUNT as usize],
37 class_regs: [AlertClassRegs; ALERT_HANDLER_PARAM_N_CLASSES as usize],
39}
40
41#[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 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 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 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 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 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, 0x00000000, 0x0000000a, 0x0000000a, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, ][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 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, 0x00000000, 0x0000000a, 0x0000000a, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, ][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 #[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}