1 /* 2 * Copyright (c) 2022 Apple Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28 29 #pragma once 30 31 #include <kern/assert.h> 32 #include <kern/thread.h> 33 34 #include <os/atomic.h> 35 36 #include <stdint.h> 37 #include <stdbool.h> 38 39 /* 40 * Epoch WAIT/WAKE blocking. 41 * 42 * Epoch WAIT/WAKE provides a generic means to block and unblock on a turnstile 43 * wait queue with minimal knowledge of the underlying synchronization 44 * implementation. This is useful in environments where it's not possible to 45 * block (like exclaves). No direct access to the memory backing the 46 * synchronization primitive is required (in fact, the exclaves security model 47 * explicitly forbids this). 48 * Instead, an epoch is passed which provides an ordering and hence a means to 49 * detect out-of-order WAITs/WAKEs. 50 * 51 * An epoch is a counter value. The epoch is incremented before calling WAKE 52 * (for example when releasing a lock). Each ID maps to an associated counter. 53 * Counters may be shared by multiple IDs. To avoid blocking when an owner has 54 * already released its resource, the epoch is checked for freshness. A stale 55 * epoch causes WAIT to immediately return without blocking. Too much sharing of 56 * counters can cause waiters to return when they could have blocked. 57 * 58 * There are two major constraints for callers of these APIs: 59 * 60 * 1. The kernel's idea of the owning thread must be kept up-to-date 61 * 62 * - On WAIT, the owner becomes the thread described by 'owner'. 63 * - On WAKE, the owner is the woken thread (iff it's not the last waiter) 64 * 65 * If the owning thread doesn't call back into WAIT or WAKE again it may run 66 * with elevated privileges when it shouldn't. 67 * 68 * 2. WAITs which result in a thread blocking must be woken with WAKE 69 * This one is somewhat obvious. The only way for a thread blocked in WAIT to 70 * come back is to be woken with a WAKE. Pre-posted wakes (wakes when there is 71 * no waiter) or stale waits (out of date epoch) return immediately. 72 * 73 */ 74 75 __BEGIN_DECLS 76 77 /* 78 * @enum esync_space_t 79 * 80 * @abstract Identifies the epoch sync space. 81 * 82 * @constant ESYNC_SPACE_EXCLAVES_Q 83 * Exclaves queues. 84 * 85 * @constant ESYNC_SPACE_EXCLAVES_T 86 * Exclaves threads. 87 */ 88 typedef enum __enum_closed { 89 ESYNC_SPACE_TEST = 0, 90 ESYNC_SPACE_EXCLAVES_Q = 1, 91 ESYNC_SPACE_EXCLAVES_T = 2, 92 93 ESYNC_SPACE_MAX = ESYNC_SPACE_EXCLAVES_T 94 } esync_space_t; 95 static_assert(ESYNC_SPACE_MAX < (1 << 8)); 96 97 /*! 98 * @enum esync_policy_t 99 * 100 * @abstract Constants defining the policy associated with a synchronization 101 * object. 102 * 103 * @constant ESYNC_POLICY_NONE 104 * Unspecified. 105 * 106 * @constant ESYNC_POLICY_USER 107 * User. 108 * 109 * @constant ESYNC_POLICY_KERNEL 110 * Kernel. 111 */ 112 typedef enum __enum_closed { 113 ESYNC_POLICY_NONE = 0, 114 ESYNC_POLICY_USER = 1, 115 ESYNC_POLICY_KERNEL = 2, 116 } esync_policy_t; 117 118 /*! 119 * @function esync_wait 120 * 121 * @abstract 122 * Wait on a turnstile associated with the specified id 123 * 124 * @param space 125 * Namespace in which 'id' lives 126 * 127 * @param id 128 * Synchronization object identifier 129 * 130 * @param epoch 131 * Latest epoch of the synchronization object 132 * 133 * @param owner_ctid 134 * Owner of the synchronization object 135 * 136 * @param interruptible 137 * Interruptible flag 138 * 139 * @param policy 140 * A user or kernel synchronization object 141 * 142 * @return 143 * Result of blocking call (or THREAD_NOT_WAITING for pre-posted waits) 144 */ 145 extern wait_result_t esync_wait(esync_space_t space, uint64_t id, 146 uint64_t epoch, os_atomic(uint64_t) * counter, ctid_t owner_ctid, 147 esync_policy_t policy, wait_interrupt_t interruptible); 148 149 /*! 150 * @enum esync_wake_mode_t 151 * 152 * @abstract Constants defining modes for esync_wake 153 * 154 * @constant ESYNC_WAKE_ONE 155 * Wake a single waiter 156 * 157 * @constant ESYNC_WAKE_ALL 158 * Wake all waiters 159 * 160 * @constant ESYNC_WAKE_ONE_WITH_OWNER 161 * Wake a single owner and identify the new owner 162 * 163 * @constant ESYNC_WAKE_THREAD 164 * Wake the specified thread. There is no new owner. 165 */ 166 typedef enum __enum_closed { 167 ESYNC_WAKE_ONE = 1, 168 ESYNC_WAKE_ALL = 2, 169 ESYNC_WAKE_ONE_WITH_OWNER = 3, 170 ESYNC_WAKE_THREAD = 4, 171 } esync_wake_mode_t; 172 173 /*! 174 * @function esync_wake 175 * 176 * @abstract 177 * Wake one or more threads which have blocked on the specified id in esync_wait 178 * 179 * @param space 180 * Namespace in which 'id' lives 181 * 182 * @param id 183 * Synchronization object identifier 184 * 185 * @param epoch 186 * Latest epoch of the synchronization object 187 * 188 * @param mode 189 * Type of wake to perform. All, one or one with specified owner (new 190 * inheritor). 191 * 192 * @param ctid 193 * Thread identifier. Can identifier the new owner (ESYNC_WAKE_ONE_WITH_OWNER) 194 * or the thread to be woken (ESYNC_WAKE_THREAD). 195 * 196 * @return 197 * KERN_SUCCESS or KERN_NOT_WAITING if no thread was woken 198 */ 199 extern kern_return_t esync_wake(esync_space_t space, uint64_t id, 200 uint64_t epoch, os_atomic(uint64_t) * counter, esync_wake_mode_t mode, 201 ctid_t ctid); 202 203 __END_DECLS 204