Loading...
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
/*
 * Copyright (c) 2024 Apple Inc. All rights reserved.
 *
 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
 *
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. The rights granted to you under the License
 * may not be used to create, or enable the creation or redistribution of,
 * unlawful or unlicensed copies of an Apple operating system, or to
 * circumvent, violate, or enable the circumvention or violation of, any
 * terms of an Apple operating system software license agreement.
 *
 * Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this file.
 *
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 *
 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
 */

#ifndef _VM_VM_ENTRY_RW_LOCK_H_
#define _VM_VM_ENTRY_RW_LOCK_H_

#include <stdbool.h>
#include <kern/kern_types.h>
#include <vm/vm_map_xnu.h>

/*!
 * @file vm_entry_lock_internal.h
 *
 * @discussion
 * This module implements locking for VM map entries.
 *
 * This implements a reader-writer like lock whose interlock is that of the map
 * the entry belongs to.
 *
 * This reader-writer lock is writer-biased, and supports invalidation making
 * it suitable for integration with SMR.
 */

__BEGIN_DECLS
__exported_push_hidden

/*!
 * @abstract
 * Initialize the VM entry lock for the specified entry, as an invalid lock.
 * Use this when initializing an entry that should never be locked
 * (e.g. because it's going into a copy map). This will cause any lock
 * operations to panic.
 *
 * @param entry the entry whose lock should be initialized as invalid
 * @param reason the reason the lock is invalid
 */
extern void vm_entry_lock_init_invalid(
	vm_map_entry_t          entry,
	vmel_invalid_reason_t   reason);

/*!
 * @abstract
 * Initialize the VM entry lock for a map header to be invalid. Because map
 * headers contain links structures, they technically have entry locks, but
 * these locks should never be used as the links structure doesn't actually
 * represent the links corresponding to an entry.
 *
 * @param hdr the header whose lock should be initialized as invalid
 */
void
vm_map_header_init_invalid_lock(
	struct vm_map_header *hdr);


/*!
 * @abstract
 * Initialize the VM entry lock for the specified entry,
 * with it exclusively held.
 */
extern void vm_entry_lock_init_locked_exclusive(
	vm_map_t                map,
	vm_map_entry_t          entry);


/*!
 * @abstract
 * Destroy a lock previously initialized with @c vm_entry_lock_init_invalid()
 * function or invalidated with @c vm_entry_lock_invalidate, and never
 * reinitialized since.
 *
 * @discussion
 * This call will panic if the lock is valid.
 */
extern void vm_entry_lock_destroy_invalid(
	vm_map_entry_t          entry);

/*!
 * @abstract
 * Invalidate a lock that was previously marked as valid, e.g. because it
 * belongs to a constant submap which is now being sealed. The entry must
 * currently be locked, and there must not be any waiters (to avoid deadlock).
 *
 * @param entry the entry whose lock should be invalidated
 * @param reason the reason the lock is invalid
 */
extern void vm_entry_lock_invalidate(
	vm_map_entry_t          entry,
	vmel_invalid_reason_t   reason);

/*!
 * @abstract
 * Variant of @c vm_entry_lock_invalidate which re-invalidates a lock previously
 * marked as invalid, changing the invalidation reason.
 *
 * @param entry the entry whose lock should be re-invalidated
 * @param allowed_reasons a bitmask of reasons the lock may have previously been marked invalid
 * @param new_reason the new reason the lock is invalid
 */
extern void vm_entry_lock_reinvalidate(
	vm_map_entry_t          entry,
	vmel_invalid_reason_t   allowed_reasons,
	vmel_invalid_reason_t   new_reason);

/*!
 * @abstract
 * Returns whether or not this entry's lock is valid.
 *
 * @param entry The entry whose lock to check.
 */
extern bool
vm_entry_lock_is_valid(
	vm_map_entry_t          entry);

/*!
 * @abstract
 * Returns the reason an entry's lock was invalidated.
 *
 * @param entry The entry whose lock to check.
 */
extern vmel_invalid_reason_t
vm_entry_lock_invalid_reason(
	vm_map_entry_t          entry);

/*!
 * @abstract
 * Tries to acquire the entry lock in shared mode.
 *
 * @discussion
 * This function never sleeps and is adequate to call in preemption disabled
 * contexts.
 *
 * Note that @c vm_entry_try_lock_shared() will fail if the lock has been
 * destroyed by any @c vm_entry_lock_*destroy() call.
 *
 * It is required for the caller to hold the map interlock,
 * otherwise priority inversions are possible.
 *
 * @returns             @c true if the lock was acquired,
 *                      @c false otherwise.
 */
extern bool vm_entry_try_lock_shared(
	vm_map_entry_t          entry) __result_use_check;

/*!
 * @abstract
 * Tries to acquire the entry lock in exclusive mode.
 *
 * @discussion
 * This function never sleeps and is adequate to call in preemption disabled
 * contexts.
 *
 * Note that @c vm_entry_try_lock_exclusive() will fail if the lock has been
 * destroyed by any @c vm_entry_lock_*destroy() call.
 *
 * It is required for the caller to hold the map interlock,
 * otherwise priority inversions are possible.
 *
 * @returns             @c true if the lock was acquired,
 *                      @c false otherwise.
 */
extern bool vm_entry_try_lock_exclusive(
	vm_map_entry_t          entry) __result_use_check;

/*
 * Entry lock upgrade/downgrade functions are involved in complex locking
 * operations which should stay centralized in the range lock and not
 * used by clients.
 */
#ifdef VM_MAP_LOCK_PRIVATE
/*!
 * @abstract
 * Converts an entry lock from exclusive to shared mode.
 *
 * @discussion
 * The lock must be held exclusively by the current thread,
 * otherwise this function will panic.
 */
extern void vm_entry_lock_exclusive_to_shared(
	vm_map_entry_t          entry);

/*!
 * @abstract
 * Attempts to convert an entry lock from shared to exclusive mode.
 *
 * @discussion
 * The lock must be held shared by the current thread,
 * otherwise this function will panic.
 *
 * On failure, the lock is unlocked.
 */
extern bool vm_entry_lock_try_shared_to_exclusive(
	vm_map_entry_t          entry);
#endif /* VM_MAP_LOCK_PRIVATE */

/*!
 * @abstract
 * Acquires a lock for the specified entry in shared mode.
 *
 * @discussion
 * Acquiring an entry lock might fail if the entry is being deleted,
 * or no longer contains the address the caller means to acquire
 * the lock for.
 *
 * This function must be called with the VM map interlock held, as its rwlock
 * boost is borrowed while acquiring the entry lock.
 *
 * This function might sleep.
 *
 * @param map           The map this entry belongs to.
 *                      This map interlock must be held.
 *
 * @param map_held      How the map interlock is being held.
 *
 * @param entry         The entry to lock.
 *
 * @param addr          The address the caller is interested in.
 *
 * @param how           How to wait/sleep.
 *
 * @returns
 * - KERN_SUCCESS       the lock was acquired,
 * - VMRL_ERR_ABORTED   if the sleep was interruptible and the wait aborted.
 * - VMRL_ERR_RELOOKUP  if the entry was deleted, or its address range modified,
 *                      and the caller needs to look it up again.
 */
extern kern_return_t vm_entry_lock_shared(
	vm_map_t                map,
	lck_rw_type_t           map_held,
	vm_map_entry_t          entry,
	vm_map_address_t        addr,
	wait_interrupt_t        how) __result_use_check;

/*!
 * @abstract
 * Acquires a lock for the specified entry in exclusive mode.
 *
 * @discussion
 * Acquiring an entry lock might fail if the entry is being deleted,
 * or no longer contains the address the caller means to acquire
 * the lock for.
 *
 * This function must be called with the VM map interlock held, as its rwlock
 * boost is borrowed while acquiring the entry lock.
 *
 * This function might sleep.
 *
 * @param map           The map this entry belongs to.
 *                      This map interlock must be held.
 *
 * @param map_held      How the map interlock is being held.
 *
 * @param entry         The entry to lock.
 *
 * @param addr          The address the caller is interested in.
 *
 * @param how           How to wait/sleep.
 *
 * @returns
 * - KERN_SUCCESS       the lock was acquired,
 * - VMRL_ERR_ABORTED   if the sleep was interruptible and the wait aborted.
 * - VMRL_ERR_RELOOKUP  if the entry was deleted, or its address range modified,
 *                      and the caller needs to look it up again.
 */
extern kern_return_t vm_entry_lock_exclusive(
	vm_map_t                map,
	lck_rw_type_t           map_held,
	vm_map_entry_t          entry,
	vm_map_address_t        addr,
	wait_interrupt_t        how) __result_use_check;


/*!
 * @abstract
 * Unlocks an entry that was locked in shared mode.
 *
 * @discussion
 * This function never sleeps and is safe to call from preemption disabled
 * contexts.
 *
 * The entry must have been locked successfully with @c vm_entry_lock_shared()
 * or @c vm_entry_try_lock_shared() by the current thread, otherwise this
 * call might panic.
 *
 * @param map           The map this entry belongs to.
 *
 * @param entry         The entry to unlock.
 */
extern void vm_entry_unlock_shared(
	vm_map_t                map,
	vm_map_entry_t          entry);

/*!
 * @abstract
 * Unlocks an entry that was locked in exclusive mode.
 *
 * @discussion
 * This function never sleeps and is safe to call from preemption disabled
 * contexts.
 *
 * The entry must have been locked successfully with @c vm_entry_lock_exclusive()
 * or @c vm_entry_try_lock_exclusive() by the current thread, otherwise this
 * call will panic.
 *
 * @param map           The map this entry belongs to.
 *
 * @param entry         The entry to unlock.
 */
extern void vm_entry_unlock_exclusive(
	vm_map_t                map,
	vm_map_entry_t          entry);


/*!
 * @abstract
 * Invalidate all waiters because the bounds of the entry changed.
 *
 * @discussion
 * This function never sleeps and is safe to call from preemption disabled
 * contexts.
 *
 * The entry must have been locked successfully with @c vm_entry_lock_exclusive()
 * or @c vm_entry_try_lock_exclusive() by the current thread, otherwise this
 * call will panic.
 *
 * This function should be called with the interlock held.
 *
 * @param map           The map this entry belongs to.
 *
 * @param entry         The entry to invalidate.
 */
extern void vm_entry_invalidate_waiters(
	vm_map_t                map,
	vm_map_entry_t          entry);

/*!
 * @abstract
 * Unlock and destroys a vm entry lock.
 *
 * @discussion
 * This function never sleeps and is safe to call from preemption disabled
 * contexts.
 *
 * The entry must have been locked successfully with @c vm_entry_lock_exclusive()
 * or @c vm_entry_try_lock_exclusive() by the current thread, otherwise this
 * call will panic.
 *
 * The lock will become destroyed, and all possible waiters woken up.  The state
 * of the lock will be as if @c vm_entry_lock_destroy() had been called.
 *
 * @param map           The map this entry belongs to.
 *
 * @param entry         The entry to unlock.
 */
extern void vm_entry_unlock_exclusive_and_destroy(
	vm_map_t                map,
	vm_map_entry_t          entry);

/*
 * These operations involve relatively complex locking to implement correctly.
 * Clients should rely on the range lock behavior (potentially with the
 * VMRL_SIMPLIFY flag or VMRL_ERR_WAIT_FOR_KUNWIRE preflight return code) to
 * handle these operations as appropriate.
 */
#ifdef VM_MAP_LOCK_PRIVATE
/*!
 * @abstract
 * Returns whether an entry has the needs-coalesce bit set.
 *
 * @param entry         The entry to query.
 * @returns             Whether needs-coalesce is set (true) or not (false).
 */
extern bool vm_entry_needs_coalesce(
	vm_map_entry_t          entry);

/*!
 * @abstract
 * Sets/clears the needs-coalesce bit on the entry.
 *
 * @param entry         The entry to modify.
 * @param value         Whether to set (true) or clear (false) the bit.
 */
extern void vm_entry_update_needs_coalesce(
	vm_map_entry_t          entry,
	bool                    value);


/*!
 * @abstract
 * Sleeps on an entry until vm_entry_wakeup_kunwire_waiters() is called,
 * or it is destroyed.
 *
 * @param map           The map this entry belongs to.
 *                      This map interlock must be held,
 *                      and will be unlocked on return.
 *
 * @param map_held      How the map interlock is being held.
 *
 * @param entry         The specified entry.
 *
 * @param addr          The address the caller is interested in.
 *
 * @param how           How to wait/sleep.
 *
 * @returns
 * - KERN_SUCCESS       the wait ended, and the entry is stable.
 * - VMRL_ERR_ABORTED   if the sleep was interruptible and the wait aborted.
 * - VMRL_ERR_RELOOKUP  the wait ended, and the entry was deleted, or its
 *                      address range modified, and the caller needs
 *                      to look it up again.
 */
extern kern_return_t vm_entry_unlock_and_wait_for_kunwire(
	vm_map_t                map,
	lck_rw_type_t           map_held,
	vm_map_entry_t          entry,
	vm_map_address_t        addr,
	wait_interrupt_t        how) __result_use_check;
#endif /* VM_MAP_LOCK_PRIVATE */

/*!
 * @abstract
 * Wake up all threads waiting in @c vm_entry_unlock_and_wait_for_kunwire() for
 * this entry.
 *
 * @discussion
 * This function never sleeps and is adequate to call in preemption disabled
 * contexts.
 */
extern void vm_entry_wakeup_kunwire_waiters(
	vm_map_entry_t          entry);


/*!
 * @abstract
 * Asserts that the current entry lock is valid.
 */
extern void vm_entry_assert_lock_is_valid(
	vm_map_entry_t          entry);

/*!
 * @abstract
 * Asserts that the current entry lock is invalid, and that the invalidation
 * reason matches one of the provided reasons.
 */
extern void vm_entry_assert_lock_is_invalid(
	vm_map_entry_t          entry,
	vmel_invalid_reason_t   allowed_reasons);


/*!
 * @abstract
 * Asserts that the current thread owns the lock on this entry in some form
 * (shared or exclusive), i.e. the assert may pass even though the lock is not
 * held by this thread.
 *
 * @discussion
 * Note that asserting the lock is held shared or exclusively is approximate and
 * has false positives.
 */
extern void vm_entry_assert_owner(
	vm_map_entry_t          entry);

/*!
 * @abstract
 * Asserts that the current thread owns the lock on this entry exclusively.
 *
 * @discussion
 * Note that asserting the lock is held exclusively is approximate and has false
 * positives, i.e. the assert may pass even though the lock is not held
 * exclusively by this thread. This is because (when MAP_ENTRY_LOCK_DEBUG is
 * not set), the entry lock bits do not encode the actual owner of the lock.
 */
extern void vm_entry_assert_excl_owner(
	vm_map_entry_t          entry);

/*!
 * @abstract
 * Asserts that the current thread owns the lock on this entry exclusively,
 * or that the current thread is otherwise permitted to make modification
 * to the entry's fields.
 *
 * @discussion
 * Note that asserting the lock is held exclusively is approximate and has false
 * positives, i.e. the assert may pass even though the lock is not held
 * exclusively by this thread.
 */
extern void vm_entry_assert_fields_writable(
	vm_map_entry_t          entry);

/*!
 * @abstract
 * Asserts that the current thread owns the lock on this entry in shared mode.
 *
 * @discussion
 * Note that asserting the lock is held shared is approximate and has false
 * positives, i.e. the assert may pas even though the lock is not held shared
 * by this thread. This is because the entry lock bits only encode the reader
 * count, not the list of shared owners.
 */
extern void vm_entry_assert_shared_owner(
	vm_map_entry_t          entry);

/*!
 * @abstract
 * Asserts that the entry lock on this entry is valid and that the current thread
 * doesn't owns the lock on this entry in any form (shared or exclusive).
 *
 * @discussion
 * Note that this assertion actually can't tell if the current thread
 * owns the lock in shared or exclusive mode and will have false positives.
 */
extern void vm_entry_assert_not_owner(
	vm_map_entry_t          entry);

/*!
 * @abstract
 * Returns whether the entry lock is held exclusively for the sake
 * of stackshots.
 */
extern bool kdp_vm_entry_lock_is_acquired_exclusive(
	vm_map_entry_t          entry);

/*!
 * @abstract
 * Returns the vm_map_entry associated with a kThreadWaitVMEntry* event.
 */
extern vm_map_entry_t kdp_vm_entry_from_event(
	event64_t               event,
	block_hint_t            hint);


#if MACH_ASSERT
#define VM_ENTRY_ASSERT_LOCK_VALID(entry) vm_entry_assert_lock_is_valid(entry)
#define VM_ENTRY_ASSERT_LOCK_INVALID(entry, allowed_reasons) vm_entry_assert_lock_is_invalid(entry, allowed_reasons)
#define VM_ENTRY_ASSERT_OWNER(entry) vm_entry_assert_owner(entry)
#define VM_ENTRY_ASSERT_EXCL_OWNER(entry) vm_entry_assert_excl_owner(entry)
#define VM_ENTRY_ASSERT_FIELDS_WRITABLE(entry) vm_entry_assert_fields_writable(entry)
#define VM_ENTRY_ASSERT_SHARED_OWNER(entry) vm_entry_assert_shared_owner(entry)
#define VM_ENTRY_ASSERT_NOT_OWNER(entry) vm_entry_assert_not_owner(entry)
#else /* !MACH_ASSERT */
#define VM_ENTRY_ASSERT_LOCK_VALID(entry) ((void)(entry))
#define VM_ENTRY_ASSERT_LOCK_INVALID(entry, allowed_reasons) ({(void)(entry);(void)(allowed_reasons);})
#define VM_ENTRY_ASSERT_OWNER(entry) ((void)(entry))
#define VM_ENTRY_ASSERT_EXCL_OWNER(entry) ((void)(entry))
#define VM_ENTRY_ASSERT_FIELDS_WRITABLE(entry) ((void)(entry))
#define VM_ENTRY_ASSERT_SHARED_OWNER(entry) ((void)(entry))
#define VM_ENTRY_ASSERT_NOT_OWNER(entry) ((void)(entry))
#endif /* !MACH_ASSERT */

__exported_pop
__END_DECLS

#endif /*  _VM_VM_ENTRY_RW_LOCK_H_ */