]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/sys_reason.c
xnu-7195.101.1.tar.gz
[apple/xnu.git] / bsd / kern / sys_reason.c
1 /*
2 * Copyright (c) 2015-2020 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 extern int maxproc;
39
40 /*
41 * Lock group attributes for os_reason subsystem
42 */
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);
46
47 os_refgrp_decl(static, os_reason_refgrp, "os_reason", NULL);
48
49 static int os_reason_alloc_buffer_internal(os_reason_t cur_reason, uint32_t osr_bufsize,
50 zalloc_flags_t flags);
51
52 /*
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
56 * reason.
57 *
58 * Returns:
59 * REASON_NULL if unable to allocate a reason or initialize the nested buffer
60 * a pointer to the reason otherwise
61 */
62 os_reason_t
63 os_reason_create(uint32_t osr_namespace, uint64_t osr_code)
64 {
65 os_reason_t new_reason;
66
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);
72
73 return new_reason;
74 }
75
76 static void
77 os_reason_dealloc_buffer(os_reason_t cur_reason)
78 {
79 assert(cur_reason != OS_REASON_NULL);
80 LCK_MTX_ASSERT(&cur_reason->osr_lock, LCK_MTX_ASSERT_OWNED);
81
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);
85 }
86
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));
90 }
91
92 /*
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.
99 *
100 * Returns:
101 * 0 on success
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
105 */
106 int
107 os_reason_alloc_buffer(os_reason_t cur_reason, uint32_t osr_bufsize)
108 {
109 return os_reason_alloc_buffer_internal(cur_reason, osr_bufsize, Z_WAITOK);
110 }
111
112 /*
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.
118 *
119 * Returns:
120 * 0 on success
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
125 */
126 int
127 os_reason_alloc_buffer_noblock(os_reason_t cur_reason, uint32_t osr_bufsize)
128 {
129 return os_reason_alloc_buffer_internal(cur_reason, osr_bufsize, Z_NOWAIT);
130 }
131
132 static int
133 os_reason_alloc_buffer_internal(os_reason_t cur_reason, uint32_t osr_bufsize,
134 zalloc_flags_t flags)
135 {
136 if (cur_reason == OS_REASON_NULL) {
137 return EINVAL;
138 }
139
140 if (osr_bufsize > OS_REASON_BUFFER_MAX_SIZE) {
141 return EINVAL;
142 }
143
144 lck_mtx_lock(&cur_reason->osr_lock);
145
146 os_reason_dealloc_buffer(cur_reason);
147
148 if (osr_bufsize == 0) {
149 lck_mtx_unlock(&cur_reason->osr_lock);
150 return 0;
151 }
152
153 cur_reason->osr_kcd_buf = kheap_alloc_tag(KHEAP_DATA_BUFFERS, osr_bufsize,
154 flags | Z_ZERO, VM_KERN_MEMORY_REASON);
155
156 if (cur_reason->osr_kcd_buf == NULL) {
157 lck_mtx_unlock(&cur_reason->osr_lock);
158 return ENOMEM;
159 }
160
161 cur_reason->osr_bufsize = osr_bufsize;
162
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) !=
166 KERN_SUCCESS) {
167 os_reason_dealloc_buffer(cur_reason);
168
169 lck_mtx_unlock(&cur_reason->osr_lock);
170 return EIO;
171 }
172
173 lck_mtx_unlock(&cur_reason->osr_lock);
174
175 return 0;
176 }
177
178 /*
179 * Returns a pointer to the kcdata descriptor associated with the specified
180 * reason if there is a buffer allocated.
181 */
182 struct kcdata_descriptor *
183 os_reason_get_kcdata_descriptor(os_reason_t cur_reason)
184 {
185 if (cur_reason == OS_REASON_NULL) {
186 return NULL;
187 }
188
189 if (cur_reason->osr_kcd_buf == NULL) {
190 return NULL;
191 }
192
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) {
197 return NULL;
198 }
199
200 return &cur_reason->osr_kcd_descriptor;
201 }
202
203 /*
204 * Takes a reference on the passed reason.
205 */
206 void
207 os_reason_ref(os_reason_t cur_reason)
208 {
209 if (cur_reason == OS_REASON_NULL) {
210 return;
211 }
212
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);
216 return;
217 }
218
219 /*
220 * Drops a reference on the passed reason, deallocates
221 * the reason if no references remain.
222 */
223 void
224 os_reason_free(os_reason_t cur_reason)
225 {
226 if (cur_reason == OS_REASON_NULL) {
227 return;
228 }
229
230 lck_mtx_lock(&cur_reason->osr_lock);
231
232 if (os_ref_release_locked(&cur_reason->osr_refcount) > 0) {
233 lck_mtx_unlock(&cur_reason->osr_lock);
234 return;
235 }
236
237 os_reason_dealloc_buffer(cur_reason);
238
239 lck_mtx_unlock(&cur_reason->osr_lock);
240 lck_mtx_destroy(&cur_reason->osr_lock, &os_reason_lock_grp);
241
242 zfree(os_reason_zone, cur_reason);
243 }
244
245 /*
246 * Sets flags on the passed reason.
247 */
248 void
249 os_reason_set_flags(os_reason_t cur_reason, uint64_t flags)
250 {
251 if (cur_reason == OS_REASON_NULL) {
252 return;
253 }
254
255 lck_mtx_lock(&cur_reason->osr_lock);
256 cur_reason->osr_flags = flags;
257 lck_mtx_unlock(&cur_reason->osr_lock);
258 }
259
260 /*
261 * Allocates space and sets description data in kcd_descriptor on the passed reason.
262 */
263 void
264 os_reason_set_description_data(os_reason_t cur_reason, uint32_t type, void *reason_data, uint32_t reason_data_len)
265 {
266 mach_vm_address_t osr_data_addr = 0;
267
268 if (cur_reason == OS_REASON_NULL) {
269 return;
270 }
271
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");
274 }
275
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");
279 }
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");
282 }
283 lck_mtx_unlock(&cur_reason->osr_lock);
284 }