]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/sys_reason.c
xnu-3789.1.32.tar.gz
[apple/xnu.git] / bsd / kern / sys_reason.c
1 /*
2 * Copyright (c) 2015 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 #if OS_REASON_DEBUG
39 #include <pexpert/pexpert.h>
40
41 extern int os_reason_debug_disabled;
42 #endif
43
44 extern int maxproc;
45
46 /*
47 * Lock group attributes for os_reason subsystem
48 */
49 lck_grp_attr_t *os_reason_lock_grp_attr;
50 lck_grp_t *os_reason_lock_grp;
51 lck_attr_t *os_reason_lock_attr;
52
53 #define OS_REASON_RESERVE_COUNT 100
54 #define OS_REASON_MAX_COUNT (maxproc + 100)
55
56 static struct zone *os_reason_zone;
57
58 void
59 os_reason_init()
60 {
61 int reasons_allocated = 0;
62
63 /*
64 * Initialize OS reason group and lock attributes
65 */
66 os_reason_lock_grp_attr = lck_grp_attr_alloc_init();
67 os_reason_lock_grp = lck_grp_alloc_init("os_reason_lock", os_reason_lock_grp_attr);
68 os_reason_lock_attr = lck_attr_alloc_init();
69
70 /*
71 * Create OS reason zone.
72 */
73 os_reason_zone = zinit(sizeof(struct os_reason), OS_REASON_MAX_COUNT * sizeof(struct os_reason),
74 OS_REASON_MAX_COUNT, "os reasons");
75 if (os_reason_zone == NULL) {
76 panic("failed to initialize os_reason_zone");
77 }
78
79 /*
80 * We pre-fill the OS reason zone to reduce the likelihood that
81 * the jetsam thread and others block when they create an exit
82 * reason. This pre-filled memory is not-collectable since it's
83 * foreign memory crammed in as part of zfill().
84 */
85 reasons_allocated = zfill(os_reason_zone, OS_REASON_RESERVE_COUNT);
86 assert(reasons_allocated > 0);
87 }
88
89 /*
90 * Creates a new reason and initializes it with the provided reason
91 * namespace and code. Also sets up the buffer and kcdata_descriptor
92 * associated with the reason. Returns a pointer to the newly created
93 * reason.
94 *
95 * Returns:
96 * REASON_NULL if unable to allocate a reason or initialize the nested buffer
97 * a pointer to the reason otherwise
98 */
99 os_reason_t
100 os_reason_create(uint32_t osr_namespace, uint64_t osr_code)
101 {
102 os_reason_t new_reason = OS_REASON_NULL;
103
104 new_reason = (os_reason_t) zalloc(os_reason_zone);
105 if (new_reason == OS_REASON_NULL) {
106 #if OS_REASON_DEBUG
107 /*
108 * We rely on OS reasons to communicate important things such
109 * as process exit reason information, we should be aware
110 * when issues prevent us from allocating them.
111 */
112 if (os_reason_debug_disabled) {
113 kprintf("os_reason_create: failed to allocate reason with namespace: %u, code : %llu\n",
114 osr_namespace, osr_code);
115 } else {
116 panic("os_reason_create: failed to allocate reason with namespace: %u, code: %llu\n",
117 osr_namespace, osr_code);
118 }
119 #endif
120 return new_reason;
121 }
122
123 bzero(new_reason, sizeof(*new_reason));
124
125 new_reason->osr_namespace = osr_namespace;
126 new_reason->osr_code = osr_code;
127 new_reason->osr_flags = 0;
128 new_reason->osr_bufsize = 0;
129 new_reason->osr_kcd_buf = NULL;
130
131 lck_mtx_init(&new_reason->osr_lock, os_reason_lock_grp, os_reason_lock_attr);
132 new_reason->osr_refcount = 1;
133
134 return new_reason;
135 }
136
137 static void
138 os_reason_dealloc_buffer(os_reason_t cur_reason)
139 {
140 assert(cur_reason != OS_REASON_NULL);
141 LCK_MTX_ASSERT(&cur_reason->osr_lock, LCK_MTX_ASSERT_OWNED);
142
143 if (cur_reason->osr_kcd_buf != NULL && cur_reason->osr_bufsize != 0) {
144 kfree(cur_reason->osr_kcd_buf, cur_reason->osr_bufsize);
145 }
146
147 cur_reason->osr_bufsize = 0;
148 cur_reason->osr_kcd_buf = NULL;
149 bzero(&cur_reason->osr_kcd_descriptor, sizeof(cur_reason->osr_kcd_descriptor));
150
151 return;
152 }
153
154 /*
155 * Allocates and initializes a buffer of specified size for the reason. Also
156 * initializes the kcdata descriptor accordingly. If there is an existing
157 * buffer, we dealloc the buffer before allocating a new one and
158 * clear the associated kcdata descriptor. If osr_bufsize is passed as 0,
159 * we deallocate the existing buffer and then return.
160 *
161 * Returns:
162 * 0 on success
163 * EINVAL if the passed reason pointer is invalid or the requested size is
164 * larger than REASON_BUFFER_MAX_SIZE
165 * ENOMEM if unable to allocate memory for the buffer
166 * EIO if we fail to initialize the kcdata buffer
167 */
168 int
169 os_reason_alloc_buffer(os_reason_t cur_reason, uint32_t osr_bufsize)
170 {
171 if (cur_reason == OS_REASON_NULL) {
172 return EINVAL;
173 }
174
175 if (osr_bufsize > OS_REASON_BUFFER_MAX_SIZE) {
176 return EINVAL;
177 }
178
179 lck_mtx_lock(&cur_reason->osr_lock);
180
181 os_reason_dealloc_buffer(cur_reason);
182
183 if (osr_bufsize == 0) {
184 lck_mtx_unlock(&cur_reason->osr_lock);
185 return 0;
186 }
187
188 /*
189 * We don't want to block trying to acquire a reason buffer and hold
190 * up important things trying to clean up the system (i.e. jetsam).
191 */
192 cur_reason->osr_kcd_buf = kalloc_noblock_tag(osr_bufsize, VM_KERN_MEMORY_REASON);
193 if (cur_reason->osr_kcd_buf == NULL) {
194 lck_mtx_unlock(&cur_reason->osr_lock);
195 return ENOMEM;
196 }
197
198 bzero(cur_reason->osr_kcd_buf, osr_bufsize);
199
200 cur_reason->osr_bufsize = osr_bufsize;
201
202 if (kcdata_memory_static_init(&cur_reason->osr_kcd_descriptor, (mach_vm_address_t) cur_reason->osr_kcd_buf,
203 KCDATA_BUFFER_BEGIN_OS_REASON, osr_bufsize, KCFLAG_USE_MEMCOPY) != KERN_SUCCESS) {
204 os_reason_dealloc_buffer(cur_reason);
205
206 lck_mtx_unlock(&cur_reason->osr_lock);
207 return EIO;
208 }
209
210 lck_mtx_unlock(&cur_reason->osr_lock);
211
212 return 0;
213 }
214
215 /*
216 * Returns a pointer to the kcdata descriptor associated with the specified
217 * reason if there is a buffer allocated.
218 */
219 struct kcdata_descriptor *
220 os_reason_get_kcdata_descriptor(os_reason_t cur_reason)
221 {
222 if (cur_reason == OS_REASON_NULL) {
223 return NULL;
224 }
225
226 if (cur_reason->osr_kcd_buf == NULL) {
227 return NULL;
228 }
229
230 assert(cur_reason->osr_kcd_descriptor.kcd_addr_begin == (mach_vm_address_t) cur_reason->osr_kcd_buf);
231 if (cur_reason->osr_kcd_descriptor.kcd_addr_begin != (mach_vm_address_t) cur_reason->osr_kcd_buf) {
232 return NULL;
233 }
234
235 return &cur_reason->osr_kcd_descriptor;
236 }
237
238 /*
239 * Takes a reference on the passed reason.
240 */
241 void
242 os_reason_ref(os_reason_t cur_reason)
243 {
244 if (cur_reason == OS_REASON_NULL) {
245 return;
246 }
247
248 lck_mtx_lock(&cur_reason->osr_lock);
249
250 assert(cur_reason->osr_refcount > 0);
251 cur_reason->osr_refcount++;
252
253 lck_mtx_unlock(&cur_reason->osr_lock);
254
255 return;
256 }
257
258 /*
259 * Drops a reference on the passed reason, deallocates
260 * the reason if no references remain.
261 */
262 void
263 os_reason_free(os_reason_t cur_reason)
264 {
265 if (cur_reason == OS_REASON_NULL) {
266 return;
267 }
268
269 lck_mtx_lock(&cur_reason->osr_lock);
270
271 assert(cur_reason->osr_refcount > 0);
272
273 cur_reason->osr_refcount--;
274 if (cur_reason->osr_refcount != 0) {
275 lck_mtx_unlock(&cur_reason->osr_lock);
276 return;
277 }
278
279 os_reason_dealloc_buffer(cur_reason);
280
281 lck_mtx_unlock(&cur_reason->osr_lock);
282 lck_mtx_destroy(&cur_reason->osr_lock, os_reason_lock_grp);
283
284 zfree(os_reason_zone, cur_reason);
285 }