aboutsummaryrefslogtreecommitdiff
path: root/libc/startup/baremetal/arm/start.cpp
blob: 4740067722022178a87560b1aad75274169a6517 (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
174
175
176
177
178
179
180
181
182
//===-- Implementation of crt for arm -------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "hdr/stdint_proxy.h"
#include "src/__support/macros/config.h"
#include "src/stdlib/atexit.h"
#include "src/stdlib/exit.h"
#include "src/string/memcpy.h"
#include "src/string/memset.h"
#include "startup/baremetal/fini.h"
#include "startup/baremetal/init.h"

#include <arm_acle.h> // For __arm_wsr

extern "C" {
int main(int argc, char **argv);
void _start();

// Semihosting library initialisation if applicable. Required for printf, etc.
[[gnu::weak]] void _platform_init() {}

// These symbols are provided by the linker. The exact names are not defined by
// a standard.
extern uintptr_t __stack;
extern uintptr_t __data_source[];
extern uintptr_t __data_start[];
extern uintptr_t __data_size[];
extern uintptr_t __bss_start[];
extern uintptr_t __bss_size[];
} // extern "C"

namespace {
#if __ARM_ARCH_PROFILE == 'M'
// Based on
// https://developer.arm.com/documentation/107565/0101/Use-case-examples/Generic-Information/What-is-inside-a-program-image-/Vector-table
void NMI_Handler() {}
void HardFault_Handler() { LIBC_NAMESPACE::exit(1); }
void MemManage_Handler() { LIBC_NAMESPACE::exit(1); }
void BusFault_Handler() { LIBC_NAMESPACE::exit(1); }
void UsageFault_Handler() { LIBC_NAMESPACE::exit(1); }
void SVC_Handler() {}
void DebugMon_Handler() {}
void PendSV_Handler() {}
void SysTick_Handler() {}

// Architecturally the bottom 7 bits of VTOR are zero, meaning the vector table
// has to be 128-byte aligned, however an implementation can require more bits
// to be zero and Cortex-M23 can require up to 10, so 1024-byte align the vector
// table.
using HandlerType = void (*)(void);
[[gnu::section(".vectors"), gnu::aligned(1024), gnu::used]]
const HandlerType vector_table[] = {
    reinterpret_cast<HandlerType>(&__stack), // SP
    _start,                                  // Reset
    NMI_Handler,                             // NMI Handler
    HardFault_Handler,                       // Hard Fault Handler
    MemManage_Handler,                       // MPU Fault Handler
    BusFault_Handler,                        // Bus Fault Handler
    UsageFault_Handler,                      // Usage Fault Handler
    0,                                       // Reserved
    0,                                       // Reserved
    0,                                       // Reserved
    0,                                       // Reserved
    SVC_Handler,                             // SVC Handler
    DebugMon_Handler,                        // Debug Monitor Handler
    0,                                       // Reserved
    PendSV_Handler,                          // PendSV Handler
    SysTick_Handler,                         // SysTick Handler
                                             // Unused
};
#else
// Based on
// https://developer.arm.com/documentation/den0013/0400/Boot-Code/Booting-a-bare-metal-system
void Reset_Handler() { LIBC_NAMESPACE::exit(1); }
void Undefined_Handler() { LIBC_NAMESPACE::exit(1); }
void SWI_Handler() { LIBC_NAMESPACE::exit(1); }
void PrefetchAbort_Handler() { LIBC_NAMESPACE::exit(1); }
void DataAbort_Handler() { LIBC_NAMESPACE::exit(1); }
void IRQ_Handler() { LIBC_NAMESPACE::exit(1); }
void FIQ_Handler() { LIBC_NAMESPACE::exit(1); }

// The AArch32 exception vector table has 8 entries, each of which is 4
// bytes long, and contains code. The whole table must be 32-byte aligned.
// The table may also be relocated, so we make it position-independent by
// having a table of handler addresses and loading the address to pc.
[[gnu::section(".vectors"), gnu::aligned(32), gnu::used, gnu::naked,
  gnu::target("arm")]]
void vector_table() {
  asm("LDR pc, [pc, #24]");
  asm("LDR pc, [pc, #24]");
  asm("LDR pc, [pc, #24]");
  asm("LDR pc, [pc, #24]");
  asm("LDR pc, [pc, #24]");
  asm("LDR pc, [pc, #24]");
  asm("LDR pc, [pc, #24]");
  asm("LDR pc, [pc, #24]");
  asm(".word %c0" : : "X"(Reset_Handler));
  asm(".word %c0" : : "X"(Undefined_Handler));
  asm(".word %c0" : : "X"(SWI_Handler));
  asm(".word %c0" : : "X"(PrefetchAbort_Handler));
  asm(".word %c0" : : "X"(DataAbort_Handler));
  asm(".word %c0" : : "X"(0));
  asm(".word %c0" : : "X"(IRQ_Handler));
  asm(".word %c0" : : "X"(FIQ_Handler));
}
#endif
} // namespace

namespace LIBC_NAMESPACE_DECL {
[[noreturn]] void do_start() {
  // FIXME: set up the QEMU test environment

#if __ARM_ARCH_PROFILE == 'A' || __ARM_ARCH_PROFILE == 'R'
  // Set up registers to be used in exception handling
  // Copy the current sp value to each of the banked copies of sp.
  __arm_wsr("CPSR_c", 0x11); // FIQ
  asm volatile("mov sp, %0" : : "r"(__builtin_frame_address(0)));
  __arm_wsr("CPSR_c", 0x12); // IRQ
  asm volatile("mov sp, %0" : : "r"(__builtin_frame_address(0)));
  __arm_wsr("CPSR_c", 0x17); // ABT
  asm volatile("mov sp, %0" : : "r"(__builtin_frame_address(0)));
  __arm_wsr("CPSR_c", 0x1B); // UND
  asm volatile("mov sp, %0" : : "r"(__builtin_frame_address(0)));
  __arm_wsr("CPSR_c", 0x1F); // SYS
  asm volatile("mov sp, %0" : : "r"(__builtin_frame_address(0)));
  __arm_wsr("CPSR_c", 0x13); // SVC
#endif

#ifdef __ARM_FP
// Enable FPU
#if __ARM_ARCH_PROFILE == 'M'
  // Based on
  // https://developer.arm.com/documentation/dui0646/c/Cortex-M7-Peripherals/Floating-Point-Unit/Enabling-the-FPU
  // Set CPACR cp10 and cp11
  auto cpacr = (volatile uint32_t *const)0xE000ED88;
  *cpacr |= (0xF << 20);
  __dsb(0xF);
  __isb(0xF);
#elif __ARM_ARCH_PROFILE == 'A' || __ARM_ARCH_PROFILE == 'R'
  // Based on
  // https://developer.arm.com/documentation/dui0472/m/Compiler-Coding-Practices/Enabling-NEON-and-FPU-for-bare-metal
  // Set CPACR cp10 and cp11
  uint32_t cpacr = __arm_rsr("p15:0:c1:c0:2");
  cpacr |= (0xF << 20);
  __arm_wsr("p15:0:c1:c0:2", cpacr);
  __isb(0xF);
  // Set FPEXC.EN
  uint32_t fpexc;
  __asm__ __volatile__("vmrs %0, FPEXC" : "=r"(fpexc) : :);
  fpexc |= (1 << 30);
  __asm__ __volatile__("vmsr FPEXC, %0" : : "r"(fpexc) :);
#endif
#endif

  // Perform the equivalent of scatterloading
  LIBC_NAMESPACE::memcpy(__data_start, __data_source,
                         reinterpret_cast<uintptr_t>(__data_size));
  LIBC_NAMESPACE::memset(__bss_start, '\0',
                         reinterpret_cast<uintptr_t>(__bss_size));
  __libc_init_array();

  _platform_init();
  LIBC_NAMESPACE::atexit(&__libc_fini_array);
  LIBC_NAMESPACE::exit(main(0, 0));
}
} // namespace LIBC_NAMESPACE_DECL

extern "C" {
#ifdef __ARM_ARCH_ISA_ARM
// If ARM state is supported, it must be used (instead of Thumb)
[[gnu::naked, gnu::target("arm")]]
#endif
void _start() {
  asm volatile("mov sp, %0" : : "r"(&__stack));
  asm volatile("bl %0" : : "X"(LIBC_NAMESPACE::do_start));
}
} // extern "C"