rp_binary_info/lib.rs
1//! Code and types for creating Picotool compatible "Binary Info" metadata
2//!
3//! ## Example usage
4//!
5//! Enable the Cargo feature `binary-info`.
6//!
7//! Add this to your linker script (usually named `memory.x`):
8//!
9//! ```not_rust,ignore
10//! SECTIONS {
11//! /* ### Boot ROM info
12//! *
13//! * Goes after .vector_table, to keep it in the first 512 bytes of flash,
14//! * where picotool can find it
15//! */
16//! .boot_info : ALIGN(4)
17//! {
18//! KEEP(*(.boot_info));
19//! } > FLASH
20//!
21//! } INSERT AFTER .vector_table;
22//!
23//! /* move .text to start /after/ the boot info */
24//! _stext = ADDR(.boot_info) + SIZEOF(.boot_info);
25//!
26//! SECTIONS {
27//! /* ### Picotool 'Binary Info' Entries
28//! *
29//! * Picotool looks through this block (as we have pointers to it in our
30//! * header) to find interesting information.
31//! */
32//! .bi_entries : ALIGN(4)
33//! {
34//! /* We put this in the header */
35//! __bi_entries_start = .;
36//! /* Here are the entries */
37//! KEEP(*(.bi_entries));
38//! /* Keep this block a nice round size */
39//! . = ALIGN(4);
40//! /* We put this in the header */
41//! __bi_entries_end = .;
42//! } > FLASH
43//! } INSERT AFTER .text;
44//!
45//! SECTIONS {
46//! .flash_end : {
47//! __flash_binary_end = .;
48//! } > FLASH
49//! } INSERT AFTER .uninit;
50//! ```
51//!
52//! Then, add this to your Rust code:
53//!
54//! ```
55//! #[link_section = ".bi_entries"]
56//! #[cfg(all(feature = "binary-info", target_os = "none"))]
57//! #[used]
58//! pub static PICOTOOL_ENTRIES: [rp_binary_info::EntryAddr; 4] = [
59//! rp_binary_info::rp_program_name!(c"Program Name Here"),
60//! rp_binary_info::rp_cargo_version!(),
61//! rp_binary_info::rp_binary_end!(__flash_binary_end),
62//! rp_binary_info::int!(
63//! rp_binary_info::make_tag(b"JP"),
64//! 0x0000_0001,
65//! 0x12345678
66//! ),
67//! ];
68//!
69//! extern "C" {
70//! static __flash_binary_end: u32;
71//! }
72//! ```
73//!
74//! ## Cargo features
75//!
76//! The `binary-info` Cargo feature enables emitting the main `PICOTOOL_HEADER`
77//! static, which is what Picotool looks for to discover the binary info.
78//!
79//! It is optional to allow you to emit the static yourself differently, for e.g.
80//! compatibility with different linker scripts, while still allowing using the
81//! rest of the utilities in the crate to format the info.
82
83#![no_std]
84#![warn(missing_docs)]
85
86pub mod consts;
87
88mod types;
89pub use types::*;
90
91#[macro_use]
92mod macros;
93
94extern "C" {
95 /// The linker script sets this symbol to have the address of the first
96 /// entry in the `.bi_entries` section.
97 static __bi_entries_start: EntryAddr;
98 /// The linker script sets this symbol to have the address just past the
99 /// last entry in the `.bi_entries` section.
100 static __bi_entries_end: EntryAddr;
101 /// The linker script sets this symbol to have the address of the first
102 /// entry in the `.data` section.
103 static __sdata: u32;
104 /// The linker script sets this symbol to have the address just past the
105 /// first entry in the `.data` section.
106 static __edata: u32;
107 /// The linker script sets this symbol to have the address of the
108 /// initialisation data for the first entry in the `.data` section (i.e. a
109 /// flash address, not a RAM address).
110 static __sidata: u32;
111}
112
113/// Picotool can find this block in our ELF file and report interesting
114/// metadata.
115///
116/// The data here tells picotool the start and end flash addresses of our
117/// metadata.
118#[link_section = ".boot_info"]
119#[cfg(all(feature = "binary-info", target_os = "none"))]
120#[used]
121#[allow(unused_unsafe)] // addr_of! is safe since rust 1.82.0
122pub static PICOTOOL_HEADER: Header = unsafe {
123 Header::new(
124 core::ptr::addr_of!(__bi_entries_start),
125 core::ptr::addr_of!(__bi_entries_end),
126 &MAPPING_TABLE,
127 )
128};
129
130/// This tells picotool how to convert RAM addresses back into Flash addresses
131pub static MAPPING_TABLE: [MappingTableEntry; 2] = [
132 // This is the entry for .data
133 #[allow(unused_unsafe)]
134 MappingTableEntry {
135 source_addr_start: unsafe { core::ptr::addr_of!(__sidata) },
136 dest_addr_start: unsafe { core::ptr::addr_of!(__sdata) },
137 dest_addr_end: unsafe { core::ptr::addr_of!(__edata) },
138 },
139 // This is the terminating marker
140 MappingTableEntry::null(),
141];
142
143/// Create a 'Binary Info' entry containing the program name
144///
145/// This is well-known to picotool, and will be displayed if you run `picotool info`.
146///
147/// * Tag: [`consts::TAG_RASPBERRY_PI`]
148/// * ID: [`consts::ID_RP_PROGRAM_NAME`]
149pub const fn rp_program_name(name: &'static core::ffi::CStr) -> StringEntry {
150 StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_PROGRAM_NAME, name)
151}
152
153/// Create a 'Binary Info' entry containing the program version.
154///
155/// * Tag: [`consts::TAG_RASPBERRY_PI`]
156/// * Id: [`consts::ID_RP_PROGRAM_VERSION_STRING`]
157pub const fn rp_program_version(name: &'static core::ffi::CStr) -> StringEntry {
158 StringEntry::new(
159 consts::TAG_RASPBERRY_PI,
160 consts::ID_RP_PROGRAM_VERSION_STRING,
161 name,
162 )
163}
164
165/// Create a 'Binary Info' entry with a URL
166///
167/// * Tag: [`consts::TAG_RASPBERRY_PI`]
168/// * Id: [`consts::ID_RP_PROGRAM_URL`]
169pub const fn rp_program_url(url: &'static core::ffi::CStr) -> StringEntry {
170 StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_PROGRAM_URL, url)
171}
172
173/// Create a 'Binary Info' with the program build date
174///
175/// * Tag: [`consts::TAG_RASPBERRY_PI`]
176/// * Id: [`consts::ID_RP_PROGRAM_BUILD_DATE_STRING`]
177pub const fn rp_program_build_date_string(value: &'static core::ffi::CStr) -> StringEntry {
178 StringEntry::new(
179 consts::TAG_RASPBERRY_PI,
180 consts::ID_RP_PROGRAM_BUILD_DATE_STRING,
181 value,
182 )
183}
184
185/// Create a 'Binary Info' with the size of the binary
186///
187/// * Tag: [`consts::TAG_RASPBERRY_PI`]
188/// * Id: [`consts::ID_RP_BINARY_END`]
189pub const fn rp_binary_end(value: *const ()) -> PointerEntry {
190 PointerEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_BINARY_END, value)
191}
192
193/// Create a 'Binary Info' with a description of the program
194///
195/// * Tag: [`consts::TAG_RASPBERRY_PI`]
196/// * Id: [`consts::ID_RP_PROGRAM_DESCRIPTION`]
197pub const fn rp_program_description(value: &'static core::ffi::CStr) -> StringEntry {
198 StringEntry::new(
199 consts::TAG_RASPBERRY_PI,
200 consts::ID_RP_PROGRAM_DESCRIPTION,
201 value,
202 )
203}
204
205/// Create a 'Binary Info' with some feature of the program
206///
207/// * Tag: [`consts::TAG_RASPBERRY_PI`]
208/// * Id: [`consts::ID_RP_PROGRAM_FEATURE`]
209pub const fn rp_program_feature(value: &'static core::ffi::CStr) -> StringEntry {
210 StringEntry::new(
211 consts::TAG_RASPBERRY_PI,
212 consts::ID_RP_PROGRAM_FEATURE,
213 value,
214 )
215}
216
217/// Create a 'Binary Info' with some whether this was a Debug or Release build
218///
219/// * Tag: [`consts::TAG_RASPBERRY_PI`]
220/// * Id: [`consts::ID_RP_PROGRAM_BUILD_ATTRIBUTE`]
221pub const fn rp_program_build_attribute(value: &'static core::ffi::CStr) -> StringEntry {
222 StringEntry::new(
223 consts::TAG_RASPBERRY_PI,
224 consts::ID_RP_PROGRAM_BUILD_ATTRIBUTE,
225 value,
226 )
227}
228
229/// Create a 'Binary Info' with the Pico SDK version used
230///
231/// * Tag: [`consts::TAG_RASPBERRY_PI`]
232/// * Id: [`consts::ID_RP_SDK_VERSION`]
233pub const fn rp_sdk_version(value: &'static core::ffi::CStr) -> StringEntry {
234 StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_SDK_VERSION, value)
235}
236
237/// Create a 'Binary Info' with which board this program targets
238///
239/// * Tag: [`consts::TAG_RASPBERRY_PI`]
240/// * Id: [`consts::ID_RP_PICO_BOARD`]
241pub const fn rp_pico_board(value: &'static core::ffi::CStr) -> StringEntry {
242 StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_PICO_BOARD, value)
243}
244
245/// Create a 'Binary Info' with which `boot2` image this program uses
246///
247/// * Tag: [`consts::TAG_RASPBERRY_PI`]
248/// * Id: [`consts::ID_RP_BOOT2_NAME`]
249pub const fn rp_boot2_name(value: &'static core::ffi::CStr) -> StringEntry {
250 StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_BOOT2_NAME, value)
251}
252
253/// Create a tag from two ASCII letters.
254///
255/// ```
256/// let tag = rp_binary_info::make_tag(b"RP");
257/// assert_eq!(tag, 0x5052);
258/// ```
259pub const fn make_tag(c: &[u8; 2]) -> u16 {
260 u16::from_le_bytes(*c)
261}
262
263// End of file