1 /*
2  * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include "pico/multicore.h"
8 #include "hardware/sync.h"
9 #include "hardware/irq.h"
10 #include "hardware/structs/scb.h"
11 #include "hardware/structs/sio.h"
12 #include "hardware/regs/psm.h"
13 #include "hardware/claim.h"
14 #if PICO_USE_STACK_GUARDS
15 #include "pico/runtime.h"
16 #endif
17 
18 // note that these are not reset by core reset, however for now, I think people resetting cores
19 // and then relying on multicore_lockout for that core without re-initializing, is probably
20 // something we can live with breaking.
21 //
22 // whilst we could clear this in core 1 reset path, that doesn't necessarily catch all,
23 // and means pulling in this array even if multicore_lockout is not used.
24 static bool lockout_victim_initialized[NUM_CORES];
25 
multicore_fifo_push_blocking_inline(uint32_t data)26 static inline void multicore_fifo_push_blocking_inline(uint32_t data) {
27     // We wait for the fifo to have some space
28     while (!multicore_fifo_wready())
29         tight_loop_contents();
30 
31     sio_hw->fifo_wr = data;
32 
33     // Fire off an event to the other core
34     __sev();
35 }
36 
multicore_fifo_push_blocking(uint32_t data)37 void multicore_fifo_push_blocking(uint32_t data) {
38     multicore_fifo_push_blocking_inline(data);
39 }
40 
multicore_fifo_push_timeout_us(uint32_t data,uint64_t timeout_us)41 bool multicore_fifo_push_timeout_us(uint32_t data, uint64_t timeout_us) {
42     absolute_time_t end_time = make_timeout_time_us(timeout_us);
43     // We wait for the fifo to have some space
44     while (!multicore_fifo_wready()) {
45         tight_loop_contents();
46         if (time_reached(end_time)) return false;
47     }
48 
49     sio_hw->fifo_wr = data;
50 
51     // Fire off an event to the other core
52     __sev();
53     return true;
54 }
55 
multicore_fifo_pop_blocking_inline(void)56 static inline uint32_t multicore_fifo_pop_blocking_inline(void) {
57     // If nothing there yet, we wait for an event first,
58     // to try and avoid too much busy waiting
59     while (!multicore_fifo_rvalid())
60         __wfe();
61 
62     return sio_hw->fifo_rd;
63 }
64 
multicore_fifo_pop_blocking()65 uint32_t multicore_fifo_pop_blocking() {
66     return multicore_fifo_pop_blocking_inline();
67 }
68 
multicore_fifo_pop_timeout_us(uint64_t timeout_us,uint32_t * out)69 bool multicore_fifo_pop_timeout_us(uint64_t timeout_us, uint32_t *out) {
70     absolute_time_t end_time = make_timeout_time_us(timeout_us);
71     // If nothing there yet, we wait for an event first,
72     // to try and avoid too much busy waiting
73     while (!multicore_fifo_rvalid()) {
74         __wfe();
75         if (time_reached(end_time)) return false;
76     }
77 
78     *out = sio_hw->fifo_rd;
79     return true;
80 }
81 
82 // Default stack for core1 ... if multicore_launch_core1 is not included then .stack1 section will be garbage collected
83 static uint32_t __attribute__((section(".stack1"))) core1_stack[PICO_CORE1_STACK_SIZE / sizeof(uint32_t)];
84 
core1_trampoline(void)85 static void __attribute__ ((naked)) core1_trampoline(void) {
86     pico_default_asm ("pop {r0, r1, pc}");
87 }
88 
core1_wrapper(int (* entry)(void),void * stack_base)89 int core1_wrapper(int (*entry)(void), void *stack_base) {
90 #if PICO_USE_STACK_GUARDS
91     // install core1 stack guard
92     runtime_install_stack_guard(stack_base);
93 #else
94     __unused void *ignore = stack_base;
95 #endif
96     irq_init_priorities();
97     return (*entry)();
98 }
99 
multicore_reset_core1()100 void multicore_reset_core1() {
101     // Use atomic aliases just in case core 1 is also manipulating some PSM state
102     io_rw_32 *power_off = (io_rw_32 *) (PSM_BASE + PSM_FRCE_OFF_OFFSET);
103     io_rw_32 *power_off_set = hw_set_alias(power_off);
104     io_rw_32 *power_off_clr = hw_clear_alias(power_off);
105 
106     // Hard-reset core 1.
107     // Reading back confirms the core 1 reset is in the correct state, but also
108     // forces APB IO bridges to fence on any internal store buffering
109     *power_off_set = PSM_FRCE_OFF_PROC1_BITS;
110     while (!(*power_off & PSM_FRCE_OFF_PROC1_BITS)) tight_loop_contents();
111 
112     // Bring core 1 back out of reset. It will drain its own mailbox FIFO, then push
113     // a 0 to our mailbox to tell us it has done this.
114     *power_off_clr = PSM_FRCE_OFF_PROC1_BITS;
115 }
116 
multicore_launch_core1_with_stack(void (* entry)(void),uint32_t * stack_bottom,size_t stack_size_bytes)117 void multicore_launch_core1_with_stack(void (*entry)(void), uint32_t *stack_bottom, size_t stack_size_bytes) {
118     assert(!(stack_size_bytes & 3u));
119     uint32_t *stack_ptr = stack_bottom + stack_size_bytes / sizeof(uint32_t);
120     // push 2 values onto top of stack for core1_trampoline
121     stack_ptr -= 3;
122     stack_ptr[0] = (uintptr_t) entry;
123     stack_ptr[1] = (uintptr_t) stack_bottom;
124     stack_ptr[2] = (uintptr_t) core1_wrapper;
125 #if PICO_VTABLE_PER_CORE
126 #warning PICO_VTABLE_PER_CORE==1 is not currently supported in pico_multicore
127     panic_unsupported();
128 #endif
129     multicore_launch_core1_raw(core1_trampoline, stack_ptr, scb_hw->vtor);
130 }
131 
multicore_launch_core1(void (* entry)(void))132 void multicore_launch_core1(void (*entry)(void)) {
133     extern uint32_t __StackOneBottom;
134     uint32_t *stack_limit = (uint32_t *) &__StackOneBottom;
135     // hack to reference core1_stack although that pointer is wrong.... core1_stack should always be <= stack_limit, if not boom!
136     uint32_t *stack = core1_stack <= stack_limit ? stack_limit : (uint32_t *) -1;
137     multicore_launch_core1_with_stack(entry, stack, sizeof(core1_stack));
138 }
139 
multicore_launch_core1_raw(void (* entry)(void),uint32_t * sp,uint32_t vector_table)140 void multicore_launch_core1_raw(void (*entry)(void), uint32_t *sp, uint32_t vector_table) {
141     // Allow for the fact that the caller may have already enabled the FIFO IRQ for their
142     // own purposes (expecting FIFO content after core 1 is launched). We must disable
143     // the IRQ during the handshake, then restore afterwards.
144     bool enabled = irq_is_enabled(SIO_IRQ_PROC0);
145     irq_set_enabled(SIO_IRQ_PROC0, false);
146 
147     // Values to be sent in order over the FIFO from core 0 to core 1
148     //
149     // vector_table is value for VTOR register
150     // sp is initial stack pointer (SP)
151     // entry is the initial program counter (PC) (don't forget to set the thumb bit!)
152     const uint32_t cmd_sequence[] =
153             {0, 0, 1, (uintptr_t) vector_table, (uintptr_t) sp, (uintptr_t) entry};
154 
155     uint seq = 0;
156     do {
157         uint cmd = cmd_sequence[seq];
158         // Always drain the READ FIFO (from core 1) before sending a 0
159         if (!cmd) {
160             multicore_fifo_drain();
161             // Execute a SEV as core 1 may be waiting for FIFO space via WFE
162             __sev();
163         }
164         multicore_fifo_push_blocking(cmd);
165         uint32_t response = multicore_fifo_pop_blocking();
166         // Move to next state on correct response (echo-d value) otherwise start over
167         seq = cmd == response ? seq + 1 : 0;
168     } while (seq < count_of(cmd_sequence));
169 
170     irq_set_enabled(SIO_IRQ_PROC0, enabled);
171 }
172 
173 #define LOCKOUT_MAGIC_START 0x73a8831eu
174 #define LOCKOUT_MAGIC_END (~LOCKOUT_MAGIC_START)
175 
176 static_assert(SIO_IRQ_PROC1 == SIO_IRQ_PROC0 + 1, "");
177 
178 static mutex_t lockout_mutex;
179 static bool lockout_in_progress;
180 
181 // note this method is in RAM because lockout is used when writing to flash
182 // it only makes inline calls
__not_in_flash_func(multicore_lockout_handler)183 static void __isr __not_in_flash_func(multicore_lockout_handler)(void) {
184     multicore_fifo_clear_irq();
185     while (multicore_fifo_rvalid()) {
186         if (sio_hw->fifo_rd == LOCKOUT_MAGIC_START) {
187             uint32_t save = save_and_disable_interrupts();
188             multicore_fifo_push_blocking_inline(LOCKOUT_MAGIC_START);
189             while (multicore_fifo_pop_blocking_inline() != LOCKOUT_MAGIC_END) {
190                 tight_loop_contents(); // not tight but endless potentially
191             }
192             restore_interrupts(save);
193             multicore_fifo_push_blocking_inline(LOCKOUT_MAGIC_END);
194         }
195     }
196 }
197 
check_lockout_mutex_init(void)198 static void check_lockout_mutex_init(void) {
199     // use known available lock - we only need it briefly
200     uint32_t save = hw_claim_lock();
201     if (!mutex_is_initialized(&lockout_mutex)) {
202         mutex_init(&lockout_mutex);
203     }
204     hw_claim_unlock(save);
205 }
206 
multicore_lockout_victim_init(void)207 void multicore_lockout_victim_init(void) {
208     check_lockout_mutex_init();
209     uint core_num = get_core_num();
210     irq_set_exclusive_handler(SIO_IRQ_PROC0 + core_num, multicore_lockout_handler);
211     irq_set_enabled(SIO_IRQ_PROC0 + core_num, true);
212     lockout_victim_initialized[core_num] = true;
213 }
214 
multicore_lockout_handshake(uint32_t magic,absolute_time_t until)215 static bool multicore_lockout_handshake(uint32_t magic, absolute_time_t until) {
216     uint irq_num = SIO_IRQ_PROC0 + get_core_num();
217     bool enabled = irq_is_enabled(irq_num);
218     if (enabled) irq_set_enabled(irq_num, false);
219     bool rc = false;
220     do {
221         int64_t next_timeout_us = absolute_time_diff_us(get_absolute_time(), until);
222         if (next_timeout_us < 0) {
223             break;
224         }
225         multicore_fifo_push_timeout_us(magic, (uint64_t)next_timeout_us);
226         next_timeout_us = absolute_time_diff_us(get_absolute_time(), until);
227         if (next_timeout_us < 0) {
228             break;
229         }
230         uint32_t word = 0;
231         if (!multicore_fifo_pop_timeout_us((uint64_t)next_timeout_us, &word)) {
232             break;
233         }
234         if (word == magic) {
235             rc = true;
236         }
237     } while (!rc);
238     if (enabled) irq_set_enabled(irq_num, true);
239     return rc;
240 }
241 
multicore_lockout_start_block_until(absolute_time_t until)242 static bool multicore_lockout_start_block_until(absolute_time_t until) {
243     check_lockout_mutex_init();
244     if (!mutex_enter_block_until(&lockout_mutex, until)) {
245         return false;
246     }
247     hard_assert(!lockout_in_progress);
248     bool rc = multicore_lockout_handshake(LOCKOUT_MAGIC_START, until);
249     lockout_in_progress = rc;
250     mutex_exit(&lockout_mutex);
251     return rc;
252 }
253 
multicore_lockout_start_timeout_us(uint64_t timeout_us)254 bool multicore_lockout_start_timeout_us(uint64_t timeout_us) {
255     return multicore_lockout_start_block_until(make_timeout_time_us(timeout_us));
256 }
257 
multicore_lockout_start_blocking(void)258 void multicore_lockout_start_blocking(void) {
259     multicore_lockout_start_block_until(at_the_end_of_time);
260 }
261 
multicore_lockout_end_block_until(absolute_time_t until)262 static bool multicore_lockout_end_block_until(absolute_time_t until) {
263     assert(mutex_is_initialized(&lockout_mutex));
264     if (!mutex_enter_block_until(&lockout_mutex, until)) {
265         return false;
266     }
267     assert(lockout_in_progress);
268     bool rc = multicore_lockout_handshake(LOCKOUT_MAGIC_END, until);
269     if (rc) {
270         lockout_in_progress = false;
271     }
272     mutex_exit(&lockout_mutex);
273     return rc;
274 }
275 
multicore_lockout_end_timeout_us(uint64_t timeout_us)276 bool multicore_lockout_end_timeout_us(uint64_t timeout_us) {
277     return multicore_lockout_end_block_until(make_timeout_time_us(timeout_us));
278 }
279 
multicore_lockout_end_blocking(void)280 void multicore_lockout_end_blocking(void) {
281     multicore_lockout_end_block_until(at_the_end_of_time);
282 }
283 
multicore_lockout_victim_is_initialized(uint core_num)284 bool multicore_lockout_victim_is_initialized(uint core_num) {
285     return lockout_victim_initialized[core_num];
286 }