1 /*
2 * Copyright (c) 2024 Raspberry Pi (Trading) Ltd.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7 #ifndef _HARDWARE_BOOT_LOCK_H
8 #define _HARDWARE_BOOT_LOCK_H
9
10 #include "pico.h"
11
12 // PICO_CONFIG: PARAM_ASSERTIONS_ENABLED_HARDWARE_BOOT_LOCK, Enable/disable assertions in the hardware_boot_lock module, type=bool, default=0, group=hardware_boot_lock
13 #ifndef PARAM_ASSERTIONS_ENABLED_HARDWARE_BOOT_LOCK
14 #define PARAM_ASSERTIONS_ENABLED_HARDWARE_BOOT_LOCK 0
15 #endif
16
17 #if NUM_BOOT_LOCKS > 0
18 #include "hardware/sync.h"
19 #include "hardware/structs/bootram.h"
20
21 /** \brief A boot lock identifier
22 * \ingroup hardware_sync
23 */
24 typedef volatile uint32_t boot_lock_t;
25
26 /*! \brief Get HW Bootlock instance from number
27 * \ingroup hardware_sync
28 *
29 * \param lock_num Bootlock ID
30 * \return The bootlock instance
31 */
boot_lock_instance(uint lock_num)32 __force_inline static boot_lock_t *boot_lock_instance(uint lock_num) {
33 invalid_params_if(HARDWARE_BOOT_LOCK, lock_num >= NUM_BOOT_LOCKS);
34 return (boot_lock_t *) (BOOTRAM_BASE + BOOTRAM_BOOTLOCK0_OFFSET + lock_num * 4);
35 }
36
37 /*! \brief Get HW Bootlock number from instance
38 * \ingroup hardware_sync
39 *
40 * \param lock The Bootlock instance
41 * \return The Bootlock ID
42 */
boot_lock_get_num(boot_lock_t * lock)43 __force_inline static uint boot_lock_get_num(boot_lock_t *lock) {
44 invalid_params_if(HARDWARE_BOOT_LOCK, (uint) lock < BOOTRAM_BASE + BOOTRAM_BOOTLOCK0_OFFSET ||
45 (uint) lock >= NUM_BOOT_LOCKS * sizeof(boot_lock_t) + BOOTRAM_BASE + BOOTRAM_BOOTLOCK0_OFFSET ||
46 ((uint) lock - BOOTRAM_BASE + BOOTRAM_BOOTLOCK0_OFFSET) % sizeof(boot_lock_t) != 0);
47 return (uint) (lock - (boot_lock_t *) (BOOTRAM_BASE + BOOTRAM_BOOTLOCK0_OFFSET));
48 }
49
50 /*! \brief Acquire a boot lock without disabling interrupts (hence unsafe)
51 * \ingroup hardware_sync
52 *
53 * \param lock Bootlock instance
54 */
boot_lock_unsafe_blocking(boot_lock_t * lock)55 __force_inline static void boot_lock_unsafe_blocking(boot_lock_t *lock) {
56 // Note we don't do a wfe or anything, because by convention these boot_locks are VERY SHORT LIVED and NEVER BLOCK and run
57 // with INTERRUPTS disabled (to ensure that)... therefore nothing on our core could be blocking us, so we just need to wait on another core
58 // anyway which should be finished soon
59 while (__builtin_expect(!*lock, 0)) { // read from bootlock register (tries to acquire the lock)
60 tight_loop_contents();
61 }
62 __mem_fence_acquire();
63 }
64
65 /*! \brief try to acquire a boot lock without disabling interrupts (hence unsafe)
66 * \ingroup hardware_sync
67 *
68 * \param lock Bootlock instance
69 */
boot_try_lock_unsafe(boot_lock_t * lock)70 __force_inline static bool boot_try_lock_unsafe(boot_lock_t *lock) {
71 if (*lock) {
72 __mem_fence_acquire();
73 return true;
74 }
75 return false;
76 }
77
78 /*! \brief Release a boot lock without re-enabling interrupts
79 * \ingroup hardware_sync
80 *
81 * \param lock Bootlock instance
82 */
boot_unlock_unsafe(boot_lock_t * lock)83 __force_inline static void boot_unlock_unsafe(boot_lock_t *lock) {
84 __mem_fence_release();
85 *lock = 0; // write to bootlock register (release lock)
86 }
87
88 /*! \brief Acquire a boot lock safely
89 * \ingroup hardware_sync
90 *
91 * This function will disable interrupts prior to acquiring the bootlock
92 *
93 * \param lock Bootlock instance
94 * \return interrupt status to be used when unlocking, to restore to original state
95 */
boot_lock_blocking(boot_lock_t * lock)96 __force_inline static uint32_t boot_lock_blocking(boot_lock_t *lock) {
97 uint32_t save = save_and_disable_interrupts();
98 boot_lock_unsafe_blocking(lock);
99 return save;
100 }
101
102 /*! \brief Check to see if a bootlock is currently acquired elsewhere.
103 * \ingroup hardware_sync
104 *
105 * \param lock Bootlock instance
106 */
is_boot_locked(boot_lock_t * lock)107 inline static bool is_boot_locked(boot_lock_t *lock) {
108 check_hw_size(boot_lock_t, 4);
109 uint lock_num = boot_lock_get_num(lock);
110 return 0 != (*(io_ro_32 *) (BOOTRAM_BASE + BOOTRAM_BOOTLOCK_STAT_OFFSET) & (1u << lock_num));
111 }
112
113 /*! \brief Release a boot lock safely
114 * \ingroup hardware_sync
115 *
116 * This function will re-enable interrupts according to the parameters.
117 *
118 * \param lock Bootlock instance
119 * \param saved_irq Return value from the \ref boot_lock_blocking() function.
120 *
121 * \sa boot_lock_blocking()
122 */
boot_unlock(boot_lock_t * lock,uint32_t saved_irq)123 __force_inline static void boot_unlock(boot_lock_t *lock, uint32_t saved_irq) {
124 boot_unlock_unsafe(lock);
125 restore_interrupts_from_disabled(saved_irq);
126 }
127
128 /*! \brief Initialise a boot lock
129 * \ingroup hardware_sync
130 *
131 * The boot lock is initially unlocked
132 *
133 * \param lock_num The boot lock number
134 * \return The boot lock instance
135 */
136 boot_lock_t *boot_lock_init(uint lock_num);
137
138 /*! \brief Release all boot locks
139 * \ingroup hardware_sync
140 */
141 void boot_locks_reset(void);
142
143 #endif
144 #endif