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();
72 static mach_atm_subaid_t
get_subaid();
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();
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 void atm_value_get_ref(atm_value_t atm_value
);
80 static kern_return_t
atm_listener_insert(atm_value_t atm_value
, atm_task_descriptor_t task_descriptor
, atm_guard_t guard
);
81 static void atm_listener_delete_all(atm_value_t atm_value
);
82 static atm_task_descriptor_t
atm_task_descriptor_alloc_init(mach_port_t trace_buffer
,uint64_t buffer_size
, __assert_only task_t task
);
83 static void atm_descriptor_get_reference(atm_task_descriptor_t task_descriptor
);
84 static void atm_task_descriptor_dealloc(atm_task_descriptor_t task_descriptor
);
85 static kern_return_t
atm_value_unregister(atm_value_t atm_value
, atm_task_descriptor_t task_descriptor
, atm_guard_t guard
);
86 static kern_return_t
atm_value_register(atm_value_t atm_value
, atm_task_descriptor_t task_descriptor
, atm_guard_t guard
);
87 static kern_return_t
atm_listener_delete(atm_value_t atm_value
, atm_task_descriptor_t task_descriptor
, atm_guard_t guard
);
88 static void atm_link_get_reference(atm_link_object_t link_object
) __unused
;
89 static void atm_link_dealloc(atm_link_object_t link_object
);
90 kern_return_t
atm_invoke_collection(atm_value_t atm_value
, mach_atm_subaid_t subaid
, uint32_t flags
);
91 kern_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
);
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
);
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 mach_voucher_attr_value_flags_t
*out_flags
,
111 ipc_voucher_t
*out_value_voucher
);
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
);
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
);
136 atm_release(ipc_voucher_attr_manager_t __assert_only manager
);
139 * communication channel from voucher system to ATM
141 struct 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
,
147 .ivam_flags
= IVAM_FLAGS_NONE
,
150 #if DEVELOPMENT || DEBUG
151 decl_lck_mtx_data(, atm_descriptors_list_lock
);
152 decl_lck_mtx_data(, atm_values_list_lock
);
154 lck_grp_t atm_dev_lock_grp
;
155 lck_attr_t atm_dev_lock_attr
;
156 lck_grp_attr_t atm_dev_lock_grp_attr
;
159 extern vm_map_t kernel_map
;
161 * Global aid. Incremented on each get_aid.
166 * Global subaid. Incremented on each get_subaid.
168 mach_atm_subaid_t global_subaid
;
171 * Lock group attributes for atm sub system.
173 lck_grp_t atm_lock_grp
;
174 lck_attr_t atm_lock_attr
;
175 lck_grp_attr_t atm_lock_grp_attr
;
178 * Global that is set by diagnosticd and readable by userspace
181 static uint32_t atm_diagnostic_config
;
185 * Purpose: Initialize the atm subsystem.
191 kern_return_t kr
= KERN_SUCCESS
;
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
)))) {
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;
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
),
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");
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
),
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
);
229 atm_hash_table_init();
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
);
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
);
240 queue_init(&atm_descriptors_list
);
241 queue_init(&atm_values_list
);
244 /* Register the atm manager with the Vouchers sub system. */
245 kr
= ipc_register_well_known_mach_voucher_attr_manager(
248 MACH_VOUCHER_ATTR_KEY_ATM
,
249 &voucher_attr_control
);
250 if (kr
!= KERN_SUCCESS
)
251 panic("ATM subsystem initialization failed");
253 kprintf("ATM subsystem is initialized\n");
259 * ATM Resource Manager Routines.
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.
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
)
276 atm_value_t atm_value
= ATM_VALUE_NULL
;
278 assert(MACH_VOUCHER_ATTR_KEY_ATM
== key
);
279 assert(manager
== &atm_manager
);
281 atm_value
= HANDLE_TO_ATM_VALUE(value
);
282 if (atm_value
== VAM_DEFAULT_VALUE
) {
283 /* Return success for default value */
287 if (atm_value
->sync
!= sync
) {
291 /* Deallocate the atm value. */
292 atm_value_hash_table_delete(atm_value
);
293 atm_value_dealloc(atm_value
);
299 * Routine: atm_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
,
311 mach_voucher_attr_value_flags_t
*out_flags
,
312 ipc_voucher_t
*out_value_voucher
)
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
;
321 kern_return_t kr
= KERN_SUCCESS
;
323 assert(MACH_VOUCHER_ATTR_KEY_ATM
== key
);
324 assert(manager
== &atm_manager
);
326 /* never an out voucher */
327 *out_value_voucher
= IPC_VOUCHER_NULL
;
328 *out_flags
= MACH_VOUCHER_ATTR_VALUE_FLAGS_NONE
;
330 if (disable_atm
|| (atm_get_diagnostic_config() & ATM_TRACE_DISABLE
))
331 return KERN_NOT_SUPPORTED
;
335 case MACH_VOUCHER_ATTR_ATM_REGISTER
:
337 for (i
= 0; i
< prev_value_count
; i
++) {
338 atm_handle
= prev_values
[i
];
339 atm_value
= HANDLE_TO_ATM_VALUE(atm_handle
);
341 if (atm_value
== VAM_DEFAULT_VALUE
)
344 if (recipe_size
!= sizeof(atm_guard_t
)) {
345 kr
= KERN_INVALID_ARGUMENT
;
348 memcpy(&guard
, recipe
, sizeof(atm_guard_t
));
350 task
= current_task();
351 task_descriptor
= task
->atm_context
;
353 kr
= atm_value_register(atm_value
, task_descriptor
, guard
);
354 if (kr
!= KERN_SUCCESS
) {
358 /* Increment sync value. */
359 atm_sync_reference_internal(atm_value
);
361 *out_value
= atm_handle
;
365 *out_value
= ATM_VALUE_TO_HANDLE(VAM_DEFAULT_VALUE
);
368 case MACH_VOUCHER_ATTR_ATM_CREATE
:
370 /* Handle the old case where aid value is created in kernel */
371 if (recipe_size
== 0) {
373 } else if (recipe_size
== sizeof(aid_t
)) {
374 memcpy(&aid
, recipe
, sizeof(aid_t
));
376 kr
= KERN_INVALID_ARGUMENT
;
380 /* Allocate a new atm value. */
381 atm_value
= atm_value_alloc_init(aid
);
382 if (atm_value
== ATM_VALUE_NULL
) {
383 kr
= KERN_RESOURCE_SHORTAGE
;
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();
393 atm_value_dealloc(atm_value
);
397 *out_value
= ATM_VALUE_TO_HANDLE(atm_value
);
400 case MACH_VOUCHER_ATTR_ATM_NULL
:
402 kr
= KERN_INVALID_ARGUMENT
;
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.
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
)
427 atm_value_t atm_value
;
428 mach_voucher_attr_value_handle_t atm_handle
;
431 assert(MACH_VOUCHER_ATTR_KEY_ATM
== key
);
432 assert(manager
== &atm_manager
);
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
)
440 if (( sizeof(aid_t
)) > *in_out_recipe_size
) {
441 *in_out_recipe_size
= 0;
442 return KERN_NO_SPACE
;
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
);
451 *in_out_recipe_size
= 0;
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.
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
)
473 assert(MACH_VOUCHER_ATTR_KEY_ATM
== key
);
474 assert(manager
== &atm_manager
);
475 atm_value_t atm_value
= ATM_VALUE_NULL
;
477 mach_atm_subaid_t
*subaid_array
= NULL
;
478 mach_atm_subaid_t next_subaid
= 0;
479 uint32_t aid_array_count
= 0;
480 atm_task_descriptor_t task_descriptor
= ATM_TASK_DESCRIPTOR_NULL
;
482 uint32_t collection_flags
= ATM_ACTION_LOGFAIL
;
483 kern_return_t kr
= KERN_SUCCESS
;
487 case ATM_ACTION_COLLECT
:
488 collection_flags
= ATM_ACTION_COLLECT
;
491 case ATM_ACTION_LOGFAIL
: {
492 mach_atm_subaid_t sub_aid
= 0;
494 if (disable_atm
|| (atm_get_diagnostic_config() & ATM_TRACE_DISABLE
))
495 return KERN_NOT_SUPPORTED
;
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
)
504 /* if we are not able to find any atm values
505 * in stack then this call was made in error
507 if (atm_value
== NULL
) {
511 if (in_content_size
>= sizeof(mach_atm_subaid_t
)) {
512 sub_aid
= *(mach_atm_subaid_t
*)(void *)in_content
;
515 *out_content_size
= 0;
516 kr
= atm_invoke_collection(atm_value
, sub_aid
, collection_flags
);
520 case ATM_FIND_MIN_SUB_AID
:
521 if ((in_content_size
/sizeof(aid_t
)) > (*out_content_size
/sizeof(mach_atm_subaid_t
)))
524 aid_array_count
= in_content_size
/ sizeof(aid_t
);
525 if (aid_array_count
> AID_ARRAY_COUNT_MAX
)
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
;
533 *out_content_size
= aid_array_count
* sizeof(mach_atm_subaid_t
);
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
)
547 /* if we are not able to find any atm values
548 * in stack then this call was made in error
550 if (atm_value
== NULL
) {
553 if (in_content
== NULL
|| in_content_size
!= sizeof(atm_guard_t
)){
554 return KERN_INVALID_ARGUMENT
;
557 memcpy(&guard
, in_content
, sizeof(atm_guard_t
));
558 task
= current_task();
559 task_descriptor
= task
->atm_context
;
561 kr
= atm_value_unregister(atm_value
, task_descriptor
, guard
);
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
)
571 /* if we are not able to find any atm values
572 * in stack then this call was made in error
574 if (atm_value
== NULL
) {
577 if (in_content
== NULL
|| in_content_size
!= sizeof(atm_guard_t
)){
578 return KERN_INVALID_ARGUMENT
;
581 memcpy(&guard
, in_content
, sizeof(atm_guard_t
));
582 task
= current_task();
583 task_descriptor
= task
->atm_context
;
585 kr
= atm_value_register(atm_value
, task_descriptor
, guard
);
589 case ATM_ACTION_GETSUBAID
:
590 if (out_content
== NULL
|| *out_content_size
!= sizeof(mach_atm_subaid_t
))
593 next_subaid
= get_subaid();
594 memcpy(out_content
, &next_subaid
, sizeof(mach_atm_subaid_t
));
598 kr
= KERN_INVALID_ARGUMENT
;
608 ipc_voucher_attr_manager_t __assert_only manager
)
610 assert(manager
== &atm_manager
);
615 * Routine: atm_invoke_collection
616 * Purpose: Sends a notification with array of memory buffer.
617 * Note: may block till user daemon responds.
620 atm_invoke_collection(
621 atm_value_t atm_value
,
622 mach_atm_subaid_t sub_aid
,
625 aid_t aid
= atm_value
->aid
;
626 kern_return_t kr
= KERN_SUCCESS
;
627 uint32_t array_count
= 0, i
= 0, j
= 0, requestor_index
= 0;
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
;
634 lck_mtx_lock(&atm_value
->listener_lock
);
635 array_count
= atm_value
->listener_count
;
636 lck_mtx_unlock(&atm_value
->listener_lock
);
638 if (array_count
== 0){
642 mem_array
= kalloc(sizeof(mach_port_t
) * array_count
);
643 if (mem_array
== NULL
){
644 return KERN_NO_SPACE
;
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
;
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
){
659 if (!need_swap_first
&& requesting_descriptor
== link_object
->descriptor
){
660 assert(requesting_descriptor
!= NULL
);
662 need_swap_first
= TRUE
;
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
])){
672 lck_mtx_unlock(&atm_value
->listener_lock
);
675 * Swap the position of requesting task ahead, diagnostics can
676 * process its buffers the first.
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
;
689 kr
= atm_send_user_notification(aid
, sub_aid
, mem_array
, sizes_array
, i
, flags
);
692 for (j
= 0; j
< i
; j
++) {
693 if (mem_array
[j
] != NULL
)
694 ipc_port_release_send(mem_array
[j
]);
697 kfree(mem_array
, sizeof(mach_port_t
) * array_count
);
698 kfree(sizes_array
, sizeof(uint64_t) * array_count
);
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.
710 atm_send_user_notification(
712 mach_atm_subaid_t sub_aid
,
713 mach_port_t
*buffers_array
,
714 uint64_t *sizes_array
,
715 mach_msg_type_number_t count
,
718 mach_port_t user_port
;
720 thread_t th
= current_thread();
723 error
= host_get_atm_notification_port(host_priv_self(), &user_port
);
724 if ((error
!= KERN_SUCCESS
) || !IPC_PORT_VALID(user_port
)) {
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
);
734 if (kr
== MACH_SEND_TIMED_OUT
) {
741 * Routine: atm_send_proc_inspect_notification
742 * Purpose: Make an upcall to user space daemon if its listening for trace
743 * notifications for per process inspection.
744 * Returns: KERN_SUCCESS for successful delivery.
745 * KERN_FAILURE if port is dead or NULL.
749 atm_send_proc_inspect_notification(
752 uint64_t traced_uniqueid
)
754 mach_port_t user_port
= MACH_PORT_NULL
;
755 mach_port_t memory_port
= MACH_PORT_NULL
;
757 atm_task_descriptor_t task_descriptor
= ATM_TASK_DESCRIPTOR_NULL
;
758 uint64_t buffer_size
= 0;
760 thread_t th
= current_thread();
762 if (disable_atm
|| (atm_get_diagnostic_config() & ATM_TRACE_DISABLE
))
763 return KERN_NOT_SUPPORTED
;
765 /* look for the requested memory in target task */
767 return KERN_INVALID_TASK
;
770 if (task
->atm_context
){
771 task_descriptor
= task
->atm_context
;
772 atm_descriptor_get_reference(task_descriptor
);
776 if (task_descriptor
== ATM_TASK_DESCRIPTOR_NULL
){
780 memory_port
= ipc_port_copy_send(task_descriptor
->trace_buffer
);
781 buffer_size
= task_descriptor
->trace_buffer_size
;
782 atm_task_descriptor_dealloc(task_descriptor
);
784 /* get the communication port */
785 error
= host_get_atm_notification_port(host_priv_self(), &user_port
);
786 if ((error
!= KERN_SUCCESS
) || !IPC_PORT_VALID(user_port
)) {
787 ipc_port_release_send(memory_port
);
791 /* Set the honor queue limit option on the thread. */
792 th
->options
|= TH_OPT_HONOR_QLIMIT
;
793 kr
= atm_inspect_process_buffer(user_port
, traced_pid
, traced_uniqueid
, buffer_size
, memory_port
);
794 /* Make sure that honor queue limit option is unset on the thread. */
795 th
->options
&= (~TH_OPT_HONOR_QLIMIT
);
797 if (kr
== MACH_SEND_TIMED_OUT
) {
801 ipc_port_release_send(memory_port
);
806 * Routine: atm_value_alloc_init
807 * Purpose: Allocates an atm value struct and initialize it.
808 * Returns: atm_value_t: On Success with a sync count on atm_value.
809 * ATM_VALUE_NULL: On failure.
812 atm_value_alloc_init(aid_t aid
)
814 atm_value_t new_atm_value
= ATM_VALUE_NULL
;
816 new_atm_value
= (atm_value_t
) zalloc(atm_value_zone
);
817 if (new_atm_value
== ATM_VALUE_NULL
)
818 panic("Ran out of ATM values structure.\n\n");
820 new_atm_value
->aid
= aid
;
821 queue_init(&new_atm_value
->listeners
);
822 new_atm_value
->sync
= 1;
823 new_atm_value
->listener_count
= 0;
824 new_atm_value
->reference_count
= 1;
825 lck_mtx_init(&new_atm_value
->listener_lock
, &atm_lock_grp
, &atm_lock_attr
);
827 #if DEVELOPMENT || DEBUG
828 lck_mtx_lock(&atm_values_list_lock
);
829 queue_enter(&atm_values_list
, new_atm_value
, atm_value_t
, value_elt
);
830 lck_mtx_unlock(&atm_values_list_lock
);
832 return new_atm_value
;
838 * Purpose: Increment the global aid counter and return it.
845 aid
= (aid_t
)OSIncrementAtomic64((SInt64
*)&global_aid
);
851 * Routine: get_subaid
852 * Purpose: Increment the global subaid counter and return it.
855 static mach_atm_subaid_t
858 mach_atm_subaid_t next_subaid
;
859 next_subaid
= (mach_atm_subaid_t
)OSIncrementAtomic64((SInt64
*)&global_subaid
);
865 * Routine: atm_value_dealloc
866 * Purpose: Drops the reference on atm value and deallocates.
867 * Deletes all the listeners on deallocation.
871 atm_value_dealloc(atm_value_t atm_value
)
873 if (0 < atm_value_release_internal(atm_value
)) {
877 assert(atm_value
->reference_count
== 0);
879 /* Free up the atm value and also remove all the listeners. */
880 atm_listener_delete_all(atm_value
);
882 lck_mtx_destroy(&atm_value
->listener_lock
, &atm_lock_grp
);
884 #if DEVELOPMENT || DEBUG
885 lck_mtx_lock(&atm_values_list_lock
);
886 queue_remove(&atm_values_list
, atm_value
, atm_value_t
, value_elt
);
887 lck_mtx_unlock(&atm_values_list_lock
);
889 zfree(atm_value_zone
, atm_value
);
895 * Routine: atm_hash_table_init
896 * Purpose: Initialize the atm aid hash table.
900 atm_hash_table_init()
904 for (i
= 0; i
< ATM_MAX_HASH_TABLE_SIZE
; i
++) {
905 queue_init(&atm_value_hash_table
[i
].hash_list
);
906 lck_mtx_init(&atm_value_hash_table
[i
].hash_list_lock
, &atm_lock_grp
, &atm_lock_attr
);
912 * Routine: atm_value_hash_table_insert
913 * Purpose: Insert an atm value in the hash table.
914 * Returns: KERN_SUCCESS on success.
915 * KERN_NAME_EXISTS if atm value already in the hash table.
918 atm_value_hash_table_insert(atm_value_t new_atm_value
)
921 atm_value_hash_t hash_list_head
;
922 aid_t aid
= new_atm_value
->aid
;
925 hash_index
= AID_TO_HASH(aid
);
926 hash_list_head
= &atm_value_hash_table
[hash_index
];
928 /* Lock the atm list and search for the aid. */
929 lck_mtx_lock(&hash_list_head
->hash_list_lock
);
931 queue_iterate(&hash_list_head
->hash_list
, next
, atm_value_t
, vid_hash_elt
) {
932 if (next
->aid
== aid
) {
934 * aid found. return error.
936 lck_mtx_unlock(&hash_list_head
->hash_list_lock
);
937 return (KERN_NAME_EXISTS
);
941 /* Enter the aid in hash and return success. */
942 queue_enter(&hash_list_head
->hash_list
, new_atm_value
, atm_value_t
, vid_hash_elt
);
943 lck_mtx_unlock(&hash_list_head
->hash_list_lock
);
949 * Routine: atm_value_hash_table_delete
950 * Purpose: Delete the atm value from the hash table.
954 atm_value_hash_table_delete(atm_value_t atm_value
)
957 atm_value_hash_t hash_list_head
;
958 aid_t aid
= atm_value
->aid
;
960 hash_index
= AID_TO_HASH(aid
);
961 hash_list_head
= &atm_value_hash_table
[hash_index
];
963 lck_mtx_lock(&hash_list_head
->hash_list_lock
);
964 queue_remove(&hash_list_head
->hash_list
, atm_value
, atm_value_t
, vid_hash_elt
);
965 lck_mtx_unlock(&hash_list_head
->hash_list_lock
);
970 * Routine: get_atm_value_from_aid
971 * Purpose: Search a given aid in atm value hash table and
972 * return the atm value stucture.
973 * Returns: atm value structure if aid found.
974 * ATM_VALUE_NULL: If aid not found in atm value hash table.
977 get_atm_value_from_aid(aid_t aid
)
980 atm_value_hash_t hash_list_head
;
983 hash_index
= AID_TO_HASH(aid
);
984 hash_list_head
= &atm_value_hash_table
[hash_index
];
986 /* Lock the atm list and search for the aid. */
987 lck_mtx_lock(&hash_list_head
->hash_list_lock
);
989 queue_iterate(&hash_list_head
->hash_list
, next
, atm_value_t
, vid_hash_elt
) {
990 if (next
->aid
== aid
) {
992 * Aid found. Incerease ref count and return
993 * the atm value structure.
995 atm_value_get_ref(next
);
996 lck_mtx_unlock(&hash_list_head
->hash_list_lock
);
1000 lck_mtx_unlock(&hash_list_head
->hash_list_lock
);
1001 return ATM_VALUE_NULL
;
1006 * Routine: atm_value_get_ref
1007 * Purpose: Get a reference on atm value.
1011 atm_value_get_ref(atm_value_t atm_value
)
1013 atm_value_reference_internal(atm_value
);
1018 * Routine: atm_listener_insert
1019 * Purpose: Insert a listener to an atm value.
1020 * Returns: KERN_SUCCESS on success.
1021 * KERN_FAILURE if the task is already present as a listener.
1023 static kern_return_t
1024 atm_listener_insert(
1025 atm_value_t atm_value
,
1026 atm_task_descriptor_t task_descriptor
,
1029 atm_link_object_t new_link_object
;
1030 atm_link_object_t next
, elem
;
1031 int32_t freed_count
= 0, dead_but_not_freed
= 0, listener_count
;
1032 boolean_t element_found
= FALSE
;
1033 queue_head_t free_listeners
;
1035 new_link_object
= (atm_link_object_t
) zalloc(atm_link_objects_zone
);
1036 new_link_object
->descriptor
= task_descriptor
;
1037 new_link_object
->reference_count
= 1;
1038 new_link_object
->guard
= guard
;
1040 /* Get a reference on the task descriptor */
1041 atm_descriptor_get_reference(task_descriptor
);
1042 queue_init(&free_listeners
);
1043 listener_count
= atm_value
->listener_count
;
1045 /* Check if the task is already on the listener list */
1046 lck_mtx_lock(&atm_value
->listener_lock
);
1048 next
= (atm_link_object_t
)(void *) queue_first(&atm_value
->listeners
);
1049 while (!queue_end(&atm_value
->listeners
, (queue_entry_t
)next
)) {
1051 next
= (atm_link_object_t
)(void *) queue_next(&next
->listeners_element
);
1053 /* Check for dead tasks */
1054 if (elem
->descriptor
->flags
== ATM_TASK_DEAD
) {
1055 if ((dead_but_not_freed
> ATM_LIST_DEAD_MAX
) || elem
->guard
== 0) {
1056 queue_remove(&atm_value
->listeners
, elem
, atm_link_object_t
, listeners_element
);
1057 queue_enter(&free_listeners
, elem
, atm_link_object_t
, listeners_element
);
1058 atm_listener_count_decr_internal(atm_value
);
1061 dead_but_not_freed
++;
1069 if (elem
->descriptor
== task_descriptor
) {
1070 /* Increment reference count on Link object. */
1071 atm_link_get_reference(elem
);
1073 /* Replace the guard with the new one, the old guard is anyways on unregister path. */
1074 elem
->guard
= guard
;
1075 element_found
= TRUE
;
1076 KERNEL_DEBUG_CONSTANT((ATM_CODE(ATM_GETVALUE_INFO
, (ATM_VALUE_REPLACED
))) | DBG_FUNC_NONE
,
1077 VM_KERNEL_ADDRPERM(atm_value
), atm_value
->aid
, guard
, 0, 0);
1082 if (element_found
) {
1083 lck_mtx_unlock(&atm_value
->listener_lock
);
1084 /* Drop the extra reference on task descriptor taken by this function. */
1085 atm_task_descriptor_dealloc(task_descriptor
);
1086 zfree(atm_link_objects_zone
, new_link_object
);
1088 KERNEL_DEBUG_CONSTANT((ATM_CODE(ATM_GETVALUE_INFO
, (ATM_VALUE_ADDED
))) | DBG_FUNC_NONE
,
1089 VM_KERNEL_ADDRPERM(atm_value
), atm_value
->aid
, guard
, 0, 0);
1091 queue_enter(&atm_value
->listeners
, new_link_object
, atm_link_object_t
, listeners_element
);
1092 atm_listener_count_incr_internal(atm_value
);
1093 lck_mtx_unlock(&atm_value
->listener_lock
);
1096 /* Free the link objects */
1097 while(!queue_empty(&free_listeners
)) {
1098 queue_remove_first(&free_listeners
, next
, atm_link_object_t
, listeners_element
);
1100 /* Deallocate the link object */
1101 atm_link_dealloc(next
);
1104 KERNEL_DEBUG_CONSTANT((ATM_CODE(ATM_SUBAID_INFO
, (ATM_LINK_LIST_TRIM
))) | DBG_FUNC_NONE
,
1105 listener_count
, freed_count
, dead_but_not_freed
, VM_KERNEL_ADDRPERM(atm_value
), 1);
1107 return KERN_SUCCESS
;
1112 * Routine: atm_listener_delete_all
1113 * Purpose: Deletes all the listeners for an atm value.
1117 atm_listener_delete_all(atm_value_t atm_value
)
1119 atm_link_object_t next
;
1121 while(!queue_empty(&atm_value
->listeners
)) {
1122 queue_remove_first(&atm_value
->listeners
, next
, atm_link_object_t
, listeners_element
);
1124 /* Deallocate the link object */
1125 atm_link_dealloc(next
);
1131 * Routine: atm_listener_delete
1132 * Purpose: Deletes a listerner for an atm value.
1133 * Returns: KERN_SUCCESS on successful unregister.
1134 * KERN_INVALID_VALUE on finding a different guard.
1135 * KERN_FAILURE on failure.
1137 static kern_return_t
1138 atm_listener_delete(
1139 atm_value_t atm_value
,
1140 atm_task_descriptor_t task_descriptor
,
1143 queue_head_t free_listeners
;
1144 atm_link_object_t next
, elem
;
1145 kern_return_t kr
= KERN_FAILURE
;
1147 queue_init(&free_listeners
);
1149 lck_mtx_lock(&atm_value
->listener_lock
);
1151 next
= (atm_link_object_t
)(void *) queue_first(&atm_value
->listeners
);
1152 while (!queue_end(&atm_value
->listeners
, (queue_entry_t
)next
)) {
1154 next
= (atm_link_object_t
)(void *) queue_next(&next
->listeners_element
);
1156 if (elem
->descriptor
== task_descriptor
) {
1157 if (elem
->guard
== guard
) {
1158 KERNEL_DEBUG_CONSTANT((ATM_CODE(ATM_UNREGISTER_INFO
,
1159 (ATM_VALUE_UNREGISTERED
))) | DBG_FUNC_NONE
,
1160 VM_KERNEL_ADDRPERM(atm_value
), atm_value
->aid
, guard
, elem
->reference_count
, 0);
1164 KERNEL_DEBUG_CONSTANT((ATM_CODE(ATM_UNREGISTER_INFO
,
1165 (ATM_VALUE_DIFF_MAILBOX
))) | DBG_FUNC_NONE
,
1166 VM_KERNEL_ADDRPERM(atm_value
), atm_value
->aid
, elem
->guard
, elem
->reference_count
, 0);
1167 kr
= KERN_INVALID_VALUE
;
1169 if (0 == atm_link_object_release_internal(elem
)) {
1170 queue_remove(&atm_value
->listeners
, elem
, atm_link_object_t
, listeners_element
);
1171 queue_enter(&free_listeners
, elem
, atm_link_object_t
, listeners_element
);
1172 atm_listener_count_decr_internal(atm_value
);
1177 lck_mtx_unlock(&atm_value
->listener_lock
);
1179 while(!queue_empty(&free_listeners
)) {
1180 queue_remove_first(&free_listeners
, next
, atm_link_object_t
, listeners_element
);
1182 /* Deallocate the link object */
1183 atm_link_dealloc(next
);
1190 * Routine: atm_descriptor_alloc_init
1191 * Purpose: Allocate an atm task descriptor and initialize it and takes a reference.
1192 * Returns: atm task descriptor: On success.
1195 static atm_task_descriptor_t
1196 atm_task_descriptor_alloc_init(
1197 mach_port_t trace_buffer
,
1198 uint64_t buffer_size
,
1199 task_t __assert_only task
)
1201 atm_task_descriptor_t new_task_descriptor
;
1203 new_task_descriptor
= (atm_task_descriptor_t
) zalloc(atm_descriptors_zone
);
1205 new_task_descriptor
->trace_buffer
= trace_buffer
;
1206 new_task_descriptor
->trace_buffer_size
= buffer_size
;
1207 new_task_descriptor
->reference_count
= 1;
1208 new_task_descriptor
->flags
= 0;
1209 lck_mtx_init(&new_task_descriptor
->lock
, &atm_lock_grp
, &atm_lock_attr
);
1211 #if DEVELOPMENT || DEBUG
1212 new_task_descriptor
->task
= task
;
1213 lck_mtx_lock(&atm_descriptors_list_lock
);
1214 queue_enter(&atm_descriptors_list
, new_task_descriptor
, atm_task_descriptor_t
, descriptor_elt
);
1215 lck_mtx_unlock(&atm_descriptors_list_lock
);
1218 return new_task_descriptor
;
1223 * Routine: atm_descriptor_get_reference
1224 * Purpose: Get a reference count on task descriptor.
1228 atm_descriptor_get_reference(atm_task_descriptor_t task_descriptor
)
1230 atm_task_desc_reference_internal(task_descriptor
);
1235 * Routine: atm_task_descriptor_dealloc
1236 * Prupose: Drops the reference on atm descriptor.
1240 atm_task_descriptor_dealloc(atm_task_descriptor_t task_descriptor
)
1242 if (0 < atm_task_desc_release_internal(task_descriptor
)) {
1246 assert(task_descriptor
->reference_count
== 0);
1248 #if DEVELOPMENT || DEBUG
1249 lck_mtx_lock(&atm_descriptors_list_lock
);
1250 queue_remove(&atm_descriptors_list
, task_descriptor
, atm_task_descriptor_t
, descriptor_elt
);
1251 lck_mtx_unlock(&atm_descriptors_list_lock
);
1253 /* release the send right for the named memory entry */
1254 ipc_port_release_send(task_descriptor
->trace_buffer
);
1255 lck_mtx_destroy(&task_descriptor
->lock
, &atm_lock_grp
);
1256 zfree(atm_descriptors_zone
, task_descriptor
);
1262 * Routine: atm_link_get_reference
1263 * Purpose: Get a reference count on atm link object.
1267 atm_link_get_reference(atm_link_object_t link_object
)
1269 atm_link_object_reference_internal(link_object
);
1274 * Routine: atm_link_dealloc
1275 * Prupose: Drops the reference on link object.
1279 atm_link_dealloc(atm_link_object_t link_object
)
1281 /* Drop the reference on atm task descriptor. */
1282 atm_task_descriptor_dealloc(link_object
->descriptor
);
1283 zfree(atm_link_objects_zone
, link_object
);
1288 * Routine: atm_register_trace_memory
1289 * Purpose: Registers trace memory for a task.
1290 * Returns: KERN_SUCCESS: on Success.
1291 * KERN_FAILURE: on Error.
1294 atm_register_trace_memory(
1296 uint64_t trace_buffer_address
,
1297 uint64_t buffer_size
)
1299 atm_task_descriptor_t task_descriptor
;
1300 mach_port_t trace_buffer
= MACH_PORT_NULL
;
1301 kern_return_t kr
= KERN_SUCCESS
;
1303 if (disable_atm
|| (atm_get_diagnostic_config() & ATM_TRACE_DISABLE
))
1304 return KERN_NOT_SUPPORTED
;
1306 if (task
!= current_task())
1307 return KERN_INVALID_ARGUMENT
;
1309 if (task
->atm_context
!= NULL
1310 || (void *)trace_buffer_address
== NULL
1312 || (buffer_size
& PAGE_MASK
) != 0
1313 || buffer_size
> MAX_TRACE_BUFFER_SIZE
) {
1314 return KERN_INVALID_ARGUMENT
;
1317 vm_map_t map
= current_map();
1318 memory_object_size_t mo_size
= (memory_object_size_t
) buffer_size
;
1319 kr
= mach_make_memory_entry_64(map
,
1321 (mach_vm_offset_t
)trace_buffer_address
,
1325 if (kr
!= KERN_SUCCESS
)
1328 task_descriptor
= atm_task_descriptor_alloc_init(trace_buffer
, buffer_size
, task
);
1329 if (task_descriptor
== ATM_TASK_DESCRIPTOR_NULL
) {
1330 ipc_port_release_send(trace_buffer
);
1331 return KERN_NO_SPACE
;
1335 if (task
->atm_context
== NULL
) {
1336 task
->atm_context
= task_descriptor
;
1343 if (kr
!= KERN_SUCCESS
) {
1344 /* undo the mapping and allocations since we failed to hook descriptor to task */
1345 atm_task_descriptor_dealloc(task_descriptor
);
1347 return KERN_SUCCESS
;
1351 * Routine: atm_set_diagnostic_config
1352 * Purpose: Set global atm_diagnostic_config and update the commpage to reflect
1354 * Returns: Error if ATM is disabled.
1356 extern uint32_t atm_diagnostic_config
; /* Proxied to commpage for fast user access */
1358 atm_set_diagnostic_config(uint32_t diagnostic_config
)
1361 return KERN_NOT_SUPPORTED
;
1363 atm_diagnostic_config
= diagnostic_config
;
1364 commpage_update_atm_diagnostic_config(atm_diagnostic_config
);
1366 return KERN_SUCCESS
;
1371 * Routine: atm_get_diagnostic_config
1372 * Purpose: Get global atm_diagnostic_config.
1373 * Returns: Diagnostic value
1376 atm_get_diagnostic_config(void)
1378 return atm_diagnostic_config
;
1383 * Routine: atm_value_unregister
1384 * Purpose: Unregisters a process from an activity id.
1385 * Returns: KERN_SUCCESS on successful unregister.
1386 * KERN_INVALID_VALUE on finding a diff guard.
1387 * KERN_FAILURE on failure.
1389 static kern_return_t
1390 atm_value_unregister(
1391 atm_value_t atm_value
,
1392 atm_task_descriptor_t task_descriptor
,
1397 if (task_descriptor
== ATM_TASK_DESCRIPTOR_NULL
)
1398 return KERN_INVALID_TASK
;
1400 kr
= atm_listener_delete(atm_value
, task_descriptor
, guard
);
1406 * Routine: atm_value_register
1407 * Purpose: Registers a process for an activity id.
1408 * Returns: KERN_SUCCESS on successful register.
1409 * KERN_INVALID_TASK on finding a null task atm context.
1410 * KERN_FAILURE on failure.
1412 static kern_return_t
1414 atm_value_t atm_value
,
1415 atm_task_descriptor_t task_descriptor
,
1420 if (task_descriptor
== ATM_TASK_DESCRIPTOR_NULL
)
1421 return KERN_INVALID_TASK
;
1423 kr
= atm_listener_insert(atm_value
, task_descriptor
, guard
);
1429 atm_task_descriptor_destroy(atm_task_descriptor_t task_descriptor
)
1431 /* Mark the task dead in the task descriptor to make task descriptor eligible for cleanup. */
1432 lck_mtx_lock(&task_descriptor
->lock
);
1433 task_descriptor
->flags
= ATM_TASK_DEAD
;
1434 lck_mtx_unlock(&task_descriptor
->lock
);
1436 atm_task_descriptor_dealloc(task_descriptor
);