• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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