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, 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    // Register values dumped from device after alert_handler initialization.
365    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    // OTP values that corrispond to the above `TEST_REG` values.
413    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, // Class 0
422                    0x00000000, 0x0000000a, 0x0000000a, 0xffffffff, // Class 1
423                    0x00000000, 0x00000000, 0x00000000, 0x00000000, // Class 2
424                    0x00000000, 0x00000000, 0x00000000, 0x00000000, // Class 3
425                ][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    // OTP values with `*_CLASS_EN` vales set to `kAlertEnableEnabled`
437    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, // Class 0
446                    0x00000000, 0x0000000a, 0x0000000a, 0xffffffff, // Class 1
447                    0x00000000, 0x00000000, 0x00000000, 0x00000000, // Class 2
448                    0x00000000, 0x00000000, 0x00000000, 0x00000000, // Class 3
449                ][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    // A sanity test to make sure the correct CRC algorithm is being used.
459    //
460    // These values are taken from the CRC32 unit tests in
461    // `sw/device/lib/base/crc32_unittest.cc`.
462    #[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}