1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
// https://www.nxp.com/docs/en/application-note/AN13037.pdf

use serde::{Deserialize, Serialize};

use crate::util::is_default;

#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
/// Wrapper around the detailed debug access settings,
/// allowing to completely enable or disable with ease.
///
/// Use the custom variant for more detail.
pub enum DebugAccess {
    /// The state of an empty key (all four relevant words are all-zero)
    ///
    /// It seems that this state has the same effect as `Enabled`,
    /// if not please open a GitHub issue!
    Default,
    /// Debugging access is not possible
    Disabled,
    /// Debugging access is possible
    Enabled,
    /// Debugger must present a Debug Credential (this functionality
    /// is not currently implemented or further exposed).
    Authenticate,
    /// "Bring your own settings"
    Custom(DebugSettings),
}

#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
/// Controls access of debuggers to specific subsystems.
///
/// To understand if access is possible:
/// - factory + customer setting: Enabled -> yes
/// - factor + customer set to Enabled or Authenticate -> need a prepared debug certificate
///   (this is not implemented in this library)
/// - any setting Illegal => device may lock up
/// - any setting Disabled => access not possible!!
///
/// Peculiarities to be aware of:
/// - Factory settings (`CC_SOCU_PIN`, `CC_SOCU_DFLT`) can be made stricter, but not relaxed,
///   using customer settings (`DCFG_CC_SOCU_NS_PIN`, `DCFG_CC_SOCU_NS_DFLT`)
/// - As long as the factory page is not sealed, the setting can be changed there.
/// - To change settings in the customer page, the ping/pong dance must be done.
/// - The enabled bit ("DFLT") determines which setting is potentially in effect
/// - The fixed bit ("PIN") determines whether a debugger may activate the potentially
///   effective settings (by presenting a Debug Authentication certificate)
/// - The non-fixed enabled setting is illegal.
/// - All bits are written in the lower half-word, and must be also written in inverted
///   form in the upper half-word.
/// - Except, it seems, the default / empty setting, where both PIN and DFLT words are all zero.
///
/// In this implementation, we disregard customer settings from configuration files,
/// enforcing that only the firmware changes them.
///
/// TODO: We could also model the factory default, where the entire word is zeros.
/// This would need some special handling + research.
pub enum DebugSetting {
    /// Default state at startup. Will only be accepted when writing to
    /// factory settings when *all* settings are default.
    Default,
    /// Disabled at startup, debugging not possible
    Disabled,
    /// Debugging enabled at startup (debugger can't turn off).
    /// "Access to the sub-domain is always enabled."
    Enabled,
    // /// Enabled the policy, but it could be changed by authenticated debugger
    /// According to UM 11126, 51.7.14, Table 1064:
    /// "Illegal setting. Part may lock-up if this setting is selected."
    Illegal,
    /// Debugging disabled at startup, only authenticated debugger can access
    Authenticate,
}

impl Default for DebugAccess {
    /// Set to default, so that `lpc55 factory-settings` does not
    /// turn off debugging on an otherwise empty key, unless explicitly
    /// configured to be turned off.
    fn default() -> Self {
        Self::Default
    }
}

impl Default for DebugSetting {
    /// Set to enabled, so that `lpc55 factory-settings` does not
    /// turn off debugging on an otherwise empty key, unless explicitly
    /// configured to be turned off.
    ///
    /// It might be possible to set the default to `Self::Default` here,
    /// if we research whether its valid for some settings to stay
    /// 0 in both bits and also in the "inverted" section.
    fn default() -> Self {
        Self::Enabled
    }
}

impl DebugSetting {
    /// Called `PIN` by NXP, e.g in UM 11126, 51.7.14
    ///
    /// The interpretation is that the setting in the "enabled"
    /// bit can not be changed by the debugger (i.e. it can
    /// not enabled debugging by providing an appropriate Debug
    /// Credential (DC)).
    ///
    /// In their words:
    /// "A bitmask that specifies which debug domains are predetermined
    /// by device configuration."
    fn fixed_bit(&self) -> u32 {
        use DebugSetting::*;
        match *self {
            Authenticate | Default | Illegal => 0,
            Disabled | Enabled => 1,
        }
    }
    /// Called `DFLT` by NXP, e.g in UM 11126, 51.7.14
    ///
    /// The setting that is applied during startup.
    ///
    /// In their words:
    /// "Provides the final access level for those bits that the SOCU_PIN
    /// field indicated are predetermined by device configuration."
    ///
    /// It seems that the meaning of this bit is incorrectly used
    /// for the enabled and disabled states in Table 1064 of that section.
    /// And indeed, AN13037 confirms this.
    fn enabled_bit(&self) -> u32 {
        use DebugSetting::*;
        match *self {
            Disabled | Default | Authenticate => 0,
            Enabled | Illegal => 1,
        }
    }
}

impl From<[bool; 2]> for DebugSetting {
    fn from(bits: [bool; 2]) -> Self {
        let [fixed, enabled] = bits;
        use DebugSetting::*;
        //  UM, 517.1.4, Table 1064
        //  It seems that Enabled and Disabled are mixed up / incorrect in the table.
        //
        //  Indeed, AN13037 confirms this.
        match (fixed, enabled) {
            (true, true) => Enabled,
            (false, false) => Authenticate,
            (false, true) => Illegal,
            (true, false) => Disabled,
        }
    }
}

#[derive(
    Clone, Copy, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize,
)]
#[serde(rename_all = "kebab-case")]
// The `{non,}secure_{non,}invasive` settings pertain to CPU0,
// whereas CPU1 has two separate settings.
pub struct DebugSettings {
    #[serde(default)]
    #[serde(skip_serializing_if = "is_default")]
    /// non-invasive debugging of the TrustZone non-secure domain of CPU0
    pub nonsecure_noninvasive: DebugSetting,
    #[serde(default)]
    #[serde(skip_serializing_if = "is_default")]
    /// invasive debugging of the TrustZone non-secure domain of CPU0
    pub nonsecure_invasive: DebugSetting,
    #[serde(default)]
    #[serde(skip_serializing_if = "is_default")]
    /// non-invasive debugging of the TrustZone secure domain of CPU0
    pub secure_noninvasive: DebugSetting,
    #[serde(default)]
    #[serde(skip_serializing_if = "is_default")]
    /// invasive debugging of the TrustZone secure domain of CPU0
    pub secure_invasive: DebugSetting,
    #[serde(default)]
    #[serde(skip_serializing_if = "is_default")]
    /// non-invasive debugging of CPU1
    pub cpu1_noninvasive: DebugSetting,
    #[serde(default)]
    #[serde(skip_serializing_if = "is_default")]
    /// invasive debugging of CPU1
    pub cpu1_invasive: DebugSetting,

    /// JTAG test access port
    #[serde(default)]
    #[serde(skip_serializing_if = "is_default")]
    pub jtag_tap: DebugSetting,

    /// Flash mass erase command
    #[serde(default)]
    #[serde(skip_serializing_if = "is_default")]
    pub flash_mass_erase_command: DebugSetting,

    /// ISP boot command
    #[serde(default)]
    #[serde(skip_serializing_if = "is_default")]
    pub isp_boot_command: DebugSetting,
    /// FA (fault analysis) command
    #[serde(default)]
    #[serde(skip_serializing_if = "is_default")]
    pub fault_analysis_command: DebugSetting,

    /// enforce UUID match during debug authentication
    #[serde(default)]
    #[serde(skip_serializing_if = "is_default")]
    pub check_uuid: bool,
}

impl DebugSettings {
    pub fn are_all_legal(&self) -> bool {
        [
            self.nonsecure_noninvasive,
            self.nonsecure_invasive,
            self.secure_noninvasive,
            self.secure_invasive,
            self.cpu1_noninvasive,
            self.cpu1_invasive,
            self.jtag_tap,
            self.flash_mass_erase_command,
            self.isp_boot_command,
            self.fault_analysis_command,
        ]
        .iter()
        .all(|&setting| setting != DebugSetting::Illegal)
    }

    pub fn are_all_non_default(&self) -> bool {
        [
            self.nonsecure_noninvasive,
            self.nonsecure_invasive,
            self.secure_noninvasive,
            self.secure_invasive,
            self.cpu1_noninvasive,
            self.cpu1_invasive,
            self.jtag_tap,
            self.flash_mass_erase_command,
            self.isp_boot_command,
            self.fault_analysis_command,
        ]
        .iter()
        .all(|&setting| setting != DebugSetting::Default)
    }

    pub fn are_all_default(&self) -> bool {
        *self == DebugAccess::Default.into()
    }
}

impl From<DebugAccess> for DebugSettings {
    fn from(value: DebugAccess) -> Self {
        use DebugSetting::*;

        fn filled_with(setting: DebugSetting, check_uuid: bool) -> DebugSettings {
            DebugSettings {
                nonsecure_noninvasive: setting,
                nonsecure_invasive: setting,
                secure_noninvasive: setting,
                secure_invasive: setting,
                cpu1_noninvasive: setting,
                cpu1_invasive: setting,
                jtag_tap: setting,
                flash_mass_erase_command: setting,
                isp_boot_command: setting,
                fault_analysis_command: setting,
                check_uuid,
            }
        }
        match value {
            DebugAccess::Default => filled_with(Default, false),
            DebugAccess::Disabled => filled_with(Disabled, true),
            DebugAccess::Enabled => filled_with(Enabled, false),
            DebugAccess::Authenticate => filled_with(Authenticate, false),

            DebugAccess::Custom(settings) => settings,
        }
    }
}

impl From<DebugSettings> for DebugAccess {
    fn from(settings: DebugSettings) -> Self {
        if settings.are_all_default() {
            return DebugAccess::Default;
        };
        if settings == DebugAccess::Authenticate.into() {
            return DebugAccess::Authenticate;
        }
        if settings == DebugAccess::Disabled.into() {
            return DebugAccess::Disabled;
        }
        if settings == DebugAccess::Enabled.into() {
            return DebugAccess::Enabled;
        }
        DebugAccess::Custom(settings)
    }
}

impl From<[u32; 2]> for DebugSettings {
    fn from(value: [u32; 2]) -> Self {
        // fix = debugger can't change = PIN
        // set = DFLT
        let [fix, set] = value;
        if (fix, set) == (0, 0) {
            Self::from(DebugAccess::Default)
        } else {
            let from_bit =
                |bit: usize| DebugSetting::from([((fix >> bit) & 1) != 0, ((set >> bit) & 1) != 0]);
            Self {
                nonsecure_noninvasive: from_bit(0),
                nonsecure_invasive: from_bit(1),
                secure_noninvasive: from_bit(2),
                secure_invasive: from_bit(3),
                jtag_tap: from_bit(4),
                cpu1_invasive: from_bit(5),
                isp_boot_command: from_bit(6),
                fault_analysis_command: from_bit(7),
                flash_mass_erase_command: from_bit(8),
                cpu1_noninvasive: from_bit(9),
                check_uuid: ((fix >> 15) & 1) != 0,
            }
        }
    }
}

impl From<DebugSettings> for [u32; 2] {
    fn from(settings: DebugSettings) -> [u32; 2] {
        if settings.are_all_default() {
            return [0, 0];
        };

        assert!(settings.are_all_legal());
        assert!(settings.are_all_non_default());

        let mut fixed: u32 = 0;
        let mut enabled: u32 = 0;

        let mut set_bits = |setting: DebugSetting, bit: usize| {
            fixed |= setting.fixed_bit() << bit;
            enabled |= setting.enabled_bit() << bit;
        };

        set_bits(settings.nonsecure_noninvasive, 0);
        set_bits(settings.nonsecure_invasive, 1);
        set_bits(settings.secure_noninvasive, 2);
        set_bits(settings.secure_invasive, 3);
        set_bits(settings.jtag_tap, 4);
        set_bits(settings.cpu1_invasive, 5);
        set_bits(settings.isp_boot_command, 6);
        set_bits(settings.fault_analysis_command, 7);
        set_bits(settings.flash_mass_erase_command, 8);
        set_bits(settings.cpu1_noninvasive, 9);

        fixed |= (settings.check_uuid as u32) << 15;

        // "Inverse value of [15:0]"
        fixed |= ((!fixed) & 0xffff) << 16;
        enabled |= ((!enabled) & 0xffff) << 16;

        [fixed, enabled]
    }
}