]> git.saurik.com Git - apple/xnu.git/blame - osfmk/atm/atm.c
xnu-3789.70.16.tar.gz
[apple/xnu.git] / osfmk / atm / atm.c
CommitLineData
fe8ab488
A
1/*
2 * Copyright (c) 2012-2013 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 <atm/atm_internal.h>
30#include <mach/mach_types.h>
31#include <mach/kern_return.h>
32#include <ipc/ipc_port.h>
33#include <mach/mach_vm.h>
34#include <mach/vm_map.h>
35#include <vm/vm_map.h>
36#include <atm/atm_notification.h>
37#include <mach/host_priv.h>
38#include <mach/host_special_ports.h>
39#include <kern/host.h>
40#include <kern/kalloc.h>
3e170ce0 41#include <machine/commpage.h>
fe8ab488
A
42
43#define MAX_ATM_VALUES (2 * 4096)
44#define MAX_TRACE_BUFFER_SIZE (0x40000000) /* Restrict to 1GB per task */
fe8ab488
A
45
46#define ATM_VALUE_TO_HANDLE(x) (CAST_DOWN(atm_voucher_id_t, (x)))
47#define HANDLE_TO_ATM_VALUE(x) (CAST_DOWN(atm_value_t, (x)))
48
49#define ATM_MAX_HASH_TABLE_SIZE (256)
50#define AID_HASH_MASK (0xFF)
51#define AID_TO_HASH(x) ((x) & (AID_HASH_MASK))
52
53#define ATM_LIST_DEAD_MAX 15
54
55#define AID_ARRAY_COUNT_MAX (256)
56
57struct atm_value_hash atm_value_hash_table[ATM_MAX_HASH_TABLE_SIZE];
58extern int maxproc;
59
60/* Global flag to disable ATM. ATM get value and memory registration will return error. */
61boolean_t disable_atm = FALSE;
62
63#if DEVELOPMENT || DEBUG
64queue_head_t atm_descriptors_list;
65queue_head_t atm_values_list;
66#endif
67
68ipc_voucher_attr_control_t voucher_attr_control; /* communication channel from ATM to voucher system */
69static zone_t atm_value_zone, atm_descriptors_zone, atm_link_objects_zone;
70
71static aid_t get_aid();
3e170ce0
A
72static mach_atm_subaid_t get_subaid();
73static atm_value_t atm_value_alloc_init(aid_t);
fe8ab488
A
74static void atm_value_dealloc(atm_value_t atm_value);
75static void atm_hash_table_init();
3e170ce0 76static kern_return_t atm_value_hash_table_insert(atm_value_t new_atm_value);
fe8ab488 77static void atm_value_hash_table_delete(atm_value_t atm_value);
3e170ce0 78static atm_value_t get_atm_value_from_aid(aid_t aid) __unused;
fe8ab488 79static void atm_value_get_ref(atm_value_t atm_value);
3e170ce0 80static kern_return_t atm_listener_insert(atm_value_t atm_value, atm_task_descriptor_t task_descriptor, atm_guard_t guard);
fe8ab488 81static void atm_listener_delete_all(atm_value_t atm_value);
3e170ce0 82static atm_task_descriptor_t atm_task_descriptor_alloc_init(mach_port_t trace_buffer,uint64_t buffer_size, __assert_only task_t task);
fe8ab488
A
83static void atm_descriptor_get_reference(atm_task_descriptor_t task_descriptor);
84static void atm_task_descriptor_dealloc(atm_task_descriptor_t task_descriptor);
3e170ce0
A
85static kern_return_t atm_value_unregister(atm_value_t atm_value, atm_task_descriptor_t task_descriptor, atm_guard_t guard);
86static kern_return_t atm_value_register(atm_value_t atm_value, atm_task_descriptor_t task_descriptor, atm_guard_t guard);
87static kern_return_t atm_listener_delete(atm_value_t atm_value, atm_task_descriptor_t task_descriptor, atm_guard_t guard);
88static void atm_link_get_reference(atm_link_object_t link_object) __unused;
fe8ab488 89static void atm_link_dealloc(atm_link_object_t link_object);
3e170ce0
A
90kern_return_t atm_invoke_collection(atm_value_t atm_value, mach_atm_subaid_t subaid, uint32_t flags);
91kern_return_t atm_send_user_notification(aid_t aid, mach_atm_subaid_t sub_aid, mach_port_t *buffers_array, uint64_t *sizes_array, mach_msg_type_number_t count, uint32_t flags);
fe8ab488
A
92
93kern_return_t
94atm_release_value(
95 ipc_voucher_attr_manager_t __assert_only manager,
96 mach_voucher_attr_key_t __assert_only key,
97 mach_voucher_attr_value_handle_t value,
98 mach_voucher_attr_value_reference_t sync);
99
100kern_return_t
101atm_get_value(
102 ipc_voucher_attr_manager_t __assert_only manager,
103 mach_voucher_attr_key_t __assert_only key,
104 mach_voucher_attr_recipe_command_t command,
105 mach_voucher_attr_value_handle_array_t prev_values,
106 mach_msg_type_number_t __assert_only prev_value_count,
107 mach_voucher_attr_content_t recipe,
108 mach_voucher_attr_content_size_t recipe_size,
109 mach_voucher_attr_value_handle_t *out_value,
490019cf 110 mach_voucher_attr_value_flags_t *out_flags,
fe8ab488
A
111 ipc_voucher_t *out_value_voucher);
112
113kern_return_t
114atm_extract_content(
115 ipc_voucher_attr_manager_t __assert_only manager,
116 mach_voucher_attr_key_t __assert_only key,
117 mach_voucher_attr_value_handle_array_t values,
118 mach_msg_type_number_t value_count,
119 mach_voucher_attr_recipe_command_t *out_command,
120 mach_voucher_attr_content_t out_recipe,
121 mach_voucher_attr_content_size_t *in_out_recipe_size);
122
123kern_return_t
124atm_command(
125 ipc_voucher_attr_manager_t __assert_only manager,
126 mach_voucher_attr_key_t __assert_only key,
127 mach_voucher_attr_value_handle_array_t values,
128 mach_msg_type_number_t value_count,
129 mach_voucher_attr_command_t command,
130 mach_voucher_attr_content_t in_content,
131 mach_voucher_attr_content_size_t in_content_size,
132 mach_voucher_attr_content_t out_content,
133 mach_voucher_attr_content_size_t *in_out_content_size);
134
135void
136atm_release(ipc_voucher_attr_manager_t __assert_only manager);
137
138/*
139 * communication channel from voucher system to ATM
140 */
141struct ipc_voucher_attr_manager atm_manager = {
142 .ivam_release_value = atm_release_value,
143 .ivam_get_value = atm_get_value,
144 .ivam_extract_content = atm_extract_content,
145 .ivam_command = atm_command,
146 .ivam_release = atm_release,
490019cf 147 .ivam_flags = IVAM_FLAGS_NONE,
fe8ab488
A
148};
149
150#if DEVELOPMENT || DEBUG
151decl_lck_mtx_data(, atm_descriptors_list_lock);
152decl_lck_mtx_data(, atm_values_list_lock);
153
154lck_grp_t atm_dev_lock_grp;
155lck_attr_t atm_dev_lock_attr;
156lck_grp_attr_t atm_dev_lock_grp_attr;
157#endif
158
159extern vm_map_t kernel_map;
160/*
161 * Global aid. Incremented on each get_aid.
162 */
163aid_t global_aid;
164
3e170ce0
A
165/*
166 * Global subaid. Incremented on each get_subaid.
167 */
168mach_atm_subaid_t global_subaid;
169
fe8ab488
A
170/*
171 * Lock group attributes for atm sub system.
172 */
173lck_grp_t atm_lock_grp;
174lck_attr_t atm_lock_attr;
175lck_grp_attr_t atm_lock_grp_attr;
176
3e170ce0
A
177/*
178 * Global that is set by diagnosticd and readable by userspace
179 * via the commpage.
180 */
181static uint32_t atm_diagnostic_config;
fe8ab488
A
182
183/*
184 * Routine: atm_init
185 * Purpose: Initialize the atm subsystem.
186 * Returns: None.
187 */
188void
189atm_init()
190{
191 kern_return_t kr = KERN_SUCCESS;
192 char temp_buf[20];
193
194 /* Disable atm if disable_atm present in device-tree properties or in boot-args */
195 if ((PE_get_default("kern.disable_atm", temp_buf, sizeof(temp_buf))) ||
196 (PE_parse_boot_argn("-disable_atm", temp_buf, sizeof(temp_buf)))) {
197 disable_atm = TRUE;
198 }
199
3e170ce0
A
200 if (!PE_parse_boot_argn("atm_diagnostic_config", &atm_diagnostic_config, sizeof(atm_diagnostic_config))) {
201 if (!PE_get_default("kern.atm_diagnostic_config", &atm_diagnostic_config, sizeof(atm_diagnostic_config))) {
202 atm_diagnostic_config = 0;
203 }
204 }
205
fe8ab488
A
206 /* setup zones for descriptors, values and link objects */
207 atm_value_zone = zinit(sizeof(struct atm_value),
208 MAX_ATM_VALUES * sizeof(struct atm_value),
209 sizeof(struct atm_value),
210 "atm_values");
211
212 atm_descriptors_zone = zinit(sizeof(struct atm_task_descriptor),
213 MAX_ATM_VALUES * sizeof(struct atm_task_descriptor),
214 sizeof(struct atm_task_descriptor),
215 "atm_task_descriptors");
216
217 atm_link_objects_zone = zinit(sizeof(struct atm_link_object),
218 MAX_ATM_VALUES * sizeof(struct atm_link_object),
219 sizeof(struct atm_link_object),
220 "atm_link_objects");
221
222 /* Initialize atm lock group and lock attributes. */
223 lck_grp_attr_setdefault(&atm_lock_grp_attr);
224 lck_grp_init(&atm_lock_grp, "atm_lock", &atm_lock_grp_attr);
225 lck_attr_setdefault(&atm_lock_attr);
226
227 global_aid = 1;
3e170ce0 228 global_subaid = 1;
fe8ab488
A
229 atm_hash_table_init();
230
231#if DEVELOPMENT || DEBUG
232 /* Initialize global atm development lock group and lock attributes. */
233 lck_grp_attr_setdefault(&atm_dev_lock_grp_attr);
234 lck_grp_init(&atm_dev_lock_grp, "atm_dev_lock", &atm_dev_lock_grp_attr);
235 lck_attr_setdefault(&atm_dev_lock_attr);
236
237 lck_mtx_init(&atm_descriptors_list_lock, &atm_dev_lock_grp, &atm_dev_lock_attr);
238 lck_mtx_init(&atm_values_list_lock, &atm_dev_lock_grp, &atm_dev_lock_attr);
239
240 queue_init(&atm_descriptors_list);
241 queue_init(&atm_values_list);
242#endif
243
244 /* Register the atm manager with the Vouchers sub system. */
245 kr = ipc_register_well_known_mach_voucher_attr_manager(
246 &atm_manager,
247 0,
248 MACH_VOUCHER_ATTR_KEY_ATM,
249 &voucher_attr_control);
250 if (kr != KERN_SUCCESS )
251 panic("ATM subsystem initialization failed");
252
253 kprintf("ATM subsystem is initialized\n");
254 return ;
255}
256
257
258/*
259 * ATM Resource Manager Routines.
260 */
261
262
263/*
264 * Routine: atm_release_value
265 * Purpose: Release a value, if sync matches the sync count in value.
266 * Returns: KERN_SUCCESS: on Successful deletion.
267 * KERN_FAILURE: if sync value does not matches.
268 */
269kern_return_t
270atm_release_value(
271 ipc_voucher_attr_manager_t __assert_only manager,
272 mach_voucher_attr_key_t __assert_only key,
273 mach_voucher_attr_value_handle_t value,
274 mach_voucher_attr_value_reference_t sync)
275{
276 atm_value_t atm_value = ATM_VALUE_NULL;
277
278 assert(MACH_VOUCHER_ATTR_KEY_ATM == key);
279 assert(manager == &atm_manager);
280
281 atm_value = HANDLE_TO_ATM_VALUE(value);
282 if (atm_value == VAM_DEFAULT_VALUE) {
283 /* Return success for default value */
284 return KERN_SUCCESS;
285 }
286
287 if (atm_value->sync != sync) {
288 return KERN_FAILURE;
289 }
290
291 /* Deallocate the atm value. */
292 atm_value_hash_table_delete(atm_value);
293 atm_value_dealloc(atm_value);
294 return KERN_SUCCESS;
295}
296
297
298/*
299 * Routine: atm_get_value
300 */
301kern_return_t
302atm_get_value(
303 ipc_voucher_attr_manager_t __assert_only manager,
304 mach_voucher_attr_key_t __assert_only key,
305 mach_voucher_attr_recipe_command_t command,
306 mach_voucher_attr_value_handle_array_t prev_values,
307 mach_msg_type_number_t __assert_only prev_value_count,
308 mach_voucher_attr_content_t __unused recipe,
309 mach_voucher_attr_content_size_t __unused recipe_size,
310 mach_voucher_attr_value_handle_t *out_value,
490019cf 311 mach_voucher_attr_value_flags_t *out_flags,
fe8ab488
A
312 ipc_voucher_t *out_value_voucher)
313{
314 atm_value_t atm_value = ATM_VALUE_NULL;
315 mach_voucher_attr_value_handle_t atm_handle;
316 atm_task_descriptor_t task_descriptor = ATM_TASK_DESCRIPTOR_NULL;
317 task_t task;
3e170ce0
A
318 aid_t aid;
319 atm_guard_t guard;
fe8ab488
A
320 natural_t i;
321 kern_return_t kr = KERN_SUCCESS;
322
323 assert(MACH_VOUCHER_ATTR_KEY_ATM == key);
324 assert(manager == &atm_manager);
325
326 /* never an out voucher */
327 *out_value_voucher = IPC_VOUCHER_NULL;
490019cf 328 *out_flags = MACH_VOUCHER_ATTR_VALUE_FLAGS_NONE;
fe8ab488 329
3e170ce0 330 if (disable_atm || (atm_get_diagnostic_config() & ATM_TRACE_DISABLE))
fe8ab488
A
331 return KERN_NOT_SUPPORTED;
332
333 switch (command) {
334
335 case MACH_VOUCHER_ATTR_ATM_REGISTER:
336
337 for (i = 0; i < prev_value_count; i++) {
338 atm_handle = prev_values[i];
339 atm_value = HANDLE_TO_ATM_VALUE(atm_handle);
340
341 if (atm_value == VAM_DEFAULT_VALUE)
342 continue;
343
3e170ce0
A
344 if (recipe_size != sizeof(atm_guard_t)) {
345 kr = KERN_INVALID_ARGUMENT;
346 break;
347 }
348 memcpy(&guard, recipe, sizeof(atm_guard_t));
349
fe8ab488
A
350 task = current_task();
351 task_descriptor = task->atm_context;
3e170ce0
A
352
353 kr = atm_value_register(atm_value, task_descriptor, guard);
354 if (kr != KERN_SUCCESS) {
04b8595b 355 break;
fe8ab488
A
356 }
357
358 /* Increment sync value. */
3e170ce0 359 atm_sync_reference_internal(atm_value);
fe8ab488
A
360
361 *out_value = atm_handle;
362 return kr;
363 }
364
365 *out_value = ATM_VALUE_TO_HANDLE(VAM_DEFAULT_VALUE);
366 break;
367
368 case MACH_VOUCHER_ATTR_ATM_CREATE:
369
3e170ce0
A
370 /* Handle the old case where aid value is created in kernel */
371 if (recipe_size == 0) {
372 aid = get_aid();
373 } else if (recipe_size == sizeof(aid_t)) {
374 memcpy(&aid, recipe, sizeof(aid_t));
375 } else {
376 kr = KERN_INVALID_ARGUMENT;
377 break;
378 }
379
fe8ab488 380 /* Allocate a new atm value. */
3e170ce0 381 atm_value = atm_value_alloc_init(aid);
fe8ab488 382 if (atm_value == ATM_VALUE_NULL) {
3e170ce0
A
383 kr = KERN_RESOURCE_SHORTAGE;
384 break;
385 }
386redrive:
387 kr = atm_value_hash_table_insert(atm_value);
388 if (kr != KERN_SUCCESS) {
389 if (recipe_size == 0) {
390 atm_value->aid = get_aid();
391 goto redrive;
392 }
393 atm_value_dealloc(atm_value);
394 break;
fe8ab488
A
395 }
396
397 *out_value = ATM_VALUE_TO_HANDLE(atm_value);
398 break;
399
400 case MACH_VOUCHER_ATTR_ATM_NULL:
401 default:
402 kr = KERN_INVALID_ARGUMENT;
403 break;
404 }
405
406 return kr;
407}
408
409
410/*
411 * Routine: atm_extract_content
412 * Purpose: Extract a set of aid from an array of voucher values.
413 * Returns: KERN_SUCCESS: on Success.
414 * KERN_FAILURE: one of the value is not present in the hash.
415 * KERN_NO_SPACE: insufficeint buffer provided to fill an array of aid.
416 */
417kern_return_t
418atm_extract_content(
419 ipc_voucher_attr_manager_t __assert_only manager,
420 mach_voucher_attr_key_t __assert_only key,
421 mach_voucher_attr_value_handle_array_t values,
422 mach_msg_type_number_t value_count,
423 mach_voucher_attr_recipe_command_t *out_command,
424 mach_voucher_attr_content_t out_recipe,
425 mach_voucher_attr_content_size_t *in_out_recipe_size)
426{
427 atm_value_t atm_value;
428 mach_voucher_attr_value_handle_t atm_handle;
429 natural_t i;
430
431 assert(MACH_VOUCHER_ATTR_KEY_ATM == key);
432 assert(manager == &atm_manager);
433
434 for (i = 0; i < value_count; i++) {
435 atm_handle = values[i];
436 atm_value = HANDLE_TO_ATM_VALUE(atm_handle);
437 if (atm_value == VAM_DEFAULT_VALUE)
438 continue;
439
440 if (( sizeof(aid_t)) > *in_out_recipe_size) {
441 *in_out_recipe_size = 0;
442 return KERN_NO_SPACE;
443 }
444
445 memcpy(&out_recipe[0], &atm_value->aid, sizeof(aid_t));
446 *out_command = MACH_VOUCHER_ATTR_ATM_NULL;
447 *in_out_recipe_size = sizeof(aid_t);
448 return KERN_SUCCESS;
449 }
450
451 *in_out_recipe_size = 0;
452 return KERN_SUCCESS;
453}
454
455/*
456 * Routine: atm_command
457 * Purpose: Execute a command against a set of ATM values.
458 * Returns: KERN_SUCCESS: On successful execution of command.
459 KERN_FAILURE: On failure.
460 */
461kern_return_t
462atm_command(
463 ipc_voucher_attr_manager_t __assert_only manager,
464 mach_voucher_attr_key_t __assert_only key,
465 mach_voucher_attr_value_handle_array_t values,
466 mach_msg_type_number_t value_count,
467 mach_voucher_attr_command_t command,
468 mach_voucher_attr_content_t in_content,
469 mach_voucher_attr_content_size_t in_content_size,
470 mach_voucher_attr_content_t out_content,
471 mach_voucher_attr_content_size_t *out_content_size)
472{
473 assert(MACH_VOUCHER_ATTR_KEY_ATM == key);
474 assert(manager == &atm_manager);
475 atm_value_t atm_value = ATM_VALUE_NULL;
476 natural_t i = 0;
fe8ab488 477 mach_atm_subaid_t *subaid_array = NULL;
3e170ce0 478 mach_atm_subaid_t next_subaid = 0;
fe8ab488
A
479 uint32_t aid_array_count = 0;
480 atm_task_descriptor_t task_descriptor = ATM_TASK_DESCRIPTOR_NULL;
481 task_t task;
482 uint32_t collection_flags = ATM_ACTION_LOGFAIL;
483 kern_return_t kr = KERN_SUCCESS;
3e170ce0 484 atm_guard_t guard;
fe8ab488
A
485
486 switch (command) {
487 case ATM_ACTION_COLLECT:
488 collection_flags = ATM_ACTION_COLLECT;
489 /* Fall through */
490
491 case ATM_ACTION_LOGFAIL: {
492 mach_atm_subaid_t sub_aid = 0;
493
3e170ce0
A
494 if (disable_atm || (atm_get_diagnostic_config() & ATM_TRACE_DISABLE))
495 return KERN_NOT_SUPPORTED;
496
fe8ab488
A
497 /* find the first non-default atm_value */
498 for (i = 0; i < value_count; i++) {
499 atm_value = HANDLE_TO_ATM_VALUE(values[i]);
500 if (atm_value != VAM_DEFAULT_VALUE)
501 break;
502 }
503
504 /* if we are not able to find any atm values
505 * in stack then this call was made in error
506 */
507 if (atm_value == NULL) {
508 return KERN_FAILURE;
509 }
3e170ce0
A
510
511 if (in_content_size >= sizeof(mach_atm_subaid_t)) {
512 sub_aid = *(mach_atm_subaid_t *)(void *)in_content;
fe8ab488
A
513 }
514
fe8ab488
A
515 *out_content_size = 0;
516 kr = atm_invoke_collection(atm_value, sub_aid, collection_flags);
517 break;
518 }
519
520 case ATM_FIND_MIN_SUB_AID:
521 if ((in_content_size/sizeof(aid_t)) > (*out_content_size/sizeof(mach_atm_subaid_t)))
522 return KERN_FAILURE;
523
524 aid_array_count = in_content_size / sizeof(aid_t);
525 if (aid_array_count > AID_ARRAY_COUNT_MAX)
526 return KERN_FAILURE;
527
3e170ce0
A
528 subaid_array = (mach_atm_subaid_t *) (void *) out_content;
529 for (i = 0; i < aid_array_count; i++) {
530 subaid_array[i] = ATM_SUBAID32_MAX;
fe8ab488
A
531 }
532
fe8ab488
A
533 *out_content_size = aid_array_count * sizeof(mach_atm_subaid_t);
534
fe8ab488
A
535 kr = KERN_SUCCESS;
536
537 break;
538
539 case ATM_ACTION_UNREGISTER:
540 /* find the first non-default atm_value */
541 for (i = 0; i < value_count; i++) {
542 atm_value = HANDLE_TO_ATM_VALUE(values[i]);
543 if (atm_value != VAM_DEFAULT_VALUE)
544 break;
545 }
546
547 /* if we are not able to find any atm values
548 * in stack then this call was made in error
549 */
550 if (atm_value == NULL) {
551 return KERN_FAILURE;
552 }
3e170ce0 553 if (in_content == NULL || in_content_size != sizeof(atm_guard_t)){
fe8ab488
A
554 return KERN_INVALID_ARGUMENT;
555 }
556
3e170ce0 557 memcpy(&guard, in_content, sizeof(atm_guard_t));
fe8ab488
A
558 task = current_task();
559 task_descriptor = task->atm_context;
560
3e170ce0 561 kr = atm_value_unregister(atm_value, task_descriptor, guard);
fe8ab488
A
562
563 break;
564
3e170ce0
A
565 case ATM_ACTION_REGISTER:
566 for (i = 0; i < value_count; i++) {
567 atm_value = HANDLE_TO_ATM_VALUE(values[i]);
568 if (atm_value != VAM_DEFAULT_VALUE)
569 break;
570 }
571 /* if we are not able to find any atm values
572 * in stack then this call was made in error
573 */
574 if (atm_value == NULL) {
575 return KERN_FAILURE;
576 }
577 if (in_content == NULL || in_content_size != sizeof(atm_guard_t)){
578 return KERN_INVALID_ARGUMENT;
579 }
580
581 memcpy(&guard, in_content, sizeof(atm_guard_t));
582 task = current_task();
583 task_descriptor = task->atm_context;
584
585 kr = atm_value_register(atm_value, task_descriptor, guard);
586
587 break;
588
589 case ATM_ACTION_GETSUBAID:
590 if (out_content == NULL || *out_content_size != sizeof(mach_atm_subaid_t))
591 return KERN_FAILURE;
592
593 next_subaid = get_subaid();
594 memcpy(out_content, &next_subaid, sizeof(mach_atm_subaid_t));
595 break;
596
fe8ab488
A
597 default:
598 kr = KERN_INVALID_ARGUMENT;
599 break;
600 }
601
602 return kr;
603}
604
605
606void
607atm_release(
608 ipc_voucher_attr_manager_t __assert_only manager)
609{
610 assert(manager == &atm_manager);
611}
612
613
614/*
615 * Routine: atm_invoke_collection
616 * Purpose: Sends a notification with array of memory buffer.
617 * Note: may block till user daemon responds.
618 */
619kern_return_t
620atm_invoke_collection(
621 atm_value_t atm_value,
3e170ce0 622 mach_atm_subaid_t sub_aid,
fe8ab488
A
623 uint32_t flags)
624{
625 aid_t aid = atm_value->aid;
626 kern_return_t kr = KERN_SUCCESS;
3e170ce0 627 uint32_t array_count = 0, i = 0, j = 0, requestor_index = 0;
fe8ab488
A
628 uint64_t *sizes_array = NULL;
629 atm_link_object_t link_object = NULL;
630 mach_port_t *mem_array = NULL;
631 boolean_t need_swap_first = FALSE;
632 atm_task_descriptor_t requesting_descriptor = current_task()->atm_context;
633
634 lck_mtx_lock(&atm_value->listener_lock);
635 array_count = atm_value->listener_count;
636 lck_mtx_unlock(&atm_value->listener_lock);
637
638 if (array_count == 0){
639 return KERN_SUCCESS;
640 }
641
642 mem_array = kalloc(sizeof(mach_port_t) * array_count);
643 if (mem_array == NULL){
644 return KERN_NO_SPACE;
645 }
646
647 sizes_array = kalloc(sizeof(uint64_t) * array_count);
648 if (sizes_array == NULL){
649 kfree(mem_array, sizeof(mach_port_t) * array_count);
650 return KERN_NO_SPACE;
651 }
652
653 lck_mtx_lock(&atm_value->listener_lock);
654 queue_iterate(&atm_value->listeners, link_object, atm_link_object_t, listeners_element) {
655 if (i >= array_count){
656 break;
657 }
658
659 if (!need_swap_first && requesting_descriptor == link_object->descriptor){
660 assert(requesting_descriptor != NULL);
661 requestor_index = i;
662 need_swap_first = TRUE;
663 }
664
665 sizes_array[i] = link_object->descriptor->trace_buffer_size;
666 mem_array[i] = ipc_port_copy_send(link_object->descriptor->trace_buffer);
667 if (!IPC_PORT_VALID(mem_array[i])){
668 mem_array[i] = NULL;
669 }
670 i++;
671 }
672 lck_mtx_unlock(&atm_value->listener_lock);
673
674 /*
675 * Swap the position of requesting task ahead, diagnostics can
676 * process its buffers the first.
677 */
678 if (need_swap_first && requestor_index != 0){
679 assert(requestor_index < array_count);
680 mach_port_t tmp_port = mem_array[0];
681 uint64_t tmp_size = sizes_array[0];
682 mem_array[0] = mem_array[requestor_index];
683 sizes_array[0] = sizes_array[requestor_index];
684 mem_array[requestor_index] = tmp_port;
685 sizes_array[requestor_index] = tmp_size;
686 }
687
688 if (i > 0) {
3e170ce0
A
689 kr = atm_send_user_notification(aid, sub_aid, mem_array, sizes_array, i, flags);
690 }
691
692 for (j = 0; j < i; j++) {
693 if (mem_array[j] != NULL)
694 ipc_port_release_send(mem_array[j]);
fe8ab488
A
695 }
696
697 kfree(mem_array, sizeof(mach_port_t) * array_count);
698 kfree(sizes_array, sizeof(uint64_t) * array_count);
699
700 return kr;
701}
702
703/*
704 * Routine: atm_send_user_notification
705 * Purpose: Make an upcall to user space daemon if its listening for atm notifications.
706 * Returns: KERN_SUCCESS for successful delivery.
707 * KERN_FAILURE if port is dead or NULL.
708 */
709kern_return_t
710atm_send_user_notification(
711 aid_t aid,
3e170ce0 712 mach_atm_subaid_t sub_aid,
fe8ab488
A
713 mach_port_t *buffers_array,
714 uint64_t *sizes_array,
715 mach_msg_type_number_t count,
716 uint32_t flags)
717{
718 mach_port_t user_port;
719 int error;
3e170ce0
A
720 thread_t th = current_thread();
721 kern_return_t kr;
722
fe8ab488
A
723 error = host_get_atm_notification_port(host_priv_self(), &user_port);
724 if ((error != KERN_SUCCESS) || !IPC_PORT_VALID(user_port)) {
725 return KERN_FAILURE;
726 }
727
3e170ce0
A
728 /* Set the honor queue limit option on the thread. */
729 th->options |= TH_OPT_HONOR_QLIMIT;
730 kr = atm_collect_trace_info(user_port, aid, sub_aid, flags, buffers_array, count, sizes_array, count);
731 /* Make sure that honor queue limit option is unset on the thread. */
732 th->options &= (~TH_OPT_HONOR_QLIMIT);
733
39037602
A
734 if (kr != KERN_SUCCESS) {
735 ipc_port_release_send(user_port);
736
737 if (kr == MACH_SEND_TIMED_OUT) {
738 kr = KERN_SUCCESS;
739 }
3e170ce0 740 }
39037602 741
3e170ce0 742 return kr;
fe8ab488
A
743}
744
745/*
746 * Routine: atm_send_proc_inspect_notification
747 * Purpose: Make an upcall to user space daemon if its listening for trace
748 * notifications for per process inspection.
749 * Returns: KERN_SUCCESS for successful delivery.
750 * KERN_FAILURE if port is dead or NULL.
751 */
752
753kern_return_t
754atm_send_proc_inspect_notification(
755 task_t task,
756 int32_t traced_pid,
757 uint64_t traced_uniqueid)
758{
759 mach_port_t user_port = MACH_PORT_NULL;
760 mach_port_t memory_port = MACH_PORT_NULL;
3e170ce0 761 kern_return_t kr;
fe8ab488
A
762 atm_task_descriptor_t task_descriptor = ATM_TASK_DESCRIPTOR_NULL;
763 uint64_t buffer_size = 0;
764 int error;
3e170ce0
A
765 thread_t th = current_thread();
766
767 if (disable_atm || (atm_get_diagnostic_config() & ATM_TRACE_DISABLE))
768 return KERN_NOT_SUPPORTED;
fe8ab488
A
769
770 /* look for the requested memory in target task */
771 if (!task)
772 return KERN_INVALID_TASK;
773
774 task_lock(task);
775 if (task->atm_context){
776 task_descriptor = task->atm_context;
777 atm_descriptor_get_reference(task_descriptor);
778 }
779 task_unlock(task);
780
781 if (task_descriptor == ATM_TASK_DESCRIPTOR_NULL){
782 return KERN_FAILURE;
783 }
784
785 memory_port = ipc_port_copy_send(task_descriptor->trace_buffer);
786 buffer_size = task_descriptor->trace_buffer_size;
787 atm_task_descriptor_dealloc(task_descriptor);
788
789 /* get the communication port */
790 error = host_get_atm_notification_port(host_priv_self(), &user_port);
791 if ((error != KERN_SUCCESS) || !IPC_PORT_VALID(user_port)) {
792 ipc_port_release_send(memory_port);
793 return KERN_FAILURE;
794 }
795
3e170ce0
A
796 /* Set the honor queue limit option on the thread. */
797 th->options |= TH_OPT_HONOR_QLIMIT;
798 kr = atm_inspect_process_buffer(user_port, traced_pid, traced_uniqueid, buffer_size, memory_port);
799 /* Make sure that honor queue limit option is unset on the thread. */
800 th->options &= (~TH_OPT_HONOR_QLIMIT);
801
39037602
A
802 if (kr != KERN_SUCCESS) {
803 ipc_port_release_send(user_port);
804
805 if (kr == MACH_SEND_TIMED_OUT) {
806 kr = KERN_SUCCESS;
807 }
3e170ce0
A
808 }
809
810 ipc_port_release_send(memory_port);
811 return kr;
fe8ab488
A
812}
813
814/*
815 * Routine: atm_value_alloc_init
816 * Purpose: Allocates an atm value struct and initialize it.
817 * Returns: atm_value_t: On Success with a sync count on atm_value.
818 * ATM_VALUE_NULL: On failure.
819 */
820static atm_value_t
3e170ce0 821atm_value_alloc_init(aid_t aid)
fe8ab488
A
822{
823 atm_value_t new_atm_value = ATM_VALUE_NULL;
824
825 new_atm_value = (atm_value_t) zalloc(atm_value_zone);
826 if (new_atm_value == ATM_VALUE_NULL)
827 panic("Ran out of ATM values structure.\n\n");
828
3e170ce0 829 new_atm_value->aid = aid;
fe8ab488
A
830 queue_init(&new_atm_value->listeners);
831 new_atm_value->sync = 1;
832 new_atm_value->listener_count = 0;
833 new_atm_value->reference_count = 1;
834 lck_mtx_init(&new_atm_value->listener_lock, &atm_lock_grp, &atm_lock_attr);
835
836#if DEVELOPMENT || DEBUG
837 lck_mtx_lock(&atm_values_list_lock);
838 queue_enter(&atm_values_list, new_atm_value, atm_value_t, value_elt);
839 lck_mtx_unlock(&atm_values_list_lock);
840#endif
841 return new_atm_value;
842}
843
844
845/*
846 * Routine: get_aid
847 * Purpose: Increment the global aid counter and return it.
848 * Returns: aid
849 */
850static aid_t
851get_aid()
852{
853 aid_t aid;
854 aid = (aid_t)OSIncrementAtomic64((SInt64 *)&global_aid);
855 return aid;
856}
857
858
3e170ce0
A
859/*
860 * Routine: get_subaid
861 * Purpose: Increment the global subaid counter and return it.
862 * Returns: subaid
863 */
864static mach_atm_subaid_t
865get_subaid()
866{
867 mach_atm_subaid_t next_subaid;
868 next_subaid = (mach_atm_subaid_t)OSIncrementAtomic64((SInt64 *)&global_subaid);
869 return next_subaid;
870}
871
872
fe8ab488
A
873/*
874 * Routine: atm_value_dealloc
875 * Purpose: Drops the reference on atm value and deallocates.
876 * Deletes all the listeners on deallocation.
877 * Returns: None.
878 */
879static void
880atm_value_dealloc(atm_value_t atm_value)
881{
3e170ce0 882 if (0 < atm_value_release_internal(atm_value)) {
fe8ab488
A
883 return;
884 }
885
3e170ce0 886 assert(atm_value->reference_count == 0);
fe8ab488
A
887
888 /* Free up the atm value and also remove all the listeners. */
889 atm_listener_delete_all(atm_value);
890
891 lck_mtx_destroy(&atm_value->listener_lock, &atm_lock_grp);
892
893#if DEVELOPMENT || DEBUG
894 lck_mtx_lock(&atm_values_list_lock);
895 queue_remove(&atm_values_list, atm_value, atm_value_t, value_elt);
896 lck_mtx_unlock(&atm_values_list_lock);
897#endif
898 zfree(atm_value_zone, atm_value);
899 return;
900}
901
902
903/*
904 * Routine: atm_hash_table_init
905 * Purpose: Initialize the atm aid hash table.
906 * Returns: None.
907 */
908static void
909atm_hash_table_init()
910{
911 int i;
912
913 for (i = 0; i < ATM_MAX_HASH_TABLE_SIZE; i++) {
914 queue_init(&atm_value_hash_table[i].hash_list);
915 lck_mtx_init(&atm_value_hash_table[i].hash_list_lock, &atm_lock_grp, &atm_lock_attr);
916 }
917}
918
919
920/*
921 * Routine: atm_value_hash_table_insert
922 * Purpose: Insert an atm value in the hash table.
3e170ce0
A
923 * Returns: KERN_SUCCESS on success.
924 * KERN_NAME_EXISTS if atm value already in the hash table.
fe8ab488 925 */
3e170ce0 926static kern_return_t
fe8ab488
A
927atm_value_hash_table_insert(atm_value_t new_atm_value)
928{
929 int hash_index;
930 atm_value_hash_t hash_list_head;
931 aid_t aid = new_atm_value->aid;
3e170ce0 932 atm_value_t next;
fe8ab488
A
933
934 hash_index = AID_TO_HASH(aid);
935 hash_list_head = &atm_value_hash_table[hash_index];
936
3e170ce0 937 /* Lock the atm list and search for the aid. */
fe8ab488 938 lck_mtx_lock(&hash_list_head->hash_list_lock);
3e170ce0
A
939
940 queue_iterate(&hash_list_head->hash_list, next, atm_value_t, vid_hash_elt) {
941 if (next->aid == aid) {
942 /*
943 * aid found. return error.
944 */
945 lck_mtx_unlock(&hash_list_head->hash_list_lock);
946 return (KERN_NAME_EXISTS);
947 }
948 }
949
950 /* Enter the aid in hash and return success. */
fe8ab488
A
951 queue_enter(&hash_list_head->hash_list, new_atm_value, atm_value_t, vid_hash_elt);
952 lck_mtx_unlock(&hash_list_head->hash_list_lock);
3e170ce0 953 return KERN_SUCCESS;
fe8ab488
A
954}
955
956
957/*
958 * Routine: atm_value_hash_table_delete
959 * Purpose: Delete the atm value from the hash table.
960 * Returns: None.
961 */
962static void
963atm_value_hash_table_delete(atm_value_t atm_value)
964{
965 int hash_index;
966 atm_value_hash_t hash_list_head;
967 aid_t aid = atm_value->aid;
968
969 hash_index = AID_TO_HASH(aid);
970 hash_list_head = &atm_value_hash_table[hash_index];
971
972 lck_mtx_lock(&hash_list_head->hash_list_lock);
973 queue_remove(&hash_list_head->hash_list, atm_value, atm_value_t, vid_hash_elt);
974 lck_mtx_unlock(&hash_list_head->hash_list_lock);
975}
976
977
978/*
979 * Routine: get_atm_value_from_aid
980 * Purpose: Search a given aid in atm value hash table and
981 * return the atm value stucture.
982 * Returns: atm value structure if aid found.
983 * ATM_VALUE_NULL: If aid not found in atm value hash table.
984 */
985static atm_value_t
986get_atm_value_from_aid(aid_t aid)
987{
988 int hash_index;
989 atm_value_hash_t hash_list_head;
990 atm_value_t next;
991
992 hash_index = AID_TO_HASH(aid);
993 hash_list_head = &atm_value_hash_table[hash_index];
994
995 /* Lock the atm list and search for the aid. */
996 lck_mtx_lock(&hash_list_head->hash_list_lock);
997
998 queue_iterate(&hash_list_head->hash_list, next, atm_value_t, vid_hash_elt) {
999 if (next->aid == aid) {
1000 /*
1001 * Aid found. Incerease ref count and return
1002 * the atm value structure.
1003 */
1004 atm_value_get_ref(next);
1005 lck_mtx_unlock(&hash_list_head->hash_list_lock);
1006 return (next);
1007 }
1008 }
1009 lck_mtx_unlock(&hash_list_head->hash_list_lock);
1010 return ATM_VALUE_NULL;
1011}
1012
1013
1014/*
1015 * Routine: atm_value_get_ref
1016 * Purpose: Get a reference on atm value.
1017 * Returns: None.
1018 */
1019static void
1020atm_value_get_ref(atm_value_t atm_value)
1021{
3e170ce0 1022 atm_value_reference_internal(atm_value);
fe8ab488
A
1023}
1024
1025
1026/*
1027 * Routine: atm_listener_insert
1028 * Purpose: Insert a listener to an atm value.
1029 * Returns: KERN_SUCCESS on success.
1030 * KERN_FAILURE if the task is already present as a listener.
1031 */
1032static kern_return_t
1033atm_listener_insert(
1034 atm_value_t atm_value,
1035 atm_task_descriptor_t task_descriptor,
3e170ce0 1036 atm_guard_t guard)
fe8ab488
A
1037{
1038 atm_link_object_t new_link_object;
3e170ce0
A
1039 atm_link_object_t next, elem;
1040 int32_t freed_count = 0, dead_but_not_freed = 0, listener_count;
1041 boolean_t element_found = FALSE;
1042 queue_head_t free_listeners;
fe8ab488
A
1043
1044 new_link_object = (atm_link_object_t) zalloc(atm_link_objects_zone);
1045 new_link_object->descriptor = task_descriptor;
1046 new_link_object->reference_count = 1;
3e170ce0 1047 new_link_object->guard = guard;
fe8ab488
A
1048
1049 /* Get a reference on the task descriptor */
1050 atm_descriptor_get_reference(task_descriptor);
3e170ce0
A
1051 queue_init(&free_listeners);
1052 listener_count = atm_value->listener_count;
fe8ab488 1053
3e170ce0 1054 /* Check if the task is already on the listener list */
fe8ab488 1055 lck_mtx_lock(&atm_value->listener_lock);
3e170ce0
A
1056
1057 next = (atm_link_object_t)(void *) queue_first(&atm_value->listeners);
1058 while (!queue_end(&atm_value->listeners, (queue_entry_t)next)) {
1059 elem = next;
1060 next = (atm_link_object_t)(void *) queue_next(&next->listeners_element);
1061
1062 /* Check for dead tasks */
1063 if (elem->descriptor->flags == ATM_TASK_DEAD) {
1064 if ((dead_but_not_freed > ATM_LIST_DEAD_MAX) || elem->guard == 0) {
1065 queue_remove(&atm_value->listeners, elem, atm_link_object_t, listeners_element);
1066 queue_enter(&free_listeners, elem, atm_link_object_t, listeners_element);
1067 atm_listener_count_decr_internal(atm_value);
1068 freed_count++;
1069 } else {
1070 dead_but_not_freed++;
1071 }
1072 continue;
1073 }
1074
1075 if (element_found)
1076 continue;
1077
1078 if (elem->descriptor == task_descriptor) {
1079 /* Increment reference count on Link object. */
1080 atm_link_get_reference(elem);
1081
1082 /* Replace the guard with the new one, the old guard is anyways on unregister path. */
1083 elem->guard = guard;
1084 element_found = TRUE;
04b8595b 1085 KERNEL_DEBUG_CONSTANT((ATM_CODE(ATM_GETVALUE_INFO, (ATM_VALUE_REPLACED))) | DBG_FUNC_NONE,
3e170ce0 1086 VM_KERNEL_ADDRPERM(atm_value), atm_value->aid, guard, 0, 0);
fe8ab488 1087
fe8ab488
A
1088 }
1089 }
fe8ab488 1090
3e170ce0
A
1091 if (element_found) {
1092 lck_mtx_unlock(&atm_value->listener_lock);
1093 /* Drop the extra reference on task descriptor taken by this function. */
1094 atm_task_descriptor_dealloc(task_descriptor);
1095 zfree(atm_link_objects_zone, new_link_object);
1096 } else {
1097 KERNEL_DEBUG_CONSTANT((ATM_CODE(ATM_GETVALUE_INFO, (ATM_VALUE_ADDED))) | DBG_FUNC_NONE,
1098 VM_KERNEL_ADDRPERM(atm_value), atm_value->aid, guard, 0, 0);
1099
1100 queue_enter(&atm_value->listeners, new_link_object, atm_link_object_t, listeners_element);
1101 atm_listener_count_incr_internal(atm_value);
1102 lck_mtx_unlock(&atm_value->listener_lock);
1103 }
1104
1105 /* Free the link objects */
1106 while(!queue_empty(&free_listeners)) {
1107 queue_remove_first(&free_listeners, next, atm_link_object_t, listeners_element);
1108
1109 /* Deallocate the link object */
1110 atm_link_dealloc(next);
1111 }
1112
1113 KERNEL_DEBUG_CONSTANT((ATM_CODE(ATM_SUBAID_INFO, (ATM_LINK_LIST_TRIM))) | DBG_FUNC_NONE,
1114 listener_count, freed_count, dead_but_not_freed, VM_KERNEL_ADDRPERM(atm_value), 1);
1115
fe8ab488
A
1116 return KERN_SUCCESS;
1117}
1118
1119
1120/*
1121 * Routine: atm_listener_delete_all
1122 * Purpose: Deletes all the listeners for an atm value.
1123 * Returns: None.
1124 */
1125static void
1126atm_listener_delete_all(atm_value_t atm_value)
1127{
1128 atm_link_object_t next;
1129
1130 while(!queue_empty(&atm_value->listeners)) {
1131 queue_remove_first(&atm_value->listeners, next, atm_link_object_t, listeners_element);
1132
3e170ce0 1133 /* Deallocate the link object */
fe8ab488
A
1134 atm_link_dealloc(next);
1135 }
1136}
1137
1138
1139/*
1140 * Routine: atm_listener_delete
1141 * Purpose: Deletes a listerner for an atm value.
1142 * Returns: KERN_SUCCESS on successful unregister.
3e170ce0 1143 * KERN_INVALID_VALUE on finding a different guard.
fe8ab488
A
1144 * KERN_FAILURE on failure.
1145 */
1146static kern_return_t
1147atm_listener_delete(
1148 atm_value_t atm_value,
1149 atm_task_descriptor_t task_descriptor,
3e170ce0 1150 atm_guard_t guard)
fe8ab488
A
1151{
1152 queue_head_t free_listeners;
1153 atm_link_object_t next, elem;
fe8ab488
A
1154 kern_return_t kr = KERN_FAILURE;
1155
1156 queue_init(&free_listeners);
1157
1158 lck_mtx_lock(&atm_value->listener_lock);
1159
1160 next = (atm_link_object_t)(void *) queue_first(&atm_value->listeners);
1161 while (!queue_end(&atm_value->listeners, (queue_entry_t)next)) {
1162 elem = next;
1163 next = (atm_link_object_t)(void *) queue_next(&next->listeners_element);
1164
1165 if (elem->descriptor == task_descriptor) {
3e170ce0 1166 if (elem->guard == guard) {
04b8595b 1167 KERNEL_DEBUG_CONSTANT((ATM_CODE(ATM_UNREGISTER_INFO,
fe8ab488 1168 (ATM_VALUE_UNREGISTERED))) | DBG_FUNC_NONE,
3e170ce0
A
1169 VM_KERNEL_ADDRPERM(atm_value), atm_value->aid, guard, elem->reference_count, 0);
1170 elem->guard = 0;
fe8ab488 1171 kr = KERN_SUCCESS;
fe8ab488 1172 } else {
04b8595b 1173 KERNEL_DEBUG_CONSTANT((ATM_CODE(ATM_UNREGISTER_INFO,
fe8ab488 1174 (ATM_VALUE_DIFF_MAILBOX))) | DBG_FUNC_NONE,
3e170ce0 1175 VM_KERNEL_ADDRPERM(atm_value), atm_value->aid, elem->guard, elem->reference_count, 0);
fe8ab488 1176 kr = KERN_INVALID_VALUE;
fe8ab488 1177 }
3e170ce0
A
1178 if (0 == atm_link_object_release_internal(elem)) {
1179 queue_remove(&atm_value->listeners, elem, atm_link_object_t, listeners_element);
1180 queue_enter(&free_listeners, elem, atm_link_object_t, listeners_element);
1181 atm_listener_count_decr_internal(atm_value);
1182 }
1183 break;
fe8ab488
A
1184 }
1185 }
1186 lck_mtx_unlock(&atm_value->listener_lock);
1187
1188 while(!queue_empty(&free_listeners)) {
1189 queue_remove_first(&free_listeners, next, atm_link_object_t, listeners_element);
1190
3e170ce0 1191 /* Deallocate the link object */
fe8ab488
A
1192 atm_link_dealloc(next);
1193 }
1194 return kr;
1195}
1196
1197
1198/*
1199 * Routine: atm_descriptor_alloc_init
1200 * Purpose: Allocate an atm task descriptor and initialize it and takes a reference.
1201 * Returns: atm task descriptor: On success.
1202 * NULL: on error.
1203 */
1204static atm_task_descriptor_t
1205atm_task_descriptor_alloc_init(
1206 mach_port_t trace_buffer,
1207 uint64_t buffer_size,
fe8ab488
A
1208 task_t __assert_only task)
1209{
1210 atm_task_descriptor_t new_task_descriptor;
1211
1212 new_task_descriptor = (atm_task_descriptor_t) zalloc(atm_descriptors_zone);
1213
1214 new_task_descriptor->trace_buffer = trace_buffer;
1215 new_task_descriptor->trace_buffer_size = buffer_size;
fe8ab488
A
1216 new_task_descriptor->reference_count = 1;
1217 new_task_descriptor->flags = 0;
1218 lck_mtx_init(&new_task_descriptor->lock, &atm_lock_grp, &atm_lock_attr);
1219
1220#if DEVELOPMENT || DEBUG
1221 new_task_descriptor->task = task;
1222 lck_mtx_lock(&atm_descriptors_list_lock);
1223 queue_enter(&atm_descriptors_list, new_task_descriptor, atm_task_descriptor_t, descriptor_elt);
1224 lck_mtx_unlock(&atm_descriptors_list_lock);
1225#endif
1226
1227 return new_task_descriptor;
1228}
1229
1230
1231/*
1232 * Routine: atm_descriptor_get_reference
1233 * Purpose: Get a reference count on task descriptor.
1234 * Returns: None.
1235 */
1236static void
1237atm_descriptor_get_reference(atm_task_descriptor_t task_descriptor)
1238{
3e170ce0 1239 atm_task_desc_reference_internal(task_descriptor);
fe8ab488
A
1240}
1241
1242
1243/*
1244 * Routine: atm_task_descriptor_dealloc
1245 * Prupose: Drops the reference on atm descriptor.
1246 * Returns: None.
1247 */
1248static void
1249atm_task_descriptor_dealloc(atm_task_descriptor_t task_descriptor)
1250{
3e170ce0 1251 if (0 < atm_task_desc_release_internal(task_descriptor)) {
fe8ab488
A
1252 return;
1253 }
1254
3e170ce0
A
1255 assert(task_descriptor->reference_count == 0);
1256
fe8ab488
A
1257#if DEVELOPMENT || DEBUG
1258 lck_mtx_lock(&atm_descriptors_list_lock);
1259 queue_remove(&atm_descriptors_list, task_descriptor, atm_task_descriptor_t, descriptor_elt);
1260 lck_mtx_unlock(&atm_descriptors_list_lock);
1261#endif
fe8ab488
A
1262 /* release the send right for the named memory entry */
1263 ipc_port_release_send(task_descriptor->trace_buffer);
fe8ab488
A
1264 lck_mtx_destroy(&task_descriptor->lock, &atm_lock_grp);
1265 zfree(atm_descriptors_zone, task_descriptor);
1266 return;
1267}
1268
1269
1270/*
1271 * Routine: atm_link_get_reference
1272 * Purpose: Get a reference count on atm link object.
1273 * Returns: None.
1274 */
1275static void
1276atm_link_get_reference(atm_link_object_t link_object)
1277{
1278 atm_link_object_reference_internal(link_object);
1279}
1280
1281
1282/*
1283 * Routine: atm_link_dealloc
1284 * Prupose: Drops the reference on link object.
1285 * Returns: None.
1286 */
1287static void
1288atm_link_dealloc(atm_link_object_t link_object)
1289{
fe8ab488
A
1290 /* Drop the reference on atm task descriptor. */
1291 atm_task_descriptor_dealloc(link_object->descriptor);
1292 zfree(atm_link_objects_zone, link_object);
1293}
1294
1295
1296/*
1297 * Routine: atm_register_trace_memory
1298 * Purpose: Registers trace memory for a task.
1299 * Returns: KERN_SUCCESS: on Success.
1300 * KERN_FAILURE: on Error.
1301 */
1302kern_return_t
1303atm_register_trace_memory(
1304 task_t task,
1305 uint64_t trace_buffer_address,
3e170ce0 1306 uint64_t buffer_size)
fe8ab488
A
1307{
1308 atm_task_descriptor_t task_descriptor;
1309 mach_port_t trace_buffer = MACH_PORT_NULL;
fe8ab488
A
1310 kern_return_t kr = KERN_SUCCESS;
1311
3e170ce0 1312 if (disable_atm || (atm_get_diagnostic_config() & ATM_TRACE_DISABLE))
fe8ab488
A
1313 return KERN_NOT_SUPPORTED;
1314
1315 if (task != current_task())
1316 return KERN_INVALID_ARGUMENT;
1317
1318 if (task->atm_context != NULL
1319 || (void *)trace_buffer_address == NULL
1320 || buffer_size == 0
1321 || (buffer_size & PAGE_MASK) != 0
3e170ce0 1322 || buffer_size > MAX_TRACE_BUFFER_SIZE) {
fe8ab488
A
1323 return KERN_INVALID_ARGUMENT;
1324 }
1325
1326 vm_map_t map = current_map();
1327 memory_object_size_t mo_size = (memory_object_size_t) buffer_size;
1328 kr = mach_make_memory_entry_64(map,
1329 &mo_size,
1330 (mach_vm_offset_t)trace_buffer_address,
1331 VM_PROT_READ,
1332 &trace_buffer,
1333 NULL);
1334 if (kr != KERN_SUCCESS)
1335 return kr;
1336
3e170ce0 1337 task_descriptor = atm_task_descriptor_alloc_init(trace_buffer, buffer_size, task);
fe8ab488
A
1338 if (task_descriptor == ATM_TASK_DESCRIPTOR_NULL) {
1339 ipc_port_release_send(trace_buffer);
fe8ab488
A
1340 return KERN_NO_SPACE;
1341 }
1342
1343 task_lock(task);
1344 if (task->atm_context == NULL) {
1345 task->atm_context = task_descriptor;
1346 kr = KERN_SUCCESS;
1347 } else {
1348 kr = KERN_FAILURE;
1349 }
1350 task_unlock(task);
1351
1352 if (kr != KERN_SUCCESS) {
1353 /* undo the mapping and allocations since we failed to hook descriptor to task */
1354 atm_task_descriptor_dealloc(task_descriptor);
1355 }
1356 return KERN_SUCCESS;
1357}
1358
fe8ab488 1359/*
3e170ce0
A
1360 * Routine: atm_set_diagnostic_config
1361 * Purpose: Set global atm_diagnostic_config and update the commpage to reflect
1362 * the new value.
1363 * Returns: Error if ATM is disabled.
fe8ab488 1364 */
3e170ce0
A
1365extern uint32_t atm_diagnostic_config; /* Proxied to commpage for fast user access */
1366kern_return_t
1367atm_set_diagnostic_config(uint32_t diagnostic_config)
fe8ab488 1368{
3e170ce0
A
1369 if (disable_atm)
1370 return KERN_NOT_SUPPORTED;
fe8ab488 1371
3e170ce0
A
1372 atm_diagnostic_config = diagnostic_config;
1373 commpage_update_atm_diagnostic_config(atm_diagnostic_config);
fe8ab488 1374
3e170ce0 1375 return KERN_SUCCESS;
fe8ab488
A
1376}
1377
1378
1379/*
3e170ce0
A
1380 * Routine: atm_get_diagnostic_config
1381 * Purpose: Get global atm_diagnostic_config.
1382 * Returns: Diagnostic value
fe8ab488 1383 */
3e170ce0
A
1384uint32_t
1385atm_get_diagnostic_config(void)
fe8ab488 1386{
3e170ce0 1387 return atm_diagnostic_config;
fe8ab488
A
1388}
1389
1390
1391/*
1392 * Routine: atm_value_unregister
1393 * Purpose: Unregisters a process from an activity id.
1394 * Returns: KERN_SUCCESS on successful unregister.
3e170ce0 1395 * KERN_INVALID_VALUE on finding a diff guard.
fe8ab488
A
1396 * KERN_FAILURE on failure.
1397 */
1398static kern_return_t
1399atm_value_unregister(
1400 atm_value_t atm_value,
1401 atm_task_descriptor_t task_descriptor,
3e170ce0 1402 atm_guard_t guard)
fe8ab488
A
1403{
1404 kern_return_t kr;
1405
1406 if (task_descriptor == ATM_TASK_DESCRIPTOR_NULL)
3e170ce0 1407 return KERN_INVALID_TASK;
fe8ab488 1408
3e170ce0 1409 kr = atm_listener_delete(atm_value, task_descriptor, guard);
fe8ab488
A
1410 return kr;
1411}
1412
3e170ce0
A
1413
1414/*
1415 * Routine: atm_value_register
1416 * Purpose: Registers a process for an activity id.
1417 * Returns: KERN_SUCCESS on successful register.
1418 * KERN_INVALID_TASK on finding a null task atm context.
1419 * KERN_FAILURE on failure.
1420 */
1421static kern_return_t
1422atm_value_register(
1423 atm_value_t atm_value,
1424 atm_task_descriptor_t task_descriptor,
1425 atm_guard_t guard)
1426{
1427 kern_return_t kr;
1428
1429 if (task_descriptor == ATM_TASK_DESCRIPTOR_NULL)
1430 return KERN_INVALID_TASK;
1431
1432 kr = atm_listener_insert(atm_value, task_descriptor, guard);
1433 return kr;
1434}
1435
1436
fe8ab488
A
1437void
1438atm_task_descriptor_destroy(atm_task_descriptor_t task_descriptor)
1439{
1440 /* Mark the task dead in the task descriptor to make task descriptor eligible for cleanup. */
1441 lck_mtx_lock(&task_descriptor->lock);
1442 task_descriptor->flags = ATM_TASK_DEAD;
1443 lck_mtx_unlock(&task_descriptor->lock);
1444
1445 atm_task_descriptor_dealloc(task_descriptor);
1446}