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 |
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(lck: &new_reason->osr_lock, grp: &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 |
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(s: &cur_reason->osr_kcd_descriptor, n: 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 |
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, flags: 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 |
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, flags: Z_NOWAIT); |
128 | } |
129 | |
130 | static int |
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(lck: &cur_reason->osr_lock); |
143 | |
144 | os_reason_dealloc_buffer(cur_reason); |
145 | |
146 | if (osr_bufsize == 0) { |
147 | lck_mtx_unlock(lck: &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(lck: &cur_reason->osr_lock); |
156 | return ENOMEM; |
157 | } |
158 | |
159 | cur_reason->osr_bufsize = osr_bufsize; |
160 | |
161 | if (kcdata_memory_static_init(data: &cur_reason->osr_kcd_descriptor, |
162 | buffer_addr_p: (mach_vm_address_t)cur_reason->osr_kcd_buf, |
163 | KCDATA_BUFFER_BEGIN_OS_REASON, size: osr_bufsize, KCFLAG_USE_MEMCOPY) != |
164 | KERN_SUCCESS) { |
165 | os_reason_dealloc_buffer(cur_reason); |
166 | |
167 | lck_mtx_unlock(lck: &cur_reason->osr_lock); |
168 | return EIO; |
169 | } |
170 | |
171 | lck_mtx_unlock(lck: &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 * |
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 |
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(lck: &cur_reason->osr_lock); |
212 | os_ref_retain_locked(rc: &cur_reason->osr_refcount); |
213 | lck_mtx_unlock(lck: &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 |
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(lck: &cur_reason->osr_lock); |
229 | |
230 | if (os_ref_release_locked(rc: &cur_reason->osr_refcount) > 0) { |
231 | lck_mtx_unlock(lck: &cur_reason->osr_lock); |
232 | return; |
233 | } |
234 | |
235 | os_reason_dealloc_buffer(cur_reason); |
236 | |
237 | lck_mtx_unlock(lck: &cur_reason->osr_lock); |
238 | lck_mtx_destroy(lck: &cur_reason->osr_lock, grp: &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 |
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(lck: &cur_reason->osr_lock); |
254 | cur_reason->osr_flags = flags; |
255 | lck_mtx_unlock(lck: &cur_reason->osr_lock); |
256 | } |
257 | |
258 | /* |
259 | * Allocates space and sets description data in kcd_descriptor on the passed reason. |
260 | */ |
261 | void |
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, osr_bufsize: kcdata_estimate_required_buffer_size(num_items: 1, payload_size: reason_data_len))) { |
271 | panic("os_reason failed to allocate" ); |
272 | } |
273 | |
274 | lck_mtx_lock(lck: &cur_reason->osr_lock); |
275 | if (KERN_SUCCESS != kcdata_get_memory_addr(data: &cur_reason->osr_kcd_descriptor, type, size: reason_data_len, user_addr: &osr_data_addr)) { |
276 | panic("os_reason failed to get data address" ); |
277 | } |
278 | if (KERN_SUCCESS != kcdata_memcpy(data: &cur_reason->osr_kcd_descriptor, dst_addr: osr_data_addr, src_addr: reason_data, size: reason_data_len)) { |
279 | panic("os_reason failed to copy description data" ); |
280 | } |
281 | lck_mtx_unlock(lck: &cur_reason->osr_lock); |
282 | } |
283 | |