lpc55_hal/peripherals/
pfr.rs

1use core::result::Result;
2// use cortex_m_semihosting::{heprint,heprintln};
3use crate::{drivers::clocks::Clocks, typestates::init_state};
4use core::ptr::copy_nonoverlapping;
5
6#[derive(Copy, Clone, PartialEq)]
7pub enum KeyType {
8    Sbkek = 0x00,
9    User = 0x01,
10    Uds = 0x02,
11    PrinceRegion0 = 0x03,
12    PrinceRegion1 = 0x04,
13    PrinceRegion2 = 0x05,
14}
15
16#[derive(Copy, Clone)]
17#[repr(C)]
18pub struct IvCodePrinceRegion {
19    pub keycode_header: u32,
20    pub iv: [u8; 52],
21}
22
23#[derive(Copy, Clone)]
24#[repr(C)]
25pub struct Cfpa {
26    pub header: u32,
27    pub version: u32,
28    pub secure_fw_version: u32,
29    pub ns_fw_version: u32,
30    pub image_key_revoke: u32,
31
32    reserved0: [u8; 4],
33
34    pub rotkh_revoke: u32,
35    vendor_usage: u32,
36    pub dcfg_ns_pin: u32,
37    pub dcfg_ns_dflt: u32,
38    enable_fa_mode: u32,
39
40    reserved1: [u8; 4],
41    // 12 * 4
42
43    // + (4 + 52) * 3
44    pub iv_code_prince_region: [IvCodePrinceRegion; 3],
45
46    // + 40 + 224 + 32
47    reserved2: [u8; 40],
48    pub customer_data: [u8; 224],
49    sha256: [u8; 32],
50}
51
52impl Cfpa {
53    /// Check if everything has been done to set up a particular HW key.
54    pub fn key_provisioned(&self, key_type: KeyType) -> bool {
55        match key_type {
56            // If there is a nonzero PRINCE IV in CFPA, then it must have provisioned.
57            KeyType::PrinceRegion0 | KeyType::PrinceRegion1 | KeyType::PrinceRegion2 => {
58                let mut iv_or = 0;
59                let index = (key_type as usize) - (KeyType::PrinceRegion0 as usize);
60                for i in 0..self.iv_code_prince_region[index].iv.len() {
61                    iv_or |= self.iv_code_prince_region[index].iv[i];
62                }
63
64                iv_or != 0
65            }
66            // Not handling the other key types currently.
67            _ => false,
68        }
69    }
70}
71
72#[derive(Copy, Clone)]
73#[repr(C)]
74pub struct Cmpa {
75    pub boot_cfg: u32,
76    pub spi_flash_cfg: u32,
77    pub usb_vid: u16,
78    pub usb_pid: u16,
79    pub sdio_cfg: u32,
80    pub dcfg_pin: u32,
81    pub dcfg_dflt: u32,
82    pub dap_vendor_usage: u32,
83    pub secure_boot_cfg: u32,
84    pub prince_base_addr: u32,
85    pub prince_sr: [u32; 3],
86    reserved0: [u8; 32],
87
88    pub rotkh: [u8; 32],
89    reserved1: [u8; 144],
90
91    pub customer_data: [u8; 224],
92    sha256: [u8; 32],
93}
94
95// This compile time guarantees that Cmpa and Cfpa are 512 bytes.
96const _: () = {
97    assert!(size_of::<Cmpa>() == 512);
98    assert!(size_of::<Cfpa>() == 512);
99};
100
101// #define BOOTLOADER_API_TREE_POINTER (bootloader_tree_t*) 0x130010f0
102#[repr(C)]
103struct BootloaderTree {
104    // All this does is a soft reset.
105    run_bootloader: extern "C" fn(arg: &u32) -> (),
106
107    version: u32,
108    copyright: *const char,
109    reserved0: u32,
110
111    flash_driver: &'static FlashDriverInterface,
112
113    // don't need these.
114    reserved_kb_interface: u32,
115    reserved1: [u32; 4],
116    reserved_skboot_authenticate_interface: u32,
117}
118
119#[repr(C)]
120struct FlashDriverInterface {
121    version: u32,
122    flash_init: unsafe extern "C" fn(config: &mut FlashConfig) -> u32,
123    flash_erase: unsafe extern "C" fn(
124        config: &mut FlashConfig,
125        start: u32,
126        length_in_bytes: u32,
127        key: u32,
128    ) -> u32,
129    flash_program: unsafe extern "C" fn(
130        config: &mut FlashConfig,
131        start: u32,
132        src: *const u8,
133        length_in_bytes: u32,
134    ) -> u32,
135    flash_verify_erase:
136        unsafe extern "C" fn(config: &mut FlashConfig, start: u32, length_in_bytes: u32) -> u32,
137    flash_verify_program: unsafe extern "C" fn(
138        config: &mut FlashConfig,
139        start: u32,
140        length_in_bytes: u32,
141        expected_data: *const u8,
142        failed_address: &mut u32,
143        failed_data: &mut u32,
144    ) -> u32,
145
146    flash_get_property:
147        unsafe extern "C" fn(config: &mut FlashConfig, tag: u32, value: &mut u32) -> u32,
148    reserved: [u32; 3],
149
150    ffr_init: unsafe extern "C" fn(config: &mut FlashConfig) -> u32,
151    ffr_lock_all: unsafe extern "C" fn(config: &mut FlashConfig) -> u32,
152    ffr_cust_factory_page_write: unsafe extern "C" fn(
153        config: &mut FlashConfig,
154        page_data: *const u8,
155        seal_part: bool,
156    ) -> u32,
157    ffr_get_uuid: unsafe extern "C" fn(config: &mut FlashConfig, uuid: *mut u8) -> u32,
158    ffr_get_customer_data: unsafe extern "C" fn(
159        config: &mut FlashConfig,
160        pData: *mut u8,
161        offset: u32,
162        len: u32,
163    ) -> u32,
164
165    // TODO
166    ffr_keystore_write: unsafe extern "C" fn(config: &mut FlashConfig) -> u32,
167    ffr_keystore_get_ac:
168        unsafe extern "C" fn(config: &mut FlashConfig, activation_code: *mut u8) -> u32,
169    ffr_keystore_get_kc:
170        unsafe extern "C" fn(config: &mut FlashConfig, keycode: *mut u8, key_index: u32) -> u32,
171
172    ffr_infield_page_write:
173        unsafe extern "C" fn(config: &mut FlashConfig, page_data: *const u8, valid_len: u32) -> u32,
174    ffr_get_customer_infield_data: unsafe extern "C" fn(
175        config: &mut FlashConfig,
176        page_data: *mut u8,
177        offset: u32,
178        len: u32,
179    ) -> u32,
180}
181
182#[repr(C)]
183pub struct FlashFfrConfig {
184    pub ffr_block_base: u32,
185    pub ffr_total_size: u32,
186    pub ffr_page_size: u32,
187    pub cfpa_page_version: u32,
188    pub cfpa_page_offset: u32,
189}
190
191#[repr(C)]
192pub struct FlashModeConfig {
193    sys_freq_in_mhz: u32,
194    single_word_mode: u32,
195    write_mode: u32,
196    read_mode: u32,
197}
198
199#[repr(C)]
200pub struct FlashConfig {
201    pflash_block_base: u32,
202    pflash_total_size: u32,
203    pflash_block_count: u32,
204    pflash_page_size: u32,
205
206    pflash_sector_size: u32,
207
208    pub ffr_config: FlashFfrConfig,
209    pub mode_config: FlashModeConfig,
210}
211
212impl FlashConfig {
213    fn new(system_clock_freq_in_mhz: u32) -> FlashConfig {
214        let flash_ffr_config = FlashFfrConfig {
215            ffr_block_base: 0,
216            ffr_total_size: 0,
217            ffr_page_size: 0,
218            cfpa_page_version: 0,
219            cfpa_page_offset: 0,
220        };
221        let flash_mode_config = FlashModeConfig {
222            sys_freq_in_mhz: system_clock_freq_in_mhz,
223            single_word_mode: 0,
224            write_mode: 0,
225            read_mode: 0,
226        };
227        FlashConfig {
228            pflash_block_base: 0,
229            pflash_total_size: 0,
230            pflash_block_count: 0,
231            pflash_page_size: 0,
232            pflash_sector_size: 0,
233            ffr_config: flash_ffr_config,
234            mode_config: flash_mode_config,
235        }
236    }
237}
238
239pub struct Pfr<State = init_state::Unknown> {
240    pub flash_config: FlashConfig,
241    pub _state: State,
242}
243impl<State> Pfr<State> {
244    fn bootloader_api_tree() -> &'static mut BootloaderTree {
245        #[allow(clippy::transmute_ptr_to_ref)]
246        unsafe {
247            core::mem::transmute(0x130010f0u32 as *const ())
248        }
249    }
250    fn check_error(err: u32) -> Result<(), u32> {
251        if err == 0 {
252            Ok(())
253        } else {
254            Err(err)
255        }
256    }
257}
258
259impl Default for Pfr {
260    fn default() -> Self {
261        Self::new()
262    }
263}
264impl Pfr {
265    pub fn new() -> Self {
266        Self {
267            flash_config: FlashConfig::new(0),
268            _state: init_state::Unknown,
269        }
270    }
271
272    pub fn enabled(mut self, clock_config: &Clocks) -> Result<Pfr<init_state::Enabled>, u32> {
273        self.flash_config = FlashConfig::new(clock_config.system_frequency.0 / 1_000_000);
274
275        let flash_init = Self::bootloader_api_tree().flash_driver.flash_init;
276        let ffr_init = Self::bootloader_api_tree().flash_driver.ffr_init;
277
278        Self::check_error(unsafe { flash_init(&mut self.flash_config) })?;
279        Self::check_error(unsafe { ffr_init(&mut self.flash_config) })?;
280
281        Ok(Pfr {
282            flash_config: self.flash_config,
283            _state: init_state::Enabled(()),
284        })
285    }
286}
287
288impl Pfr<init_state::Enabled> {
289    pub fn read_cmpa(&mut self) -> Result<Cmpa, u32> {
290        let mut cmpa_bytes = [0u8; 512];
291
292        let ffr_get_customer_data = Self::bootloader_api_tree()
293            .flash_driver
294            .ffr_get_customer_data;
295
296        Self::check_error(unsafe {
297            ffr_get_customer_data(&mut self.flash_config, cmpa_bytes.as_mut_ptr(), 0, 512)
298        })?;
299        // heprintln!("cfpa:").ok();
300        // dump_hex!(cfpa_bytes, 512);
301
302        let cmpa: &Cmpa = unsafe { &*(cmpa_bytes.as_ptr() as *const _) };
303
304        Ok(*cmpa)
305    }
306
307    /// Get a readonly static reference to the customer data in CMPA.
308    pub fn cmpa_customer_data(&mut self) -> &'static [u8] {
309        let cmpa_ptr = (0x9E500) as *const u8;
310        let slice = unsafe { core::slice::from_raw_parts(cmpa_ptr, 224) };
311        slice
312    }
313
314    /// Keeping here for reference, but this sometimes returns unexpected old versions of the CFPA page that
315    /// are not seen on scratch, ping, or pong pages.
316    /// Findings:
317    /// - Immediately after CFPA is updated, this method returns the latest CFPA data.
318    /// - After boot/reset, this method will potentially return expected old versions of CFPA.
319    /// - There is a pattern of how to increment VERSION to result in this method returning old CFPA versions or not which is impractical.
320    ///
321    /// It's almost like there is some other cfpa page storage not documented and this bootrom method mismanages the VERSION.
322    pub fn read_cfpa_with_bootrom(&mut self) -> Result<Cfpa, u32> {
323        let mut cfpa_bytes = [0u8; 512];
324
325        let ffr_get_customer_infield_data = Self::bootloader_api_tree()
326            .flash_driver
327            .ffr_get_customer_infield_data;
328
329        Self::check_error(unsafe {
330            ffr_get_customer_infield_data(&mut self.flash_config, cfpa_bytes.as_mut_ptr(), 0, 512)
331        })?;
332        // heprintln!("cfpa:").ok();
333        // dump_hex!(cfpa_bytes, 512);
334
335        let cfpa: &Cfpa = unsafe { &*(cfpa_bytes.as_ptr() as *const _) };
336
337        Ok(*cfpa)
338    }
339
340    /// Reads CFPA without use of bootrom.  Appears that the bootrom method sometimes
341    /// returns previous versions of the CFPA page (not seen on scratch, ping, or pong pages).
342    /// This method always returns the most recently updated Cfpa from ping or pong pages.
343    pub fn read_latest_cfpa(&mut self) -> Result<Cfpa, u32> {
344        use core::ptr::copy_nonoverlapping;
345        let mut cfpa_bytes = [0u32; 128];
346
347        let ping_ptr = (0x0009_DE00 + 512) as *const u32;
348        let pong_ptr = (0x0009_DE00 + 512 + 512) as *const u32;
349
350        let ping_version: u32 = unsafe { *(ping_ptr).offset(1) };
351        let pong_version: u32 = unsafe { *(pong_ptr).offset(1) };
352
353        let cfpa_ptr: *const u32 = if ping_version > pong_version {
354            ping_ptr
355        } else {
356            pong_ptr
357        };
358
359        unsafe {
360            copy_nonoverlapping(cfpa_ptr, cfpa_bytes.as_mut_ptr(), 128);
361        }
362
363        let cfpa: &Cfpa = unsafe { &*(cfpa_bytes.as_ptr() as *const _) };
364
365        Ok(*cfpa)
366    }
367
368    pub fn read_cfpa_ping(&mut self) -> Result<Cfpa, u32> {
369        let mut cfpa_bytes = [0u32; 128];
370
371        const CFPA_PTR: *const u32 = (0x0009_DE00 + 512) as *const u32;
372        unsafe {
373            copy_nonoverlapping(CFPA_PTR, cfpa_bytes.as_mut_ptr(), 128);
374        }
375
376        let cfpa: &Cfpa = unsafe { &*(cfpa_bytes.as_ptr() as *const _) };
377
378        Ok(*cfpa)
379    }
380
381    pub fn read_cfpa_pong(&mut self) -> Result<Cfpa, u32> {
382        let mut cfpa_bytes = [0u32; 128];
383
384        const CFPA_PTR: *const u32 = (0x0009_DE00 + 512 + 512) as *const u32;
385        unsafe {
386            copy_nonoverlapping(CFPA_PTR, cfpa_bytes.as_mut_ptr(), 128);
387        }
388
389        let cfpa: &Cfpa = unsafe { &*(cfpa_bytes.as_ptr() as *const _) };
390
391        Ok(*cfpa)
392    }
393
394    pub fn write_cfpa(&mut self, cfpa: &Cfpa) -> Result<(), u32> {
395        let ffr_infield_page_write = Self::bootloader_api_tree()
396            .flash_driver
397            .ffr_infield_page_write;
398        let cfpa_bytes: *const u8 = unsafe { core::mem::transmute(cfpa as *const Cfpa) };
399        Self::check_error(unsafe {
400            ffr_infield_page_write(&mut self.flash_config, cfpa_bytes, 512)
401        })?;
402        Ok(())
403    }
404
405    pub fn read_key_code(&mut self, key_type: KeyType) -> Result<[u8; 52], u32> {
406        let mut bytes = [0u8; 52];
407        let ffr_keystore_get_kc = Self::bootloader_api_tree().flash_driver.ffr_keystore_get_kc;
408
409        Self::check_error(unsafe {
410            ffr_keystore_get_kc(&mut self.flash_config, bytes.as_mut_ptr(), key_type as u32)
411        })?;
412
413        Ok(bytes)
414    }
415
416    pub fn read_activation_code(&mut self) -> Result<[u8; 1192], u32> {
417        let mut ac = [0u8; 1192];
418        let ffr_keystore_get_ac = Self::bootloader_api_tree().flash_driver.ffr_keystore_get_ac;
419        Self::check_error(unsafe { ffr_keystore_get_ac(&mut self.flash_config, ac.as_mut_ptr()) })?;
420
421        Ok(ac)
422    }
423
424    /// Set write protection to PFR pages.  Lasts until next power on reset.
425    pub fn lock_all(&mut self) -> Result<(), u32> {
426        let ffr_lock_all = Self::bootloader_api_tree().flash_driver.ffr_lock_all;
427        Self::check_error(unsafe { ffr_lock_all(&mut self.flash_config) })?;
428        Ok(())
429    }
430}