diff --git a/libc/cmake/modules/LLVMLibCTestRules.cmake b/libc/cmake/modules/LLVMLibCTestRules.cmake index 267c32e956945..66ef3d3c1cb78 100644 --- a/libc/cmake/modules/LLVMLibCTestRules.cmake +++ b/libc/cmake/modules/LLVMLibCTestRules.cmake @@ -836,7 +836,7 @@ function(add_libc_hermetic test_name) ${fq_deps_list}) # TODO: currently the dependency chain is broken such that getauxval cannot properly # propagate to hermetic tests. This is a temporary workaround. - if (LIBC_TARGET_ARCHITECTURE_IS_AARCH64) + if (LIBC_TARGET_ARCHITECTURE_IS_AARCH64 AND NOT(LIBC_TARGET_OS_IS_BAREMETAL)) target_link_libraries( ${fq_build_target_name} PRIVATE diff --git a/libc/startup/baremetal/aarch64/CMakeLists.txt b/libc/startup/baremetal/aarch64/CMakeLists.txt new file mode 100644 index 0000000000000..f75bd893c68bc --- /dev/null +++ b/libc/startup/baremetal/aarch64/CMakeLists.txt @@ -0,0 +1,16 @@ +add_startup_object( + crt1 + SRC + start.cpp + DEPENDS + libc.src.stdlib.atexit + libc.src.stdlib.exit + libc.src.string.memcpy + libc.src.string.memset + libc.startup.baremetal.init + libc.startup.baremetal.fini + COMPILE_OPTIONS + -ffreestanding # To avoid compiler warnings about calling the main function. + -fno-builtin + -Wno-global-constructors # To allow vector table initialization +) diff --git a/libc/startup/baremetal/aarch64/start.cpp b/libc/startup/baremetal/aarch64/start.cpp new file mode 100644 index 0000000000000..ff3ea933c5240 --- /dev/null +++ b/libc/startup/baremetal/aarch64/start.cpp @@ -0,0 +1,112 @@ +//===-- Implementation of crt for aarch64 ---------------------------------===// +// +// 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 + +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 { +// The Arm ARM for the A-profile architecture (D14.1.5) defines the exceptions. +// However, for simplicity, we don't bother logging, and just exit. +void GenericException_Handler() { LIBC_NAMESPACE::exit(1); } + +// The AArch64 exception vector table has 16 entries, each of which is 128 +// bytes long, and contains code. The whole table must be 2048-byte aligned. +// For our purposes, each entry just contains one branch instruction to the +// exception reporting function, since we never want to resume after an +// exception. +[[gnu::section(".vectors"), gnu::aligned(2048), gnu::used, gnu::naked]] +void vector_table() { +#define VECTOR_TABLE_ENTRY \ + asm(".balign 128"); \ + asm("B %0" : : "X"(GenericException_Handler)); + + VECTOR_TABLE_ENTRY; + VECTOR_TABLE_ENTRY; + VECTOR_TABLE_ENTRY; + VECTOR_TABLE_ENTRY; + VECTOR_TABLE_ENTRY; + VECTOR_TABLE_ENTRY; + VECTOR_TABLE_ENTRY; + VECTOR_TABLE_ENTRY; + VECTOR_TABLE_ENTRY; + VECTOR_TABLE_ENTRY; + VECTOR_TABLE_ENTRY; + VECTOR_TABLE_ENTRY; + VECTOR_TABLE_ENTRY; + VECTOR_TABLE_ENTRY; + VECTOR_TABLE_ENTRY; + VECTOR_TABLE_ENTRY; +} +} // namespace + +namespace LIBC_NAMESPACE_DECL { + +[[noreturn]] void do_start() { + // TODO: This startup code is not extensive, but rather the MVP for QEMU + // testing. + // TODO: Setup memory (MMU, page table, caches) + // TODO: Consider v8-R variants + + // Set up exception handling + __arm_wsr64("VBAR_EL1", reinterpret_cast(&vector_table)); + +#ifdef __ARM_FP + // Do not trap FP/SME/SVE instructions + static constexpr uint64_t CPACR_SHIFT_FPEN = 20; + static constexpr uint64_t CPACR_SHIFT_SMEN = 24; + uint64_t cpacr = __arm_rsr64("CPACR_EL1"); + cpacr |= (0x3 << CPACR_SHIFT_FPEN); + cpacr |= (0x3 << CPACR_SHIFT_SMEN); + __arm_wsr64("CPACR_EL1", cpacr); +#endif + + // Perform the equivalent of scatterloading + LIBC_NAMESPACE::memcpy(__data_start, __data_source, + reinterpret_cast(__data_size)); + LIBC_NAMESPACE::memset(__bss_start, '\0', + reinterpret_cast(__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" { +[[gnu::section(".text.init.enter"), gnu::naked]] +void _start() { + asm volatile("mov sp, %0" : : "r"(&__stack)); + asm volatile("bl %0" : : "X"(LIBC_NAMESPACE::do_start)); +} +} // extern "C"