/* * Copyright 2019-2022 Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. */ #include #include #include "asm_offsets.h" #include "syscall_numbers.h" .text FUNCTION(_thread_exit_syscall): svc #((SYSCALL_EXIT_THREAD << 5) | 1) FUNCTION_END(_thread_exit_syscall) .macro xchg_sp xt add sp, sp, \xt sub \xt, sp, \xt sub sp, sp, \xt .endm .macro EXCEPTION_ENTRY el // interrupts are automatically disabled by hardware // avoid using sp in case it is misaligned // swap sp with x19 and use it instead xchg_sp x19 // x19 is now the stack top, make room for IFRAME sub x19, x19, #(IFRAME_sizeof) stp x0, x1, [x19, #(IFRAME_x + 0 * 8)] stp x2, x3, [x19, #(IFRAME_x + 2 * 8)] stp x4, x5, [x19, #(IFRAME_x + 4 * 8)] stp x6, x7, [x19, #(IFRAME_x + 6 * 8)] stp x8, x9, [x19, #(IFRAME_x + 8 * 8)] stp x10, x11, [x19, #(IFRAME_x + 10 * 8)] stp x12, x13, [x19, #(IFRAME_x + 12 * 8)] stp x14, x15, [x19, #(IFRAME_x + 14 * 8)] stp x16, x17, [x19, #(IFRAME_x + 16 * 8)] mov x0, sp // original x19 that we swapped with sp stp x18, x0, [x19, #(IFRAME_x + 18 * 8)] // x20-x28 won't be clobbered // thus we don't really need to store these str x29, [x19, #(IFRAME_fp)] str x30, [x19, #(IFRAME_lr)] .if \el == 0 mrs x0, SP_EL0 .else // add sizeof back here to store original sp add x0, x19, #(IFRAME_sizeof) .endif mrs x1, ELR_EL1 mrs x2, SPSR_EL1 mrs x3, ESR_EL1 mrs x4, FAR_EL1 str x0, [x19, #(IFRAME_sp)] str x1, [x19, #(IFRAME_elr)] str x2, [x19, #(IFRAME_spsr)] str x3, [x19, #(IFRAME_esr)] str x4, [x19, #(IFRAME_far)] .endm .macro EXCEPTION_RETURN el // x19 is callee-saved so it still points to IFRAME // x0, x1, x18, x19 will be restored at the very end ldr x0, [x19, #(IFRAME_elr)] ldr x1, [x19, #(IFRAME_spsr)] ldr x18, [x19, #(IFRAME_sp)] // x0 and x1 will be restored later ldp x2, x3, [x19, #(IFRAME_x + 2 * 8)] ldp x4, x5, [x19, #(IFRAME_x + 4 * 8)] ldp x6, x7, [x19, #(IFRAME_x + 6 * 8)] ldp x8, x9, [x19, #(IFRAME_x + 8 * 8)] ldp x10, x11, [x19, #(IFRAME_x + 10 * 8)] ldp x12, x13, [x19, #(IFRAME_x + 12 * 8)] ldp x14, x15, [x19, #(IFRAME_x + 14 * 8)] ldp x16, x17, [x19, #(IFRAME_x + 16 * 8)] // x18 and x19 will be restored later ldr x29, [x19, #(IFRAME_fp)] ldr x30, [x19, #(IFRAME_lr)] // disable interrupts before restoring ELR/SPSR/sp msr DAIFSet, #0xf msr ELR_EL1, x0 msr SPSR_EL1, x1 .if \el == 0 // load stack pointer for EL0 from IFRAME msr SP_EL0, x18 // unwind our own stack pointer add sp, x19, #(IFRAME_sizeof) .else // we stored original pointer to IFRAME, no need to unwind again there mov sp, x18 .endif // finally restore remaining registers ldp x0, x1, [x19, #(IFRAME_x + 0 * 8)] ldp x18, x19, [x19, #(IFRAME_x + 18 * 8)] eret .endm .macro EXCEPTION_HANDLER el name func STATIC_FUNCTION(handle_\name): EXCEPTION_ENTRY \el // prepare aligned sp for C function and sp, x19, #0xfffffffffffffff0 // call C handler, passing IFRAME in x0 // handler can enable interrupts if it wants to mov x0, x19 mov x29, x0 bl \func EXCEPTION_RETURN \el FUNCTION_END(handle_\name) .endm .macro vector name .align 7 b handle_\name .endm .macro vempty .align 7 brk 0xfff 1: b 1b .endm .align 11 .globl _exception_vectors _exception_vectors: vempty /* Synchronous EL1t */ vempty /* IRQ EL1t */ vempty /* FIQ EL1t */ vempty /* Error EL1t */ vector el1h_sync /* Synchronous EL1h */ vector el1h_irq /* IRQ EL1h */ vector el1h_fiq /* FIQ EL1h */ vector el1h_error /* Error EL1h */ vector el0_sync /* Synchronous 64-bit EL0 */ vector el0_irq /* IRQ 64-bit EL0 */ vector el0_fiq /* FIQ 64-bit EL0 */ vector el0_error /* Error 64-bit EL0 */ vempty /* Synchronous 32-bit EL0 */ vempty /* IRQ 32-bit EL0 */ vempty /* FIQ 32-bit EL0 */ vempty /* Error 32-bit EL0 */ EXCEPTION_HANDLER 1 el1h_sync do_sync_handler EXCEPTION_HANDLER 1 el1h_irq do_irq_handler EXCEPTION_HANDLER 1 el1h_fiq do_fiq_handler EXCEPTION_HANDLER 1 el1h_error do_error_handler EXCEPTION_HANDLER 0 el0_sync do_sync_handler EXCEPTION_HANDLER 0 el0_irq do_irq_handler EXCEPTION_HANDLER 0 el0_fiq do_fiq_handler EXCEPTION_HANDLER 0 el0_error do_error_handler FUNCTION(_eret_with_iframe): mov x20, xzr mov x21, xzr mov x22, xzr mov x23, xzr mov x24, xzr mov x25, xzr mov x26, xzr mov x27, xzr mov x28, xzr mov x29, xzr mov x19, x0 EXCEPTION_RETURN 0 FUNCTION_END(_eret_with_iframe) FUNCTION(_fp_save): stp q0, q1, [x0], #32 stp q2, q3, [x0], #32 stp q4, q5, [x0], #32 stp q6, q7, [x0], #32 stp q8, q9, [x0], #32 stp q10, q11, [x0], #32 stp q12, q13, [x0], #32 stp q14, q15, [x0], #32 stp q16, q17, [x0], #32 stp q18, q19, [x0], #32 stp q20, q21, [x0], #32 stp q22, q23, [x0], #32 stp q24, q25, [x0], #32 stp q26, q27, [x0], #32 stp q28, q29, [x0], #32 stp q30, q31, [x0], #32 mrs x1, FPSR mrs x2, FPCR str x1, [x0], #8 str x2, [x0], #8 // reset FPCR and FPSR to prevent userspace state affecting kernel msr FPSR, xzr cmp x2, xzr beq 1f msr FPCR, xzr 1: ret FUNCTION_END(_fp_save) FUNCTION(_fp_restore): ldp q0, q1, [x0], #32 ldp q2, q3, [x0], #32 ldp q4, q5, [x0], #32 ldp q6, q7, [x0], #32 ldp q8, q9, [x0], #32 ldp q10, q11, [x0], #32 ldp q12, q13, [x0], #32 ldp q14, q15, [x0], #32 ldp q16, q17, [x0], #32 ldp q18, q19, [x0], #32 ldp q20, q21, [x0], #32 ldp q22, q23, [x0], #32 ldp q24, q25, [x0], #32 ldp q26, q27, [x0], #32 ldp q28, q29, [x0], #32 ldp q30, q31, [x0], #32 ldr x1, [x0], #8 msr FPSR, x1 // avoid restoring FPCR if it hasn't changed ldr x2, [x0], #8 mrs x3, FPCR cmp x3, x2 beq 1f msr FPCR, x2 1: ret FUNCTION_END(_fp_restore) FUNCTION(_arch_context_swap): // save stp x19, x20, [x0], #16 stp x21, x22, [x0], #16 stp x23, x24, [x0], #16 stp x25, x26, [x0], #16 stp x27, x28, [x0], #16 stp x29, x30, [x0], #16 mov x2, sp mrs x3, TPIDR_EL0 stp x2, x3, [x0], #16 stp d8, d9, [x0], #16 stp d10, d11, [x0], #16 stp d12, d13, [x0], #16 stp d14, d15, [x0], #16 // restore ldp x19, x20, [x1], #16 ldp x21, x22, [x1], #16 ldp x23, x24, [x1], #16 ldp x25, x26, [x1], #16 ldp x27, x28, [x1], #16 ldp x29, x30, [x1], #16 ldp x2, x3, [x1], #16 mov sp, x2 msr TPIDR_EL0, x3 ldp d8, d9, [x1], #16 ldp d10, d11, [x1], #16 ldp d12, d13, [x1], #16 ldp d14, d15, [x1], #16 // pass x29 as argument to thread entry function mov x0, x29 ret FUNCTION_END(_arch_context_swap) /*! \fn void arch_debug_call_with_fault_handler(cpu_ent* cpu, jmp_buf jumpBuffer, void (*function)(void*), void* parameter) Called by debug_call_with_fault_handler() to do the dirty work of setting the fault handler and calling the function. If the function causes a page fault, the arch_debug_call_with_fault_handler() calls longjmp() with the given \a jumpBuffer. Otherwise it returns normally. debug_call_with_fault_handler() has already saved the CPU's fault_handler and fault_handler_stack_pointer and will reset them later, so arch_debug_call_with_fault_handler() doesn't need to care about it. \param cpu The \c cpu_ent for the current CPU. \param jumpBuffer Buffer to be used for longjmp(). \param function The function to be called. \param parameter The parameter to be passed to the function to be called. */ FUNCTION(arch_debug_call_with_fault_handler): ldr x4, =fault str x4, [x0, #CPU_ENT_fault_handler] str x1, [x0, #CPU_ENT_fault_handler_stack_pointer] mov x0, x3 br x2 fault: mov x0, sp mov x1, #1 b longjmp FUNCTION_END(arch_debug_call_with_fault_handler) /* addr_t arm64_get_fp(void) */ FUNCTION(arm64_get_fp): mov x0, x29 ret FUNCTION_END(arm64_get_fp)