From e878daf624dd4413512b162cd53116c9392ceca7 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Sun, 28 Jul 2024 16:06:52 +0100 Subject: [PATCH] Adds binary info block support. Bumps MSRV to 1.77. --- .github/workflows/build_and_test.yml | 2 +- memory.x | 44 +++++- rp2040-hal/CHANGELOG.md | 9 ++ rp2040-hal/Cargo.toml | 12 +- rp2040-hal/examples/binary_info_demo.rs | 109 +++++++++++++ rp2040-hal/src/binary_info/consts.rs | 31 ++++ rp2040-hal/src/binary_info/macros.rs | 145 +++++++++++++++++ rp2040-hal/src/binary_info/mod.rs | 198 ++++++++++++++++++++++++ rp2040-hal/src/binary_info/types.rs | 192 +++++++++++++++++++++++ rp2040-hal/src/lib.rs | 1 + 10 files changed, 740 insertions(+), 3 deletions(-) create mode 100644 rp2040-hal/examples/binary_info_demo.rs create mode 100644 rp2040-hal/src/binary_info/consts.rs create mode 100644 rp2040-hal/src/binary_info/macros.rs create mode 100644 rp2040-hal/src/binary_info/mod.rs create mode 100644 rp2040-hal/src/binary_info/types.rs diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 593197f21..703a34d29 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -62,7 +62,7 @@ jobs: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@master with: - toolchain: 1.75 + toolchain: 1.77 target: thumbv6m-none-eabi - name: Install cargo-hack run: | diff --git a/memory.x b/memory.x index e6b80c473..67df0fc02 100644 --- a/memory.x +++ b/memory.x @@ -28,9 +28,51 @@ MEMORY { EXTERN(BOOT2_FIRMWARE) SECTIONS { - /* ### Boot loader */ + /* ### Boot loader + * + * An executable block of code which sets up the QSPI interface for + * 'Execute-In-Place' (or XIP) mode. Also sends chip-specific commands to + * the external flash chip. + * + * Must go at the start of external flash, where the Boot ROM expects it. + */ .boot2 ORIGIN(BOOT2) : { KEEP(*(.boot2)); } > BOOT2 } INSERT BEFORE .text; + +SECTIONS { + /* ### Boot ROM info + * + * Goes after .vector_table, to keep it in the first 512 bytes of flash, + * where picotool can find it + */ + .boot_info : ALIGN(4) + { + KEEP(*(.boot_info)); + } > FLASH + +} INSERT AFTER .vector_table; + +/* move .text to start /after/ the boot info */ +_stext = ADDR(.boot_info) + SIZEOF(.boot_info); + +SECTIONS { + /* ### Picotool 'Binary Info' Entries + * + * Picotool looks through this block (as we have pointers to it in our + * header) to find interesting information. + */ + .bi_entries : ALIGN(4) + { + /* We put this in the header */ + __bi_entries_start = .; + /* Here are the entries */ + KEEP(*(.bi_entries)); + /* Keep this block a nice round size */ + . = ALIGN(4); + /* We put this in the header */ + __bi_entries_end = .; + } > FLASH +} INSERT AFTER .text; \ No newline at end of file diff --git a/rp2040-hal/CHANGELOG.md b/rp2040-hal/CHANGELOG.md index b9195cbbf..967f2dc5a 100644 --- a/rp2040-hal/CHANGELOG.md +++ b/rp2040-hal/CHANGELOG.md @@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### MSRV + +The Minimum-Supported Rust Version (MSRV) for the next release is 1.77 + +### Added + +- Support for *binary info*, which is metadata that `picotool` can read from your binary. +- Bump MSRV to 1.77, because *binary info* examples need C-Strings. + ## [0.10.0] - 2024-03-10 ### Added diff --git a/rp2040-hal/Cargo.toml b/rp2040-hal/Cargo.toml index 0718629ff..48a5caa8f 100644 --- a/rp2040-hal/Cargo.toml +++ b/rp2040-hal/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" homepage = "https://github.com/rp-rs/rp-hal" description = "A Rust Embedded-HAL impl for the rp2040 microcontroller" license = "MIT OR Apache-2.0" -rust-version = "1.75" +rust-version = "1.77" repository = "https://github.com/rp-rs/rp-hal" categories = ["embedded", "hardware-support", "no-std", "no-std::no-alloc"] keywords = ["embedded", "hal", "raspberry-pi", "rp2040", "embedded-hal"] @@ -106,6 +106,16 @@ rtic-monotonic = ["dep:rtic-monotonic"] # Implement `i2c-write-iter` traits i2c-write-iter = ["dep:i2c-write-iter"] +# Add a binary-info header block containing picotool-compatible metadata. +# +# Requires 'rt' so that the vector table is correctly sized and therefore the +# header is within reach of picotool. +binary-info = ["rt"] + +[[example]] +name = "binary_info_demo" +required-features = ["binary-info", "critical-section-impl"] + [[example]] # irq example uses cortex-m-rt::interrupt, need rt feature for that name = "gpio_irq_example" diff --git a/rp2040-hal/examples/binary_info_demo.rs b/rp2040-hal/examples/binary_info_demo.rs new file mode 100644 index 000000000..b8837aaf2 --- /dev/null +++ b/rp2040-hal/examples/binary_info_demo.rs @@ -0,0 +1,109 @@ +//! # GPIO 'Blinky' Example, with Binary Info +//! +//! This application demonstrates how to control a GPIO pin on the RP2040, and +//! includes some picotool-compatible metadata. +//! +//! It may need to be adapted to your particular board layout and/or pin assignment. +//! +//! See the `Cargo.toml` file for Copyright and license details. + +#![no_std] +#![no_main] + +// Ensure we halt the program on panic (if we don't mention this crate it won't +// be linked) +use panic_halt as _; + +// Alias for our HAL crate +use rp2040_hal as hal; + +// A shorter alias for the Peripheral Access Crate, which provides low-level +// register access +use hal::pac; + +use hal::binary_info; + +// Some traits we need +use embedded_hal::delay::DelayNs; +use embedded_hal::digital::OutputPin; + +/// The linker will place this boot block at the start of our program image. We +/// need this to help the ROM bootloader get our code up and running. +/// Note: This boot block is not necessary when using a rp-hal based BSP +/// as the BSPs already perform this step. +#[link_section = ".boot2"] +#[used] +pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_GENERIC_03H; + +/// External high-speed crystal on the Raspberry Pi Pico board is 12 MHz. Adjust +/// if your board has a different frequency +const XTAL_FREQ_HZ: u32 = 12_000_000u32; + +/// Entry point to our bare-metal application. +/// +/// The `#[rp2040_hal::entry]` macro ensures the Cortex-M start-up code calls this function +/// as soon as all global variables and the spinlock are initialised. +/// +/// The function configures the RP2040 peripherals, then toggles a GPIO pin in +/// an infinite loop. If there is an LED connected to that pin, it will blink. +#[hal::entry] +fn main() -> ! { + // Grab our singleton objects + let mut pac = pac::Peripherals::take().unwrap(); + + // Set up the watchdog driver - needed by the clock setup code + let mut watchdog = hal::Watchdog::new(pac.WATCHDOG); + + // Configure the clocks + let clocks = hal::clocks::init_clocks_and_plls( + XTAL_FREQ_HZ, + pac.XOSC, + pac.CLOCKS, + pac.PLL_SYS, + pac.PLL_USB, + &mut pac.RESETS, + &mut watchdog, + ) + .unwrap(); + + let mut timer = rp2040_hal::Timer::new(pac.TIMER, &mut pac.RESETS, &clocks); + + // The single-cycle I/O block controls our GPIO pins + let sio = hal::Sio::new(pac.SIO); + + // Set the pins to their default state + let pins = hal::gpio::Pins::new( + pac.IO_BANK0, + pac.PADS_BANK0, + sio.gpio_bank0, + &mut pac.RESETS, + ); + + // Configure GPIO25 as an output + let mut led_pin = pins.gpio25.into_push_pull_output(); + loop { + led_pin.set_high().unwrap(); + timer.delay_ms(500); + led_pin.set_low().unwrap(); + timer.delay_ms(500); + } +} + +/// This is a list of references to our table entries +/// +/// They must be in the `.bi_entries` section as we tell picotool the start and +/// end addresses of that section. +#[link_section = ".bi_entries"] +#[used] +pub static PICOTOOL_ENTRIES: [binary_info::EntryAddr; 7] = [ + hal::binary_info_rp_program_name!(c"rp2040-hal Binary Info Example"), + hal::binary_info_rp_cargo_version!(), + hal::binary_info_rp_program_description!(c"A GPIO blinky with extra metadata."), + hal::binary_info_rp_program_url!(c"https://github.com/rp-rs/rp-hal"), + hal::binary_info_rp_program_build_attribute!(), + hal::binary_info_rp_pico_board!(c"pico"), + // An example with a non-Raspberry-Pi tag + hal::binary_info_int!(binary_info::make_tag(b"JP"), 0x0000_0001, 0x12345678), +]; + +// End of file diff --git a/rp2040-hal/src/binary_info/consts.rs b/rp2040-hal/src/binary_info/consts.rs new file mode 100644 index 000000000..c8270c081 --- /dev/null +++ b/rp2040-hal/src/binary_info/consts.rs @@ -0,0 +1,31 @@ +//! Constants for binary info + +/// All Raspberry Pi specified IDs have this tag. +/// +/// You can create your own for custom fields. +pub const TAG_RASPBERRY_PI: u16 = super::make_tag(b"RP"); + +/// Used to note the program name - use with StringEntry +pub const ID_RP_PROGRAM_NAME: u32 = 0x02031c86; +/// Used to note the program version - use with StringEntry +pub const ID_RP_PROGRAM_VERSION_STRING: u32 = 0x11a9bc3a; +/// Used to note the program build date - use with StringEntry +pub const ID_RP_PROGRAM_BUILD_DATE_STRING: u32 = 0x9da22254; +/// Used to note the size of the binary - use with IntegerEntry +pub const ID_RP_BINARY_END: u32 = 0x68f465de; +/// Used to note a URL for the program - use with StringEntry +pub const ID_RP_PROGRAM_URL: u32 = 0x1856239a; +/// Used to note a description of the program - use with StringEntry +pub const ID_RP_PROGRAM_DESCRIPTION: u32 = 0xb6a07c19; +/// Used to note some feature of the program - use with StringEntry +pub const ID_RP_PROGRAM_FEATURE: u32 = 0xa1f4b453; +/// Used to note some whether this was a Debug or Release build - use with StringEntry +pub const ID_RP_PROGRAM_BUILD_ATTRIBUTE: u32 = 0x4275f0d3; +/// Used to note the Pico SDK version used - use with StringEntry +pub const ID_RP_SDK_VERSION: u32 = 0x5360b3ab; +/// Used to note which board this program targets - use with StringEntry +pub const ID_RP_PICO_BOARD: u32 = 0xb63cffbb; +/// Used to note which `boot2` image this program uses - use with StringEntry +pub const ID_RP_BOOT2_NAME: u32 = 0x7f8882e1; + +// End of file diff --git a/rp2040-hal/src/binary_info/macros.rs b/rp2040-hal/src/binary_info/macros.rs new file mode 100644 index 000000000..760525615 --- /dev/null +++ b/rp2040-hal/src/binary_info/macros.rs @@ -0,0 +1,145 @@ +//! Handy macros for making Binary Info entries + +/// Generate a static item containing the given environment variable, +/// and return its [`EntryAddr`](super::EntryAddr). +#[macro_export] +macro_rules! binary_info_env { + ($tag:expr, $id:expr, $env_var_name:expr) => { + $crate::binary_info_str!($tag, $id, { + let value = concat!(env!($env_var_name), "\0"); + // # Safety + // + // We used `concat!` to null-terminate on the line above. + let value_cstr = + unsafe { core::ffi::CStr::from_bytes_with_nul_unchecked(value.as_bytes()) }; + value_cstr + }) + }; +} + +/// Generate a static item containing the given string, and return its +/// [`EntryAddr`](super::EntryAddr). +/// +/// You must pass a numeric tag, a numeric ID, and `&CStr` (which is always +/// null-terminated). +#[macro_export] +macro_rules! binary_info_str { + ($tag:expr, $id:expr, $str:expr) => {{ + static ENTRY: $crate::binary_info::StringEntry = + $crate::binary_info::StringEntry::new($tag, $id, $str); + ENTRY.addr() + }}; +} + +/// Generate a static item containing the given string, and return its +/// [`EntryAddr`](super::EntryAddr). +/// +/// You must pass a numeric tag, a numeric ID, and `&CStr` (which is always +/// null-terminated). +#[macro_export] +macro_rules! binary_info_int { + ($tag:expr, $id:expr, $int:expr) => {{ + static ENTRY: $crate::binary_info::IntegerEntry = + $crate::binary_info::IntegerEntry::new($tag, $id, $int); + ENTRY.addr() + }}; +} + +/// Generate a static item containing the program name, and return its +/// [`EntryAddr`](super::EntryAddr). +#[macro_export] +macro_rules! binary_info_rp_program_name { + ($name:expr) => { + $crate::binary_info_str!( + $crate::binary_info::consts::TAG_RASPBERRY_PI, + $crate::binary_info::consts::ID_RP_PROGRAM_NAME, + $name + ) + }; +} + +/// Generate a static item containing the program version, and return its +/// [`EntryAddr`](super::EntryAddr). +#[macro_export] +macro_rules! binary_info_rp_program_version { + ($version:expr) => {{ + $crate::binary_info_str!( + $crate::binary_info::consts::TAG_RASPBERRY_PI, + $crate::binary_info::consts::ID_RP_PROGRAM_VERSION, + $version + ) + }}; +} + +/// Generate a static item containing the `CARGO_PKG_VERSION` as the program +/// version, and return its [`EntryAddr`](super::EntryAddr). +#[macro_export] +macro_rules! binary_info_rp_cargo_version { + () => { + $crate::binary_info_env!( + $crate::binary_info::consts::TAG_RASPBERRY_PI, + $crate::binary_info::consts::ID_RP_PROGRAM_VERSION_STRING, + "CARGO_PKG_VERSION" + ) + }; +} + +/// Generate a static item containing the program url, and return its +/// [`EntryAddr`](super::EntryAddr). +#[macro_export] +macro_rules! binary_info_rp_program_url { + ($url:expr) => { + $crate::binary_info_str!( + $crate::binary_info::consts::TAG_RASPBERRY_PI, + $crate::binary_info::consts::ID_RP_PROGRAM_URL, + $url + ) + }; +} + +/// Generate a static item containing the program description, and return its +/// [`EntryAddr`](super::EntryAddr). +#[macro_export] +macro_rules! binary_info_rp_program_description { + ($description:expr) => { + $crate::binary_info_str!( + $crate::binary_info::consts::TAG_RASPBERRY_PI, + $crate::binary_info::consts::ID_RP_PROGRAM_DESCRIPTION, + $description + ) + }; +} + +/// Generate a static item containing whether this is a debug or a release +/// build, and return its [`EntryAddr`](super::EntryAddr). +#[macro_export] +macro_rules! binary_info_rp_program_build_attribute { + () => { + $crate::binary_info_str!( + $crate::binary_info::consts::TAG_RASPBERRY_PI, + $crate::binary_info::consts::ID_RP_PROGRAM_BUILD_ATTRIBUTE, + { + if cfg!(debug_assertions) { + c"debug" + } else { + c"release" + } + } + ) + }; +} + +/// Generate a static item containing the specific board this program runs on, +/// and return its [`EntryAddr`](super::EntryAddr). +#[macro_export] +macro_rules! binary_info_rp_pico_board { + ($board:expr) => { + $crate::binary_info_str!( + $crate::binary_info::consts::TAG_RASPBERRY_PI, + $crate::binary_info::consts::ID_RP_PICO_BOARD, + $board + ) + }; +} + +// End of file diff --git a/rp2040-hal/src/binary_info/mod.rs b/rp2040-hal/src/binary_info/mod.rs new file mode 100644 index 000000000..ab2167e37 --- /dev/null +++ b/rp2040-hal/src/binary_info/mod.rs @@ -0,0 +1,198 @@ +//! Code and types for creating Picotool compatible "Binary Info" metadata +//! +//! Add something like this to your program, and compile with the "binary-info" +//! and "rt" features: +//! +//! ``` +//! # use rp2040_hal as hal; +//! #[link_section = ".bi_entries"] +//! #[used] +//! pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 3] = [ +//! hal::binary_info_rp_program_name!(c"Program Name Here"), +//! hal::binary_info_rp_cargo_version!(), +//! hal::binary_info_int!( +//! hal::binary_info::make_tag(b"JP"), +//! 0x0000_0001, +//! 0x12345678 +//! ), +//! ]; +//! ``` + +pub mod consts; + +mod types; +pub use types::*; + +#[macro_use] +mod macros; + +extern "C" { + /// The linker script sets this symbol to have the address of the first + /// entry in the `.bi_entries` section. + static __bi_entries_start: EntryAddr; + /// The linker script sets this symbol to have the address just past the + /// last entry in the `.bi_entries` section. + static __bi_entries_end: EntryAddr; + /// The linker script sets this symbol to have the address of the first + /// entry in the `.data` section. + static __sdata: u32; + /// The linker script sets this symbol to have the address just past the + /// first entry in the `.data` section. + static __edata: u32; + /// The linker script sets this symbol to have the address of the + /// initialisation data for the first entry in the `.data` section (i.e. a + /// flash address, not a RAM address). + static __sidata: u32; +} + +/// Picotool can find this block in our ELF file and report interesting +/// metadata. +/// +/// The data here tells picotool the start and end flash addresses of our +/// metadata. +#[cfg(feature = "binary-info")] +#[link_section = ".boot_info"] +#[used] +pub static PICOTOOL_HEADER: Header = unsafe { + Header::new( + core::ptr::addr_of!(__bi_entries_start), + core::ptr::addr_of!(__bi_entries_end), + &MAPPING_TABLE, + ) +}; + +/// This tells picotool how to convert RAM addresses back into Flash addresses +#[cfg(feature = "binary-info")] +pub static MAPPING_TABLE: [MappingTableEntry; 2] = [ + // This is the entry for .data + MappingTableEntry { + source_addr_start: unsafe { core::ptr::addr_of!(__sidata) }, + dest_addr_start: unsafe { core::ptr::addr_of!(__sdata) }, + dest_addr_end: unsafe { core::ptr::addr_of!(__edata) }, + }, + // This is the terminating marker + MappingTableEntry::null(), +]; + +/// Create a 'Binary Info' entry containing the program name +/// +/// This is well-known to picotool, and will be displayed if you run `picotool info`. +/// +/// * Tag: [`consts::TAG_RASPBERRY_PI`] +/// * ID: [`consts::ID_RP_PROGRAM_NAME`] +pub const fn rp_program_name(name: &'static core::ffi::CStr) -> StringEntry { + StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_PROGRAM_NAME, name) +} + +/// Create a 'Binary Info' entry containing the program version. +/// +/// * Tag: [`consts::TAG_RASPBERRY_PI`] +/// * Id: [`consts::ID_RP_PROGRAM_VERSION_STRING`] +pub const fn rp_program_version(name: &'static core::ffi::CStr) -> StringEntry { + StringEntry::new( + consts::TAG_RASPBERRY_PI, + consts::ID_RP_PROGRAM_VERSION_STRING, + name, + ) +} + +/// Create a 'Binary Info' entry with a URL +/// +/// * Tag: [`consts::TAG_RASPBERRY_PI`] +/// * Id: [`consts::ID_RP_PROGRAM_URL`] +pub const fn rp_program_url(url: &'static core::ffi::CStr) -> StringEntry { + StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_PROGRAM_URL, url) +} + +/// Create a 'Binary Info' with the program build date +/// +/// * Tag: [`consts::TAG_RASPBERRY_PI`] +/// * Id: [`consts::ID_RP_PROGRAM_BUILD_DATE_STRING`] +pub const fn rp_program_build_date_string(value: &'static core::ffi::CStr) -> StringEntry { + StringEntry::new( + consts::TAG_RASPBERRY_PI, + consts::ID_RP_PROGRAM_BUILD_DATE_STRING, + value, + ) +} + +/// Create a 'Binary Info' with the size of the binary +/// +/// * Tag: [`consts::TAG_RASPBERRY_PI`] +/// * Id: [`consts::ID_RP_BINARY_END`] +pub const fn rp_binary_end(value: u32) -> IntegerEntry { + IntegerEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_BINARY_END, value) +} + +/// Create a 'Binary Info' with a description of the program +/// +/// * Tag: [`consts::TAG_RASPBERRY_PI`] +/// * Id: [`consts::ID_RP_PROGRAM_DESCRIPTION`] +pub const fn rp_program_description(value: &'static core::ffi::CStr) -> StringEntry { + StringEntry::new( + consts::TAG_RASPBERRY_PI, + consts::ID_RP_PROGRAM_DESCRIPTION, + value, + ) +} + +/// Create a 'Binary Info' with some feature of the program +/// +/// * Tag: [`consts::TAG_RASPBERRY_PI`] +/// * Id: [`consts::ID_RP_PROGRAM_FEATURE`] +pub const fn rp_program_feature(value: &'static core::ffi::CStr) -> StringEntry { + StringEntry::new( + consts::TAG_RASPBERRY_PI, + consts::ID_RP_PROGRAM_FEATURE, + value, + ) +} + +/// Create a 'Binary Info' with some whether this was a Debug or Release build +/// +/// * Tag: [`consts::TAG_RASPBERRY_PI`] +/// * Id: [`consts::ID_RP_PROGRAM_BUILD_ATTRIBUTE`] +pub const fn rp_program_build_attribute(value: &'static core::ffi::CStr) -> StringEntry { + StringEntry::new( + consts::TAG_RASPBERRY_PI, + consts::ID_RP_PROGRAM_BUILD_ATTRIBUTE, + value, + ) +} + +/// Create a 'Binary Info' with the Pico SDK version used +/// +/// * Tag: [`consts::TAG_RASPBERRY_PI`] +/// * Id: [`consts::ID_RP_SDK_VERSION`] +pub const fn rp_sdk_version(value: &'static core::ffi::CStr) -> StringEntry { + StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_SDK_VERSION, value) +} + +/// Create a 'Binary Info' with which board this program targets +/// +/// * Tag: [`consts::TAG_RASPBERRY_PI`] +/// * Id: [`consts::ID_RP_PICO_BOARD`] +pub const fn rp_pico_board(value: &'static core::ffi::CStr) -> StringEntry { + StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_PICO_BOARD, value) +} + +/// Create a 'Binary Info' with which `boot2` image this program uses +/// +/// * Tag: [`consts::TAG_RASPBERRY_PI`] +/// * Id: [`consts::ID_RP_BOOT2_NAME`] +pub const fn rp_boot2_name(value: &'static core::ffi::CStr) -> StringEntry { + StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_BOOT2_NAME, value) +} + +/// Create a tag from two ASCII letters. +/// +/// ``` +/// # use rp2040_hal as hal; +/// let tag = hal::binary_info::make_tag(b"RP"); +/// assert_eq!(tag, 0x5052); +/// ``` +pub const fn make_tag(c: &[u8; 2]) -> u16 { + u16::from_le_bytes(*c) +} + +// End of file diff --git a/rp2040-hal/src/binary_info/types.rs b/rp2040-hal/src/binary_info/types.rs new file mode 100644 index 000000000..319d6e2c3 --- /dev/null +++ b/rp2040-hal/src/binary_info/types.rs @@ -0,0 +1,192 @@ +//! Types for the Binary Info system + +/// This is the 'Binary Info' header block that `picotool` looks for in your UF2 +/// file/ELF file/Pico in Bootloader Mode to give you useful metadata about your +/// program. +/// +/// It should be placed in the first 512 bytes of flash, so use your `memory.x` +/// to insert a section between `.text` and `.vector_table` and put a static +/// value of this type in that section. +#[repr(C)] +pub struct Header { + /// Must be equal to Picotool::MARKER_START + marker_start: u32, + /// The first in our table of pointers to Entries + entries_start: *const EntryAddr, + /// The last in our table of pointers to Entries + entries_end: *const EntryAddr, + /// The first entry in a null-terminated RAM/Flash mapping table + mapping_table: *const MappingTableEntry, + /// Must be equal to Picotool::MARKER_END + marker_end: u32, +} + +impl Header { + /// This is the `BINARY_INFO_MARKER_START` magic value from `picotool` + const MARKER_START: u32 = 0x7188ebf2; + /// This is the `BINARY_INFO_MARKER_END` magic value from `picotool` + const MARKER_END: u32 = 0xe71aa390; + + /// Create a new `picotool` compatible header. + /// + /// * `entries_start` - the first [`EntryAddr`] in the table + /// * `entries_end` - the last [`EntryAddr`] in the table + /// * `mapping_table` - the RAM/Flash address mapping table + pub const fn new( + entries_start: *const EntryAddr, + entries_end: *const EntryAddr, + mapping_table: &'static [MappingTableEntry], + ) -> Self { + let mapping_table = mapping_table.as_ptr(); + Self { + marker_start: Self::MARKER_START, + entries_start, + entries_end, + mapping_table, + marker_end: Self::MARKER_END, + } + } +} + +// We need this as rustc complains that is is unsafe to share `*const u32` +// pointers between threads. We only allow these to be created with static +// data, so this is OK. +unsafe impl Sync for Header {} + +/// This is a reference to an entry. It's like a `&dyn` ref to some type `T: +/// Entry`, except that the run-time type information is encoded into the +/// Entry itself in very specific way. +#[repr(transparent)] +pub struct EntryAddr(*const u32); + +// We need this as rustc complains that is is unsafe to share `*const u32` +// pointers between threads. We only allow these to be created with static +// data, so this is OK. +unsafe impl Sync for EntryAddr {} + +/// Allows us to tell picotool where values are in the UF2 given their run-time +/// address. +/// +/// The most obvious example is RAM variables, which must be found in the +/// `.data` section of the UF2. +#[repr(C)] +pub struct MappingTableEntry { + /// The start address in RAM (or wherever the address picotool finds will + /// point) + pub source_addr_start: *const u32, + /// The start address in flash (or whever the data actually lives in the + /// ELF) + pub dest_addr_start: *const u32, + /// The end address in flash + pub dest_addr_end: *const u32, +} + +impl MappingTableEntry { + /// Generate a null entry to mark the end of the list + pub const fn null() -> MappingTableEntry { + MappingTableEntry { + source_addr_start: core::ptr::null(), + dest_addr_start: core::ptr::null(), + dest_addr_end: core::ptr::null(), + } + } +} + +// We need this as rustc complains that is is unsafe to share `*const u32` +// pointers between threads. We only allow these to be created with static +// data, so this is OK. +unsafe impl Sync for MappingTableEntry {} + +/// This is the set of data types that `picotool` supports. +#[repr(u16)] +pub enum DataType { + /// Raw data + Raw = 1, + /// Data with a size + SizedData = 2, + /// A list of binary data + BinaryInfoListZeroTerminated = 3, + /// A BSON encoded blob + Bson = 4, + /// An Integer with an ID + IdAndInt = 5, + /// A string with an Id + IdAndString = 6, + /// A block device + BlockDevice = 7, + /// GPIO pins, with their function + PinsWithFunction = 8, + /// GPIO pins, with their name + PinsWithName = 9, + /// GPIO pins, with multiple names? + PinsWithNames = 10, +} + +/// All Entries start with this common header +#[repr(C)] +struct EntryCommon { + data_type: DataType, + tag: u16, +} + +/// An entry which contains both an ID (e.g. `ID_RP_PROGRAM_NAME`) and a pointer +/// to a null-terminated string. +#[repr(C)] +pub struct StringEntry { + header: EntryCommon, + id: u32, + value: *const core::ffi::c_char, +} + +impl StringEntry { + /// Create a new `StringEntry` + pub const fn new(tag: u16, id: u32, value: &'static core::ffi::CStr) -> StringEntry { + StringEntry { + header: EntryCommon { + data_type: DataType::IdAndString, + tag, + }, + id, + value: value.as_ptr(), + } + } + + /// Get this entry's address + pub const fn addr(&self) -> EntryAddr { + EntryAddr(self as *const Self as *const u32) + } +} + +// We need this as rustc complains that is is unsafe to share `*const +// core::ffi::c_char` pointers between threads. We only allow these to be +// created with static string slices, so it's OK. +unsafe impl Sync for StringEntry {} + +/// An entry which contains both an ID (e.g. `ID_RP_BINARY_END`) and an integer. +#[repr(C)] +pub struct IntegerEntry { + header: EntryCommon, + id: u32, + value: u32, +} + +impl IntegerEntry { + /// Create a new `StringEntry` + pub const fn new(tag: u16, id: u32, value: u32) -> IntegerEntry { + IntegerEntry { + header: EntryCommon { + data_type: DataType::IdAndInt, + tag, + }, + id, + value, + } + } + + /// Get this entry's address + pub const fn addr(&self) -> EntryAddr { + EntryAddr(self as *const Self as *const u32) + } +} + +// End of file diff --git a/rp2040-hal/src/lib.rs b/rp2040-hal/src/lib.rs index 4f47d4d6a..fd12b3e36 100644 --- a/rp2040-hal/src/lib.rs +++ b/rp2040-hal/src/lib.rs @@ -52,6 +52,7 @@ pub mod adc; #[macro_use] pub mod async_utils; pub(crate) mod atomic_register_access; +pub mod binary_info; pub mod clocks; #[cfg(feature = "critical-section-impl")] mod critical_section_impl;