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