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: [0x00000001; ALERT_HANDLER_PARAM_N_ALERTS as usize],
367 en: [0x00000000; ALERT_HANDLER_PARAM_N_ALERTS as usize],
368 class: [0x00000000; ALERT_HANDLER_PARAM_N_ALERTS as usize],
369 loc_regwen: [
370 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001,
371 ],
372 loc_en: [
373 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
374 ],
375 loc_class: [
376 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
377 ],
378 class_regs: [
379 AlertClassRegs {
380 regwen: 0x00000001,
381 ctrl: 0x00003900,
382 accum_thresh: 0x00000000,
383 timeout_cyc: 0x00000000,
384 phase_cycs: [0x00000000, 0x0000000a, 0x0000000a, 0xffffffff],
385 },
386 AlertClassRegs {
387 regwen: 0x00000001,
388 ctrl: 0x00003900,
389 accum_thresh: 0x00000000,
390 timeout_cyc: 0x00000000,
391 phase_cycs: [0x00000000, 0x0000000a, 0x0000000a, 0xffffffff],
392 },
393 AlertClassRegs {
394 regwen: 0x00000001,
395 ctrl: 0x00003900,
396 accum_thresh: 0x00000000,
397 timeout_cyc: 0x00000000,
398 phase_cycs: [0x00000000, 0x00000000, 0x00000000, 0x00000000],
399 },
400 AlertClassRegs {
401 regwen: 0x00000001,
402 ctrl: 0x00003900,
403 accum_thresh: 0x00000000,
404 timeout_cyc: 0x00000000,
405 phase_cycs: [0x00000000, 0x00000000, 0x00000000, 0x00000000],
406 },
407 ],
408 };
409
410 struct TestOtpAlertsDisabled {}
411
412 impl OtpRead for TestOtpAlertsDisabled {
414 fn read32_offset(&self, name: &str, offset: usize) -> Result<u32> {
415 Ok(match name {
416 "OWNER_SW_CFG_ROM_ALERT_CLASS_EN" => 0xa9a9a9a9,
417 "OWNER_SW_CFG_ROM_ALERT_ESCALATION" => 0xd1d1d1d1,
418 "OWNER_SW_CFG_ROM_ALERT_CLASSIFICATION"
419 | "OWNER_SW_CFG_ROM_LOCAL_ALERT_CLASSIFICATION" => 0x94949494,
420 "OWNER_SW_CFG_ROM_ALERT_PHASE_CYCLES" => [
421 0x00000000, 0x0000000a, 0x0000000a, 0xffffffff, 0x00000000, 0x0000000a, 0x0000000a, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, ][offset / 4],
426 "OWNER_SW_CFG_ROM_ALERT_ACCUM_THRESH" | "OWNER_SW_CFG_ROM_ALERT_TIMEOUT_CYCLES" => {
427 0x00000000
428 }
429 _ => panic!("No such OTP value {}", name),
430 })
431 }
432 }
433
434 struct TestOtpAlertsEnabled {}
435
436 impl OtpRead for TestOtpAlertsEnabled {
438 fn read32_offset(&self, name: &str, offset: usize) -> Result<u32> {
439 Ok(match name {
440 "OWNER_SW_CFG_ROM_ALERT_CLASS_EN" => 0x07070707,
441 "OWNER_SW_CFG_ROM_ALERT_ESCALATION" => 0xd1d1d1d1,
442 "OWNER_SW_CFG_ROM_ALERT_CLASSIFICATION"
443 | "OWNER_SW_CFG_ROM_LOCAL_ALERT_CLASSIFICATION" => 0x94949494,
444 "OWNER_SW_CFG_ROM_ALERT_PHASE_CYCLES" => [
445 0x00000000, 0x0000000a, 0x0000000a, 0xffffffff, 0x00000000, 0x0000000a, 0x0000000a, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, ][offset / 4],
450 "OWNER_SW_CFG_ROM_ALERT_ACCUM_THRESH" | "OWNER_SW_CFG_ROM_ALERT_TIMEOUT_CYCLES" => {
451 0x00000000
452 }
453 _ => panic!("No such OTP value {}", name),
454 })
455 }
456 }
457
458 #[test]
463 fn test_new_crc() {
464 let crc = new_crc();
465 let mut digest = crc.digest();
466 digest.update(b"123456789");
467 assert_eq!(digest.finalize(), 0xcbf43926);
468
469 let crc = new_crc();
470 let mut digest = crc.digest();
471 digest.update(b"The quick brown fox jumps over the lazy dog");
472 assert_eq!(digest.finalize(), 0x414fa339);
473
474 let crc = new_crc();
475 let mut digest = crc.digest();
476 digest.update(b"\xfe\xca\xfe\xca\x02\xb0\xad\x1b");
477 assert_eq!(digest.finalize(), 0x9508ac14);
478 }
479
480 #[test]
481 fn test_crc_from_regs() {
482 assert_eq!(
483 TEST_REGS.crc32(),
484 match ALERT_HANDLER_PARAM_N_ALERTS {
485 65 => 0xf9616122,
486 77 => 0x07bea283,
487 n => panic!("Unexpected alert count: {}", n),
488 }
489 );
490 }
491
492 #[test]
493 fn test_regs_from_otp() {
494 assert_eq!(
495 TEST_REGS,
496 AlertRegs::try_new(LcStateVal::Dev, &TestOtpAlertsDisabled {}).unwrap()
497 );
498 }
499
500 #[test]
501 fn test_crc_disabled() {
502 assert_eq!(
503 AlertRegs::try_new(LcStateVal::Dev, &TestOtpAlertsDisabled {})
504 .unwrap()
505 .crc32(),
506 match ALERT_HANDLER_PARAM_N_ALERTS {
507 65 => 0xf9616122,
508 77 => 0x07bea283,
509 n => panic!("Unexpected alert count: {}", n),
510 },
511 );
512 }
513
514 #[test]
515 fn test_crc_enabled() {
516 assert_eq!(
517 AlertRegs::try_new(LcStateVal::Dev, &TestOtpAlertsEnabled {})
518 .unwrap()
519 .crc32(),
520 match ALERT_HANDLER_PARAM_N_ALERTS {
521 65 => 0x561bcb14,
522 77 => 0xa8c408b5,
523 n => panic!("Unexpected alert count: {}", n),
524 },
525 );
526 }
527}