]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/sys_reason.c
xnu-7195.60.75.tar.gz
[apple/xnu.git] / bsd / kern / sys_reason.c
1 /*
2 * Copyright (c) 2015-2020 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 #include <kern/kalloc.h>
29 #include <kern/locks.h>
30 #include <sys/kernel.h>
31 #include <sys/kernel_types.h>
32 #include <kern/zalloc.h>
33 #include <sys/reason.h>
34 #include <string.h>
35 #include <kern/assert.h>
36 #include <kern/debug.h>
37
38 #if OS_REASON_DEBUG
39 #include <pexpert/pexpert.h>
40
41 extern int os_reason_debug_disabled;
42 #endif
43
44 extern int maxproc;
45
46 /*
47 * Lock group attributes for os_reason subsystem
48 */
49 static LCK_GRP_DECLARE(os_reason_lock_grp, "os_reason_lock");
50 static ZONE_DECLARE(os_reason_zone, "os reasons",
51 sizeof(struct os_reason), ZC_ZFREE_CLEARMEM);
52
53 os_refgrp_decl(static, os_reason_refgrp, "os_reason", NULL);
54
55 #define OS_REASON_RESERVE_COUNT 100
56
57 static int os_reason_alloc_buffer_internal(os_reason_t cur_reason, uint32_t osr_bufsize,
58 zalloc_flags_t flags);
59
60 void
61 os_reason_init(void)
62 {
63 int reasons_allocated = 0;
64
65 /*
66 * We pre-fill the OS reason zone to reduce the likelihood that
67 * the jetsam thread and others block when they create an exit
68 * reason.
69 */
70 reasons_allocated = zfill(os_reason_zone, OS_REASON_RESERVE_COUNT);
71 assert(reasons_allocated >= OS_REASON_RESERVE_COUNT);
72 }
73
74 /*
75 * Creates a new reason and initializes it with the provided reason
76 * namespace and code. Also sets up the buffer and kcdata_descriptor
77 * associated with the reason. Returns a pointer to the newly created
78 * reason.
79 *
80 * Returns:
81 * REASON_NULL if unable to allocate a reason or initialize the nested buffer
82 * a pointer to the reason otherwise
83 */
84 os_reason_t
85 os_reason_create(uint32_t osr_namespace, uint64_t osr_code)
86 {
87 os_reason_t new_reason;
88
89 new_reason = zalloc_flags(os_reason_zone, Z_WAITOK | Z_ZERO);
90 new_reason->osr_namespace = osr_namespace;
91 new_reason->osr_code = osr_code;
92 lck_mtx_init(&new_reason->osr_lock, &os_reason_lock_grp, LCK_ATTR_NULL);
93 os_ref_init(&new_reason->osr_refcount, &os_reason_refgrp);
94
95 return new_reason;
96 }
97
98 static void
99 os_reason_dealloc_buffer(os_reason_t cur_reason)
100 {
101 assert(cur_reason != OS_REASON_NULL);
102 LCK_MTX_ASSERT(&cur_reason->osr_lock, LCK_MTX_ASSERT_OWNED);
103
104 if (cur_reason->osr_kcd_buf != NULL && cur_reason->osr_bufsize != 0) {
105 kheap_free(KHEAP_DATA_BUFFERS, cur_reason->osr_kcd_buf,
106 cur_reason->osr_bufsize);
107 }
108
109 cur_reason->osr_bufsize = 0;
110 cur_reason->osr_kcd_buf = NULL;
111 bzero(&cur_reason->osr_kcd_descriptor, sizeof(cur_reason->osr_kcd_descriptor));
112 }
113
114 /*
115 * Allocates and initializes a buffer of specified size for the reason. This function
116 * may block and should not be called from extremely performance sensitive contexts
117 * (i.e. jetsam). Also initializes the kcdata descriptor accordingly. If there is an
118 * existing buffer, we dealloc the buffer before allocating a new one and
119 * clear the associated kcdata descriptor. If osr_bufsize is passed as 0,
120 * we deallocate the existing buffer and then return.
121 *
122 * Returns:
123 * 0 on success
124 * EINVAL if the passed reason pointer is invalid or the requested size is
125 * larger than REASON_BUFFER_MAX_SIZE
126 * EIO if we fail to initialize the kcdata buffer
127 */
128 int
129 os_reason_alloc_buffer(os_reason_t cur_reason, uint32_t osr_bufsize)
130 {
131 return os_reason_alloc_buffer_internal(cur_reason, osr_bufsize, Z_WAITOK);
132 }
133
134 /*
135 * Allocates and initializes a buffer of specified size for the reason. Also
136 * initializes the kcdata descriptor accordingly. If there is an existing
137 * buffer, we dealloc the buffer before allocating a new one and
138 * clear the associated kcdata descriptor. If osr_bufsize is passed as 0,
139 * we deallocate the existing buffer and then return.
140 *
141 * Returns:
142 * 0 on success
143 * EINVAL if the passed reason pointer is invalid or the requested size is
144 * larger than REASON_BUFFER_MAX_SIZE
145 * ENOMEM if unable to allocate memory for the buffer
146 * EIO if we fail to initialize the kcdata buffer
147 */
148 int
149 os_reason_alloc_buffer_noblock(os_reason_t cur_reason, uint32_t osr_bufsize)
150 {
151 return os_reason_alloc_buffer_internal(cur_reason, osr_bufsize, Z_NOWAIT);
152 }
153
154 static int
155 os_reason_alloc_buffer_internal(os_reason_t cur_reason, uint32_t osr_bufsize,
156 zalloc_flags_t flags)
157 {
158 if (cur_reason == OS_REASON_NULL) {
159 return EINVAL;
160 }
161
162 if (osr_bufsize > OS_REASON_BUFFER_MAX_SIZE) {
163 return EINVAL;
164 }
165
166 lck_mtx_lock(&cur_reason->osr_lock);
167
168 os_reason_dealloc_buffer(cur_reason);
169
170 if (osr_bufsize == 0) {
171 lck_mtx_unlock(&cur_reason->osr_lock);
172 return 0;
173 }
174
175 cur_reason->osr_kcd_buf = kheap_alloc_tag(KHEAP_DATA_BUFFERS, osr_bufsize,
176 flags | Z_ZERO, VM_KERN_MEMORY_REASON);
177
178 if (cur_reason->osr_kcd_buf == NULL) {
179 lck_mtx_unlock(&cur_reason->osr_lock);
180 return ENOMEM;
181 }
182
183 cur_reason->osr_bufsize = osr_bufsize;
184
185 if (kcdata_memory_static_init(&cur_reason->osr_kcd_descriptor,
186 (mach_vm_address_t)cur_reason->osr_kcd_buf,
187 KCDATA_BUFFER_BEGIN_OS_REASON, osr_bufsize, KCFLAG_USE_MEMCOPY) !=
188 KERN_SUCCESS) {
189 os_reason_dealloc_buffer(cur_reason);
190
191 lck_mtx_unlock(&cur_reason->osr_lock);
192 return EIO;
193 }
194
195 lck_mtx_unlock(&cur_reason->osr_lock);
196
197 return 0;
198 }
199
200 /*
201 * Returns a pointer to the kcdata descriptor associated with the specified
202 * reason if there is a buffer allocated.
203 */
204 struct kcdata_descriptor *
205 os_reason_get_kcdata_descriptor(os_reason_t cur_reason)
206 {
207 if (cur_reason == OS_REASON_NULL) {
208 return NULL;
209 }
210
211 if (cur_reason->osr_kcd_buf == NULL) {
212 return NULL;
213 }
214
215 assert(cur_reason->osr_kcd_descriptor.kcd_addr_begin ==
216 (mach_vm_address_t)cur_reason->osr_kcd_buf);
217 if (cur_reason->osr_kcd_descriptor.kcd_addr_begin !=
218 (mach_vm_address_t)cur_reason->osr_kcd_buf) {
219 return NULL;
220 }
221
222 return &cur_reason->osr_kcd_descriptor;
223 }
224
225 /*
226 * Takes a reference on the passed reason.
227 */
228 void
229 os_reason_ref(os_reason_t cur_reason)
230 {
231 if (cur_reason == OS_REASON_NULL) {
232 return;
233 }
234
235 lck_mtx_lock(&cur_reason->osr_lock);
236 os_ref_retain_locked(&cur_reason->osr_refcount);
237 lck_mtx_unlock(&cur_reason->osr_lock);
238 return;
239 }
240
241 /*
242 * Drops a reference on the passed reason, deallocates
243 * the reason if no references remain.
244 */
245 void
246 os_reason_free(os_reason_t cur_reason)
247 {
248 if (cur_reason == OS_REASON_NULL) {
249 return;
250 }
251
252 lck_mtx_lock(&cur_reason->osr_lock);
253
254 if (os_ref_release_locked(&cur_reason->osr_refcount) > 0) {
255 lck_mtx_unlock(&cur_reason->osr_lock);
256 return;
257 }
258
259 os_reason_dealloc_buffer(cur_reason);
260
261 lck_mtx_unlock(&cur_reason->osr_lock);
262 lck_mtx_destroy(&cur_reason->osr_lock, &os_reason_lock_grp);
263
264 zfree(os_reason_zone, cur_reason);
265 }
266
267 /*
268 * Sets flags on the passed reason.
269 */
270 void
271 os_reason_set_flags(os_reason_t cur_reason, uint64_t flags)
272 {
273 if (cur_reason == OS_REASON_NULL) {
274 return;
275 }
276
277 lck_mtx_lock(&cur_reason->osr_lock);
278 cur_reason->osr_flags = flags;
279 lck_mtx_unlock(&cur_reason->osr_lock);
280 }
281
282 /*
283 * Allocates space and sets description data in kcd_descriptor on the passed reason.
284 */
285 void
286 os_reason_set_description_data(os_reason_t cur_reason, uint32_t type, void *reason_data, uint32_t reason_data_len)
287 {
288 mach_vm_address_t osr_data_addr = 0;
289
290 if (cur_reason == OS_REASON_NULL) {
291 return;
292 }
293
294 if (0 != os_reason_alloc_buffer(cur_reason, kcdata_estimate_required_buffer_size(1, reason_data_len))) {
295 panic("os_reason failed to allocate");
296 }
297
298 lck_mtx_lock(&cur_reason->osr_lock);
299 if (KERN_SUCCESS != kcdata_get_memory_addr(&cur_reason->osr_kcd_descriptor, type, reason_data_len, &osr_data_addr)) {
300 panic("os_reason failed to get data address");
301 }
302 if (KERN_SUCCESS != kcdata_memcpy(&cur_reason->osr_kcd_descriptor, osr_data_addr, reason_data, reason_data_len)) {
303 panic("os_reason failed to copy description data");
304 }
305 lck_mtx_unlock(&cur_reason->osr_lock);
306 }