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