diff --git a/libc/startup/baremetal/arm/start.cpp b/libc/startup/baremetal/arm/start.cpp index 493445e2922b..c089a14f5f78 100644 --- a/libc/startup/baremetal/arm/start.cpp +++ b/libc/startup/baremetal/arm/start.cpp @@ -15,6 +15,8 @@ #include "startup/baremetal/fini.h" #include "startup/baremetal/init.h" +#include // For __arm_wsr + extern "C" { int main(int argc, char **argv); void _start(); @@ -33,6 +35,7 @@ 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() {} @@ -55,8 +58,8 @@ const HandlerType vector_table[] = { reinterpret_cast(&__stack), // SP _start, // Reset NMI_Handler, // NMI Handler - HardFault_Handler, // Hard Fault Handlerß - MemManage_Handler, // MPU Fault Han`dler + HardFault_Handler, // Hard Fault Handler + MemManage_Handler, // MPU Fault Handler BusFault_Handler, // Bus Fault Handler UsageFault_Handler, // Usage Fault Handler 0, // Reserved @@ -70,12 +73,64 @@ const HandlerType vector_table[] = { 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 + // Perform the equivalent of scatterloading LIBC_NAMESPACE::memcpy(__data_start, __data_source, reinterpret_cast(__data_size)); @@ -89,7 +144,13 @@ namespace LIBC_NAMESPACE_DECL { } } // namespace LIBC_NAMESPACE_DECL -extern "C" void _start() { +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"