xco_platform.c (4961B)
1 /* 2 * platform/arm64/xco_platform.c — AArch64 init + switch + xco_trampoline thunk. 3 * 4 * The switch primitive and the xco_trampoline thunk are written as 5 * file-scope global asm rather than __attribute__((naked)) functions: 6 * GCC silently ignores `naked` on AArch64, so naked-function inline 7 * asm only works under Clang. File-scope __asm__ is portable across 8 * both GCC and Clang and keeps everything in one .c file. 9 * 10 * Layout offsets used in the asm: 11 * 0 regs[0..9] x19-x28 12 * 80 regs[10..11] fp (x29), lr (x30) 13 * 96 regs[12] sp 14 * 104 fp_regs[0..7] d8-d15 15 * 16 * Symbol naming: asm labels are built with the compiler-provided 17 * __USER_LABEL_PREFIX__ (empty on ELF, "_" on Mach-O) so the labels 18 * emitted here match what the C compiler generates for references to 19 * xco_platform_switch / xco_platform_trampoline_thunk on either OS. 20 */ 21 22 #include "xco_platform_internal.h" 23 #include "xco_platform.h" 24 25 #include <assert.h> 26 #include <stddef.h> 27 #include <stdint.h> 28 #include <string.h> 29 30 /* The platform context type forward-declared in xco_platform.h. */ 31 struct xco_platform_ctx { 32 uintptr_t regs[13]; 33 uint64_t fp_regs[8]; 34 } __attribute__((aligned(16))); 35 36 /* ---- layout checks -------------------------------------------------- 37 * 38 * xco.c sized its embedded ctx buffer using these macros — verify 39 * they still match the real struct, and verify the byte offsets the 40 * asm hardcodes are still correct. 41 */ 42 _Static_assert(sizeof(struct xco_platform_ctx) == XCO__CTX_SIZE, 43 "XCO__CTX_SIZE out of sync with struct layout"); 44 _Static_assert(_Alignof(struct xco_platform_ctx) == XCO__CTX_ALIGN, 45 "XCO__CTX_ALIGN out of sync with struct layout"); 46 _Static_assert(sizeof(uintptr_t) == 8, "AArch64"); 47 _Static_assert(offsetof(struct xco_platform_ctx, regs) == 0, ""); 48 _Static_assert(offsetof(struct xco_platform_ctx, fp_regs) == 104, ""); 49 50 /* Forward declaration: the symbol is defined by the file-scope asm 51 * below. Its address is taken in xco_platform_init. */ 52 extern void xco_platform_trampoline_thunk(void); 53 54 /* ---- xco_platform_init --------------------------------------------- */ 55 56 void xco_platform_init(xco_platform_ctx_t *ctx, 57 void *stack_base, size_t stack_len, 58 void (*entry)(uintptr_t)) { 59 assert(((uintptr_t)stack_base & (XCO_STACK_ALIGN - 1)) == 0); 60 61 /* AArch64 stacks grow down. Compute and align the top. */ 62 uintptr_t top = (uintptr_t)stack_base + stack_len; 63 top &= ~(uintptr_t)(XCO_STACK_ALIGN - 1); 64 65 memset(ctx, 0, sizeof(*ctx)); 66 67 /* The switch primitive will load these into x19, fp, lr, sp. */ 68 ctx->regs[0] = (uintptr_t)entry; /* x19 */ 69 ctx->regs[10] = 0; /* fp */ 70 ctx->regs[11] = (uintptr_t)xco_platform_trampoline_thunk; /* lr */ 71 ctx->regs[12] = top; /* sp */ 72 } 73 74 /* ---- xco_platform_switch and xco_trampoline thunk (file-scope asm) ----- 75 * 76 * xco_platform_switch(from x0, to x1, value x2) -> x0: 77 * saves callee-saved state into *from, restores from *to, and 78 * delivers `value` to the destination as x0 — which is the first-arg 79 * register on the xco_trampoline thunk's first run, and the return-value 80 * register when resuming a previously-suspended switch. 81 * 82 * xco_platform_trampoline_thunk(): 83 * On entry x0 = value, x19 = entry; tail-calls entry(value); brk if 84 * it ever returns. 85 */ 86 87 /* Stringify-after-expand so __USER_LABEL_PREFIX__ (a token, possibly 88 * empty) becomes a string literal we can concatenate into the asm. */ 89 #define XCO_STR_(x) #x 90 #define XCO_STR(x) XCO_STR_(x) 91 #define XCO_SYM(name) XCO_STR(__USER_LABEL_PREFIX__) #name 92 93 __asm__ ( 94 ".text\n" 95 ".align 4\n" 96 97 ".globl " XCO_SYM(xco_platform_switch) "\n" 98 XCO_SYM(xco_platform_switch) ":\n" 99 " stp x19, x20, [x0, #0]\n" 100 " stp x21, x22, [x0, #16]\n" 101 " stp x23, x24, [x0, #32]\n" 102 " stp x25, x26, [x0, #48]\n" 103 " stp x27, x28, [x0, #64]\n" 104 " stp fp, lr, [x0, #80]\n" 105 " mov x9, sp\n" 106 " str x9, [x0, #96]\n" 107 " stp d8, d9, [x0, #104]\n" 108 " stp d10, d11, [x0, #120]\n" 109 " stp d12, d13, [x0, #136]\n" 110 " stp d14, d15, [x0, #152]\n" 111 112 " ldp d8, d9, [x1, #104]\n" 113 " ldp d10, d11, [x1, #120]\n" 114 " ldp d12, d13, [x1, #136]\n" 115 " ldp d14, d15, [x1, #152]\n" 116 " ldp x19, x20, [x1, #0]\n" 117 " ldp x21, x22, [x1, #16]\n" 118 " ldp x23, x24, [x1, #32]\n" 119 " ldp x25, x26, [x1, #48]\n" 120 " ldp x27, x28, [x1, #64]\n" 121 " ldp fp, lr, [x1, #80]\n" 122 " ldr x9, [x1, #96]\n" 123 " mov sp, x9\n" 124 125 " mov x0, x2\n" 126 " ret\n" 127 128 ".globl " XCO_SYM(xco_platform_trampoline_thunk) "\n" 129 XCO_SYM(xco_platform_trampoline_thunk) ":\n" 130 " blr x19\n" 131 " brk #0\n" 132 );