aboutsummaryrefslogtreecommitdiff
path: root/embassy-rp/src/block.rs
diff options
context:
space:
mode:
authorCaleb Jamison <[email protected]>2024-08-07 23:20:26 -0400
committerCaleb Jamison <[email protected]>2024-08-08 21:35:21 -0400
commitb185e02a42ad751ec6c31ffa6a1b87503f15489d (patch)
tree0f0c66747267d24d95b5957b22db7e5c525cb00e /embassy-rp/src/block.rs
parent891c5ee10584cd990dad529e3506fe1328e4e69d (diff)
Initial rp235x support
Examples have been run, but there is not yet a test suite.
Diffstat (limited to 'embassy-rp/src/block.rs')
-rw-r--r--embassy-rp/src/block.rs1076
1 files changed, 1076 insertions, 0 deletions
diff --git a/embassy-rp/src/block.rs b/embassy-rp/src/block.rs
new file mode 100644
index 000000000..d270bbf1c
--- /dev/null
+++ b/embassy-rp/src/block.rs
@@ -0,0 +1,1076 @@
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 core::assert!(first_sector < 0x2000);
365 core::assert!(last_sector < 0x2000);
366 core::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 core::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 & !Self::FLAGS_HAS_EXTRA_FAMILIES_MASK)
432 | (extra_families.len() as u32) << Self::FLAGS_HAS_EXTRA_FAMILIES_SHIFT,
433 ..self
434 }
435 }
436
437 /// Set the ID
438 pub const fn with_id(self, id: u64) -> Self {
439 Self {
440 id: Some(id),
441 permissions_and_flags: self.permissions_and_flags | Self::FLAGS_HAS_ID,
442 ..self
443 }
444 }
445
446 /// Add a link
447 pub const fn with_link(self, link: Link) -> Self {
448 let mut new_flags = self.permissions_and_flags & !Self::FLAGS_LINK_MASK;
449 match link {
450 Link::Nothing => {}
451 Link::ToA { partition_idx } => {
452 core::assert!(partition_idx < 16);
453 new_flags |= Self::FLAGS_LINK_TYPE_A_PARTITION;
454 new_flags |= (partition_idx as u32) << 3;
455 }
456 Link::ToOwner { partition_idx } => {
457 core::assert!(partition_idx < 16);
458 new_flags |= Self::FLAGS_LINK_TYPE_OWNER;
459 new_flags |= (partition_idx as u32) << 3;
460 }
461 }
462 Self {
463 permissions_and_flags: new_flags,
464 ..self
465 }
466 }
467
468 /// Set a flag
469 pub const fn with_flag(self, flag: PartitionFlag) -> Self {
470 Self {
471 permissions_and_flags: self.permissions_and_flags | flag as u32,
472 ..self
473 }
474 }
475
476 /// Get the partition start and end
477 ///
478 /// The offsets are in 4 KiB sectors, inclusive.
479 pub fn get_first_last_sectors(&self) -> (u16, u16) {
480 (
481 (self.permissions_and_location & 0x0000_1FFF) as u16,
482 ((self.permissions_and_location >> 13) & 0x0000_1FFF) as u16,
483 )
484 }
485
486 /// Get the partition start and end
487 ///
488 /// The offsets are in bytes, inclusive.
489 pub fn get_first_last_bytes(&self) -> (u32, u32) {
490 let (first, last) = self.get_first_last_sectors();
491 (u32::from(first) * 4096, (u32::from(last) * 4096) + 4095)
492 }
493
494 /// Check if it has a permission
495 pub fn has_permission(&self, permission: Permission) -> bool {
496 let mask = permission as u32;
497 (self.permissions_and_flags & mask) != 0
498 }
499
500 /// Get which extra families are allowed in this partition
501 pub fn get_extra_families(&self) -> &[u32] {
502 &self.extra_families[0..self.extra_families_len]
503 }
504
505 /// Get the name of the partition
506 ///
507 /// Returns `None` if there's no name, or the name is not valid UTF-8.
508 pub fn get_name(&self) -> Option<&str> {
509 let len = self.name[0] as usize;
510 if len == 0 {
511 None
512 } else {
513 core::str::from_utf8(&self.name[1..=len]).ok()
514 }
515 }
516
517 /// Get the ID
518 pub fn get_id(&self) -> Option<u64> {
519 self.id
520 }
521
522 /// Check if this partition is linked
523 pub fn get_link(&self) -> Link {
524 if (self.permissions_and_flags & Self::FLAGS_LINK_TYPE_A_PARTITION) != 0 {
525 let partition_idx = ((self.permissions_and_flags >> 3) & 0x0F) as u8;
526 Link::ToA { partition_idx }
527 } else if (self.permissions_and_flags & Self::FLAGS_LINK_TYPE_OWNER) != 0 {
528 let partition_idx = ((self.permissions_and_flags >> 3) & 0x0F) as u8;
529 Link::ToOwner { partition_idx }
530 } else {
531 Link::Nothing
532 }
533 }
534
535 /// Check if the partition has a flag set
536 pub fn has_flag(&self, flag: PartitionFlag) -> bool {
537 let mask = flag as u32;
538 (self.permissions_and_flags & mask) != 0
539 }
540}
541
542impl core::fmt::Display for Partition {
543 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
544 let (first, last) = self.get_first_last_bytes();
545 write!(
546 f,
547 "{:#010x}..{:#010x} S:{}{} NS:{}{} B:{}{}",
548 first,
549 last,
550 if self.has_permission(Permission::SecureRead) {
551 'R'
552 } else {
553 '_'
554 },
555 if self.has_permission(Permission::SecureWrite) {
556 'W'
557 } else {
558 '_'
559 },
560 if self.has_permission(Permission::NonSecureRead) {
561 'R'
562 } else {
563 '_'
564 },
565 if self.has_permission(Permission::NonSecureWrite) {
566 'W'
567 } else {
568 '_'
569 },
570 if self.has_permission(Permission::BootRead) {
571 'R'
572 } else {
573 '_'
574 },
575 if self.has_permission(Permission::BootWrite) {
576 'W'
577 } else {
578 '_'
579 }
580 )
581 }
582}
583
584/// Describes a partition table.
585///
586/// Don't store this as a static - make sure you convert it to a block.
587#[derive(Clone)]
588pub struct PartitionTableBlock {
589 /// This must look like a block, including the 1 word header and the 3 word footer.
590 contents: [u32; PARTITION_TABLE_MAX_ITEMS],
591 /// This value doesn't include the 1 word header or the 3 word footer
592 num_items: usize,
593}
594
595impl PartitionTableBlock {
596 /// Create an empty Block, big enough for a partition table.
597 ///
598 /// At a minimum you need to call [`Self::add_partition_item`].
599 pub const fn new() -> PartitionTableBlock {
600 let mut contents = [0; PARTITION_TABLE_MAX_ITEMS];
601 contents[0] = BLOCK_MARKER_START;
602 contents[1] = item_last(0);
603 contents[2] = 0;
604 contents[3] = BLOCK_MARKER_END;
605 PartitionTableBlock { contents, num_items: 0 }
606 }
607
608 /// Add a partition to the partition table
609 pub const fn add_partition_item(self, unpartitioned: UnpartitionedSpace, partitions: &[Partition]) -> Self {
610 let mut new_table = PartitionTableBlock::new();
611 let mut idx = 0;
612 // copy over old table, with the header but not the footer
613 while idx < self.num_items + 1 {
614 new_table.contents[idx] = self.contents[idx];
615 idx += 1;
616 }
617
618 // 1. add item header space (we fill this in later)
619 let header_idx = idx;
620 new_table.contents[idx] = 0;
621 idx += 1;
622
623 // 2. unpartitioned space flags
624 //
625 // (the location of unpartition space is not recorded here - it is
626 // inferred because the unpartitioned space is where the partitions are
627 // not)
628 new_table.contents[idx] = unpartitioned.permissions_and_flags;
629 idx += 1;
630
631 // 3. partition info
632
633 let mut partition_no = 0;
634 while partition_no < partitions.len() {
635 // a. permissions_and_location (4K units)
636 new_table.contents[idx] = partitions[partition_no].permissions_and_location;
637 idx += 1;
638
639 // b. permissions_and_flags
640 new_table.contents[idx] = partitions[partition_no].permissions_and_flags;
641 idx += 1;
642
643 // c. ID
644 if let Some(id) = partitions[partition_no].id {
645 new_table.contents[idx] = id as u32;
646 new_table.contents[idx + 1] = (id >> 32) as u32;
647 idx += 2;
648 }
649
650 // d. Extra Families
651 let mut extra_families_idx = 0;
652 while extra_families_idx < partitions[partition_no].extra_families_len {
653 new_table.contents[idx] = partitions[partition_no].extra_families[extra_families_idx];
654 idx += 1;
655 extra_families_idx += 1;
656 }
657
658 // e. Name
659 let mut name_idx = 0;
660 while name_idx < partitions[partition_no].name[0] as usize {
661 let name_chunk = [
662 partitions[partition_no].name[name_idx],
663 partitions[partition_no].name[name_idx + 1],
664 partitions[partition_no].name[name_idx + 2],
665 partitions[partition_no].name[name_idx + 3],
666 ];
667 new_table.contents[idx] = u32::from_le_bytes(name_chunk);
668 name_idx += 4;
669 idx += 1;
670 }
671
672 partition_no += 1;
673 }
674
675 let len = idx - header_idx;
676 new_table.contents[header_idx] = item_generic_2bs(partitions.len() as u8, len as u16, ITEM_2BS_PARTITION_TABLE);
677
678 // 7. New Footer
679 new_table.contents[idx] = item_last(idx as u16 - 1);
680 new_table.contents[idx + 1] = 0;
681 new_table.contents[idx + 2] = BLOCK_MARKER_END;
682
683 // ignore the header
684 new_table.num_items = idx - 1;
685 new_table
686 }
687
688 /// Add a version number to the partition table
689 pub const fn with_version(self, major: u16, minor: u16) -> Self {
690 let mut new_table = PartitionTableBlock::new();
691 let mut idx = 0;
692 // copy over old table, with the header but not the footer
693 while idx < self.num_items + 1 {
694 new_table.contents[idx] = self.contents[idx];
695 idx += 1;
696 }
697
698 // 1. add item
699 new_table.contents[idx] = item_generic_2bs(0, 2, ITEM_1BS_VERSION);
700 idx += 1;
701 new_table.contents[idx] = (major as u32) << 16 | minor as u32;
702 idx += 1;
703
704 // 2. New Footer
705 new_table.contents[idx] = item_last(idx as u16 - 1);
706 new_table.contents[idx + 1] = 0;
707 new_table.contents[idx + 2] = BLOCK_MARKER_END;
708
709 // ignore the header
710 new_table.num_items = idx - 1;
711 new_table
712 }
713
714 /// Add a a SHA256 hash of the Block
715 ///
716 /// Adds a `HASH_DEF` covering all the previous items in the Block, and a
717 /// `HASH_VALUE` with a SHA-256 hash of them.
718 pub const fn with_sha256(self) -> Self {
719 let mut new_table = PartitionTableBlock::new();
720 let mut idx = 0;
721 // copy over old table, with the header but not the footer
722 while idx < self.num_items + 1 {
723 new_table.contents[idx] = self.contents[idx];
724 idx += 1;
725 }
726
727 // 1. HASH_DEF says what is hashed
728 new_table.contents[idx] = item_generic_2bs(1, 2, ITEM_2BS_HASH_DEF);
729 idx += 1;
730 // we're hashing all the previous contents - including this line.
731 new_table.contents[idx] = (idx + 1) as u32;
732 idx += 1;
733
734 // calculate hash over prior contents
735 let input = unsafe { core::slice::from_raw_parts(new_table.contents.as_ptr() as *const u8, idx * 4) };
736 let hash: [u8; 32] = sha2_const_stable::Sha256::new().update(input).finalize();
737
738 // 2. HASH_VALUE contains the hash
739 new_table.contents[idx] = item_generic_2bs(0, 9, ITEM_1BS_HASH_VALUE);
740 idx += 1;
741
742 let mut hash_idx = 0;
743 while hash_idx < hash.len() {
744 new_table.contents[idx] = u32::from_le_bytes([
745 hash[hash_idx],
746 hash[hash_idx + 1],
747 hash[hash_idx + 2],
748 hash[hash_idx + 3],
749 ]);
750 idx += 1;
751 hash_idx += 4;
752 }
753
754 // 3. New Footer
755 new_table.contents[idx] = item_last(idx as u16 - 1);
756 new_table.contents[idx + 1] = 0;
757 new_table.contents[idx + 2] = BLOCK_MARKER_END;
758
759 // ignore the header
760 new_table.num_items = idx - 1;
761 new_table
762 }
763}
764
765impl Default for PartitionTableBlock {
766 fn default() -> Self {
767 Self::new()
768 }
769}
770
771/// Flags that a Partition can have
772#[derive(Debug, Copy, Clone, PartialEq, Eq)]
773#[repr(u32)]
774#[allow(missing_docs)]
775pub enum PartitionFlag {
776 NotBootableArm = 1 << 9,
777 NotBootableRiscv = 1 << 10,
778 Uf2DownloadAbNonBootableOwnerAffinity = 1 << 11,
779 Uf2DownloadNoReboot = 1 << 13,
780 AcceptsDefaultFamilyRp2040 = 1 << 14,
781 AcceptsDefaultFamilyData = 1 << 16,
782 AcceptsDefaultFamilyRp2350ArmS = 1 << 17,
783 AcceptsDefaultFamilyRp2350Riscv = 1 << 18,
784 AcceptsDefaultFamilyRp2350ArmNs = 1 << 19,
785}
786
787/// Flags that a Partition can have
788#[derive(Debug, Copy, Clone, PartialEq, Eq)]
789#[repr(u32)]
790#[allow(missing_docs)]
791pub enum UnpartitionedFlag {
792 Uf2DownloadNoReboot = 1 << 13,
793 AcceptsDefaultFamilyRp2040 = 1 << 14,
794 AcceptsDefaultFamilyAbsolute = 1 << 15,
795 AcceptsDefaultFamilyData = 1 << 16,
796 AcceptsDefaultFamilyRp2350ArmS = 1 << 17,
797 AcceptsDefaultFamilyRp2350Riscv = 1 << 18,
798 AcceptsDefaultFamilyRp2350ArmNs = 1 << 19,
799}
800
801/// Kinds of linked partition
802#[derive(Debug, Copy, Clone, PartialEq, Eq)]
803pub enum Link {
804 /// Not linked to anything
805 Nothing,
806 /// This is a B partition - link to our A partition.
807 ToA {
808 /// The index of our matching A partition.
809 partition_idx: u8,
810 },
811 /// Link to the partition that owns this one.
812 ToOwner {
813 /// The idx of our owner
814 partition_idx: u8,
815 },
816}
817
818/// Permissions that a Partition can have
819#[derive(Debug, Copy, Clone, PartialEq, Eq)]
820#[repr(u32)]
821pub enum Permission {
822 /// Can be read in Secure Mode
823 ///
824 /// Corresponds to `PERMISSION_S_R_BITS` in the Pico SDK
825 SecureRead = 1 << 26,
826 /// Can be written in Secure Mode
827 ///
828 /// Corresponds to `PERMISSION_S_W_BITS` in the Pico SDK
829 SecureWrite = 1 << 27,
830 /// Can be read in Non-Secure Mode
831 ///
832 /// Corresponds to `PERMISSION_NS_R_BITS` in the Pico SDK
833 NonSecureRead = 1 << 28,
834 /// Can be written in Non-Secure Mode
835 ///
836 /// Corresponds to `PERMISSION_NS_W_BITS` in the Pico SDK
837 NonSecureWrite = 1 << 29,
838 /// Can be read in Non-Secure Bootloader mode
839 ///
840 /// Corresponds to `PERMISSION_NSBOOT_R_BITS` in the Pico SDK
841 BootRead = 1 << 30,
842 /// Can be written in Non-Secure Bootloader mode
843 ///
844 /// Corresponds to `PERMISSION_NSBOOT_W_BITS` in the Pico SDK
845 BootWrite = 1 << 31,
846}
847
848impl Permission {
849 /// Is this permission bit set this in this bitmask?
850 pub const fn is_in(self, mask: u32) -> bool {
851 (mask & (self as u32)) != 0
852 }
853}
854
855/// The supported RP2350 CPU architectures
856#[derive(Debug, Copy, Clone, PartialEq, Eq)]
857pub enum Architecture {
858 /// Core is in Arm Cortex-M33 mode
859 Arm,
860 /// Core is in RISC-V / Hazard3 mode
861 Riscv,
862}
863
864/// The kinds of Secure Boot we support
865#[derive(Debug, Copy, Clone)]
866pub enum Security {
867 /// Security mode not given
868 Unspecified,
869 /// Start in Non-Secure mode
870 NonSecure,
871 /// Start in Secure mode
872 Secure,
873}
874
875/// Make an item containing a tag, 1 byte length and two extra bytes.
876///
877/// The `command` arg should contain `1BS`
878pub const fn item_generic_1bs(value: u16, length: u8, command: u8) -> u32 {
879 ((value as u32) << 16) | ((length as u32) << 8) | (command as u32)
880}
881
882/// Make an item containing a tag, 2 byte length and one extra byte.
883///
884/// The `command` arg should contain `2BS`
885pub const fn item_generic_2bs(value: u8, length: u16, command: u8) -> u32 {
886 ((value as u32) << 24) | ((length as u32) << 8) | (command as u32)
887}
888
889/// Create Image Type item, of type IGNORED.
890pub const fn item_ignored() -> u32 {
891 item_generic_2bs(0, 1, ITEM_2BS_IGNORED)
892}
893
894/// Create Image Type item, of type INVALID.
895pub const fn item_image_type_invalid() -> u32 {
896 let value = IMAGE_TYPE_INVALID;
897 item_generic_1bs(value, 1, ITEM_1BS_IMAGE_TYPE)
898}
899
900/// Create Image Type item, of type DATA.
901pub const fn item_image_type_data() -> u32 {
902 let value = IMAGE_TYPE_DATA;
903 item_generic_1bs(value, 1, ITEM_1BS_IMAGE_TYPE)
904}
905
906/// Create Image Type item, of type EXE.
907pub const fn item_image_type_exe(security: Security, arch: Architecture) -> u32 {
908 let mut value = IMAGE_TYPE_EXE | IMAGE_TYPE_EXE_CHIP_RP2350;
909
910 match arch {
911 Architecture::Arm => {
912 value |= IMAGE_TYPE_EXE_CPU_ARM;
913 }
914 Architecture::Riscv => {
915 value |= IMAGE_TYPE_EXE_CPU_RISCV;
916 }
917 }
918
919 match security {
920 Security::Unspecified => value |= IMAGE_TYPE_EXE_TYPE_SECURITY_UNSPECIFIED,
921 Security::NonSecure => value |= IMAGE_TYPE_EXE_TYPE_SECURITY_NS,
922 Security::Secure => value |= IMAGE_TYPE_EXE_TYPE_SECURITY_S,
923 }
924
925 item_generic_1bs(value, 1, ITEM_1BS_IMAGE_TYPE)
926}
927
928/// Create a Block Last item.
929pub const fn item_last(length: u16) -> u32 {
930 item_generic_2bs(0, length, ITEM_2BS_LAST)
931}
932
933/// Create a Vector Table item.
934///
935/// This is only allowed on Arm systems.
936pub const fn item_vector_table(table_ptr: u32) -> [u32; 2] {
937 [item_generic_1bs(0, 2, ITEM_1BS_VECTOR_TABLE), table_ptr]
938}
939
940/// Create an Entry Point item.
941pub const fn item_entry_point(entry_point: u32, initial_sp: u32) -> [u32; 3] {
942 [item_generic_1bs(0, 3, ITEM_1BS_ENTRY_POINT), entry_point, initial_sp]
943}
944
945/// Create an Rolling Window item.
946///
947/// The delta is the number of bytes into the image that 0x10000000 should
948/// be mapped.
949pub const fn item_rolling_window(delta: u32) -> [u32; 2] {
950 [item_generic_1bs(0, 3, ITEM_1BS_ROLLING_WINDOW_DELTA), delta]
951}
952
953#[cfg(test)]
954mod test {
955 use super::*;
956
957 /// I used this JSON, with `picotool partition create`:
958 ///
959 /// ```json
960 /// {
961 /// "version": [1, 0],
962 /// "unpartitioned": {
963 /// "families": ["absolute"],
964 /// "permissions": {
965 /// "secure": "rw",
966 /// "nonsecure": "rw",
967 /// "bootloader": "rw"
968 /// }
969 /// },
970 /// "partitions": [
971 /// {
972 /// "name": "A",
973 /// "id": 0,
974 /// "size": "2044K",
975 /// "families": ["rp2350-arm-s", "rp2350-riscv"],
976 /// "permissions": {
977 /// "secure": "rw",
978 /// "nonsecure": "rw",
979 /// "bootloader": "rw"
980 /// }
981 /// },
982 /// {
983 /// "name": "B",
984 /// "id": 1,
985 /// "size": "2044K",
986 /// "families": ["rp2350-arm-s", "rp2350-riscv"],
987 /// "permissions": {
988 /// "secure": "rw",
989 /// "nonsecure": "rw",
990 /// "bootloader": "rw"
991 /// },
992 /// "link": ["a", 0]
993 /// }
994 /// ]
995 /// }
996 /// ```
997 #[test]
998 fn make_hashed_partition_table() {
999 let table = PartitionTableBlock::new()
1000 .add_partition_item(
1001 UnpartitionedSpace::new()
1002 .with_permission(Permission::SecureRead)
1003 .with_permission(Permission::SecureWrite)
1004 .with_permission(Permission::NonSecureRead)
1005 .with_permission(Permission::NonSecureWrite)
1006 .with_permission(Permission::BootRead)
1007 .with_permission(Permission::BootWrite)
1008 .with_flag(UnpartitionedFlag::AcceptsDefaultFamilyAbsolute),
1009 &[
1010 Partition::new(2, 512)
1011 .with_id(0)
1012 .with_flag(PartitionFlag::AcceptsDefaultFamilyRp2350ArmS)
1013 .with_flag(PartitionFlag::AcceptsDefaultFamilyRp2350Riscv)
1014 .with_permission(Permission::SecureRead)
1015 .with_permission(Permission::SecureWrite)
1016 .with_permission(Permission::NonSecureRead)
1017 .with_permission(Permission::NonSecureWrite)
1018 .with_permission(Permission::BootRead)
1019 .with_permission(Permission::BootWrite)
1020 .with_name("A"),
1021 Partition::new(513, 1023)
1022 .with_id(1)
1023 .with_flag(PartitionFlag::AcceptsDefaultFamilyRp2350ArmS)
1024 .with_flag(PartitionFlag::AcceptsDefaultFamilyRp2350Riscv)
1025 .with_link(Link::ToA { partition_idx: 0 })
1026 .with_permission(Permission::SecureRead)
1027 .with_permission(Permission::SecureWrite)
1028 .with_permission(Permission::NonSecureRead)
1029 .with_permission(Permission::NonSecureWrite)
1030 .with_permission(Permission::BootRead)
1031 .with_permission(Permission::BootWrite)
1032 .with_name("B"),
1033 ],
1034 )
1035 .with_version(1, 0)
1036 .with_sha256();
1037 let expected = &[
1038 0xffffded3, // start
1039 0x02000c0a, // Item = PARTITION_TABLE
1040 0xfc008000, // Unpartitioned Space - permissions_and_flags
1041 0xfc400002, // Partition 0 - permissions_and_location (512 * 4096, 2 * 4096)
1042 0xfc061001, // permissions_and_flags HAS_ID | HAS_NAME | ARM-S | RISC-V
1043 0x00000000, // ID
1044 0x00000000, // ID
1045 0x00004101, // Name ("A")
1046 0xfc7fe201, // Partition 1 - permissions_and_location (1023 * 4096, 513 * 4096)
1047 0xfc061003, // permissions_and_flags LINKA(0) | HAS_ID | HAS_NAME | ARM-S | RISC-V
1048 0x00000001, // ID
1049 0x00000000, // ID
1050 0x00004201, // Name ("B")
1051 0x00000248, // Item = Version
1052 0x00010000, // 0, 1
1053 0x01000247, // HASH_DEF with 2 words, and SHA256 hash
1054 0x00000011, // 17 words hashed
1055 0x0000094b, // HASH_VALUE with 9 words
1056 0x1945cdad, // Hash word 0
1057 0x6b5f9773, // Hash word 1
1058 0xe2bf39bd, // Hash word 2
1059 0xb243e599, // Hash word 3
1060 0xab2f0e9a, // Hash word 4
1061 0x4d5d6d0b, // Hash word 5
1062 0xf973050f, // Hash word 6
1063 0x5ab6dadb, // Hash word 7
1064 0x000019ff, // Last Item
1065 0x00000000, // Block Loop Next Offset
1066 0xab123579, // End
1067 ];
1068 core::assert_eq!(
1069 &table.contents[..29],
1070 expected,
1071 "{:#010x?}\n != \n{:#010x?}",
1072 &table.contents[0..29],
1073 expected,
1074 );
1075 }
1076}