]> git.saurik.com Git - apple/xnu.git/blob - osfmk/kern/kern_cdata.c
503032ae7774685fb90daa828bffe04d5af797c0
[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 /*
42 *
43 * The format for data is setup in a generic format as follows
44 *
45 * Layout of data structure:
46 *
47 * | 8 - bytes |
48 * | type = MAGIC | LENGTH |
49 * | 0 |
50 * | type | size |
51 * | flags |
52 * | data |
53 * |___________data____________|
54 * | type | size |
55 * | flags |
56 * |___________data____________|
57 * | type = END | size=0 |
58 * | 0 |
59 *
60 *
61 * The type field describes what kind of data is passed. For example type = TASK_CRASHINFO_UUID means the following data is a uuid.
62 * These types need to be defined in task_corpses.h for easy consumption by userspace inspection tools.
63 *
64 * Some range of types is reserved for special types like ints, longs etc. A cool new functionality made possible with this
65 * extensible data format is that kernel can decide to put more information as required without requiring user space tools to
66 * re-compile to be compatible. The case of rusage struct versions could be introduced without breaking existing tools.
67 *
68 * Feature description: Generic data with description
69 * -------------------
70 * Further more generic data with description is very much possible now. For example
71 *
72 * - kcdata_add_uint64_with_description(cdatainfo, 0x700, "NUM MACH PORTS");
73 * - and more functions that allow adding description.
74 * The userspace tools can then look at the description and print the data even if they are not compiled with knowledge of the field apriori.
75 *
76 * Example data:
77 * 0000 57 f1 ad de 00 00 00 00 00 00 00 00 00 00 00 00 W...............
78 * 0010 01 00 00 00 00 00 00 00 30 00 00 00 00 00 00 00 ........0.......
79 * 0020 50 49 44 00 00 00 00 00 00 00 00 00 00 00 00 00 PID.............
80 * 0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
81 * 0040 9c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
82 * 0050 01 00 00 00 00 00 00 00 30 00 00 00 00 00 00 00 ........0.......
83 * 0060 50 41 52 45 4e 54 20 50 49 44 00 00 00 00 00 00 PARENT PID......
84 * 0070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
85 * 0080 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
86 * 0090 ed 58 91 f1
87 *
88 * Feature description: Container markers for compound data
89 * ------------------
90 * If a given kernel data type is complex and requires adding multiple optional fields inside a container
91 * object for a consumer to understand arbitrary data, we package it using container markers.
92 *
93 * For example, the stackshot code gathers information and describes the state of a given task with respect
94 * to many subsystems. It includes data such as io stats, vm counters, process names/flags and syscall counts.
95 *
96 * kcdata_add_container_marker(kcdata_p, KCDATA_TYPE_CONTAINER_BEGIN, STACKSHOT_KCCONTAINER_TASK, task_uniqueid);
97 * // add multiple data, or add_<type>_with_description()s here
98 *
99 * kcdata_add_container_marker(kcdata_p, KCDATA_TYPE_CONTAINER_END, STACKSHOT_KCCONTAINER_TASK, task_uniqueid);
100 *
101 * Feature description: Custom Data formats on demand
102 * --------------------
103 * With the self describing nature of format, the kernel provider can describe a data type (uniquely identified by a number) and use
104 * it in the buffer for sending data. The consumer can parse the type information and have knowledge of describing incoming data.
105 * Following is an example of how we can describe a kernel specific struct sample_disk_io_stats in buffer.
106 *
107 * struct sample_disk_io_stats {
108 * uint64_t disk_reads_count;
109 * uint64_t disk_reads_size;
110 * uint64_t io_priority_count[4];
111 * uint64_t io_priority_size;
112 * } __attribute__ ((packed));
113 *
114 *
115 * struct kcdata_subtype_descriptor disk_io_stats_def[] = {
116 * {KCS_SUBTYPE_FLAGS_NONE, KC_ST_UINT64, 0 * sizeof(uint64_t), sizeof(uint64_t), "disk_reads_count"},
117 * {KCS_SUBTYPE_FLAGS_NONE, KC_ST_UINT64, 1 * sizeof(uint64_t), sizeof(uint64_t), "disk_reads_size"},
118 * {KCS_SUBTYPE_FLAGS_ARRAY, KC_ST_UINT64, 2 * sizeof(uint64_t), KCS_SUBTYPE_PACK_SIZE(4, sizeof(uint64_t)), "io_priority_count"},
119 * {KCS_SUBTYPE_FLAGS_ARRAY, KC_ST_UINT64, (2 + 4) * sizeof(uint64_t), sizeof(uint64_t), "io_priority_size"},
120 * };
121 *
122 * Now you can add this custom type definition into the buffer as
123 * kcdata_add_type_definition(kcdata_p, KCTYPE_SAMPLE_DISK_IO_STATS, "sample_disk_io_stats",
124 * &disk_io_stats_def[0], sizeof(disk_io_stats_def)/sizeof(struct kcdata_subtype_descriptor));
125 *
126 */
127
128 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);
129
130 kcdata_descriptor_t kcdata_memory_alloc_init(mach_vm_address_t buffer_addr_p, unsigned data_type, unsigned size, unsigned flags)
131 {
132 kcdata_descriptor_t data = NULL;
133 mach_vm_address_t user_addr = 0;
134
135 data = kalloc(sizeof(struct kcdata_descriptor));
136 if (data == NULL) {
137 return NULL;
138 }
139 bzero(data, sizeof(struct kcdata_descriptor));
140 data->kcd_addr_begin = buffer_addr_p;
141 data->kcd_addr_end = buffer_addr_p;
142 data->kcd_flags = (flags & KCFLAG_USE_COPYOUT)? KCFLAG_USE_COPYOUT : KCFLAG_USE_MEMCOPY;
143 data->kcd_length = size;
144
145 /* Initialize the BEGIN header */
146 if (KERN_SUCCESS != kcdata_get_memory_addr(data, data_type, 0, &user_addr)){
147 kcdata_memory_destroy(data);
148 return NULL;
149 }
150
151 return data;
152 }
153
154 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)
155 {
156 mach_vm_address_t user_addr = 0;
157
158 if (data == NULL) {
159 return KERN_INVALID_ARGUMENT;
160 }
161 bzero(data, sizeof(struct kcdata_descriptor));
162 data->kcd_addr_begin = buffer_addr_p;
163 data->kcd_addr_end = buffer_addr_p;
164 data->kcd_flags = (flags & KCFLAG_USE_COPYOUT)? KCFLAG_USE_COPYOUT : KCFLAG_USE_MEMCOPY;
165 data->kcd_length = size;
166
167 /* Initialize the BEGIN header */
168 return kcdata_get_memory_addr(data, data_type, 0, &user_addr);
169 }
170
171 uint64_t kcdata_memory_get_used_bytes(kcdata_descriptor_t kcd)
172 {
173 assert(kcd != NULL);
174 return ((uint64_t)kcd->kcd_addr_end - (uint64_t)kcd->kcd_addr_begin) + sizeof(struct kcdata_item);
175 }
176
177 /*
178 * Free up the memory associated with kcdata
179 */
180 kern_return_t kcdata_memory_destroy(kcdata_descriptor_t data)
181 {
182 if (!data) {
183 return KERN_INVALID_ARGUMENT;
184 }
185
186 /*
187 * data->kcd_addr_begin points to memory in not tracked by
188 * kcdata lib. So not clearing that here.
189 */
190 kfree(data, sizeof(struct kcdata_descriptor));
191 return KERN_SUCCESS;
192 }
193
194
195
196 /*
197 * Routine: kcdata_get_memory_addr
198 * Desc: get memory address in the userspace memory for corpse info
199 * NOTE: The caller is responsible to zero the resulting memory or
200 * user other means to mark memory if it has failed populating the
201 * data in middle of operation.
202 * params: data - pointer describing the crash info allocation
203 * type - type of data to be put. See corpse.h for defined types
204 * size - size requested. The header describes this size
205 * returns: mach_vm_address_t address in user memory for copyout().
206 */
207 kern_return_t kcdata_get_memory_addr(
208 kcdata_descriptor_t data,
209 uint32_t type,
210 uint32_t size,
211 mach_vm_address_t *user_addr)
212 {
213 return kcdata_get_memory_addr_with_flavor(data, type, size, 0, user_addr);
214 }
215
216 /*
217 * Routine: kcdata_get_memory_addr_with_flavor
218 * Desc: internal function with flags field. See documentation for kcdata_get_memory_addr for details
219 */
220
221 static kern_return_t kcdata_get_memory_addr_with_flavor(
222 kcdata_descriptor_t data,
223 uint32_t type,
224 uint32_t size,
225 uint64_t flags,
226 mach_vm_address_t *user_addr)
227 {
228 struct kcdata_item info;
229 uint32_t total_size;
230
231 if (user_addr == NULL || data == NULL) {
232 return KERN_INVALID_ARGUMENT;
233 }
234
235 /* make sure 16 byte aligned */
236 if (size & 0xf) {
237 size += (0x10 - (size & 0xf));
238 }
239
240 bzero(&info, sizeof(info));
241 KCDATA_ITEM_TYPE(&info) = type;
242 KCDATA_ITEM_SIZE(&info) = size;
243 KCDATA_ITEM_FLAGS(&info) = flags;
244 total_size = size + sizeof(info);
245
246 /* check available memory, including trailer size for KCDATA_TYPE_BUFFER_END */
247 if (data->kcd_length < ((data->kcd_addr_end - data->kcd_addr_begin) + total_size + sizeof(info))) {
248 return KERN_RESOURCE_SHORTAGE;
249 }
250
251 if (data->kcd_flags & KCFLAG_USE_COPYOUT) {
252 if (copyout(&info, data->kcd_addr_end, sizeof(info)))
253 return KERN_NO_ACCESS;
254 } else {
255 memcpy((void *)data->kcd_addr_end, &info, sizeof(info));
256 }
257
258 data->kcd_addr_end += sizeof(info);
259 *user_addr = data->kcd_addr_end;
260 data->kcd_addr_end += size;
261
262 /* setup the end header as well */
263 bzero(&info, sizeof(info));
264 KCDATA_ITEM_TYPE(&info) = KCDATA_TYPE_BUFFER_END;
265 KCDATA_ITEM_SIZE(&info) = 0;
266
267 if (data->kcd_flags & KCFLAG_USE_COPYOUT) {
268 if (copyout(&info, data->kcd_addr_end, sizeof(info)))
269 return KERN_NO_ACCESS;
270 } else {
271 memcpy((void *)data->kcd_addr_end, &info, sizeof(info));
272 }
273
274 return KERN_SUCCESS;
275 }
276
277 /*
278 * Routine: kcdata_get_memory_addr_for_array
279 * Desc: get memory address in the userspace memory for corpse info
280 * NOTE: The caller is responsible to zero the resulting memory or
281 * user other means to mark memory if it has failed populating the
282 * data in middle of operation.
283 * params: data - pointer describing the crash info allocation
284 * type_of_element - type of data to be put. See kern_cdata.h for defined types
285 * size_of_element - size of element. The header describes this size
286 * count - num of elements in array.
287 * returns: mach_vm_address_t address in user memory for copyout().
288 */
289
290 kern_return_t kcdata_get_memory_addr_for_array(
291 kcdata_descriptor_t data,
292 uint32_t type_of_element,
293 uint32_t size_of_element,
294 uint32_t count,
295 mach_vm_address_t *user_addr)
296 {
297 uint64_t flags = type_of_element;
298 flags = (flags << 32) | count;
299 uint32_t total_size = count * size_of_element;
300 return kcdata_get_memory_addr_with_flavor(data, KCDATA_TYPE_ARRAY, total_size, flags, user_addr);
301 }
302
303 /*
304 * Routine: kcdata_add_container_marker
305 * Desc: Add a container marker in the buffer for type and identifier.
306 * params: data - pointer describing the crash info allocation
307 * header_type - one of (KCDATA_TYPE_CONTAINER_BEGIN ,KCDATA_TYPE_CONTAINER_END)
308 * container_type - type of data to be put. See kern_cdata.h for defined types
309 * identifier - unique identifier. This is required to match nested containers.
310 * returns: return value of kcdata_get_memory_addr()
311 */
312
313 kern_return_t kcdata_add_container_marker(
314 kcdata_descriptor_t data,
315 uint32_t header_type,
316 uint32_t container_type,
317 uint64_t identifier)
318 {
319 mach_vm_address_t user_addr;
320 kern_return_t kr;
321 assert(header_type == KCDATA_TYPE_CONTAINER_END || header_type == KCDATA_TYPE_CONTAINER_BEGIN);
322 uint32_t data_size = (header_type == KCDATA_TYPE_CONTAINER_BEGIN)? sizeof(uint32_t): 0;
323 kr = kcdata_get_memory_addr_with_flavor(data, header_type, data_size, identifier, &user_addr);
324 if (kr != KERN_SUCCESS)
325 return kr;
326
327 if (data_size)
328 kr = kcdata_memcpy(data, user_addr, &container_type, data_size);
329 return kr;
330 }
331
332 /*
333 * Routine: kcdata_memcpy
334 * Desc: a common function to copy data out based on either copyout or memcopy flags
335 * params: data - pointer describing the kcdata buffer
336 * dst_addr - destination address
337 * src_addr - source address
338 * size - size in bytes to copy.
339 * returns: KERN_NO_ACCESS if copyout fails.
340 */
341
342 kern_return_t kcdata_memcpy(kcdata_descriptor_t data, mach_vm_address_t dst_addr, void *src_addr, uint32_t size)
343 {
344 if (data->kcd_flags & KCFLAG_USE_COPYOUT) {
345 if (copyout(src_addr, dst_addr, size))
346 return KERN_NO_ACCESS;
347 } else {
348 memcpy((void *)dst_addr, src_addr, size);
349 }
350 return KERN_SUCCESS;
351 }
352
353 /*
354 * Routine: kcdata_add_type_definition
355 * Desc: add type definition to kcdata buffer.
356 * see feature description in documentation above.
357 * params: data - pointer describing the kcdata buffer
358 * type_id - unique type identifier for this data
359 * type_name - a string of max KCDATA_DESC_MAXLEN size for name of type
360 * elements_array - address to descriptors for each field in struct
361 * elements_count - count of how many fields are there in struct.
362 * returns: return code from kcdata_get_memory_addr in case of failure.
363 */
364
365 kern_return_t kcdata_add_type_definition(
366 kcdata_descriptor_t data,
367 uint32_t type_id,
368 char *type_name,
369 struct kcdata_subtype_descriptor *elements_array_addr,
370 uint32_t elements_count)
371 {
372 kern_return_t kr = KERN_SUCCESS;
373 struct kcdata_type_definition kc_type_definition;
374 mach_vm_address_t user_addr;
375 uint32_t total_size = sizeof(struct kcdata_type_definition);
376
377 if (strnlen(type_name, KCDATA_DESC_MAXLEN + 1) >= KCDATA_DESC_MAXLEN)
378 return KERN_INVALID_ARGUMENT;
379 strlcpy(&kc_type_definition.kct_name[0], type_name, KCDATA_DESC_MAXLEN);
380 kc_type_definition.kct_num_elements = elements_count;
381 kc_type_definition.kct_type_identifier = type_id;
382
383 total_size += elements_count * sizeof(struct kcdata_subtype_descriptor);
384 if (KERN_SUCCESS != (kr = kcdata_get_memory_addr_with_flavor(data, KCDATA_TYPE_TYPEDEFINTION, total_size, 0, &user_addr)))
385 return kr;
386 if (KERN_SUCCESS != (kr = kcdata_memcpy(data, user_addr, (void *)&kc_type_definition, sizeof(struct kcdata_type_definition))))
387 return kr;
388 user_addr += sizeof(struct kcdata_type_definition);
389 if (KERN_SUCCESS != (kr = kcdata_memcpy(data, user_addr, (void *)elements_array_addr, elements_count * sizeof(struct kcdata_subtype_descriptor))))
390 return kr;
391 return kr;
392 }
393
394 #pragma pack(4)
395
396 /* Internal structs for convenience */
397 struct _uint64_with_description_data {
398 char desc[KCDATA_DESC_MAXLEN];
399 uint64_t data;
400 };
401
402 struct _uint32_with_description_data {
403 char desc[KCDATA_DESC_MAXLEN];
404 uint32_t data;
405 };
406
407 #pragma pack()
408
409 kern_return_t kcdata_add_uint64_with_description(
410 kcdata_descriptor_t data_desc,
411 uint64_t data,
412 const char *description)
413 {
414 if (strnlen(description, KCDATA_DESC_MAXLEN + 1) >= KCDATA_DESC_MAXLEN)
415 return KERN_INVALID_ARGUMENT;
416
417 kern_return_t kr = 0;
418 mach_vm_address_t user_addr;
419 struct _uint64_with_description_data save_data;
420 const uint64_t size_req = sizeof(save_data);
421 bzero(&save_data, size_req);
422
423 strlcpy(&(save_data.desc[0]), description, sizeof(save_data.desc));
424 save_data.data = data;
425
426 kr = kcdata_get_memory_addr(data_desc, KCDATA_TYPE_UINT64_DESC, size_req, &user_addr);
427 if (kr != KERN_SUCCESS)
428 return kr;
429
430 if (data_desc->kcd_flags & KCFLAG_USE_COPYOUT) {
431 if (copyout(&save_data, user_addr, size_req))
432 return KERN_NO_ACCESS;
433 } else {
434 memcpy((void *)user_addr, &save_data, size_req);
435 }
436 return KERN_SUCCESS;
437 }
438
439 kern_return_t kcdata_add_uint32_with_description(
440 kcdata_descriptor_t data_desc,
441 uint32_t data,
442 const char *description)
443 {
444 assert(strlen(description) < KCDATA_DESC_MAXLEN);
445 if (strnlen(description, KCDATA_DESC_MAXLEN + 1) >= KCDATA_DESC_MAXLEN)
446 return KERN_INVALID_ARGUMENT;
447 kern_return_t kr = 0;
448 mach_vm_address_t user_addr;
449 struct _uint32_with_description_data save_data;
450 const uint64_t size_req = sizeof(save_data);
451
452 bzero(&save_data, size_req);
453 strlcpy(&(save_data.desc[0]), description, sizeof(save_data.desc));
454 save_data.data = data;
455
456 kr = kcdata_get_memory_addr(data_desc, KCDATA_TYPE_UINT32_DESC, size_req, &user_addr);
457 if (kr != KERN_SUCCESS)
458 return kr;
459 if (data_desc->kcd_flags & KCFLAG_USE_COPYOUT) {
460 if (copyout(&save_data, user_addr, size_req))
461 return KERN_NO_ACCESS;
462 } else {
463 memcpy((void *)user_addr, &save_data, size_req);
464 }
465 return KERN_SUCCESS;
466 }
467
468
469 /* end buffer management api */