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>
41 * Lock group attributes for os_reason subsystem
43 static LCK_GRP_DECLARE(os_reason_lock_grp
, "os_reason_lock");
44 static ZONE_DECLARE(os_reason_zone
, "os reasons",
45 sizeof(struct os_reason
), ZC_ZFREE_CLEARMEM
);
47 os_refgrp_decl(static, os_reason_refgrp
, "os_reason", NULL
);
49 static int os_reason_alloc_buffer_internal(os_reason_t cur_reason
, uint32_t osr_bufsize
,
50 zalloc_flags_t flags
);
53 * Creates a new reason and initializes it with the provided reason
54 * namespace and code. Also sets up the buffer and kcdata_descriptor
55 * associated with the reason. Returns a pointer to the newly created
59 * REASON_NULL if unable to allocate a reason or initialize the nested buffer
60 * a pointer to the reason otherwise
63 os_reason_create(uint32_t osr_namespace
, uint64_t osr_code
)
65 os_reason_t new_reason
;
67 new_reason
= zalloc_flags(os_reason_zone
, Z_WAITOK
| Z_ZERO
);
68 new_reason
->osr_namespace
= osr_namespace
;
69 new_reason
->osr_code
= osr_code
;
70 lck_mtx_init(&new_reason
->osr_lock
, &os_reason_lock_grp
, LCK_ATTR_NULL
);
71 os_ref_init(&new_reason
->osr_refcount
, &os_reason_refgrp
);
77 os_reason_dealloc_buffer(os_reason_t cur_reason
)
79 assert(cur_reason
!= OS_REASON_NULL
);
80 LCK_MTX_ASSERT(&cur_reason
->osr_lock
, LCK_MTX_ASSERT_OWNED
);
82 if (cur_reason
->osr_kcd_buf
!= NULL
&& cur_reason
->osr_bufsize
!= 0) {
83 kheap_free(KHEAP_DATA_BUFFERS
, cur_reason
->osr_kcd_buf
,
84 cur_reason
->osr_bufsize
);
87 cur_reason
->osr_bufsize
= 0;
88 cur_reason
->osr_kcd_buf
= NULL
;
89 bzero(&cur_reason
->osr_kcd_descriptor
, sizeof(cur_reason
->osr_kcd_descriptor
));
93 * Allocates and initializes a buffer of specified size for the reason. This function
94 * may block and should not be called from extremely performance sensitive contexts
95 * (i.e. jetsam). Also initializes the kcdata descriptor accordingly. If there is an
96 * existing buffer, we dealloc the buffer before allocating a new one and
97 * clear the associated kcdata descriptor. If osr_bufsize is passed as 0,
98 * we deallocate the existing buffer and then return.
102 * EINVAL if the passed reason pointer is invalid or the requested size is
103 * larger than REASON_BUFFER_MAX_SIZE
104 * EIO if we fail to initialize the kcdata buffer
107 os_reason_alloc_buffer(os_reason_t cur_reason
, uint32_t osr_bufsize
)
109 return os_reason_alloc_buffer_internal(cur_reason
, osr_bufsize
, Z_WAITOK
);
113 * Allocates and initializes a buffer of specified size for the reason. Also
114 * initializes the kcdata descriptor accordingly. If there is an existing
115 * buffer, we dealloc the buffer before allocating a new one and
116 * clear the associated kcdata descriptor. If osr_bufsize is passed as 0,
117 * we deallocate the existing buffer and then return.
121 * EINVAL if the passed reason pointer is invalid or the requested size is
122 * larger than REASON_BUFFER_MAX_SIZE
123 * ENOMEM if unable to allocate memory for the buffer
124 * EIO if we fail to initialize the kcdata buffer
127 os_reason_alloc_buffer_noblock(os_reason_t cur_reason
, uint32_t osr_bufsize
)
129 return os_reason_alloc_buffer_internal(cur_reason
, osr_bufsize
, Z_NOWAIT
);
133 os_reason_alloc_buffer_internal(os_reason_t cur_reason
, uint32_t osr_bufsize
,
134 zalloc_flags_t flags
)
136 if (cur_reason
== OS_REASON_NULL
) {
140 if (osr_bufsize
> OS_REASON_BUFFER_MAX_SIZE
) {
144 lck_mtx_lock(&cur_reason
->osr_lock
);
146 os_reason_dealloc_buffer(cur_reason
);
148 if (osr_bufsize
== 0) {
149 lck_mtx_unlock(&cur_reason
->osr_lock
);
153 cur_reason
->osr_kcd_buf
= kheap_alloc_tag(KHEAP_DATA_BUFFERS
, osr_bufsize
,
154 flags
| Z_ZERO
, VM_KERN_MEMORY_REASON
);
156 if (cur_reason
->osr_kcd_buf
== NULL
) {
157 lck_mtx_unlock(&cur_reason
->osr_lock
);
161 cur_reason
->osr_bufsize
= osr_bufsize
;
163 if (kcdata_memory_static_init(&cur_reason
->osr_kcd_descriptor
,
164 (mach_vm_address_t
)cur_reason
->osr_kcd_buf
,
165 KCDATA_BUFFER_BEGIN_OS_REASON
, osr_bufsize
, KCFLAG_USE_MEMCOPY
) !=
167 os_reason_dealloc_buffer(cur_reason
);
169 lck_mtx_unlock(&cur_reason
->osr_lock
);
173 lck_mtx_unlock(&cur_reason
->osr_lock
);
179 * Returns a pointer to the kcdata descriptor associated with the specified
180 * reason if there is a buffer allocated.
182 struct kcdata_descriptor
*
183 os_reason_get_kcdata_descriptor(os_reason_t cur_reason
)
185 if (cur_reason
== OS_REASON_NULL
) {
189 if (cur_reason
->osr_kcd_buf
== NULL
) {
193 assert(cur_reason
->osr_kcd_descriptor
.kcd_addr_begin
==
194 (mach_vm_address_t
)cur_reason
->osr_kcd_buf
);
195 if (cur_reason
->osr_kcd_descriptor
.kcd_addr_begin
!=
196 (mach_vm_address_t
)cur_reason
->osr_kcd_buf
) {
200 return &cur_reason
->osr_kcd_descriptor
;
204 * Takes a reference on the passed reason.
207 os_reason_ref(os_reason_t cur_reason
)
209 if (cur_reason
== OS_REASON_NULL
) {
213 lck_mtx_lock(&cur_reason
->osr_lock
);
214 os_ref_retain_locked(&cur_reason
->osr_refcount
);
215 lck_mtx_unlock(&cur_reason
->osr_lock
);
220 * Drops a reference on the passed reason, deallocates
221 * the reason if no references remain.
224 os_reason_free(os_reason_t cur_reason
)
226 if (cur_reason
== OS_REASON_NULL
) {
230 lck_mtx_lock(&cur_reason
->osr_lock
);
232 if (os_ref_release_locked(&cur_reason
->osr_refcount
) > 0) {
233 lck_mtx_unlock(&cur_reason
->osr_lock
);
237 os_reason_dealloc_buffer(cur_reason
);
239 lck_mtx_unlock(&cur_reason
->osr_lock
);
240 lck_mtx_destroy(&cur_reason
->osr_lock
, &os_reason_lock_grp
);
242 zfree(os_reason_zone
, cur_reason
);
246 * Sets flags on the passed reason.
249 os_reason_set_flags(os_reason_t cur_reason
, uint64_t flags
)
251 if (cur_reason
== OS_REASON_NULL
) {
255 lck_mtx_lock(&cur_reason
->osr_lock
);
256 cur_reason
->osr_flags
= flags
;
257 lck_mtx_unlock(&cur_reason
->osr_lock
);
261 * Allocates space and sets description data in kcd_descriptor on the passed reason.
264 os_reason_set_description_data(os_reason_t cur_reason
, uint32_t type
, void *reason_data
, uint32_t reason_data_len
)
266 mach_vm_address_t osr_data_addr
= 0;
268 if (cur_reason
== OS_REASON_NULL
) {
272 if (0 != os_reason_alloc_buffer(cur_reason
, kcdata_estimate_required_buffer_size(1, reason_data_len
))) {
273 panic("os_reason failed to allocate");
276 lck_mtx_lock(&cur_reason
->osr_lock
);
277 if (KERN_SUCCESS
!= kcdata_get_memory_addr(&cur_reason
->osr_kcd_descriptor
, type
, reason_data_len
, &osr_data_addr
)) {
278 panic("os_reason failed to get data address");
280 if (KERN_SUCCESS
!= kcdata_memcpy(&cur_reason
->osr_kcd_descriptor
, osr_data_addr
, reason_data
, reason_data_len
)) {
281 panic("os_reason failed to copy description data");
283 lck_mtx_unlock(&cur_reason
->osr_lock
);