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