rp_binary_info/types.rs
1//! Types for the Binary Info system
2
3/// This is the 'Binary Info' header block that `picotool` looks for in your UF2
4/// file/ELF file/Pico in Bootloader Mode to give you useful metadata about your
5/// program.
6///
7/// It should be placed in the first 4096 bytes of flash, so use your `memory.x`
8/// to insert a section between `.text` and `.vector_table` and put a static
9/// value of this type in that section.
10#[repr(C)]
11pub struct Header {
12 /// Must be equal to Picotool::MARKER_START
13 marker_start: u32,
14 /// The first in our table of pointers to Entries
15 entries_start: *const EntryAddr,
16 /// The last in our table of pointers to Entries
17 entries_end: *const EntryAddr,
18 /// The first entry in a null-terminated RAM/Flash mapping table
19 mapping_table: *const MappingTableEntry,
20 /// Must be equal to Picotool::MARKER_END
21 marker_end: u32,
22}
23
24impl Header {
25 /// This is the `BINARY_INFO_MARKER_START` magic value from `picotool`
26 const MARKER_START: u32 = 0x7188ebf2;
27 /// This is the `BINARY_INFO_MARKER_END` magic value from `picotool`
28 const MARKER_END: u32 = 0xe71aa390;
29
30 /// Create a new `picotool` compatible header.
31 ///
32 /// * `entries_start` - the first [`EntryAddr`] in the table
33 /// * `entries_end` - the last [`EntryAddr`] in the table
34 /// * `mapping_table` - the RAM/Flash address mapping table
35 pub const fn new(
36 entries_start: *const EntryAddr,
37 entries_end: *const EntryAddr,
38 mapping_table: &'static [MappingTableEntry],
39 ) -> Self {
40 let mapping_table = mapping_table.as_ptr();
41 Self {
42 marker_start: Self::MARKER_START,
43 entries_start,
44 entries_end,
45 mapping_table,
46 marker_end: Self::MARKER_END,
47 }
48 }
49}
50
51// We need this as rustc complains that is is unsafe to share `*const u32`
52// pointers between threads. We only allow these to be created with static
53// data, so this is OK.
54unsafe impl Sync for Header {}
55
56/// This is a reference to an entry. It's like a `&dyn` ref to some type `T:
57/// Entry`, except that the run-time type information is encoded into the
58/// Entry itself in very specific way.
59#[repr(transparent)]
60pub struct EntryAddr(*const u32);
61
62// We need this as rustc complains that is is unsafe to share `*const u32`
63// pointers between threads. We only allow these to be created with static
64// data, so this is OK.
65unsafe impl Sync for EntryAddr {}
66
67/// Allows us to tell picotool where values are in the UF2 given their run-time
68/// address.
69///
70/// The most obvious example is RAM variables, which must be found in the
71/// `.data` section of the UF2.
72#[repr(C)]
73pub struct MappingTableEntry {
74 /// The start address in RAM (or wherever the address picotool finds will
75 /// point)
76 pub source_addr_start: *const u32,
77 /// The start address in flash (or whever the data actually lives in the
78 /// ELF)
79 pub dest_addr_start: *const u32,
80 /// The end address in flash
81 pub dest_addr_end: *const u32,
82}
83
84impl MappingTableEntry {
85 /// Generate a null entry to mark the end of the list
86 pub const fn null() -> MappingTableEntry {
87 MappingTableEntry {
88 source_addr_start: core::ptr::null(),
89 dest_addr_start: core::ptr::null(),
90 dest_addr_end: core::ptr::null(),
91 }
92 }
93}
94
95// We need this as rustc complains that is is unsafe to share `*const u32`
96// pointers between threads. We only allow these to be created with static
97// data, so this is OK.
98unsafe impl Sync for MappingTableEntry {}
99
100/// This is the set of data types that `picotool` supports.
101#[repr(u16)]
102pub enum DataType {
103 /// Raw data
104 Raw = 1,
105 /// Data with a size
106 SizedData = 2,
107 /// A list of binary data
108 BinaryInfoListZeroTerminated = 3,
109 /// A BSON encoded blob
110 Bson = 4,
111 /// An Integer with an ID
112 IdAndInt = 5,
113 /// A string with an Id
114 IdAndString = 6,
115 /// A block device
116 BlockDevice = 7,
117 /// GPIO pins, with their function
118 PinsWithFunction = 8,
119 /// GPIO pins, with their name
120 PinsWithName = 9,
121 /// GPIO pins, with multiple names?
122 PinsWithNames = 10,
123}
124
125/// All Entries start with this common header
126#[repr(C)]
127struct EntryCommon {
128 data_type: DataType,
129 tag: u16,
130}
131
132/// An entry which contains both an ID (e.g. `ID_RP_PROGRAM_NAME`) and a pointer
133/// to a null-terminated string.
134#[repr(C)]
135pub struct StringEntry {
136 header: EntryCommon,
137 id: u32,
138 value: *const core::ffi::c_char,
139}
140
141impl StringEntry {
142 /// Create a new `StringEntry`
143 pub const fn new(tag: u16, id: u32, value: &'static core::ffi::CStr) -> StringEntry {
144 StringEntry {
145 header: EntryCommon {
146 data_type: DataType::IdAndString,
147 tag,
148 },
149 id,
150 value: value.as_ptr(),
151 }
152 }
153
154 /// Get this entry's address
155 pub const fn addr(&self) -> EntryAddr {
156 EntryAddr(self as *const Self as *const u32)
157 }
158}
159
160// We need this as rustc complains that is is unsafe to share `*const
161// core::ffi::c_char` pointers between threads. We only allow these to be
162// created with static string slices, so it's OK.
163unsafe impl Sync for StringEntry {}
164
165/// An entry which contains both an ID (e.g. `ID_RP_BINARY_END`) and an integer.
166#[repr(C)]
167pub struct IntegerEntry {
168 header: EntryCommon,
169 id: u32,
170 value: u32,
171}
172
173impl IntegerEntry {
174 /// Create a new `StringEntry`
175 pub const fn new(tag: u16, id: u32, value: u32) -> IntegerEntry {
176 IntegerEntry {
177 header: EntryCommon {
178 data_type: DataType::IdAndInt,
179 tag,
180 },
181 id,
182 value,
183 }
184 }
185
186 /// Get this entry's address
187 pub const fn addr(&self) -> EntryAddr {
188 EntryAddr(self as *const Self as *const u32)
189 }
190}
191
192// End of file