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