2 * Copyright (c) 2012-2013 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
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>
43 #define MAX_ATM_VALUES (2 * 4096)
44 #define MAX_TRACE_BUFFER_SIZE (0x40000000) /* Restrict to 1GB per task */
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)))
49 #define ATM_MAX_HASH_TABLE_SIZE (256)
50 #define AID_HASH_MASK (0xFF)
51 #define AID_TO_HASH(x) ((x) & (AID_HASH_MASK))
53 #define ATM_LIST_DEAD_MAX 15
55 #define AID_ARRAY_COUNT_MAX (256)
57 struct atm_value_hash atm_value_hash_table
[ATM_MAX_HASH_TABLE_SIZE
];
60 /* Global flag to disable ATM. ATM get value and memory registration will return error. */
61 boolean_t disable_atm
= FALSE
;
63 #if DEVELOPMENT || DEBUG
64 queue_head_t atm_descriptors_list
;
65 queue_head_t atm_values_list
;
68 ipc_voucher_attr_control_t voucher_attr_control
; /* communication channel from ATM to voucher system */
69 static zone_t atm_value_zone
, atm_descriptors_zone
, atm_link_objects_zone
;
71 static aid_t
get_aid(void);
72 static mach_atm_subaid_t
get_subaid(void);
73 static atm_value_t
atm_value_alloc_init(aid_t
);
74 static void atm_value_dealloc(atm_value_t atm_value
);
75 static void atm_hash_table_init(void);
76 static kern_return_t
atm_value_hash_table_insert(atm_value_t new_atm_value
);
77 static void atm_value_hash_table_delete(atm_value_t atm_value
);
78 static atm_value_t
get_atm_value_from_aid(aid_t aid
) __unused
;
79 static kern_return_t
atm_listener_insert(atm_value_t atm_value
, atm_task_descriptor_t task_descriptor
, atm_guard_t guard
);
80 static void atm_listener_delete_all(atm_value_t atm_value
);
81 static atm_task_descriptor_t
atm_task_descriptor_alloc_init(mach_port_t trace_buffer
, uint64_t buffer_size
, __assert_only task_t task
);
82 static void atm_task_descriptor_dealloc(atm_task_descriptor_t task_descriptor
);
83 static kern_return_t
atm_value_unregister(atm_value_t atm_value
, atm_task_descriptor_t task_descriptor
, atm_guard_t guard
);
84 static kern_return_t
atm_value_register(atm_value_t atm_value
, atm_task_descriptor_t task_descriptor
, atm_guard_t guard
);
85 static kern_return_t
atm_listener_delete(atm_value_t atm_value
, atm_task_descriptor_t task_descriptor
, atm_guard_t guard
);
86 static void atm_link_dealloc(atm_link_object_t link_object
);
90 ipc_voucher_attr_manager_t __assert_only manager
,
91 mach_voucher_attr_key_t __assert_only key
,
92 mach_voucher_attr_value_handle_t value
,
93 mach_voucher_attr_value_reference_t sync
);
97 ipc_voucher_attr_manager_t __assert_only manager
,
98 mach_voucher_attr_key_t __assert_only key
,
99 mach_voucher_attr_recipe_command_t command
,
100 mach_voucher_attr_value_handle_array_t prev_values
,
101 mach_msg_type_number_t __assert_only prev_value_count
,
102 mach_voucher_attr_content_t recipe
,
103 mach_voucher_attr_content_size_t recipe_size
,
104 mach_voucher_attr_value_handle_t
*out_value
,
105 mach_voucher_attr_value_flags_t
*out_flags
,
106 ipc_voucher_t
*out_value_voucher
);
110 ipc_voucher_attr_manager_t __assert_only manager
,
111 mach_voucher_attr_key_t __assert_only key
,
112 mach_voucher_attr_value_handle_array_t values
,
113 mach_msg_type_number_t value_count
,
114 mach_voucher_attr_recipe_command_t
*out_command
,
115 mach_voucher_attr_content_t out_recipe
,
116 mach_voucher_attr_content_size_t
*in_out_recipe_size
);
120 ipc_voucher_attr_manager_t __assert_only manager
,
121 mach_voucher_attr_key_t __assert_only key
,
122 mach_voucher_attr_value_handle_array_t values
,
123 mach_msg_type_number_t value_count
,
124 mach_voucher_attr_command_t command
,
125 mach_voucher_attr_content_t in_content
,
126 mach_voucher_attr_content_size_t in_content_size
,
127 mach_voucher_attr_content_t out_content
,
128 mach_voucher_attr_content_size_t
*in_out_content_size
);
131 atm_release(ipc_voucher_attr_manager_t __assert_only manager
);
134 * communication channel from voucher system to ATM
136 const struct ipc_voucher_attr_manager atm_manager
= {
137 .ivam_release_value
= atm_release_value
,
138 .ivam_get_value
= atm_get_value
,
139 .ivam_extract_content
= atm_extract_content
,
140 .ivam_command
= atm_command
,
141 .ivam_release
= atm_release
,
142 .ivam_flags
= IVAM_FLAGS_NONE
,
145 #if DEVELOPMENT || DEBUG
146 decl_lck_mtx_data(, atm_descriptors_list_lock
);
147 decl_lck_mtx_data(, atm_values_list_lock
);
149 lck_grp_t atm_dev_lock_grp
;
150 lck_attr_t atm_dev_lock_attr
;
151 lck_grp_attr_t atm_dev_lock_grp_attr
;
154 extern vm_map_t kernel_map
;
156 * Global aid. Incremented on each get_aid.
161 * Global subaid. Incremented on each get_subaid.
163 mach_atm_subaid_t global_subaid
;
166 * Lock group attributes for atm sub system.
168 lck_grp_t atm_lock_grp
;
169 lck_attr_t atm_lock_attr
;
170 lck_grp_attr_t atm_lock_grp_attr
;
173 * Global that is set by diagnosticd and readable by userspace
176 static uint32_t atm_diagnostic_config
;
180 * Purpose: Initialize the atm subsystem.
186 kern_return_t kr
= KERN_SUCCESS
;
189 /* Disable atm if disable_atm present in device-tree properties or in boot-args */
190 if ((PE_get_default("kern.disable_atm", temp_buf
, sizeof(temp_buf
))) ||
191 (PE_parse_boot_argn("-disable_atm", temp_buf
, sizeof(temp_buf
)))) {
195 if (!PE_parse_boot_argn("atm_diagnostic_config", &atm_diagnostic_config
, sizeof(atm_diagnostic_config
))) {
196 if (!PE_get_default("kern.atm_diagnostic_config", &atm_diagnostic_config
, sizeof(atm_diagnostic_config
))) {
197 atm_diagnostic_config
= 0;
201 /* setup zones for descriptors, values and link objects */
202 atm_value_zone
= zinit(sizeof(struct atm_value
),
203 MAX_ATM_VALUES
* sizeof(struct atm_value
),
204 sizeof(struct atm_value
),
207 atm_descriptors_zone
= zinit(sizeof(struct atm_task_descriptor
),
208 MAX_ATM_VALUES
* sizeof(struct atm_task_descriptor
),
209 sizeof(struct atm_task_descriptor
),
210 "atm_task_descriptors");
212 atm_link_objects_zone
= zinit(sizeof(struct atm_link_object
),
213 MAX_ATM_VALUES
* sizeof(struct atm_link_object
),
214 sizeof(struct atm_link_object
),
217 /* Initialize atm lock group and lock attributes. */
218 lck_grp_attr_setdefault(&atm_lock_grp_attr
);
219 lck_grp_init(&atm_lock_grp
, "atm_lock", &atm_lock_grp_attr
);
220 lck_attr_setdefault(&atm_lock_attr
);
224 atm_hash_table_init();
226 #if DEVELOPMENT || DEBUG
227 /* Initialize global atm development lock group and lock attributes. */
228 lck_grp_attr_setdefault(&atm_dev_lock_grp_attr
);
229 lck_grp_init(&atm_dev_lock_grp
, "atm_dev_lock", &atm_dev_lock_grp_attr
);
230 lck_attr_setdefault(&atm_dev_lock_attr
);
232 lck_mtx_init(&atm_descriptors_list_lock
, &atm_dev_lock_grp
, &atm_dev_lock_attr
);
233 lck_mtx_init(&atm_values_list_lock
, &atm_dev_lock_grp
, &atm_dev_lock_attr
);
235 queue_init(&atm_descriptors_list
);
236 queue_init(&atm_values_list
);
239 /* Register the atm manager with the Vouchers sub system. */
240 kr
= ipc_register_well_known_mach_voucher_attr_manager(
243 MACH_VOUCHER_ATTR_KEY_ATM
,
244 &voucher_attr_control
);
245 if (kr
!= KERN_SUCCESS
) {
246 panic("ATM subsystem initialization failed");
249 kprintf("ATM subsystem is initialized\n");
255 * ATM Resource Manager Routines.
260 * Routine: atm_release_value
261 * Purpose: Release a value, if sync matches the sync count in value.
262 * Returns: KERN_SUCCESS: on Successful deletion.
263 * KERN_FAILURE: if sync value does not matches.
267 ipc_voucher_attr_manager_t __assert_only manager
,
268 mach_voucher_attr_key_t __assert_only key
,
269 mach_voucher_attr_value_handle_t value
,
270 mach_voucher_attr_value_reference_t sync
)
272 atm_value_t atm_value
= ATM_VALUE_NULL
;
274 assert(MACH_VOUCHER_ATTR_KEY_ATM
== key
);
275 assert(manager
== &atm_manager
);
277 atm_value
= HANDLE_TO_ATM_VALUE(value
);
278 if (atm_value
== VAM_DEFAULT_VALUE
) {
279 /* Return success for default value */
283 if (atm_value
->sync
!= sync
) {
287 /* Deallocate the atm value. */
288 atm_value_hash_table_delete(atm_value
);
289 atm_value_dealloc(atm_value
);
295 * Routine: atm_get_value
299 ipc_voucher_attr_manager_t __assert_only manager
,
300 mach_voucher_attr_key_t __assert_only key
,
301 mach_voucher_attr_recipe_command_t command
,
302 mach_voucher_attr_value_handle_array_t prev_values
,
303 mach_msg_type_number_t __assert_only prev_value_count
,
304 mach_voucher_attr_content_t __unused recipe
,
305 mach_voucher_attr_content_size_t __unused recipe_size
,
306 mach_voucher_attr_value_handle_t
*out_value
,
307 mach_voucher_attr_value_flags_t
*out_flags
,
308 ipc_voucher_t
*out_value_voucher
)
310 atm_value_t atm_value
= ATM_VALUE_NULL
;
311 mach_voucher_attr_value_handle_t atm_handle
;
312 atm_task_descriptor_t task_descriptor
= ATM_TASK_DESCRIPTOR_NULL
;
317 kern_return_t kr
= KERN_SUCCESS
;
319 assert(MACH_VOUCHER_ATTR_KEY_ATM
== key
);
320 assert(manager
== &atm_manager
);
322 /* never an out voucher */
323 *out_value_voucher
= IPC_VOUCHER_NULL
;
324 *out_flags
= MACH_VOUCHER_ATTR_VALUE_FLAGS_NONE
;
326 if (disable_atm
|| (atm_get_diagnostic_config() & ATM_TRACE_DISABLE
)) {
327 return KERN_NOT_SUPPORTED
;
331 case MACH_VOUCHER_ATTR_ATM_REGISTER
:
333 for (i
= 0; i
< prev_value_count
; i
++) {
334 atm_handle
= prev_values
[i
];
335 atm_value
= HANDLE_TO_ATM_VALUE(atm_handle
);
337 if (atm_value
== VAM_DEFAULT_VALUE
) {
341 if (recipe_size
!= sizeof(atm_guard_t
)) {
342 kr
= KERN_INVALID_ARGUMENT
;
345 memcpy(&guard
, recipe
, sizeof(atm_guard_t
));
347 task
= current_task();
348 task_descriptor
= task
->atm_context
;
350 kr
= atm_value_register(atm_value
, task_descriptor
, guard
);
351 if (kr
!= KERN_SUCCESS
) {
355 /* Increment sync value. */
356 atm_sync_reference_internal(atm_value
);
358 *out_value
= atm_handle
;
362 *out_value
= ATM_VALUE_TO_HANDLE(VAM_DEFAULT_VALUE
);
365 case MACH_VOUCHER_ATTR_ATM_CREATE
:
367 /* Handle the old case where aid value is created in kernel */
368 if (recipe_size
== 0) {
370 } else if (recipe_size
== sizeof(aid_t
)) {
371 memcpy(&aid
, recipe
, sizeof(aid_t
));
373 kr
= KERN_INVALID_ARGUMENT
;
377 /* Allocate a new atm value. */
378 atm_value
= atm_value_alloc_init(aid
);
379 if (atm_value
== ATM_VALUE_NULL
) {
380 kr
= KERN_RESOURCE_SHORTAGE
;
384 kr
= atm_value_hash_table_insert(atm_value
);
385 if (kr
!= KERN_SUCCESS
) {
386 if (recipe_size
== 0) {
387 atm_value
->aid
= get_aid();
390 atm_value_dealloc(atm_value
);
394 *out_value
= ATM_VALUE_TO_HANDLE(atm_value
);
397 case MACH_VOUCHER_ATTR_ATM_NULL
:
399 kr
= KERN_INVALID_ARGUMENT
;
408 * Routine: atm_extract_content
409 * Purpose: Extract a set of aid from an array of voucher values.
410 * Returns: KERN_SUCCESS: on Success.
411 * KERN_FAILURE: one of the value is not present in the hash.
412 * KERN_NO_SPACE: insufficeint buffer provided to fill an array of aid.
416 ipc_voucher_attr_manager_t __assert_only manager
,
417 mach_voucher_attr_key_t __assert_only key
,
418 mach_voucher_attr_value_handle_array_t values
,
419 mach_msg_type_number_t value_count
,
420 mach_voucher_attr_recipe_command_t
*out_command
,
421 mach_voucher_attr_content_t out_recipe
,
422 mach_voucher_attr_content_size_t
*in_out_recipe_size
)
424 atm_value_t atm_value
;
425 mach_voucher_attr_value_handle_t atm_handle
;
428 assert(MACH_VOUCHER_ATTR_KEY_ATM
== key
);
429 assert(manager
== &atm_manager
);
431 for (i
= 0; i
< value_count
&& *in_out_recipe_size
> 0; i
++) {
432 atm_handle
= values
[i
];
433 atm_value
= HANDLE_TO_ATM_VALUE(atm_handle
);
434 if (atm_value
== VAM_DEFAULT_VALUE
) {
438 if ((sizeof(aid_t
)) > *in_out_recipe_size
) {
439 *in_out_recipe_size
= 0;
440 return KERN_NO_SPACE
;
443 memcpy(&out_recipe
[0], &atm_value
->aid
, sizeof(aid_t
));
444 *out_command
= MACH_VOUCHER_ATTR_ATM_NULL
;
445 *in_out_recipe_size
= sizeof(aid_t
);
449 *in_out_recipe_size
= 0;
454 * Routine: atm_command
455 * Purpose: Execute a command against a set of ATM values.
456 * Returns: KERN_SUCCESS: On successful execution of command.
457 * KERN_FAILURE: On failure.
461 ipc_voucher_attr_manager_t __assert_only manager
,
462 mach_voucher_attr_key_t __assert_only key
,
463 mach_voucher_attr_value_handle_array_t values
,
464 mach_msg_type_number_t value_count
,
465 mach_voucher_attr_command_t command
,
466 mach_voucher_attr_content_t in_content
,
467 mach_voucher_attr_content_size_t in_content_size
,
468 mach_voucher_attr_content_t out_content
,
469 mach_voucher_attr_content_size_t
*out_content_size
)
471 assert(MACH_VOUCHER_ATTR_KEY_ATM
== key
);
472 assert(manager
== &atm_manager
);
473 atm_value_t atm_value
= ATM_VALUE_NULL
;
475 mach_atm_subaid_t
*subaid_array
= NULL
;
476 mach_atm_subaid_t next_subaid
= 0;
477 uint32_t aid_array_count
= 0;
478 atm_task_descriptor_t task_descriptor
= ATM_TASK_DESCRIPTOR_NULL
;
480 kern_return_t kr
= KERN_SUCCESS
;
484 case ATM_ACTION_COLLECT
:
487 case ATM_ACTION_LOGFAIL
:
488 return KERN_NOT_SUPPORTED
;
490 case ATM_FIND_MIN_SUB_AID
:
491 if ((in_content_size
/ sizeof(aid_t
)) > (*out_content_size
/ sizeof(mach_atm_subaid_t
))) {
495 aid_array_count
= in_content_size
/ sizeof(aid_t
);
496 if (aid_array_count
> AID_ARRAY_COUNT_MAX
) {
500 subaid_array
= (mach_atm_subaid_t
*) (void *) out_content
;
501 for (i
= 0; i
< aid_array_count
; i
++) {
502 subaid_array
[i
] = ATM_SUBAID32_MAX
;
505 *out_content_size
= aid_array_count
* sizeof(mach_atm_subaid_t
);
511 case ATM_ACTION_UNREGISTER
:
512 /* find the first non-default atm_value */
513 for (i
= 0; i
< value_count
; i
++) {
514 atm_value
= HANDLE_TO_ATM_VALUE(values
[i
]);
515 if (atm_value
!= VAM_DEFAULT_VALUE
) {
520 /* if we are not able to find any atm values
521 * in stack then this call was made in error
523 if (atm_value
== NULL
) {
526 if (in_content
== NULL
|| in_content_size
!= sizeof(atm_guard_t
)) {
527 return KERN_INVALID_ARGUMENT
;
530 memcpy(&guard
, in_content
, sizeof(atm_guard_t
));
531 task
= current_task();
532 task_descriptor
= task
->atm_context
;
534 kr
= atm_value_unregister(atm_value
, task_descriptor
, guard
);
538 case ATM_ACTION_REGISTER
:
539 for (i
= 0; i
< value_count
; i
++) {
540 atm_value
= HANDLE_TO_ATM_VALUE(values
[i
]);
541 if (atm_value
!= VAM_DEFAULT_VALUE
) {
545 /* if we are not able to find any atm values
546 * in stack then this call was made in error
548 if (atm_value
== NULL
) {
551 if (in_content
== NULL
|| in_content_size
!= sizeof(atm_guard_t
)) {
552 return KERN_INVALID_ARGUMENT
;
555 memcpy(&guard
, in_content
, sizeof(atm_guard_t
));
556 task
= current_task();
557 task_descriptor
= task
->atm_context
;
559 kr
= atm_value_register(atm_value
, task_descriptor
, guard
);
563 case ATM_ACTION_GETSUBAID
:
564 if (out_content
== NULL
|| *out_content_size
!= sizeof(mach_atm_subaid_t
)) {
568 next_subaid
= get_subaid();
569 memcpy(out_content
, &next_subaid
, sizeof(mach_atm_subaid_t
));
573 kr
= KERN_INVALID_ARGUMENT
;
583 ipc_voucher_attr_manager_t __assert_only manager
)
585 assert(manager
== &atm_manager
);
590 * Routine: atm_value_alloc_init
591 * Purpose: Allocates an atm value struct and initialize it.
592 * Returns: atm_value_t: On Success with a sync count on atm_value.
593 * ATM_VALUE_NULL: On failure.
596 atm_value_alloc_init(aid_t aid
)
598 atm_value_t new_atm_value
= ATM_VALUE_NULL
;
600 new_atm_value
= (atm_value_t
) zalloc(atm_value_zone
);
601 if (new_atm_value
== ATM_VALUE_NULL
) {
602 panic("Ran out of ATM values structure.\n\n");
605 new_atm_value
->aid
= aid
;
606 queue_init(&new_atm_value
->listeners
);
607 new_atm_value
->sync
= 1;
608 new_atm_value
->listener_count
= 0;
609 os_ref_init(&new_atm_value
->reference_count
, NULL
);
610 lck_mtx_init(&new_atm_value
->listener_lock
, &atm_lock_grp
, &atm_lock_attr
);
612 #if DEVELOPMENT || DEBUG
613 lck_mtx_lock(&atm_values_list_lock
);
614 queue_enter(&atm_values_list
, new_atm_value
, atm_value_t
, value_elt
);
615 lck_mtx_unlock(&atm_values_list_lock
);
617 return new_atm_value
;
623 * Purpose: Increment the global aid counter and return it.
630 aid
= (aid_t
)OSIncrementAtomic64((SInt64
*)&global_aid
);
636 * Routine: get_subaid
637 * Purpose: Increment the global subaid counter and return it.
640 static mach_atm_subaid_t
643 mach_atm_subaid_t next_subaid
;
644 next_subaid
= (mach_atm_subaid_t
)OSIncrementAtomic64((SInt64
*)&global_subaid
);
650 * Routine: atm_value_dealloc
651 * Purpose: Drops the reference on atm value and deallocates.
652 * Deletes all the listeners on deallocation.
656 atm_value_dealloc(atm_value_t atm_value
)
658 if (os_ref_release(&atm_value
->reference_count
) == 0) {
659 /* Free up the atm value and also remove all the listeners. */
660 atm_listener_delete_all(atm_value
);
662 lck_mtx_destroy(&atm_value
->listener_lock
, &atm_lock_grp
);
664 #if DEVELOPMENT || DEBUG
665 lck_mtx_lock(&atm_values_list_lock
);
666 queue_remove(&atm_values_list
, atm_value
, atm_value_t
, value_elt
);
667 lck_mtx_unlock(&atm_values_list_lock
);
669 zfree(atm_value_zone
, atm_value
);
675 * Routine: atm_hash_table_init
676 * Purpose: Initialize the atm aid hash table.
680 atm_hash_table_init()
684 for (i
= 0; i
< ATM_MAX_HASH_TABLE_SIZE
; i
++) {
685 queue_init(&atm_value_hash_table
[i
].hash_list
);
686 lck_mtx_init(&atm_value_hash_table
[i
].hash_list_lock
, &atm_lock_grp
, &atm_lock_attr
);
692 * Routine: atm_value_hash_table_insert
693 * Purpose: Insert an atm value in the hash table.
694 * Returns: KERN_SUCCESS on success.
695 * KERN_NAME_EXISTS if atm value already in the hash table.
698 atm_value_hash_table_insert(atm_value_t new_atm_value
)
701 atm_value_hash_t hash_list_head
;
702 aid_t aid
= new_atm_value
->aid
;
705 hash_index
= AID_TO_HASH(aid
);
706 hash_list_head
= &atm_value_hash_table
[hash_index
];
708 /* Lock the atm list and search for the aid. */
709 lck_mtx_lock(&hash_list_head
->hash_list_lock
);
711 queue_iterate(&hash_list_head
->hash_list
, next
, atm_value_t
, vid_hash_elt
) {
712 if (next
->aid
== aid
) {
714 * aid found. return error.
716 lck_mtx_unlock(&hash_list_head
->hash_list_lock
);
717 return KERN_NAME_EXISTS
;
721 /* Enter the aid in hash and return success. */
722 queue_enter(&hash_list_head
->hash_list
, new_atm_value
, atm_value_t
, vid_hash_elt
);
723 lck_mtx_unlock(&hash_list_head
->hash_list_lock
);
729 * Routine: atm_value_hash_table_delete
730 * Purpose: Delete the atm value from the hash table.
734 atm_value_hash_table_delete(atm_value_t atm_value
)
737 atm_value_hash_t hash_list_head
;
738 aid_t aid
= atm_value
->aid
;
740 hash_index
= AID_TO_HASH(aid
);
741 hash_list_head
= &atm_value_hash_table
[hash_index
];
743 lck_mtx_lock(&hash_list_head
->hash_list_lock
);
744 queue_remove(&hash_list_head
->hash_list
, atm_value
, atm_value_t
, vid_hash_elt
);
745 lck_mtx_unlock(&hash_list_head
->hash_list_lock
);
750 * Routine: get_atm_value_from_aid
751 * Purpose: Search a given aid in atm value hash table and
752 * return the atm value stucture.
753 * Returns: atm value structure if aid found.
754 * ATM_VALUE_NULL: If aid not found in atm value hash table.
757 get_atm_value_from_aid(aid_t aid
)
760 atm_value_hash_t hash_list_head
;
763 hash_index
= AID_TO_HASH(aid
);
764 hash_list_head
= &atm_value_hash_table
[hash_index
];
766 /* Lock the atm list and search for the aid. */
767 lck_mtx_lock(&hash_list_head
->hash_list_lock
);
769 queue_iterate(&hash_list_head
->hash_list
, next
, atm_value_t
, vid_hash_elt
) {
770 if (next
->aid
== aid
) {
772 * Aid found. Incerease ref count and return
773 * the atm value structure.
775 os_ref_retain(&next
->reference_count
);
776 lck_mtx_unlock(&hash_list_head
->hash_list_lock
);
780 lck_mtx_unlock(&hash_list_head
->hash_list_lock
);
781 return ATM_VALUE_NULL
;
786 * Routine: atm_listener_insert
787 * Purpose: Insert a listener to an atm value.
788 * Returns: KERN_SUCCESS on success.
789 * KERN_FAILURE if the task is already present as a listener.
793 atm_value_t atm_value
,
794 atm_task_descriptor_t task_descriptor
,
797 atm_link_object_t new_link_object
;
798 atm_link_object_t next
, elem
;
799 int32_t freed_count
= 0, dead_but_not_freed
= 0, listener_count
;
800 boolean_t element_found
= FALSE
;
801 queue_head_t free_listeners
;
803 new_link_object
= (atm_link_object_t
) zalloc(atm_link_objects_zone
);
804 new_link_object
->descriptor
= task_descriptor
;
805 os_ref_init(&new_link_object
->reference_count
, NULL
);
806 new_link_object
->guard
= guard
;
808 /* Get a reference on the task descriptor */
809 os_ref_retain(&task_descriptor
->reference_count
);
810 queue_init(&free_listeners
);
811 listener_count
= atm_value
->listener_count
;
813 /* Check if the task is already on the listener list */
814 lck_mtx_lock(&atm_value
->listener_lock
);
816 next
= (atm_link_object_t
)(void *) queue_first(&atm_value
->listeners
);
817 while (!queue_end(&atm_value
->listeners
, (queue_entry_t
)next
)) {
819 next
= (atm_link_object_t
)(void *) queue_next(&next
->listeners_element
);
821 /* Check for dead tasks */
822 if (elem
->descriptor
->flags
== ATM_TASK_DEAD
) {
823 if ((dead_but_not_freed
> ATM_LIST_DEAD_MAX
) || elem
->guard
== 0) {
824 queue_remove(&atm_value
->listeners
, elem
, atm_link_object_t
, listeners_element
);
825 queue_enter(&free_listeners
, elem
, atm_link_object_t
, listeners_element
);
826 atm_listener_count_decr_internal(atm_value
);
829 dead_but_not_freed
++;
838 if (elem
->descriptor
== task_descriptor
) {
839 /* Increment reference count on Link object. */
840 os_ref_retain(&elem
->reference_count
);
842 /* Replace the guard with the new one, the old guard is anyways on unregister path. */
844 element_found
= TRUE
;
845 KERNEL_DEBUG_CONSTANT((ATM_CODE(ATM_GETVALUE_INFO
, (ATM_VALUE_REPLACED
))) | DBG_FUNC_NONE
,
846 VM_KERNEL_ADDRPERM(atm_value
), atm_value
->aid
, guard
, 0, 0);
851 lck_mtx_unlock(&atm_value
->listener_lock
);
852 /* Drop the extra reference on task descriptor taken by this function. */
853 atm_task_descriptor_dealloc(task_descriptor
);
854 zfree(atm_link_objects_zone
, new_link_object
);
856 KERNEL_DEBUG_CONSTANT((ATM_CODE(ATM_GETVALUE_INFO
, (ATM_VALUE_ADDED
))) | DBG_FUNC_NONE
,
857 VM_KERNEL_ADDRPERM(atm_value
), atm_value
->aid
, guard
, 0, 0);
859 queue_enter(&atm_value
->listeners
, new_link_object
, atm_link_object_t
, listeners_element
);
860 atm_listener_count_incr_internal(atm_value
);
861 lck_mtx_unlock(&atm_value
->listener_lock
);
864 /* Free the link objects */
865 while (!queue_empty(&free_listeners
)) {
866 queue_remove_first(&free_listeners
, next
, atm_link_object_t
, listeners_element
);
868 /* Deallocate the link object */
869 atm_link_dealloc(next
);
872 KERNEL_DEBUG_CONSTANT((ATM_CODE(ATM_SUBAID_INFO
, (ATM_LINK_LIST_TRIM
))) | DBG_FUNC_NONE
,
873 listener_count
, freed_count
, dead_but_not_freed
, VM_KERNEL_ADDRPERM(atm_value
), 1);
880 * Routine: atm_listener_delete_all
881 * Purpose: Deletes all the listeners for an atm value.
885 atm_listener_delete_all(atm_value_t atm_value
)
887 atm_link_object_t next
;
889 while (!queue_empty(&atm_value
->listeners
)) {
890 queue_remove_first(&atm_value
->listeners
, next
, atm_link_object_t
, listeners_element
);
892 /* Deallocate the link object */
893 atm_link_dealloc(next
);
899 * Routine: atm_listener_delete
900 * Purpose: Deletes a listerner for an atm value.
901 * Returns: KERN_SUCCESS on successful unregister.
902 * KERN_INVALID_VALUE on finding a different guard.
903 * KERN_FAILURE on failure.
907 atm_value_t atm_value
,
908 atm_task_descriptor_t task_descriptor
,
911 queue_head_t free_listeners
;
912 atm_link_object_t next
, elem
;
913 kern_return_t kr
= KERN_FAILURE
;
915 queue_init(&free_listeners
);
917 lck_mtx_lock(&atm_value
->listener_lock
);
919 next
= (atm_link_object_t
)(void *) queue_first(&atm_value
->listeners
);
920 while (!queue_end(&atm_value
->listeners
, (queue_entry_t
)next
)) {
922 next
= (atm_link_object_t
)(void *) queue_next(&next
->listeners_element
);
924 if (elem
->descriptor
== task_descriptor
) {
925 if (elem
->guard
== guard
) {
926 KERNEL_DEBUG_CONSTANT((ATM_CODE(ATM_UNREGISTER_INFO
,
927 (ATM_VALUE_UNREGISTERED
))) | DBG_FUNC_NONE
,
928 VM_KERNEL_ADDRPERM(atm_value
), atm_value
->aid
, guard
, os_ref_get_count(&elem
->reference_count
), 0);
932 KERNEL_DEBUG_CONSTANT((ATM_CODE(ATM_UNREGISTER_INFO
,
933 (ATM_VALUE_DIFF_MAILBOX
))) | DBG_FUNC_NONE
,
934 VM_KERNEL_ADDRPERM(atm_value
), atm_value
->aid
, elem
->guard
, os_ref_get_count(&elem
->reference_count
), 0);
935 kr
= KERN_INVALID_VALUE
;
937 if (os_ref_release(&elem
->reference_count
) == 0) {
938 queue_remove(&atm_value
->listeners
, elem
, atm_link_object_t
, listeners_element
);
939 queue_enter(&free_listeners
, elem
, atm_link_object_t
, listeners_element
);
940 atm_listener_count_decr_internal(atm_value
);
945 lck_mtx_unlock(&atm_value
->listener_lock
);
947 while (!queue_empty(&free_listeners
)) {
948 queue_remove_first(&free_listeners
, next
, atm_link_object_t
, listeners_element
);
950 /* Deallocate the link object */
951 atm_link_dealloc(next
);
958 * Routine: atm_descriptor_alloc_init
959 * Purpose: Allocate an atm task descriptor and initialize it and takes a reference.
960 * Returns: atm task descriptor: On success.
963 static atm_task_descriptor_t
964 atm_task_descriptor_alloc_init(
965 mach_port_t trace_buffer
,
966 uint64_t buffer_size
,
967 task_t __assert_only task
)
969 atm_task_descriptor_t new_task_descriptor
;
971 new_task_descriptor
= (atm_task_descriptor_t
) zalloc(atm_descriptors_zone
);
973 new_task_descriptor
->trace_buffer
= trace_buffer
;
974 new_task_descriptor
->trace_buffer_size
= buffer_size
;
975 os_ref_init(&new_task_descriptor
->reference_count
, NULL
);
976 new_task_descriptor
->flags
= 0;
977 lck_mtx_init(&new_task_descriptor
->lock
, &atm_lock_grp
, &atm_lock_attr
);
979 #if DEVELOPMENT || DEBUG
980 new_task_descriptor
->task
= task
;
981 lck_mtx_lock(&atm_descriptors_list_lock
);
982 queue_enter(&atm_descriptors_list
, new_task_descriptor
, atm_task_descriptor_t
, descriptor_elt
);
983 lck_mtx_unlock(&atm_descriptors_list_lock
);
986 return new_task_descriptor
;
991 * Routine: atm_task_descriptor_dealloc
992 * Prupose: Drops the reference on atm descriptor.
996 atm_task_descriptor_dealloc(atm_task_descriptor_t task_descriptor
)
998 if (os_ref_release(&task_descriptor
->reference_count
) == 0) {
999 #if DEVELOPMENT || DEBUG
1000 lck_mtx_lock(&atm_descriptors_list_lock
);
1001 queue_remove(&atm_descriptors_list
, task_descriptor
, atm_task_descriptor_t
, descriptor_elt
);
1002 lck_mtx_unlock(&atm_descriptors_list_lock
);
1004 /* release the send right for the named memory entry */
1005 ipc_port_release_send(task_descriptor
->trace_buffer
);
1006 lck_mtx_destroy(&task_descriptor
->lock
, &atm_lock_grp
);
1007 zfree(atm_descriptors_zone
, task_descriptor
);
1013 * Routine: atm_link_dealloc
1014 * Prupose: Drops the reference on link object.
1018 atm_link_dealloc(atm_link_object_t link_object
)
1020 /* Drop the reference on atm task descriptor. */
1021 atm_task_descriptor_dealloc(link_object
->descriptor
);
1022 zfree(atm_link_objects_zone
, link_object
);
1027 * Routine: atm_register_trace_memory
1028 * Purpose: Registers trace memory for a task.
1029 * Returns: KERN_SUCCESS: on Success.
1030 * KERN_FAILURE: on Error.
1033 atm_register_trace_memory(
1035 uint64_t trace_buffer_address
,
1036 uint64_t buffer_size
)
1038 atm_task_descriptor_t task_descriptor
;
1039 mach_port_t trace_buffer
= MACH_PORT_NULL
;
1040 kern_return_t kr
= KERN_SUCCESS
;
1042 if (disable_atm
|| (atm_get_diagnostic_config() & ATM_TRACE_DISABLE
)) {
1043 return KERN_NOT_SUPPORTED
;
1046 if (task
!= current_task()) {
1047 return KERN_INVALID_ARGUMENT
;
1050 if (task
->atm_context
!= NULL
1051 || (void *)trace_buffer_address
== NULL
1053 || (buffer_size
& PAGE_MASK
) != 0
1054 || buffer_size
> MAX_TRACE_BUFFER_SIZE
) {
1055 return KERN_INVALID_ARGUMENT
;
1058 vm_map_t map
= current_map();
1059 memory_object_size_t mo_size
= (memory_object_size_t
) buffer_size
;
1060 kr
= mach_make_memory_entry_64(map
,
1062 (mach_vm_offset_t
)trace_buffer_address
,
1066 if (kr
!= KERN_SUCCESS
) {
1070 task_descriptor
= atm_task_descriptor_alloc_init(trace_buffer
, buffer_size
, task
);
1071 if (task_descriptor
== ATM_TASK_DESCRIPTOR_NULL
) {
1072 ipc_port_release_send(trace_buffer
);
1073 return KERN_NO_SPACE
;
1077 if (task
->atm_context
== NULL
) {
1078 task
->atm_context
= task_descriptor
;
1085 if (kr
!= KERN_SUCCESS
) {
1086 /* undo the mapping and allocations since we failed to hook descriptor to task */
1087 atm_task_descriptor_dealloc(task_descriptor
);
1089 return KERN_SUCCESS
;
1093 * Routine: atm_set_diagnostic_config
1094 * Purpose: Set global atm_diagnostic_config and update the commpage to reflect
1096 * Returns: Error if ATM is disabled.
1098 extern uint32_t atm_diagnostic_config
; /* Proxied to commpage for fast user access */
1100 atm_set_diagnostic_config(uint32_t diagnostic_config
)
1103 return KERN_NOT_SUPPORTED
;
1106 atm_diagnostic_config
= diagnostic_config
;
1107 commpage_update_atm_diagnostic_config(atm_diagnostic_config
);
1109 return KERN_SUCCESS
;
1114 * Routine: atm_get_diagnostic_config
1115 * Purpose: Get global atm_diagnostic_config.
1116 * Returns: Diagnostic value
1119 atm_get_diagnostic_config(void)
1121 return atm_diagnostic_config
;
1126 * Routine: atm_value_unregister
1127 * Purpose: Unregisters a process from an activity id.
1128 * Returns: KERN_SUCCESS on successful unregister.
1129 * KERN_INVALID_VALUE on finding a diff guard.
1130 * KERN_FAILURE on failure.
1132 static kern_return_t
1133 atm_value_unregister(
1134 atm_value_t atm_value
,
1135 atm_task_descriptor_t task_descriptor
,
1140 if (task_descriptor
== ATM_TASK_DESCRIPTOR_NULL
) {
1141 return KERN_INVALID_TASK
;
1144 kr
= atm_listener_delete(atm_value
, task_descriptor
, guard
);
1150 * Routine: atm_value_register
1151 * Purpose: Registers a process for an activity id.
1152 * Returns: KERN_SUCCESS on successful register.
1153 * KERN_INVALID_TASK on finding a null task atm context.
1154 * KERN_FAILURE on failure.
1156 static kern_return_t
1158 atm_value_t atm_value
,
1159 atm_task_descriptor_t task_descriptor
,
1164 if (task_descriptor
== ATM_TASK_DESCRIPTOR_NULL
) {
1165 return KERN_INVALID_TASK
;
1168 kr
= atm_listener_insert(atm_value
, task_descriptor
, guard
);
1174 atm_task_descriptor_destroy(atm_task_descriptor_t task_descriptor
)
1176 /* Mark the task dead in the task descriptor to make task descriptor eligible for cleanup. */
1177 lck_mtx_lock(&task_descriptor
->lock
);
1178 task_descriptor
->flags
= ATM_TASK_DEAD
;
1179 lck_mtx_unlock(&task_descriptor
->lock
);
1181 atm_task_descriptor_dealloc(task_descriptor
);