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