rp2040_rom/
lib.rs

1#![no_std]
2
3//! This module provides the ability to call Raspberry Pi ROM functions.
4//!
5//! The RP2040 ROM contains several useful functions that can be called
6//! directly from user code, including functions to reset the device and
7//! enter the USB bootloader.
8//!
9//! # Safety
10//!
11//! All functions in this crate are marked as `unsafe` because they involve
12//! direct hardware manipulation and can reset the device.
13//!
14//! # Example
15//!
16//! ```rust,no_run
17//! use rp2040_rom::ROM;
18//!
19//! // Reset into USB bootloader mode
20//! unsafe {
21//!     ROM::reset_usb_boot(0, 0);
22//! }
23//! ```
24
25// Copyright (c) 2025 Piers Finlayson <piers@piers.rocks>
26//
27// MIT licensed - see https://opensource.org/licenses/MIT
28
29/// ROM function table offset for the RP2040
30/// From the datasheet:
31///   Pointer to a public function lookup table (rom_func_table)
32const BOOTROM_FUNC_TABLE_OFFSET: u16 = 0x14;
33
34/// ROM lookup table offset for the RP2040
35/// From the datasheet:
36///   Pointer to a helper function (rom_table_lookup())
37const BOOTROM_TABLE_LOOKUP_OFFSET: u16 = 0x18;
38
39/// Object containing exposed ROM functions
40#[allow(clippy::upper_case_acronyms)]
41pub struct ROM {}
42
43/// Public functions
44impl ROM {
45    /// Resets the device and enters USB bootloader mode.
46    ///
47    /// # Parameters
48    ///
49    /// * `usb_activity_gpio_pin_mask` - Bitmask of GPIO pins to check for USB activity
50    /// * `disable_interface_mask` - Bitmask to disable specific interfaces
51    ///
52    /// # Safety
53    ///
54    /// This function will reset the device and not return.
55    pub unsafe fn reset_usb_boot(usb_activity_gpio_pin_mask: u32, disable_interface_mask: u32) -> ! {
56        // ROM reset_usb_boot function definition
57        type RomResetUsbBootFn =
58            unsafe extern "C" fn(usb_activity_gpio_pin_mask: u32, disable_interface_mask: u32)  -> !;
59
60        // The two character code for the reset_usb_boot function in the
61        // lookup table
62        const ROM_FUNC_RESET_USB_BOOT: (u8, u8) = (b'U', b'B');
63
64        // Get the function pointer for reset_usb_boot and turn it into a
65        // function we can call
66        let func_ptr = Self::rom_func_lookup(ROM_FUNC_RESET_USB_BOOT);
67        let func: RomResetUsbBootFn = core::mem::transmute(func_ptr);
68
69        // Call the function
70        func(usb_activity_gpio_pin_mask, disable_interface_mask);
71    }
72}
73
74// Private functions
75impl ROM {
76    // Get the lookup code for a function, based on the two characters
77    // While the lookup table technically takes a u16, the lookup function
78    // takes a u32, so we'll use a u32 internally.
79    const fn rom_table_code(c1: u8, c2: u8) -> u32 {
80        (c1 as u32) | ((c2 as u32) << 8)
81    }
82
83    // Convert a u16 provided by the ROM lookup table to a pointer
84    unsafe fn rom_hword_as_ptr(rom_address: u16) -> *mut core::ffi::c_void {
85        // Convert to usize first
86        let addr_val = rom_address as usize;
87
88        // Create pointer AND dereference it
89        let value = unsafe { *(addr_val as *const u16) };
90
91        // Convert value to pointer size then to void pointer
92        value as usize as *mut core::ffi::c_void
93    }
94
95    // Get the pointer for a function, based on the two characters used to
96    // index it
97    unsafe fn rom_func_lookup(code: (u8, u8)) -> *mut core::ffi::c_void {
98        // The ROM reset_usb_boot function definition
99        type RomTableLookupFn =
100            unsafe extern "C" fn(table: *const u16, code: u32) -> *mut core::ffi::c_void;
101
102        // Get the 32-bit code for the two characters that we need to pass
103        // into the lookup function
104        let (c1, c2) = code;
105        let code = Self::rom_table_code(c1, c2);
106
107        // Get the function table address
108        let func_table_addr = Self::rom_hword_as_ptr(BOOTROM_FUNC_TABLE_OFFSET);
109        let func_table = func_table_addr as *const u16;
110
111        // Get the lookup function address
112        let lookup_addr = Self::rom_hword_as_ptr(BOOTROM_TABLE_LOOKUP_OFFSET);
113        let rom_table_lookup: RomTableLookupFn = core::mem::transmute(lookup_addr);
114
115        // Use the lookup function to lookup this code
116        rom_table_lookup(func_table, code)
117    }
118}