/* $NetBSD: cpuswitch.S,v 1.41 2023/03/01 08:17:24 riastradh Exp $ */ /*- * Copyright (c) 2014, 2020 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Matt Thomas of 3am Software Foundry. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include "assym.h" #include "opt_cpuoptions.h" #include "opt_ddb.h" #include "opt_kasan.h" RCSID("$NetBSD: cpuswitch.S,v 1.41 2023/03/01 08:17:24 riastradh Exp $") ARMV8_DEFINE_OPTIONS /* * At IPL_SCHED: * x0 = oldlwp * x1 = newlwp * x2 = returning * returns x0-x2 unchanged */ ENTRY_NP(cpu_switchto) /* * Store the callee saved register on the stack. */ sub sp, sp, #TF_SIZE /* make switchframe */ stp x19, x20, [sp, #TF_X19] stp x21, x22, [sp, #TF_X21] stp x23, x24, [sp, #TF_X23] stp x25, x26, [sp, #TF_X25] stp x27, x28, [sp, #TF_X27] stp x29, x30, [sp, #TF_X29] #ifdef DDB /* mark as switchframe for backtrace */ .if TF_SP + 8 == TF_PC stp xzr, lr, [sp, #TF_SP] .else str xzr, [sp, #TF_SP] str lr, [sp, #TF_PC] .endif #endif /* * Save the current stack pointer and the CPACR and save them in * old lwp md area. */ mov x4, sp mrs x5, cpacr_el1 str x5, [x0, #L_MD_CPACR] ldr x6, [x0, #L_PCB] /* x6 = lwp_getpcb(oldlwp) */ str x4, [x6, #PCB_TF] /* We are done with the old lwp */ ldr x6, [x1, #L_PCB] /* x6 = lwp_getpcb(newlwp) */ ldr x4, [x6, #PCB_TF] /* get trapframe ptr (aka SP) */ #ifdef DDB str xzr, [x6, #PCB_TF] /* clear l->l_addr->pcb_tf */ #endif ldr x5, [x1, #L_MD_CPACR] /* get cpacr_el1 */ DISABLE_INTERRUPT mov sp, x4 /* restore stack pointer */ msr cpacr_el1, x5 /* restore cpacr_el1 */ isb #ifdef ARMV83_PAC /* Switch the PAC key. */ adrl x4, _C_LABEL(aarch64_pac_enabled) ldr w4, [x4] cbz w4, 1f ldp x5, x6, [x1, #L_MD_IA_KERN] msr APIAKeyLo_EL1, x5 msr APIAKeyHi_EL1, x6 /* Other keys only need to be updated when switching to user process */ ldr w5, [x1, #L_FLAG] and w5, w5, #LW_SYSTEM /* (lwp->l_flag & LW_SYSTEM) ? */ cbnz w5, 1f ldp x5, x6, [x1, #L_MD_IB_USER] msr APIBKeyLo_EL1, x5 msr APIBKeyHi_EL1, x6 ldp x5, x6, [x1, #L_MD_DA_USER] msr APDAKeyLo_EL1, x5 msr APDAKeyHi_EL1, x6 ldp x5, x6, [x1, #L_MD_DB_USER] msr APDBKeyLo_EL1, x5 msr APDBKeyHi_EL1, x6 ldp x5, x6, [x1, #L_MD_GA_USER] msr APGAKeyLo_EL1, x5 msr APGAKeyHi_EL1, x6 1: isb #endif msr tpidr_el1, x1 /* switch curlwp to new lwp */ ldr x3, [x1, #L_CPU] /* * Issue barriers to coordinate mutex_exit on this CPU with * mutex_vector_enter on another CPU. * * 1. Any prior mutex_exit by oldlwp must be visible to other * CPUs before we set ci_curlwp := newlwp on this one, * requiring a store-before-store barrier. * * 2. ci_curlwp := newlwp must be visible on all other CPUs * before any subsequent mutex_exit by newlwp can even test * whether there might be waiters, requiring a * store-before-load barrier. * * See kern_mutex.c for details -- this is necessary for * adaptive mutexes to detect whether the lwp is on the CPU in * order to safely block without requiring atomic r/m/w in * mutex_exit. */ dmb ishst /* store-before-store */ str x1, [x3, #CI_CURLWP] /* switch curlwp to new lwp */ dmb ish /* store-before-load */ ENABLE_INTERRUPT /* * Restore callee save registers. */ ldp x19, x20, [sp, #TF_X19] ldp x21, x22, [sp, #TF_X21] ldp x23, x24, [sp, #TF_X23] ldp x25, x26, [sp, #TF_X25] ldp x27, x28, [sp, #TF_X27] ldp x29, lr, [sp, #TF_X29] add sp, sp, #TF_SIZE /* unwind switchframe */ ret END(cpu_switchto) /* * void * cpu_switchto_softint(struct lwp *softlwp, int ipl) * { * build a switchframe on kernel stack. * craft TF_X30 to have softint_cleanup. * pinned_lwp = curlwp * switch to softlwp context. * call softint_dispatch(pinned_lwp, ipl); * switch back to pinned_lwp context. * unwind switchframe made on kernel stack. * return to caller this time. * } */ ENTRY_NP(cpu_switchto_softint) #ifdef DDB mov x7, sp /* keep original sp for backtrace */ #endif stp x19, x20, [sp, #-16]! /* save */ sub sp, sp, #TF_SIZE /* make switchframe */ adr x2, softint_cleanup /* return address for cpu_switchto() */ mrs x19, daif /* x19 := original interrupt mask */ stp x19, lr, [sp, #TF_X19] /* x20 := original lr */ stp x21, x22, [sp, #TF_X21] stp x23, x24, [sp, #TF_X23] stp x25, x26, [sp, #TF_X25] stp x27, x28, [sp, #TF_X27] stp x29, x2, [sp, #TF_X29] /* tf->tf_lr = softint_cleanup; */ mrs x19, tpidr_el1 /* x19 := curlwp */ mov x4, sp mrs x5, cpacr_el1 ldr x6, [x19, #L_PCB] /* x6 = lwp_getpcb(curlwp) */ str x4, [x6, #PCB_TF] str x5, [x19, #L_MD_CPACR] #ifdef KASAN /* clear the new stack */ stp x0, x1, [sp, #-16]! #ifdef DDB stp x7, lr, [sp, #-16]! /* original sp and lr */ #endif bl _C_LABEL(kasan_softint) #ifdef DDB ldp x7, lr, [sp], #16 #endif ldp x0, x1, [sp], #16 #endif ldr x4, [x0, #L_MD_UTF] DISABLE_INTERRUPT ldr x20, [x19, #L_CPU] /* x20 := curlwp->l_cpu */ /* onto new stack */ sub sp, x4, #TF_SIZE /* new sp := softlwp->l_md_utf - 1 */ msr tpidr_el1, x0 /* curlwp = softlwp; */ dmb ishst /* for mutex_enter; see cpu_switchto */ str x0, [x20, #CI_CURLWP] /* curcpu()->ci_curlwp = softlwp; */ /* * No need for barrier after ci->ci_curlwp = softlwp -- when we * enter a softint lwp, it can't be holding any mutexes, so it * can't release any until after it has acquired them, so we * need not participate in the protocol with mutex_vector_enter * barriers here. */ mov x5, #CPACR_FPEN_NONE msr cpacr_el1, x5 /* cpacr_el1 = CPACR_FPEN_NONE */ isb #ifdef ARMV83_PAC /* Switch the PAC key. */ adrl x4, _C_LABEL(aarch64_pac_enabled) ldr w4, [x4] cbz w4, 1f ldp x5, x6, [x0, #L_MD_IA_KERN] msr APIAKeyLo_EL1, x5 msr APIAKeyHi_EL1, x6 isb 1: #endif ENABLE_INTERRUPT #ifdef DDB /* for db_trace.c:db_sp_trace() */ stp x7, lr, [sp, #-16]! /* push original sp,lr for backtrace info */ #endif /* softint_dispatch(pinned_lwp, ipl) */ mov x0, x19 /* x0 := pinned_lwp */ bl _C_LABEL(softint_dispatch) #ifdef DDB add sp, sp, #16 /* pop backtrace info */ #endif ldr x6, [x19, #L_PCB] /* x6 = lwp_getpcb(curlwp) */ ldr x4, [x6, #PCB_TF] /* x4 := pinned_lwp->l_addr->pcb_tf */ #ifdef DDB str xzr, [x6, #PCB_TF] /* clear l->l_addr->pcb_tf */ #endif ldr x5, [x19, #L_MD_CPACR] /* x5 := pinned_lwp->l_md_cpacr */ DISABLE_INTERRUPT msr tpidr_el1, x19 /* curlwp = pinned_lwp */ ldr x3, [x19, #L_CPU] /* x3 = curlwp->l_cpu */ dmb ishst /* for mutex_enter; see cpu_switchto */ str x19, [x3, #CI_CURLWP] /* curlwp->l_cpu->ci_curlwp := x19 */ dmb ish /* for mutex_enter; see cpu_switchto */ mov sp, x4 /* restore pinned_lwp sp */ msr cpacr_el1, x5 /* restore pinned_lwp cpacr */ isb #ifdef ARMV83_PAC /* Restore the PAC key. */ adrl x4, _C_LABEL(aarch64_pac_enabled) ldr w4, [x4] cbz w4, 1f ldp x5, x6, [x19, #L_MD_IA_KERN] msr APIAKeyLo_EL1, x5 msr APIAKeyHi_EL1, x6 isb 1: #endif ldp x19, lr, [sp, #TF_X19] /* restore pinned_lwp lr */ msr daif, x19 /* restore interrupt mask */ add sp, sp, #TF_SIZE /* unwind switchframe */ ldp x19, x20, [sp], #16 /* restore */ ret END(cpu_switchto_softint) /* * void * softint_cleanup(struct lwp *softlwp) * { * cpu_switchto() bottom half arranges to start this when softlwp. * kernel thread is to yield CPU for the pinned_lwp in the above. * curcpu()->ci_mtx_count += 1; * this returns as if cpu_switchto_softint finished normally. * } */ ENTRY_NP(softint_cleanup) mov lr, x20 /* restore original lr */ mrs x20, tpidr_el1 /* curlwp */ ldr x3, [x20, #L_CPU] /* curcpu */ ldr w2, [x3, #CI_MTX_COUNT] /* ->ci_mtx_count */ add w2, w2, #1 str w2, [x3, #CI_MTX_COUNT] msr daif, x19 /* restore interrupt mask */ ldp x19, x20, [sp], #16 /* restore */ ret END(softint_cleanup) /* * Called at IPL_SCHED: * x0 = old lwp (from cpu_switchto) * x1 = new lwp (from cpu_switchto) * x27 = func * x28 = arg */ ENTRY_NP(lwp_trampoline) stp x0, x1, [sp, #-16]! /* save x0, x1 */ bl _C_LABEL(lwp_startup) ldp x0, x1, [sp], #16 /* restore x0, x1 */ #ifdef ARMV83_PAC /* Generate a new PAC key. */ adrl x4, _C_LABEL(aarch64_pac_enabled) ldr w4, [x4] cbz w4, 1f mov x26, x1 bl _C_LABEL(cprng_strong64) mov x25, x0 bl _C_LABEL(cprng_strong64) stp x25, x0, [x26, #L_MD_IA_KERN] msr APIAKeyLo_EL1, x25 msr APIAKeyHi_EL1, x0 isb 1: #endif /* * When the x27 function returns, it will jump to el0_trap_exit. */ adr lr, el0_trap_exit /* tail call via lr */ mov x0, x28 /* mov arg into place */ mov x16, x27 /* use x16 as jump register, for BTI */ br x16 /* call function with arg */ END(lwp_trampoline) /* * int cpu_set_onfault(struct faultbuf *fb) */ ENTRY_NP(cpu_set_onfault) mrs x2, tpidr_el1 /* x2 = curlwp */ str x0, [x2, #L_MD_ONFAULT] /* l_md.md_onfault = fb */ stp x19, x20, [x0, #(FB_X19 * 8)] stp x21, x22, [x0, #(FB_X21 * 8)] stp x23, x24, [x0, #(FB_X23 * 8)] stp x25, x26, [x0, #(FB_X25 * 8)] stp x27, x28, [x0, #(FB_X27 * 8)] stp x29, x30, [x0, #(FB_X29 * 8)] mov x1, sp str x1, [x0, #(FB_SP * 8)] mov x0, #0 ret END(cpu_set_onfault) /* * setjmp(9) * int setjmp(label_t *label); * void longjmp(label_t *label); */ ENTRY_NP(setjmp) stp x19, x20, [x0, #(LBL_X19 * 8)] stp x21, x22, [x0, #(LBL_X21 * 8)] stp x23, x24, [x0, #(LBL_X23 * 8)] stp x25, x26, [x0, #(LBL_X25 * 8)] stp x27, x28, [x0, #(LBL_X27 * 8)] stp x29, x30, [x0, #(LBL_X29 * 8)] mov x1, sp str x1, [x0, #(LBL_SP * 8)] mov x0, #0 ret END(setjmp) ENTRY_NP(longjmp) ldp x19, x20, [x0, #(LBL_X19 * 8)] ldp x21, x22, [x0, #(LBL_X21 * 8)] ldp x23, x24, [x0, #(LBL_X23 * 8)] ldp x25, x26, [x0, #(LBL_X25 * 8)] ldp x27, x28, [x0, #(LBL_X27 * 8)] ldp x29, x30, [x0, #(LBL_X29 * 8)] ldr x1, [x0, #(LBL_SP * 8)] mov sp, x1 mov x0, #1 ret END(longjmp) ENTRY_NP(load_fpregs) ldp q0, q1, [x0, #FPREG_Q0] ldp q2, q3, [x0, #FPREG_Q2] ldp q4, q5, [x0, #FPREG_Q4] ldp q6, q7, [x0, #FPREG_Q6] ldp q8, q9, [x0, #FPREG_Q8] ldp q10, q11, [x0, #FPREG_Q10] ldp q12, q13, [x0, #FPREG_Q12] ldp q14, q15, [x0, #FPREG_Q14] ldp q16, q17, [x0, #FPREG_Q16] ldp q18, q19, [x0, #FPREG_Q18] ldp q20, q21, [x0, #FPREG_Q20] ldp q22, q23, [x0, #FPREG_Q22] ldp q24, q25, [x0, #FPREG_Q24] ldp q26, q27, [x0, #FPREG_Q26] ldp q28, q29, [x0, #FPREG_Q28] ldp q30, q31, [x0, #FPREG_Q30] ldr w8, [x0, #FPREG_FPCR] ldr w9, [x0, #FPREG_FPSR] msr fpcr, x8 msr fpsr, x9 ret END(load_fpregs) ENTRY_NP(save_fpregs) stp q0, q1, [x0, #FPREG_Q0] stp q2, q3, [x0, #FPREG_Q2] stp q4, q5, [x0, #FPREG_Q4] stp q6, q7, [x0, #FPREG_Q6] stp q8, q9, [x0, #FPREG_Q8] stp q10, q11, [x0, #FPREG_Q10] stp q12, q13, [x0, #FPREG_Q12] stp q14, q15, [x0, #FPREG_Q14] stp q16, q17, [x0, #FPREG_Q16] stp q18, q19, [x0, #FPREG_Q18] stp q20, q21, [x0, #FPREG_Q20] stp q22, q23, [x0, #FPREG_Q22] stp q24, q25, [x0, #FPREG_Q24] stp q26, q27, [x0, #FPREG_Q26] stp q28, q29, [x0, #FPREG_Q28] stp q30, q31, [x0, #FPREG_Q30] mrs x8, fpcr mrs x9, fpsr str w8, [x0, #FPREG_FPCR] str w9, [x0, #FPREG_FPSR] ret END(save_fpregs)