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 // This file is similar to a traditional assembly startup file. It turns out
26 // that everything typically done in ARMv7-M assembly startup can be done
27 // straight from C code. This makes startup code easier to maintain, modify,
28 // and read.
29 //
30 // When execution begins due to SoC power-on (or the device is reset), three
31 // key things must happen to properly enter C++ execution context:
32 // 1. Static variables must be loaded from flash to RAM.
33 // 2. Zero-initialized variables must be zero-initialized.
34 // 3. Statically allocated objects must have their constructors run.
35 // The SoC doesn't inherently have a notion of how to do this, so this is
36 // handled in StaticInit();
37 //
38 // Following this, execution is handed over to pw_PreMainInit() to facilitate
39 // platform, project, or application pre-main initialization. When
40 // pw_PreMainInit() returns, main() is executed.
41 //
42 // The simple flow is as follows:
43 // 1. Power on
44 // 2. PC and SP set (from vector_table by SoC, or by bootloader)
45 // 3. pw_boot_Entry()
46 // 3.1. pw_boot_PreStaticMemoryInit();
47 // 3.2. Static-init memory (.data, .bss)
48 // 3.3. pw_boot_PreStaticConstructorInit();
49 // 3.4. Static C++ constructors
50 // 3.5. pw_boot_PreMainInit()
51 // 3.6. main()
52 // 3.7. pw_boot_PostMain()
53
54 #include <stdbool.h>
55 #include <stdint.h>
56 #include <string.h>
57
58 #include "pw_boot/boot.h"
59 #include "pw_boot_cortex_m/boot.h"
60 #include "pw_preprocessor/arch.h"
61 #include "pw_preprocessor/compiler.h"
62
63 #if !_PW_ARCH_ARM_CORTEX_M
64 #error You can only build this for ARM Cortex-M architectures. If you are \
65 trying to do this and are still seeing this error, see \
66 pw_preprocessor/arch.h
67 #endif // !_PW_ARCH_ARM_CORTEX_M
68
69 #if !_PW_ARCH_ARM_V6M && !_PW_ARCH_ARM_V7M && !_PW_ARCH_ARM_V7EM && \
70 !_PW_ARCH_ARM_V8M_MAINLINE && !_PW_ARCH_ARM_V8_1M_MAINLINE
71 #error "Your selected Cortex-M arch is not yet supported by this module."
72 #endif
73
74 // Extern symbols provided by linker script.
75 // These symbols tell us where various memory sections start and end.
76 extern uint8_t _pw_static_init_ram_start;
77 extern uint8_t _pw_static_init_ram_end;
78 extern uint8_t _pw_static_init_flash_start;
79 extern uint8_t _pw_zero_init_ram_start;
80 extern uint8_t _pw_zero_init_ram_end;
81
82 // Functions called as part of firmware initialization.
83 void __libc_init_array(void);
84
85 // WARNING: Be EXTREMELY careful when running code before this function
86 // completes. The context before this function violates the C spec
87 // (Section 6.7.8, paragraph 10 for example, which requires uninitialized static
88 // values to be zero-initialized).
StaticMemoryInit(void)89 void StaticMemoryInit(void) {
90 // Static-init RAM (load static values into ram, .data section init).
91 memcpy(&_pw_static_init_ram_start,
92 &_pw_static_init_flash_start,
93 &_pw_static_init_ram_end - &_pw_static_init_ram_start);
94
95 // Zero-init RAM (.bss section init).
96 memset(&_pw_zero_init_ram_start,
97 0,
98 &_pw_zero_init_ram_end - &_pw_zero_init_ram_start);
99 }
100
101 // WARNING: This code is run immediately upon boot, and performs initialization
102 // of RAM. Note that code running before this function finishes memory
103 // initialization will violate the C spec (Section 6.7.8, paragraph 10 for
104 // example, which requires uninitialized static values to be zero-initialized).
105 // Be EXTREMELY careful when running code before this function finishes RAM
106 // initialization.
107 //
108 // This function runs immediately at boot because it is at index 1 of the
109 // interrupt vector table.
pw_boot_Entry(void)110 void pw_boot_Entry(void) {
111 // Disable interrupts.
112 //
113 // Until pw_boot_PreStaticMemoryInit() has completed, depending on the
114 // bootloader (or lack thereof), there is no guarantee that the vector
115 // table has been correctly set up, so it's not safe to run interrupts
116 // until after this function returns.
117 //
118 // Until StaticMemoryInit() has completed, interrupt handlers cannot use
119 // either statically initialized RAM or zero initialized RAM. Since most
120 // interrupt handlers need one or the other to change system state, it's
121 // not safe to run handlers until after this function returns.
122 asm volatile("cpsid i");
123
124 #if _PW_ARCH_ARM_V8M_MAINLINE || _PW_ARCH_ARM_V8_1M_MAINLINE
125 // Configure MSP and MSPLIM.
126 asm volatile(
127 "msr msp, %0 \n"
128 "msr msplim, %1 \n"
129 // clang-format off
130 : /*output=*/
131 : /*input=*/ "r"(&pw_boot_stack_high_addr), "r"(&pw_boot_stack_low_addr)
132 : /*clobbers=*/
133 // clang-format on
134 );
135 #endif // _PW_ARCH_ARM_V8M_MAINLINE || _PW_ARCH_ARM_V8_1M_MAINLINE
136
137 // Run any init that must be done before static init of RAM which preps the
138 // .data (static values not yet loaded into ram) and .bss sections (not yet
139 // zero-initialized).
140 pw_boot_PreStaticMemoryInit();
141
142 // Note that code running before this function finishes memory
143 // initialization will violate the C spec (Section 6.7.8, paragraph 10 for
144 // example, which requires uninitialized static values to be
145 // zero-initialized). Be EXTREMELY careful when running code before this
146 // function finishes static memory initialization.
147 StaticMemoryInit();
148
149 // Reenable interrupts.
150 //
151 // Care is still required since C++ static constructors have not yet been
152 // initialized, however it's a lot less likely that an interrupt handler
153 // (which are small and focused) will have an issue there.
154 asm volatile("cpsie i");
155
156 // Run any init that must be done before C++ static constructors.
157 pw_boot_PreStaticConstructorInit();
158
159 // Call static constructors.
160 __libc_init_array();
161
162 // This function is not provided by pw_boot_cortex_m, a platform layer,
163 // project, or application is expected to implement it.
164 pw_boot_PreMainInit();
165
166 // Run main.
167 main();
168
169 // In case main() returns, invoke this hook.
170 pw_boot_PostMain();
171
172 PW_UNREACHABLE;
173 }
174