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