]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/sys_reason.c
xnu-6153.81.5.tar.gz
[apple/xnu.git] / bsd / kern / sys_reason.c
1 /*
2 * Copyright (c) 2015 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 lck_grp_attr_t *os_reason_lock_grp_attr;
50 lck_grp_t *os_reason_lock_grp;
51 lck_attr_t *os_reason_lock_attr;
52
53 os_refgrp_decl(static, os_reason_refgrp, "os_reason", NULL);
54
55 #define OS_REASON_RESERVE_COUNT 100
56 #define OS_REASON_MAX_COUNT (maxproc + 100)
57
58 static struct zone *os_reason_zone;
59 static int os_reason_alloc_buffer_internal(os_reason_t cur_reason, uint32_t osr_bufsize,
60 boolean_t can_block);
61
62 void
63 os_reason_init()
64 {
65 int reasons_allocated = 0;
66
67 /*
68 * Initialize OS reason group and lock attributes
69 */
70 os_reason_lock_grp_attr = lck_grp_attr_alloc_init();
71 os_reason_lock_grp = lck_grp_alloc_init("os_reason_lock", os_reason_lock_grp_attr);
72 os_reason_lock_attr = lck_attr_alloc_init();
73
74 /*
75 * Create OS reason zone.
76 */
77 os_reason_zone = zinit(sizeof(struct os_reason), OS_REASON_MAX_COUNT * sizeof(struct os_reason),
78 OS_REASON_MAX_COUNT, "os reasons");
79 if (os_reason_zone == NULL) {
80 panic("failed to initialize os_reason_zone");
81 }
82
83 /*
84 * We pre-fill the OS reason zone to reduce the likelihood that
85 * the jetsam thread and others block when they create an exit
86 * reason. This pre-filled memory is not-collectable since it's
87 * foreign memory crammed in as part of zfill().
88 */
89 reasons_allocated = zfill(os_reason_zone, OS_REASON_RESERVE_COUNT);
90 assert(reasons_allocated > 0);
91 }
92
93 /*
94 * Creates a new reason and initializes it with the provided reason
95 * namespace and code. Also sets up the buffer and kcdata_descriptor
96 * associated with the reason. Returns a pointer to the newly created
97 * reason.
98 *
99 * Returns:
100 * REASON_NULL if unable to allocate a reason or initialize the nested buffer
101 * a pointer to the reason otherwise
102 */
103 os_reason_t
104 os_reason_create(uint32_t osr_namespace, uint64_t osr_code)
105 {
106 os_reason_t new_reason = OS_REASON_NULL;
107
108 new_reason = (os_reason_t) zalloc(os_reason_zone);
109 if (new_reason == OS_REASON_NULL) {
110 #if OS_REASON_DEBUG
111 /*
112 * We rely on OS reasons to communicate important things such
113 * as process exit reason information, we should be aware
114 * when issues prevent us from allocating them.
115 */
116 if (os_reason_debug_disabled) {
117 kprintf("os_reason_create: failed to allocate reason with namespace: %u, code : %llu\n",
118 osr_namespace, osr_code);
119 } else {
120 panic("os_reason_create: failed to allocate reason with namespace: %u, code: %llu\n",
121 osr_namespace, osr_code);
122 }
123 #endif
124 return new_reason;
125 }
126
127 bzero(new_reason, sizeof(*new_reason));
128
129 new_reason->osr_namespace = osr_namespace;
130 new_reason->osr_code = osr_code;
131 new_reason->osr_flags = 0;
132 new_reason->osr_bufsize = 0;
133 new_reason->osr_kcd_buf = NULL;
134
135 lck_mtx_init(&new_reason->osr_lock, os_reason_lock_grp, os_reason_lock_attr);
136 os_ref_init(&new_reason->osr_refcount, &os_reason_refgrp);
137
138 return new_reason;
139 }
140
141 static void
142 os_reason_dealloc_buffer(os_reason_t cur_reason)
143 {
144 assert(cur_reason != OS_REASON_NULL);
145 LCK_MTX_ASSERT(&cur_reason->osr_lock, LCK_MTX_ASSERT_OWNED);
146
147 if (cur_reason->osr_kcd_buf != NULL && cur_reason->osr_bufsize != 0) {
148 kfree(cur_reason->osr_kcd_buf, cur_reason->osr_bufsize);
149 }
150
151 cur_reason->osr_bufsize = 0;
152 cur_reason->osr_kcd_buf = NULL;
153 bzero(&cur_reason->osr_kcd_descriptor, sizeof(cur_reason->osr_kcd_descriptor));
154
155 return;
156 }
157
158 /*
159 * Allocates and initializes a buffer of specified size for the reason. This function
160 * may block and should not be called from extremely performance sensitive contexts
161 * (i.e. jetsam). Also initializes the kcdata descriptor accordingly. If there is an
162 * existing buffer, we dealloc the buffer before allocating a new one and
163 * clear the associated kcdata descriptor. If osr_bufsize is passed as 0,
164 * we deallocate the existing buffer and then return.
165 *
166 * Returns:
167 * 0 on success
168 * EINVAL if the passed reason pointer is invalid or the requested size is
169 * larger than REASON_BUFFER_MAX_SIZE
170 * EIO if we fail to initialize the kcdata buffer
171 */
172 int
173 os_reason_alloc_buffer(os_reason_t cur_reason, uint32_t osr_bufsize)
174 {
175 return os_reason_alloc_buffer_internal(cur_reason, osr_bufsize, TRUE);
176 }
177
178 /*
179 * Allocates and initializes a buffer of specified size for the reason. Also
180 * initializes the kcdata descriptor accordingly. If there is an existing
181 * buffer, we dealloc the buffer before allocating a new one and
182 * clear the associated kcdata descriptor. If osr_bufsize is passed as 0,
183 * we deallocate the existing buffer and then return.
184 *
185 * Returns:
186 * 0 on success
187 * EINVAL if the passed reason pointer is invalid or the requested size is
188 * larger than REASON_BUFFER_MAX_SIZE
189 * ENOMEM if unable to allocate memory for the buffer
190 * EIO if we fail to initialize the kcdata buffer
191 */
192 int
193 os_reason_alloc_buffer_noblock(os_reason_t cur_reason, uint32_t osr_bufsize)
194 {
195 return os_reason_alloc_buffer_internal(cur_reason, osr_bufsize, FALSE);
196 }
197
198 static int
199 os_reason_alloc_buffer_internal(os_reason_t cur_reason, uint32_t osr_bufsize,
200 boolean_t can_block)
201 {
202 if (cur_reason == OS_REASON_NULL) {
203 return EINVAL;
204 }
205
206 if (osr_bufsize > OS_REASON_BUFFER_MAX_SIZE) {
207 return EINVAL;
208 }
209
210 lck_mtx_lock(&cur_reason->osr_lock);
211
212 os_reason_dealloc_buffer(cur_reason);
213
214 if (osr_bufsize == 0) {
215 lck_mtx_unlock(&cur_reason->osr_lock);
216 return 0;
217 }
218
219 if (can_block) {
220 cur_reason->osr_kcd_buf = kalloc_tag(osr_bufsize, VM_KERN_MEMORY_REASON);
221 assert(cur_reason->osr_kcd_buf != NULL);
222 } else {
223 cur_reason->osr_kcd_buf = kalloc_noblock_tag(osr_bufsize, VM_KERN_MEMORY_REASON);
224 if (cur_reason->osr_kcd_buf == NULL) {
225 lck_mtx_unlock(&cur_reason->osr_lock);
226 return ENOMEM;
227 }
228 }
229
230 bzero(cur_reason->osr_kcd_buf, osr_bufsize);
231
232 cur_reason->osr_bufsize = osr_bufsize;
233
234 if (kcdata_memory_static_init(&cur_reason->osr_kcd_descriptor, (mach_vm_address_t) cur_reason->osr_kcd_buf,
235 KCDATA_BUFFER_BEGIN_OS_REASON, osr_bufsize, KCFLAG_USE_MEMCOPY) != KERN_SUCCESS) {
236 os_reason_dealloc_buffer(cur_reason);
237
238 lck_mtx_unlock(&cur_reason->osr_lock);
239 return EIO;
240 }
241
242 lck_mtx_unlock(&cur_reason->osr_lock);
243
244 return 0;
245 }
246
247 /*
248 * Returns a pointer to the kcdata descriptor associated with the specified
249 * reason if there is a buffer allocated.
250 */
251 struct kcdata_descriptor *
252 os_reason_get_kcdata_descriptor(os_reason_t cur_reason)
253 {
254 if (cur_reason == OS_REASON_NULL) {
255 return NULL;
256 }
257
258 if (cur_reason->osr_kcd_buf == NULL) {
259 return NULL;
260 }
261
262 assert(cur_reason->osr_kcd_descriptor.kcd_addr_begin == (mach_vm_address_t) cur_reason->osr_kcd_buf);
263 if (cur_reason->osr_kcd_descriptor.kcd_addr_begin != (mach_vm_address_t) cur_reason->osr_kcd_buf) {
264 return NULL;
265 }
266
267 return &cur_reason->osr_kcd_descriptor;
268 }
269
270 /*
271 * Takes a reference on the passed reason.
272 */
273 void
274 os_reason_ref(os_reason_t cur_reason)
275 {
276 if (cur_reason == OS_REASON_NULL) {
277 return;
278 }
279
280 lck_mtx_lock(&cur_reason->osr_lock);
281 os_ref_retain_locked(&cur_reason->osr_refcount);
282 lck_mtx_unlock(&cur_reason->osr_lock);
283 return;
284 }
285
286 /*
287 * Drops a reference on the passed reason, deallocates
288 * the reason if no references remain.
289 */
290 void
291 os_reason_free(os_reason_t cur_reason)
292 {
293 if (cur_reason == OS_REASON_NULL) {
294 return;
295 }
296
297 lck_mtx_lock(&cur_reason->osr_lock);
298
299 if (os_ref_release_locked(&cur_reason->osr_refcount) > 0) {
300 lck_mtx_unlock(&cur_reason->osr_lock);
301 return;
302 }
303
304 os_reason_dealloc_buffer(cur_reason);
305
306 lck_mtx_unlock(&cur_reason->osr_lock);
307 lck_mtx_destroy(&cur_reason->osr_lock, os_reason_lock_grp);
308
309 zfree(os_reason_zone, cur_reason);
310 }
311
312 /*
313 * Sets flags on the passed reason.
314 */
315 void
316 os_reason_set_flags(os_reason_t cur_reason, uint64_t flags)
317 {
318 if (cur_reason == OS_REASON_NULL) {
319 return;
320 }
321
322 lck_mtx_lock(&cur_reason->osr_lock);
323 cur_reason->osr_flags = flags;
324 lck_mtx_unlock(&cur_reason->osr_lock);
325 }
326
327 /*
328 * Allocates space and sets description data in kcd_descriptor on the passed reason.
329 */
330 void
331 os_reason_set_description_data(os_reason_t cur_reason, uint32_t type, void *reason_data, uint32_t reason_data_len)
332 {
333 mach_vm_address_t osr_data_addr = 0;
334
335 if (cur_reason == OS_REASON_NULL) {
336 return;
337 }
338
339 if (0 != os_reason_alloc_buffer(cur_reason, kcdata_estimate_required_buffer_size(1, reason_data_len))) {
340 panic("os_reason failed to allocate");
341 }
342
343 lck_mtx_lock(&cur_reason->osr_lock);
344 if (KERN_SUCCESS != kcdata_get_memory_addr(&cur_reason->osr_kcd_descriptor, type, reason_data_len, &osr_data_addr)) {
345 panic("os_reason failed to get data address");
346 }
347 if (KERN_SUCCESS != kcdata_memcpy(&cur_reason->osr_kcd_descriptor, osr_data_addr, reason_data, reason_data_len)) {
348 panic("os_reason failed to copy description data");
349 }
350 lck_mtx_unlock(&cur_reason->osr_lock);
351 }