aboutsummaryrefslogtreecommitdiff
path: root/pw_boot_cortex_m/core_init.c
blob: d2d6db3e444535d82ae149df1448f17ba25c57ac (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
// Copyright 2020 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

//                               !!!WARNING!!!
//
// Some of the code in this file is run without static initialization expected
// by C/C++. Any accesses to statically initialized objects/variables before
// memory is initialized will result in undefined values and violates the C
// specification. Only code run after memory initialization is complete will be
// compliant and truly safe to run. In general, make early initialization code
// run AFTER memory initialization has completed unless it is ABSOLUTELY
// NECESSARY to modify the way memory is initialized.
//
// This file is similar to a traditional assembly startup file. It turns out
// that everything typically done in ARMv7-M assembly startup can be done
// straight from C code. This makes startup code easier to maintain, modify,
// and read.
//
// When execution begins due to SoC power-on (or the device is reset), three
// key things must happen to properly enter C++ execution context:
//   1. Static variables must be loaded from flash to RAM.
//   2. Zero-initialized variables must be zero-initialized.
//   3. Statically allocated objects must have their constructors run.
// The SoC doesn't inherently have a notion of how to do this, so this is
// handled in StaticInit();
//
// Following this, execution is handed over to pw_PreMainInit() to facilitate
// platform, project, or application pre-main initialization. When
// pw_PreMainInit() returns, main() is executed.
//
// The simple flow is as follows:
//   1. Power on
//   2. PC and SP set (from vector_table by SoC, or by bootloader)
//   3. pw_boot_Entry()
//     3.1. pw_boot_PreStaticMemoryInit();
//     3.2. Static-init memory (.data, .bss)
//     3.3. pw_boot_PreStaticConstructorInit();
//     3.4. Static C++ constructors
//     3.5. pw_boot_PreMainInit()
//     3.6. main()
//     3.7. pw_boot_PostMain()

#include <stdbool.h>
#include <stdint.h>
#include <string.h>

#include "pw_boot/boot.h"
#include "pw_boot_cortex_m/boot.h"
#include "pw_preprocessor/arch.h"
#include "pw_preprocessor/compiler.h"

#if !_PW_ARCH_ARM_CORTEX_M
#error You can only build this for ARM Cortex-M architectures. If you are \
       trying to do this and are still seeing this error, see \
       pw_preprocessor/arch.h
#endif  // !_PW_ARCH_ARM_CORTEX_M

#if !_PW_ARCH_ARM_V6M && !_PW_ARCH_ARM_V7M && !_PW_ARCH_ARM_V7EM && \
    !_PW_ARCH_ARM_V8M_MAINLINE && !_PW_ARCH_ARM_V8_1M_MAINLINE
#error "Your selected Cortex-M arch is not yet supported by this module."
#endif

// Extern symbols provided by linker script.
// These symbols tell us where various memory sections start and end.
extern uint8_t _pw_static_init_ram_start;
extern uint8_t _pw_static_init_ram_end;
extern uint8_t _pw_static_init_flash_start;
extern uint8_t _pw_zero_init_ram_start;
extern uint8_t _pw_zero_init_ram_end;

// Functions called as part of firmware initialization.
void __libc_init_array(void);

// WARNING: Be EXTREMELY careful when running code before this function
// completes. The context before this function violates the C spec
// (Section 6.7.8, paragraph 10 for example, which requires uninitialized static
// values to be zero-initialized).
void StaticMemoryInit(void) {
  // Static-init RAM (load static values into ram, .data section init).
  memcpy(&_pw_static_init_ram_start,
         &_pw_static_init_flash_start,
         &_pw_static_init_ram_end - &_pw_static_init_ram_start);

  // Zero-init RAM (.bss section init).
  memset(&_pw_zero_init_ram_start,
         0,
         &_pw_zero_init_ram_end - &_pw_zero_init_ram_start);
}

// WARNING: This code is run immediately upon boot, and performs initialization
// of RAM. Note that code running before this function finishes memory
// initialization will violate the C spec (Section 6.7.8, paragraph 10 for
// example, which requires uninitialized static values to be zero-initialized).
// Be EXTREMELY careful when running code before this function finishes RAM
// initialization.
//
// This function runs immediately at boot because it is at index 1 of the
// interrupt vector table.
void pw_boot_Entry(void) {
  // Disable interrupts.
  //
  // Until pw_boot_PreStaticMemoryInit() has completed, depending on the
  // bootloader (or lack thereof), there is no guarantee that the vector
  // table has been correctly set up, so it's not safe to run interrupts
  // until after this function returns.
  //
  // Until StaticMemoryInit() has completed, interrupt handlers cannot use
  // either statically initialized RAM or zero initialized RAM. Since most
  // interrupt handlers need one or the other to change system state, it's
  // not safe to run handlers until after this function returns.
  asm volatile("cpsid i");

#if _PW_ARCH_ARM_V8M_MAINLINE || _PW_ARCH_ARM_V8_1M_MAINLINE
  // Configure MSP and MSPLIM.
  asm volatile(
      "msr msp, %0    \n"
      "msr msplim, %1 \n"
      // clang-format off
      : /*output=*/
      : /*input=*/ "r"(&pw_boot_stack_high_addr), "r"(&pw_boot_stack_low_addr)
      : /*clobbers=*/
      // clang-format on
  );
#endif  // _PW_ARCH_ARM_V8M_MAINLINE || _PW_ARCH_ARM_V8_1M_MAINLINE

  // Run any init that must be done before static init of RAM which preps the
  // .data (static values not yet loaded into ram) and .bss sections (not yet
  // zero-initialized).
  pw_boot_PreStaticMemoryInit();

  // Note that code running before this function finishes memory
  // initialization will violate the C spec (Section 6.7.8, paragraph 10 for
  // example, which requires uninitialized static values to be
  // zero-initialized). Be EXTREMELY careful when running code before this
  // function finishes static memory initialization.
  StaticMemoryInit();

  // Reenable interrupts.
  //
  // Care is still required since C++ static constructors have not yet been
  // initialized, however it's a lot less likely that an interrupt handler
  // (which are small and focused) will have an issue there.
  asm volatile("cpsie i");

  // Run any init that must be done before C++ static constructors.
  pw_boot_PreStaticConstructorInit();

  // Call static constructors.
  __libc_init_array();

  // This function is not provided by pw_boot_cortex_m, a platform layer,
  // project, or application is expected to implement it.
  pw_boot_PreMainInit();

  // Run main.
  main();

  // In case main() returns, invoke this hook.
  pw_boot_PostMain();

  PW_UNREACHABLE;
}