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, digest: &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 ],
377 en: [
378 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
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 ],
388 class: [
389 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
390 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
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 ],
399 loc_regwen: [
400 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001,
401 ],
402 loc_en: [
403 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
404 ],
405 loc_class: [
406 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
407 ],
408 class_regs: [
409 AlertClassRegs {
410 regwen: 0x00000001,
411 ctrl: 0x00003900,
412 accum_thresh: 0x00000000,
413 timeout_cyc: 0x00000000,
414 phase_cycs: [0x00000000, 0x0000000a, 0x0000000a, 0xffffffff],
415 },
416 AlertClassRegs {
417 regwen: 0x00000001,
418 ctrl: 0x00003900,
419 accum_thresh: 0x00000000,
420 timeout_cyc: 0x00000000,
421 phase_cycs: [0x00000000, 0x0000000a, 0x0000000a, 0xffffffff],
422 },
423 AlertClassRegs {
424 regwen: 0x00000001,
425 ctrl: 0x00003900,
426 accum_thresh: 0x00000000,
427 timeout_cyc: 0x00000000,
428 phase_cycs: [0x00000000, 0x00000000, 0x00000000, 0x00000000],
429 },
430 AlertClassRegs {
431 regwen: 0x00000001,
432 ctrl: 0x00003900,
433 accum_thresh: 0x00000000,
434 timeout_cyc: 0x00000000,
435 phase_cycs: [0x00000000, 0x00000000, 0x00000000, 0x00000000],
436 },
437 ],
438 };
439
440 struct TestOtpAlertsDisabled {}
441
442 impl OtpRead for TestOtpAlertsDisabled {
444 fn read32_offset(&self, name: &str, offset: usize) -> Result<u32> {
445 Ok(match name {
446 "OWNER_SW_CFG_ROM_ALERT_CLASS_EN" => 0xa9a9a9a9,
447 "OWNER_SW_CFG_ROM_ALERT_ESCALATION" => 0xd1d1d1d1,
448 "OWNER_SW_CFG_ROM_ALERT_CLASSIFICATION"
449 | "OWNER_SW_CFG_ROM_LOCAL_ALERT_CLASSIFICATION" => 0x94949494,
450 "OWNER_SW_CFG_ROM_ALERT_PHASE_CYCLES" => [
451 0x00000000, 0x0000000a, 0x0000000a, 0xffffffff, 0x00000000, 0x0000000a, 0x0000000a, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, ][offset / 4],
456 "OWNER_SW_CFG_ROM_ALERT_ACCUM_THRESH" | "OWNER_SW_CFG_ROM_ALERT_TIMEOUT_CYCLES" => {
457 0x00000000
458 }
459 _ => panic!("No such OTP value {}", name),
460 })
461 }
462 }
463
464 struct TestOtpAlertsEnabled {}
465
466 impl OtpRead for TestOtpAlertsEnabled {
468 fn read32_offset(&self, name: &str, offset: usize) -> Result<u32> {
469 Ok(match name {
470 "OWNER_SW_CFG_ROM_ALERT_CLASS_EN" => 0x07070707,
471 "OWNER_SW_CFG_ROM_ALERT_ESCALATION" => 0xd1d1d1d1,
472 "OWNER_SW_CFG_ROM_ALERT_CLASSIFICATION"
473 | "OWNER_SW_CFG_ROM_LOCAL_ALERT_CLASSIFICATION" => 0x94949494,
474 "OWNER_SW_CFG_ROM_ALERT_PHASE_CYCLES" => [
475 0x00000000, 0x0000000a, 0x0000000a, 0xffffffff, 0x00000000, 0x0000000a, 0x0000000a, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, ][offset / 4],
480 "OWNER_SW_CFG_ROM_ALERT_ACCUM_THRESH" | "OWNER_SW_CFG_ROM_ALERT_TIMEOUT_CYCLES" => {
481 0x00000000
482 }
483 _ => panic!("No such OTP value {}", name),
484 })
485 }
486 }
487
488 #[test]
493 fn test_new_crc() {
494 let crc = new_crc();
495 let mut digest = crc.digest();
496 digest.update(b"123456789");
497 assert_eq!(digest.finalize(), 0xcbf43926);
498
499 let crc = new_crc();
500 let mut digest = crc.digest();
501 digest.update(b"The quick brown fox jumps over the lazy dog");
502 assert_eq!(digest.finalize(), 0x414fa339);
503
504 let crc = new_crc();
505 let mut digest = crc.digest();
506 digest.update(b"\xfe\xca\xfe\xca\x02\xb0\xad\x1b");
507 assert_eq!(digest.finalize(), 0x9508ac14);
508 }
509
510 #[test]
511 fn test_crc_from_regs() {
512 assert_eq!(TEST_REGS.crc32(), 0xd812e34);
513 }
514
515 #[test]
516 fn test_regs_from_otp() {
517 assert_eq!(
518 TEST_REGS,
519 AlertRegs::try_new(LcStateVal::Dev, &TestOtpAlertsDisabled {}).unwrap()
520 );
521 }
522
523 #[test]
524 fn test_crc_disabled() {
525 assert_eq!(
526 AlertRegs::try_new(LcStateVal::Dev, &TestOtpAlertsDisabled {})
527 .unwrap()
528 .crc32(),
529 0xd812e34
530 );
531 }
532
533 #[test]
534 fn test_crc_enabled() {
535 assert_eq!(
536 AlertRegs::try_new(LcStateVal::Dev, &TestOtpAlertsEnabled {})
537 .unwrap()
538 .crc32(),
539 0xa2fb8402
540 );
541 }
542}