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
, bank_task_t bank_holder
, bank_task_t bank_merchant
);
78 static void init_bank_ledgers(void);
79 static boolean_t
bank_task_is_propagate_entitled(task_t t
);
80 static struct thread_group
*bank_get_bank_task_thread_group(bank_task_t bank_task __unused
);
81 static struct thread_group
*bank_get_bank_account_thread_group(bank_account_t bank_account __unused
);
83 static lck_spin_t g_bank_task_lock_data
; /* lock to protect task->bank_context transition */
85 #define global_bank_task_lock_init() \
86 lck_spin_init(&g_bank_task_lock_data, &bank_lock_grp, &bank_lock_attr)
87 #define global_bank_task_lock_destroy() \
88 lck_spin_destroy(&g_bank_task_lock_data, &bank_lock_grp)
89 #define global_bank_task_lock() \
90 lck_spin_lock(&g_bank_task_lock_data)
91 #define global_bank_task_lock_try() \
92 lck_spin_try_lock(&g_bank_task_lock_data)
93 #define global_bank_task_unlock() \
94 lck_spin_unlock(&g_bank_task_lock_data)
96 extern uint64_t proc_uniqueid(void *p
);
97 extern int32_t proc_pid(void *p
);
98 extern int32_t proc_pidversion(void *p
);
99 extern uint32_t proc_persona_id(void *p
);
100 extern uint32_t proc_getuid(void *p
);
101 extern uint32_t proc_getgid(void *p
);
102 extern void proc_getexecutableuuid(void *p
, unsigned char *uuidbuf
, unsigned long size
);
103 extern int kauth_cred_issuser(void *cred
);
104 extern void* kauth_cred_get(void);
109 ipc_voucher_attr_manager_t __assert_only manager
,
110 mach_voucher_attr_key_t __assert_only key
,
111 mach_voucher_attr_value_handle_t value
,
112 mach_voucher_attr_value_reference_t sync
);
116 ipc_voucher_attr_manager_t __assert_only manager
,
117 mach_voucher_attr_key_t __assert_only key
,
118 mach_voucher_attr_recipe_command_t command
,
119 mach_voucher_attr_value_handle_array_t prev_values
,
120 mach_msg_type_number_t __assert_only prev_value_count
,
121 mach_voucher_attr_content_t recipe
,
122 mach_voucher_attr_content_size_t recipe_size
,
123 mach_voucher_attr_value_handle_t
*out_value
,
124 mach_voucher_attr_value_flags_t
*out_flags
,
125 ipc_voucher_t
*out_value_voucher
);
128 bank_extract_content(
129 ipc_voucher_attr_manager_t __assert_only manager
,
130 mach_voucher_attr_key_t __assert_only key
,
131 mach_voucher_attr_value_handle_array_t values
,
132 mach_msg_type_number_t value_count
,
133 mach_voucher_attr_recipe_command_t
*out_command
,
134 mach_voucher_attr_content_t out_recipe
,
135 mach_voucher_attr_content_size_t
*in_out_recipe_size
);
139 ipc_voucher_attr_manager_t __assert_only manager
,
140 mach_voucher_attr_key_t __assert_only key
,
141 mach_voucher_attr_value_handle_array_t values
,
142 mach_msg_type_number_t value_count
,
143 mach_voucher_attr_command_t command
,
144 mach_voucher_attr_content_t in_content
,
145 mach_voucher_attr_content_size_t in_content_size
,
146 mach_voucher_attr_content_t out_content
,
147 mach_voucher_attr_content_size_t
*in_out_content_size
);
150 bank_release(ipc_voucher_attr_manager_t __assert_only manager
);
153 * communication channel from voucher system to ATM
155 struct ipc_voucher_attr_manager bank_manager
= {
156 .ivam_release_value
= bank_release_value
,
157 .ivam_get_value
= bank_get_value
,
158 .ivam_extract_content
= bank_extract_content
,
159 .ivam_command
= bank_command
,
160 .ivam_release
= bank_release
,
161 .ivam_flags
= (IVAM_FLAGS_SUPPORT_SEND_PREPROCESS
| IVAM_FLAGS_SUPPORT_RECEIVE_POSTPROCESS
),
165 #if DEVELOPMENT || DEBUG
166 decl_lck_mtx_data(, bank_tasks_list_lock
);
167 decl_lck_mtx_data(, bank_accounts_list_lock
);
169 lck_grp_t bank_dev_lock_grp
;
170 lck_attr_t bank_dev_lock_attr
;
171 lck_grp_attr_t bank_dev_lock_grp_attr
;
175 * Lock group attributes for bank sub system.
177 lck_grp_t bank_lock_grp
;
178 lck_attr_t bank_lock_attr
;
179 lck_grp_attr_t bank_lock_grp_attr
;
183 * Purpose: Initialize the BANK subsystem.
189 kern_return_t kr
= KERN_SUCCESS
;
190 /* setup zones for bank_task and bank_account objects */
191 bank_task_zone
= zinit(sizeof(struct bank_task
),
192 MAX_BANK_TASK
* sizeof(struct bank_task
),
193 sizeof(struct bank_task
),
196 bank_account_zone
= zinit(sizeof(struct bank_account
),
197 MAX_BANK_ACCOUNT
* sizeof(struct bank_account
),
198 sizeof(struct bank_account
),
203 /* Initialize bank lock group and lock attributes. */
204 lck_grp_attr_setdefault(&bank_lock_grp_attr
);
205 lck_grp_init(&bank_lock_grp
, "bank_lock", &bank_lock_grp_attr
);
206 lck_attr_setdefault(&bank_lock_attr
);
207 global_bank_task_lock_init();
209 #if DEVELOPMENT || DEBUG
210 /* Initialize global bank development lock group and lock attributes. */
211 lck_grp_attr_setdefault(&bank_dev_lock_grp_attr
);
212 lck_grp_init(&bank_dev_lock_grp
, "bank_dev_lock", &bank_dev_lock_grp_attr
);
213 lck_attr_setdefault(&bank_dev_lock_attr
);
215 lck_mtx_init(&bank_tasks_list_lock
, &bank_dev_lock_grp
, &bank_dev_lock_attr
);
216 lck_mtx_init(&bank_accounts_list_lock
, &bank_dev_lock_grp
, &bank_dev_lock_attr
);
218 queue_init(&bank_tasks_list
);
219 queue_init(&bank_accounts_list
);
222 /* Register the bank manager with the Vouchers sub system. */
223 kr
= ipc_register_well_known_mach_voucher_attr_manager(
226 MACH_VOUCHER_ATTR_KEY_BANK
,
227 &bank_voucher_attr_control
);
228 if (kr
!= KERN_SUCCESS
)
229 panic("BANK subsystem initialization failed");
231 kprintf("BANK subsystem is initialized\n");
237 * BANK Resource Manager Routines.
242 * Routine: bank_release_value
243 * Purpose: Release a value, if sync matches the sync count in value.
244 * Returns: KERN_SUCCESS: on Successful deletion.
245 * KERN_FAILURE: if sync value does not matches.
249 ipc_voucher_attr_manager_t __assert_only manager
,
250 mach_voucher_attr_key_t __assert_only key
,
251 mach_voucher_attr_value_handle_t value
,
252 mach_voucher_attr_value_reference_t sync
)
254 bank_task_t bank_task
= BANK_TASK_NULL
;
255 bank_element_t bank_element
= BANK_ELEMENT_NULL
;
256 bank_account_t bank_account
= BANK_ACCOUNT_NULL
;
257 kern_return_t kr
= KERN_SUCCESS
;
259 assert(MACH_VOUCHER_ATTR_KEY_BANK
== key
);
260 assert(manager
== &bank_manager
);
263 bank_element
= HANDLE_TO_BANK_ELEMENT(value
);
264 /* Voucher system should never release the default or persistent value */
265 assert(bank_element
!= BANK_DEFAULT_VALUE
&& bank_element
!= BANK_DEFAULT_TASK_VALUE
);
267 if (bank_element
== BANK_DEFAULT_VALUE
|| bank_element
== BANK_DEFAULT_TASK_VALUE
) {
268 /* Return success for default and default task value */
273 if (bank_element
->be_type
== BANK_TASK
) {
274 bank_task
= CAST_TO_BANK_TASK(bank_element
);
276 /* Checking of the made ref with sync and clearing of voucher ref should be done under a lock */
277 lck_mtx_lock(&bank_task
->bt_acc_to_pay_lock
);
278 if (bank_task
->bt_made
!= sync
) {
279 lck_mtx_unlock(&bank_task
->bt_acc_to_pay_lock
);
283 bank_task_made_release_num(bank_task
, sync
);
284 assert(bank_task
->bt_voucher_ref
== 1);
285 bank_task
->bt_voucher_ref
= 0;
286 lck_mtx_unlock(&bank_task
->bt_acc_to_pay_lock
);
288 bank_task_dealloc(bank_task
, 1);
289 } else if (bank_element
->be_type
== BANK_ACCOUNT
) {
290 bank_account
= CAST_TO_BANK_ACCOUNT(bank_element
);
291 kr
= bank_account_dealloc_with_sync(bank_account
, sync
);
293 panic("Bogus bank type: %d passed in get_value\n", bank_element
->be_type
);
301 * Routine: bank_get_value
305 ipc_voucher_attr_manager_t __assert_only manager
,
306 mach_voucher_attr_key_t __assert_only key
,
307 mach_voucher_attr_recipe_command_t command
,
308 mach_voucher_attr_value_handle_array_t prev_values
,
309 mach_msg_type_number_t prev_value_count
,
310 mach_voucher_attr_content_t __unused recipe
,
311 mach_voucher_attr_content_size_t __unused recipe_size
,
312 mach_voucher_attr_value_handle_t
*out_value
,
313 mach_voucher_attr_value_flags_t
*out_flags
,
314 ipc_voucher_t
*out_value_voucher
)
316 bank_task_t bank_task
= BANK_TASK_NULL
;
317 bank_task_t bank_holder
= BANK_TASK_NULL
;
318 bank_task_t bank_merchant
= BANK_TASK_NULL
;
319 bank_task_t bank_secureoriginator
= BANK_TASK_NULL
;
320 bank_task_t bank_proximateprocess
= BANK_TASK_NULL
;
321 bank_element_t bank_element
= BANK_ELEMENT_NULL
;
322 bank_account_t bank_account
= BANK_ACCOUNT_NULL
;
323 bank_account_t old_bank_account
= BANK_ACCOUNT_NULL
;
324 mach_voucher_attr_value_handle_t bank_handle
;
326 kern_return_t kr
= KERN_SUCCESS
;
327 mach_msg_type_number_t i
;
328 struct thread_group
*thread_group
= NULL
;
329 struct thread_group
*cur_thread_group
= NULL
;
331 assert(MACH_VOUCHER_ATTR_KEY_BANK
== key
);
332 assert(manager
== &bank_manager
);
334 /* never an out voucher */
335 *out_value_voucher
= IPC_VOUCHER_NULL
;
336 *out_flags
= MACH_VOUCHER_ATTR_VALUE_FLAGS_NONE
;
340 case MACH_VOUCHER_ATTR_BANK_CREATE
:
342 /* Return the default task value instead of bank task */
343 *out_value
= BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_TASK_VALUE
);
344 *out_flags
= MACH_VOUCHER_ATTR_VALUE_FLAGS_PERSIST
;
347 case MACH_VOUCHER_ATTR_AUTO_REDEEM
:
349 for (i
= 0; i
< prev_value_count
; i
++) {
350 bank_handle
= prev_values
[i
];
351 bank_element
= HANDLE_TO_BANK_ELEMENT(bank_handle
);
353 /* Should not have received default task value from an IPC */
354 if (bank_element
== BANK_DEFAULT_VALUE
|| bank_element
== BANK_DEFAULT_TASK_VALUE
)
357 task
= current_task();
358 if (bank_element
->be_type
== BANK_TASK
) {
359 bank_holder
= CAST_TO_BANK_TASK(bank_element
);
360 bank_secureoriginator
= bank_holder
;
361 bank_proximateprocess
= bank_holder
;
362 thread_group
= bank_get_bank_task_thread_group(bank_holder
);
363 } else if (bank_element
->be_type
== BANK_ACCOUNT
) {
364 old_bank_account
= CAST_TO_BANK_ACCOUNT(bank_element
);
365 bank_holder
= old_bank_account
->ba_holder
;
366 bank_secureoriginator
= old_bank_account
->ba_secureoriginator
;
367 bank_proximateprocess
= old_bank_account
->ba_proximateprocess
;
368 thread_group
= bank_get_bank_account_thread_group(old_bank_account
);
371 panic("Bogus bank type: %d passed in get_value\n", bank_element
->be_type
);
374 bank_merchant
= get_bank_task_context(task
, FALSE
);
375 if (bank_merchant
== BANK_TASK_NULL
)
376 return KERN_RESOURCE_SHORTAGE
;
378 cur_thread_group
= bank_get_bank_task_thread_group(bank_merchant
);
380 /* Change voucher thread group to current thread group for Apps */
381 if (task_is_app(task
)) {
382 thread_group
= cur_thread_group
;
385 /* Check if trying to redeem for self task, return the default bank task */
386 if (bank_holder
== bank_merchant
&&
387 bank_holder
== bank_secureoriginator
&&
388 bank_holder
== bank_proximateprocess
&&
389 thread_group
== cur_thread_group
) {
390 *out_value
= BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_TASK_VALUE
);
391 *out_flags
= MACH_VOUCHER_ATTR_VALUE_FLAGS_PERSIST
;
395 bank_account
= bank_account_alloc_init(bank_holder
, bank_merchant
,
396 bank_secureoriginator
, bank_proximateprocess
,
398 if (bank_account
== BANK_ACCOUNT_NULL
)
399 return KERN_RESOURCE_SHORTAGE
;
401 *out_value
= BANK_ELEMENT_TO_HANDLE(bank_account
);
405 *out_value
= BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_VALUE
);
408 case MACH_VOUCHER_ATTR_SEND_PREPROCESS
:
410 for (i
= 0; i
< prev_value_count
; i
++) {
411 bank_handle
= prev_values
[i
];
412 bank_element
= HANDLE_TO_BANK_ELEMENT(bank_handle
);
414 if (bank_element
== BANK_DEFAULT_VALUE
)
417 task
= current_task();
418 if (bank_element
== BANK_DEFAULT_TASK_VALUE
) {
419 bank_element
= CAST_TO_BANK_ELEMENT(get_bank_task_context(task
, FALSE
));
422 if (bank_element
->be_type
== BANK_TASK
) {
423 bank_holder
= CAST_TO_BANK_TASK(bank_element
);
424 bank_secureoriginator
= bank_holder
;
425 thread_group
= bank_get_bank_task_thread_group(bank_holder
);
426 } else if (bank_element
->be_type
== BANK_ACCOUNT
) {
427 old_bank_account
= CAST_TO_BANK_ACCOUNT(bank_element
);
428 bank_holder
= old_bank_account
->ba_holder
;
429 bank_secureoriginator
= old_bank_account
->ba_secureoriginator
;
430 thread_group
= bank_get_bank_account_thread_group(old_bank_account
);
432 panic("Bogus bank type: %d passed in get_value\n", bank_element
->be_type
);
435 bank_merchant
= get_bank_task_context(task
, FALSE
);
436 if (bank_merchant
== BANK_TASK_NULL
)
437 return KERN_RESOURCE_SHORTAGE
;
439 cur_thread_group
= bank_get_bank_task_thread_group(bank_merchant
);
442 * If the process doesn't have secure persona entitlement,
443 * then replace the secure originator to current task.
445 if (bank_merchant
->bt_hasentitlement
== 0) {
446 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
,
447 (BANK_CODE(BANK_ACCOUNT_INFO
, (BANK_SECURE_ORIGINATOR_CHANGED
))) | DBG_FUNC_NONE
,
448 bank_secureoriginator
->bt_pid
, bank_merchant
->bt_pid
, 0, 0, 0);
449 bank_secureoriginator
= bank_merchant
;
452 bank_proximateprocess
= bank_merchant
;
454 /* Check if trying to redeem for self task, return the bank task */
455 if (bank_holder
== bank_merchant
&&
456 bank_holder
== bank_secureoriginator
&&
457 bank_holder
== bank_proximateprocess
&&
458 thread_group
== cur_thread_group
) {
460 lck_mtx_lock(&bank_holder
->bt_acc_to_pay_lock
);
461 bank_task_made_reference(bank_holder
);
462 if (bank_holder
->bt_voucher_ref
== 0) {
463 /* Take a ref for voucher system, if voucher system does not have a ref */
464 bank_task_reference(bank_holder
);
465 bank_holder
->bt_voucher_ref
= 1;
467 lck_mtx_unlock(&bank_holder
->bt_acc_to_pay_lock
);
469 *out_value
= BANK_ELEMENT_TO_HANDLE(bank_holder
);
472 bank_account
= bank_account_alloc_init(bank_holder
, bank_merchant
,
473 bank_secureoriginator
, bank_proximateprocess
,
475 if (bank_account
== BANK_ACCOUNT_NULL
)
476 return KERN_RESOURCE_SHORTAGE
;
478 *out_value
= BANK_ELEMENT_TO_HANDLE(bank_account
);
482 *out_value
= BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_VALUE
);
485 case MACH_VOUCHER_ATTR_REDEEM
:
487 for (i
= 0; i
< prev_value_count
; i
++) {
488 bank_handle
= prev_values
[i
];
489 bank_element
= HANDLE_TO_BANK_ELEMENT(bank_handle
);
491 if (bank_element
== BANK_DEFAULT_VALUE
)
494 task
= current_task();
495 if (bank_element
== BANK_DEFAULT_TASK_VALUE
) {
496 *out_value
= BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_TASK_VALUE
);
497 *out_flags
= MACH_VOUCHER_ATTR_VALUE_FLAGS_PERSIST
;
500 if (bank_element
->be_type
== BANK_TASK
) {
501 bank_task
= CAST_TO_BANK_TASK(bank_element
);
502 panic("Found a bank task in MACH_VOUCHER_ATTR_REDEEM: %p", bank_task
);
506 } else if (bank_element
->be_type
== BANK_ACCOUNT
) {
507 bank_account
= CAST_TO_BANK_ACCOUNT(bank_element
);
508 bank_merchant
= bank_account
->ba_merchant
;
509 if (bank_merchant
!= get_bank_task_context(task
, FALSE
)) {
510 panic("Found another bank task: %p as a bank merchant\n", bank_merchant
);
513 bank_account_made_reference(bank_account
);
514 *out_value
= BANK_ELEMENT_TO_HANDLE(bank_account
);
517 panic("Bogus bank type: %d passed in get_value\n", bank_element
->be_type
);
521 *out_value
= BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_VALUE
);
525 kr
= KERN_INVALID_ARGUMENT
;
534 * Routine: bank_extract_content
535 * Purpose: Extract a set of aid from an array of voucher values.
536 * Returns: KERN_SUCCESS: on Success.
537 * KERN_FAILURE: one of the value is not present in the hash.
538 * KERN_NO_SPACE: insufficeint buffer provided to fill an array of aid.
541 bank_extract_content(
542 ipc_voucher_attr_manager_t __assert_only manager
,
543 mach_voucher_attr_key_t __assert_only key
,
544 mach_voucher_attr_value_handle_array_t values
,
545 mach_msg_type_number_t value_count
,
546 mach_voucher_attr_recipe_command_t
*out_command
,
547 mach_voucher_attr_content_t out_recipe
,
548 mach_voucher_attr_content_size_t
*in_out_recipe_size
)
550 bank_task_t bank_task
= BANK_TASK_NULL
;
551 bank_element_t bank_element
= BANK_ELEMENT_NULL
;
552 bank_account_t bank_account
= BANK_ACCOUNT_NULL
;
553 mach_voucher_attr_value_handle_t bank_handle
;
554 char buf
[MACH_VOUCHER_BANK_CONTENT_SIZE
];
555 mach_msg_type_number_t i
;
557 assert(MACH_VOUCHER_ATTR_KEY_BANK
== key
);
558 assert(manager
== &bank_manager
);
560 for (i
= 0; i
< value_count
; i
++) {
561 bank_handle
= values
[i
];
562 bank_element
= HANDLE_TO_BANK_ELEMENT(bank_handle
);
563 if (bank_element
== BANK_DEFAULT_VALUE
)
566 if (bank_element
== BANK_DEFAULT_TASK_VALUE
) {
567 bank_element
= CAST_TO_BANK_ELEMENT(get_bank_task_context(current_task(), FALSE
));
570 if (MACH_VOUCHER_BANK_CONTENT_SIZE
> *in_out_recipe_size
) {
571 *in_out_recipe_size
= 0;
572 return KERN_NO_SPACE
;
575 if (bank_element
->be_type
== BANK_TASK
) {
576 bank_task
= CAST_TO_BANK_TASK(bank_element
);
577 snprintf(buf
, MACH_VOUCHER_BANK_CONTENT_SIZE
,
578 " Bank Context for a pid %d\n", bank_task
->bt_pid
);
579 } else if (bank_element
->be_type
== BANK_ACCOUNT
) {
580 bank_account
= CAST_TO_BANK_ACCOUNT(bank_element
);
581 snprintf(buf
, MACH_VOUCHER_BANK_CONTENT_SIZE
,
582 " Bank Account linking holder pid %d with merchant pid %d, originator PID/persona: %d, %u and proximate PID/persona: %d, %u\n",
583 bank_account
->ba_holder
->bt_pid
,
584 bank_account
->ba_merchant
->bt_pid
,
585 bank_account
->ba_secureoriginator
->bt_pid
,
586 bank_account
->ba_secureoriginator
->bt_persona_id
,
587 bank_account
->ba_proximateprocess
->bt_pid
,
588 bank_account
->ba_proximateprocess
->bt_persona_id
);
590 panic("Bogus bank type: %d passed in get_value\n", bank_element
->be_type
);
594 memcpy(&out_recipe
[0], buf
, strlen(buf
) + 1);
595 *out_command
= MACH_VOUCHER_ATTR_BANK_NULL
;
596 *in_out_recipe_size
= (mach_voucher_attr_content_size_t
)strlen(buf
) + 1;
604 * Routine: bank_command
605 * Purpose: Execute a command against a set of ATM values.
606 * Returns: KERN_SUCCESS: On successful execution of command.
607 KERN_FAILURE: On failure.
611 ipc_voucher_attr_manager_t __assert_only manager
,
612 mach_voucher_attr_key_t __assert_only key
,
613 mach_voucher_attr_value_handle_array_t __unused values
,
614 mach_msg_type_number_t __unused value_count
,
615 mach_voucher_attr_command_t __unused command
,
616 mach_voucher_attr_content_t __unused in_content
,
617 mach_voucher_attr_content_size_t __unused in_content_size
,
618 mach_voucher_attr_content_t __unused out_content
,
619 mach_voucher_attr_content_size_t __unused
*out_content_size
)
621 bank_task_t bank_task
= BANK_TASK_NULL
;
622 bank_task_t bank_secureoriginator
= BANK_TASK_NULL
;
623 bank_task_t bank_proximateprocess
= BANK_TASK_NULL
;
624 struct persona_token
*token
= NULL
;
625 bank_element_t bank_element
= BANK_ELEMENT_NULL
;
626 bank_account_t bank_account
= BANK_ACCOUNT_NULL
;
627 mach_voucher_attr_value_handle_t bank_handle
;
628 mach_msg_type_number_t i
;
631 assert(MACH_VOUCHER_ATTR_KEY_BANK
== key
);
632 assert(manager
== &bank_manager
);
635 case BANK_ORIGINATOR_PID
:
637 if ((sizeof(pid
)) > *out_content_size
) {
638 *out_content_size
= 0;
639 return KERN_NO_SPACE
;
642 for (i
= 0; i
< value_count
; i
++) {
643 bank_handle
= values
[i
];
644 bank_element
= HANDLE_TO_BANK_ELEMENT(bank_handle
);
645 if (bank_element
== BANK_DEFAULT_VALUE
)
648 if (bank_element
== BANK_DEFAULT_TASK_VALUE
) {
649 bank_element
= CAST_TO_BANK_ELEMENT(get_bank_task_context(current_task(), FALSE
));
652 if (bank_element
->be_type
== BANK_TASK
) {
653 bank_task
= CAST_TO_BANK_TASK(bank_element
);
654 } else if (bank_element
->be_type
== BANK_ACCOUNT
) {
655 bank_account
= CAST_TO_BANK_ACCOUNT(bank_element
);
656 bank_task
= bank_account
->ba_holder
;
658 panic("Bogus bank type: %d passed in voucher_command\n", bank_element
->be_type
);
660 pid
= bank_task
->bt_pid
;
662 memcpy(&out_content
[0], &pid
, sizeof(pid
));
663 *out_content_size
= (mach_voucher_attr_content_size_t
)sizeof(pid
);
666 /* In the case of no value, return error KERN_INVALID_VALUE */
667 *out_content_size
= 0;
668 return KERN_INVALID_VALUE
;
670 case BANK_PERSONA_TOKEN
:
672 if ((sizeof(struct persona_token
)) > *out_content_size
) {
673 *out_content_size
= 0;
674 return KERN_NO_SPACE
;
676 for (i
= 0; i
< value_count
; i
++) {
677 bank_handle
= values
[i
];
678 bank_element
= HANDLE_TO_BANK_ELEMENT(bank_handle
);
679 if (bank_element
== BANK_DEFAULT_VALUE
)
682 if (bank_element
== BANK_DEFAULT_TASK_VALUE
) {
683 bank_element
= CAST_TO_BANK_ELEMENT(get_bank_task_context(current_task(), FALSE
));
686 if (bank_element
->be_type
== BANK_TASK
) {
687 *out_content_size
= 0;
688 return KERN_INVALID_OBJECT
;
689 } else if (bank_element
->be_type
== BANK_ACCOUNT
) {
690 bank_account
= CAST_TO_BANK_ACCOUNT(bank_element
);
691 bank_secureoriginator
= bank_account
->ba_secureoriginator
;
692 bank_proximateprocess
= bank_account
->ba_proximateprocess
;
694 panic("Bogus bank type: %d passed in voucher_command\n", bank_element
->be_type
);
696 token
= (struct persona_token
*)(void *)&out_content
[0];
697 memcpy(&token
->originator
, &bank_secureoriginator
->bt_proc_persona
, sizeof(struct proc_persona_info
));
698 memcpy(&token
->proximate
, &bank_proximateprocess
->bt_proc_persona
, sizeof(struct proc_persona_info
));
700 *out_content_size
= (mach_voucher_attr_content_size_t
)sizeof(*token
);
703 /* In the case of no value, return error KERN_INVALID_VALUE */
704 *out_content_size
= 0;
705 return KERN_INVALID_VALUE
;
708 return KERN_INVALID_ARGUMENT
;
716 ipc_voucher_attr_manager_t __assert_only manager
)
718 assert(manager
== &bank_manager
);
724 * Bank Internal Routines.
728 * Routine: bank_task_alloc_init
729 * Purpose: Allocate and initialize a bank task structure.
730 * Returns: bank_task_t on Success.
731 * BANK_TASK_NULL: on Failure.
732 * Notes: Leaves the task and creditcard blank and has only 1 ref,
733 needs to take 1 extra ref after the task field is initialized.
736 bank_task_alloc_init(task_t task
)
738 bank_task_t new_bank_task
;
740 new_bank_task
= (bank_task_t
) zalloc(bank_task_zone
);
741 if (new_bank_task
== BANK_TASK_NULL
)
742 return BANK_TASK_NULL
;
744 new_bank_task
->bt_type
= BANK_TASK
;
745 new_bank_task
->bt_voucher_ref
= 0;
746 new_bank_task
->bt_refs
= 1;
747 new_bank_task
->bt_made
= 0;
748 new_bank_task
->bt_creditcard
= NULL
;
749 new_bank_task
->bt_hasentitlement
= bank_task_is_propagate_entitled(task
);
750 queue_init(&new_bank_task
->bt_accounts_to_pay
);
751 queue_init(&new_bank_task
->bt_accounts_to_charge
);
752 lck_mtx_init(&new_bank_task
->bt_acc_to_pay_lock
, &bank_lock_grp
, &bank_lock_attr
);
753 lck_mtx_init(&new_bank_task
->bt_acc_to_charge_lock
, &bank_lock_grp
, &bank_lock_attr
);
756 * Initialize the persona_id struct
758 bzero(&new_bank_task
->bt_proc_persona
, sizeof(new_bank_task
->bt_proc_persona
));
759 new_bank_task
->bt_flags
= 0;
760 new_bank_task
->bt_unique_pid
= proc_uniqueid(task
->bsd_info
);
761 new_bank_task
->bt_pid
= proc_pid(task
->bsd_info
);
762 new_bank_task
->bt_pidversion
= proc_pidversion(task
->bsd_info
);
763 new_bank_task
->bt_persona_id
= proc_persona_id(task
->bsd_info
);
764 new_bank_task
->bt_uid
= proc_getuid(task
->bsd_info
);
765 new_bank_task
->bt_gid
= proc_getgid(task
->bsd_info
);
766 proc_getexecutableuuid(task
->bsd_info
, new_bank_task
->bt_macho_uuid
, sizeof(new_bank_task
->bt_macho_uuid
));
768 #if DEVELOPMENT || DEBUG
769 new_bank_task
->bt_task
= NULL
;
770 lck_mtx_lock(&bank_tasks_list_lock
);
771 queue_enter(&bank_tasks_list
, new_bank_task
, bank_task_t
, bt_global_elt
);
772 lck_mtx_unlock(&bank_tasks_list_lock
);
774 return (new_bank_task
);
778 * Routine: proc_is_propagate_entitled
779 * Purpose: Check if the process has persona propagate entitlement.
780 * Returns: TRUE if entitled.
784 bank_task_is_propagate_entitled(task_t t
)
786 /* Return TRUE if root process */
787 if (0 == kauth_cred_issuser(kauth_cred_get())) {
788 /* If it's a non-root process, it needs to have the entitlement for secure originator propagation */
789 boolean_t entitled
= FALSE
;
790 entitled
= IOTaskHasEntitlement(t
, ENTITLEMENT_PERSONA_PROPAGATE
);
798 * Routine: bank_account_alloc_init
799 * Purpose: Allocate and Initialize the bank account struct.
800 * Returns: bank_account_t : On Success.
801 * BANK_ACCOUNT_NULL: On Failure.
803 static bank_account_t
804 bank_account_alloc_init(
805 bank_task_t bank_holder
,
806 bank_task_t bank_merchant
,
807 bank_task_t bank_secureoriginator
,
808 bank_task_t bank_proximateprocess
,
809 struct thread_group
*thread_group
)
811 bank_account_t new_bank_account
;
812 bank_account_t bank_account
;
813 boolean_t entry_found
= FALSE
;
814 ledger_t new_ledger
= ledger_instantiate(bank_ledger_template
, LEDGER_CREATE_INACTIVE_ENTRIES
);
816 if (new_ledger
== NULL
)
817 return BANK_ACCOUNT_NULL
;
819 ledger_entry_setactive(new_ledger
, bank_ledgers
.cpu_time
);
820 ledger_entry_setactive(new_ledger
, bank_ledgers
.energy
);
821 new_bank_account
= (bank_account_t
) zalloc(bank_account_zone
);
822 if (new_bank_account
== BANK_ACCOUNT_NULL
) {
823 ledger_dereference(new_ledger
);
824 return BANK_ACCOUNT_NULL
;
827 new_bank_account
->ba_type
= BANK_ACCOUNT
;
828 new_bank_account
->ba_voucher_ref
= 0;
829 new_bank_account
->ba_refs
= 1;
830 new_bank_account
->ba_made
= 1;
831 new_bank_account
->ba_bill
= new_ledger
;
832 new_bank_account
->ba_merchant
= bank_merchant
;
833 new_bank_account
->ba_holder
= bank_holder
;
834 new_bank_account
->ba_secureoriginator
= bank_secureoriginator
;
835 new_bank_account
->ba_proximateprocess
= bank_proximateprocess
;
837 /* Iterate through accounts need to pay list to find the existing entry */
838 lck_mtx_lock(&bank_holder
->bt_acc_to_pay_lock
);
839 queue_iterate(&bank_holder
->bt_accounts_to_pay
, bank_account
, bank_account_t
, ba_next_acc_to_pay
) {
840 if (bank_account
->ba_merchant
!= bank_merchant
||
841 bank_account
->ba_secureoriginator
!= bank_secureoriginator
||
842 bank_account
->ba_proximateprocess
!= bank_proximateprocess
||
843 bank_get_bank_account_thread_group(bank_account
) != thread_group
)
847 /* Take a made ref, since this value would be returned to voucher system. */
848 bank_account_made_reference(bank_account
);
854 /* Create a linkage between the holder and the merchant task, Grab both the list locks before adding it to the list. */
855 lck_mtx_lock(&bank_merchant
->bt_acc_to_charge_lock
);
857 /* Add the account entry into Accounts need to pay account link list. */
858 queue_enter(&bank_holder
->bt_accounts_to_pay
, new_bank_account
, bank_account_t
, ba_next_acc_to_pay
);
860 /* Add the account entry into Accounts need to charge account link list. */
861 queue_enter(&bank_merchant
->bt_accounts_to_charge
, new_bank_account
, bank_account_t
, ba_next_acc_to_charge
);
863 lck_mtx_unlock(&bank_merchant
->bt_acc_to_charge_lock
);
866 lck_mtx_unlock(&bank_holder
->bt_acc_to_pay_lock
);
869 ledger_dereference(new_ledger
);
870 zfree(bank_account_zone
, new_bank_account
);
874 bank_task_reference(bank_holder
);
875 bank_task_reference(bank_merchant
);
876 bank_task_reference(bank_secureoriginator
);
877 bank_task_reference(bank_proximateprocess
);
879 #if DEVELOPMENT || DEBUG
880 new_bank_account
->ba_task
= NULL
;
881 lck_mtx_lock(&bank_accounts_list_lock
);
882 queue_enter(&bank_accounts_list
, new_bank_account
, bank_account_t
, ba_global_elt
);
883 lck_mtx_unlock(&bank_accounts_list_lock
);
886 return (new_bank_account
);
890 * Routine: get_bank_task_context
891 * Purpose: Get the bank context of the given task
892 * Returns: bank_task_t on Success.
893 * BANK_TASK_NULL: on Failure.
894 * Note: Initialize bank context if NULL.
897 get_bank_task_context
899 boolean_t initialize
)
901 bank_task_t bank_task
;
903 if (task
->bank_context
|| !initialize
) {
904 assert(task
->bank_context
!= NULL
);
905 return (task
->bank_context
);
908 bank_task
= bank_task_alloc_init(task
);
910 /* Grab the task lock and check if we won the race. */
912 if (task
->bank_context
) {
914 if (bank_task
!= BANK_TASK_NULL
)
915 bank_task_dealloc(bank_task
, 1);
916 return (task
->bank_context
);
917 } else if (bank_task
== BANK_TASK_NULL
) {
919 return BANK_TASK_NULL
;
921 /* We won the race. Take a ref on the ledger and initialize bank task. */
922 bank_task
->bt_creditcard
= task
->ledger
;
923 #if DEVELOPMENT || DEBUG
924 bank_task
->bt_task
= task
;
926 ledger_reference(task
->ledger
);
928 /* Grab the global bank task lock before setting the bank context on a task */
929 global_bank_task_lock();
930 task
->bank_context
= bank_task
;
931 global_bank_task_unlock();
939 * Routine: bank_task_dealloc
940 * Purpose: Drops the reference on bank task.
945 bank_task_t bank_task
,
946 mach_voucher_attr_value_reference_t sync
)
948 assert(bank_task
->bt_refs
>= 0);
950 if (bank_task_release_num(bank_task
, sync
) > (int)sync
)
953 assert(bank_task
->bt_refs
== 0);
954 assert(queue_empty(&bank_task
->bt_accounts_to_pay
));
955 assert(queue_empty(&bank_task
->bt_accounts_to_charge
));
957 ledger_dereference(bank_task
->bt_creditcard
);
958 lck_mtx_destroy(&bank_task
->bt_acc_to_pay_lock
, &bank_lock_grp
);
959 lck_mtx_destroy(&bank_task
->bt_acc_to_charge_lock
, &bank_lock_grp
);
962 #if DEVELOPMENT || DEBUG
963 lck_mtx_lock(&bank_tasks_list_lock
);
964 queue_remove(&bank_tasks_list
, bank_task
, bank_task_t
, bt_global_elt
);
965 lck_mtx_unlock(&bank_tasks_list_lock
);
968 zfree(bank_task_zone
, bank_task
);
972 * Routine: bank_account_dealloc_with_sync
973 * Purpose: Drop the reference on bank account if the sync matches.
974 * Returns: KERN_SUCCESS if sync matches.
975 * KERN_FAILURE on mismatch.
978 bank_account_dealloc_with_sync(
979 bank_account_t bank_account
,
980 mach_voucher_attr_value_reference_t sync
)
982 bank_task_t bank_holder
= bank_account
->ba_holder
;
983 bank_task_t bank_merchant
= bank_account
->ba_merchant
;
984 bank_task_t bank_secureoriginator
= bank_account
->ba_secureoriginator
;
985 bank_task_t bank_proximateprocess
= bank_account
->ba_proximateprocess
;
987 /* Grab the acc to pay list lock and check the sync value */
988 lck_mtx_lock(&bank_holder
->bt_acc_to_pay_lock
);
990 if (bank_account
->ba_made
!= sync
) {
991 lck_mtx_unlock(&bank_holder
->bt_acc_to_pay_lock
);
995 bank_account_made_release_num(bank_account
, sync
);
997 if (bank_account_release_num(bank_account
, 1) > 1)
998 panic("Releasing a non zero ref bank account %p\n", bank_account
);
1001 /* Grab both the acc to pay and acc to charge locks */
1002 lck_mtx_lock(&bank_merchant
->bt_acc_to_charge_lock
);
1004 bank_rollup_chit_to_tasks(bank_account
->ba_bill
, bank_holder
, bank_merchant
);
1006 /* Remove the account entry from Accounts need to pay account link list. */
1007 queue_remove(&bank_holder
->bt_accounts_to_pay
, bank_account
, bank_account_t
, ba_next_acc_to_pay
);
1009 /* Remove the account entry from Accounts need to charge account link list. */
1010 queue_remove(&bank_merchant
->bt_accounts_to_charge
, bank_account
, bank_account_t
, ba_next_acc_to_charge
);
1012 lck_mtx_unlock(&bank_merchant
->bt_acc_to_charge_lock
);
1013 lck_mtx_unlock(&bank_holder
->bt_acc_to_pay_lock
);
1015 ledger_dereference(bank_account
->ba_bill
);
1017 /* Drop the reference of bank holder and merchant */
1018 bank_task_dealloc(bank_holder
, 1);
1019 bank_task_dealloc(bank_merchant
, 1);
1020 bank_task_dealloc(bank_secureoriginator
, 1);
1021 bank_task_dealloc(bank_proximateprocess
, 1);
1023 #if DEVELOPMENT || DEBUG
1024 lck_mtx_lock(&bank_accounts_list_lock
);
1025 queue_remove(&bank_accounts_list
, bank_account
, bank_account_t
, ba_global_elt
);
1026 lck_mtx_unlock(&bank_accounts_list_lock
);
1029 zfree(bank_account_zone
, bank_account
);
1030 return KERN_SUCCESS
;
1034 * Routine: bank_rollup_chit_to_tasks
1035 * Purpose: Debit and Credit holder's and merchant's ledgers.
1039 bank_rollup_chit_to_tasks(
1041 bank_task_t bank_holder
,
1042 bank_task_t bank_merchant
)
1044 ledger_amount_t credit
;
1045 ledger_amount_t debit
;
1048 if (bank_holder
== bank_merchant
)
1051 ret
= ledger_get_entries(bill
, bank_ledgers
.cpu_time
, &credit
, &debit
);
1052 if (ret
== KERN_SUCCESS
) {
1053 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
,
1054 (BANK_CODE(BANK_ACCOUNT_INFO
, (BANK_SETTLE_CPU_TIME
))) | DBG_FUNC_NONE
,
1055 bank_merchant
->bt_pid
, bank_holder
->bt_pid
, credit
, debit
, 0);
1056 ledger_credit(bank_holder
->bt_creditcard
, task_ledgers
.cpu_time_billed_to_me
, credit
);
1057 ledger_debit(bank_holder
->bt_creditcard
, task_ledgers
.cpu_time_billed_to_me
, debit
);
1059 ledger_credit(bank_merchant
->bt_creditcard
, task_ledgers
.cpu_time_billed_to_others
, credit
);
1060 ledger_debit(bank_merchant
->bt_creditcard
, task_ledgers
.cpu_time_billed_to_others
, debit
);
1063 ret
= ledger_get_entries(bill
, bank_ledgers
.energy
, &credit
, &debit
);
1064 if (ret
== KERN_SUCCESS
) {
1065 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
,
1066 (BANK_CODE(BANK_ACCOUNT_INFO
, (BANK_SETTLE_ENERGY
))) | DBG_FUNC_NONE
,
1067 bank_merchant
->bt_pid
, bank_holder
->bt_pid
, credit
, debit
, 0);
1068 ledger_credit(bank_holder
->bt_creditcard
, task_ledgers
.energy_billed_to_me
, credit
);
1069 ledger_debit(bank_holder
->bt_creditcard
, task_ledgers
.energy_billed_to_me
, debit
);
1071 ledger_credit(bank_merchant
->bt_creditcard
, task_ledgers
.energy_billed_to_others
, credit
);
1072 ledger_debit(bank_merchant
->bt_creditcard
, task_ledgers
.energy_billed_to_others
, debit
);
1079 * Routine: bank_task_destroy
1080 * Purpose: Drops reference on bank task.
1084 bank_task_destroy(task_t task
)
1086 bank_task_t bank_task
;
1088 /* Grab the global bank task lock before dropping the ref on task bank context */
1089 global_bank_task_lock();
1090 bank_task
= task
->bank_context
;
1091 task
->bank_context
= NULL
;
1092 global_bank_task_unlock();
1094 bank_task_dealloc(bank_task
, 1);
1098 * Routine: bank_task_initialize
1099 * Purpose: Initialize the bank context of a task.
1103 bank_task_initialize(task_t task
)
1105 get_bank_task_context(task
, TRUE
);
1109 * Routine: init_bank_ledgers
1110 * Purpose: Initialize template for bank ledgers.
1114 init_bank_ledgers(void) {
1115 ledger_template_t t
;
1118 assert(bank_ledger_template
== NULL
);
1120 if ((t
= ledger_template_create("Bank ledger")) == NULL
)
1121 panic("couldn't create bank ledger template");
1123 if ((idx
= ledger_entry_add(t
, "cpu_time", "sched", "ns")) < 0) {
1124 panic("couldn't create cpu_time entry for bank ledger template");
1126 bank_ledgers
.cpu_time
= idx
;
1128 if ((idx
= ledger_entry_add(t
, "energy", "power", "nj")) < 0) {
1129 panic("couldn't create energy entry for bank ledger template");
1131 bank_ledgers
.energy
= idx
;
1133 ledger_template_complete(t
);
1134 bank_ledger_template
= t
;
1137 /* Routine: bank_billed_balance_safe
1138 * Purpose: Walk through all the bank accounts billed to me by other tasks and get the current billing balance.
1139 * Called from another task. It takes global bank task lock to make sure the bank context is
1140 not deallocated while accesing it.
1141 * Returns: cpu balance and energy balance in out paremeters.
1144 bank_billed_balance_safe(task_t task
, uint64_t *cpu_time
, uint64_t *energy
)
1146 bank_task_t bank_task
= BANK_TASK_NULL
;
1147 ledger_amount_t credit
, debit
;
1148 uint64_t cpu_balance
= 0;
1149 uint64_t energy_balance
= 0;
1152 /* Task might be in exec, grab the global bank task lock before accessing bank context. */
1153 global_bank_task_lock();
1154 /* Grab a reference on bank context */
1155 if (task
->bank_context
!= NULL
) {
1156 bank_task
= task
->bank_context
;
1157 bank_task_reference(bank_task
);
1159 global_bank_task_unlock();
1162 bank_billed_balance(bank_task
, &cpu_balance
, &energy_balance
);
1163 bank_task_dealloc(bank_task
, 1);
1165 kr
= ledger_get_entries(task
->ledger
, task_ledgers
.cpu_time_billed_to_me
,
1167 if (kr
== KERN_SUCCESS
) {
1168 cpu_balance
= credit
- debit
;
1170 kr
= ledger_get_entries(task
->ledger
, task_ledgers
.energy_billed_to_me
,
1172 if (kr
== KERN_SUCCESS
) {
1173 energy_balance
= credit
- debit
;
1177 *cpu_time
= cpu_balance
;
1178 *energy
= energy_balance
;
1183 * Routine: bank_billed_time
1184 * Purpose: Walk through the Accounts need to pay account list and get the current billing balance.
1185 * Returns: cpu balance and energy balance in out paremeters.
1188 bank_billed_balance(bank_task_t bank_task
, uint64_t *cpu_time
, uint64_t *energy
)
1190 int64_t cpu_balance
= 0;
1191 int64_t energy_balance
= 0;
1192 bank_account_t bank_account
;
1195 if (bank_task
== BANK_TASK_NULL
) {
1201 lck_mtx_lock(&bank_task
->bt_acc_to_pay_lock
);
1203 kr
= ledger_get_balance(bank_task
->bt_creditcard
, task_ledgers
.cpu_time_billed_to_me
, &temp
);
1204 if (kr
== KERN_SUCCESS
&& temp
>= 0) {
1205 cpu_balance
+= temp
;
1207 #if DEVELOPMENT || DEBUG
1209 printf("bank_bill_time: ledger_get_balance failed or negative balance in ledger: %lld\n", temp
);
1211 #endif /* DEVELOPMENT || DEBUG */
1213 kr
= ledger_get_balance(bank_task
->bt_creditcard
, task_ledgers
.energy_billed_to_me
, &temp
);
1214 if (kr
== KERN_SUCCESS
&& temp
>= 0) {
1215 energy_balance
+= temp
;
1218 queue_iterate(&bank_task
->bt_accounts_to_pay
, bank_account
, bank_account_t
, ba_next_acc_to_pay
) {
1220 kr
= ledger_get_balance(bank_account
->ba_bill
, bank_ledgers
.cpu_time
, &temp
);
1221 if (kr
== KERN_SUCCESS
&& temp
>= 0) {
1222 cpu_balance
+= temp
;
1224 #if DEVELOPMENT || DEBUG
1226 printf("bank_bill_time: ledger_get_balance failed or negative balance in ledger: %lld\n", temp
);
1228 #endif /* DEVELOPMENT || DEBUG */
1230 kr
= ledger_get_balance(bank_account
->ba_bill
, bank_ledgers
.energy
, &temp
);
1231 if (kr
== KERN_SUCCESS
&& temp
>= 0) {
1232 energy_balance
+= temp
;
1235 lck_mtx_unlock(&bank_task
->bt_acc_to_pay_lock
);
1236 *cpu_time
= (uint64_t)cpu_balance
;
1237 *energy
= (uint64_t)energy_balance
;
1241 /* Routine: bank_serviced_balance_safe
1242 * Purpose: Walk through the bank accounts billed to other tasks by me and get the current balance to be charged.
1243 * Called from another task. It takes global bank task lock to make sure the bank context is
1244 not deallocated while accesing it.
1245 * Returns: cpu balance and energy balance in out paremeters.
1248 bank_serviced_balance_safe(task_t task
, uint64_t *cpu_time
, uint64_t *energy
)
1250 bank_task_t bank_task
= BANK_TASK_NULL
;
1251 ledger_amount_t credit
, debit
;
1252 uint64_t cpu_balance
= 0;
1253 uint64_t energy_balance
= 0;
1256 /* Task might be in exec, grab the global bank task lock before accessing bank context. */
1257 global_bank_task_lock();
1258 /* Grab a reference on bank context */
1259 if (task
->bank_context
!= NULL
) {
1260 bank_task
= task
->bank_context
;
1261 bank_task_reference(bank_task
);
1263 global_bank_task_unlock();
1266 bank_serviced_balance(bank_task
, &cpu_balance
, &energy_balance
);
1267 bank_task_dealloc(bank_task
, 1);
1269 kr
= ledger_get_entries(task
->ledger
, task_ledgers
.cpu_time_billed_to_others
,
1271 if (kr
== KERN_SUCCESS
) {
1272 cpu_balance
= credit
- debit
;
1275 kr
= ledger_get_entries(task
->ledger
, task_ledgers
.energy_billed_to_others
,
1277 if (kr
== KERN_SUCCESS
) {
1278 energy_balance
= credit
- debit
;
1282 *cpu_time
= cpu_balance
;
1283 *energy
= energy_balance
;
1288 * Routine: bank_serviced_balance
1289 * Purpose: Walk through the Account need to charge account list and get the current balance to be charged.
1290 * Returns: cpu balance and energy balance in out paremeters.
1293 bank_serviced_balance(bank_task_t bank_task
, uint64_t *cpu_time
, uint64_t *energy
)
1295 int64_t cpu_balance
= 0;
1296 int64_t energy_balance
= 0;
1297 bank_account_t bank_account
;
1300 if (bank_task
== BANK_TASK_NULL
) {
1306 lck_mtx_lock(&bank_task
->bt_acc_to_charge_lock
);
1308 kr
= ledger_get_balance(bank_task
->bt_creditcard
, task_ledgers
.cpu_time_billed_to_others
, &temp
);
1309 if (kr
== KERN_SUCCESS
&& temp
>= 0) {
1310 cpu_balance
+= temp
;
1312 #if DEVELOPMENT || DEBUG
1314 printf("bank_serviced_time: ledger_get_balance failed or negative balance in ledger: %lld\n", temp
);
1316 #endif /* DEVELOPMENT || DEBUG */
1318 kr
= ledger_get_balance(bank_task
->bt_creditcard
, task_ledgers
.energy_billed_to_others
, &temp
);
1319 if (kr
== KERN_SUCCESS
&& temp
>= 0) {
1320 energy_balance
+= temp
;
1323 queue_iterate(&bank_task
->bt_accounts_to_charge
, bank_account
, bank_account_t
, ba_next_acc_to_charge
) {
1325 kr
= ledger_get_balance(bank_account
->ba_bill
, bank_ledgers
.cpu_time
, &temp
);
1326 if (kr
== KERN_SUCCESS
&& temp
>= 0) {
1327 cpu_balance
+= temp
;
1329 #if DEVELOPMENT || DEBUG
1331 printf("bank_serviced_time: ledger_get_balance failed or negative balance in ledger: %lld\n", temp
);
1333 #endif /* DEVELOPMENT || DEBUG */
1335 kr
= ledger_get_balance(bank_account
->ba_bill
, bank_ledgers
.energy
, &temp
);
1336 if (kr
== KERN_SUCCESS
&& temp
>= 0) {
1337 energy_balance
+= temp
;
1340 lck_mtx_unlock(&bank_task
->bt_acc_to_charge_lock
);
1341 *cpu_time
= (uint64_t)cpu_balance
;
1342 *energy
= (uint64_t)energy_balance
;
1347 * Routine: bank_get_voucher_bank_account
1348 * Purpose: Get the bank account from the voucher.
1349 * Returns: bank_account if bank_account attribute present in voucher.
1350 * NULL on no attribute, no bank_element, or if holder and merchant bank accounts
1351 * and voucher thread group and current thread group are the same.
1353 static bank_account_t
1354 bank_get_voucher_bank_account(ipc_voucher_t voucher
)
1356 bank_element_t bank_element
= BANK_ELEMENT_NULL
;
1357 bank_account_t bank_account
= BANK_ACCOUNT_NULL
;
1358 mach_voucher_attr_value_handle_t vals
[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
];
1359 mach_voucher_attr_value_handle_array_size_t val_count
;
1362 val_count
= MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
;
1363 kr
= mach_voucher_attr_control_get_values(bank_voucher_attr_control
,
1368 if (kr
!= KERN_SUCCESS
|| val_count
== 0)
1369 return BANK_ACCOUNT_NULL
;
1371 bank_element
= HANDLE_TO_BANK_ELEMENT(vals
[0]);
1372 if (bank_element
== BANK_DEFAULT_VALUE
)
1373 return BANK_ACCOUNT_NULL
;
1374 if (bank_element
== BANK_DEFAULT_TASK_VALUE
)
1375 bank_element
= CAST_TO_BANK_ELEMENT(get_bank_task_context(current_task(), FALSE
));
1377 if (bank_element
->be_type
== BANK_TASK
) {
1378 return BANK_ACCOUNT_NULL
;
1379 } else if (bank_element
->be_type
== BANK_ACCOUNT
) {
1380 bank_account
= CAST_TO_BANK_ACCOUNT(bank_element
);
1382 * Return BANK_ACCOUNT_NULL if the ba_holder is same as ba_merchant
1383 * and bank account thread group is same as current thread group
1384 * i.e. ba_merchant's thread group.
1386 * The bank account might have ba_holder same as ba_merchant but different
1387 * thread group if daemon sends a voucher to an App and then App sends the
1388 * same voucher back to the daemon (IPC code will replace thread group in the
1389 * voucher to App's thread group when it gets auto redeemed by the App).
1391 if (bank_account
->ba_holder
!= bank_account
->ba_merchant
||
1392 bank_get_bank_account_thread_group(bank_account
) !=
1393 bank_get_bank_task_thread_group(bank_account
->ba_merchant
)) {
1394 return bank_account
;
1396 return BANK_ACCOUNT_NULL
;
1399 panic("Bogus bank type: %d passed in bank_get_voucher_bank_account\n", bank_element
->be_type
);
1401 return BANK_ACCOUNT_NULL
;
1405 * Routine: bank_get_bank_account_ledger
1406 * Purpose: Get the bankledger from the bank account if ba_merchant different than ba_holder
1409 bank_get_bank_account_ledger(bank_account_t bank_account
)
1411 ledger_t bankledger
= NULL
;
1413 if (bank_account
!= BANK_ACCOUNT_NULL
&&
1414 bank_account
->ba_holder
!= bank_account
->ba_merchant
)
1415 bankledger
= bank_account
->ba_bill
;
1417 return (bankledger
);
1421 * Routine: bank_get_bank_task_thread_group
1422 * Purpose: Get the bank task's thread group from the bank task
1424 static struct thread_group
*
1425 bank_get_bank_task_thread_group(bank_task_t bank_task __unused
)
1427 struct thread_group
*banktg
= NULL
;
1434 * Routine: bank_get_bank_account_thread_group
1435 * Purpose: Get the bank account's thread group from the bank account
1437 static struct thread_group
*
1438 bank_get_bank_account_thread_group(bank_account_t bank_account __unused
)
1440 thread_group_t banktg
= NULL
;
1447 * Routine: bank_get_bank_ledger_and_thread_group
1448 * Purpose: Get the bankledger (chit) and thread group from the voucher.
1449 * Returns: bankledger and thread group if bank_account attribute present in voucher.
1453 bank_get_bank_ledger_and_thread_group(
1454 ipc_voucher_t voucher
,
1455 ledger_t
*bankledger
,
1456 thread_group_t
*banktg
)
1458 bank_account_t bank_account
;
1459 struct thread_group
*thread_group
= NULL
;
1461 bank_account
= bank_get_voucher_bank_account(voucher
);
1462 *bankledger
= bank_get_bank_account_ledger(bank_account
);
1463 thread_group
= bank_get_bank_account_thread_group(bank_account
);
1465 /* Return NULL thread group if voucher has current task's thread group */
1466 if (thread_group
== bank_get_bank_task_thread_group(
1467 get_bank_task_context(current_task(), FALSE
))) {
1468 thread_group
= NULL
;
1470 *banktg
= thread_group
;
1471 return KERN_SUCCESS
;
1475 * Routine: bank_swap_thread_bank_ledger
1476 * Purpose: swap the bank ledger on the thread.
1478 * Note: Should be only called for current thread or thread which is not started.
1481 bank_swap_thread_bank_ledger(thread_t thread __unused
, ledger_t new_ledger __unused
)
1484 processor_t processor
;
1485 ledger_t old_ledger
= thread
->t_bankledger
;
1486 int64_t ctime
, effective_ledger_time_consumed
= 0;
1487 int64_t remainder
= 0, consumed
= 0;
1488 int64_t effective_energy_consumed
= 0;
1489 uint64_t thread_energy
;
1491 if (old_ledger
== NULL
&& new_ledger
== NULL
)
1494 assert((thread
== current_thread() || thread
->started
== 0));
1497 thread_lock(thread
);
1500 * Calculation of time elapsed by the thread before voucher swap.
1501 * Following is the timeline which shows all the variables used in the calculation below.
1505 * |<- consumed ->|<- remainder ->|
1506 * timeline ----------------------------------------------------------------->
1508 * thread_dispatch ctime quantum end
1510 * |<-effective_ledger_time -> |
1511 * deduct_bank_ledger_time
1514 ctime
= mach_absolute_time();
1515 processor
= thread
->last_processor
;
1516 if (processor
!= NULL
) {
1517 if ((int64_t)processor
->quantum_end
> ctime
)
1518 remainder
= (int64_t)processor
->quantum_end
- ctime
;
1520 consumed
= thread
->quantum_remaining
- remainder
;
1521 effective_ledger_time_consumed
= consumed
- thread
->t_deduct_bank_ledger_time
;
1524 thread
->t_deduct_bank_ledger_time
= consumed
;
1526 thread_energy
= ml_energy_stat(thread
);
1527 effective_energy_consumed
=
1528 thread_energy
- thread
->t_deduct_bank_ledger_energy
;
1529 assert(effective_energy_consumed
>= 0);
1530 thread
->t_deduct_bank_ledger_energy
= thread_energy
;
1532 thread
->t_bankledger
= new_ledger
;
1534 thread_unlock(thread
);
1537 if (old_ledger
!= NULL
) {
1538 ledger_credit(old_ledger
,
1539 bank_ledgers
.cpu_time
,
1540 effective_ledger_time_consumed
);
1541 ledger_credit(old_ledger
,
1542 bank_ledgers
.energy
,
1543 effective_energy_consumed
);