]> git.saurik.com Git - apple/xnu.git/blob - osfmk/kern/kern_cdata.c
xnu-3789.51.2.tar.gz
[apple/xnu.git] / osfmk / kern / kern_cdata.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
29 #include <kern/assert.h>
30 #include <mach/mach_types.h>
31 #include <mach/boolean.h>
32 #include <mach/vm_param.h>
33 #include <kern/kern_types.h>
34 #include <kern/mach_param.h>
35 #include <kern/thread.h>
36 #include <kern/task.h>
37 #include <kern/kern_cdata.h>
38 #include <kern/kalloc.h>
39 #include <mach/mach_vm.h>
40
41 static kern_return_t kcdata_get_memory_addr_with_flavor(kcdata_descriptor_t data, uint32_t type, uint32_t size, uint64_t flags, mach_vm_address_t *user_addr);
42
43 /*
44 * Estimates how large of a buffer that should be allocated for a buffer that will contain
45 * num_items items of known types with overall length payload_size.
46 *
47 * NOTE: This function will not give an accurate estimate for buffers that will
48 * contain unknown types (those with string descriptions).
49 */
50 uint32_t kcdata_estimate_required_buffer_size(uint32_t num_items, uint32_t payload_size)
51 {
52 /*
53 * In the worst case each item will need (KCDATA_ALIGNMENT_SIZE - 1) padding
54 */
55 uint32_t max_padding_bytes = num_items * (KCDATA_ALIGNMENT_SIZE - 1);
56 uint32_t item_description_bytes = num_items * sizeof(struct kcdata_item);
57 uint32_t begin_and_end_marker_bytes = 2 * sizeof(struct kcdata_item);
58
59 return max_padding_bytes + item_description_bytes + begin_and_end_marker_bytes + payload_size;
60 }
61
62 kcdata_descriptor_t kcdata_memory_alloc_init(mach_vm_address_t buffer_addr_p, unsigned data_type, unsigned size, unsigned flags)
63 {
64 kcdata_descriptor_t data = NULL;
65 mach_vm_address_t user_addr = 0;
66
67 data = kalloc(sizeof(struct kcdata_descriptor));
68 if (data == NULL) {
69 return NULL;
70 }
71 bzero(data, sizeof(struct kcdata_descriptor));
72 data->kcd_addr_begin = buffer_addr_p;
73 data->kcd_addr_end = buffer_addr_p;
74 data->kcd_flags = (flags & KCFLAG_USE_COPYOUT)? KCFLAG_USE_COPYOUT : KCFLAG_USE_MEMCOPY;
75 data->kcd_length = size;
76
77 /* Initialize the BEGIN header */
78 if (KERN_SUCCESS != kcdata_get_memory_addr(data, data_type, 0, &user_addr)){
79 kcdata_memory_destroy(data);
80 return NULL;
81 }
82
83 return data;
84 }
85
86 kern_return_t kcdata_memory_static_init(kcdata_descriptor_t data, mach_vm_address_t buffer_addr_p, unsigned data_type, unsigned size, unsigned flags)
87 {
88 mach_vm_address_t user_addr = 0;
89
90 if (data == NULL) {
91 return KERN_INVALID_ARGUMENT;
92 }
93 bzero(data, sizeof(struct kcdata_descriptor));
94 data->kcd_addr_begin = buffer_addr_p;
95 data->kcd_addr_end = buffer_addr_p;
96 data->kcd_flags = (flags & KCFLAG_USE_COPYOUT)? KCFLAG_USE_COPYOUT : KCFLAG_USE_MEMCOPY;
97 data->kcd_length = size;
98
99 /* Initialize the BEGIN header */
100 return kcdata_get_memory_addr(data, data_type, 0, &user_addr);
101 }
102
103 uint64_t kcdata_memory_get_used_bytes(kcdata_descriptor_t kcd)
104 {
105 assert(kcd != NULL);
106 return ((uint64_t)kcd->kcd_addr_end - (uint64_t)kcd->kcd_addr_begin) + sizeof(struct kcdata_item);
107 }
108
109 /*
110 * Free up the memory associated with kcdata
111 */
112 kern_return_t kcdata_memory_destroy(kcdata_descriptor_t data)
113 {
114 if (!data) {
115 return KERN_INVALID_ARGUMENT;
116 }
117
118 /*
119 * data->kcd_addr_begin points to memory in not tracked by
120 * kcdata lib. So not clearing that here.
121 */
122 kfree(data, sizeof(struct kcdata_descriptor));
123 return KERN_SUCCESS;
124 }
125
126
127
128 /*
129 * Routine: kcdata_get_memory_addr
130 * Desc: get memory address in the userspace memory for corpse info
131 * NOTE: The caller is responsible for zeroing the resulting memory or
132 * using other means to mark memory if it has failed populating the
133 * data in middle of operation.
134 * params: data - pointer describing the crash info allocation
135 * type - type of data to be put. See corpse.h for defined types
136 * size - size requested. The header describes this size
137 * returns: mach_vm_address_t address in user memory for copyout().
138 */
139 kern_return_t
140 kcdata_get_memory_addr(kcdata_descriptor_t data, uint32_t type, uint32_t size, mach_vm_address_t * user_addr)
141 {
142 /* record number of padding bytes as lower 4 bits of flags */
143 uint64_t flags = (KCDATA_FLAGS_STRUCT_PADDING_MASK & kcdata_calc_padding(size)) | KCDATA_FLAGS_STRUCT_HAS_PADDING;
144 return kcdata_get_memory_addr_with_flavor(data, type, size, flags, user_addr);
145 }
146
147 /*
148 * Routine: kcdata_add_buffer_end
149 *
150 * Desc: Write buffer end marker. This does not advance the end pointer in the
151 * kcdata_descriptor_t, so it may be used conservatively before additional data
152 * is added, as long as it is at least called after the last time data is added.
153 *
154 * params: data - pointer describing the crash info allocation
155 */
156
157 kern_return_t
158 kcdata_write_buffer_end(kcdata_descriptor_t data)
159 {
160 struct kcdata_item info;
161 bzero(&info, sizeof(info));
162 info.type = KCDATA_TYPE_BUFFER_END;
163 info.size = 0;
164 return kcdata_memcpy(data, data->kcd_addr_end, &info, sizeof(info));
165 }
166
167 /*
168 * Routine: kcdata_get_memory_addr_with_flavor
169 * Desc: internal function with flags field. See documentation for kcdata_get_memory_addr for details
170 */
171
172 static kern_return_t kcdata_get_memory_addr_with_flavor(
173 kcdata_descriptor_t data,
174 uint32_t type,
175 uint32_t size,
176 uint64_t flags,
177 mach_vm_address_t *user_addr)
178 {
179 struct kcdata_item info;
180 uint32_t total_size;
181
182 if (user_addr == NULL || data == NULL) {
183 return KERN_INVALID_ARGUMENT;
184 }
185
186 /* make sure 16 byte aligned */
187 size += kcdata_calc_padding(size);
188
189 bzero(&info, sizeof(info));
190 info.type = type;
191 info.size = size;
192 info.flags = flags;
193 total_size = size + sizeof(info);
194
195 /* check available memory, including trailer size for KCDATA_TYPE_BUFFER_END */
196 if (data->kcd_length < ((data->kcd_addr_end - data->kcd_addr_begin) + total_size + sizeof(info))) {
197 return KERN_RESOURCE_SHORTAGE;
198 }
199
200 if (data->kcd_flags & KCFLAG_USE_COPYOUT) {
201 if (copyout(&info, data->kcd_addr_end, sizeof(info)))
202 return KERN_NO_ACCESS;
203 } else {
204 memcpy((void *)data->kcd_addr_end, &info, sizeof(info));
205 }
206
207 data->kcd_addr_end += sizeof(info);
208 *user_addr = data->kcd_addr_end;
209 data->kcd_addr_end += size;
210
211 if (!(data->kcd_flags & KCFLAG_NO_AUTO_ENDBUFFER)) {
212 /* setup the end header as well */
213 return kcdata_write_buffer_end(data);
214 } else {
215 return KERN_SUCCESS;
216 }
217 }
218
219 /*
220 * Routine: kcdata_get_memory_addr_for_array
221 * Desc: get memory address in the userspace memory for corpse info
222 * NOTE: The caller is responsible to zero the resulting memory or
223 * user other means to mark memory if it has failed populating the
224 * data in middle of operation.
225 * params: data - pointer describing the crash info allocation
226 * type_of_element - type of data to be put. See kern_cdata.h for defined types
227 * size_of_element - size of element. The header describes this size
228 * count - num of elements in array.
229 * returns: mach_vm_address_t address in user memory for copyout().
230 */
231
232 kern_return_t kcdata_get_memory_addr_for_array(
233 kcdata_descriptor_t data,
234 uint32_t type_of_element,
235 uint32_t size_of_element,
236 uint32_t count,
237 mach_vm_address_t *user_addr)
238 {
239 /* for arrays we record the number of padding bytes as the low-order 4 bits
240 * of the type field. KCDATA_TYPE_ARRAY_PAD{x} means x bytes of pad. */
241 uint64_t flags = type_of_element;
242 flags = (flags << 32) | count;
243 uint32_t total_size = count * size_of_element;
244 uint32_t pad = kcdata_calc_padding(total_size);
245
246 return kcdata_get_memory_addr_with_flavor(data, KCDATA_TYPE_ARRAY_PAD0 | pad, total_size, flags, user_addr);
247 }
248
249 /*
250 * Routine: kcdata_add_container_marker
251 * Desc: Add a container marker in the buffer for type and identifier.
252 * params: data - pointer describing the crash info allocation
253 * header_type - one of (KCDATA_TYPE_CONTAINER_BEGIN ,KCDATA_TYPE_CONTAINER_END)
254 * container_type - type of data to be put. See kern_cdata.h for defined types
255 * identifier - unique identifier. This is required to match nested containers.
256 * returns: return value of kcdata_get_memory_addr()
257 */
258
259 kern_return_t kcdata_add_container_marker(
260 kcdata_descriptor_t data,
261 uint32_t header_type,
262 uint32_t container_type,
263 uint64_t identifier)
264 {
265 mach_vm_address_t user_addr;
266 kern_return_t kr;
267 assert(header_type == KCDATA_TYPE_CONTAINER_END || header_type == KCDATA_TYPE_CONTAINER_BEGIN);
268 uint32_t data_size = (header_type == KCDATA_TYPE_CONTAINER_BEGIN)? sizeof(uint32_t): 0;
269 kr = kcdata_get_memory_addr_with_flavor(data, header_type, data_size, identifier, &user_addr);
270 if (kr != KERN_SUCCESS)
271 return kr;
272
273 if (data_size)
274 kr = kcdata_memcpy(data, user_addr, &container_type, data_size);
275 return kr;
276 }
277
278 /*
279 * Routine: kcdata_undo_addcontainer_begin
280 * Desc: call this after adding a container begin but before adding anything else to revert.
281 */
282 kern_return_t
283 kcdata_undo_add_container_begin(kcdata_descriptor_t data)
284 {
285 /*
286 * the payload of a container begin is a single uint64_t. It is padded out
287 * to 16 bytes.
288 */
289 const mach_vm_address_t padded_payload_size = 16;
290 data->kcd_addr_end -= sizeof(struct kcdata_item) + padded_payload_size;
291
292 if (!(data->kcd_flags & KCFLAG_NO_AUTO_ENDBUFFER)) {
293 /* setup the end header as well */
294 return kcdata_write_buffer_end(data);
295 } else {
296 return KERN_SUCCESS;
297 }
298 }
299
300 /*
301 * Routine: kcdata_memcpy
302 * Desc: a common function to copy data out based on either copyout or memcopy flags
303 * params: data - pointer describing the kcdata buffer
304 * dst_addr - destination address
305 * src_addr - source address
306 * size - size in bytes to copy.
307 * returns: KERN_NO_ACCESS if copyout fails.
308 */
309
310 kern_return_t kcdata_memcpy(kcdata_descriptor_t data, mach_vm_address_t dst_addr, void *src_addr, uint32_t size)
311 {
312 if (data->kcd_flags & KCFLAG_USE_COPYOUT) {
313 if (copyout(src_addr, dst_addr, size))
314 return KERN_NO_ACCESS;
315 } else {
316 memcpy((void *)dst_addr, src_addr, size);
317 }
318 return KERN_SUCCESS;
319 }
320
321 /*
322 * Routine: kcdata_add_type_definition
323 * Desc: add type definition to kcdata buffer.
324 * see feature description in documentation above.
325 * params: data - pointer describing the kcdata buffer
326 * type_id - unique type identifier for this data
327 * type_name - a string of max KCDATA_DESC_MAXLEN size for name of type
328 * elements_array - address to descriptors for each field in struct
329 * elements_count - count of how many fields are there in struct.
330 * returns: return code from kcdata_get_memory_addr in case of failure.
331 */
332
333 kern_return_t kcdata_add_type_definition(
334 kcdata_descriptor_t data,
335 uint32_t type_id,
336 char *type_name,
337 struct kcdata_subtype_descriptor *elements_array_addr,
338 uint32_t elements_count)
339 {
340 kern_return_t kr = KERN_SUCCESS;
341 struct kcdata_type_definition kc_type_definition;
342 mach_vm_address_t user_addr;
343 uint32_t total_size = sizeof(struct kcdata_type_definition);
344 bzero(&kc_type_definition, sizeof(kc_type_definition));
345
346 if (strnlen(type_name, KCDATA_DESC_MAXLEN + 1) >= KCDATA_DESC_MAXLEN)
347 return KERN_INVALID_ARGUMENT;
348 strlcpy(&kc_type_definition.kct_name[0], type_name, KCDATA_DESC_MAXLEN);
349 kc_type_definition.kct_num_elements = elements_count;
350 kc_type_definition.kct_type_identifier = type_id;
351
352 total_size += elements_count * sizeof(struct kcdata_subtype_descriptor);
353 /* record number of padding bytes as lower 4 bits of flags */
354 if (KERN_SUCCESS != (kr = kcdata_get_memory_addr_with_flavor(data, KCDATA_TYPE_TYPEDEFINTION, total_size,
355 kcdata_calc_padding(total_size), &user_addr)))
356 return kr;
357 if (KERN_SUCCESS != (kr = kcdata_memcpy(data, user_addr, (void *)&kc_type_definition, sizeof(struct kcdata_type_definition))))
358 return kr;
359 user_addr += sizeof(struct kcdata_type_definition);
360 if (KERN_SUCCESS != (kr = kcdata_memcpy(data, user_addr, (void *)elements_array_addr, elements_count * sizeof(struct kcdata_subtype_descriptor))))
361 return kr;
362 return kr;
363 }
364
365 #pragma pack(4)
366
367 /* Internal structs for convenience */
368 struct _uint64_with_description_data {
369 char desc[KCDATA_DESC_MAXLEN];
370 uint64_t data;
371 };
372
373 struct _uint32_with_description_data {
374 char desc[KCDATA_DESC_MAXLEN];
375 uint32_t data;
376 };
377
378 #pragma pack()
379
380 kern_return_t
381 kcdata_add_uint64_with_description(kcdata_descriptor_t data_desc, uint64_t data, const char * description)
382 {
383 if (strnlen(description, KCDATA_DESC_MAXLEN + 1) >= KCDATA_DESC_MAXLEN)
384 return KERN_INVALID_ARGUMENT;
385
386 kern_return_t kr = 0;
387 mach_vm_address_t user_addr;
388 struct _uint64_with_description_data save_data;
389 const uint64_t size_req = sizeof(save_data);
390 bzero(&save_data, size_req);
391
392 strlcpy(&(save_data.desc[0]), description, sizeof(save_data.desc));
393 save_data.data = data;
394
395 kr = kcdata_get_memory_addr(data_desc, KCDATA_TYPE_UINT64_DESC, size_req, &user_addr);
396 if (kr != KERN_SUCCESS)
397 return kr;
398
399 if (data_desc->kcd_flags & KCFLAG_USE_COPYOUT) {
400 if (copyout(&save_data, user_addr, size_req))
401 return KERN_NO_ACCESS;
402 } else {
403 memcpy((void *)user_addr, &save_data, size_req);
404 }
405 return KERN_SUCCESS;
406 }
407
408 kern_return_t kcdata_add_uint32_with_description(
409 kcdata_descriptor_t data_desc,
410 uint32_t data,
411 const char *description)
412 {
413 assert(strlen(description) < KCDATA_DESC_MAXLEN);
414 if (strnlen(description, KCDATA_DESC_MAXLEN + 1) >= KCDATA_DESC_MAXLEN)
415 return KERN_INVALID_ARGUMENT;
416 kern_return_t kr = 0;
417 mach_vm_address_t user_addr;
418 struct _uint32_with_description_data save_data;
419 const uint64_t size_req = sizeof(save_data);
420
421 bzero(&save_data, size_req);
422 strlcpy(&(save_data.desc[0]), description, sizeof(save_data.desc));
423 save_data.data = data;
424
425 kr = kcdata_get_memory_addr(data_desc, KCDATA_TYPE_UINT32_DESC, size_req, &user_addr);
426 if (kr != KERN_SUCCESS)
427 return kr;
428 if (data_desc->kcd_flags & KCFLAG_USE_COPYOUT) {
429 if (copyout(&save_data, user_addr, size_req))
430 return KERN_NO_ACCESS;
431 } else {
432 memcpy((void *)user_addr, &save_data, size_req);
433 }
434 return KERN_SUCCESS;
435 }
436
437
438 /* end buffer management api */