2 * Copyright (c) 2015-2020 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 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
);
53 os_refgrp_decl(static, os_reason_refgrp
, "os_reason", NULL
);
55 #define OS_REASON_RESERVE_COUNT 100
57 static int os_reason_alloc_buffer_internal(os_reason_t cur_reason
, uint32_t osr_bufsize
,
58 zalloc_flags_t flags
);
63 int reasons_allocated
= 0;
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
70 reasons_allocated
= zfill(os_reason_zone
, OS_REASON_RESERVE_COUNT
);
71 assert(reasons_allocated
>= OS_REASON_RESERVE_COUNT
);
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
81 * REASON_NULL if unable to allocate a reason or initialize the nested buffer
82 * a pointer to the reason otherwise
85 os_reason_create(uint32_t osr_namespace
, uint64_t osr_code
)
87 os_reason_t new_reason
;
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
);
99 os_reason_dealloc_buffer(os_reason_t cur_reason
)
101 assert(cur_reason
!= OS_REASON_NULL
);
102 LCK_MTX_ASSERT(&cur_reason
->osr_lock
, LCK_MTX_ASSERT_OWNED
);
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
);
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
));
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.
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
129 os_reason_alloc_buffer(os_reason_t cur_reason
, uint32_t osr_bufsize
)
131 return os_reason_alloc_buffer_internal(cur_reason
, osr_bufsize
, Z_WAITOK
);
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.
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
149 os_reason_alloc_buffer_noblock(os_reason_t cur_reason
, uint32_t osr_bufsize
)
151 return os_reason_alloc_buffer_internal(cur_reason
, osr_bufsize
, Z_NOWAIT
);
155 os_reason_alloc_buffer_internal(os_reason_t cur_reason
, uint32_t osr_bufsize
,
156 zalloc_flags_t flags
)
158 if (cur_reason
== OS_REASON_NULL
) {
162 if (osr_bufsize
> OS_REASON_BUFFER_MAX_SIZE
) {
166 lck_mtx_lock(&cur_reason
->osr_lock
);
168 os_reason_dealloc_buffer(cur_reason
);
170 if (osr_bufsize
== 0) {
171 lck_mtx_unlock(&cur_reason
->osr_lock
);
175 cur_reason
->osr_kcd_buf
= kheap_alloc_tag(KHEAP_DATA_BUFFERS
, osr_bufsize
,
176 flags
| Z_ZERO
, VM_KERN_MEMORY_REASON
);
178 if (cur_reason
->osr_kcd_buf
== NULL
) {
179 lck_mtx_unlock(&cur_reason
->osr_lock
);
183 cur_reason
->osr_bufsize
= osr_bufsize
;
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
) !=
189 os_reason_dealloc_buffer(cur_reason
);
191 lck_mtx_unlock(&cur_reason
->osr_lock
);
195 lck_mtx_unlock(&cur_reason
->osr_lock
);
201 * Returns a pointer to the kcdata descriptor associated with the specified
202 * reason if there is a buffer allocated.
204 struct kcdata_descriptor
*
205 os_reason_get_kcdata_descriptor(os_reason_t cur_reason
)
207 if (cur_reason
== OS_REASON_NULL
) {
211 if (cur_reason
->osr_kcd_buf
== NULL
) {
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
) {
222 return &cur_reason
->osr_kcd_descriptor
;
226 * Takes a reference on the passed reason.
229 os_reason_ref(os_reason_t cur_reason
)
231 if (cur_reason
== OS_REASON_NULL
) {
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
);
242 * Drops a reference on the passed reason, deallocates
243 * the reason if no references remain.
246 os_reason_free(os_reason_t cur_reason
)
248 if (cur_reason
== OS_REASON_NULL
) {
252 lck_mtx_lock(&cur_reason
->osr_lock
);
254 if (os_ref_release_locked(&cur_reason
->osr_refcount
) > 0) {
255 lck_mtx_unlock(&cur_reason
->osr_lock
);
259 os_reason_dealloc_buffer(cur_reason
);
261 lck_mtx_unlock(&cur_reason
->osr_lock
);
262 lck_mtx_destroy(&cur_reason
->osr_lock
, &os_reason_lock_grp
);
264 zfree(os_reason_zone
, cur_reason
);
268 * Sets flags on the passed reason.
271 os_reason_set_flags(os_reason_t cur_reason
, uint64_t flags
)
273 if (cur_reason
== OS_REASON_NULL
) {
277 lck_mtx_lock(&cur_reason
->osr_lock
);
278 cur_reason
->osr_flags
= flags
;
279 lck_mtx_unlock(&cur_reason
->osr_lock
);
283 * Allocates space and sets description data in kcd_descriptor on the passed reason.
286 os_reason_set_description_data(os_reason_t cur_reason
, uint32_t type
, void *reason_data
, uint32_t reason_data_len
)
288 mach_vm_address_t osr_data_addr
= 0;
290 if (cur_reason
== OS_REASON_NULL
) {
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");
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");
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");
305 lck_mtx_unlock(&cur_reason
->osr_lock
);