2 * Copyright (c) 2012-2016 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 #include <bank/bank_internal.h>
30 #include <bank/bank_types.h>
31 #include <mach/mach_types.h>
32 #include <mach/kern_return.h>
33 #include <ipc/ipc_port.h>
34 #include <mach/mach_vm.h>
35 #include <mach/vm_map.h>
36 #include <vm/vm_map.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 <kern/ledger.h>
42 #include <kern/coalition.h>
43 #include <kern/thread_group.h>
44 #include <sys/kdebug.h>
45 #include <IOKit/IOBSD.h>
46 #include <mach/mach_voucher_attr_control.h>
47 #include <kern/policy_internal.h>
49 static zone_t bank_task_zone
, bank_account_zone
;
50 #define MAX_BANK_TASK (CONFIG_TASK_MAX)
51 #define MAX_BANK_ACCOUNT (CONFIG_TASK_MAX + CONFIG_THREAD_MAX)
53 #define BANK_ELEMENT_TO_HANDLE(x) (CAST_DOWN(bank_handle_t, (x)))
54 #define HANDLE_TO_BANK_ELEMENT(x) (CAST_DOWN(bank_element_t, (x)))
56 /* Need macro since bank_element_t is 4 byte aligned on release kernel and direct type case gives compilation error */
57 #define CAST_TO_BANK_ELEMENT(x) ((bank_element_t)((void *)(x)))
58 #define CAST_TO_BANK_TASK(x) ((bank_task_t)((void *)(x)))
59 #define CAST_TO_BANK_ACCOUNT(x) ((bank_account_t)((void *)(x)))
61 ipc_voucher_attr_control_t bank_voucher_attr_control
; /* communication channel from ATM to voucher system */
63 #if DEVELOPMENT || DEBUG
64 queue_head_t bank_tasks_list
;
65 queue_head_t bank_accounts_list
;
68 static ledger_template_t bank_ledger_template
= NULL
;
69 struct _bank_ledger_indices bank_ledgers
= { -1, -1 };
71 static bank_task_t
bank_task_alloc_init(task_t task
);
72 static bank_account_t
bank_account_alloc_init(bank_task_t bank_holder
, bank_task_t bank_merchant
,
73 bank_task_t bank_secureoriginator
, bank_task_t bank_proximateprocess
, struct thread_group
* banktg
);
74 static bank_task_t
get_bank_task_context(task_t task
, boolean_t initialize
);
75 static void bank_task_dealloc(bank_task_t bank_task
, mach_voucher_attr_value_reference_t sync
);
76 static kern_return_t
bank_account_dealloc_with_sync(bank_account_t bank_account
, mach_voucher_attr_value_reference_t sync
);
77 static void bank_rollup_chit_to_tasks(ledger_t bill
, ledger_t bank_holder_ledger
, ledger_t bank_merchant_ledger
,
78 int bank_holder_pid
, int bank_merchant_pid
);
79 static ledger_t
bank_get_bank_task_ledger_with_ref(bank_task_t bank_task
);
80 static void bank_destroy_bank_task_ledger(bank_task_t bank_task
);
81 static void init_bank_ledgers(void);
82 static boolean_t
bank_task_is_propagate_entitled(task_t t
);
83 static struct thread_group
*bank_get_bank_task_thread_group(bank_task_t bank_task __unused
);
84 static struct thread_group
*bank_get_bank_account_thread_group(bank_account_t bank_account __unused
);
86 static lck_spin_t g_bank_task_lock_data
; /* lock to protect task->bank_context transition */
88 #define global_bank_task_lock_init() \
89 lck_spin_init(&g_bank_task_lock_data, &bank_lock_grp, &bank_lock_attr)
90 #define global_bank_task_lock_destroy() \
91 lck_spin_destroy(&g_bank_task_lock_data, &bank_lock_grp)
92 #define global_bank_task_lock() \
93 lck_spin_lock_grp(&g_bank_task_lock_data, &bank_lock_grp)
94 #define global_bank_task_lock_try() \
95 lck_spin_try_lock_grp(&g_bank_task_lock_data, &bank_lock_grp)
96 #define global_bank_task_unlock() \
97 lck_spin_unlock(&g_bank_task_lock_data)
99 extern uint64_t proc_uniqueid(void *p
);
100 extern int32_t proc_pid(void *p
);
101 extern int32_t proc_pidversion(void *p
);
102 extern uint32_t proc_persona_id(void *p
);
103 extern uint32_t proc_getuid(void *p
);
104 extern uint32_t proc_getgid(void *p
);
105 extern void proc_getexecutableuuid(void *p
, unsigned char *uuidbuf
, unsigned long size
);
106 extern int kauth_cred_issuser(void *cred
);
107 extern void* kauth_cred_get(void);
112 ipc_voucher_attr_manager_t __assert_only manager
,
113 mach_voucher_attr_key_t __assert_only key
,
114 mach_voucher_attr_value_handle_t value
,
115 mach_voucher_attr_value_reference_t sync
);
119 ipc_voucher_attr_manager_t __assert_only manager
,
120 mach_voucher_attr_key_t __assert_only key
,
121 mach_voucher_attr_recipe_command_t command
,
122 mach_voucher_attr_value_handle_array_t prev_values
,
123 mach_msg_type_number_t __assert_only prev_value_count
,
124 mach_voucher_attr_content_t recipe
,
125 mach_voucher_attr_content_size_t recipe_size
,
126 mach_voucher_attr_value_handle_t
*out_value
,
127 mach_voucher_attr_value_flags_t
*out_flags
,
128 ipc_voucher_t
*out_value_voucher
);
131 bank_extract_content(
132 ipc_voucher_attr_manager_t __assert_only manager
,
133 mach_voucher_attr_key_t __assert_only key
,
134 mach_voucher_attr_value_handle_array_t values
,
135 mach_msg_type_number_t value_count
,
136 mach_voucher_attr_recipe_command_t
*out_command
,
137 mach_voucher_attr_content_t out_recipe
,
138 mach_voucher_attr_content_size_t
*in_out_recipe_size
);
142 ipc_voucher_attr_manager_t __assert_only manager
,
143 mach_voucher_attr_key_t __assert_only key
,
144 mach_voucher_attr_value_handle_array_t values
,
145 mach_msg_type_number_t value_count
,
146 mach_voucher_attr_command_t command
,
147 mach_voucher_attr_content_t in_content
,
148 mach_voucher_attr_content_size_t in_content_size
,
149 mach_voucher_attr_content_t out_content
,
150 mach_voucher_attr_content_size_t
*in_out_content_size
);
153 bank_release(ipc_voucher_attr_manager_t __assert_only manager
);
156 * communication channel from voucher system to ATM
158 struct ipc_voucher_attr_manager bank_manager
= {
159 .ivam_release_value
= bank_release_value
,
160 .ivam_get_value
= bank_get_value
,
161 .ivam_extract_content
= bank_extract_content
,
162 .ivam_command
= bank_command
,
163 .ivam_release
= bank_release
,
164 .ivam_flags
= (IVAM_FLAGS_SUPPORT_SEND_PREPROCESS
| IVAM_FLAGS_SUPPORT_RECEIVE_POSTPROCESS
),
168 #if DEVELOPMENT || DEBUG
169 decl_lck_mtx_data(, bank_tasks_list_lock
);
170 decl_lck_mtx_data(, bank_accounts_list_lock
);
172 lck_grp_t bank_dev_lock_grp
;
173 lck_attr_t bank_dev_lock_attr
;
174 lck_grp_attr_t bank_dev_lock_grp_attr
;
178 * Lock group attributes for bank sub system.
180 lck_grp_t bank_lock_grp
;
181 lck_attr_t bank_lock_attr
;
182 lck_grp_attr_t bank_lock_grp_attr
;
186 * Purpose: Initialize the BANK subsystem.
192 kern_return_t kr
= KERN_SUCCESS
;
193 /* setup zones for bank_task and bank_account objects */
194 bank_task_zone
= zinit(sizeof(struct bank_task
),
195 MAX_BANK_TASK
* sizeof(struct bank_task
),
196 sizeof(struct bank_task
),
199 bank_account_zone
= zinit(sizeof(struct bank_account
),
200 MAX_BANK_ACCOUNT
* sizeof(struct bank_account
),
201 sizeof(struct bank_account
),
206 /* Initialize bank lock group and lock attributes. */
207 lck_grp_attr_setdefault(&bank_lock_grp_attr
);
208 lck_grp_init(&bank_lock_grp
, "bank_lock", &bank_lock_grp_attr
);
209 lck_attr_setdefault(&bank_lock_attr
);
210 global_bank_task_lock_init();
212 #if DEVELOPMENT || DEBUG
213 /* Initialize global bank development lock group and lock attributes. */
214 lck_grp_attr_setdefault(&bank_dev_lock_grp_attr
);
215 lck_grp_init(&bank_dev_lock_grp
, "bank_dev_lock", &bank_dev_lock_grp_attr
);
216 lck_attr_setdefault(&bank_dev_lock_attr
);
218 lck_mtx_init(&bank_tasks_list_lock
, &bank_dev_lock_grp
, &bank_dev_lock_attr
);
219 lck_mtx_init(&bank_accounts_list_lock
, &bank_dev_lock_grp
, &bank_dev_lock_attr
);
221 queue_init(&bank_tasks_list
);
222 queue_init(&bank_accounts_list
);
225 /* Register the bank manager with the Vouchers sub system. */
226 kr
= ipc_register_well_known_mach_voucher_attr_manager(
229 MACH_VOUCHER_ATTR_KEY_BANK
,
230 &bank_voucher_attr_control
);
231 if (kr
!= KERN_SUCCESS
) {
232 panic("BANK subsystem initialization failed");
235 kprintf("BANK subsystem is initialized\n");
241 * BANK Resource Manager Routines.
246 * Routine: bank_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.
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
)
258 bank_task_t bank_task
= BANK_TASK_NULL
;
259 bank_element_t bank_element
= BANK_ELEMENT_NULL
;
260 bank_account_t bank_account
= BANK_ACCOUNT_NULL
;
261 kern_return_t kr
= KERN_SUCCESS
;
263 assert(MACH_VOUCHER_ATTR_KEY_BANK
== key
);
264 assert(manager
== &bank_manager
);
267 bank_element
= HANDLE_TO_BANK_ELEMENT(value
);
268 /* Voucher system should never release the default or persistent value */
269 assert(bank_element
!= BANK_DEFAULT_VALUE
&& bank_element
!= BANK_DEFAULT_TASK_VALUE
);
271 if (bank_element
== BANK_DEFAULT_VALUE
|| bank_element
== BANK_DEFAULT_TASK_VALUE
) {
272 /* Return success for default and default task value */
277 if (bank_element
->be_type
== BANK_TASK
) {
278 bank_task
= CAST_TO_BANK_TASK(bank_element
);
280 /* Checking of the made ref with sync and clearing of voucher ref should be done under a lock */
281 lck_mtx_lock(&bank_task
->bt_acc_to_pay_lock
);
282 if (bank_task
->bt_made
!= sync
) {
283 lck_mtx_unlock(&bank_task
->bt_acc_to_pay_lock
);
287 bank_task_made_release_num(bank_task
, sync
);
288 assert(bank_task
->bt_voucher_ref
== 1);
289 bank_task
->bt_voucher_ref
= 0;
290 lck_mtx_unlock(&bank_task
->bt_acc_to_pay_lock
);
292 bank_task_dealloc(bank_task
, 1);
293 } else if (bank_element
->be_type
== BANK_ACCOUNT
) {
294 bank_account
= CAST_TO_BANK_ACCOUNT(bank_element
);
295 kr
= bank_account_dealloc_with_sync(bank_account
, sync
);
297 panic("Bogus bank type: %d passed in get_value\n", bank_element
->be_type
);
305 * Routine: bank_get_value
309 ipc_voucher_attr_manager_t __assert_only manager
,
310 mach_voucher_attr_key_t __assert_only key
,
311 mach_voucher_attr_recipe_command_t command
,
312 mach_voucher_attr_value_handle_array_t prev_values
,
313 mach_msg_type_number_t prev_value_count
,
314 mach_voucher_attr_content_t __unused recipe
,
315 mach_voucher_attr_content_size_t __unused recipe_size
,
316 mach_voucher_attr_value_handle_t
*out_value
,
317 mach_voucher_attr_value_flags_t
*out_flags
,
318 ipc_voucher_t
*out_value_voucher
)
320 bank_task_t bank_task
= BANK_TASK_NULL
;
321 bank_task_t bank_holder
= BANK_TASK_NULL
;
322 bank_task_t bank_merchant
= BANK_TASK_NULL
;
323 bank_task_t bank_secureoriginator
= BANK_TASK_NULL
;
324 bank_task_t bank_proximateprocess
= BANK_TASK_NULL
;
325 bank_element_t bank_element
= BANK_ELEMENT_NULL
;
326 bank_account_t bank_account
= BANK_ACCOUNT_NULL
;
327 bank_account_t old_bank_account
= BANK_ACCOUNT_NULL
;
328 mach_voucher_attr_value_handle_t bank_handle
;
330 kern_return_t kr
= KERN_SUCCESS
;
331 mach_msg_type_number_t i
;
332 struct thread_group
*thread_group
= NULL
;
333 struct thread_group
*cur_thread_group
= NULL
;
335 assert(MACH_VOUCHER_ATTR_KEY_BANK
== key
);
336 assert(manager
== &bank_manager
);
338 /* never an out voucher */
339 *out_value_voucher
= IPC_VOUCHER_NULL
;
340 *out_flags
= MACH_VOUCHER_ATTR_VALUE_FLAGS_NONE
;
343 case MACH_VOUCHER_ATTR_BANK_CREATE
:
345 /* Return the default task value instead of bank task */
346 *out_value
= BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_TASK_VALUE
);
347 *out_flags
= MACH_VOUCHER_ATTR_VALUE_FLAGS_PERSIST
;
350 case MACH_VOUCHER_ATTR_AUTO_REDEEM
:
352 for (i
= 0; i
< prev_value_count
; i
++) {
353 bank_handle
= prev_values
[i
];
354 bank_element
= HANDLE_TO_BANK_ELEMENT(bank_handle
);
356 /* Should not have received default task value from an IPC */
357 if (bank_element
== BANK_DEFAULT_VALUE
|| bank_element
== BANK_DEFAULT_TASK_VALUE
) {
361 task
= current_task();
362 if (bank_element
->be_type
== BANK_TASK
) {
363 bank_holder
= CAST_TO_BANK_TASK(bank_element
);
364 bank_secureoriginator
= bank_holder
;
365 bank_proximateprocess
= bank_holder
;
366 thread_group
= bank_get_bank_task_thread_group(bank_holder
);
367 } else if (bank_element
->be_type
== BANK_ACCOUNT
) {
368 old_bank_account
= CAST_TO_BANK_ACCOUNT(bank_element
);
369 bank_holder
= old_bank_account
->ba_holder
;
370 bank_secureoriginator
= old_bank_account
->ba_secureoriginator
;
371 bank_proximateprocess
= old_bank_account
->ba_proximateprocess
;
372 thread_group
= bank_get_bank_account_thread_group(old_bank_account
);
374 panic("Bogus bank type: %d passed in get_value\n", bank_element
->be_type
);
377 bank_merchant
= get_bank_task_context(task
, FALSE
);
378 if (bank_merchant
== BANK_TASK_NULL
) {
379 return KERN_RESOURCE_SHORTAGE
;
382 cur_thread_group
= bank_get_bank_task_thread_group(bank_merchant
);
384 /* Change voucher thread group to current thread group for Apps */
385 if (task_is_app(task
)) {
386 thread_group
= cur_thread_group
;
389 /* Check if trying to redeem for self task, return the default bank task */
390 if (bank_holder
== bank_merchant
&&
391 bank_holder
== bank_secureoriginator
&&
392 bank_holder
== bank_proximateprocess
&&
393 thread_group
== cur_thread_group
) {
394 *out_value
= BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_TASK_VALUE
);
395 *out_flags
= MACH_VOUCHER_ATTR_VALUE_FLAGS_PERSIST
;
399 bank_account
= bank_account_alloc_init(bank_holder
, bank_merchant
,
400 bank_secureoriginator
, bank_proximateprocess
,
402 if (bank_account
== BANK_ACCOUNT_NULL
) {
403 return KERN_RESOURCE_SHORTAGE
;
406 *out_value
= BANK_ELEMENT_TO_HANDLE(bank_account
);
410 *out_value
= BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_VALUE
);
413 case MACH_VOUCHER_ATTR_SEND_PREPROCESS
:
415 for (i
= 0; i
< prev_value_count
; i
++) {
416 bank_handle
= prev_values
[i
];
417 bank_element
= HANDLE_TO_BANK_ELEMENT(bank_handle
);
419 if (bank_element
== BANK_DEFAULT_VALUE
) {
423 task
= current_task();
424 if (bank_element
== BANK_DEFAULT_TASK_VALUE
) {
425 bank_element
= CAST_TO_BANK_ELEMENT(get_bank_task_context(task
, FALSE
));
428 if (bank_element
->be_type
== BANK_TASK
) {
429 bank_holder
= CAST_TO_BANK_TASK(bank_element
);
430 bank_secureoriginator
= bank_holder
;
431 thread_group
= bank_get_bank_task_thread_group(bank_holder
);
432 } else if (bank_element
->be_type
== BANK_ACCOUNT
) {
433 old_bank_account
= CAST_TO_BANK_ACCOUNT(bank_element
);
434 bank_holder
= old_bank_account
->ba_holder
;
435 bank_secureoriginator
= old_bank_account
->ba_secureoriginator
;
436 thread_group
= bank_get_bank_account_thread_group(old_bank_account
);
438 panic("Bogus bank type: %d passed in get_value\n", bank_element
->be_type
);
441 bank_merchant
= get_bank_task_context(task
, FALSE
);
442 if (bank_merchant
== BANK_TASK_NULL
) {
443 return KERN_RESOURCE_SHORTAGE
;
446 cur_thread_group
= bank_get_bank_task_thread_group(bank_merchant
);
449 * If the process doesn't have secure persona entitlement,
450 * then replace the secure originator to current task.
452 if (bank_merchant
->bt_hasentitlement
== 0) {
453 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
,
454 (BANK_CODE(BANK_ACCOUNT_INFO
, (BANK_SECURE_ORIGINATOR_CHANGED
))) | DBG_FUNC_NONE
,
455 bank_secureoriginator
->bt_pid
, bank_merchant
->bt_pid
, 0, 0, 0);
456 bank_secureoriginator
= bank_merchant
;
459 bank_proximateprocess
= bank_merchant
;
461 /* Check if trying to redeem for self task, return the bank task */
462 if (bank_holder
== bank_merchant
&&
463 bank_holder
== bank_secureoriginator
&&
464 bank_holder
== bank_proximateprocess
&&
465 thread_group
== cur_thread_group
) {
466 lck_mtx_lock(&bank_holder
->bt_acc_to_pay_lock
);
467 bank_task_made_reference(bank_holder
);
468 if (bank_holder
->bt_voucher_ref
== 0) {
469 /* Take a ref for voucher system, if voucher system does not have a ref */
470 bank_task_reference(bank_holder
);
471 bank_holder
->bt_voucher_ref
= 1;
473 lck_mtx_unlock(&bank_holder
->bt_acc_to_pay_lock
);
475 *out_value
= BANK_ELEMENT_TO_HANDLE(bank_holder
);
478 bank_account
= bank_account_alloc_init(bank_holder
, bank_merchant
,
479 bank_secureoriginator
, bank_proximateprocess
,
481 if (bank_account
== BANK_ACCOUNT_NULL
) {
482 return KERN_RESOURCE_SHORTAGE
;
485 *out_value
= BANK_ELEMENT_TO_HANDLE(bank_account
);
489 *out_value
= BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_VALUE
);
492 case MACH_VOUCHER_ATTR_REDEEM
:
494 for (i
= 0; i
< prev_value_count
; i
++) {
495 bank_handle
= prev_values
[i
];
496 bank_element
= HANDLE_TO_BANK_ELEMENT(bank_handle
);
498 if (bank_element
== BANK_DEFAULT_VALUE
) {
502 task
= current_task();
503 if (bank_element
== BANK_DEFAULT_TASK_VALUE
) {
504 *out_value
= BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_TASK_VALUE
);
505 *out_flags
= MACH_VOUCHER_ATTR_VALUE_FLAGS_PERSIST
;
508 if (bank_element
->be_type
== BANK_TASK
) {
509 bank_task
= CAST_TO_BANK_TASK(bank_element
);
510 panic("Found a bank task in MACH_VOUCHER_ATTR_REDEEM: %p", bank_task
);
513 } else if (bank_element
->be_type
== BANK_ACCOUNT
) {
514 bank_account
= CAST_TO_BANK_ACCOUNT(bank_element
);
515 bank_merchant
= bank_account
->ba_merchant
;
516 if (bank_merchant
!= get_bank_task_context(task
, FALSE
)) {
517 panic("Found another bank task: %p as a bank merchant\n", bank_merchant
);
520 bank_account_made_reference(bank_account
);
521 *out_value
= BANK_ELEMENT_TO_HANDLE(bank_account
);
524 panic("Bogus bank type: %d passed in get_value\n", bank_element
->be_type
);
528 *out_value
= BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_VALUE
);
532 kr
= KERN_INVALID_ARGUMENT
;
541 * Routine: bank_extract_content
542 * Purpose: Extract a set of aid from an array of voucher values.
543 * Returns: KERN_SUCCESS: on Success.
544 * KERN_FAILURE: one of the value is not present in the hash.
545 * KERN_NO_SPACE: insufficeint buffer provided to fill an array of aid.
548 bank_extract_content(
549 ipc_voucher_attr_manager_t __assert_only manager
,
550 mach_voucher_attr_key_t __assert_only key
,
551 mach_voucher_attr_value_handle_array_t values
,
552 mach_msg_type_number_t value_count
,
553 mach_voucher_attr_recipe_command_t
*out_command
,
554 mach_voucher_attr_content_t out_recipe
,
555 mach_voucher_attr_content_size_t
*in_out_recipe_size
)
557 bank_task_t bank_task
= BANK_TASK_NULL
;
558 bank_element_t bank_element
= BANK_ELEMENT_NULL
;
559 bank_account_t bank_account
= BANK_ACCOUNT_NULL
;
560 mach_voucher_attr_value_handle_t bank_handle
;
561 char buf
[MACH_VOUCHER_BANK_CONTENT_SIZE
];
562 mach_msg_type_number_t i
;
564 assert(MACH_VOUCHER_ATTR_KEY_BANK
== key
);
565 assert(manager
== &bank_manager
);
567 for (i
= 0; i
< value_count
&& *in_out_recipe_size
> 0; i
++) {
568 bank_handle
= values
[i
];
569 bank_element
= HANDLE_TO_BANK_ELEMENT(bank_handle
);
570 if (bank_element
== BANK_DEFAULT_VALUE
) {
574 if (bank_element
== BANK_DEFAULT_TASK_VALUE
) {
575 bank_element
= CAST_TO_BANK_ELEMENT(get_bank_task_context(current_task(), FALSE
));
578 if (MACH_VOUCHER_BANK_CONTENT_SIZE
> *in_out_recipe_size
) {
579 *in_out_recipe_size
= 0;
580 return KERN_NO_SPACE
;
583 if (bank_element
->be_type
== BANK_TASK
) {
584 bank_task
= CAST_TO_BANK_TASK(bank_element
);
585 snprintf(buf
, MACH_VOUCHER_BANK_CONTENT_SIZE
,
586 " Bank Context for a pid %d\n", bank_task
->bt_pid
);
587 } else if (bank_element
->be_type
== BANK_ACCOUNT
) {
588 bank_account
= CAST_TO_BANK_ACCOUNT(bank_element
);
589 snprintf(buf
, MACH_VOUCHER_BANK_CONTENT_SIZE
,
590 " Bank Account linking holder pid %d with merchant pid %d, originator PID/persona: %d, %u and proximate PID/persona: %d, %u\n",
591 bank_account
->ba_holder
->bt_pid
,
592 bank_account
->ba_merchant
->bt_pid
,
593 bank_account
->ba_secureoriginator
->bt_pid
,
594 bank_account
->ba_secureoriginator
->bt_persona_id
,
595 bank_account
->ba_proximateprocess
->bt_pid
,
596 bank_account
->ba_proximateprocess
->bt_persona_id
);
598 panic("Bogus bank type: %d passed in get_value\n", bank_element
->be_type
);
602 memcpy(&out_recipe
[0], buf
, strlen(buf
) + 1);
603 *out_command
= MACH_VOUCHER_ATTR_BANK_NULL
;
604 *in_out_recipe_size
= (mach_voucher_attr_content_size_t
)strlen(buf
) + 1;
612 * Routine: bank_command
613 * Purpose: Execute a command against a set of ATM values.
614 * Returns: KERN_SUCCESS: On successful execution of command.
615 * KERN_FAILURE: On failure.
619 ipc_voucher_attr_manager_t __assert_only manager
,
620 mach_voucher_attr_key_t __assert_only key
,
621 mach_voucher_attr_value_handle_array_t __unused values
,
622 mach_msg_type_number_t __unused value_count
,
623 mach_voucher_attr_command_t __unused command
,
624 mach_voucher_attr_content_t __unused in_content
,
625 mach_voucher_attr_content_size_t __unused in_content_size
,
626 mach_voucher_attr_content_t __unused out_content
,
627 mach_voucher_attr_content_size_t __unused
*out_content_size
)
629 bank_task_t bank_task
= BANK_TASK_NULL
;
630 bank_task_t bank_secureoriginator
= BANK_TASK_NULL
;
631 bank_task_t bank_proximateprocess
= BANK_TASK_NULL
;
632 struct persona_token
*token
= NULL
;
633 bank_element_t bank_element
= BANK_ELEMENT_NULL
;
634 bank_account_t bank_account
= BANK_ACCOUNT_NULL
;
635 mach_voucher_attr_value_handle_t bank_handle
;
636 mach_msg_type_number_t i
;
639 assert(MACH_VOUCHER_ATTR_KEY_BANK
== key
);
640 assert(manager
== &bank_manager
);
643 case BANK_ORIGINATOR_PID
:
645 if ((sizeof(pid
)) > *out_content_size
) {
646 *out_content_size
= 0;
647 return KERN_NO_SPACE
;
650 for (i
= 0; i
< value_count
; i
++) {
651 bank_handle
= values
[i
];
652 bank_element
= HANDLE_TO_BANK_ELEMENT(bank_handle
);
653 if (bank_element
== BANK_DEFAULT_VALUE
) {
657 if (bank_element
== BANK_DEFAULT_TASK_VALUE
) {
658 bank_element
= CAST_TO_BANK_ELEMENT(get_bank_task_context(current_task(), FALSE
));
661 if (bank_element
->be_type
== BANK_TASK
) {
662 bank_task
= CAST_TO_BANK_TASK(bank_element
);
663 } else if (bank_element
->be_type
== BANK_ACCOUNT
) {
664 bank_account
= CAST_TO_BANK_ACCOUNT(bank_element
);
665 bank_task
= bank_account
->ba_holder
;
667 panic("Bogus bank type: %d passed in voucher_command\n", bank_element
->be_type
);
669 pid
= bank_task
->bt_pid
;
671 memcpy(&out_content
[0], &pid
, sizeof(pid
));
672 *out_content_size
= (mach_voucher_attr_content_size_t
)sizeof(pid
);
675 /* In the case of no value, return error KERN_INVALID_VALUE */
676 *out_content_size
= 0;
677 return KERN_INVALID_VALUE
;
679 case BANK_PERSONA_TOKEN
:
681 if ((sizeof(struct persona_token
)) > *out_content_size
) {
682 *out_content_size
= 0;
683 return KERN_NO_SPACE
;
685 for (i
= 0; i
< value_count
; i
++) {
686 bank_handle
= values
[i
];
687 bank_element
= HANDLE_TO_BANK_ELEMENT(bank_handle
);
688 if (bank_element
== BANK_DEFAULT_VALUE
) {
692 if (bank_element
== BANK_DEFAULT_TASK_VALUE
) {
693 bank_element
= CAST_TO_BANK_ELEMENT(get_bank_task_context(current_task(), FALSE
));
696 if (bank_element
->be_type
== BANK_TASK
) {
697 *out_content_size
= 0;
698 return KERN_INVALID_OBJECT
;
699 } else if (bank_element
->be_type
== BANK_ACCOUNT
) {
700 bank_account
= CAST_TO_BANK_ACCOUNT(bank_element
);
701 bank_secureoriginator
= bank_account
->ba_secureoriginator
;
702 bank_proximateprocess
= bank_account
->ba_proximateprocess
;
704 panic("Bogus bank type: %d passed in voucher_command\n", bank_element
->be_type
);
706 token
= (struct persona_token
*)(void *)&out_content
[0];
707 memcpy(&token
->originator
, &bank_secureoriginator
->bt_proc_persona
, sizeof(struct proc_persona_info
));
708 memcpy(&token
->proximate
, &bank_proximateprocess
->bt_proc_persona
, sizeof(struct proc_persona_info
));
710 *out_content_size
= (mach_voucher_attr_content_size_t
)sizeof(*token
);
713 /* In the case of no value, return error KERN_INVALID_VALUE */
714 *out_content_size
= 0;
715 return KERN_INVALID_VALUE
;
718 return KERN_INVALID_ARGUMENT
;
726 ipc_voucher_attr_manager_t __assert_only manager
)
728 assert(manager
== &bank_manager
);
734 * Bank Internal Routines.
738 * Routine: bank_task_alloc_init
739 * Purpose: Allocate and initialize a bank task structure.
740 * Returns: bank_task_t on Success.
741 * BANK_TASK_NULL: on Failure.
742 * Notes: Leaves the task and ledger blank and has only 1 ref,
743 * needs to take 1 extra ref after the task field is initialized.
746 bank_task_alloc_init(task_t task
)
748 bank_task_t new_bank_task
;
750 new_bank_task
= (bank_task_t
) zalloc(bank_task_zone
);
751 if (new_bank_task
== BANK_TASK_NULL
) {
752 return BANK_TASK_NULL
;
755 new_bank_task
->bt_type
= BANK_TASK
;
756 new_bank_task
->bt_voucher_ref
= 0;
757 new_bank_task
->bt_refs
= 1;
758 new_bank_task
->bt_made
= 0;
759 new_bank_task
->bt_ledger
= LEDGER_NULL
;
760 new_bank_task
->bt_hasentitlement
= bank_task_is_propagate_entitled(task
);
761 queue_init(&new_bank_task
->bt_accounts_to_pay
);
762 queue_init(&new_bank_task
->bt_accounts_to_charge
);
763 lck_mtx_init(&new_bank_task
->bt_acc_to_pay_lock
, &bank_lock_grp
, &bank_lock_attr
);
764 lck_mtx_init(&new_bank_task
->bt_acc_to_charge_lock
, &bank_lock_grp
, &bank_lock_attr
);
767 * Initialize the persona_id struct
769 bzero(&new_bank_task
->bt_proc_persona
, sizeof(new_bank_task
->bt_proc_persona
));
770 new_bank_task
->bt_flags
= 0;
771 new_bank_task
->bt_unique_pid
= proc_uniqueid(task
->bsd_info
);
772 new_bank_task
->bt_pid
= proc_pid(task
->bsd_info
);
773 new_bank_task
->bt_pidversion
= proc_pidversion(task
->bsd_info
);
774 new_bank_task
->bt_persona_id
= proc_persona_id(task
->bsd_info
);
775 new_bank_task
->bt_uid
= proc_getuid(task
->bsd_info
);
776 new_bank_task
->bt_gid
= proc_getgid(task
->bsd_info
);
777 proc_getexecutableuuid(task
->bsd_info
, new_bank_task
->bt_macho_uuid
, sizeof(new_bank_task
->bt_macho_uuid
));
779 #if DEVELOPMENT || DEBUG
780 new_bank_task
->bt_task
= NULL
;
781 lck_mtx_lock(&bank_tasks_list_lock
);
782 queue_enter(&bank_tasks_list
, new_bank_task
, bank_task_t
, bt_global_elt
);
783 lck_mtx_unlock(&bank_tasks_list_lock
);
785 return new_bank_task
;
789 * Routine: proc_is_propagate_entitled
790 * Purpose: Check if the process has persona propagate entitlement.
791 * Returns: TRUE if entitled.
795 bank_task_is_propagate_entitled(task_t t
)
797 /* Return TRUE if root process */
798 if (0 == kauth_cred_issuser(kauth_cred_get())) {
799 /* If it's a non-root process, it needs to have the entitlement for secure originator propagation */
800 boolean_t entitled
= FALSE
;
801 entitled
= IOTaskHasEntitlement(t
, ENTITLEMENT_PERSONA_PROPAGATE
);
809 * Routine: bank_account_alloc_init
810 * Purpose: Allocate and Initialize the bank account struct.
811 * Returns: bank_account_t : On Success.
812 * BANK_ACCOUNT_NULL: On Failure.
814 static bank_account_t
815 bank_account_alloc_init(
816 bank_task_t bank_holder
,
817 bank_task_t bank_merchant
,
818 bank_task_t bank_secureoriginator
,
819 bank_task_t bank_proximateprocess
,
820 struct thread_group
*thread_group
)
822 bank_account_t new_bank_account
;
823 bank_account_t bank_account
;
824 boolean_t entry_found
= FALSE
;
825 ledger_t new_ledger
= ledger_instantiate(bank_ledger_template
, LEDGER_CREATE_INACTIVE_ENTRIES
);
827 if (new_ledger
== LEDGER_NULL
) {
828 return BANK_ACCOUNT_NULL
;
831 ledger_entry_setactive(new_ledger
, bank_ledgers
.cpu_time
);
832 ledger_entry_setactive(new_ledger
, bank_ledgers
.energy
);
833 new_bank_account
= (bank_account_t
) zalloc(bank_account_zone
);
834 if (new_bank_account
== BANK_ACCOUNT_NULL
) {
835 ledger_dereference(new_ledger
);
836 return BANK_ACCOUNT_NULL
;
839 new_bank_account
->ba_type
= BANK_ACCOUNT
;
840 new_bank_account
->ba_voucher_ref
= 0;
841 new_bank_account
->ba_refs
= 1;
842 new_bank_account
->ba_made
= 1;
843 new_bank_account
->ba_bill
= new_ledger
;
844 new_bank_account
->ba_merchant
= bank_merchant
;
845 new_bank_account
->ba_holder
= bank_holder
;
846 new_bank_account
->ba_secureoriginator
= bank_secureoriginator
;
847 new_bank_account
->ba_proximateprocess
= bank_proximateprocess
;
849 /* Iterate through accounts need to pay list to find the existing entry */
850 lck_mtx_lock(&bank_holder
->bt_acc_to_pay_lock
);
851 queue_iterate(&bank_holder
->bt_accounts_to_pay
, bank_account
, bank_account_t
, ba_next_acc_to_pay
) {
852 if (bank_account
->ba_merchant
!= bank_merchant
||
853 bank_account
->ba_secureoriginator
!= bank_secureoriginator
||
854 bank_account
->ba_proximateprocess
!= bank_proximateprocess
||
855 bank_get_bank_account_thread_group(bank_account
) != thread_group
) {
860 /* Take a made ref, since this value would be returned to voucher system. */
861 bank_account_made_reference(bank_account
);
866 /* Create a linkage between the holder and the merchant task, Grab both the list locks before adding it to the list. */
867 lck_mtx_lock(&bank_merchant
->bt_acc_to_charge_lock
);
869 /* Add the account entry into Accounts need to pay account link list. */
870 queue_enter(&bank_holder
->bt_accounts_to_pay
, new_bank_account
, bank_account_t
, ba_next_acc_to_pay
);
872 /* Add the account entry into Accounts need to charge account link list. */
873 queue_enter(&bank_merchant
->bt_accounts_to_charge
, new_bank_account
, bank_account_t
, ba_next_acc_to_charge
);
875 lck_mtx_unlock(&bank_merchant
->bt_acc_to_charge_lock
);
878 lck_mtx_unlock(&bank_holder
->bt_acc_to_pay_lock
);
881 ledger_dereference(new_ledger
);
882 zfree(bank_account_zone
, new_bank_account
);
886 bank_task_reference(bank_holder
);
887 bank_task_reference(bank_merchant
);
888 bank_task_reference(bank_secureoriginator
);
889 bank_task_reference(bank_proximateprocess
);
891 #if DEVELOPMENT || DEBUG
892 new_bank_account
->ba_task
= NULL
;
893 lck_mtx_lock(&bank_accounts_list_lock
);
894 queue_enter(&bank_accounts_list
, new_bank_account
, bank_account_t
, ba_global_elt
);
895 lck_mtx_unlock(&bank_accounts_list_lock
);
898 return new_bank_account
;
902 * Routine: get_bank_task_context
903 * Purpose: Get the bank context of the given task
904 * Returns: bank_task_t on Success.
905 * BANK_TASK_NULL: on Failure.
906 * Note: Initialize bank context if NULL.
909 get_bank_task_context
911 boolean_t initialize
)
913 bank_task_t bank_task
;
915 if (task
->bank_context
|| !initialize
) {
916 assert(task
->bank_context
!= NULL
);
917 return task
->bank_context
;
920 bank_task
= bank_task_alloc_init(task
);
922 /* Grab the task lock and check if we won the race. */
924 if (task
->bank_context
) {
926 if (bank_task
!= BANK_TASK_NULL
) {
927 bank_task_dealloc(bank_task
, 1);
929 return task
->bank_context
;
930 } else if (bank_task
== BANK_TASK_NULL
) {
932 return BANK_TASK_NULL
;
934 /* We won the race. Take a ref on the ledger and initialize bank task. */
935 bank_task
->bt_ledger
= task
->ledger
;
936 #if DEVELOPMENT || DEBUG
937 bank_task
->bt_task
= task
;
939 ledger_reference(task
->ledger
);
941 /* Grab the global bank task lock before setting the bank context on a task */
942 global_bank_task_lock();
943 task
->bank_context
= bank_task
;
944 global_bank_task_unlock();
952 * Routine: bank_task_dealloc
953 * Purpose: Drops the reference on bank task.
958 bank_task_t bank_task
,
959 mach_voucher_attr_value_reference_t sync
)
961 assert(bank_task
->bt_refs
>= 0);
963 if (bank_task_release_num(bank_task
, sync
) > (int)sync
) {
967 assert(bank_task
->bt_refs
== 0);
968 assert(queue_empty(&bank_task
->bt_accounts_to_pay
));
969 assert(queue_empty(&bank_task
->bt_accounts_to_charge
));
971 assert(!LEDGER_VALID(bank_task
->bt_ledger
));
972 lck_mtx_destroy(&bank_task
->bt_acc_to_pay_lock
, &bank_lock_grp
);
973 lck_mtx_destroy(&bank_task
->bt_acc_to_charge_lock
, &bank_lock_grp
);
976 #if DEVELOPMENT || DEBUG
977 lck_mtx_lock(&bank_tasks_list_lock
);
978 queue_remove(&bank_tasks_list
, bank_task
, bank_task_t
, bt_global_elt
);
979 lck_mtx_unlock(&bank_tasks_list_lock
);
982 zfree(bank_task_zone
, bank_task
);
986 * Routine: bank_account_dealloc_with_sync
987 * Purpose: Drop the reference on bank account if the sync matches.
988 * Returns: KERN_SUCCESS if sync matches.
989 * KERN_FAILURE on mismatch.
992 bank_account_dealloc_with_sync(
993 bank_account_t bank_account
,
994 mach_voucher_attr_value_reference_t sync
)
996 bank_task_t bank_holder
= bank_account
->ba_holder
;
997 bank_task_t bank_merchant
= bank_account
->ba_merchant
;
998 bank_task_t bank_secureoriginator
= bank_account
->ba_secureoriginator
;
999 bank_task_t bank_proximateprocess
= bank_account
->ba_proximateprocess
;
1000 ledger_t bank_merchant_ledger
= LEDGER_NULL
;
1003 * Grab a reference on the bank_merchant_ledger, since we would not be able
1004 * to take bt_acc_to_pay_lock for bank_merchant later.
1006 bank_merchant_ledger
= bank_get_bank_task_ledger_with_ref(bank_merchant
);
1008 /* Grab the acc to pay list lock and check the sync value */
1009 lck_mtx_lock(&bank_holder
->bt_acc_to_pay_lock
);
1011 if (bank_account
->ba_made
!= sync
) {
1012 lck_mtx_unlock(&bank_holder
->bt_acc_to_pay_lock
);
1013 if (bank_merchant_ledger
) {
1014 ledger_dereference(bank_merchant_ledger
);
1016 return KERN_FAILURE
;
1019 bank_account_made_release_num(bank_account
, sync
);
1021 if (bank_account_release_num(bank_account
, 1) > 1) {
1022 panic("Releasing a non zero ref bank account %p\n", bank_account
);
1026 /* Grab both the acc to pay and acc to charge locks */
1027 lck_mtx_lock(&bank_merchant
->bt_acc_to_charge_lock
);
1029 /* No need to take ledger reference for bank_holder ledger since bt_acc_to_pay_lock is locked */
1030 bank_rollup_chit_to_tasks(bank_account
->ba_bill
, bank_holder
->bt_ledger
, bank_merchant_ledger
,
1031 bank_holder
->bt_pid
, bank_merchant
->bt_pid
);
1033 /* Remove the account entry from Accounts need to pay account link list. */
1034 queue_remove(&bank_holder
->bt_accounts_to_pay
, bank_account
, bank_account_t
, ba_next_acc_to_pay
);
1036 /* Remove the account entry from Accounts need to charge account link list. */
1037 queue_remove(&bank_merchant
->bt_accounts_to_charge
, bank_account
, bank_account_t
, ba_next_acc_to_charge
);
1039 lck_mtx_unlock(&bank_merchant
->bt_acc_to_charge_lock
);
1040 lck_mtx_unlock(&bank_holder
->bt_acc_to_pay_lock
);
1042 if (bank_merchant_ledger
) {
1043 ledger_dereference(bank_merchant_ledger
);
1045 ledger_dereference(bank_account
->ba_bill
);
1047 /* Drop the reference of bank holder and merchant */
1048 bank_task_dealloc(bank_holder
, 1);
1049 bank_task_dealloc(bank_merchant
, 1);
1050 bank_task_dealloc(bank_secureoriginator
, 1);
1051 bank_task_dealloc(bank_proximateprocess
, 1);
1053 #if DEVELOPMENT || DEBUG
1054 lck_mtx_lock(&bank_accounts_list_lock
);
1055 queue_remove(&bank_accounts_list
, bank_account
, bank_account_t
, ba_global_elt
);
1056 lck_mtx_unlock(&bank_accounts_list_lock
);
1059 zfree(bank_account_zone
, bank_account
);
1060 return KERN_SUCCESS
;
1064 * Routine: bank_rollup_chit_to_tasks
1065 * Purpose: Debit and Credit holder's and merchant's ledgers.
1069 bank_rollup_chit_to_tasks(
1071 ledger_t bank_holder_ledger
,
1072 ledger_t bank_merchant_ledger
,
1073 int bank_holder_pid
,
1074 int bank_merchant_pid
)
1076 ledger_amount_t credit
;
1077 ledger_amount_t debit
;
1080 if (bank_holder_ledger
== bank_merchant_ledger
) {
1084 ret
= ledger_get_entries(bill
, bank_ledgers
.cpu_time
, &credit
, &debit
);
1085 if (ret
== KERN_SUCCESS
) {
1086 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
,
1087 (BANK_CODE(BANK_ACCOUNT_INFO
, (BANK_SETTLE_CPU_TIME
))) | DBG_FUNC_NONE
,
1088 bank_merchant_pid
, bank_holder_pid
, credit
, debit
, 0);
1090 if (bank_holder_ledger
) {
1091 ledger_credit(bank_holder_ledger
, task_ledgers
.cpu_time_billed_to_me
, credit
);
1092 ledger_debit(bank_holder_ledger
, task_ledgers
.cpu_time_billed_to_me
, debit
);
1095 if (bank_merchant_ledger
) {
1096 ledger_credit(bank_merchant_ledger
, task_ledgers
.cpu_time_billed_to_others
, credit
);
1097 ledger_debit(bank_merchant_ledger
, task_ledgers
.cpu_time_billed_to_others
, debit
);
1101 ret
= ledger_get_entries(bill
, bank_ledgers
.energy
, &credit
, &debit
);
1102 if (ret
== KERN_SUCCESS
) {
1103 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
,
1104 (BANK_CODE(BANK_ACCOUNT_INFO
, (BANK_SETTLE_ENERGY
))) | DBG_FUNC_NONE
,
1105 bank_merchant_pid
, bank_holder_pid
, credit
, debit
, 0);
1107 if (bank_holder_ledger
) {
1108 ledger_credit(bank_holder_ledger
, task_ledgers
.energy_billed_to_me
, credit
);
1109 ledger_debit(bank_holder_ledger
, task_ledgers
.energy_billed_to_me
, debit
);
1112 if (bank_merchant_ledger
) {
1113 ledger_credit(bank_merchant_ledger
, task_ledgers
.energy_billed_to_others
, credit
);
1114 ledger_debit(bank_merchant_ledger
, task_ledgers
.energy_billed_to_others
, debit
);
1122 * Routine: bank_task_destroy
1123 * Purpose: Drops reference on bank task.
1127 bank_task_destroy(task_t task
)
1129 bank_task_t bank_task
;
1131 /* Grab the global bank task lock before dropping the ref on task bank context */
1132 global_bank_task_lock();
1133 bank_task
= task
->bank_context
;
1134 task
->bank_context
= NULL
;
1135 global_bank_task_unlock();
1137 bank_destroy_bank_task_ledger(bank_task
);
1138 bank_task_dealloc(bank_task
, 1);
1142 * Routine: bank_task_initialize
1143 * Purpose: Initialize the bank context of a task.
1147 bank_task_initialize(task_t task
)
1149 get_bank_task_context(task
, TRUE
);
1153 * Routine: init_bank_ledgers
1154 * Purpose: Initialize template for bank ledgers.
1158 init_bank_ledgers(void)
1160 ledger_template_t t
;
1163 assert(bank_ledger_template
== NULL
);
1165 if ((t
= ledger_template_create("Bank ledger")) == NULL
) {
1166 panic("couldn't create bank ledger template");
1169 if ((idx
= ledger_entry_add(t
, "cpu_time", "sched", "ns")) < 0) {
1170 panic("couldn't create cpu_time entry for bank ledger template");
1172 bank_ledgers
.cpu_time
= idx
;
1174 if ((idx
= ledger_entry_add(t
, "energy", "power", "nj")) < 0) {
1175 panic("couldn't create energy entry for bank ledger template");
1177 bank_ledgers
.energy
= idx
;
1179 ledger_template_complete(t
);
1180 bank_ledger_template
= t
;
1183 /* Routine: bank_billed_balance_safe
1184 * Purpose: Walk through all the bank accounts billed to me by other tasks and get the current billing balance.
1185 * Called from another task. It takes global bank task lock to make sure the bank context is
1186 * not deallocated while accesing it.
1187 * Returns: cpu balance and energy balance in out paremeters.
1190 bank_billed_balance_safe(task_t task
, uint64_t *cpu_time
, uint64_t *energy
)
1192 bank_task_t bank_task
= BANK_TASK_NULL
;
1193 ledger_amount_t credit
, debit
;
1194 uint64_t cpu_balance
= 0;
1195 uint64_t energy_balance
= 0;
1198 /* Task might be in exec, grab the global bank task lock before accessing bank context. */
1199 global_bank_task_lock();
1200 /* Grab a reference on bank context */
1201 if (task
->bank_context
!= NULL
) {
1202 bank_task
= task
->bank_context
;
1203 bank_task_reference(bank_task
);
1205 global_bank_task_unlock();
1208 bank_billed_balance(bank_task
, &cpu_balance
, &energy_balance
);
1209 bank_task_dealloc(bank_task
, 1);
1211 kr
= ledger_get_entries(task
->ledger
, task_ledgers
.cpu_time_billed_to_me
,
1213 if (kr
== KERN_SUCCESS
) {
1214 cpu_balance
= credit
- debit
;
1216 kr
= ledger_get_entries(task
->ledger
, task_ledgers
.energy_billed_to_me
,
1218 if (kr
== KERN_SUCCESS
) {
1219 energy_balance
= credit
- debit
;
1223 *cpu_time
= cpu_balance
;
1224 *energy
= energy_balance
;
1229 * Routine: bank_billed_time
1230 * Purpose: Walk through the Accounts need to pay account list and get the current billing balance.
1231 * Returns: cpu balance and energy balance in out paremeters.
1234 bank_billed_balance(bank_task_t bank_task
, uint64_t *cpu_time
, uint64_t *energy
)
1236 int64_t cpu_balance
= 0;
1237 int64_t energy_balance
= 0;
1238 bank_account_t bank_account
;
1241 if (bank_task
== BANK_TASK_NULL
) {
1247 lck_mtx_lock(&bank_task
->bt_acc_to_pay_lock
);
1249 /* bt_acc_to_pay_lock locked, no need to take ledger reference for bt_ledger */
1250 if (bank_task
->bt_ledger
!= LEDGER_NULL
) {
1251 kr
= ledger_get_balance(bank_task
->bt_ledger
, task_ledgers
.cpu_time_billed_to_me
, &temp
);
1252 if (kr
== KERN_SUCCESS
&& temp
>= 0) {
1253 cpu_balance
+= temp
;
1255 #if DEVELOPMENT || DEBUG
1257 printf("bank_bill_time: ledger_get_balance failed or negative balance in ledger: %lld\n", temp
);
1259 #endif /* DEVELOPMENT || DEBUG */
1261 kr
= ledger_get_balance(bank_task
->bt_ledger
, task_ledgers
.energy_billed_to_me
, &temp
);
1262 if (kr
== KERN_SUCCESS
&& temp
>= 0) {
1263 energy_balance
+= temp
;
1267 queue_iterate(&bank_task
->bt_accounts_to_pay
, bank_account
, bank_account_t
, ba_next_acc_to_pay
) {
1269 kr
= ledger_get_balance(bank_account
->ba_bill
, bank_ledgers
.cpu_time
, &temp
);
1270 if (kr
== KERN_SUCCESS
&& temp
>= 0) {
1271 cpu_balance
+= temp
;
1273 #if DEVELOPMENT || DEBUG
1275 printf("bank_bill_time: ledger_get_balance failed or negative balance in ledger: %lld\n", temp
);
1277 #endif /* DEVELOPMENT || DEBUG */
1279 kr
= ledger_get_balance(bank_account
->ba_bill
, bank_ledgers
.energy
, &temp
);
1280 if (kr
== KERN_SUCCESS
&& temp
>= 0) {
1281 energy_balance
+= temp
;
1284 lck_mtx_unlock(&bank_task
->bt_acc_to_pay_lock
);
1285 *cpu_time
= (uint64_t)cpu_balance
;
1286 *energy
= (uint64_t)energy_balance
;
1290 /* Routine: bank_serviced_balance_safe
1291 * Purpose: Walk through the bank accounts billed to other tasks by me and get the current balance to be charged.
1292 * Called from another task. It takes global bank task lock to make sure the bank context is
1293 * not deallocated while accesing it.
1294 * Returns: cpu balance and energy balance in out paremeters.
1297 bank_serviced_balance_safe(task_t task
, uint64_t *cpu_time
, uint64_t *energy
)
1299 bank_task_t bank_task
= BANK_TASK_NULL
;
1300 ledger_amount_t credit
, debit
;
1301 uint64_t cpu_balance
= 0;
1302 uint64_t energy_balance
= 0;
1305 /* Task might be in exec, grab the global bank task lock before accessing bank context. */
1306 global_bank_task_lock();
1307 /* Grab a reference on bank context */
1308 if (task
->bank_context
!= NULL
) {
1309 bank_task
= task
->bank_context
;
1310 bank_task_reference(bank_task
);
1312 global_bank_task_unlock();
1315 bank_serviced_balance(bank_task
, &cpu_balance
, &energy_balance
);
1316 bank_task_dealloc(bank_task
, 1);
1318 kr
= ledger_get_entries(task
->ledger
, task_ledgers
.cpu_time_billed_to_others
,
1320 if (kr
== KERN_SUCCESS
) {
1321 cpu_balance
= credit
- debit
;
1324 kr
= ledger_get_entries(task
->ledger
, task_ledgers
.energy_billed_to_others
,
1326 if (kr
== KERN_SUCCESS
) {
1327 energy_balance
= credit
- debit
;
1331 *cpu_time
= cpu_balance
;
1332 *energy
= energy_balance
;
1337 * Routine: bank_serviced_balance
1338 * Purpose: Walk through the Account need to charge account list and get the current balance to be charged.
1339 * Returns: cpu balance and energy balance in out paremeters.
1342 bank_serviced_balance(bank_task_t bank_task
, uint64_t *cpu_time
, uint64_t *energy
)
1344 int64_t cpu_balance
= 0;
1345 int64_t energy_balance
= 0;
1346 bank_account_t bank_account
;
1349 ledger_t ledger
= LEDGER_NULL
;
1350 if (bank_task
== BANK_TASK_NULL
) {
1356 /* Grab a ledger reference on bt_ledger for bank_task */
1357 ledger
= bank_get_bank_task_ledger_with_ref(bank_task
);
1359 lck_mtx_lock(&bank_task
->bt_acc_to_charge_lock
);
1362 kr
= ledger_get_balance(ledger
, task_ledgers
.cpu_time_billed_to_others
, &temp
);
1363 if (kr
== KERN_SUCCESS
&& temp
>= 0) {
1364 cpu_balance
+= temp
;
1366 #if DEVELOPMENT || DEBUG
1368 printf("bank_serviced_time: ledger_get_balance failed or negative balance in ledger: %lld\n", temp
);
1370 #endif /* DEVELOPMENT || DEBUG */
1372 kr
= ledger_get_balance(ledger
, task_ledgers
.energy_billed_to_others
, &temp
);
1373 if (kr
== KERN_SUCCESS
&& temp
>= 0) {
1374 energy_balance
+= temp
;
1378 queue_iterate(&bank_task
->bt_accounts_to_charge
, bank_account
, bank_account_t
, ba_next_acc_to_charge
) {
1380 kr
= ledger_get_balance(bank_account
->ba_bill
, bank_ledgers
.cpu_time
, &temp
);
1381 if (kr
== KERN_SUCCESS
&& temp
>= 0) {
1382 cpu_balance
+= temp
;
1384 #if DEVELOPMENT || DEBUG
1386 printf("bank_serviced_time: ledger_get_balance failed or negative balance in ledger: %lld\n", temp
);
1388 #endif /* DEVELOPMENT || DEBUG */
1390 kr
= ledger_get_balance(bank_account
->ba_bill
, bank_ledgers
.energy
, &temp
);
1391 if (kr
== KERN_SUCCESS
&& temp
>= 0) {
1392 energy_balance
+= temp
;
1395 lck_mtx_unlock(&bank_task
->bt_acc_to_charge_lock
);
1397 ledger_dereference(ledger
);
1399 *cpu_time
= (uint64_t)cpu_balance
;
1400 *energy
= (uint64_t)energy_balance
;
1405 * Routine: bank_get_voucher_bank_account
1406 * Purpose: Get the bank account from the voucher.
1407 * Returns: bank_account if bank_account attribute present in voucher.
1408 * NULL on no attribute, no bank_element, or if holder and merchant bank accounts
1409 * and voucher thread group and current thread group are the same.
1411 static bank_account_t
1412 bank_get_voucher_bank_account(ipc_voucher_t voucher
)
1414 bank_element_t bank_element
= BANK_ELEMENT_NULL
;
1415 bank_account_t bank_account
= BANK_ACCOUNT_NULL
;
1416 mach_voucher_attr_value_handle_t vals
[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
];
1417 mach_voucher_attr_value_handle_array_size_t val_count
;
1420 val_count
= MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
;
1421 kr
= mach_voucher_attr_control_get_values(bank_voucher_attr_control
,
1426 if (kr
!= KERN_SUCCESS
|| val_count
== 0) {
1427 return BANK_ACCOUNT_NULL
;
1430 bank_element
= HANDLE_TO_BANK_ELEMENT(vals
[0]);
1431 if (bank_element
== BANK_DEFAULT_VALUE
) {
1432 return BANK_ACCOUNT_NULL
;
1434 if (bank_element
== BANK_DEFAULT_TASK_VALUE
) {
1435 bank_element
= CAST_TO_BANK_ELEMENT(get_bank_task_context(current_task(), FALSE
));
1438 if (bank_element
->be_type
== BANK_TASK
) {
1439 return BANK_ACCOUNT_NULL
;
1440 } else if (bank_element
->be_type
== BANK_ACCOUNT
) {
1441 bank_account
= CAST_TO_BANK_ACCOUNT(bank_element
);
1443 * Return BANK_ACCOUNT_NULL if the ba_holder is same as ba_merchant
1444 * and bank account thread group is same as current thread group
1445 * i.e. ba_merchant's thread group.
1447 * The bank account might have ba_holder same as ba_merchant but different
1448 * thread group if daemon sends a voucher to an App and then App sends the
1449 * same voucher back to the daemon (IPC code will replace thread group in the
1450 * voucher to App's thread group when it gets auto redeemed by the App).
1452 if (bank_account
->ba_holder
!= bank_account
->ba_merchant
||
1453 bank_get_bank_account_thread_group(bank_account
) !=
1454 bank_get_bank_task_thread_group(bank_account
->ba_merchant
)) {
1455 return bank_account
;
1457 return BANK_ACCOUNT_NULL
;
1460 panic("Bogus bank type: %d passed in bank_get_voucher_bank_account\n", bank_element
->be_type
);
1462 return BANK_ACCOUNT_NULL
;
1466 * Routine: bank_get_bank_task_ledger_with_ref
1467 * Purpose: Get the bank ledger from the bank task and return a reference to it.
1470 bank_get_bank_task_ledger_with_ref(bank_task_t bank_task
)
1472 ledger_t ledger
= LEDGER_NULL
;
1474 lck_mtx_lock(&bank_task
->bt_acc_to_pay_lock
);
1475 ledger
= bank_task
->bt_ledger
;
1477 ledger_reference(ledger
);
1479 lck_mtx_unlock(&bank_task
->bt_acc_to_pay_lock
);
1485 * Routine: bank_destroy_bank_task_ledger
1486 * Purpose: Drop the bank task reference on the task ledger.
1489 bank_destroy_bank_task_ledger(bank_task_t bank_task
)
1493 /* Remove the ledger reference from the bank task */
1494 lck_mtx_lock(&bank_task
->bt_acc_to_pay_lock
);
1495 assert(LEDGER_VALID(bank_task
->bt_ledger
));
1496 ledger
= bank_task
->bt_ledger
;
1497 bank_task
->bt_ledger
= LEDGER_NULL
;
1498 lck_mtx_unlock(&bank_task
->bt_acc_to_pay_lock
);
1500 ledger_dereference(ledger
);
1504 * Routine: bank_get_bank_account_ledger
1505 * Purpose: Get the bankledger from the bank account if ba_merchant different than ba_holder
1508 bank_get_bank_account_ledger(bank_account_t bank_account
)
1510 ledger_t bankledger
= LEDGER_NULL
;
1512 if (bank_account
!= BANK_ACCOUNT_NULL
&&
1513 bank_account
->ba_holder
!= bank_account
->ba_merchant
) {
1514 bankledger
= bank_account
->ba_bill
;
1521 * Routine: bank_get_bank_task_thread_group
1522 * Purpose: Get the bank task's thread group from the bank task
1524 static struct thread_group
*
1525 bank_get_bank_task_thread_group(bank_task_t bank_task __unused
)
1527 struct thread_group
*banktg
= NULL
;
1534 * Routine: bank_get_bank_account_thread_group
1535 * Purpose: Get the bank account's thread group from the bank account
1537 static struct thread_group
*
1538 bank_get_bank_account_thread_group(bank_account_t bank_account __unused
)
1540 struct thread_group
*banktg
= NULL
;
1547 * Routine: bank_get_bank_ledger_and_thread_group
1548 * Purpose: Get the bankledger (chit) and thread group from the voucher.
1549 * Returns: bankledger and thread group if bank_account attribute present in voucher.
1553 bank_get_bank_ledger_and_thread_group(
1554 ipc_voucher_t voucher
,
1555 ledger_t
*bankledger
,
1556 struct thread_group
**banktg
)
1558 bank_account_t bank_account
;
1559 struct thread_group
*thread_group
= NULL
;
1561 bank_account
= bank_get_voucher_bank_account(voucher
);
1562 *bankledger
= bank_get_bank_account_ledger(bank_account
);
1563 thread_group
= bank_get_bank_account_thread_group(bank_account
);
1565 /* Return NULL thread group if voucher has current task's thread group */
1566 if (thread_group
== bank_get_bank_task_thread_group(
1567 get_bank_task_context(current_task(), FALSE
))) {
1568 thread_group
= NULL
;
1570 *banktg
= thread_group
;
1571 return KERN_SUCCESS
;
1575 * Routine: bank_swap_thread_bank_ledger
1576 * Purpose: swap the bank ledger on the thread.
1578 * Note: Should be only called for current thread or thread which is not started.
1581 bank_swap_thread_bank_ledger(thread_t thread __unused
, ledger_t new_ledger __unused
)
1584 processor_t processor
;
1585 ledger_t old_ledger
= thread
->t_bankledger
;
1586 int64_t ctime
, effective_ledger_time_consumed
= 0;
1587 int64_t remainder
= 0, consumed
= 0;
1588 int64_t effective_energy_consumed
= 0;
1589 uint64_t thread_energy
;
1591 if (old_ledger
== LEDGER_NULL
&& new_ledger
== LEDGER_NULL
) {
1595 assert((thread
== current_thread() || thread
->started
== 0));
1598 thread_lock(thread
);
1601 * Calculation of time elapsed by the thread before voucher swap.
1602 * Following is the timeline which shows all the variables used in the calculation below.
1606 * |<- consumed ->|<- remainder ->|
1607 * timeline ----------------------------------------------------------------->
1609 * thread_dispatch ctime quantum end
1611 * |<-effective_ledger_time -> |
1612 * deduct_bank_ledger_time
1615 ctime
= mach_absolute_time();
1616 processor
= thread
->last_processor
;
1617 if (processor
!= NULL
) {
1618 if ((int64_t)processor
->quantum_end
> ctime
) {
1619 remainder
= (int64_t)processor
->quantum_end
- ctime
;
1622 consumed
= thread
->quantum_remaining
- remainder
;
1623 effective_ledger_time_consumed
= consumed
- thread
->t_deduct_bank_ledger_time
;
1626 thread
->t_deduct_bank_ledger_time
= consumed
;
1628 thread_energy
= ml_energy_stat(thread
);
1629 effective_energy_consumed
=
1630 thread_energy
- thread
->t_deduct_bank_ledger_energy
;
1631 assert(effective_energy_consumed
>= 0);
1632 thread
->t_deduct_bank_ledger_energy
= thread_energy
;
1634 thread
->t_bankledger
= new_ledger
;
1636 thread_unlock(thread
);
1639 if (old_ledger
!= LEDGER_NULL
) {
1640 ledger_credit(old_ledger
,
1641 bank_ledgers
.cpu_time
,
1642 effective_ledger_time_consumed
);
1643 ledger_credit(old_ledger
,
1644 bank_ledgers
.energy
,
1645 effective_energy_consumed
);