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