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_DEFINE(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
os_reason_create(uint32_t osr_namespace,uint64_t osr_code)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
os_reason_dealloc_buffer(os_reason_t cur_reason)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 kfree_data(cur_reason->osr_kcd_buf, cur_reason->osr_bufsize);
84 }
85
86 cur_reason->osr_bufsize = 0;
87 cur_reason->osr_kcd_buf = NULL;
88 bzero(&cur_reason->osr_kcd_descriptor, sizeof(cur_reason->osr_kcd_descriptor));
89 }
90
91 /*
92 * Allocates and initializes a buffer of specified size for the reason. This function
93 * may block and should not be called from extremely performance sensitive contexts
94 * (i.e. jetsam). Also initializes the kcdata descriptor accordingly. If there is an
95 * existing buffer, we dealloc the buffer before allocating a new one and
96 * clear the associated kcdata descriptor. If osr_bufsize is passed as 0,
97 * we deallocate the existing buffer and then return.
98 *
99 * Returns:
100 * 0 on success
101 * EINVAL if the passed reason pointer is invalid or the requested size is
102 * larger than REASON_BUFFER_MAX_SIZE
103 * EIO if we fail to initialize the kcdata buffer
104 */
105 int
os_reason_alloc_buffer(os_reason_t cur_reason,uint32_t osr_bufsize)106 os_reason_alloc_buffer(os_reason_t cur_reason, uint32_t osr_bufsize)
107 {
108 return os_reason_alloc_buffer_internal(cur_reason, osr_bufsize, Z_WAITOK);
109 }
110
111 /*
112 * Allocates and initializes a buffer of specified size for the reason. Also
113 * initializes the kcdata descriptor accordingly. If there is an existing
114 * buffer, we dealloc the buffer before allocating a new one and
115 * clear the associated kcdata descriptor. If osr_bufsize is passed as 0,
116 * we deallocate the existing buffer and then return.
117 *
118 * Returns:
119 * 0 on success
120 * EINVAL if the passed reason pointer is invalid or the requested size is
121 * larger than REASON_BUFFER_MAX_SIZE
122 * ENOMEM if unable to allocate memory for the buffer
123 * EIO if we fail to initialize the kcdata buffer
124 */
125 int
os_reason_alloc_buffer_noblock(os_reason_t cur_reason,uint32_t osr_bufsize)126 os_reason_alloc_buffer_noblock(os_reason_t cur_reason, uint32_t osr_bufsize)
127 {
128 return os_reason_alloc_buffer_internal(cur_reason, osr_bufsize, Z_NOWAIT);
129 }
130
131 static int
os_reason_alloc_buffer_internal(os_reason_t cur_reason,uint32_t osr_bufsize,zalloc_flags_t flags)132 os_reason_alloc_buffer_internal(os_reason_t cur_reason, uint32_t osr_bufsize,
133 zalloc_flags_t flags)
134 {
135 if (cur_reason == OS_REASON_NULL) {
136 return EINVAL;
137 }
138
139 if (osr_bufsize > OS_REASON_BUFFER_MAX_SIZE) {
140 return EINVAL;
141 }
142
143 lck_mtx_lock(&cur_reason->osr_lock);
144
145 os_reason_dealloc_buffer(cur_reason);
146
147 if (osr_bufsize == 0) {
148 lck_mtx_unlock(&cur_reason->osr_lock);
149 return 0;
150 }
151
152 cur_reason->osr_kcd_buf = kalloc_data_tag(osr_bufsize, flags | Z_ZERO,
153 VM_KERN_MEMORY_REASON);
154
155 if (cur_reason->osr_kcd_buf == NULL) {
156 lck_mtx_unlock(&cur_reason->osr_lock);
157 return ENOMEM;
158 }
159
160 cur_reason->osr_bufsize = osr_bufsize;
161
162 if (kcdata_memory_static_init(&cur_reason->osr_kcd_descriptor,
163 (mach_vm_address_t)cur_reason->osr_kcd_buf,
164 KCDATA_BUFFER_BEGIN_OS_REASON, osr_bufsize, KCFLAG_USE_MEMCOPY) !=
165 KERN_SUCCESS) {
166 os_reason_dealloc_buffer(cur_reason);
167
168 lck_mtx_unlock(&cur_reason->osr_lock);
169 return EIO;
170 }
171
172 lck_mtx_unlock(&cur_reason->osr_lock);
173
174 return 0;
175 }
176
177 /*
178 * Returns a pointer to the kcdata descriptor associated with the specified
179 * reason if there is a buffer allocated.
180 */
181 struct kcdata_descriptor *
os_reason_get_kcdata_descriptor(os_reason_t cur_reason)182 os_reason_get_kcdata_descriptor(os_reason_t cur_reason)
183 {
184 if (cur_reason == OS_REASON_NULL) {
185 return NULL;
186 }
187
188 if (cur_reason->osr_kcd_buf == NULL) {
189 return NULL;
190 }
191
192 assert(cur_reason->osr_kcd_descriptor.kcd_addr_begin ==
193 (mach_vm_address_t)cur_reason->osr_kcd_buf);
194 if (cur_reason->osr_kcd_descriptor.kcd_addr_begin !=
195 (mach_vm_address_t)cur_reason->osr_kcd_buf) {
196 return NULL;
197 }
198
199 return &cur_reason->osr_kcd_descriptor;
200 }
201
202 /*
203 * Takes a reference on the passed reason.
204 */
205 void
os_reason_ref(os_reason_t cur_reason)206 os_reason_ref(os_reason_t cur_reason)
207 {
208 if (cur_reason == OS_REASON_NULL) {
209 return;
210 }
211
212 lck_mtx_lock(&cur_reason->osr_lock);
213 os_ref_retain_locked(&cur_reason->osr_refcount);
214 lck_mtx_unlock(&cur_reason->osr_lock);
215 return;
216 }
217
218 /*
219 * Drops a reference on the passed reason, deallocates
220 * the reason if no references remain.
221 */
222 void
os_reason_free(os_reason_t cur_reason)223 os_reason_free(os_reason_t cur_reason)
224 {
225 if (cur_reason == OS_REASON_NULL) {
226 return;
227 }
228
229 lck_mtx_lock(&cur_reason->osr_lock);
230
231 if (os_ref_release_locked(&cur_reason->osr_refcount) > 0) {
232 lck_mtx_unlock(&cur_reason->osr_lock);
233 return;
234 }
235
236 os_reason_dealloc_buffer(cur_reason);
237
238 lck_mtx_unlock(&cur_reason->osr_lock);
239 lck_mtx_destroy(&cur_reason->osr_lock, &os_reason_lock_grp);
240
241 zfree(os_reason_zone, cur_reason);
242 }
243
244 /*
245 * Sets flags on the passed reason.
246 */
247 void
os_reason_set_flags(os_reason_t cur_reason,uint64_t flags)248 os_reason_set_flags(os_reason_t cur_reason, uint64_t flags)
249 {
250 if (cur_reason == OS_REASON_NULL) {
251 return;
252 }
253
254 lck_mtx_lock(&cur_reason->osr_lock);
255 cur_reason->osr_flags = flags;
256 lck_mtx_unlock(&cur_reason->osr_lock);
257 }
258
259 /*
260 * Allocates space and sets description data in kcd_descriptor on the passed reason.
261 */
262 void
os_reason_set_description_data(os_reason_t cur_reason,uint32_t type,void * reason_data,uint32_t reason_data_len)263 os_reason_set_description_data(os_reason_t cur_reason, uint32_t type, void *reason_data, uint32_t reason_data_len)
264 {
265 mach_vm_address_t osr_data_addr = 0;
266
267 if (cur_reason == OS_REASON_NULL) {
268 return;
269 }
270
271 if (0 != os_reason_alloc_buffer(cur_reason, kcdata_estimate_required_buffer_size(1, reason_data_len))) {
272 panic("os_reason failed to allocate");
273 }
274
275 lck_mtx_lock(&cur_reason->osr_lock);
276 if (KERN_SUCCESS != kcdata_get_memory_addr(&cur_reason->osr_kcd_descriptor, type, reason_data_len, &osr_data_addr)) {
277 panic("os_reason failed to get data address");
278 }
279 if (KERN_SUCCESS != kcdata_memcpy(&cur_reason->osr_kcd_descriptor, osr_data_addr, reason_data, reason_data_len)) {
280 panic("os_reason failed to copy description data");
281 }
282 lck_mtx_unlock(&cur_reason->osr_lock);
283 }
284