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//!
46//! Then, add this to your Rust code:
47//!
48//! ```
49//! #[link_section = ".bi_entries"]
50//! #[used]
51//! pub static PICOTOOL_ENTRIES: [rp_binary_info::EntryAddr; 3] = [
52//! rp_binary_info::rp_program_name!(c"Program Name Here"),
53//! rp_binary_info::rp_cargo_version!(),
54//! rp_binary_info::int!(
55//! rp_binary_info::make_tag(b"JP"),
56//! 0x0000_0001,
57//! 0x12345678
58//! ),
59//! ];
60//! ```
61//!
62//! ## Cargo features
63//!
64//! The `binary-info` Cargo feature enables emitting the main `PICOTOOL_HEADER`
65//! static, which is what Picotool looks for to discover the binary info.
66//!
67//! It is optional to allow you to emit the static yourself differently, for e.g.
68//! compatibility with different linker scripts, while still allowing using the
69//! rest of the utilities in the crate to format the info.
70
71#![no_std]
72#![warn(missing_docs)]
73
74pub mod consts;
75
76mod types;
77pub use types::*;
78
79#[macro_use]
80mod macros;
81
82extern "C" {
83 /// The linker script sets this symbol to have the address of the first
84 /// entry in the `.bi_entries` section.
85 static __bi_entries_start: EntryAddr;
86 /// The linker script sets this symbol to have the address just past the
87 /// last entry in the `.bi_entries` section.
88 static __bi_entries_end: EntryAddr;
89 /// The linker script sets this symbol to have the address of the first
90 /// entry in the `.data` section.
91 static __sdata: u32;
92 /// The linker script sets this symbol to have the address just past the
93 /// first entry in the `.data` section.
94 static __edata: u32;
95 /// The linker script sets this symbol to have the address of the
96 /// initialisation data for the first entry in the `.data` section (i.e. a
97 /// flash address, not a RAM address).
98 static __sidata: u32;
99}
100
101/// Picotool can find this block in our ELF file and report interesting
102/// metadata.
103///
104/// The data here tells picotool the start and end flash addresses of our
105/// metadata.
106#[link_section = ".boot_info"]
107#[cfg(feature = "binary-info")]
108#[used]
109#[allow(unused_unsafe)] // addr_of! is safe since rust 1.82.0
110pub static PICOTOOL_HEADER: Header = unsafe {
111 Header::new(
112 core::ptr::addr_of!(__bi_entries_start),
113 core::ptr::addr_of!(__bi_entries_end),
114 &MAPPING_TABLE,
115 )
116};
117
118/// This tells picotool how to convert RAM addresses back into Flash addresses
119pub static MAPPING_TABLE: [MappingTableEntry; 2] = [
120 // This is the entry for .data
121 #[allow(unused_unsafe)]
122 MappingTableEntry {
123 source_addr_start: unsafe { core::ptr::addr_of!(__sidata) },
124 dest_addr_start: unsafe { core::ptr::addr_of!(__sdata) },
125 dest_addr_end: unsafe { core::ptr::addr_of!(__edata) },
126 },
127 // This is the terminating marker
128 MappingTableEntry::null(),
129];
130
131/// Create a 'Binary Info' entry containing the program name
132///
133/// This is well-known to picotool, and will be displayed if you run `picotool info`.
134///
135/// * Tag: [`consts::TAG_RASPBERRY_PI`]
136/// * ID: [`consts::ID_RP_PROGRAM_NAME`]
137pub const fn rp_program_name(name: &'static core::ffi::CStr) -> StringEntry {
138 StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_PROGRAM_NAME, name)
139}
140
141/// Create a 'Binary Info' entry containing the program version.
142///
143/// * Tag: [`consts::TAG_RASPBERRY_PI`]
144/// * Id: [`consts::ID_RP_PROGRAM_VERSION_STRING`]
145pub const fn rp_program_version(name: &'static core::ffi::CStr) -> StringEntry {
146 StringEntry::new(
147 consts::TAG_RASPBERRY_PI,
148 consts::ID_RP_PROGRAM_VERSION_STRING,
149 name,
150 )
151}
152
153/// Create a 'Binary Info' entry with a URL
154///
155/// * Tag: [`consts::TAG_RASPBERRY_PI`]
156/// * Id: [`consts::ID_RP_PROGRAM_URL`]
157pub const fn rp_program_url(url: &'static core::ffi::CStr) -> StringEntry {
158 StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_PROGRAM_URL, url)
159}
160
161/// Create a 'Binary Info' with the program build date
162///
163/// * Tag: [`consts::TAG_RASPBERRY_PI`]
164/// * Id: [`consts::ID_RP_PROGRAM_BUILD_DATE_STRING`]
165pub const fn rp_program_build_date_string(value: &'static core::ffi::CStr) -> StringEntry {
166 StringEntry::new(
167 consts::TAG_RASPBERRY_PI,
168 consts::ID_RP_PROGRAM_BUILD_DATE_STRING,
169 value,
170 )
171}
172
173/// Create a 'Binary Info' with the size of the binary
174///
175/// * Tag: [`consts::TAG_RASPBERRY_PI`]
176/// * Id: [`consts::ID_RP_BINARY_END`]
177pub const fn rp_binary_end(value: u32) -> IntegerEntry {
178 IntegerEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_BINARY_END, value)
179}
180
181/// Create a 'Binary Info' with a description of the program
182///
183/// * Tag: [`consts::TAG_RASPBERRY_PI`]
184/// * Id: [`consts::ID_RP_PROGRAM_DESCRIPTION`]
185pub const fn rp_program_description(value: &'static core::ffi::CStr) -> StringEntry {
186 StringEntry::new(
187 consts::TAG_RASPBERRY_PI,
188 consts::ID_RP_PROGRAM_DESCRIPTION,
189 value,
190 )
191}
192
193/// Create a 'Binary Info' with some feature of the program
194///
195/// * Tag: [`consts::TAG_RASPBERRY_PI`]
196/// * Id: [`consts::ID_RP_PROGRAM_FEATURE`]
197pub const fn rp_program_feature(value: &'static core::ffi::CStr) -> StringEntry {
198 StringEntry::new(
199 consts::TAG_RASPBERRY_PI,
200 consts::ID_RP_PROGRAM_FEATURE,
201 value,
202 )
203}
204
205/// Create a 'Binary Info' with some whether this was a Debug or Release build
206///
207/// * Tag: [`consts::TAG_RASPBERRY_PI`]
208/// * Id: [`consts::ID_RP_PROGRAM_BUILD_ATTRIBUTE`]
209pub const fn rp_program_build_attribute(value: &'static core::ffi::CStr) -> StringEntry {
210 StringEntry::new(
211 consts::TAG_RASPBERRY_PI,
212 consts::ID_RP_PROGRAM_BUILD_ATTRIBUTE,
213 value,
214 )
215}
216
217/// Create a 'Binary Info' with the Pico SDK version used
218///
219/// * Tag: [`consts::TAG_RASPBERRY_PI`]
220/// * Id: [`consts::ID_RP_SDK_VERSION`]
221pub const fn rp_sdk_version(value: &'static core::ffi::CStr) -> StringEntry {
222 StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_SDK_VERSION, value)
223}
224
225/// Create a 'Binary Info' with which board this program targets
226///
227/// * Tag: [`consts::TAG_RASPBERRY_PI`]
228/// * Id: [`consts::ID_RP_PICO_BOARD`]
229pub const fn rp_pico_board(value: &'static core::ffi::CStr) -> StringEntry {
230 StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_PICO_BOARD, value)
231}
232
233/// Create a 'Binary Info' with which `boot2` image this program uses
234///
235/// * Tag: [`consts::TAG_RASPBERRY_PI`]
236/// * Id: [`consts::ID_RP_BOOT2_NAME`]
237pub const fn rp_boot2_name(value: &'static core::ffi::CStr) -> StringEntry {
238 StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_BOOT2_NAME, value)
239}
240
241/// Create a tag from two ASCII letters.
242///
243/// ```
244/// let tag = rp_binary_info::make_tag(b"RP");
245/// assert_eq!(tag, 0x5052);
246/// ```
247pub const fn make_tag(c: &[u8; 2]) -> u16 {
248 u16::from_le_bytes(*c)
249}
250
251// End of file