Skip to main content

rp235x_hal/
block.rs

1//! Support for the RP235x Boot ROM's "Block" structures
2//!
3//! Blocks contain pointers, to form Block Loops.
4//!
5//! The `IMAGE_DEF` Block (here the `ImageDef` type) tells the ROM how to boot a
6//! firmware image. The `PARTITION_TABLE` Block (here the `PartitionTable` type)
7//! tells the ROM how to divide the flash space up into partitions.
8
9// These all have a 1 byte size
10
11/// An item ID for encoding a Vector Table address
12pub const ITEM_1BS_VECTOR_TABLE: u8 = 0x03;
13
14/// An item ID for encoding a Rolling Window Delta
15pub const ITEM_1BS_ROLLING_WINDOW_DELTA: u8 = 0x05;
16
17/// An item ID for encoding a Signature
18pub const ITEM_1BS_SIGNATURE: u8 = 0x09;
19
20/// An item ID for encoding a Salt
21pub const ITEM_1BS_SALT: u8 = 0x0c;
22
23/// An item ID for encoding an Image Type
24pub const ITEM_1BS_IMAGE_TYPE: u8 = 0x42;
25
26/// An item ID for encoding the image's Entry Point
27pub const ITEM_1BS_ENTRY_POINT: u8 = 0x44;
28
29/// An item ID for encoding the definition of a Hash
30pub const ITEM_2BS_HASH_DEF: u8 = 0x47;
31
32/// An item ID for encoding a Version
33pub const ITEM_1BS_VERSION: u8 = 0x48;
34
35/// An item ID for encoding a Hash
36pub const ITEM_1BS_HASH_VALUE: u8 = 0x4b;
37
38// These all have a 2-byte size
39
40/// An item ID for encoding a Load Map
41pub const ITEM_2BS_LOAD_MAP: u8 = 0x06;
42
43/// An item ID for encoding a Partition Table
44pub const ITEM_2BS_PARTITION_TABLE: u8 = 0x0a;
45
46/// An item ID for encoding a placeholder entry that is ignored
47///
48/// Allows a Block to not be empty.
49pub const ITEM_2BS_IGNORED: u8 = 0xfe;
50
51/// An item ID for encoding the special last item in a Block
52///
53/// It records how long the Block is.
54pub const ITEM_2BS_LAST: u8 = 0xff;
55
56// Options for ITEM_1BS_IMAGE_TYPE
57
58/// A [`ITEM_1BS_IMAGE_TYPE`] value bitmask to mark an image as invalid
59pub const IMAGE_TYPE_INVALID: u16 = 0x0000;
60
61/// A [`ITEM_1BS_IMAGE_TYPE`] value bitmask to mark an image as an executable
62pub const IMAGE_TYPE_EXE: u16 = 0x0001;
63
64/// A [`ITEM_1BS_IMAGE_TYPE`] value bitmask to mark an image as data
65pub const IMAGE_TYPE_DATA: u16 = 0x0002;
66
67/// A [`ITEM_1BS_IMAGE_TYPE`] value bitmask to mark the CPU security mode as unspecified
68pub const IMAGE_TYPE_EXE_TYPE_SECURITY_UNSPECIFIED: u16 = 0x0000;
69
70/// A [`ITEM_1BS_IMAGE_TYPE`] value bitmask to mark the CPU security mode as Non Secure
71pub const IMAGE_TYPE_EXE_TYPE_SECURITY_NS: u16 = 0x0010;
72
73/// A [`ITEM_1BS_IMAGE_TYPE`] value bitmask to mark the CPU security mode as Non Secure
74pub const IMAGE_TYPE_EXE_TYPE_SECURITY_S: u16 = 0x0020;
75
76/// A [`ITEM_1BS_IMAGE_TYPE`] value bitmask to mark the CPU type as Arm
77pub const IMAGE_TYPE_EXE_CPU_ARM: u16 = 0x0000;
78
79/// A [`ITEM_1BS_IMAGE_TYPE`] value bitmask to mark the CPU type as RISC-V
80pub const IMAGE_TYPE_EXE_CPU_RISCV: u16 = 0x0100;
81
82/// A [`ITEM_1BS_IMAGE_TYPE`] value bitmask to mark the CPU as an RP2040
83pub const IMAGE_TYPE_EXE_CHIP_RP2040: u16 = 0x0000;
84
85/// A [`ITEM_1BS_IMAGE_TYPE`] value bitmask to mark the CPU as an RP2350
86pub const IMAGE_TYPE_EXE_CHIP_RP2350: u16 = 0x1000;
87
88/// A [`ITEM_1BS_IMAGE_TYPE`] value bitmask to mark the image as Try Before You Buy.
89///
90/// This means the image must be marked as 'Bought' with the ROM before the
91/// watchdog times out the trial period, otherwise it is erased and the previous
92/// image will be booted.
93pub const IMAGE_TYPE_TBYB: u16 = 0x8000;
94
95/// This is the magic Block Start value.
96///
97/// The Pico-SDK calls it `PICOBIN_BLOCK_MARKER_START`
98const BLOCK_MARKER_START: u32 = 0xffffded3;
99
100/// This is the magic Block END value.
101///
102/// The Pico-SDK calls it `PICOBIN_BLOCK_MARKER_END`
103const BLOCK_MARKER_END: u32 = 0xab123579;
104
105/// An Image Definition has one item in it - an [`ITEM_1BS_IMAGE_TYPE`]
106pub type ImageDef = Block<1>;
107
108/// A Block as understood by the Boot ROM.
109///
110/// This could be an Image Definition, or a Partition Table, or maybe some other
111/// kind of block.
112///
113/// It contains within the special start and end markers the Boot ROM is looking
114/// for.
115#[derive(Debug)]
116#[repr(C)]
117pub struct Block<const N: usize> {
118    marker_start: u32,
119    items: [u32; N],
120    length: u32,
121    offset: *const u32,
122    marker_end: u32,
123}
124
125unsafe impl<const N: usize> Sync for Block<N> {}
126
127impl<const N: usize> Block<N> {
128    /// Construct a new Binary Block, with the given items.
129    ///
130    /// The length, and the Start and End markers are added automatically. The
131    /// Block Loop pointer initially points to itself.
132    pub const fn new(items: [u32; N]) -> Block<N> {
133        Block {
134            marker_start: BLOCK_MARKER_START,
135            items,
136            length: item_last(N as u16),
137            // offset from this block to next block in loop. By default
138            // we form a Block Loop with a single Block in it.
139            offset: core::ptr::null(),
140            marker_end: BLOCK_MARKER_END,
141        }
142    }
143
144    /// Change the Block Loop offset value.
145    ///
146    /// This method isn't that useful because you can't evaluate the difference
147    /// between two pointers in a const context as the addresses aren't assigned
148    /// until long after the const evaluator has run.
149    ///
150    /// If you think you need this method, you might want to set a unique random
151    /// value here and swap it for the real offset as a post-processing step.
152    pub const fn with_offset(self, offset: *const u32) -> Block<N> {
153        Block { offset, ..self }
154    }
155}
156
157impl Block<0> {
158    /// Construct an empty block.
159    pub const fn empty() -> Block<0> {
160        Block::new([])
161    }
162
163    /// Make the block one word larger
164    pub const fn extend(self, word: u32) -> Block<1> {
165        Block::new([word])
166    }
167}
168
169impl Block<1> {
170    /// Make the block one word larger
171    pub const fn extend(self, word: u32) -> Block<2> {
172        Block::new([self.items[0], word])
173    }
174}
175
176impl Block<2> {
177    /// Make the block one word larger
178    pub const fn extend(self, word: u32) -> Block<3> {
179        Block::new([self.items[0], self.items[1], word])
180    }
181}
182
183impl ImageDef {
184    /// Construct a new IMAGE_DEF Block, for an EXE with the given security and
185    /// architecture.
186    pub const fn arch_exe(security: Security, architecture: Architecture) -> Self {
187        Self::new([item_image_type_exe(security, architecture)])
188    }
189
190    /// Construct a new IMAGE_DEF Block, for an EXE with the given security.
191    ///
192    /// The target architecture is taken from the current build target (i.e. Arm
193    /// or RISC-V).
194    pub const fn exe(security: Security) -> Self {
195        if cfg!(all(target_arch = "riscv32", target_os = "none")) {
196            Self::arch_exe(security, Architecture::Riscv)
197        } else {
198            Self::arch_exe(security, Architecture::Arm)
199        }
200    }
201
202    /// Construct a new IMAGE_DEF Block, for a Non-Secure EXE.
203    ///
204    /// The target architecture is taken from the current build target (i.e. Arm
205    /// or RISC-V).
206    pub const fn non_secure_exe() -> Self {
207        Self::exe(Security::NonSecure)
208    }
209
210    /// Construct a new IMAGE_DEF Block, for a Secure EXE.
211    ///
212    /// The target architecture is taken from the current build target (i.e. Arm
213    /// or RISC-V).
214    pub const fn secure_exe() -> Self {
215        Self::exe(Security::Secure)
216    }
217}
218
219/// We make our partition table this fixed size.
220pub const PARTITION_TABLE_MAX_ITEMS: usize = 128;
221
222/// Describes a unpartitioned space
223#[derive(Debug, Clone, PartialEq, Eq, Default)]
224pub struct UnpartitionedSpace {
225    permissions_and_location: u32,
226    permissions_and_flags: u32,
227}
228
229impl UnpartitionedSpace {
230    /// Create a new unpartitioned space.
231    ///
232    /// It defaults to no permissions.
233    pub const fn new() -> Self {
234        Self {
235            permissions_and_location: 0,
236            permissions_and_flags: 0,
237        }
238    }
239
240    /// Create a new unpartition space from run-time values.
241    ///
242    /// Get these from the ROM function `get_partition_table_info` with an argument of `PT_INFO`.
243    pub const fn from_raw(permissions_and_location: u32, permissions_and_flags: u32) -> Self {
244        Self {
245            permissions_and_location,
246            permissions_and_flags,
247        }
248    }
249
250    /// Add a permission
251    pub const fn with_permission(self, permission: Permission) -> Self {
252        Self {
253            permissions_and_flags: self.permissions_and_flags | permission as u32,
254            permissions_and_location: self.permissions_and_location | permission as u32,
255        }
256    }
257
258    /// Set a flag
259    pub const fn with_flag(self, flag: UnpartitionedFlag) -> Self {
260        Self {
261            permissions_and_flags: self.permissions_and_flags | flag as u32,
262            ..self
263        }
264    }
265
266    /// Get the partition start and end
267    ///
268    /// The offsets are in 4 KiB sectors, inclusive.
269    pub fn get_first_last_sectors(&self) -> (u16, u16) {
270        (
271            (self.permissions_and_location & 0x0000_1FFF) as u16,
272            ((self.permissions_and_location >> 13) & 0x0000_1FFF) as u16,
273        )
274    }
275
276    /// Get the partition start and end
277    ///
278    /// The offsets are in bytes, inclusive.
279    pub fn get_first_last_bytes(&self) -> (u32, u32) {
280        let (first, last) = self.get_first_last_sectors();
281        (u32::from(first) * 4096, (u32::from(last) * 4096) + 4095)
282    }
283
284    /// Check if it has a permission
285    pub fn has_permission(&self, permission: Permission) -> bool {
286        let mask = permission as u32;
287        (self.permissions_and_flags & mask) != 0
288    }
289
290    /// Check if the partition has a flag set
291    pub fn has_flag(&self, flag: UnpartitionedFlag) -> bool {
292        let mask = flag as u32;
293        (self.permissions_and_flags & mask) != 0
294    }
295}
296
297impl core::fmt::Display for UnpartitionedSpace {
298    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
299        let (first, last) = self.get_first_last_bytes();
300        write!(
301            f,
302            "{:#010x}..{:#010x} S:{}{} NS:{}{} B:{}{}",
303            first,
304            last,
305            if self.has_permission(Permission::SecureRead) {
306                'R'
307            } else {
308                '_'
309            },
310            if self.has_permission(Permission::SecureWrite) {
311                'W'
312            } else {
313                '_'
314            },
315            if self.has_permission(Permission::NonSecureRead) {
316                'R'
317            } else {
318                '_'
319            },
320            if self.has_permission(Permission::NonSecureWrite) {
321                'W'
322            } else {
323                '_'
324            },
325            if self.has_permission(Permission::BootRead) {
326                'R'
327            } else {
328                '_'
329            },
330            if self.has_permission(Permission::BootWrite) {
331                'W'
332            } else {
333                '_'
334            }
335        )
336    }
337}
338
339/// Describes a Partition
340#[derive(Debug, Clone, PartialEq, Eq)]
341pub struct Partition {
342    permissions_and_location: u32,
343    permissions_and_flags: u32,
344    id: Option<u64>,
345    extra_families: [u32; 4],
346    extra_families_len: usize,
347    name: [u8; 128],
348}
349
350impl Partition {
351    const FLAGS_HAS_ID: u32 = 0b1;
352    const FLAGS_LINK_TYPE_A_PARTITION: u32 = 0b01 << 1;
353    const FLAGS_LINK_TYPE_OWNER: u32 = 0b10 << 1;
354    const FLAGS_LINK_MASK: u32 = 0b111111 << 1;
355    const FLAGS_HAS_NAME: u32 = 0b1 << 12;
356    const FLAGS_HAS_EXTRA_FAMILIES_SHIFT: u8 = 7;
357    const FLAGS_HAS_EXTRA_FAMILIES_MASK: u32 = 0b11 << Self::FLAGS_HAS_EXTRA_FAMILIES_SHIFT;
358
359    /// Create a new partition, with the given start and end sectors.
360    ///
361    /// It defaults to no permissions.
362    pub const fn new(first_sector: u16, last_sector: u16) -> Self {
363        // 0x2000 sectors of 4 KiB is 32 MiB, which is the total XIP area
364        assert!(first_sector < 0x2000);
365        assert!(last_sector < 0x2000);
366        assert!(first_sector <= last_sector);
367        Self {
368            permissions_and_location: ((last_sector as u32) << 13) | first_sector as u32,
369            permissions_and_flags: 0,
370            id: None,
371            extra_families: [0; 4],
372            extra_families_len: 0,
373            name: [0; 128],
374        }
375    }
376
377    /// Create a new partition from run-time values.
378    ///
379    /// Get these from the ROM function `get_partition_table_info` with an argument of `PARTITION_LOCATION_AND_FLAGS`.
380    pub const fn from_raw(permissions_and_location: u32, permissions_and_flags: u32) -> Self {
381        Self {
382            permissions_and_location,
383            permissions_and_flags,
384            id: None,
385            extra_families: [0; 4],
386            extra_families_len: 0,
387            name: [0; 128],
388        }
389    }
390
391    /// Add a permission
392    pub const fn with_permission(self, permission: Permission) -> Self {
393        Self {
394            permissions_and_location: self.permissions_and_location | permission as u32,
395            permissions_and_flags: self.permissions_and_flags | permission as u32,
396            ..self
397        }
398    }
399
400    /// Set the name of the partition
401    pub const fn with_name(self, name: &str) -> Self {
402        let mut new_name = [0u8; 128];
403        let name = name.as_bytes();
404        let mut idx = 0;
405        new_name[0] = name.len() as u8;
406        while idx < name.len() {
407            new_name[idx + 1] = name[idx];
408            idx += 1;
409        }
410        Self {
411            name: new_name,
412            permissions_and_flags: self.permissions_and_flags | Self::FLAGS_HAS_NAME,
413            ..self
414        }
415    }
416
417    /// Set the extra families for the partition.
418    ///
419    /// You can supply up to four.
420    pub const fn with_extra_families(self, extra_families: &[u32]) -> Self {
421        assert!(extra_families.len() <= 4);
422        let mut new_extra_families = [0u32; 4];
423        let mut idx = 0;
424        while idx < extra_families.len() {
425            new_extra_families[idx] = extra_families[idx];
426            idx += 1;
427        }
428        Self {
429            extra_families: new_extra_families,
430            extra_families_len: extra_families.len(),
431            permissions_and_flags: (self.permissions_and_flags
432                & !Self::FLAGS_HAS_EXTRA_FAMILIES_MASK)
433                | ((extra_families.len() as u32) << Self::FLAGS_HAS_EXTRA_FAMILIES_SHIFT),
434            ..self
435        }
436    }
437
438    /// Set the ID
439    pub const fn with_id(self, id: u64) -> Self {
440        Self {
441            id: Some(id),
442            permissions_and_flags: self.permissions_and_flags | Self::FLAGS_HAS_ID,
443            ..self
444        }
445    }
446
447    /// Add a link
448    pub const fn with_link(self, link: Link) -> Self {
449        let mut new_flags = self.permissions_and_flags & !Self::FLAGS_LINK_MASK;
450        match link {
451            Link::Nothing => {}
452            Link::ToA { partition_idx } => {
453                assert!(partition_idx < 16);
454                new_flags |= Self::FLAGS_LINK_TYPE_A_PARTITION;
455                new_flags |= (partition_idx as u32) << 3;
456            }
457            Link::ToOwner { partition_idx } => {
458                assert!(partition_idx < 16);
459                new_flags |= Self::FLAGS_LINK_TYPE_OWNER;
460                new_flags |= (partition_idx as u32) << 3;
461            }
462        }
463        Self {
464            permissions_and_flags: new_flags,
465            ..self
466        }
467    }
468
469    /// Set a flag
470    pub const fn with_flag(self, flag: PartitionFlag) -> Self {
471        Self {
472            permissions_and_flags: self.permissions_and_flags | flag as u32,
473            ..self
474        }
475    }
476
477    /// Get the partition start and end
478    ///
479    /// The offsets are in 4 KiB sectors, inclusive.
480    pub fn get_first_last_sectors(&self) -> (u16, u16) {
481        (
482            (self.permissions_and_location & 0x0000_1FFF) as u16,
483            ((self.permissions_and_location >> 13) & 0x0000_1FFF) as u16,
484        )
485    }
486
487    /// Get the partition start and end
488    ///
489    /// The offsets are in bytes, inclusive.
490    pub fn get_first_last_bytes(&self) -> (u32, u32) {
491        let (first, last) = self.get_first_last_sectors();
492        (u32::from(first) * 4096, (u32::from(last) * 4096) + 4095)
493    }
494
495    /// Check if it has a permission
496    pub fn has_permission(&self, permission: Permission) -> bool {
497        let mask = permission as u32;
498        (self.permissions_and_flags & mask) != 0
499    }
500
501    /// Get which extra families are allowed in this partition
502    pub fn get_extra_families(&self) -> &[u32] {
503        &self.extra_families[0..self.extra_families_len]
504    }
505
506    /// Get the name of the partition
507    ///
508    /// Returns `None` if there's no name, or the name is not valid UTF-8.
509    pub fn get_name(&self) -> Option<&str> {
510        let len = self.name[0] as usize;
511        if len == 0 {
512            None
513        } else {
514            core::str::from_utf8(&self.name[1..=len]).ok()
515        }
516    }
517
518    /// Get the ID
519    pub fn get_id(&self) -> Option<u64> {
520        self.id
521    }
522
523    /// Check if this partition is linked
524    pub fn get_link(&self) -> Link {
525        if (self.permissions_and_flags & Self::FLAGS_LINK_TYPE_A_PARTITION) != 0 {
526            let partition_idx = ((self.permissions_and_flags >> 3) & 0x0F) as u8;
527            Link::ToA { partition_idx }
528        } else if (self.permissions_and_flags & Self::FLAGS_LINK_TYPE_OWNER) != 0 {
529            let partition_idx = ((self.permissions_and_flags >> 3) & 0x0F) as u8;
530            Link::ToOwner { partition_idx }
531        } else {
532            Link::Nothing
533        }
534    }
535
536    /// Check if the partition has a flag set
537    pub fn has_flag(&self, flag: PartitionFlag) -> bool {
538        let mask = flag as u32;
539        (self.permissions_and_flags & mask) != 0
540    }
541}
542
543impl core::fmt::Display for Partition {
544    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
545        let (first, last) = self.get_first_last_bytes();
546        write!(
547            f,
548            "{:#010x}..{:#010x} S:{}{} NS:{}{} B:{}{}",
549            first,
550            last,
551            if self.has_permission(Permission::SecureRead) {
552                'R'
553            } else {
554                '_'
555            },
556            if self.has_permission(Permission::SecureWrite) {
557                'W'
558            } else {
559                '_'
560            },
561            if self.has_permission(Permission::NonSecureRead) {
562                'R'
563            } else {
564                '_'
565            },
566            if self.has_permission(Permission::NonSecureWrite) {
567                'W'
568            } else {
569                '_'
570            },
571            if self.has_permission(Permission::BootRead) {
572                'R'
573            } else {
574                '_'
575            },
576            if self.has_permission(Permission::BootWrite) {
577                'W'
578            } else {
579                '_'
580            }
581        )
582    }
583}
584
585/// Describes a partition table.
586///
587/// Don't store this as a static - make sure you convert it to a block.
588#[derive(Clone)]
589pub struct PartitionTableBlock {
590    /// This must look like a block, including the 1 word header and the 3 word footer.
591    contents: [u32; PARTITION_TABLE_MAX_ITEMS],
592    /// This value doesn't include the 1 word header or the 3 word footer
593    num_items: usize,
594}
595
596impl PartitionTableBlock {
597    /// Create an empty Block, big enough for a partition table.
598    ///
599    /// At a minimum you need to call [`Self::add_partition_item`].
600    pub const fn new() -> PartitionTableBlock {
601        let mut contents = [0; PARTITION_TABLE_MAX_ITEMS];
602        contents[0] = BLOCK_MARKER_START;
603        contents[1] = item_last(0);
604        contents[2] = 0;
605        contents[3] = BLOCK_MARKER_END;
606        PartitionTableBlock {
607            contents,
608            num_items: 0,
609        }
610    }
611
612    /// Add a partition to the partition table
613    pub const fn add_partition_item(
614        self,
615        unpartitioned: UnpartitionedSpace,
616        partitions: &[Partition],
617    ) -> Self {
618        let mut new_table = PartitionTableBlock::new();
619        let mut idx = 0;
620        // copy over old table, with the header but not the footer
621        while idx < self.num_items + 1 {
622            new_table.contents[idx] = self.contents[idx];
623            idx += 1;
624        }
625
626        // 1. add item header space (we fill this in later)
627        let header_idx = idx;
628        new_table.contents[idx] = 0;
629        idx += 1;
630
631        // 2. unpartitioned space flags
632        //
633        // (the location of unpartition space is not recorded here - it is
634        // inferred because the unpartitioned space is where the partitions are
635        // not)
636        new_table.contents[idx] = unpartitioned.permissions_and_flags;
637        idx += 1;
638
639        // 3. partition info
640
641        let mut partition_no = 0;
642        while partition_no < partitions.len() {
643            // a. permissions_and_location (4K units)
644            new_table.contents[idx] = partitions[partition_no].permissions_and_location;
645            idx += 1;
646
647            // b. permissions_and_flags
648            new_table.contents[idx] = partitions[partition_no].permissions_and_flags;
649            idx += 1;
650
651            // c. ID
652            if let Some(id) = partitions[partition_no].id {
653                new_table.contents[idx] = id as u32;
654                new_table.contents[idx + 1] = (id >> 32) as u32;
655                idx += 2;
656            }
657
658            // d. Extra Families
659            let mut extra_families_idx = 0;
660            while extra_families_idx < partitions[partition_no].extra_families_len {
661                new_table.contents[idx] =
662                    partitions[partition_no].extra_families[extra_families_idx];
663                idx += 1;
664                extra_families_idx += 1;
665            }
666
667            // e. Name
668            let mut name_idx = 0;
669            while name_idx < partitions[partition_no].name[0] as usize {
670                let name_chunk = [
671                    partitions[partition_no].name[name_idx],
672                    partitions[partition_no].name[name_idx + 1],
673                    partitions[partition_no].name[name_idx + 2],
674                    partitions[partition_no].name[name_idx + 3],
675                ];
676                new_table.contents[idx] = u32::from_le_bytes(name_chunk);
677                name_idx += 4;
678                idx += 1;
679            }
680
681            partition_no += 1;
682        }
683
684        let len = idx - header_idx;
685        new_table.contents[header_idx] =
686            item_generic_2bs(partitions.len() as u8, len as u16, ITEM_2BS_PARTITION_TABLE);
687
688        // 7. New Footer
689        new_table.contents[idx] = item_last(idx as u16 - 1);
690        new_table.contents[idx + 1] = 0;
691        new_table.contents[idx + 2] = BLOCK_MARKER_END;
692
693        // ignore the header
694        new_table.num_items = idx - 1;
695        new_table
696    }
697
698    /// Add a version number to the partition table
699    pub const fn with_version(self, major: u16, minor: u16) -> Self {
700        let mut new_table = PartitionTableBlock::new();
701        let mut idx = 0;
702        // copy over old table, with the header but not the footer
703        while idx < self.num_items + 1 {
704            new_table.contents[idx] = self.contents[idx];
705            idx += 1;
706        }
707
708        // 1. add item
709        new_table.contents[idx] = item_generic_2bs(0, 2, ITEM_1BS_VERSION);
710        idx += 1;
711        new_table.contents[idx] = ((major as u32) << 16) | minor as u32;
712        idx += 1;
713
714        // 2. New Footer
715        new_table.contents[idx] = item_last(idx as u16 - 1);
716        new_table.contents[idx + 1] = 0;
717        new_table.contents[idx + 2] = BLOCK_MARKER_END;
718
719        // ignore the header
720        new_table.num_items = idx - 1;
721        new_table
722    }
723
724    /// Add a a SHA256 hash of the Block
725    ///
726    /// Adds a `HASH_DEF` covering all the previous items in the Block, and a
727    /// `HASH_VALUE` with a SHA-256 hash of them.
728    pub const fn with_sha256(self) -> Self {
729        let mut new_table = PartitionTableBlock::new();
730        let mut idx = 0;
731        // copy over old table, with the header but not the footer
732        while idx < self.num_items + 1 {
733            new_table.contents[idx] = self.contents[idx];
734            idx += 1;
735        }
736
737        // 1. HASH_DEF says what is hashed
738        new_table.contents[idx] = item_generic_2bs(1, 2, ITEM_2BS_HASH_DEF);
739        idx += 1;
740        // we're hashing all the previous contents - including this line.
741        new_table.contents[idx] = (idx + 1) as u32;
742        idx += 1;
743
744        // calculate hash over prior contents
745        let input = unsafe {
746            core::slice::from_raw_parts(new_table.contents.as_ptr() as *const u8, idx * 4)
747        };
748        let hash: [u8; 32] = sha2_const_stable::Sha256::new().update(input).finalize();
749
750        // 2. HASH_VALUE contains the hash
751        new_table.contents[idx] = item_generic_2bs(0, 9, ITEM_1BS_HASH_VALUE);
752        idx += 1;
753
754        let mut hash_idx = 0;
755        while hash_idx < hash.len() {
756            new_table.contents[idx] = u32::from_le_bytes([
757                hash[hash_idx],
758                hash[hash_idx + 1],
759                hash[hash_idx + 2],
760                hash[hash_idx + 3],
761            ]);
762            idx += 1;
763            hash_idx += 4;
764        }
765
766        // 3. New Footer
767        new_table.contents[idx] = item_last(idx as u16 - 1);
768        new_table.contents[idx + 1] = 0;
769        new_table.contents[idx + 2] = BLOCK_MARKER_END;
770
771        // ignore the header
772        new_table.num_items = idx - 1;
773        new_table
774    }
775}
776
777impl Default for PartitionTableBlock {
778    fn default() -> Self {
779        Self::new()
780    }
781}
782
783/// Flags that a Partition can have
784#[derive(Debug, Copy, Clone, PartialEq, Eq)]
785#[repr(u32)]
786#[allow(missing_docs)]
787pub enum PartitionFlag {
788    NotBootableArm = 1 << 9,
789    NotBootableRiscv = 1 << 10,
790    Uf2DownloadAbNonBootableOwnerAffinity = 1 << 11,
791    Uf2DownloadNoReboot = 1 << 13,
792    AcceptsDefaultFamilyRp2040 = 1 << 14,
793    AcceptsDefaultFamilyData = 1 << 16,
794    AcceptsDefaultFamilyRp2350ArmS = 1 << 17,
795    AcceptsDefaultFamilyRp2350Riscv = 1 << 18,
796    AcceptsDefaultFamilyRp2350ArmNs = 1 << 19,
797}
798
799/// Flags that a Partition can have
800#[derive(Debug, Copy, Clone, PartialEq, Eq)]
801#[repr(u32)]
802#[allow(missing_docs)]
803pub enum UnpartitionedFlag {
804    Uf2DownloadNoReboot = 1 << 13,
805    AcceptsDefaultFamilyRp2040 = 1 << 14,
806    AcceptsDefaultFamilyAbsolute = 1 << 15,
807    AcceptsDefaultFamilyData = 1 << 16,
808    AcceptsDefaultFamilyRp2350ArmS = 1 << 17,
809    AcceptsDefaultFamilyRp2350Riscv = 1 << 18,
810    AcceptsDefaultFamilyRp2350ArmNs = 1 << 19,
811}
812
813/// Kinds of linked partition
814#[derive(Debug, Copy, Clone, PartialEq, Eq)]
815pub enum Link {
816    /// Not linked to anything
817    Nothing,
818    /// This is a B partition - link to our A partition.
819    ToA {
820        /// The index of our matching A partition.
821        partition_idx: u8,
822    },
823    /// Link to the partition that owns this one.
824    ToOwner {
825        /// The idx of our owner
826        partition_idx: u8,
827    },
828}
829
830/// Permissions that a Partition can have
831#[derive(Debug, Copy, Clone, PartialEq, Eq)]
832#[repr(u32)]
833pub enum Permission {
834    /// Can be read in Secure Mode
835    ///
836    /// Corresponds to `PERMISSION_S_R_BITS` in the Pico SDK
837    SecureRead = 1 << 26,
838    /// Can be written in Secure Mode
839    ///
840    /// Corresponds to `PERMISSION_S_W_BITS` in the Pico SDK
841    SecureWrite = 1 << 27,
842    /// Can be read in Non-Secure Mode
843    ///
844    /// Corresponds to `PERMISSION_NS_R_BITS` in the Pico SDK
845    NonSecureRead = 1 << 28,
846    /// Can be written in Non-Secure Mode
847    ///
848    /// Corresponds to `PERMISSION_NS_W_BITS` in the Pico SDK
849    NonSecureWrite = 1 << 29,
850    /// Can be read in Non-Secure Bootloader mode
851    ///
852    /// Corresponds to `PERMISSION_NSBOOT_R_BITS` in the Pico SDK
853    BootRead = 1 << 30,
854    /// Can be written in Non-Secure Bootloader mode
855    ///
856    /// Corresponds to `PERMISSION_NSBOOT_W_BITS` in the Pico SDK
857    BootWrite = 1 << 31,
858}
859
860impl Permission {
861    /// Is this permission bit set this in this bitmask?
862    pub const fn is_in(self, mask: u32) -> bool {
863        (mask & (self as u32)) != 0
864    }
865}
866
867/// The supported RP2350 CPU architectures
868#[derive(Debug, Copy, Clone, PartialEq, Eq)]
869pub enum Architecture {
870    /// Core is in Arm Cortex-M33 mode
871    Arm,
872    /// Core is in RISC-V / Hazard3 mode
873    Riscv,
874}
875
876/// The kinds of Secure Boot we support
877#[derive(Debug, Copy, Clone)]
878pub enum Security {
879    /// Security mode not given
880    Unspecified,
881    /// Start in Non-Secure mode
882    NonSecure,
883    /// Start in Secure mode
884    Secure,
885}
886
887/// Make an item containing a tag, 1 byte length and two extra bytes.
888///
889/// The `command` arg should contain `1BS`
890pub const fn item_generic_1bs(value: u16, length: u8, command: u8) -> u32 {
891    ((value as u32) << 16) | ((length as u32) << 8) | (command as u32)
892}
893
894/// Make an item containing a tag, 2 byte length and one extra byte.
895///
896/// The `command` arg should contain `2BS`
897pub const fn item_generic_2bs(value: u8, length: u16, command: u8) -> u32 {
898    ((value as u32) << 24) | ((length as u32) << 8) | (command as u32)
899}
900
901/// Create Image Type item, of type IGNORED.
902pub const fn item_ignored() -> u32 {
903    item_generic_2bs(0, 1, ITEM_2BS_IGNORED)
904}
905
906/// Create Image Type item, of type INVALID.
907pub const fn item_image_type_invalid() -> u32 {
908    let value = IMAGE_TYPE_INVALID;
909    item_generic_1bs(value, 1, ITEM_1BS_IMAGE_TYPE)
910}
911
912/// Create Image Type item, of type DATA.
913pub const fn item_image_type_data() -> u32 {
914    let value = IMAGE_TYPE_DATA;
915    item_generic_1bs(value, 1, ITEM_1BS_IMAGE_TYPE)
916}
917
918/// Create Image Type item, of type EXE.
919pub const fn item_image_type_exe(security: Security, arch: Architecture) -> u32 {
920    let mut value = IMAGE_TYPE_EXE | IMAGE_TYPE_EXE_CHIP_RP2350;
921
922    match arch {
923        Architecture::Arm => {
924            value |= IMAGE_TYPE_EXE_CPU_ARM;
925        }
926        Architecture::Riscv => {
927            value |= IMAGE_TYPE_EXE_CPU_RISCV;
928        }
929    }
930
931    match security {
932        Security::Unspecified => value |= IMAGE_TYPE_EXE_TYPE_SECURITY_UNSPECIFIED,
933        Security::NonSecure => value |= IMAGE_TYPE_EXE_TYPE_SECURITY_NS,
934        Security::Secure => value |= IMAGE_TYPE_EXE_TYPE_SECURITY_S,
935    }
936
937    item_generic_1bs(value, 1, ITEM_1BS_IMAGE_TYPE)
938}
939
940/// Create a Block Last item.
941pub const fn item_last(length: u16) -> u32 {
942    item_generic_2bs(0, length, ITEM_2BS_LAST)
943}
944
945/// Create a Vector Table item.
946///
947/// This is only allowed on Arm systems.
948pub const fn item_vector_table(table_ptr: u32) -> [u32; 2] {
949    [item_generic_1bs(0, 2, ITEM_1BS_VECTOR_TABLE), table_ptr]
950}
951
952/// Create an Entry Point item.
953pub const fn item_entry_point(entry_point: u32, initial_sp: u32) -> [u32; 3] {
954    [
955        item_generic_1bs(0, 3, ITEM_1BS_ENTRY_POINT),
956        entry_point,
957        initial_sp,
958    ]
959}
960
961/// Create an Rolling Window item.
962///
963/// The delta is the number of bytes into the image that 0x10000000 should
964/// be mapped.
965pub const fn item_rolling_window(delta: u32) -> [u32; 2] {
966    [item_generic_1bs(0, 3, ITEM_1BS_ROLLING_WINDOW_DELTA), delta]
967}
968
969#[cfg(test)]
970mod test {
971    use super::*;
972
973    /// I used this JSON, with `picotool partition create`:
974    ///
975    /// ```json
976    /// {
977    ///   "version": [1, 0],
978    ///   "unpartitioned": {
979    ///     "families": ["absolute"],
980    ///     "permissions": {
981    ///       "secure": "rw",
982    ///       "nonsecure": "rw",
983    ///       "bootloader": "rw"
984    ///     }
985    ///   },
986    ///   "partitions": [
987    ///     {
988    ///       "name": "A",
989    ///       "id": 0,
990    ///       "size": "2044K",
991    ///       "families": ["rp2350-arm-s", "rp2350-riscv"],
992    ///       "permissions": {
993    ///         "secure": "rw",
994    ///         "nonsecure": "rw",
995    ///         "bootloader": "rw"
996    ///       }
997    ///     },
998    ///     {
999    ///       "name": "B",
1000    ///       "id": 1,
1001    ///       "size": "2044K",
1002    ///       "families": ["rp2350-arm-s", "rp2350-riscv"],
1003    ///       "permissions": {
1004    ///         "secure": "rw",
1005    ///         "nonsecure": "rw",
1006    ///         "bootloader": "rw"
1007    ///       },
1008    ///       "link": ["a", 0]
1009    ///     }
1010    ///   ]
1011    /// }
1012    /// ```
1013    #[test]
1014    fn make_hashed_partition_table() {
1015        let table = PartitionTableBlock::new()
1016            .add_partition_item(
1017                UnpartitionedSpace::new()
1018                    .with_permission(Permission::SecureRead)
1019                    .with_permission(Permission::SecureWrite)
1020                    .with_permission(Permission::NonSecureRead)
1021                    .with_permission(Permission::NonSecureWrite)
1022                    .with_permission(Permission::BootRead)
1023                    .with_permission(Permission::BootWrite)
1024                    .with_flag(UnpartitionedFlag::AcceptsDefaultFamilyAbsolute),
1025                &[
1026                    Partition::new(2, 512)
1027                        .with_id(0)
1028                        .with_flag(PartitionFlag::AcceptsDefaultFamilyRp2350ArmS)
1029                        .with_flag(PartitionFlag::AcceptsDefaultFamilyRp2350Riscv)
1030                        .with_permission(Permission::SecureRead)
1031                        .with_permission(Permission::SecureWrite)
1032                        .with_permission(Permission::NonSecureRead)
1033                        .with_permission(Permission::NonSecureWrite)
1034                        .with_permission(Permission::BootRead)
1035                        .with_permission(Permission::BootWrite)
1036                        .with_name("A"),
1037                    Partition::new(513, 1023)
1038                        .with_id(1)
1039                        .with_flag(PartitionFlag::AcceptsDefaultFamilyRp2350ArmS)
1040                        .with_flag(PartitionFlag::AcceptsDefaultFamilyRp2350Riscv)
1041                        .with_link(Link::ToA { partition_idx: 0 })
1042                        .with_permission(Permission::SecureRead)
1043                        .with_permission(Permission::SecureWrite)
1044                        .with_permission(Permission::NonSecureRead)
1045                        .with_permission(Permission::NonSecureWrite)
1046                        .with_permission(Permission::BootRead)
1047                        .with_permission(Permission::BootWrite)
1048                        .with_name("B"),
1049                ],
1050            )
1051            .with_version(1, 0)
1052            .with_sha256();
1053        let expected = &[
1054            0xffffded3, // start
1055            0x02000c0a, // Item = PARTITION_TABLE
1056            0xfc008000, // Unpartitioned Space - permissions_and_flags
1057            0xfc400002, // Partition 0 - permissions_and_location (512 * 4096, 2 * 4096)
1058            0xfc061001, //     permissions_and_flags HAS_ID | HAS_NAME | ARM-S | RISC-V
1059            0x00000000, //     ID
1060            0x00000000, //     ID
1061            0x00004101, //     Name ("A")
1062            0xfc7fe201, // Partition 1 - permissions_and_location (1023 * 4096, 513 * 4096)
1063            0xfc061003, //     permissions_and_flags LINKA(0) | HAS_ID | HAS_NAME | ARM-S | RISC-V
1064            0x00000001, //     ID
1065            0x00000000, //     ID
1066            0x00004201, //     Name ("B")
1067            0x00000248, // Item = Version
1068            0x00010000, // 0, 1
1069            0x01000247, // HASH_DEF with 2 words, and SHA256 hash
1070            0x00000011, // 17 words hashed
1071            0x0000094b, // HASH_VALUE with 9 words
1072            0x1945cdad, // Hash word 0
1073            0x6b5f9773, // Hash word 1
1074            0xe2bf39bd, // Hash word 2
1075            0xb243e599, // Hash word 3
1076            0xab2f0e9a, // Hash word 4
1077            0x4d5d6d0b, // Hash word 5
1078            0xf973050f, // Hash word 6
1079            0x5ab6dadb, // Hash word 7
1080            0x000019ff, // Last Item
1081            0x00000000, // Block Loop Next Offset
1082            0xab123579, // End
1083        ];
1084        assert_eq!(
1085            &table.contents[..29],
1086            expected,
1087            "{:#010x?}\n != \n{:#010x?}",
1088            &table.contents[0..29],
1089            expected,
1090        );
1091    }
1092}