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