]> git.saurik.com Git - apple/xnu.git/blame - bsd/kern/sys_reason.c
xnu-6153.41.3.tar.gz
[apple/xnu.git] / bsd / kern / sys_reason.c
CommitLineData
39037602
A
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
41extern int os_reason_debug_disabled;
42#endif
43
44extern int maxproc;
45
46/*
47 * Lock group attributes for os_reason subsystem
48 */
0a7de745
A
49lck_grp_attr_t *os_reason_lock_grp_attr;
50lck_grp_t *os_reason_lock_grp;
51lck_attr_t *os_reason_lock_attr;
39037602 52
cb323159
A
53os_refgrp_decl(static, os_reason_refgrp, "os_reason", NULL);
54
0a7de745
A
55#define OS_REASON_RESERVE_COUNT 100
56#define OS_REASON_MAX_COUNT (maxproc + 100)
39037602
A
57
58static struct zone *os_reason_zone;
d190cdc3 59static int os_reason_alloc_buffer_internal(os_reason_t cur_reason, uint32_t osr_bufsize,
0a7de745 60 boolean_t can_block);
39037602
A
61
62void
63os_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),
0a7de745 78 OS_REASON_MAX_COUNT, "os reasons");
39037602
A
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 */
103os_reason_t
104os_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",
0a7de745 118 osr_namespace, osr_code);
39037602
A
119 } else {
120 panic("os_reason_create: failed to allocate reason with namespace: %u, code: %llu\n",
0a7de745 121 osr_namespace, osr_code);
39037602
A
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);
cb323159 136 os_ref_init(&new_reason->osr_refcount, &os_reason_refgrp);
39037602
A
137
138 return new_reason;
139}
140
141static void
142os_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
d190cdc3
A
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
0a7de745 169 * larger than REASON_BUFFER_MAX_SIZE
d190cdc3
A
170 * EIO if we fail to initialize the kcdata buffer
171 */
172int
173os_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
39037602
A
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
0a7de745 188 * larger than REASON_BUFFER_MAX_SIZE
39037602
A
189 * ENOMEM if unable to allocate memory for the buffer
190 * EIO if we fail to initialize the kcdata buffer
191 */
192int
d190cdc3
A
193os_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
198static int
199os_reason_alloc_buffer_internal(os_reason_t cur_reason, uint32_t osr_bufsize,
0a7de745 200 boolean_t can_block)
39037602
A
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
d190cdc3
A
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 }
39037602
A
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,
0a7de745 235 KCDATA_BUFFER_BEGIN_OS_REASON, osr_bufsize, KCFLAG_USE_MEMCOPY) != KERN_SUCCESS) {
39037602
A
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 */
251struct kcdata_descriptor *
252os_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 */
273void
274os_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);
cb323159 281 os_ref_retain_locked(&cur_reason->osr_refcount);
39037602 282 lck_mtx_unlock(&cur_reason->osr_lock);
39037602
A
283 return;
284}
285
286/*
287 * Drops a reference on the passed reason, deallocates
288 * the reason if no references remain.
289 */
290void
291os_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
cb323159 299 if (os_ref_release_locked(&cur_reason->osr_refcount) > 0) {
39037602
A
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}
cb323159
A
311
312/*
313 * Sets flags on the passed reason.
314 */
315void
316os_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 */
330void
331os_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}