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