1*0f4c859eSApple OSS Distributions/* 2*0f4c859eSApple OSS Distributions * Copyright (c) 2020 Apple Inc. All rights reserved. 3*0f4c859eSApple OSS Distributions * 4*0f4c859eSApple OSS Distributions * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5*0f4c859eSApple OSS Distributions * 6*0f4c859eSApple OSS Distributions * This file contains Original Code and/or Modifications of Original Code 7*0f4c859eSApple OSS Distributions * as defined in and that are subject to the Apple Public Source License 8*0f4c859eSApple OSS Distributions * Version 2.0 (the 'License'). You may not use this file except in 9*0f4c859eSApple OSS Distributions * compliance with the License. The rights granted to you under the License 10*0f4c859eSApple OSS Distributions * may not be used to create, or enable the creation or redistribution of, 11*0f4c859eSApple OSS Distributions * unlawful or unlicensed copies of an Apple operating system, or to 12*0f4c859eSApple OSS Distributions * circumvent, violate, or enable the circumvention or violation of, any 13*0f4c859eSApple OSS Distributions * terms of an Apple operating system software license agreement. 14*0f4c859eSApple OSS Distributions * 15*0f4c859eSApple OSS Distributions * Please obtain a copy of the License at 16*0f4c859eSApple OSS Distributions * http://www.opensource.apple.com/apsl/ and read it before using this file. 17*0f4c859eSApple OSS Distributions * 18*0f4c859eSApple OSS Distributions * The Original Code and all software distributed under the License are 19*0f4c859eSApple OSS Distributions * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20*0f4c859eSApple OSS Distributions * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21*0f4c859eSApple OSS Distributions * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22*0f4c859eSApple OSS Distributions * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23*0f4c859eSApple OSS Distributions * Please see the License for the specific language governing rights and 24*0f4c859eSApple OSS Distributions * limitations under the License. 25*0f4c859eSApple OSS Distributions * 26*0f4c859eSApple OSS Distributions * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27*0f4c859eSApple OSS Distributions */ 28*0f4c859eSApple OSS Distributions 29*0f4c859eSApple OSS Distributions#include <machine/asm.h> 30*0f4c859eSApple OSS Distributions 31*0f4c859eSApple OSS Distributions/* This section has all the code necessary for the atomic operations supported by 32*0f4c859eSApple OSS Distributions * OSAtomicFifoEnqueue, OSAtomicFifoDequeue APIs in libplatform. 33*0f4c859eSApple OSS Distributions * 34*0f4c859eSApple OSS Distributions * This code needs to be compiled as 1 section and should not make branches 35*0f4c859eSApple OSS Distributions * outside of this section. This allows us to copy the entire section to the 36*0f4c859eSApple OSS Distributions * text comm page once it is created - see osfmk/arm/commpage/commpage.c 37*0f4c859eSApple OSS Distributions * 38*0f4c859eSApple OSS Distributions * This section is split into 2 parts - the preemption-free zone (PFZ) routines 39*0f4c859eSApple OSS Distributions * and the preemptible routines (non-PFZ). The PFZ routines will not be 40*0f4c859eSApple OSS Distributions * preempted by the scheduler if the pc of the userspace process is in that 41*0f4c859eSApple OSS Distributions * region while handling asynchronous interrupts (note that traps are still 42*0f4c859eSApple OSS Distributions * possible in the PFZ). Instead, the scheduler will mark x15 (known through 43*0f4c859eSApple OSS Distributions * coordination with the functions in the commpage section) to indicate to the 44*0f4c859eSApple OSS Distributions * userspace code that it needs to take a delayed preemption. The PFZ functions 45*0f4c859eSApple OSS Distributions * may make callouts to preemptible routines and vice-versa. When a function 46*0f4c859eSApple OSS Distributions * returns to a preemptible routine after a callout to a function in the PFZ, it 47*0f4c859eSApple OSS Distributions * needs to check x15 to determine if a delayed preemption needs to be taken. In 48*0f4c859eSApple OSS Distributions * addition, functions in the PFZ should not have backwards branches. 49*0f4c859eSApple OSS Distributions * 50*0f4c859eSApple OSS Distributions * The entry point to execute code in the commpage text section is through the 51*0f4c859eSApple OSS Distributions * jump table at the very top of the section. The base of the jump table is 52*0f4c859eSApple OSS Distributions * exposed to userspace via the APPLE array and the offsets from the base of the 53*0f4c859eSApple OSS Distributions * jump table are listed in the arm/cpu_capabilities.h header. Adding any new 54*0f4c859eSApple OSS Distributions * functions in the PFZ requires a lockstep change to the cpu_capabilities.h 55*0f4c859eSApple OSS Distributions * header. 56*0f4c859eSApple OSS Distributions * 57*0f4c859eSApple OSS Distributions * Functions in PFZ: 58*0f4c859eSApple OSS Distributions * Enqueue function 59*0f4c859eSApple OSS Distributions * Dequeue function 60*0f4c859eSApple OSS Distributions * 61*0f4c859eSApple OSS Distributions * Functions not in PFZ: 62*0f4c859eSApple OSS Distributions * Backoff function as part of spin loop 63*0f4c859eSApple OSS Distributions * Preempt function to take delayed preemption as indicated by kernel 64*0f4c859eSApple OSS Distributions * 65*0f4c859eSApple OSS Distributions * ---------------------------------------------------------------------- 66*0f4c859eSApple OSS Distributions * 67*0f4c859eSApple OSS Distributions * The high level goal of the asm code in this section is to enqueue and dequeue 68*0f4c859eSApple OSS Distributions * from a FIFO linked list. 69*0f4c859eSApple OSS Distributions * 70*0f4c859eSApple OSS Distributions * typedef volatile struct { 71*0f4c859eSApple OSS Distributions * void *opaque1; <-- ptr to first queue element or null 72*0f4c859eSApple OSS Distributions * void *opaque2; <-- ptr to second queue element or null 73*0f4c859eSApple OSS Distributions * int opaque3; <-- spinlock 74*0f4c859eSApple OSS Distributions * } OSFifoQueueHead; 75*0f4c859eSApple OSS Distributions * 76*0f4c859eSApple OSS Distributions * This is done through a userspace spin lock stored in the linked list head 77*0f4c859eSApple OSS Distributions * for synchronization. 78*0f4c859eSApple OSS Distributions * 79*0f4c859eSApple OSS Distributions * Here is the pseudocode for the spin lock acquire algorithm which is split 80*0f4c859eSApple OSS Distributions * between the PFZ and the non-PFZ areas of the commpage text section. The 81*0f4c859eSApple OSS Distributions * pseudocode here is just for the enqueue operation but it is symmetrical for 82*0f4c859eSApple OSS Distributions * the dequeue operation. 83*0f4c859eSApple OSS Distributions * 84*0f4c859eSApple OSS Distributions * // Not in the PFZ. Entry from jump table. 85*0f4c859eSApple OSS Distributions * ENQUEUE() 86*0f4c859eSApple OSS Distributions * enqueued = TRY_LOCK_AND_ENQUEUE(lock_addr); 87*0f4c859eSApple OSS Distributions * // We're running here after running the TRY_LOCK_AND_ENQUEUE code in 88*0f4c859eSApple OSS Distributions * // the PFZ so we need to check if we need to take a delayed 89*0f4c859eSApple OSS Distributions * // preemption. 90*0f4c859eSApple OSS Distributions * if (kernel_wants_to_preempt_us){ 91*0f4c859eSApple OSS Distributions * // This is done through the pfz_exit() mach trap which is a dummy 92*0f4c859eSApple OSS Distributions * // syscall whose sole purpose is to allow the thread to enter the 93*0f4c859eSApple OSS Distributions * // kernel so that it can be preempted at AST. 94*0f4c859eSApple OSS Distributions * enter_kernel_to_take_delayed_preemption() 95*0f4c859eSApple OSS Distributions * } 96*0f4c859eSApple OSS Distributions * 97*0f4c859eSApple OSS Distributions * if (!enqueued) { 98*0f4c859eSApple OSS Distributions * ARM_MONITOR; 99*0f4c859eSApple OSS Distributions * WFE; 100*0f4c859eSApple OSS Distributions * enqueued = TRY_LOCK_AND_ENQUEUE(lock_addr); 101*0f4c859eSApple OSS Distributions * if (!enqueued) { 102*0f4c859eSApple OSS Distributions * // We failed twice, take a backoff 103*0f4c859eSApple OSS Distributions * BACKOFF(); 104*0f4c859eSApple OSS Distributions * goto ENQUEUE() 105*0f4c859eSApple OSS Distributions * } else { 106*0f4c859eSApple OSS Distributions * // We got here from PFZ, check for delayed preemption 107*0f4c859eSApple OSS Distributions * if (kernel_wants_to_preempt_us){ 108*0f4c859eSApple OSS Distributions * enter_kernel_to_take_delayed_preemption() 109*0f4c859eSApple OSS Distributions * } 110*0f4c859eSApple OSS Distributions * } 111*0f4c859eSApple OSS Distributions * } 112*0f4c859eSApple OSS Distributions * 113*0f4c859eSApple OSS Distributions * // in PFZ 114*0f4c859eSApple OSS Distributions * TRY_LOCK_AND_ENQUEUE(): 115*0f4c859eSApple OSS Distributions * is_locked = try_lock(lock_addr); 116*0f4c859eSApple OSS Distributions * if (is_locked) { 117*0f4c859eSApple OSS Distributions * <do enqueue operation> 118*0f4c859eSApple OSS Distributions * return true 119*0f4c859eSApple OSS Distributions * } else { 120*0f4c859eSApple OSS Distributions * return false 121*0f4c859eSApple OSS Distributions * } 122*0f4c859eSApple OSS Distributions * 123*0f4c859eSApple OSS Distributions * 124*0f4c859eSApple OSS Distributions * // Not in the PFZ 125*0f4c859eSApple OSS Distributions * BACKOFF(): 126*0f4c859eSApple OSS Distributions * // We're running here after running the TRY_LOCK_AND_ENQUEUE code in 127*0f4c859eSApple OSS Distributions * // the PFZ so we need to check if we need to take a delayed 128*0f4c859eSApple OSS Distributions * // preemption. 129*0f4c859eSApple OSS Distributions * if (kernel_wants_to_preempt_us) { 130*0f4c859eSApple OSS Distributions * enter_kernel_to_take_preemption() 131*0f4c859eSApple OSS Distributions * } else { 132*0f4c859eSApple OSS Distributions * // Note that it is safe to do this loop here since the entire 133*0f4c859eSApple OSS Distributions * // BACKOFF function isn't in the PFZ and so can be preempted at any 134*0f4c859eSApple OSS Distributions * // time 135*0f4c859eSApple OSS Distributions * do { 136*0f4c859eSApple OSS Distributions * lock_is_free = peek(lock_addr); 137*0f4c859eSApple OSS Distributions * if (lock_is_free) { 138*0f4c859eSApple OSS Distributions * return 139*0f4c859eSApple OSS Distributions * } else { 140*0f4c859eSApple OSS Distributions * pause_with_monitor(lock_addr) 141*0f4c859eSApple OSS Distributions * } 142*0f4c859eSApple OSS Distributions * } while (1) 143*0f4c859eSApple OSS Distributions * } 144*0f4c859eSApple OSS Distributions */ 145*0f4c859eSApple OSS Distributions 146*0f4c859eSApple OSS Distributions/* Macros and helpers */ 147*0f4c859eSApple OSS Distributions 148*0f4c859eSApple OSS Distributions.macro BACKOFF lock_addr 149*0f4c859eSApple OSS Distributions // Save registers we can't clobber 150*0f4c859eSApple OSS Distributions stp x0, x1, [sp, #-16]! 151*0f4c859eSApple OSS Distributions stp x2, x9, [sp, #-16]! 152*0f4c859eSApple OSS Distributions 153*0f4c859eSApple OSS Distributions // Pass in lock addr to backoff function 154*0f4c859eSApple OSS Distributions mov x0, \lock_addr 155*0f4c859eSApple OSS Distributions bl _backoff // Jump out of the PFZ zone now 156*0f4c859eSApple OSS Distributions 157*0f4c859eSApple OSS Distributions // Restore registers 158*0f4c859eSApple OSS Distributions ldp x2, x9, [sp], #16 159*0f4c859eSApple OSS Distributions ldp x0, x1, [sp], #16 160*0f4c859eSApple OSS Distributions.endmacro 161*0f4c859eSApple OSS Distributions 162*0f4c859eSApple OSS Distributions/* x0 = pointer to queue head 163*0f4c859eSApple OSS Distributions * x1 = pointer to new elem to enqueue 164*0f4c859eSApple OSS Distributions * x2 = offset of link field inside element 165*0f4c859eSApple OSS Distributions * x3 = Address of lock 166*0f4c859eSApple OSS Distributions * 167*0f4c859eSApple OSS Distributions * Moves result of the helper function to the register specified 168*0f4c859eSApple OSS Distributions */ 169*0f4c859eSApple OSS Distributions.macro TRYLOCK_ENQUEUE result 170*0f4c859eSApple OSS Distributions stp x0, xzr, [sp, #-16]! // Save x0 since it'll be clobbered by return value 171*0f4c859eSApple OSS Distributions 172*0f4c859eSApple OSS Distributions bl _pfz_trylock_and_enqueue 173*0f4c859eSApple OSS Distributions mov \result, x0 174*0f4c859eSApple OSS Distributions 175*0f4c859eSApple OSS Distributions ldp x0, xzr, [sp], #16 // Restore saved registers 176*0f4c859eSApple OSS Distributions.endmacro 177*0f4c859eSApple OSS Distributions 178*0f4c859eSApple OSS Distributions/* x0 = pointer to queue head 179*0f4c859eSApple OSS Distributions * x1 = offset of link field inside element 180*0f4c859eSApple OSS Distributions * x2 = Address of lock 181*0f4c859eSApple OSS Distributions * 182*0f4c859eSApple OSS Distributions * Moves result of the helper function to the register specified 183*0f4c859eSApple OSS Distributions */ 184*0f4c859eSApple OSS Distributions.macro TRYLOCK_DEQUEUE result 185*0f4c859eSApple OSS Distributions stp x0, xzr, [sp, #-16]! // Save x0 since it'll be clobbered by return value 186*0f4c859eSApple OSS Distributions 187*0f4c859eSApple OSS Distributions bl _pfz_trylock_and_dequeue 188*0f4c859eSApple OSS Distributions mov \result, x0 189*0f4c859eSApple OSS Distributions 190*0f4c859eSApple OSS Distributions ldp x0, xzr, [sp], #16 // Restore saved registers 191*0f4c859eSApple OSS Distributions.endmacro 192*0f4c859eSApple OSS Distributions 193*0f4c859eSApple OSS Distributions/* 194*0f4c859eSApple OSS Distributions * Takes a delayed preemption if needed and then branches to the label 195*0f4c859eSApple OSS Distributions * specified. 196*0f4c859eSApple OSS Distributions * 197*0f4c859eSApple OSS Distributions * Modifies x15 198*0f4c859eSApple OSS Distributions */ 199*0f4c859eSApple OSS Distributions.macro PREEMPT_SELF_THEN branch_to_take_on_success 200*0f4c859eSApple OSS Distributions cbz x15, \branch_to_take_on_success // No delayed preemption to take, just try again 201*0f4c859eSApple OSS Distributions 202*0f4c859eSApple OSS Distributions mov x15, xzr // zero out the preemption pending field 203*0f4c859eSApple OSS Distributions bl _preempt_self 204*0f4c859eSApple OSS Distributions b \branch_to_take_on_success 205*0f4c859eSApple OSS Distributions.endmacro 206*0f4c859eSApple OSS Distributions 207*0f4c859eSApple OSS Distributions .section __TEXT_EXEC,__commpage_text,regular,pure_instructions 208*0f4c859eSApple OSS Distributions 209*0f4c859eSApple OSS Distributions /* Preemption free functions */ 210*0f4c859eSApple OSS Distributions .align 2 211*0f4c859eSApple OSS Distributions_jump_table: // 32 entry jump table, only 2 are used 212*0f4c859eSApple OSS Distributions b _pfz_enqueue 213*0f4c859eSApple OSS Distributions b _pfz_dequeue 214*0f4c859eSApple OSS Distributions brk #666 215*0f4c859eSApple OSS Distributions brk #666 216*0f4c859eSApple OSS Distributions brk #666 217*0f4c859eSApple OSS Distributions brk #666 218*0f4c859eSApple OSS Distributions brk #666 219*0f4c859eSApple OSS Distributions brk #666 220*0f4c859eSApple OSS Distributions brk #666 221*0f4c859eSApple OSS Distributions brk #666 222*0f4c859eSApple OSS Distributions brk #666 223*0f4c859eSApple OSS Distributions brk #666 224*0f4c859eSApple OSS Distributions brk #666 225*0f4c859eSApple OSS Distributions brk #666 226*0f4c859eSApple OSS Distributions brk #666 227*0f4c859eSApple OSS Distributions brk #666 228*0f4c859eSApple OSS Distributions brk #666 229*0f4c859eSApple OSS Distributions brk #666 230*0f4c859eSApple OSS Distributions brk #666 231*0f4c859eSApple OSS Distributions brk #666 232*0f4c859eSApple OSS Distributions brk #666 233*0f4c859eSApple OSS Distributions brk #666 234*0f4c859eSApple OSS Distributions brk #666 235*0f4c859eSApple OSS Distributions brk #666 236*0f4c859eSApple OSS Distributions brk #666 237*0f4c859eSApple OSS Distributions brk #666 238*0f4c859eSApple OSS Distributions brk #666 239*0f4c859eSApple OSS Distributions brk #666 240*0f4c859eSApple OSS Distributions brk #666 241*0f4c859eSApple OSS Distributions brk #666 242*0f4c859eSApple OSS Distributions brk #666 243*0f4c859eSApple OSS Distributions brk #666 244*0f4c859eSApple OSS Distributions 245*0f4c859eSApple OSS Distributions 246*0f4c859eSApple OSS Distributions/* 247*0f4c859eSApple OSS Distributions * typedef volatile struct { 248*0f4c859eSApple OSS Distributions * void *opaque1; <-- ptr to first queue element or null 249*0f4c859eSApple OSS Distributions * void *opaque2; <-- ptr to second queue element or null 250*0f4c859eSApple OSS Distributions * int opaque3; <-- spinlock 251*0f4c859eSApple OSS Distributions * } osfifoqueuehead; 252*0f4c859eSApple OSS Distributions */ 253*0f4c859eSApple OSS Distributions 254*0f4c859eSApple OSS Distributions/* Non-preemptible helper routine to FIFO enqueue: 255*0f4c859eSApple OSS Distributions * int pfz_trylock_and_enqueue(OSFifoQueueHead *__list, void *__new, size_t __offset, uint32_t *lock_addr); 256*0f4c859eSApple OSS Distributions * 257*0f4c859eSApple OSS Distributions * x0 = pointer to queue head structure 258*0f4c859eSApple OSS Distributions * x1 = pointer to new element to enqueue 259*0f4c859eSApple OSS Distributions * x2 = offset of link field inside element 260*0f4c859eSApple OSS Distributions * x3 = address of lock 261*0f4c859eSApple OSS Distributions * 262*0f4c859eSApple OSS Distributions * Only caller save registers (x9 - x15) are used in this function 263*0f4c859eSApple OSS Distributions * 264*0f4c859eSApple OSS Distributions * Returns 0 on success and non-zero value on failure 265*0f4c859eSApple OSS Distributions */ 266*0f4c859eSApple OSS Distributions .globl _pfz_trylock_and_enqueue 267*0f4c859eSApple OSS Distributions .align 2 268*0f4c859eSApple OSS Distributions_pfz_trylock_and_enqueue: 269*0f4c859eSApple OSS Distributions ARM64_STACK_PROLOG 270*0f4c859eSApple OSS Distributions PUSH_FRAME 271*0f4c859eSApple OSS Distributions 272*0f4c859eSApple OSS Distributions mov w10, wzr // unlock value = w10 = 0 273*0f4c859eSApple OSS Distributions mov w11, #1 // locked value = w11 = 1 274*0f4c859eSApple OSS Distributions 275*0f4c859eSApple OSS Distributions // Try to grab the lock 276*0f4c859eSApple OSS Distributions casa w10, w11, [x3] // Atomic CAS with acquire barrier 277*0f4c859eSApple OSS Distributions cbz w10, Ltrylock_enqueue_success 278*0f4c859eSApple OSS Distributions 279*0f4c859eSApple OSS Distributions mov x0, #-1 // Failed 280*0f4c859eSApple OSS Distributions b Ltrylock_enqueue_exit 281*0f4c859eSApple OSS Distributions 282*0f4c859eSApple OSS Distributions /* We got the lock, enqueue the element */ 283*0f4c859eSApple OSS Distributions 284*0f4c859eSApple OSS DistributionsLtrylock_enqueue_success: 285*0f4c859eSApple OSS Distributions ldr x10, [x0, #8] // x10 = tail of the queue 286*0f4c859eSApple OSS Distributions cbnz x10, Lnon_empty_queue // tail not NULL 287*0f4c859eSApple OSS Distributions str x1, [x0] // Set head to new element 288*0f4c859eSApple OSS Distributions b Lset_new_tail 289*0f4c859eSApple OSS Distributions 290*0f4c859eSApple OSS DistributionsLnon_empty_queue: 291*0f4c859eSApple OSS Distributions str x1, [x10, x2] // Set old tail -> offset = new elem 292*0f4c859eSApple OSS Distributions 293*0f4c859eSApple OSS DistributionsLset_new_tail: 294*0f4c859eSApple OSS Distributions str x1, [x0, #8] // Set tail = new elem 295*0f4c859eSApple OSS Distributions 296*0f4c859eSApple OSS Distributions // Drop spin lock with release barrier (pairs with acquire in casa) 297*0f4c859eSApple OSS Distributions stlr wzr, [x3] 298*0f4c859eSApple OSS Distributions 299*0f4c859eSApple OSS Distributions mov x0, xzr // Mark success 300*0f4c859eSApple OSS Distributions 301*0f4c859eSApple OSS DistributionsLtrylock_enqueue_exit: 302*0f4c859eSApple OSS Distributions POP_FRAME 303*0f4c859eSApple OSS Distributions ARM64_STACK_EPILOG 304*0f4c859eSApple OSS Distributions 305*0f4c859eSApple OSS Distributions/* Non-preemptible helper routine to FIFO dequeue: 306*0f4c859eSApple OSS Distributions * void *pfz_trylock_and_dequeue(OSFifoQueueHead *__list, size_t __offset, uint32_t *lock_addr); 307*0f4c859eSApple OSS Distributions * 308*0f4c859eSApple OSS Distributions * x0 = pointer to queue head structure 309*0f4c859eSApple OSS Distributions * x1 = pointer to new element to enqueue 310*0f4c859eSApple OSS Distributions * x2 = address of lock 311*0f4c859eSApple OSS Distributions * 312*0f4c859eSApple OSS Distributions * Only caller save registers (x9 - x15) are used in this function 313*0f4c859eSApple OSS Distributions * 314*0f4c859eSApple OSS Distributions * Returns -1 on failure, and the pointer on success (can be NULL) 315*0f4c859eSApple OSS Distributions */ 316*0f4c859eSApple OSS Distributions .globl _pfz_trylock_and_dequeue 317*0f4c859eSApple OSS Distributions .align 2 318*0f4c859eSApple OSS Distributions_pfz_trylock_and_dequeue: 319*0f4c859eSApple OSS Distributions ARM64_STACK_PROLOG 320*0f4c859eSApple OSS Distributions PUSH_FRAME 321*0f4c859eSApple OSS Distributions 322*0f4c859eSApple OSS Distributions // Try to grab the lock 323*0f4c859eSApple OSS Distributions mov w10, wzr // unlock value = w10 = 0 324*0f4c859eSApple OSS Distributions mov w11, #1 // locked value = w11 = 1 325*0f4c859eSApple OSS Distributions 326*0f4c859eSApple OSS Distributions casa w10, w11, [x2] // Atomic CAS with acquire barrier 327*0f4c859eSApple OSS Distributions cbz w10, Ltrylock_dequeue_success 328*0f4c859eSApple OSS Distributions 329*0f4c859eSApple OSS Distributions mov x0, #-1 // Failed 330*0f4c859eSApple OSS Distributions b Ltrylock_dequeue_exit 331*0f4c859eSApple OSS Distributions 332*0f4c859eSApple OSS Distributions /* We got the lock, dequeue the element */ 333*0f4c859eSApple OSS DistributionsLtrylock_dequeue_success: 334*0f4c859eSApple OSS Distributions ldr x10, [x0] // x10 = head of the queue 335*0f4c859eSApple OSS Distributions cbz x10, Lreturn_head // if head is null, return 336*0f4c859eSApple OSS Distributions 337*0f4c859eSApple OSS Distributions ldr x11, [x10, x1] // get ptr to new head 338*0f4c859eSApple OSS Distributions cbnz x11, Lupdate_new_head // If new head != NULL, then not singleton. Only need to update head 339*0f4c859eSApple OSS Distributions 340*0f4c859eSApple OSS Distributions // Singleton case 341*0f4c859eSApple OSS Distributions str xzr, [x0, #8] // dequeuing from singleton queue, update tail to NULL 342*0f4c859eSApple OSS Distributions 343*0f4c859eSApple OSS DistributionsLupdate_new_head: 344*0f4c859eSApple OSS Distributions str xzr, [x10, x1] // zero the link in the old head 345*0f4c859eSApple OSS Distributions str x11, [x0] // Set up a new head 346*0f4c859eSApple OSS Distributions 347*0f4c859eSApple OSS DistributionsLreturn_head: 348*0f4c859eSApple OSS Distributions mov x0, x10 // Move head to x0 349*0f4c859eSApple OSS Distributions stlr wzr, [x2] // Drop spin lock with release barrier (pairs with acquire in casa) 350*0f4c859eSApple OSS Distributions 351*0f4c859eSApple OSS DistributionsLtrylock_dequeue_exit: 352*0f4c859eSApple OSS Distributions POP_FRAME 353*0f4c859eSApple OSS Distributions ARM64_STACK_EPILOG 354*0f4c859eSApple OSS Distributions 355*0f4c859eSApple OSS Distributions 356*0f4c859eSApple OSS Distributions /* Preemptible functions */ 357*0f4c859eSApple OSS Distributions .private_extern _commpage_text_preemptible_functions 358*0f4c859eSApple OSS Distributions_commpage_text_preemptible_functions: 359*0f4c859eSApple OSS Distributions 360*0f4c859eSApple OSS Distributions 361*0f4c859eSApple OSS Distributions/* 362*0f4c859eSApple OSS Distributions * void pfz_enqueue(OSFifoQueueHead *__list, void *__new, size_t __offset); 363*0f4c859eSApple OSS Distributions * x0 = pointer to queue head 364*0f4c859eSApple OSS Distributions * x1 = pointer to new elem to enqueue 365*0f4c859eSApple OSS Distributions * x2 = offset of link field inside element 366*0f4c859eSApple OSS Distributions */ 367*0f4c859eSApple OSS Distributions .globl _pfz_enqueue 368*0f4c859eSApple OSS Distributions 369*0f4c859eSApple OSS Distributions .align 2 370*0f4c859eSApple OSS Distributions_pfz_enqueue: 371*0f4c859eSApple OSS Distributions ARM64_STACK_PROLOG 372*0f4c859eSApple OSS Distributions PUSH_FRAME 373*0f4c859eSApple OSS Distributions 374*0f4c859eSApple OSS Distributions str xzr, [x1, x2] // Zero the forward link in the new element 375*0f4c859eSApple OSS Distributions mov x15, xzr // zero out the register used to communicate with kernel 376*0f4c859eSApple OSS Distributions 377*0f4c859eSApple OSS Distributions add x3, x0, #16 // address of lock = x3 = x0 + 16 378*0f4c859eSApple OSS DistributionsLenqueue_trylock_loop: 379*0f4c859eSApple OSS Distributions 380*0f4c859eSApple OSS Distributions // Attempt #1 381*0f4c859eSApple OSS Distributions TRYLOCK_ENQUEUE x9 382*0f4c859eSApple OSS Distributions PREEMPT_SELF_THEN Lenqueue_determine_success 383*0f4c859eSApple OSS Distributions 384*0f4c859eSApple OSS DistributionsLenqueue_determine_success: 385*0f4c859eSApple OSS Distributions 386*0f4c859eSApple OSS Distributions cbz x9, Lenqueue_success // did we succeed? if so, exit 387*0f4c859eSApple OSS Distributions 388*0f4c859eSApple OSS Distributions ldxr w9, [x3] // arm the monitor for the lock address 389*0f4c859eSApple OSS Distributions cbz w9, Lenqueue_clear_monitor // lock is available, retry. 390*0f4c859eSApple OSS Distributions 391*0f4c859eSApple OSS Distributions wfe // Wait with monitor armed 392*0f4c859eSApple OSS Distributions 393*0f4c859eSApple OSS Distributions // Attempt #2 394*0f4c859eSApple OSS Distributions TRYLOCK_ENQUEUE x9 395*0f4c859eSApple OSS Distributions cbz x9, Lenqueue_take_delayed_preemption_upon_success // did we succeed? if so, exit 396*0f4c859eSApple OSS Distributions 397*0f4c859eSApple OSS Distributions // We failed twice - backoff then try again 398*0f4c859eSApple OSS Distributions 399*0f4c859eSApple OSS Distributions BACKOFF x3 400*0f4c859eSApple OSS Distributions b Lenqueue_trylock_loop 401*0f4c859eSApple OSS Distributions 402*0f4c859eSApple OSS DistributionsLenqueue_clear_monitor: 403*0f4c859eSApple OSS Distributions clrex // Pairs with the ldxr 404*0f4c859eSApple OSS Distributions 405*0f4c859eSApple OSS Distributions // Take a preemption if needed then branch to enqueue_trylock_loop 406*0f4c859eSApple OSS Distributions PREEMPT_SELF_THEN Lenqueue_trylock_loop 407*0f4c859eSApple OSS Distributions 408*0f4c859eSApple OSS DistributionsLenqueue_take_delayed_preemption_upon_success: 409*0f4c859eSApple OSS Distributions PREEMPT_SELF_THEN Lenqueue_success 410*0f4c859eSApple OSS Distributions 411*0f4c859eSApple OSS DistributionsLenqueue_success: 412*0f4c859eSApple OSS Distributions POP_FRAME 413*0f4c859eSApple OSS Distributions ARM64_STACK_EPILOG 414*0f4c859eSApple OSS Distributions 415*0f4c859eSApple OSS Distributions/* 416*0f4c859eSApple OSS Distributions * void *pfz_dequeue(OSFifoQueueHead *__list, size_t __offset); 417*0f4c859eSApple OSS Distributions * x0 = pointer to queue head 418*0f4c859eSApple OSS Distributions * x1 = offset of link field inside element 419*0f4c859eSApple OSS Distributions * 420*0f4c859eSApple OSS Distributions * This function is not in the PFZ but calls out to a helper which is in the PFZ 421*0f4c859eSApple OSS Distributions * (_pfz_trylock_and_dequeue) 422*0f4c859eSApple OSS Distributions */ 423*0f4c859eSApple OSS Distributions .globl _pfz_dequeue 424*0f4c859eSApple OSS Distributions .align 2 425*0f4c859eSApple OSS Distributions_pfz_dequeue: 426*0f4c859eSApple OSS Distributions ARM64_STACK_PROLOG 427*0f4c859eSApple OSS Distributions PUSH_FRAME 428*0f4c859eSApple OSS Distributions 429*0f4c859eSApple OSS Distributions mov x15, xzr // zero out the register used to communicate with kernel 430*0f4c859eSApple OSS Distributions 431*0f4c859eSApple OSS Distributions add x2, x0, #16 // address of lock = x2 = x0 + 16 432*0f4c859eSApple OSS DistributionsLdequeue_trylock_loop: 433*0f4c859eSApple OSS Distributions 434*0f4c859eSApple OSS Distributions // Attempt #1 435*0f4c859eSApple OSS Distributions TRYLOCK_DEQUEUE x9 436*0f4c859eSApple OSS Distributions PREEMPT_SELF_THEN Ldequeue_determine_success 437*0f4c859eSApple OSS Distributions 438*0f4c859eSApple OSS DistributionsLdequeue_determine_success: 439*0f4c859eSApple OSS Distributions cmp x9, #-1 // is result of dequeue == -1? 440*0f4c859eSApple OSS Distributions b.ne Ldequeue_success // no, we succeeded 441*0f4c859eSApple OSS Distributions 442*0f4c859eSApple OSS Distributions ldxr w9, [x2] // arm the monitor for the lock address 443*0f4c859eSApple OSS Distributions cbz w9, Ldequeue_clear_monitor // lock is available, retry. 444*0f4c859eSApple OSS Distributions 445*0f4c859eSApple OSS Distributions wfe // Wait with monitor armed 446*0f4c859eSApple OSS Distributions 447*0f4c859eSApple OSS Distributions // Attempt #2 448*0f4c859eSApple OSS Distributions TRYLOCK_DEQUEUE x9 449*0f4c859eSApple OSS Distributions cmp x9, #-1 // did we fail? 450*0f4c859eSApple OSS Distributions b.ne Ldequeue_take_delayed_preemption_upon_success // no, we succeeded 451*0f4c859eSApple OSS Distributions 452*0f4c859eSApple OSS Distributions // We failed twice - backoff then try again 453*0f4c859eSApple OSS Distributions 454*0f4c859eSApple OSS Distributions BACKOFF x2 455*0f4c859eSApple OSS Distributions b Ldequeue_trylock_loop 456*0f4c859eSApple OSS Distributions 457*0f4c859eSApple OSS DistributionsLdequeue_take_delayed_preemption_upon_success: 458*0f4c859eSApple OSS Distributions // We just got here after executing PFZ code, check if we need a preemption 459*0f4c859eSApple OSS Distributions PREEMPT_SELF_THEN Ldequeue_success 460*0f4c859eSApple OSS Distributions 461*0f4c859eSApple OSS DistributionsLdequeue_clear_monitor: 462*0f4c859eSApple OSS Distributions clrex // Pairs with the ldxr 463*0f4c859eSApple OSS Distributions // Take a preemption if needed then branch to dequeue_trylock_loop. 464*0f4c859eSApple OSS Distributions PREEMPT_SELF_THEN Ldequeue_trylock_loop 465*0f4c859eSApple OSS Distributions 466*0f4c859eSApple OSS DistributionsLdequeue_success: 467*0f4c859eSApple OSS Distributions mov x0, x9 // Move x9 (where result was stored earlier) to x0 468*0f4c859eSApple OSS Distributions POP_FRAME 469*0f4c859eSApple OSS Distributions ARM64_STACK_EPILOG 470*0f4c859eSApple OSS Distributions 471*0f4c859eSApple OSS Distributions 472*0f4c859eSApple OSS Distributions/* void preempt_self(void) 473*0f4c859eSApple OSS Distributions * 474*0f4c859eSApple OSS Distributions * Make a syscall to take a preemption. This function is not in the PFZ. 475*0f4c859eSApple OSS Distributions */ 476*0f4c859eSApple OSS Distributions .align 2 477*0f4c859eSApple OSS Distributions_preempt_self: 478*0f4c859eSApple OSS Distributions ARM64_STACK_PROLOG 479*0f4c859eSApple OSS Distributions PUSH_FRAME 480*0f4c859eSApple OSS Distributions 481*0f4c859eSApple OSS Distributions // Save registers on which will be clobbered by mach trap on stack and keep 482*0f4c859eSApple OSS Distributions // it 16 byte aligned 483*0f4c859eSApple OSS Distributions stp x0, x1, [sp, #-16]! 484*0f4c859eSApple OSS Distributions 485*0f4c859eSApple OSS Distributions // Note: We don't need to caller save registers since svc will trigger an 486*0f4c859eSApple OSS Distributions // exception and kernel will save and restore register state 487*0f4c859eSApple OSS Distributions 488*0f4c859eSApple OSS Distributions // Make syscall to take delayed preemption 489*0f4c859eSApple OSS Distributions mov x16, #-58 // -58 = pfz_exit 490*0f4c859eSApple OSS Distributions svc #0x80 491*0f4c859eSApple OSS Distributions 492*0f4c859eSApple OSS Distributions // Restore registers from stack 493*0f4c859eSApple OSS Distributions ldp x0, x1, [sp], #16 494*0f4c859eSApple OSS Distributions 495*0f4c859eSApple OSS Distributions POP_FRAME 496*0f4c859eSApple OSS Distributions ARM64_STACK_EPILOG 497*0f4c859eSApple OSS Distributions 498*0f4c859eSApple OSS Distributions/* 499*0f4c859eSApple OSS Distributions * void backoff(uint32_t *lock_addr); 500*0f4c859eSApple OSS Distributions * The function returns when it observes that the lock has become available. 501*0f4c859eSApple OSS Distributions * This function is not in the PFZ. 502*0f4c859eSApple OSS Distributions * 503*0f4c859eSApple OSS Distributions * x0 = lock address 504*0f4c859eSApple OSS Distributions */ 505*0f4c859eSApple OSS Distributions .align 2 506*0f4c859eSApple OSS Distributions .globl _backoff 507*0f4c859eSApple OSS Distributions_backoff: 508*0f4c859eSApple OSS Distributions ARM64_STACK_PROLOG 509*0f4c859eSApple OSS Distributions PUSH_FRAME 510*0f4c859eSApple OSS Distributions 511*0f4c859eSApple OSS Distributions cbz x15, Lno_preempt // Kernel doesn't want to preempt us, jump to loop 512*0f4c859eSApple OSS Distributions 513*0f4c859eSApple OSS Distributions mov x15, xzr // zero out the preemption pending field 514*0f4c859eSApple OSS Distributions bl _preempt_self 515*0f4c859eSApple OSS Distributions 516*0f4c859eSApple OSS DistributionsLno_preempt: 517*0f4c859eSApple OSS Distributions ldxr w9, [x0] // Snoop on lock and arm the monitor 518*0f4c859eSApple OSS Distributions cbz w9, Lend_backoff // The lock seems to be available, return 519*0f4c859eSApple OSS Distributions 520*0f4c859eSApple OSS Distributions wfe // pause 521*0f4c859eSApple OSS Distributions 522*0f4c859eSApple OSS Distributions b Lno_preempt 523*0f4c859eSApple OSS Distributions 524*0f4c859eSApple OSS DistributionsLend_backoff: 525*0f4c859eSApple OSS Distributions clrex 526*0f4c859eSApple OSS Distributions 527*0f4c859eSApple OSS Distributions POP_FRAME 528*0f4c859eSApple OSS Distributions ARM64_STACK_EPILOG 529