diff --git a/CMakeLists.txt b/CMakeLists.txt index cefbcb35..6d1bebc0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,7 +47,7 @@ # git -C repos clone https://github.com/llvm/llvm-project.git # git -C repos/llvm-project am -k $PWD/patches/llvm-project/*.patch # git -C repos clone https://github.com/picolibc/picolibc.git -# git -C repos/picolibc apply $PWD/patches/picolibc.patch +# git -C repos/picolibc apply $PWD/patches/picolibc/*.patch # mkdir build # cd build # cmake .. -GNinja -DFETCHCONTENT_SOURCE_DIR_LLVMPROJECT=../repos/llvm-project -DFETCHCONTENT_SOURCE_DIR_PICOLIBC=../repos/picolibc @@ -289,6 +289,11 @@ set(LLVM_PATCH_COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/p if(APPLY_LLVM_PERFORMANCE_PATCHES) set(LLVM_PATCH_COMMAND ${LLVM_PATCH_COMMAND} && ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/patch_llvm.py ${CMAKE_CURRENT_SOURCE_DIR}/patches/llvm-project-perf) endif() +set( + picolibc_patches + ${CMAKE_CURRENT_SOURCE_DIR}/patches/picolibc/0001-Enable-libcxx-builds.patch + ${CMAKE_CURRENT_SOURCE_DIR}/patches/picolibc/0002-Add-bootcode-for-AArch64-FVPs.patch +) FetchContent_Declare(llvmproject GIT_REPOSITORY https://github.com/llvm/llvm-project.git @@ -308,7 +313,7 @@ FetchContent_Declare(picolibc GIT_TAG "${picolibc_TAG}" GIT_SHALLOW "${picolibc_SHALLOW}" GIT_PROGRESS TRUE - PATCH_COMMAND git reset --quiet --hard && git clean --quiet --force -dx && git apply ${CMAKE_CURRENT_SOURCE_DIR}/patches/picolibc.patch + PATCH_COMMAND git reset --quiet --hard && git clean --quiet --force -dx && git apply ${picolibc_patches} # We only want to download the content, not configure it at this # stage. picolibc will be built in many configurations using # ExternalProject_Add using the sources that are checked out here. diff --git a/docs/building-from-source.md b/docs/building-from-source.md index 823d2ab7..80c3366c 100644 --- a/docs/building-from-source.md +++ b/docs/building-from-source.md @@ -89,7 +89,7 @@ mkdir repos git -C repos clone https://github.com/llvm/llvm-project.git git -C repos/llvm-project am -k "$PWD"/patches/llvm-project/*.patch git -C repos clone https://github.com/picolibc/picolibc.git -git -C repos/picolibc apply "$PWD"/patches/picolibc.patch +git -C repos/picolibc apply "$PWD"/patches/picolibc/*.patch mkdir build cd build cmake .. -GNinja -DFETCHCONTENT_SOURCE_DIR_LLVMPROJECT=../repos/llvm-project -DFETCHCONTENT_SOURCE_DIR_PICOLIBC=../repos/picolibc diff --git a/patches/picolibc.patch b/patches/picolibc/0001-Enable-libcxx-builds.patch similarity index 74% rename from patches/picolibc.patch rename to patches/picolibc/0001-Enable-libcxx-builds.patch index a77d8795..4ff07a16 100644 --- a/patches/picolibc.patch +++ b/patches/picolibc/0001-Enable-libcxx-builds.patch @@ -1,8 +1,20 @@ +From 42f07bef7775a1387f9707e959f8b6782f9d6808 Mon Sep 17 00:00:00 2001 +From: Simi Pallipurath +Date: Thu, 14 Nov 2024 10:07:08 +0000 +Subject: [PATCH 1/2] [PATCH 1/2] Enable libcxx builds + +Modifications to build config and linker script required to enable +libc++ builds. +--- + meson.build | 12 ++++++++++++ + picolibc.ld.in | 3 +++ + 2 files changed, 15 insertions(+) + diff --git a/meson.build b/meson.build -index f90f5b818..2f8d63733 100644 +index 012d664bd..4161d6574 100644 --- a/meson.build +++ b/meson.build -@@ -1310,6 +1310,18 @@ if get_option('newlib-retargetable-locking') != get_option('newlib-multithread') +@@ -1318,6 +1318,18 @@ if get_option('newlib-retargetable-locking') != get_option('newlib-multithread') error('newlib-retargetable-locking and newlib-multithread must be set to the same value') endif diff --git a/patches/picolibc/0002-Add-bootcode-for-AArch64-FVPs.patch b/patches/picolibc/0002-Add-bootcode-for-AArch64-FVPs.patch new file mode 100644 index 00000000..cde586a3 --- /dev/null +++ b/patches/picolibc/0002-Add-bootcode-for-AArch64-FVPs.patch @@ -0,0 +1,870 @@ +From f2ca20cebc85850a50b80424bb0f81c927edd04b Mon Sep 17 00:00:00 2001 +From: Simi Pallipurath +Date: Thu, 14 Nov 2024 10:12:33 +0000 +Subject: [PATCH 2/2] [PATCH 2/2] Add bootcode for AArch64 FVPs + +The AArch64 FVP (Fixed Virtual Platform) models differ from QEMU in a +few ways which affect the crt0 code: + +* The memory map is different, so needs different page tables. +* They boot up at EL3, instead of EL1, so we need to set the EL3 versions of the system registers. +* Add option to build crt0 bootcode for different machines +* Build new FVP variants of crt0, instead of replacing QEMU ones +* Split assembly parts of AArch64 crt0 into a separate file +* Make error checking target-specific +--- + meson.build | 1 + + meson_options.txt | 3 + + picocrt/machine/aarch64/crt0.S | 200 +++++++++++++++++++ + picocrt/machine/aarch64/crt0.c | 198 +++---------------- + picocrt/machine/aarch64/meson.build | 9 +- + picocrt/meson.build | 296 ++++++++++++++++------------ + 6 files changed, 404 insertions(+), 303 deletions(-) + create mode 100644 picocrt/machine/aarch64/crt0.S + +diff --git a/meson.build b/meson.build +index 4161d6574..9d3f5c672 100644 +--- a/meson.build ++++ b/meson.build +@@ -151,6 +151,7 @@ multilib_exclude = get_option('multilib-exclude') + enable_picolib = get_option('picolib') + enable_picocrt = get_option('picocrt') + enable_picocrt_lib = get_option('picocrt-lib') ++test_machine = get_option('test-machine') + enable_semihost = get_option('semihost') + enable_tests = get_option('tests') + if get_option('tests-cdefs') == 'auto' +diff --git a/meson_options.txt b/meson_options.txt +index e0eacb443..766129ebd 100644 +--- a/meson_options.txt ++++ b/meson_options.txt +@@ -132,6 +132,9 @@ option('test-stdin', type: 'boolean', value: false, + description: 'Enable tests that use stdin. This only works on a few targets') + option('fortify-source', type: 'combo', choices: ['none', '1', '2', '3'], value: '3', + description: 'Set _FORTIFY_SOURCE= when building tests') ++option('test-machine', type: 'string', value: 'qemu', ++ description: 'Machine-specific startup code to use when running tests') ++ + + option('tinystdio', type: 'boolean', value: true, + description: 'Use tiny stdio from avr libc') +diff --git a/picocrt/machine/aarch64/crt0.S b/picocrt/machine/aarch64/crt0.S +new file mode 100644 +index 000000000..4cb19854a +--- /dev/null ++++ b/picocrt/machine/aarch64/crt0.S +@@ -0,0 +1,200 @@ ++/************ Page table ************/ ++/* ++ * The smallest VA we can construct is 8GB, which needs 8 block page table ++ * entries, each covering 1GiB. ++ */ ++#define MMU_BLOCK_COUNT 8 ++ ++#define MMU_DESCRIPTOR_VALID (1 << 0) ++#define MMU_DESCRIPTOR_BLOCK (0 << 1) ++#define MMU_DESCRIPTOR_TABLE (1 << 1) ++ ++#define MMU_BLOCK_XN (1LL << 54) ++#define MMU_BLOCK_PXN (1LL << 53) ++#define MMU_BLOCK_CONTIG (1LL << 52) ++#define MMU_BLOCK_DBM (1LL << 51) ++#define MMU_BLOCK_GP (1LL << 50) ++ ++#define MMU_BLOCK_NT (1 << 16) ++#define MMU_BLOCK_OA_BIT 12 ++#define MMU_BLOCK_NG (1 << 11) ++#define MMU_BLOCK_AF (1 << 10) ++#define MMU_BLOCK_SH_BIT 8 ++#define MMU_BLOCK_SH_NS (0 << MMU_BLOCK_SH_BIT) ++#define MMU_BLOCK_SH_OS (2 << MMU_BLOCK_SH_BIT) ++#define MMU_BLOCK_SH_IS (3 << MMU_BLOCK_SH_BIT) ++#define MMU_BLOCK_AP_BIT 6 ++#define MMU_BLOCK_NS (1 << 5) ++#define MMU_BLOCK_ATTR_BIT 2 ++ ++#define MMU_NORMAL_FLAGS (MMU_DESCRIPTOR_VALID | \ ++ MMU_DESCRIPTOR_BLOCK | \ ++ MMU_BLOCK_AF | \ ++ MMU_BLOCK_SH_IS | \ ++ (0 << MMU_BLOCK_ATTR_BIT)) ++ ++#define MMU_DEVICE_FLAGS (MMU_DESCRIPTOR_VALID | \ ++ MMU_DESCRIPTOR_BLOCK | \ ++ MMU_BLOCK_AF | \ ++ (1 << MMU_BLOCK_ATTR_BIT)) ++ ++#define MMU_INVALID_FLAGS 0 ++ ++ .macro start_page_table ++ .section .rodata ++ .global __identity_page_table ++ .balign 65536 ++__identity_page_table: ++ .set block_num, 0 ++ .endm ++ ++ .macro page_table_entries count, flags ++ .rept \count ++ .8byte (block_num << 30) | \flags ++ .set block_num, block_num + 1 ++ .endr ++ .endm ++ ++ .macro end_page_table ++ .size __identity_page_table, MMU_BLOCK_COUNT * 8 ++ .if block_num != MMU_BLOCK_COUNT ++ .error "Wrong number of page table entries" ++ .endif ++ .endm ++ ++#if defined(MACHINE_qemu) ++ start_page_table ++ // [0x0000_0000,0x8000_0000): 2GiB normal memory ++ page_table_entries 2, MMU_NORMAL_FLAGS ++ // [0x8000_0000,0x1_0000_0000): 2GiB device memory ++ page_table_entries 2, MMU_DEVICE_FLAGS ++ // [0x1_0000_0000,0x2_0000_0000): 4GiB un-mapped ++ page_table_entries 4, MMU_INVALID_FLAGS ++ end_page_table ++#elif defined(MACHINE_fvp) ++ start_page_table ++ // [0x0000_0000,0x8000_0000): 2GiB unmapped. This actually contains a lot ++ // of different memory regions and devices, but we don't need any of them ++ // for testing. ++ page_table_entries 2, MMU_INVALID_FLAGS ++ // [0x8000_0000,0x1_0000_0000): 2GiB normal memory ++ page_table_entries 2, MMU_NORMAL_FLAGS ++ // [0x1_0000_0000,0x2_0000_0000): 4GiB un-mapped ++ page_table_entries 4, MMU_INVALID_FLAGS ++ end_page_table ++#else ++#error "Unknown machine type" ++#endif ++ ++ ++/************ Entry point ************/ ++ ++ // Defined in crt0.c ++ .global _cstart ++ .type cstart, %function ++ ++ // _start: Main entry point function, sets up the hardware to the point where ++ // we can execute C code. ++ .section .text.init.enter, "ax", %progbits ++ .global _start ++ .type _start, %function ++_start: ++ /* Use EL-banked stack pointer */ ++ msr SPSel, #1 ++ ++ /* Initialize stack */ ++ adrp x1, __stack ++ add x1, x1, :lo12:__stack ++ mov sp, x1 ++ ++ /* Enable FPU */ ++#if __ARM_FP ++#if defined(MACHINE_qemu) ++ mov x1, #(0x3 << 20) ++ msr CPACR_EL1, x1 ++#elif defined(MACHINE_fvp) ++ mrs x0, CPTR_EL3 ++ /* Clear CPTR_ELx.TFP, to enable FP/SIMD instructions at EL0 and EL1. */ ++ and x0, x0, #~(1<<10) ++ /* Set CPTR_ELx.EZ and .ESM, to enable SVE and SME instructions at EL3. These ++ * bits are ignored for cores which don't have the relevant feature. */ ++ ORR x0, x0, #1<<8 ++ ORR x0, x0, #1<<12 ++ msr CPTR_EL3, x0 ++#else ++#error "Unknown machine type" ++#endif ++#endif // __ARM_FP ++ ++ /* Jump into C code */ ++ bl _cstart ++ .size _start, .-_start ++ ++ ++ ++/************ Exception handlers ************/ ++#ifdef CRT0_SEMIHOST ++ ++ .macro vector_common ++ sub sp, sp, #256 ++ str x0, [sp, #0] ++ str x1, [sp, #8] ++ str x2, [sp, #16] ++ str x3, [sp, #24] ++ str x4, [sp, #32] ++ str x5, [sp, #40] ++ str x6, [sp, #48] ++ str x7, [sp, #56] ++ str x8, [sp, #64] ++ str x9, [sp, #72] ++ str x10, [sp, #80] ++ str x11, [sp, #88] ++ str x12, [sp, #96] ++ str x13, [sp, #104] ++ str x14, [sp, #112] ++ str x15, [sp, #120] ++ str x16, [sp, #128] ++ str x17, [sp, #136] ++ str x18, [sp, #144] ++ str x19, [sp, #152] ++ str x20, [sp, #160] ++ str x21, [sp, #168] ++ str x22, [sp, #176] ++ str x23, [sp, #184] ++ str x24, [sp, #192] ++ str x25, [sp, #200] ++ str x26, [sp, #208] ++ str x27, [sp, #216] ++ str x28, [sp, #224] ++ str x29, [sp, #232] ++ str x30, [sp, #240] ++#if defined(MACHINE_qemu) ++ mrs x0, ELR_EL1 ++#elif defined(MACHINE_fvp) ++ mrs x0, ELR_EL3 ++#else ++#error "Unknown machine type" ++#endif ++ str x0, [sp, #248] ++ mov x0, sp ++ .endm ++ ++ .global aarch64_fault ++ .type aarch64_fault, %function ++ ++ .macro exception_handler name, number ++ .section .init, "ax", %progbits ++ .global aarch64_\name\()_vector ++ .type aarch64_\name\()_vector, %function ++aarch64_\name\()_vector: ++ vector_common ++ mov x1, #\number ++ b aarch64_fault ++ .endm ++ ++ exception_handler sync, 0 ++ exception_handler irq, 1 ++ exception_handler fiq, 2 ++ exception_handler serror, 3 ++ ++#endif // CRT0_SEMIHOST +diff --git a/picocrt/machine/aarch64/crt0.c b/picocrt/machine/aarch64/crt0.c +index affb41fa9..dfe838111 100644 +--- a/picocrt/machine/aarch64/crt0.c ++++ b/picocrt/machine/aarch64/crt0.c +@@ -60,75 +60,11 @@ _set_tls(void *tls) + + #include "../../crt0.h" + +-/* +- * We need 4 1GB mappings to cover the usual Normal memory space, +- * which runs from 0x00000000 to 0x7fffffff along with the usual +- * Device space which runs from 0x80000000 to 0xffffffff. However, +- * it looks like the smallest VA we can construct is 8GB, so we'll +- * pad the space with invalid PTEs +- */ +-#define MMU_NORMAL_COUNT 2 +-#define MMU_DEVICE_COUNT 2 +-#define MMU_INVALID_COUNT 4 +-extern uint64_t __identity_page_table[MMU_NORMAL_COUNT + MMU_DEVICE_COUNT + MMU_INVALID_COUNT]; +- +-#define MMU_DESCRIPTOR_VALID (1 << 0) +-#define MMU_DESCRIPTOR_BLOCK (0 << 1) +-#define MMU_DESCRIPTOR_TABLE (1 << 1) +- +-#define MMU_BLOCK_XN (1LL << 54) +-#define MMU_BLOCK_PXN (1LL << 53) +-#define MMU_BLOCK_CONTIG (1LL << 52) +-#define MMU_BLOCK_DBM (1LL << 51) +-#define MMU_BLOCK_GP (1LL << 50) +- +-#define MMU_BLOCK_NT (1 << 16) +-#define MMU_BLOCK_OA_BIT 12 +-#define MMU_BLOCK_NG (1 << 11) +-#define MMU_BLOCK_AF (1 << 10) +-#define MMU_BLOCK_SH_BIT 8 +-#define MMU_BLOCK_SH_NS (0 << MMU_BLOCK_SH_BIT) +-#define MMU_BLOCK_SH_OS (2 << MMU_BLOCK_SH_BIT) +-#define MMU_BLOCK_SH_IS (3 << MMU_BLOCK_SH_BIT) +-#define MMU_BLOCK_AP_BIT 6 +-#define MMU_BLOCK_NS (1 << 5) +-#define MMU_BLOCK_ATTR_BIT 2 +- +-#define MMU_NORMAL_FLAGS (MMU_DESCRIPTOR_VALID | \ +- MMU_DESCRIPTOR_BLOCK | \ +- MMU_BLOCK_AF | \ +- MMU_BLOCK_SH_IS | \ +- (0 << MMU_BLOCK_ATTR_BIT)) +- +-#define MMU_DEVICE_FLAGS (MMU_DESCRIPTOR_VALID | \ +- MMU_DESCRIPTOR_BLOCK | \ +- MMU_BLOCK_AF | \ +- (1 << MMU_BLOCK_ATTR_BIT)) +- +-#define MMU_INVALID_FLAGS 0 +- +-__asm__( +- ".section .rodata\n" +- ".global __identity_page_table\n" +- ".balign 65536\n" +- "__identity_page_table:\n" +- ".set _i, 0\n" +- ".rept " __XSTRING(MMU_NORMAL_COUNT) "\n" +- " .8byte (_i << 30) |" __XSTRING(MMU_NORMAL_FLAGS) "\n" +- " .set _i, _i + 1\n" +- ".endr\n" +- ".set _i, 0\n" +- ".rept " __XSTRING(MMU_DEVICE_COUNT) "\n" +- " .8byte (1 << 31) | (_i << 30) |" __XSTRING(MMU_DEVICE_FLAGS) "\n" +- " .set _i, _i + 1\n" +- ".endr\n" +- ".set _i, 0\n" +- ".rept " __XSTRING(MMU_INVALID_COUNT) "\n" +- " .8byte " __XSTRING(MMU_INVALID_FLAGS) "\n" +- " .set _i, _i + 1\n" +- ".endr\n" +- ".size __identity_page_table, " __XSTRING((MMU_NORMAL_COUNT + MMU_DEVICE_COUNT + MMU_INVALID_COUNT) * 8) "\n" +-); ++/* Defined in crt0.S */ ++#define MMU_BLOCK_COUNT 8 ++extern uint64_t __identity_page_table[MMU_BLOCK_COUNT]; ++extern void _start(void); ++extern const void *__vector_table[]; + + #define SCTLR_MMU (1 << 0) + #define SCTLR_A (1 << 1) +@@ -159,12 +95,19 @@ __asm__( + #define TCR_IPS_BIT 32 + #define TCR_IPS_4GB (0LL << TCR_IPS_BIT) + +-extern const void *__vector_table[]; ++/* QEMU boots into EL1, and FVPs boot into EL3, so we need to use the correct ++ * system registers. */ ++#if defined(MACHINE_qemu) ++#define BOOT_EL "EL1" ++#elif defined(MACHINE_fvp) ++#define BOOT_EL "EL3" ++#else ++#error "Unknown machine type" ++#endif + +-static void __attribute((used)) +-_cstart(void) ++void _cstart(void) + { +- uint64_t sctlr_el1; ++ uint64_t sctlr; + + /* Invalidate the cache */ + __asm__("ic iallu"); +@@ -174,7 +117,7 @@ _cstart(void) + * Set up the TCR register to provide a 33bit VA space using + * 4kB pages over 4GB of PA + */ +- __asm__("msr tcr_el1, %x0" :: ++ __asm__("msr tcr_"BOOT_EL", %x0" :: + "r" ((0x1f << TCR_T0SZ_BIT) | + TCR_IRGN0_WB_WA | + TCR_ORGN0_WB_WA | +@@ -184,7 +127,7 @@ _cstart(void) + TCR_IPS_4GB)); + + /* Load the page table base */ +- __asm__("msr ttbr0_el1, %x0" :: "r" (__identity_page_table)); ++ __asm__("msr ttbr0_"BOOT_EL", %x0" :: "r" (__identity_page_table)); + + /* + * Set the memory attributions in the MAIR register: +@@ -192,42 +135,24 @@ _cstart(void) + * Region 0 is Normal memory + * Region 1 is Device memory + */ +- __asm__("msr mair_el1, %x0" :: ++ __asm__("msr mair_"BOOT_EL", %x0" :: + "r" ((0xffLL << 0) | (0x00LL << 8))); + + /* + * Enable caches, and the MMU, disable alignment requirements + * and write-implies-XN + */ +- __asm__("mrs %x0, sctlr_el1" : "=r" (sctlr_el1)); +- sctlr_el1 |= SCTLR_ICACHE | SCTLR_C | SCTLR_MMU; +- sctlr_el1 &= ~(SCTLR_A | SCTLR_WXN); +- __asm__("msr sctlr_el1, %x0" :: "r" (sctlr_el1)); ++ __asm__("mrs %x0, sctlr_"BOOT_EL"" : "=r" (sctlr)); ++ sctlr |= SCTLR_ICACHE | SCTLR_C | SCTLR_MMU; ++ sctlr &= ~(SCTLR_A | SCTLR_WXN); ++ __asm__("msr sctlr_"BOOT_EL", %x0" :: "r" (sctlr)); + __asm__("isb\n"); + + /* Set the vector base address register */ +- __asm__("msr vbar_el1, %x0" :: "r" (__vector_table)); ++ __asm__("msr vbar_"BOOT_EL", %x0" :: "r" (__vector_table)); + __start(); + } + +-void __section(".text.init.enter") +-_start(void) +-{ +- /* Switch to EL1 */ +- __asm__("msr SPSel, #1"); +- +- /* Initialize stack */ +- __asm__("adrp x1, __stack"); +- __asm__("add x1, x1, :lo12:__stack"); +- __asm__("mov sp, x1"); +-#if __ARM_FP +- /* Enable FPU */ +- __asm__("mov x1, #(0x3 << 20)"); +- __asm__("msr cpacr_el1,x1"); +-#endif +- /* Jump into C code */ +- __asm__("bl _cstart"); +-} + + #ifdef CRT0_SEMIHOST + +@@ -269,13 +194,9 @@ static const char *const reasons[] = { + "serror\n" + }; + +-#define REASON_SYNC 0 +-#define REASON_IRQ 1 +-#define REASON_FIQ 2 +-#define REASON_SERROR 3 +- +-static void __attribute__((used)) +-aarch64_fault(struct fault *f, int reason) ++/* Called from assembly wrappers in crt0.S, which fills *f with the register ++ * values at the point the fault happened. */ ++void aarch64_fault(struct fault *f, int reason) + { + int r; + fputs("AARCH64 fault: ", stdout); +@@ -292,69 +213,4 @@ aarch64_fault(struct fault *f, int reason) + _exit(1); + } + +-#define VECTOR_COMMON \ +- __asm__("sub sp, sp, #256"); \ +- __asm__("str x0, [sp, #0]"); \ +- __asm__("str x1, [sp, #8]"); \ +- __asm__("str x2, [sp, #16]"); \ +- __asm__("str x3, [sp, #24]"); \ +- __asm__("str x4, [sp, #32]"); \ +- __asm__("str x5, [sp, #40]"); \ +- __asm__("str x6, [sp, #48]"); \ +- __asm__("str x7, [sp, #56]"); \ +- __asm__("str x8, [sp, #64]"); \ +- __asm__("str x9, [sp, #72]"); \ +- __asm__("str x10, [sp, #80]"); \ +- __asm__("str x11, [sp, #88]"); \ +- __asm__("str x12, [sp, #96]"); \ +- __asm__("str x13, [sp, #104]"); \ +- __asm__("str x14, [sp, #112]"); \ +- __asm__("str x15, [sp, #120]"); \ +- __asm__("str x16, [sp, #128]"); \ +- __asm__("str x17, [sp, #136]"); \ +- __asm__("str x18, [sp, #144]"); \ +- __asm__("str x19, [sp, #152]"); \ +- __asm__("str x20, [sp, #160]"); \ +- __asm__("str x21, [sp, #168]"); \ +- __asm__("str x22, [sp, #176]"); \ +- __asm__("str x23, [sp, #184]"); \ +- __asm__("str x24, [sp, #192]"); \ +- __asm__("str x25, [sp, #200]"); \ +- __asm__("str x26, [sp, #208]"); \ +- __asm__("str x27, [sp, #216]"); \ +- __asm__("str x28, [sp, #224]"); \ +- __asm__("str x29, [sp, #232]"); \ +- __asm__("str x30, [sp, #240]"); \ +- __asm__("mrs x0, ELR_EL1\n"); \ +- __asm__("str x0, [sp, #248]"); \ +- __asm__("mrs x0, ESR_EL1\n"); \ +- __asm__("str x0, [sp, #256]"); \ +- __asm__("mrs x0, FAR_EL1\n"); \ +- __asm__("str x0, [sp, #264]"); \ +- __asm__("mov x0, sp") +- +-void __section(".init") +-aarch64_sync_vector(void) +-{ +- VECTOR_COMMON; +- __asm__("mov x1, #" REASON(REASON_SYNC)); +- __asm__("b aarch64_fault"); +-} +- +-void __section(".init") +-aarch64_irq_vector(void) +-{ +- VECTOR_COMMON; +- __asm__("mov x1, #" REASON(REASON_IRQ)); +- __asm__("b aarch64_fault"); +-} +- +-void __section(".init") +-aarch64_fiq_vector(void) +-{ +- VECTOR_COMMON; +- __asm__("mov x1, #" REASON(REASON_FIQ)); +- __asm__("b aarch64_fault"); +-} +- + #endif /* CRT0_SEMIHOST */ +diff --git a/picocrt/machine/aarch64/meson.build b/picocrt/machine/aarch64/meson.build +index 808d691a5..923d32c3b 100644 +--- a/picocrt/machine/aarch64/meson.build ++++ b/picocrt/machine/aarch64/meson.build +@@ -32,4 +32,11 @@ + # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + # OF THE POSSIBILITY OF SUCH DAMAGE. + # +-src_picocrt += files('crt0.c') ++src_picocrt += files('crt0.c', 'crt0.S') ++ ++picocrt_machines += [ ++ { ++ 'name': 'fvp', ++ 'suffix': '-fvp', ++ }, ++] +diff --git a/picocrt/meson.build b/picocrt/meson.build +index 76965990f..be8f875be 100644 +--- a/picocrt/meson.build ++++ b/picocrt/meson.build +@@ -36,6 +36,17 @@ + src_picocrt = [] + src_picocrt_none = files('crt0-none.c') + ++# Machine-specific crt0 variants to build. ++picocrt_machines = [ ++ { ++ # Must match a valid value of the 'test-machine' option, and will cause the ++ # MACHINE_ preprocessor macro to be defined when compiling crt0. ++ 'name': 'qemu', ++ # Suffix used on file names, QEMU's is empty because it is the default. ++ 'suffix': '', ++ }, ++] ++ + machine_dir = 'machine' / host_cpu_family + picocrt_march_add='' + if fs.is_dir(machine_dir) +@@ -44,6 +55,16 @@ else + src_picocrt = files('shared/crt0.c') + endif + ++machine_found = false ++foreach machine : picocrt_machines ++ if machine['name'] == test_machine ++ machine_found = true ++ endif ++endforeach ++if not machine_found ++ error(test_machine + ': requested test machine not found') ++endif ++ + foreach target : targets + value = get_variable('target_' + target) + +@@ -60,150 +81,163 @@ foreach target : targets + value = [value[0], new_cflags] + endif + +- if target == '' +- crt_name = 'crt0.o' +- crt_hosted_name = 'crt0-hosted.o' +- crt_minimal_name = 'crt0-minimal.o' +- crt_semihost_name = 'crt0-semihost.o' +- crt_none_name = 'crt0-none.o' +- libcrt_name = 'crt0' +- libcrt_hosted_name = 'crt0-hosted' +- libcrt_minimal_name = 'crt0-minimal' +- libcrt_semihost_name = 'crt0-semihost' +- libcrt_none_name = 'crt0-none' +- else +- crt_name = join_paths(target, 'crt0.o') +- crt_hosted_name = join_paths(target, 'crt0-hosted.o') +- crt_minimal_name = join_paths(target, 'crt0-minimal.o') +- crt_semihost_name = join_paths(target, 'crt0-semihost.o') +- crt_none_name = join_paths(target, 'crt0-none.o') +- libcrt_name = join_paths(target, 'libcrt0') +- libcrt_hosted_name = join_paths(target, 'libcrt0-hosted') +- libcrt_minimal_name = join_paths(target, 'libcrt0-minimal') +- libcrt_semihost_name = join_paths(target, 'libcrt0-semihost') +- libcrt_none_name = join_paths(target, 'libcrt0-none') +- endif ++ foreach machine : picocrt_machines ++ suffix = machine['suffix'] ++ if target == '' ++ crt_name = 'crt0' + suffix + '.o' ++ crt_hosted_name = 'crt0-hosted' + suffix + '.o' ++ crt_minimal_name = 'crt0-minimal' + suffix + '.o' ++ crt_semihost_name = 'crt0-semihost' + suffix + '.o' ++ crt_none_name = 'crt0-none' + suffix + '.o' ++ libcrt_name = 'crt0' + suffix ++ libcrt_hosted_name = 'crt0-hosted' + suffix ++ libcrt_minimal_name = 'crt0-minimal' + suffix ++ libcrt_semihost_name = 'crt0-semihost' + suffix ++ libcrt_none_name = 'crt0-none' + suffix ++ else ++ crt_name = join_paths(target, 'crt0' + suffix + '.o') ++ crt_hosted_name = join_paths(target, 'crt0-hosted' + suffix + '.o') ++ crt_minimal_name = join_paths(target, 'crt0-minimal' + suffix + '.o') ++ crt_semihost_name = join_paths(target, 'crt0-semihost' + suffix + '.o') ++ crt_none_name = join_paths(target, 'crt0-none' + suffix + '.o') ++ libcrt_name = join_paths(target, 'libcrt0' + suffix) ++ libcrt_hosted_name = join_paths(target, 'libcrt0-hosted' + suffix) ++ libcrt_minimal_name = join_paths(target, 'libcrt0-minimal' + suffix) ++ libcrt_semihost_name = join_paths(target, 'libcrt0-semihost' + suffix) ++ libcrt_none_name = join_paths(target, 'libcrt0-none' + suffix) ++ endif + +- crt0_name = 'crt0' + target +- crt0_hosted_name = 'crt0_hosted' + target +- crt0_minimal_name = 'crt0_minimal' + target +- crt0_semihost_name = 'crt0_semihost' + target +- crt0_none_name = 'crt0_none' + target +- +- _c_args = value[1] + arg_fnobuiltin + ['-ffreestanding'] +- _link_args = value[1] + ['-r', '-ffreestanding'] +- +- # The normal variant does not call 'exit' after return from main (c lingo: freestanding execution environment) +- _crt = executable(crt_name, +- src_picocrt, +- include_directories : inc, +- install : true, +- install_dir : instdir, +- c_args : _c_args, +- link_args : _link_args) +- +- set_variable(crt0_name, +- _crt.extract_objects(src_picocrt) +- ) +- +- if enable_picocrt_lib +- static_library(libcrt_name, +- [], +- include_directories : inc, +- install : true, +- install_dir : instdir, +- c_args : _c_args, +- objects: get_variable(crt0_name), +- pic: false) +- endif ++ crt0_name = 'crt0' + target ++ crt0_hosted_name = 'crt0_hosted' + target ++ crt0_minimal_name = 'crt0_minimal' + target ++ crt0_semihost_name = 'crt0_semihost' + target ++ crt0_none_name = 'crt0_none' + target ++ ++ _c_args = value[1] + arg_fnobuiltin + ['-ffreestanding', '-DMACHINE_' + machine['name']] ++ _link_args = value[1] + ['-r', '-ffreestanding'] ++ ++ # The normal variant does not call 'exit' after return from main (c lingo: freestanding execution environment) ++ _crt = executable(crt_name, ++ src_picocrt, ++ include_directories : inc, ++ install : true, ++ install_dir : instdir, ++ c_args : _c_args, ++ link_args : _link_args) ++ ++ if machine['name'] == test_machine ++ set_variable(crt0_name, ++ _crt.extract_objects(src_picocrt) ++ ) ++ endif + +- # The 'hosted' variant calls 'exit' after return from main (c lingo: hosted execution environment) +- _crt = executable(crt_hosted_name, +- src_picocrt, +- include_directories : inc, +- install : true, +- install_dir : instdir, +- c_args : _c_args + ['-DCRT0_EXIT'], +- link_args : _link_args) +- +- set_variable(crt0_hosted_name, +- _crt.extract_objects(src_picocrt) +- ) +- +- if enable_picocrt_lib +- static_library(libcrt_hosted_name, +- [], +- include_directories : inc, +- install : true, +- install_dir : instdir, +- pic: false, +- objects: get_variable(crt0_hosted_name), +- c_args : value[1] + ['-DCRT0_EXIT']) +- endif ++ if enable_picocrt_lib ++ static_library(libcrt_name, ++ [], ++ include_directories : inc, ++ install : true, ++ install_dir : instdir, ++ c_args : _c_args, ++ objects: _crt.extract_objects(src_picocrt), ++ pic: false) ++ endif + +- # The 'minimal' variant doesn't call exit, nor does it invoke any constructors +- _crt = executable(crt_minimal_name, +- src_picocrt, +- include_directories : inc, +- install : true, +- install_dir : instdir, +- c_args : _c_args + ['-DCONSTRUCTORS=0'], +- link_args : _link_args) +- +- set_variable(crt0_minimal_name, +- _crt.extract_objects(src_picocrt) +- ) +- +- if enable_picocrt_lib +- static_library(libcrt_minimal_name, +- [], +- include_directories : inc, +- install : true, +- install_dir : instdir, +- pic: false, +- objects: get_variable(crt0_minimal_name), +- c_args : _c_args + ['-DCONSTRUCTORS=0']) +- endif ++ # The 'hosted' variant calls 'exit' after return from main (c lingo: hosted execution environment) ++ _crt = executable(crt_hosted_name, ++ src_picocrt, ++ include_directories : inc, ++ install : true, ++ install_dir : instdir, ++ c_args : _c_args + ['-DCRT0_EXIT'], ++ link_args : _link_args) ++ ++ if machine['name'] == test_machine ++ set_variable(crt0_hosted_name, ++ _crt.extract_objects(src_picocrt) ++ ) ++ endif + +- if has_arm_semihost +- # The 'semihost' variant calls sys_semihost_get_cmdline to build argv +- # and calls exit when main returns +- _crt = executable(crt_semihost_name, +- src_picocrt, +- include_directories : inc, +- install : true, +- install_dir : instdir, +- c_args : _c_args + ['-DCRT0_EXIT', '-DCRT0_SEMIHOST'], +- link_args : _link_args) +- +- set_variable(crt0_semihost_name, +- _crt.extract_objects(src_picocrt) +- ) ++ if enable_picocrt_lib ++ static_library(libcrt_hosted_name, ++ [], ++ include_directories : inc, ++ install : true, ++ install_dir : instdir, ++ pic: false, ++ objects: _crt.extract_objects(src_picocrt), ++ c_args : value[1] + ['-DCRT0_EXIT']) ++ endif ++ ++ # The 'minimal' variant doesn't call exit, nor does it invoke any constructors ++ _crt = executable(crt_minimal_name, ++ src_picocrt, ++ include_directories : inc, ++ install : true, ++ install_dir : instdir, ++ c_args : _c_args + ['-DCONSTRUCTORS=0'], ++ link_args : _link_args) ++ ++ if machine['name'] == test_machine ++ set_variable(crt0_minimal_name, ++ _crt.extract_objects(src_picocrt) ++ ) ++ endif + + if enable_picocrt_lib +- static_library(libcrt_semihost_name, ++ static_library(libcrt_minimal_name, + [], +- include_directories : inc, ++ include_directories : inc, + install : true, + install_dir : instdir, + pic: false, +- objects: get_variable(crt0_semihost_name), +- c_args : value[1] + ['-DCRT0_EXIT', '-DCRT0_SEMIHOST']) ++ objects: _crt.extract_objects(src_picocrt), ++ c_args : _c_args + ['-DCONSTRUCTORS=0']) + endif +- endif + +- # The 'none' variant is completely empty +- _crt = executable(crt_none_name, +- src_picocrt_none, +- include_directories : inc, +- install : true, +- install_dir : instdir, +- c_args : _c_args, +- link_args : _link_args) ++ if has_arm_semihost ++ # The 'semihost' variant calls sys_semihost_get_cmdline to build argv ++ # and calls exit when main returns ++ _crt = executable(crt_semihost_name, ++ src_picocrt, ++ include_directories : inc, ++ install : true, ++ install_dir : instdir, ++ c_args : _c_args + ['-DCRT0_EXIT', '-DCRT0_SEMIHOST'], ++ link_args : _link_args) ++ ++ if machine['name'] == test_machine ++ set_variable(crt0_semihost_name, ++ _crt.extract_objects(src_picocrt) ++ ) ++ endif + +- set_variable(crt0_none_name, +- _crt.extract_objects(src_picocrt_none) +- ) ++ if enable_picocrt_lib ++ static_library(libcrt_semihost_name, ++ [], ++ include_directories : inc, ++ install : true, ++ install_dir : instdir, ++ pic: false, ++ objects: _crt.extract_objects(src_picocrt), ++ c_args : value[1] + ['-DCRT0_EXIT', '-DCRT0_SEMIHOST']) ++ endif ++ endif ++ ++ # The 'none' variant is completely empty ++ _crt = executable(crt_none_name, ++ src_picocrt_none, ++ include_directories : inc, ++ install : true, ++ install_dir : instdir, ++ c_args : _c_args, ++ link_args : _link_args) ++ ++ if machine['name'] == test_machine ++ set_variable(crt0_none_name, ++ _crt.extract_objects(src_picocrt_none) ++ ) ++ endif + ++ endforeach + + endforeach