xref: /xnu-8796.121.2/osfmk/arm/commpage/commpage_asm.s (revision c54f35ca767986246321eb901baf8f5ff7923f6a)
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