1 /*
2  * Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #ifndef _PICO_FLASH_H
8 #define _PICO_FLASH_H
9 
10 #include "pico.h"
11 
12 #include "hardware/flash.h"
13 #include "pico/time.h"
14 
15 /** \file pico/flash.h
16  *  \defgroup pico_flash pico_flash
17  *
18  * High level flash API
19  *
20  * Flash cannot be erased or written to when in XIP mode. However the system cannot directly access memory in the flash
21  * address space when not in XIP mode.
22  *
23  * It is therefore critical that no code or data is being read from flash while flash is been written or erased.
24  *
25  * If only one core is being used, then the problem is simple - just disable interrupts; however if code is running on
26  * the other core, then it has to be asked, nicely, to avoid flash for a bit. This is hard to do if you don't have
27  * complete control of the code running on that core at all times.
28  *
29  * This library provides a \ref flash_safe_execute method which calls a function back having sucessfully gotten
30  * into a state where interrupts are disabled, and the other core is not executing or reading from flash.
31  *
32  * How it does this is dependent on the supported environment (Free RTOS SMP or pico_multicore). Additionally
33  * the user can provide their own mechanism by providing a strong definition of \ref get_flash_safety_helper().
34  *
35  * Using the default settings, flash_safe_execute will only call the callback function if the state is safe
36  * otherwise returning an error (or an assert depending on \ref PICO_FLASH_ASSERT_ON_UNSAFE).
37  *
38  * There are conditions where safety would not be guaranteed:
39  *
40  * 1. FreeRTOS smp with `configNUM_CORES=1` - FreeRTOS still uses pico_multicore in this case, so \ref flash_safe_execute
41  * cannot know what the other core is doing, and there is no way to force code execution between a FreeRTOS core
42  * and a non FreeRTOS core.
43  * 2. FreeRTOS non SMP with pico_multicore - Again, there is no way to force code execution between a FreeRTOS core and
44  * a non FreeRTOS core.
45  * 3. pico_multicore without \ref flash_safe_execute_core_init() having been called on the other core - The
46  * \ref flash_safe_execute method does not know if code is executing on the other core, so it has to assume it is. Either
47  * way, it is not able to intervene if \ref flash_safe_execute_core_init() has not been called on the other core.
48  *
49  * Fortunately, all is not lost in this situation, you may:
50  *
51  * * Set \ref PICO_FLASH_ASSUME_CORE0_SAFE=1 to explicitly say that core 0 is never using flash.
52  * * Set \ref PICO_FLASH_ASSUME_CORE1_SAFE=1 to explicitly say that core 1 is never using flash.
53  */
54 
55 #ifdef __cplusplus
56 extern "C" {
57 #endif
58 
59 /**
60  * Initialize a core such that the other core can lock it out during \ref flash_safe_execute.
61  * \ingroup pico_flash
62  *
63  * \note This is not necessary for FreeRTOS SMP, but should be used when launching via \ref multicore_launch_core1
64  * \return true on success; there is no need to call \ref flash_safe_execute_core_deinit() on failure.
65  */
66 bool flash_safe_execute_core_init(void);
67 
68 /**
69  * De-initialize work done by \ref flash_safe_execute_core_init
70  * \ingroup pico_flash
71  * \return true on success
72  */
73 bool flash_safe_execute_core_deinit(void);
74 
75 /**
76  * Execute a function with IRQs disabled and with the other core also not executing/reading flash
77  * \ingroup pico_flash
78  *
79  * \param func the function to call
80  * \param param the parameter to pass to the function
81  * \param enter_exit_timeout_ms the timeout for each of the enter/exit phases when coordinating with the other core
82  *
83  * \return PICO_OK on success (the function will have been called).
84  *         PICO_TIMEOUT on timeout (the function may have been called).
85  *         PICO_ERROR_NOT_PERMITTED if safe execution is not possible (the function will not have been called).
86  *         PICO_ERROR_INSUFFICIENT_RESOURCES if the method fails due to dynamic resource exhaustion (the function will not have been called)
87  * \note if \ref PICO_FLASH_ASSERT_ON_UNSAFE is 1, this function will assert in debug mode vs returning
88  *       PICO_ERROR_NOT_PERMITTED
89  */
90 int flash_safe_execute(void (*func)(void *), void *param, uint32_t enter_exit_timeout_ms);
91 
92 // PICO_CONFIG: PICO_FLASH_ASSERT_ON_UNSAFE, Assert in debug mode rather than returning an error if flash_safe_execute cannot guarantee safety to catch bugs early, type=bool, default=1, group=pico_flash
93 #ifndef PICO_FLASH_ASSERT_ON_UNSAFE
94 #define PICO_FLASH_ASSERT_ON_UNSAFE 1
95 #endif
96 
97 // PICO_CONFIG: PICO_FLASH_ASSUME_CORE0_SAFE, Assume that core 0 will never be accessing flash and so doesn't need to be considered during flash_safe_execute, type=bool, default=0, group=pico_flash
98 #ifndef PICO_FLASH_ASSUME_CORE0_SAFE
99 #define PICO_FLASH_ASSUME_CORE0_SAFE 0
100 #endif
101 
102 // PICO_CONFIG: PICO_FLASH_ASSUME_CORE1_SAFE, Assume that core 1 will never be accessing flash and so doesn't need to be considered during flash_safe_execute, type=bool, default=0, group=pico_flash
103 #ifndef PICO_FLASH_ASSUME_CORE1_SAFE
104 #define PICO_FLASH_ASSUME_CORE1_SAFE 0
105 #endif
106 
107 // PICO_CONFIG: PICO_FLASH_SAFE_EXECUTE_SUPPORT_FREERTOS_SMP, Support using FreeRTOS SMP to make the other core safe during flash_safe_execute, type=bool, default=1 when using FreeRTOS SMP, group=pico_flash
108 #ifndef PICO_FLASH_SAFE_EXECUTE_SUPPORT_FREERTOS_SMP
109 #if LIB_FREERTOS_KERNEL && FREE_RTOS_KERNEL_SMP // set by RP2040 SMP port
110 #define PICO_FLASH_SAFE_EXECUTE_SUPPORT_FREERTOS_SMP 1
111 #endif
112 #endif
113 
114 // PICO_CONFIG: PICO_FLASH_SAFE_EXECUTE_PICO_SUPPORT_MULTICORE_LOCKOUT, Support using multicore_lockout functions to make the other core safe during flash_safe_execute, type=bool, default=1 when using pico_multicore, group=pico_flash
115 #ifndef PICO_FLASH_SAFE_EXECUTE_PICO_SUPPORT_MULTICORE_LOCKOUT
116 #if LIB_PICO_MULTICORE
117 #define PICO_FLASH_SAFE_EXECUTE_PICO_SUPPORT_MULTICORE_LOCKOUT 1
118 #endif
119 #endif
120 
121 typedef struct {
122     bool (*core_init_deinit)(bool init);
123     int (*enter_safe_zone_timeout_ms)(uint32_t timeout_ms);
124     int (*exit_safe_zone_timeout_ms)(uint32_t timeout_ms);
125 } flash_safety_helper_t;
126 
127 /**
128  * Internal method to return the flash safety helper implementation.
129  * \ingroup pico_flash
130  *
131  * Advanced users can provide their own implementation of this function to perform
132  * different inter-core coordination before disabling XIP mode.
133  *
134  * @return the \ref flash_safety_helper_t
135  */
136 flash_safety_helper_t *get_flash_safety_helper(void);
137 
138 #ifdef __cplusplus
139 }
140 #endif
141 
142 #endif
143