xref: /aosp_15_r20/external/pigweed/pw_boot_cortex_m/core_init.c (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2020 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 
15 //                               !!!WARNING!!!
16 //
17 // Some of the code in this file is run without static initialization expected
18 // by C/C++. Any accesses to statically initialized objects/variables before
19 // memory is initialized will result in undefined values and violates the C
20 // specification. Only code run after memory initialization is complete will be
21 // compliant and truly safe to run. In general, make early initialization code
22 // run AFTER memory initialization has completed unless it is ABSOLUTELY
23 // NECESSARY to modify the way memory is initialized.
24 //
25 // When execution begins due to SoC power-on (or the device is reset), three
26 // key things must happen to properly enter C++ execution context:
27 //   1. Static variables must be loaded from flash to RAM.
28 //   2. Zero-initialized variables must be zero-initialized.
29 //   3. Statically allocated objects must have their constructors run.
30 // The SoC doesn't inherently have a notion of how to do this, so this is
31 // handled in StaticMemoryInit();
32 //
33 // Following this, execution is handed over to pw_PreMainInit() to facilitate
34 // platform, project, or application pre-main initialization. When
35 // pw_PreMainInit() returns, main() is executed.
36 //
37 // The simple flow is as follows:
38 //   1. Power on
39 //   2. PC (and maybe SP) set (from vector_table by SoC, or by bootloader)
40 //   3. pw_boot_Entry()
41 //     3.0. Initialize critical registers (VTOR, SP)
42 //     3.1. pw_boot_PreStaticMemoryInit();
43 //     3.2. Static-init memory (.data, .bss)
44 //     3.3. pw_boot_PreStaticConstructorInit();
45 //     3.4. Static C++ constructors
46 //     3.5. pw_boot_PreMainInit()
47 //     3.6. main()
48 //     3.7. pw_boot_PostMain()
49 
50 #include <stdbool.h>
51 #include <stdint.h>
52 #include <string.h>
53 
54 #include "pw_boot/boot.h"
55 #include "pw_boot_cortex_m/boot.h"
56 #include "pw_preprocessor/arch.h"
57 #include "pw_preprocessor/compiler.h"
58 
59 #if !_PW_ARCH_ARM_CORTEX_M
60 #error You can only build this for ARM Cortex-M architectures. If you are \
61        trying to do this and are still seeing this error, see \
62        pw_preprocessor/arch.h
63 #endif  // !_PW_ARCH_ARM_CORTEX_M
64 
65 #if !_PW_ARCH_ARM_V6M && !_PW_ARCH_ARM_V7M && !_PW_ARCH_ARM_V7EM && \
66     !_PW_ARCH_ARM_V8M_MAINLINE && !_PW_ARCH_ARM_V8_1M_MAINLINE
67 #error "Your selected Cortex-M arch is not yet supported by this module."
68 #endif
69 
70 // Extern symbols provided by linker script.
71 // These symbols tell us where various memory sections start and end.
72 extern uint8_t _pw_static_init_ram_start;
73 extern uint8_t _pw_static_init_ram_end;
74 extern uint8_t _pw_static_init_flash_start;
75 extern uint8_t _pw_zero_init_ram_start;
76 extern uint8_t _pw_zero_init_ram_end;
77 
78 // Functions called as part of firmware initialization.
79 void __libc_init_array(void);
80 
81 // WARNING: Be EXTREMELY careful when running code before this function
82 // completes. The context before this function violates the C spec
83 // (Section 6.7.8, paragraph 10 for example, which requires uninitialized static
84 // values to be zero-initialized).
StaticMemoryInit(void)85 void StaticMemoryInit(void) {
86   // Static-init RAM (load static values into ram, .data section init).
87   memcpy(&_pw_static_init_ram_start,
88          &_pw_static_init_flash_start,
89          &_pw_static_init_ram_end - &_pw_static_init_ram_start);
90 
91   // Zero-init RAM (.bss section init).
92   memset(&_pw_zero_init_ram_start,
93          0,
94          &_pw_zero_init_ram_end - &_pw_zero_init_ram_start);
95 }
96 
97 // WARNING: This code is run immediately upon boot, and performs initialization
98 // of RAM. Note that code running before this function finishes memory
99 // initialization will violate the C spec (Section 6.7.8, paragraph 10 for
100 // example, which requires uninitialized static values to be zero-initialized).
101 // Be EXTREMELY careful when running code before this function finishes RAM
102 // initialization.
103 //
104 // This function runs immediately at boot because it is at index 1 of the
105 // interrupt vector table.
106 //
107 // This initial stage is written in assembly in a function with no
108 // prologue/epilogue because it cannot assume a valid stack pointer has been
109 // set up.
110 PW_NO_PROLOGUE
pw_boot_Entry(void)111 void pw_boot_Entry(void) {
112   // No C code allowed here due to PW_NO_PROLOGUE, and don't use extended
113   // asm that might try to spill a register to the stack. See
114   // https://gcc.gnu.org/onlinedocs/gcc-14.1.0/gcc/ARM-Function-Attributes.html
115   asm volatile(
116       // Disable interrupts.
117       //
118       // Until pw_boot_PreStaticMemoryInit() has completed, depending on the
119       // bootloader (or lack thereof), there is no guarantee that the vector
120       // table has been correctly set up, so it's not safe to run interrupts
121       // until after this function returns.
122       //
123       // Until StaticMemoryInit() has completed, interrupt handlers cannot use
124       // either statically initialized RAM or zero initialized RAM. Since most
125       // interrupt handlers need one or the other to change system state, it's
126       // not safe to run handlers until after this function returns.
127       "cpsid i                           \n"
128 
129 #if _PW_ARCH_ARM_V8M_MAINLINE || _PW_ARCH_ARM_V8_1M_MAINLINE
130       // Set VTOR to the location of the vector table.
131       //
132       // Devices with a bootloader will often set VTOR after parsing the loaded
133       // binary and prior to launching it. When no bootloader is present, or if
134       // launched directly from memory after a reset, VTOR will be zero and must
135       // be assigned the correct value.
136       "ldr r0, =0xE000ED08               \n"
137       "ldr r1, =pw_boot_vector_table_addr\n"
138       "str r1, [r0]                      \n"
139 
140       // Configure MSP and MSPLIM.
141       "ldr r0, =pw_boot_stack_high_addr  \n"
142       "msr msp, r0                       \n"
143 
144       "ldr r0, =pw_boot_stack_low_addr   \n"
145       "msr msplim, r0                    \n"
146 #endif  // _PW_ARCH_ARM_V8M_MAINLINE || _PW_ARCH_ARM_V8_1M_MAINLINE
147 
148       // We have a stack; proceed to actual C code.
149       "b _pw_boot_Entry                  \n");
150 }
151 
152 // C continuation of pw_boot_Entry, this cannot be marked static because
153 // it is only referenced by asm and may be optimized away.
_pw_boot_Entry(void)154 void _pw_boot_Entry(void) {
155   // Run any init that must be done before static init of RAM which preps the
156   // .data (static values not yet loaded into ram) and .bss sections (not yet
157   // zero-initialized).
158   pw_boot_PreStaticMemoryInit();
159 
160   // Note that code running before this function finishes memory
161   // initialization will violate the C spec (Section 6.7.8, paragraph 10 for
162   // example, which requires uninitialized static values to be
163   // zero-initialized). Be EXTREMELY careful when running code before this
164   // function finishes static memory initialization.
165   StaticMemoryInit();
166 
167   // Reenable interrupts.
168   //
169   // Care is still required since C++ static constructors have not yet been
170   // initialized, however it's a lot less likely that an interrupt handler
171   // (which are small and focused) will have an issue there.
172   asm volatile("cpsie i");
173 
174   // Run any init that must be done before C++ static constructors.
175   pw_boot_PreStaticConstructorInit();
176 
177   // Call static constructors.
178   __libc_init_array();
179 
180   // This function is not provided by pw_boot_cortex_m, a platform layer,
181   // project, or application is expected to implement it.
182   pw_boot_PreMainInit();
183 
184   // Run main.
185   main();
186 
187   // In case main() returns, invoke this hook.
188   pw_boot_PostMain();
189 
190   PW_UNREACHABLE;
191 }
192