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>
48 static zone_t bank_task_zone
, bank_account_zone
;
49 #define MAX_BANK_TASK (CONFIG_TASK_MAX)
50 #define MAX_BANK_ACCOUNT (CONFIG_TASK_MAX + CONFIG_THREAD_MAX)
52 #define BANK_ELEMENT_TO_HANDLE(x) (CAST_DOWN(bank_handle_t, (x)))
53 #define HANDLE_TO_BANK_ELEMENT(x) (CAST_DOWN(bank_element_t, (x)))
55 /* Need macro since bank_element_t is 4 byte aligned on release kernel and direct type case gives compilation error */
56 #define CAST_TO_BANK_ELEMENT(x) ((bank_element_t)((void *)(x)))
57 #define CAST_TO_BANK_TASK(x) ((bank_task_t)((void *)(x)))
58 #define CAST_TO_BANK_ACCOUNT(x) ((bank_account_t)((void *)(x)))
60 ipc_voucher_attr_control_t bank_voucher_attr_control
; /* communication channel from ATM to voucher system */
62 #if DEVELOPMENT || DEBUG
63 queue_head_t bank_tasks_list
;
64 queue_head_t bank_accounts_list
;
67 static ledger_template_t bank_ledger_template
= NULL
;
68 struct _bank_ledger_indices bank_ledgers
= { -1, -1 };
70 static bank_task_t
bank_task_alloc_init(task_t task
);
71 static bank_account_t
bank_account_alloc_init(bank_task_t bank_holder
, bank_task_t bank_merchant
,
72 bank_task_t bank_secureoriginator
, bank_task_t bank_proximateprocess
);
73 static bank_task_t
get_bank_task_context(task_t task
, boolean_t initialize
);
74 static void bank_task_dealloc(bank_task_t bank_task
, mach_voucher_attr_value_reference_t sync
);
75 static kern_return_t
bank_account_dealloc_with_sync(bank_account_t bank_account
, mach_voucher_attr_value_reference_t sync
);
76 static void bank_rollup_chit_to_tasks(ledger_t bill
, bank_task_t bank_holder
, bank_task_t bank_merchant
);
77 static void init_bank_ledgers(void);
78 static boolean_t
bank_task_is_propagate_entitled(task_t t
);
80 static lck_spin_t g_bank_task_lock_data
; /* lock to protect task->bank_context transition */
82 #define global_bank_task_lock_init() \
83 lck_spin_init(&g_bank_task_lock_data, &bank_lock_grp, &bank_lock_attr)
84 #define global_bank_task_lock_destroy() \
85 lck_spin_destroy(&g_bank_task_lock_data, &bank_lock_grp)
86 #define global_bank_task_lock() \
87 lck_spin_lock(&g_bank_task_lock_data)
88 #define global_bank_task_lock_try() \
89 lck_spin_try_lock(&g_bank_task_lock_data)
90 #define global_bank_task_unlock() \
91 lck_spin_unlock(&g_bank_task_lock_data)
93 extern uint64_t proc_uniqueid(void *p
);
94 extern int32_t proc_pid(void *p
);
95 extern int32_t proc_pidversion(void *p
);
96 extern uint32_t proc_persona_id(void *p
);
97 extern uint32_t proc_getuid(void *p
);
98 extern uint32_t proc_getgid(void *p
);
99 extern void proc_getexecutableuuid(void *p
, unsigned char *uuidbuf
, unsigned long size
);
100 extern int kauth_cred_issuser(void *cred
);
101 extern void* kauth_cred_get(void);
106 ipc_voucher_attr_manager_t __assert_only manager
,
107 mach_voucher_attr_key_t __assert_only key
,
108 mach_voucher_attr_value_handle_t value
,
109 mach_voucher_attr_value_reference_t sync
);
113 ipc_voucher_attr_manager_t __assert_only manager
,
114 mach_voucher_attr_key_t __assert_only key
,
115 mach_voucher_attr_recipe_command_t command
,
116 mach_voucher_attr_value_handle_array_t prev_values
,
117 mach_msg_type_number_t __assert_only prev_value_count
,
118 mach_voucher_attr_content_t recipe
,
119 mach_voucher_attr_content_size_t recipe_size
,
120 mach_voucher_attr_value_handle_t
*out_value
,
121 mach_voucher_attr_value_flags_t
*out_flags
,
122 ipc_voucher_t
*out_value_voucher
);
125 bank_extract_content(
126 ipc_voucher_attr_manager_t __assert_only manager
,
127 mach_voucher_attr_key_t __assert_only key
,
128 mach_voucher_attr_value_handle_array_t values
,
129 mach_msg_type_number_t value_count
,
130 mach_voucher_attr_recipe_command_t
*out_command
,
131 mach_voucher_attr_content_t out_recipe
,
132 mach_voucher_attr_content_size_t
*in_out_recipe_size
);
136 ipc_voucher_attr_manager_t __assert_only manager
,
137 mach_voucher_attr_key_t __assert_only key
,
138 mach_voucher_attr_value_handle_array_t values
,
139 mach_msg_type_number_t value_count
,
140 mach_voucher_attr_command_t command
,
141 mach_voucher_attr_content_t in_content
,
142 mach_voucher_attr_content_size_t in_content_size
,
143 mach_voucher_attr_content_t out_content
,
144 mach_voucher_attr_content_size_t
*in_out_content_size
);
147 bank_release(ipc_voucher_attr_manager_t __assert_only manager
);
150 * communication channel from voucher system to ATM
152 struct ipc_voucher_attr_manager bank_manager
= {
153 .ivam_release_value
= bank_release_value
,
154 .ivam_get_value
= bank_get_value
,
155 .ivam_extract_content
= bank_extract_content
,
156 .ivam_command
= bank_command
,
157 .ivam_release
= bank_release
,
158 .ivam_flags
= (IVAM_FLAGS_SUPPORT_SEND_PREPROCESS
| IVAM_FLAGS_SUPPORT_RECEIVE_POSTPROCESS
),
162 #if DEVELOPMENT || DEBUG
163 decl_lck_mtx_data(, bank_tasks_list_lock
);
164 decl_lck_mtx_data(, bank_accounts_list_lock
);
166 lck_grp_t bank_dev_lock_grp
;
167 lck_attr_t bank_dev_lock_attr
;
168 lck_grp_attr_t bank_dev_lock_grp_attr
;
172 * Lock group attributes for bank sub system.
174 lck_grp_t bank_lock_grp
;
175 lck_attr_t bank_lock_attr
;
176 lck_grp_attr_t bank_lock_grp_attr
;
180 * Purpose: Initialize the BANK subsystem.
186 kern_return_t kr
= KERN_SUCCESS
;
187 /* setup zones for bank_task and bank_account objects */
188 bank_task_zone
= zinit(sizeof(struct bank_task
),
189 MAX_BANK_TASK
* sizeof(struct bank_task
),
190 sizeof(struct bank_task
),
193 bank_account_zone
= zinit(sizeof(struct bank_account
),
194 MAX_BANK_ACCOUNT
* sizeof(struct bank_account
),
195 sizeof(struct bank_account
),
200 /* Initialize bank lock group and lock attributes. */
201 lck_grp_attr_setdefault(&bank_lock_grp_attr
);
202 lck_grp_init(&bank_lock_grp
, "bank_lock", &bank_lock_grp_attr
);
203 lck_attr_setdefault(&bank_lock_attr
);
204 global_bank_task_lock_init();
206 #if DEVELOPMENT || DEBUG
207 /* Initialize global bank development lock group and lock attributes. */
208 lck_grp_attr_setdefault(&bank_dev_lock_grp_attr
);
209 lck_grp_init(&bank_dev_lock_grp
, "bank_dev_lock", &bank_dev_lock_grp_attr
);
210 lck_attr_setdefault(&bank_dev_lock_attr
);
212 lck_mtx_init(&bank_tasks_list_lock
, &bank_dev_lock_grp
, &bank_dev_lock_attr
);
213 lck_mtx_init(&bank_accounts_list_lock
, &bank_dev_lock_grp
, &bank_dev_lock_attr
);
215 queue_init(&bank_tasks_list
);
216 queue_init(&bank_accounts_list
);
219 /* Register the bank manager with the Vouchers sub system. */
220 kr
= ipc_register_well_known_mach_voucher_attr_manager(
223 MACH_VOUCHER_ATTR_KEY_BANK
,
224 &bank_voucher_attr_control
);
225 if (kr
!= KERN_SUCCESS
)
226 panic("BANK subsystem initialization failed");
228 kprintf("BANK subsystem is initialized\n");
234 * BANK Resource Manager Routines.
239 * Routine: bank_release_value
240 * Purpose: Release a value, if sync matches the sync count in value.
241 * Returns: KERN_SUCCESS: on Successful deletion.
242 * KERN_FAILURE: if sync value does not matches.
246 ipc_voucher_attr_manager_t __assert_only manager
,
247 mach_voucher_attr_key_t __assert_only key
,
248 mach_voucher_attr_value_handle_t value
,
249 mach_voucher_attr_value_reference_t sync
)
251 bank_task_t bank_task
= BANK_TASK_NULL
;
252 bank_element_t bank_element
= BANK_ELEMENT_NULL
;
253 bank_account_t bank_account
= BANK_ACCOUNT_NULL
;
254 kern_return_t kr
= KERN_SUCCESS
;
256 assert(MACH_VOUCHER_ATTR_KEY_BANK
== key
);
257 assert(manager
== &bank_manager
);
260 bank_element
= HANDLE_TO_BANK_ELEMENT(value
);
261 /* Voucher system should never release the default or persistent value */
262 assert(bank_element
!= BANK_DEFAULT_VALUE
&& bank_element
!= BANK_DEFAULT_TASK_VALUE
);
264 if (bank_element
== BANK_DEFAULT_VALUE
|| bank_element
== BANK_DEFAULT_TASK_VALUE
) {
265 /* Return success for default and default task value */
270 if (bank_element
->be_type
== BANK_TASK
) {
271 bank_task
= CAST_TO_BANK_TASK(bank_element
);
273 /* Checking of the made ref with sync and clearing of voucher ref should be done under a lock */
274 lck_mtx_lock(&bank_task
->bt_acc_to_pay_lock
);
275 if (bank_task
->bt_made
!= sync
) {
276 lck_mtx_unlock(&bank_task
->bt_acc_to_pay_lock
);
280 bank_task_made_release_num(bank_task
, sync
);
281 assert(bank_task
->bt_voucher_ref
== 1);
282 bank_task
->bt_voucher_ref
= 0;
283 lck_mtx_unlock(&bank_task
->bt_acc_to_pay_lock
);
285 bank_task_dealloc(bank_task
, 1);
286 } else if (bank_element
->be_type
== BANK_ACCOUNT
) {
287 bank_account
= CAST_TO_BANK_ACCOUNT(bank_element
);
288 kr
= bank_account_dealloc_with_sync(bank_account
, sync
);
290 panic("Bogus bank type: %d passed in get_value\n", bank_element
->be_type
);
298 * Routine: bank_get_value
302 ipc_voucher_attr_manager_t __assert_only manager
,
303 mach_voucher_attr_key_t __assert_only key
,
304 mach_voucher_attr_recipe_command_t command
,
305 mach_voucher_attr_value_handle_array_t prev_values
,
306 mach_msg_type_number_t prev_value_count
,
307 mach_voucher_attr_content_t __unused recipe
,
308 mach_voucher_attr_content_size_t __unused recipe_size
,
309 mach_voucher_attr_value_handle_t
*out_value
,
310 mach_voucher_attr_value_flags_t
*out_flags
,
311 ipc_voucher_t
*out_value_voucher
)
313 bank_task_t bank_task
= BANK_TASK_NULL
;
314 bank_task_t bank_holder
= BANK_TASK_NULL
;
315 bank_task_t bank_merchant
= BANK_TASK_NULL
;
316 bank_task_t bank_secureoriginator
= BANK_TASK_NULL
;
317 bank_task_t bank_proximateprocess
= BANK_TASK_NULL
;
318 bank_element_t bank_element
= BANK_ELEMENT_NULL
;
319 bank_account_t bank_account
= BANK_ACCOUNT_NULL
;
320 bank_account_t old_bank_account
= BANK_ACCOUNT_NULL
;
321 mach_voucher_attr_value_handle_t bank_handle
;
323 kern_return_t kr
= KERN_SUCCESS
;
324 mach_msg_type_number_t i
;
326 assert(MACH_VOUCHER_ATTR_KEY_BANK
== key
);
327 assert(manager
== &bank_manager
);
329 /* never an out voucher */
330 *out_value_voucher
= IPC_VOUCHER_NULL
;
331 *out_flags
= MACH_VOUCHER_ATTR_VALUE_FLAGS_NONE
;
335 case MACH_VOUCHER_ATTR_BANK_CREATE
:
337 /* Return the default task value instead of bank task */
338 *out_value
= BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_TASK_VALUE
);
339 *out_flags
= MACH_VOUCHER_ATTR_VALUE_FLAGS_PERSIST
;
342 case MACH_VOUCHER_ATTR_AUTO_REDEEM
:
344 for (i
= 0; i
< prev_value_count
; i
++) {
345 bank_handle
= prev_values
[i
];
346 bank_element
= HANDLE_TO_BANK_ELEMENT(bank_handle
);
348 /* Should not have received default task value from an IPC */
349 if (bank_element
== BANK_DEFAULT_VALUE
|| bank_element
== BANK_DEFAULT_TASK_VALUE
)
352 task
= current_task();
353 if (bank_element
->be_type
== BANK_TASK
) {
354 bank_holder
= CAST_TO_BANK_TASK(bank_element
);
355 bank_secureoriginator
= bank_holder
;
356 bank_proximateprocess
= bank_holder
;
357 } else if (bank_element
->be_type
== BANK_ACCOUNT
) {
358 old_bank_account
= CAST_TO_BANK_ACCOUNT(bank_element
);
359 bank_holder
= old_bank_account
->ba_holder
;
360 bank_secureoriginator
= old_bank_account
->ba_secureoriginator
;
361 bank_proximateprocess
= old_bank_account
->ba_proximateprocess
;
363 panic("Bogus bank type: %d passed in get_value\n", bank_element
->be_type
);
366 bank_merchant
= get_bank_task_context(task
, FALSE
);
367 if (bank_merchant
== BANK_TASK_NULL
)
368 return KERN_RESOURCE_SHORTAGE
;
370 /* Check if trying to redeem for self task, return the default bank task */
371 if (bank_holder
== bank_merchant
&&
372 bank_holder
== bank_secureoriginator
&&
373 bank_holder
== bank_proximateprocess
) {
374 *out_value
= BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_TASK_VALUE
);
375 *out_flags
= MACH_VOUCHER_ATTR_VALUE_FLAGS_PERSIST
;
379 bank_account
= bank_account_alloc_init(bank_holder
, bank_merchant
,
380 bank_secureoriginator
, bank_proximateprocess
);
381 if (bank_account
== BANK_ACCOUNT_NULL
)
382 return KERN_RESOURCE_SHORTAGE
;
384 *out_value
= BANK_ELEMENT_TO_HANDLE(bank_account
);
388 *out_value
= BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_VALUE
);
391 case MACH_VOUCHER_ATTR_SEND_PREPROCESS
:
393 for (i
= 0; i
< prev_value_count
; i
++) {
394 bank_handle
= prev_values
[i
];
395 bank_element
= HANDLE_TO_BANK_ELEMENT(bank_handle
);
397 if (bank_element
== BANK_DEFAULT_VALUE
)
400 task
= current_task();
401 if (bank_element
== BANK_DEFAULT_TASK_VALUE
) {
402 bank_element
= CAST_TO_BANK_ELEMENT(get_bank_task_context(task
, FALSE
));
405 if (bank_element
->be_type
== BANK_TASK
) {
406 bank_holder
= CAST_TO_BANK_TASK(bank_element
);
407 bank_secureoriginator
= bank_holder
;
408 } else if (bank_element
->be_type
== BANK_ACCOUNT
) {
409 old_bank_account
= CAST_TO_BANK_ACCOUNT(bank_element
);
410 bank_holder
= old_bank_account
->ba_holder
;
411 bank_secureoriginator
= old_bank_account
->ba_secureoriginator
;
413 panic("Bogus bank type: %d passed in get_value\n", bank_element
->be_type
);
416 bank_merchant
= get_bank_task_context(task
, FALSE
);
417 if (bank_merchant
== BANK_TASK_NULL
)
418 return KERN_RESOURCE_SHORTAGE
;
421 * If the process doesn't have secure persona entitlement,
422 * then replace the secure originator to current task.
424 if (bank_merchant
->bt_hasentitlement
== 0) {
425 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
,
426 (BANK_CODE(BANK_ACCOUNT_INFO
, (BANK_SECURE_ORIGINATOR_CHANGED
))) | DBG_FUNC_NONE
,
427 bank_secureoriginator
->bt_pid
, bank_merchant
->bt_pid
, 0, 0, 0);
428 bank_secureoriginator
= bank_merchant
;
431 bank_proximateprocess
= bank_merchant
;
433 /* Check if trying to redeem for self task, return the bank task */
434 if (bank_holder
== bank_merchant
&&
435 bank_holder
== bank_secureoriginator
&&
436 bank_holder
== bank_proximateprocess
) {
438 lck_mtx_lock(&bank_holder
->bt_acc_to_pay_lock
);
439 bank_task_made_reference(bank_holder
);
440 if (bank_holder
->bt_voucher_ref
== 0) {
441 /* Take a ref for voucher system, if voucher system does not have a ref */
442 bank_task_reference(bank_holder
);
443 bank_holder
->bt_voucher_ref
= 1;
445 lck_mtx_unlock(&bank_holder
->bt_acc_to_pay_lock
);
447 *out_value
= BANK_ELEMENT_TO_HANDLE(bank_holder
);
450 bank_account
= bank_account_alloc_init(bank_holder
, bank_merchant
,
451 bank_secureoriginator
, bank_proximateprocess
);
452 if (bank_account
== BANK_ACCOUNT_NULL
)
453 return KERN_RESOURCE_SHORTAGE
;
455 *out_value
= BANK_ELEMENT_TO_HANDLE(bank_account
);
459 *out_value
= BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_VALUE
);
462 case MACH_VOUCHER_ATTR_REDEEM
:
464 for (i
= 0; i
< prev_value_count
; i
++) {
465 bank_handle
= prev_values
[i
];
466 bank_element
= HANDLE_TO_BANK_ELEMENT(bank_handle
);
468 if (bank_element
== BANK_DEFAULT_VALUE
)
471 task
= current_task();
472 if (bank_element
== BANK_DEFAULT_TASK_VALUE
) {
473 *out_value
= BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_TASK_VALUE
);
474 *out_flags
= MACH_VOUCHER_ATTR_VALUE_FLAGS_PERSIST
;
477 if (bank_element
->be_type
== BANK_TASK
) {
478 bank_task
= CAST_TO_BANK_TASK(bank_element
);
479 panic("Found a bank task in MACH_VOUCHER_ATTR_REDEEM: %p", bank_task
);
483 } else if (bank_element
->be_type
== BANK_ACCOUNT
) {
484 bank_account
= CAST_TO_BANK_ACCOUNT(bank_element
);
485 bank_merchant
= bank_account
->ba_merchant
;
486 if (bank_merchant
!= get_bank_task_context(task
, FALSE
)) {
487 panic("Found another bank task: %p as a bank merchant\n", bank_merchant
);
490 bank_account_made_reference(bank_account
);
491 *out_value
= BANK_ELEMENT_TO_HANDLE(bank_account
);
494 panic("Bogus bank type: %d passed in get_value\n", bank_element
->be_type
);
498 *out_value
= BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_VALUE
);
502 kr
= KERN_INVALID_ARGUMENT
;
511 * Routine: bank_extract_content
512 * Purpose: Extract a set of aid from an array of voucher values.
513 * Returns: KERN_SUCCESS: on Success.
514 * KERN_FAILURE: one of the value is not present in the hash.
515 * KERN_NO_SPACE: insufficeint buffer provided to fill an array of aid.
518 bank_extract_content(
519 ipc_voucher_attr_manager_t __assert_only manager
,
520 mach_voucher_attr_key_t __assert_only key
,
521 mach_voucher_attr_value_handle_array_t values
,
522 mach_msg_type_number_t value_count
,
523 mach_voucher_attr_recipe_command_t
*out_command
,
524 mach_voucher_attr_content_t out_recipe
,
525 mach_voucher_attr_content_size_t
*in_out_recipe_size
)
527 bank_task_t bank_task
= BANK_TASK_NULL
;
528 bank_element_t bank_element
= BANK_ELEMENT_NULL
;
529 bank_account_t bank_account
= BANK_ACCOUNT_NULL
;
530 mach_voucher_attr_value_handle_t bank_handle
;
531 char buf
[MACH_VOUCHER_BANK_CONTENT_SIZE
];
532 mach_msg_type_number_t i
;
534 assert(MACH_VOUCHER_ATTR_KEY_BANK
== key
);
535 assert(manager
== &bank_manager
);
537 for (i
= 0; i
< value_count
; i
++) {
538 bank_handle
= values
[i
];
539 bank_element
= HANDLE_TO_BANK_ELEMENT(bank_handle
);
540 if (bank_element
== BANK_DEFAULT_VALUE
)
543 if (bank_element
== BANK_DEFAULT_TASK_VALUE
) {
544 bank_element
= CAST_TO_BANK_ELEMENT(get_bank_task_context(current_task(), FALSE
));
547 if (MACH_VOUCHER_BANK_CONTENT_SIZE
> *in_out_recipe_size
) {
548 *in_out_recipe_size
= 0;
549 return KERN_NO_SPACE
;
552 if (bank_element
->be_type
== BANK_TASK
) {
553 bank_task
= CAST_TO_BANK_TASK(bank_element
);
554 snprintf(buf
, MACH_VOUCHER_BANK_CONTENT_SIZE
,
555 " Bank Context for a pid %d\n", bank_task
->bt_pid
);
556 } else if (bank_element
->be_type
== BANK_ACCOUNT
) {
557 bank_account
= CAST_TO_BANK_ACCOUNT(bank_element
);
558 snprintf(buf
, MACH_VOUCHER_BANK_CONTENT_SIZE
,
559 " Bank Account linking holder pid %d with merchant pid %d, originator PID/persona: %d, %u and proximate PID/persona: %d, %u\n",
560 bank_account
->ba_holder
->bt_pid
,
561 bank_account
->ba_merchant
->bt_pid
,
562 bank_account
->ba_secureoriginator
->bt_pid
,
563 bank_account
->ba_secureoriginator
->bt_persona_id
,
564 bank_account
->ba_proximateprocess
->bt_pid
,
565 bank_account
->ba_proximateprocess
->bt_persona_id
);
567 panic("Bogus bank type: %d passed in get_value\n", bank_element
->be_type
);
571 memcpy(&out_recipe
[0], buf
, strlen(buf
) + 1);
572 *out_command
= MACH_VOUCHER_ATTR_BANK_NULL
;
573 *in_out_recipe_size
= (mach_voucher_attr_content_size_t
)strlen(buf
) + 1;
581 * Routine: bank_command
582 * Purpose: Execute a command against a set of ATM values.
583 * Returns: KERN_SUCCESS: On successful execution of command.
584 KERN_FAILURE: On failure.
588 ipc_voucher_attr_manager_t __assert_only manager
,
589 mach_voucher_attr_key_t __assert_only key
,
590 mach_voucher_attr_value_handle_array_t __unused values
,
591 mach_msg_type_number_t __unused value_count
,
592 mach_voucher_attr_command_t __unused command
,
593 mach_voucher_attr_content_t __unused in_content
,
594 mach_voucher_attr_content_size_t __unused in_content_size
,
595 mach_voucher_attr_content_t __unused out_content
,
596 mach_voucher_attr_content_size_t __unused
*out_content_size
)
598 bank_task_t bank_task
= BANK_TASK_NULL
;
599 bank_task_t bank_secureoriginator
= BANK_TASK_NULL
;
600 bank_task_t bank_proximateprocess
= BANK_TASK_NULL
;
601 struct persona_token
*token
= NULL
;
602 bank_element_t bank_element
= BANK_ELEMENT_NULL
;
603 bank_account_t bank_account
= BANK_ACCOUNT_NULL
;
604 mach_voucher_attr_value_handle_t bank_handle
;
605 mach_msg_type_number_t i
;
608 assert(MACH_VOUCHER_ATTR_KEY_BANK
== key
);
609 assert(manager
== &bank_manager
);
612 case BANK_ORIGINATOR_PID
:
614 if ((sizeof(pid
)) > *out_content_size
) {
615 *out_content_size
= 0;
616 return KERN_NO_SPACE
;
619 for (i
= 0; i
< value_count
; i
++) {
620 bank_handle
= values
[i
];
621 bank_element
= HANDLE_TO_BANK_ELEMENT(bank_handle
);
622 if (bank_element
== BANK_DEFAULT_VALUE
)
625 if (bank_element
== BANK_DEFAULT_TASK_VALUE
) {
626 bank_element
= CAST_TO_BANK_ELEMENT(get_bank_task_context(current_task(), FALSE
));
629 if (bank_element
->be_type
== BANK_TASK
) {
630 bank_task
= CAST_TO_BANK_TASK(bank_element
);
631 } else if (bank_element
->be_type
== BANK_ACCOUNT
) {
632 bank_account
= CAST_TO_BANK_ACCOUNT(bank_element
);
633 bank_task
= bank_account
->ba_holder
;
635 panic("Bogus bank type: %d passed in voucher_command\n", bank_element
->be_type
);
637 pid
= bank_task
->bt_pid
;
639 memcpy(&out_content
[0], &pid
, sizeof(pid
));
640 *out_content_size
= (mach_voucher_attr_content_size_t
)sizeof(pid
);
643 /* In the case of no value, return error KERN_INVALID_VALUE */
644 *out_content_size
= 0;
645 return KERN_INVALID_VALUE
;
647 case BANK_PERSONA_TOKEN
:
649 if ((sizeof(struct persona_token
)) > *out_content_size
) {
650 *out_content_size
= 0;
651 return KERN_NO_SPACE
;
653 for (i
= 0; i
< value_count
; i
++) {
654 bank_handle
= values
[i
];
655 bank_element
= HANDLE_TO_BANK_ELEMENT(bank_handle
);
656 if (bank_element
== BANK_DEFAULT_VALUE
)
659 if (bank_element
== BANK_DEFAULT_TASK_VALUE
) {
660 bank_element
= CAST_TO_BANK_ELEMENT(get_bank_task_context(current_task(), FALSE
));
663 if (bank_element
->be_type
== BANK_TASK
) {
664 *out_content_size
= 0;
665 return KERN_INVALID_OBJECT
;
666 } else if (bank_element
->be_type
== BANK_ACCOUNT
) {
667 bank_account
= CAST_TO_BANK_ACCOUNT(bank_element
);
668 bank_secureoriginator
= bank_account
->ba_secureoriginator
;
669 bank_proximateprocess
= bank_account
->ba_proximateprocess
;
671 panic("Bogus bank type: %d passed in voucher_command\n", bank_element
->be_type
);
673 token
= (struct persona_token
*)(void *)&out_content
[0];
674 memcpy(&token
->originator
, &bank_secureoriginator
->bt_proc_persona
, sizeof(struct proc_persona_info
));
675 memcpy(&token
->proximate
, &bank_proximateprocess
->bt_proc_persona
, sizeof(struct proc_persona_info
));
677 *out_content_size
= (mach_voucher_attr_content_size_t
)sizeof(*token
);
680 /* In the case of no value, return error KERN_INVALID_VALUE */
681 *out_content_size
= 0;
682 return KERN_INVALID_VALUE
;
685 return KERN_INVALID_ARGUMENT
;
693 ipc_voucher_attr_manager_t __assert_only manager
)
695 assert(manager
== &bank_manager
);
701 * Bank Internal Routines.
705 * Routine: bank_task_alloc_init
706 * Purpose: Allocate and initialize a bank task structure.
707 * Returns: bank_task_t on Success.
708 * BANK_TASK_NULL: on Failure.
709 * Notes: Leaves the task and creditcard blank and has only 1 ref,
710 needs to take 1 extra ref after the task field is initialized.
713 bank_task_alloc_init(task_t task
)
715 bank_task_t new_bank_task
;
717 new_bank_task
= (bank_task_t
) zalloc(bank_task_zone
);
718 if (new_bank_task
== BANK_TASK_NULL
)
719 return BANK_TASK_NULL
;
721 new_bank_task
->bt_type
= BANK_TASK
;
722 new_bank_task
->bt_voucher_ref
= 0;
723 new_bank_task
->bt_refs
= 1;
724 new_bank_task
->bt_made
= 0;
725 new_bank_task
->bt_creditcard
= NULL
;
726 new_bank_task
->bt_hasentitlement
= bank_task_is_propagate_entitled(task
);
727 queue_init(&new_bank_task
->bt_accounts_to_pay
);
728 queue_init(&new_bank_task
->bt_accounts_to_charge
);
729 lck_mtx_init(&new_bank_task
->bt_acc_to_pay_lock
, &bank_lock_grp
, &bank_lock_attr
);
730 lck_mtx_init(&new_bank_task
->bt_acc_to_charge_lock
, &bank_lock_grp
, &bank_lock_attr
);
733 * Initialize the persona_id struct
735 bzero(&new_bank_task
->bt_proc_persona
, sizeof(new_bank_task
->bt_proc_persona
));
736 new_bank_task
->bt_flags
= 0;
737 new_bank_task
->bt_unique_pid
= proc_uniqueid(task
->bsd_info
);
738 new_bank_task
->bt_pid
= proc_pid(task
->bsd_info
);
739 new_bank_task
->bt_pidversion
= proc_pidversion(task
->bsd_info
);
740 new_bank_task
->bt_persona_id
= proc_persona_id(task
->bsd_info
);
741 new_bank_task
->bt_uid
= proc_getuid(task
->bsd_info
);
742 new_bank_task
->bt_gid
= proc_getgid(task
->bsd_info
);
743 proc_getexecutableuuid(task
->bsd_info
, new_bank_task
->bt_macho_uuid
, sizeof(new_bank_task
->bt_macho_uuid
));
745 #if DEVELOPMENT || DEBUG
746 new_bank_task
->bt_task
= NULL
;
747 lck_mtx_lock(&bank_tasks_list_lock
);
748 queue_enter(&bank_tasks_list
, new_bank_task
, bank_task_t
, bt_global_elt
);
749 lck_mtx_unlock(&bank_tasks_list_lock
);
751 return (new_bank_task
);
755 * Routine: proc_is_propagate_entitled
756 * Purpose: Check if the process has persona propagate entitlement.
757 * Returns: TRUE if entitled.
761 bank_task_is_propagate_entitled(task_t t
)
763 /* Return TRUE if root process */
764 if (0 == kauth_cred_issuser(kauth_cred_get())) {
765 /* If it's a non-root process, it needs to have the entitlement for secure originator propagation */
766 boolean_t entitled
= FALSE
;
767 entitled
= IOTaskHasEntitlement(t
, ENTITLEMENT_PERSONA_PROPAGATE
);
775 * Routine: bank_account_alloc_init
776 * Purpose: Allocate and Initialize the bank account struct.
777 * Returns: bank_account_t : On Success.
778 * BANK_ACCOUNT_NULL: On Failure.
780 static bank_account_t
781 bank_account_alloc_init(
782 bank_task_t bank_holder
,
783 bank_task_t bank_merchant
,
784 bank_task_t bank_secureoriginator
,
785 bank_task_t bank_proximateprocess
)
787 bank_account_t new_bank_account
;
788 bank_account_t bank_account
;
789 boolean_t entry_found
= FALSE
;
790 ledger_t new_ledger
= ledger_instantiate(bank_ledger_template
, LEDGER_CREATE_INACTIVE_ENTRIES
);
792 if (new_ledger
== NULL
)
793 return BANK_ACCOUNT_NULL
;
795 ledger_entry_setactive(new_ledger
, bank_ledgers
.cpu_time
);
796 ledger_entry_setactive(new_ledger
, bank_ledgers
.energy
);
797 new_bank_account
= (bank_account_t
) zalloc(bank_account_zone
);
798 if (new_bank_account
== BANK_ACCOUNT_NULL
) {
799 ledger_dereference(new_ledger
);
800 return BANK_ACCOUNT_NULL
;
803 new_bank_account
->ba_type
= BANK_ACCOUNT
;
804 new_bank_account
->ba_voucher_ref
= 0;
805 new_bank_account
->ba_refs
= 1;
806 new_bank_account
->ba_made
= 1;
807 new_bank_account
->ba_bill
= new_ledger
;
808 new_bank_account
->ba_merchant
= bank_merchant
;
809 new_bank_account
->ba_holder
= bank_holder
;
810 new_bank_account
->ba_secureoriginator
= bank_secureoriginator
;
811 new_bank_account
->ba_proximateprocess
= bank_proximateprocess
;
813 /* Iterate through accounts need to pay list to find the existing entry */
814 lck_mtx_lock(&bank_holder
->bt_acc_to_pay_lock
);
815 queue_iterate(&bank_holder
->bt_accounts_to_pay
, bank_account
, bank_account_t
, ba_next_acc_to_pay
) {
816 if (bank_account
->ba_merchant
!= bank_merchant
||
817 bank_account
->ba_secureoriginator
!= bank_secureoriginator
||
818 bank_account
->ba_proximateprocess
!= bank_proximateprocess
)
822 /* Take a made ref, since this value would be returned to voucher system. */
823 bank_account_made_reference(bank_account
);
829 /* Create a linkage between the holder and the merchant task, Grab both the list locks before adding it to the list. */
830 lck_mtx_lock(&bank_merchant
->bt_acc_to_charge_lock
);
832 /* Add the account entry into Accounts need to pay account link list. */
833 queue_enter(&bank_holder
->bt_accounts_to_pay
, new_bank_account
, bank_account_t
, ba_next_acc_to_pay
);
835 /* Add the account entry into Accounts need to charge account link list. */
836 queue_enter(&bank_merchant
->bt_accounts_to_charge
, new_bank_account
, bank_account_t
, ba_next_acc_to_charge
);
838 lck_mtx_unlock(&bank_merchant
->bt_acc_to_charge_lock
);
841 lck_mtx_unlock(&bank_holder
->bt_acc_to_pay_lock
);
844 ledger_dereference(new_ledger
);
845 zfree(bank_account_zone
, new_bank_account
);
849 bank_task_reference(bank_holder
);
850 bank_task_reference(bank_merchant
);
851 bank_task_reference(bank_secureoriginator
);
852 bank_task_reference(bank_proximateprocess
);
854 #if DEVELOPMENT || DEBUG
855 new_bank_account
->ba_task
= NULL
;
856 lck_mtx_lock(&bank_accounts_list_lock
);
857 queue_enter(&bank_accounts_list
, new_bank_account
, bank_account_t
, ba_global_elt
);
858 lck_mtx_unlock(&bank_accounts_list_lock
);
861 return (new_bank_account
);
865 * Routine: get_bank_task_context
866 * Purpose: Get the bank context of the given task
867 * Returns: bank_task_t on Success.
868 * BANK_TASK_NULL: on Failure.
869 * Note: Initialize bank context if NULL.
872 get_bank_task_context
874 boolean_t initialize
)
876 bank_task_t bank_task
;
878 if (task
->bank_context
|| !initialize
) {
879 assert(task
->bank_context
!= NULL
);
880 return (task
->bank_context
);
883 bank_task
= bank_task_alloc_init(task
);
885 /* Grab the task lock and check if we won the race. */
887 if (task
->bank_context
) {
889 if (bank_task
!= BANK_TASK_NULL
)
890 bank_task_dealloc(bank_task
, 1);
891 return (task
->bank_context
);
892 } else if (bank_task
== BANK_TASK_NULL
) {
894 return BANK_TASK_NULL
;
896 /* We won the race. Take a ref on the ledger and initialize bank task. */
897 bank_task
->bt_creditcard
= task
->ledger
;
898 #if DEVELOPMENT || DEBUG
899 bank_task
->bt_task
= task
;
901 ledger_reference(task
->ledger
);
903 /* Grab the global bank task lock before setting the bank context on a task */
904 global_bank_task_lock();
905 task
->bank_context
= bank_task
;
906 global_bank_task_unlock();
914 * Routine: bank_task_dealloc
915 * Purpose: Drops the reference on bank task.
920 bank_task_t bank_task
,
921 mach_voucher_attr_value_reference_t sync
)
923 assert(bank_task
->bt_refs
>= 0);
925 if (bank_task_release_num(bank_task
, sync
) > (int)sync
)
928 assert(bank_task
->bt_refs
== 0);
929 assert(queue_empty(&bank_task
->bt_accounts_to_pay
));
930 assert(queue_empty(&bank_task
->bt_accounts_to_charge
));
932 ledger_dereference(bank_task
->bt_creditcard
);
933 lck_mtx_destroy(&bank_task
->bt_acc_to_pay_lock
, &bank_lock_grp
);
934 lck_mtx_destroy(&bank_task
->bt_acc_to_charge_lock
, &bank_lock_grp
);
937 #if DEVELOPMENT || DEBUG
938 lck_mtx_lock(&bank_tasks_list_lock
);
939 queue_remove(&bank_tasks_list
, bank_task
, bank_task_t
, bt_global_elt
);
940 lck_mtx_unlock(&bank_tasks_list_lock
);
943 zfree(bank_task_zone
, bank_task
);
947 * Routine: bank_account_dealloc_with_sync
948 * Purpose: Drop the reference on bank account if the sync matches.
949 * Returns: KERN_SUCCESS if sync matches.
950 * KERN_FAILURE on mismatch.
953 bank_account_dealloc_with_sync(
954 bank_account_t bank_account
,
955 mach_voucher_attr_value_reference_t sync
)
957 bank_task_t bank_holder
= bank_account
->ba_holder
;
958 bank_task_t bank_merchant
= bank_account
->ba_merchant
;
959 bank_task_t bank_secureoriginator
= bank_account
->ba_secureoriginator
;
960 bank_task_t bank_proximateprocess
= bank_account
->ba_proximateprocess
;
962 /* Grab the acc to pay list lock and check the sync value */
963 lck_mtx_lock(&bank_holder
->bt_acc_to_pay_lock
);
965 if (bank_account
->ba_made
!= sync
) {
966 lck_mtx_unlock(&bank_holder
->bt_acc_to_pay_lock
);
970 bank_account_made_release_num(bank_account
, sync
);
972 if (bank_account_release_num(bank_account
, 1) > 1)
973 panic("Releasing a non zero ref bank account %p\n", bank_account
);
976 /* Grab both the acc to pay and acc to charge locks */
977 lck_mtx_lock(&bank_merchant
->bt_acc_to_charge_lock
);
979 bank_rollup_chit_to_tasks(bank_account
->ba_bill
, bank_holder
, bank_merchant
);
981 /* Remove the account entry from Accounts need to pay account link list. */
982 queue_remove(&bank_holder
->bt_accounts_to_pay
, bank_account
, bank_account_t
, ba_next_acc_to_pay
);
984 /* Remove the account entry from Accounts need to charge account link list. */
985 queue_remove(&bank_merchant
->bt_accounts_to_charge
, bank_account
, bank_account_t
, ba_next_acc_to_charge
);
987 lck_mtx_unlock(&bank_merchant
->bt_acc_to_charge_lock
);
988 lck_mtx_unlock(&bank_holder
->bt_acc_to_pay_lock
);
990 ledger_dereference(bank_account
->ba_bill
);
992 /* Drop the reference of bank holder and merchant */
993 bank_task_dealloc(bank_holder
, 1);
994 bank_task_dealloc(bank_merchant
, 1);
995 bank_task_dealloc(bank_secureoriginator
, 1);
996 bank_task_dealloc(bank_proximateprocess
, 1);
998 #if DEVELOPMENT || DEBUG
999 lck_mtx_lock(&bank_accounts_list_lock
);
1000 queue_remove(&bank_accounts_list
, bank_account
, bank_account_t
, ba_global_elt
);
1001 lck_mtx_unlock(&bank_accounts_list_lock
);
1004 zfree(bank_account_zone
, bank_account
);
1005 return KERN_SUCCESS
;
1009 * Routine: bank_rollup_chit_to_tasks
1010 * Purpose: Debit and Credit holder's and merchant's ledgers.
1014 bank_rollup_chit_to_tasks(
1016 bank_task_t bank_holder
,
1017 bank_task_t bank_merchant
)
1019 ledger_amount_t credit
;
1020 ledger_amount_t debit
;
1023 if (bank_holder
== bank_merchant
)
1026 ret
= ledger_get_entries(bill
, bank_ledgers
.cpu_time
, &credit
, &debit
);
1027 if (ret
== KERN_SUCCESS
) {
1028 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
,
1029 (BANK_CODE(BANK_ACCOUNT_INFO
, (BANK_SETTLE_CPU_TIME
))) | DBG_FUNC_NONE
,
1030 bank_merchant
->bt_pid
, bank_holder
->bt_pid
, credit
, debit
, 0);
1031 ledger_credit(bank_holder
->bt_creditcard
, task_ledgers
.cpu_time_billed_to_me
, credit
);
1032 ledger_debit(bank_holder
->bt_creditcard
, task_ledgers
.cpu_time_billed_to_me
, debit
);
1034 ledger_credit(bank_merchant
->bt_creditcard
, task_ledgers
.cpu_time_billed_to_others
, credit
);
1035 ledger_debit(bank_merchant
->bt_creditcard
, task_ledgers
.cpu_time_billed_to_others
, debit
);
1038 ret
= ledger_get_entries(bill
, bank_ledgers
.energy
, &credit
, &debit
);
1039 if (ret
== KERN_SUCCESS
) {
1040 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
,
1041 (BANK_CODE(BANK_ACCOUNT_INFO
, (BANK_SETTLE_ENERGY
))) | DBG_FUNC_NONE
,
1042 bank_merchant
->bt_pid
, bank_holder
->bt_pid
, credit
, debit
, 0);
1043 ledger_credit(bank_holder
->bt_creditcard
, task_ledgers
.energy_billed_to_me
, credit
);
1044 ledger_debit(bank_holder
->bt_creditcard
, task_ledgers
.energy_billed_to_me
, debit
);
1046 ledger_credit(bank_merchant
->bt_creditcard
, task_ledgers
.energy_billed_to_others
, credit
);
1047 ledger_debit(bank_merchant
->bt_creditcard
, task_ledgers
.energy_billed_to_others
, debit
);
1054 * Routine: bank_task_destroy
1055 * Purpose: Drops reference on bank task.
1059 bank_task_destroy(task_t task
)
1061 bank_task_t bank_task
;
1063 /* Grab the global bank task lock before dropping the ref on task bank context */
1064 global_bank_task_lock();
1065 bank_task
= task
->bank_context
;
1066 task
->bank_context
= NULL
;
1067 global_bank_task_unlock();
1069 bank_task_dealloc(bank_task
, 1);
1073 * Routine: bank_task_initialize
1074 * Purpose: Initialize the bank context of a task.
1078 bank_task_initialize(task_t task
)
1080 get_bank_task_context(task
, TRUE
);
1084 * Routine: init_bank_ledgers
1085 * Purpose: Initialize template for bank ledgers.
1089 init_bank_ledgers(void) {
1090 ledger_template_t t
;
1093 assert(bank_ledger_template
== NULL
);
1095 if ((t
= ledger_template_create("Bank ledger")) == NULL
)
1096 panic("couldn't create bank ledger template");
1098 if ((idx
= ledger_entry_add(t
, "cpu_time", "sched", "ns")) < 0) {
1099 panic("couldn't create cpu_time entry for bank ledger template");
1101 bank_ledgers
.cpu_time
= idx
;
1103 if ((idx
= ledger_entry_add(t
, "energy", "power", "nj")) < 0) {
1104 panic("couldn't create energy entry for bank ledger template");
1106 bank_ledgers
.energy
= idx
;
1108 ledger_template_complete(t
);
1109 bank_ledger_template
= t
;
1112 /* Routine: bank_billed_balance_safe
1113 * Purpose: Walk through all the bank accounts billed to me by other tasks and get the current billing balance.
1114 * Called from another task. It takes global bank task lock to make sure the bank context is
1115 not deallocated while accesing it.
1116 * Returns: cpu balance and energy balance in out paremeters.
1119 bank_billed_balance_safe(task_t task
, uint64_t *cpu_time
, uint64_t *energy
)
1121 bank_task_t bank_task
= BANK_TASK_NULL
;
1122 ledger_amount_t credit
, debit
;
1123 uint64_t cpu_balance
= 0;
1124 uint64_t energy_balance
= 0;
1127 /* Task might be in exec, grab the global bank task lock before accessing bank context. */
1128 global_bank_task_lock();
1129 /* Grab a reference on bank context */
1130 if (task
->bank_context
!= NULL
) {
1131 bank_task
= task
->bank_context
;
1132 bank_task_reference(bank_task
);
1134 global_bank_task_unlock();
1137 bank_billed_balance(bank_task
, &cpu_balance
, &energy_balance
);
1138 bank_task_dealloc(bank_task
, 1);
1140 kr
= ledger_get_entries(task
->ledger
, task_ledgers
.cpu_time_billed_to_me
,
1142 if (kr
== KERN_SUCCESS
) {
1143 cpu_balance
= credit
- debit
;
1145 kr
= ledger_get_entries(task
->ledger
, task_ledgers
.energy_billed_to_me
,
1147 if (kr
== KERN_SUCCESS
) {
1148 energy_balance
= credit
- debit
;
1152 *cpu_time
= cpu_balance
;
1153 *energy
= energy_balance
;
1158 * Routine: bank_billed_time
1159 * Purpose: Walk through the Accounts need to pay account list and get the current billing balance.
1160 * Returns: cpu balance and energy balance in out paremeters.
1163 bank_billed_balance(bank_task_t bank_task
, uint64_t *cpu_time
, uint64_t *energy
)
1165 int64_t cpu_balance
= 0;
1166 int64_t energy_balance
= 0;
1167 bank_account_t bank_account
;
1170 if (bank_task
== BANK_TASK_NULL
) {
1176 lck_mtx_lock(&bank_task
->bt_acc_to_pay_lock
);
1178 kr
= ledger_get_balance(bank_task
->bt_creditcard
, task_ledgers
.cpu_time_billed_to_me
, &temp
);
1179 if (kr
== KERN_SUCCESS
&& temp
>= 0) {
1180 cpu_balance
+= temp
;
1182 #if DEVELOPMENT || DEBUG
1184 printf("bank_bill_time: ledger_get_balance failed or negative balance in ledger: %lld\n", temp
);
1186 #endif /* DEVELOPMENT || DEBUG */
1188 kr
= ledger_get_balance(bank_task
->bt_creditcard
, task_ledgers
.energy_billed_to_me
, &temp
);
1189 if (kr
== KERN_SUCCESS
&& temp
>= 0) {
1190 energy_balance
+= temp
;
1193 queue_iterate(&bank_task
->bt_accounts_to_pay
, bank_account
, bank_account_t
, ba_next_acc_to_pay
) {
1195 kr
= ledger_get_balance(bank_account
->ba_bill
, bank_ledgers
.cpu_time
, &temp
);
1196 if (kr
== KERN_SUCCESS
&& temp
>= 0) {
1197 cpu_balance
+= temp
;
1199 #if DEVELOPMENT || DEBUG
1201 printf("bank_bill_time: ledger_get_balance failed or negative balance in ledger: %lld\n", temp
);
1203 #endif /* DEVELOPMENT || DEBUG */
1205 kr
= ledger_get_balance(bank_account
->ba_bill
, bank_ledgers
.energy
, &temp
);
1206 if (kr
== KERN_SUCCESS
&& temp
>= 0) {
1207 energy_balance
+= temp
;
1210 lck_mtx_unlock(&bank_task
->bt_acc_to_pay_lock
);
1211 *cpu_time
= (uint64_t)cpu_balance
;
1212 *energy
= (uint64_t)energy_balance
;
1216 /* Routine: bank_serviced_balance_safe
1217 * Purpose: Walk through the bank accounts billed to other tasks by me and get the current balance to be charged.
1218 * Called from another task. It takes global bank task lock to make sure the bank context is
1219 not deallocated while accesing it.
1220 * Returns: cpu balance and energy balance in out paremeters.
1223 bank_serviced_balance_safe(task_t task
, uint64_t *cpu_time
, uint64_t *energy
)
1225 bank_task_t bank_task
= BANK_TASK_NULL
;
1226 ledger_amount_t credit
, debit
;
1227 uint64_t cpu_balance
= 0;
1228 uint64_t energy_balance
= 0;
1231 /* Task might be in exec, grab the global bank task lock before accessing bank context. */
1232 global_bank_task_lock();
1233 /* Grab a reference on bank context */
1234 if (task
->bank_context
!= NULL
) {
1235 bank_task
= task
->bank_context
;
1236 bank_task_reference(bank_task
);
1238 global_bank_task_unlock();
1241 bank_serviced_balance(bank_task
, &cpu_balance
, &energy_balance
);
1242 bank_task_dealloc(bank_task
, 1);
1244 kr
= ledger_get_entries(task
->ledger
, task_ledgers
.cpu_time_billed_to_others
,
1246 if (kr
== KERN_SUCCESS
) {
1247 cpu_balance
= credit
- debit
;
1250 kr
= ledger_get_entries(task
->ledger
, task_ledgers
.energy_billed_to_others
,
1252 if (kr
== KERN_SUCCESS
) {
1253 energy_balance
= credit
- debit
;
1257 *cpu_time
= cpu_balance
;
1258 *energy
= energy_balance
;
1263 * Routine: bank_serviced_balance
1264 * Purpose: Walk through the Account need to charge account list and get the current balance to be charged.
1265 * Returns: cpu balance and energy balance in out paremeters.
1268 bank_serviced_balance(bank_task_t bank_task
, uint64_t *cpu_time
, uint64_t *energy
)
1270 int64_t cpu_balance
= 0;
1271 int64_t energy_balance
= 0;
1272 bank_account_t bank_account
;
1275 if (bank_task
== BANK_TASK_NULL
) {
1281 lck_mtx_lock(&bank_task
->bt_acc_to_charge_lock
);
1283 kr
= ledger_get_balance(bank_task
->bt_creditcard
, task_ledgers
.cpu_time_billed_to_others
, &temp
);
1284 if (kr
== KERN_SUCCESS
&& temp
>= 0) {
1285 cpu_balance
+= temp
;
1287 #if DEVELOPMENT || DEBUG
1289 printf("bank_serviced_time: ledger_get_balance failed or negative balance in ledger: %lld\n", temp
);
1291 #endif /* DEVELOPMENT || DEBUG */
1293 kr
= ledger_get_balance(bank_task
->bt_creditcard
, task_ledgers
.energy_billed_to_others
, &temp
);
1294 if (kr
== KERN_SUCCESS
&& temp
>= 0) {
1295 energy_balance
+= temp
;
1298 queue_iterate(&bank_task
->bt_accounts_to_charge
, bank_account
, bank_account_t
, ba_next_acc_to_charge
) {
1300 kr
= ledger_get_balance(bank_account
->ba_bill
, bank_ledgers
.cpu_time
, &temp
);
1301 if (kr
== KERN_SUCCESS
&& temp
>= 0) {
1302 cpu_balance
+= temp
;
1304 #if DEVELOPMENT || DEBUG
1306 printf("bank_serviced_time: ledger_get_balance failed or negative balance in ledger: %lld\n", temp
);
1308 #endif /* DEVELOPMENT || DEBUG */
1310 kr
= ledger_get_balance(bank_account
->ba_bill
, bank_ledgers
.energy
, &temp
);
1311 if (kr
== KERN_SUCCESS
&& temp
>= 0) {
1312 energy_balance
+= temp
;
1315 lck_mtx_unlock(&bank_task
->bt_acc_to_charge_lock
);
1316 *cpu_time
= (uint64_t)cpu_balance
;
1317 *energy
= (uint64_t)energy_balance
;
1322 * Routine: bank_get_voucher_bank_account
1323 * Purpose: Get the bank account from the voucher.
1324 * Returns: bank_account if bank_account attribute present in voucher.
1325 * NULL on no attribute, no bank_element, or if holder and merchant bank accounts are the same.
1327 static bank_account_t
1328 bank_get_voucher_bank_account(ipc_voucher_t voucher
)
1330 bank_element_t bank_element
= BANK_ELEMENT_NULL
;
1331 bank_account_t bank_account
= BANK_ACCOUNT_NULL
;
1332 mach_voucher_attr_value_handle_t vals
[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
];
1333 mach_voucher_attr_value_handle_array_size_t val_count
;
1336 val_count
= MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
;
1337 kr
= mach_voucher_attr_control_get_values(bank_voucher_attr_control
,
1342 if (kr
!= KERN_SUCCESS
|| val_count
== 0)
1343 return BANK_ACCOUNT_NULL
;
1345 bank_element
= HANDLE_TO_BANK_ELEMENT(vals
[0]);
1346 if (bank_element
== BANK_DEFAULT_VALUE
)
1347 return BANK_ACCOUNT_NULL
;
1348 if (bank_element
== BANK_DEFAULT_TASK_VALUE
)
1349 bank_element
= CAST_TO_BANK_ELEMENT(get_bank_task_context(current_task(), FALSE
));
1351 if (bank_element
->be_type
== BANK_TASK
) {
1352 return BANK_ACCOUNT_NULL
;
1353 } else if (bank_element
->be_type
== BANK_ACCOUNT
) {
1354 bank_account
= CAST_TO_BANK_ACCOUNT(bank_element
);
1355 if (bank_account
->ba_holder
!= bank_account
->ba_merchant
) {
1356 bank_task_t bank_merchant
= get_bank_task_context(current_task(), FALSE
);
1357 if (bank_account
->ba_merchant
== bank_merchant
)
1358 return bank_account
;
1360 return BANK_ACCOUNT_NULL
;
1362 return BANK_ACCOUNT_NULL
;
1365 panic("Bogus bank type: %d passed in bank_get_voucher_bank_account\n", bank_element
->be_type
);
1367 return BANK_ACCOUNT_NULL
;
1371 * Routine: bank_get_bank_account_ledger
1372 * Purpose: Get the bankledger from the bank account
1375 bank_get_bank_account_ledger(bank_account_t bank_account
)
1377 ledger_t bankledger
= NULL
;
1379 if (bank_account
!= BANK_ACCOUNT_NULL
)
1380 bankledger
= bank_account
->ba_bill
;
1382 return (bankledger
);
1387 * Routine: bank_get_bank_ledger_and_thread_group
1388 * Purpose: Get the bankledger (chit) and thread group from the voucher.
1389 * Returns: bankledger and thread group if bank_account attribute present in voucher.
1393 bank_get_bank_ledger_and_thread_group(
1394 ipc_voucher_t voucher
,
1395 ledger_t
*bankledger
,
1396 thread_group_t
*banktg __unused
)
1398 bank_account_t bank_account
;
1400 bank_account
= bank_get_voucher_bank_account(voucher
);
1401 *bankledger
= bank_get_bank_account_ledger(bank_account
);
1402 return KERN_SUCCESS
;
1406 * Routine: bank_swap_thread_bank_ledger
1407 * Purpose: swap the bank ledger on the thread.
1409 * Note: Should be only called for current thread or thread which is not started.
1412 bank_swap_thread_bank_ledger(thread_t thread __unused
, ledger_t new_ledger __unused
)
1415 processor_t processor
;
1416 ledger_t old_ledger
= thread
->t_bankledger
;
1417 int64_t ctime
, effective_ledger_time_consumed
= 0;
1418 int64_t remainder
= 0, consumed
= 0;
1419 int64_t effective_energy_consumed
= 0;
1420 uint64_t thread_energy
;
1422 if (old_ledger
== NULL
&& new_ledger
== NULL
)
1425 assert((thread
== current_thread() || thread
->started
== 0));
1428 thread_lock(thread
);
1431 * Calculation of time elapsed by the thread before voucher swap.
1432 * Following is the timeline which shows all the variables used in the calculation below.
1436 * |<- consumed ->|<- remainder ->|
1437 * timeline ----------------------------------------------------------------->
1439 * thread_dispatch ctime quantum end
1441 * |<-effective_ledger_time -> |
1442 * deduct_bank_ledger_time
1445 ctime
= mach_absolute_time();
1446 processor
= thread
->last_processor
;
1447 if (processor
!= NULL
) {
1448 if ((int64_t)processor
->quantum_end
> ctime
)
1449 remainder
= (int64_t)processor
->quantum_end
- ctime
;
1451 consumed
= thread
->quantum_remaining
- remainder
;
1452 effective_ledger_time_consumed
= consumed
- thread
->t_deduct_bank_ledger_time
;
1455 thread
->t_deduct_bank_ledger_time
= consumed
;
1457 thread_energy
= ml_energy_stat(thread
);
1458 effective_energy_consumed
=
1459 thread_energy
- thread
->t_deduct_bank_ledger_energy
;
1460 assert(effective_energy_consumed
>= 0);
1461 thread
->t_deduct_bank_ledger_energy
= thread_energy
;
1463 thread
->t_bankledger
= new_ledger
;
1465 thread_unlock(thread
);
1468 if (old_ledger
!= NULL
) {
1469 ledger_credit(old_ledger
,
1470 bank_ledgers
.cpu_time
,
1471 effective_ledger_time_consumed
);
1472 ledger_credit(old_ledger
,
1473 bank_ledgers
.energy
,
1474 effective_energy_consumed
);