2 * Copyright (c) 2015 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
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>
35 #include <kern/assert.h>
36 #include <kern/debug.h>
39 #include <pexpert/pexpert.h>
41 extern int os_reason_debug_disabled
;
47 * Lock group attributes for os_reason subsystem
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
;
53 os_refgrp_decl(static, os_reason_refgrp
, "os_reason", NULL
);
55 #define OS_REASON_RESERVE_COUNT 100
56 #define OS_REASON_MAX_COUNT (maxproc + 100)
58 static struct zone
*os_reason_zone
;
59 static int os_reason_alloc_buffer_internal(os_reason_t cur_reason
, uint32_t osr_bufsize
,
65 int reasons_allocated
= 0;
68 * Initialize OS reason group and lock attributes
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();
75 * Create OS reason zone.
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");
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().
89 reasons_allocated
= zfill(os_reason_zone
, OS_REASON_RESERVE_COUNT
);
90 assert(reasons_allocated
> 0);
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
100 * REASON_NULL if unable to allocate a reason or initialize the nested buffer
101 * a pointer to the reason otherwise
104 os_reason_create(uint32_t osr_namespace
, uint64_t osr_code
)
106 os_reason_t new_reason
= OS_REASON_NULL
;
108 new_reason
= (os_reason_t
) zalloc(os_reason_zone
);
109 if (new_reason
== OS_REASON_NULL
) {
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.
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
);
120 panic("os_reason_create: failed to allocate reason with namespace: %u, code: %llu\n",
121 osr_namespace
, osr_code
);
127 bzero(new_reason
, sizeof(*new_reason
));
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
;
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
);
142 os_reason_dealloc_buffer(os_reason_t cur_reason
)
144 assert(cur_reason
!= OS_REASON_NULL
);
145 LCK_MTX_ASSERT(&cur_reason
->osr_lock
, LCK_MTX_ASSERT_OWNED
);
147 if (cur_reason
->osr_kcd_buf
!= NULL
&& cur_reason
->osr_bufsize
!= 0) {
148 kfree(cur_reason
->osr_kcd_buf
, cur_reason
->osr_bufsize
);
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
));
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.
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
173 os_reason_alloc_buffer(os_reason_t cur_reason
, uint32_t osr_bufsize
)
175 return os_reason_alloc_buffer_internal(cur_reason
, osr_bufsize
, TRUE
);
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.
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
193 os_reason_alloc_buffer_noblock(os_reason_t cur_reason
, uint32_t osr_bufsize
)
195 return os_reason_alloc_buffer_internal(cur_reason
, osr_bufsize
, FALSE
);
199 os_reason_alloc_buffer_internal(os_reason_t cur_reason
, uint32_t osr_bufsize
,
202 if (cur_reason
== OS_REASON_NULL
) {
206 if (osr_bufsize
> OS_REASON_BUFFER_MAX_SIZE
) {
210 lck_mtx_lock(&cur_reason
->osr_lock
);
212 os_reason_dealloc_buffer(cur_reason
);
214 if (osr_bufsize
== 0) {
215 lck_mtx_unlock(&cur_reason
->osr_lock
);
220 cur_reason
->osr_kcd_buf
= kalloc_tag(osr_bufsize
, VM_KERN_MEMORY_REASON
);
221 assert(cur_reason
->osr_kcd_buf
!= NULL
);
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
);
230 bzero(cur_reason
->osr_kcd_buf
, osr_bufsize
);
232 cur_reason
->osr_bufsize
= osr_bufsize
;
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
);
238 lck_mtx_unlock(&cur_reason
->osr_lock
);
242 lck_mtx_unlock(&cur_reason
->osr_lock
);
248 * Returns a pointer to the kcdata descriptor associated with the specified
249 * reason if there is a buffer allocated.
251 struct kcdata_descriptor
*
252 os_reason_get_kcdata_descriptor(os_reason_t cur_reason
)
254 if (cur_reason
== OS_REASON_NULL
) {
258 if (cur_reason
->osr_kcd_buf
== NULL
) {
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
) {
267 return &cur_reason
->osr_kcd_descriptor
;
271 * Takes a reference on the passed reason.
274 os_reason_ref(os_reason_t cur_reason
)
276 if (cur_reason
== OS_REASON_NULL
) {
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
);
287 * Drops a reference on the passed reason, deallocates
288 * the reason if no references remain.
291 os_reason_free(os_reason_t cur_reason
)
293 if (cur_reason
== OS_REASON_NULL
) {
297 lck_mtx_lock(&cur_reason
->osr_lock
);
299 if (os_ref_release_locked(&cur_reason
->osr_refcount
) > 0) {
300 lck_mtx_unlock(&cur_reason
->osr_lock
);
304 os_reason_dealloc_buffer(cur_reason
);
306 lck_mtx_unlock(&cur_reason
->osr_lock
);
307 lck_mtx_destroy(&cur_reason
->osr_lock
, os_reason_lock_grp
);
309 zfree(os_reason_zone
, cur_reason
);
313 * Sets flags on the passed reason.
316 os_reason_set_flags(os_reason_t cur_reason
, uint64_t flags
)
318 if (cur_reason
== OS_REASON_NULL
) {
322 lck_mtx_lock(&cur_reason
->osr_lock
);
323 cur_reason
->osr_flags
= flags
;
324 lck_mtx_unlock(&cur_reason
->osr_lock
);
328 * Allocates space and sets description data in kcd_descriptor on the passed reason.
331 os_reason_set_description_data(os_reason_t cur_reason
, uint32_t type
, void *reason_data
, uint32_t reason_data_len
)
333 mach_vm_address_t osr_data_addr
= 0;
335 if (cur_reason
== OS_REASON_NULL
) {
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");
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");
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");
350 lck_mtx_unlock(&cur_reason
->osr_lock
);