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