xref: /xnu-10002.61.3/osfmk/arm/commpage/commpage_asm.s (revision 0f4c859e951fba394238ab619495c4e1d54d0f34) !
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