]> git.saurik.com Git - apple/xnu.git/blame_incremental - osfmk/bank/bank.c
xnu-7195.101.1.tar.gz
[apple/xnu.git] / osfmk / bank / bank.c
... / ...
CommitLineData
1/*
2 * Copyright (c) 2012-2020 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29#include <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/ledger.h>
41#include <kern/coalition.h>
42#include <kern/thread_group.h>
43#include <sys/kdebug.h>
44#include <IOKit/IOBSD.h>
45#include <mach/mach_voucher_attr_control.h>
46#include <kern/policy_internal.h>
47
48static ZONE_DECLARE(bank_task_zone, "bank_task",
49 sizeof(struct bank_task), ZC_NONE);
50static ZONE_DECLARE(bank_account_zone, "bank_account",
51 sizeof(struct bank_account), ZC_NONE);
52
53#define MAX_BANK_TASK (CONFIG_TASK_MAX)
54#define MAX_BANK_ACCOUNT (CONFIG_TASK_MAX + CONFIG_THREAD_MAX)
55
56#define BANK_ELEMENT_TO_HANDLE(x) (CAST_DOWN(bank_handle_t, (x)))
57#define HANDLE_TO_BANK_ELEMENT(x) (CAST_DOWN(bank_element_t, (x)))
58
59/* Need macro since bank_element_t is 4 byte aligned on release kernel and direct type case gives compilation error */
60#define CAST_TO_BANK_ELEMENT(x) ((bank_element_t)((void *)(x)))
61#define CAST_TO_BANK_TASK(x) ((bank_task_t)((void *)(x)))
62#define CAST_TO_BANK_ACCOUNT(x) ((bank_account_t)((void *)(x)))
63
64ipc_voucher_attr_control_t bank_voucher_attr_control; /* communication channel from ATM to voucher system */
65struct persona;
66extern struct persona *system_persona, *proxy_system_persona;
67uint32_t persona_get_id(struct persona *persona);
68extern int unique_persona;
69
70static ledger_template_t bank_ledger_template = NULL;
71struct _bank_ledger_indices bank_ledgers = { .cpu_time = -1, .energy = -1 };
72
73static bank_task_t bank_task_alloc_init(task_t task);
74static bank_account_t bank_account_alloc_init(bank_task_t bank_holder, bank_task_t bank_merchant,
75 bank_task_t bank_secureoriginator, bank_task_t bank_proximateprocess, struct thread_group* banktg, uint32_t persona_id);
76static bank_task_t get_bank_task_context(task_t task, boolean_t initialize);
77static void bank_task_dealloc(bank_task_t bank_task, mach_voucher_attr_value_reference_t sync);
78static kern_return_t bank_account_dealloc_with_sync(bank_account_t bank_account, mach_voucher_attr_value_reference_t sync);
79static void bank_rollup_chit_to_tasks(ledger_t bill, ledger_t bank_holder_ledger, ledger_t bank_merchant_ledger,
80 int bank_holder_pid, int bank_merchant_pid);
81static ledger_t bank_get_bank_task_ledger_with_ref(bank_task_t bank_task);
82static void bank_destroy_bank_task_ledger(bank_task_t bank_task);
83static void init_bank_ledgers(void);
84static boolean_t bank_task_is_propagate_entitled(task_t t);
85static boolean_t bank_task_is_persona_modify_entitled(task_t t);
86static struct thread_group *bank_get_bank_task_thread_group(bank_task_t bank_task __unused);
87static struct thread_group *bank_get_bank_account_thread_group(bank_account_t bank_account __unused);
88static boolean_t bank_verify_persona_id(uint32_t persona_id);
89
90/* lock to protect task->bank_context transition */
91static LCK_GRP_DECLARE(bank_lock_grp, "bank_lock");
92static LCK_ATTR_DECLARE(bank_lock_attr, 0, 0);
93static LCK_SPIN_DECLARE_ATTR(g_bank_task_lock_data, &bank_lock_grp, &bank_lock_attr);
94
95static TUNABLE(bool, disable_persona_propagate_check,
96 "disable_persona_propagate_check", false);
97
98#define global_bank_task_lock() \
99 lck_spin_lock_grp(&g_bank_task_lock_data, &bank_lock_grp)
100#define global_bank_task_lock_try() \
101 lck_spin_try_lock_grp(&g_bank_task_lock_data, &bank_lock_grp)
102#define global_bank_task_unlock() \
103 lck_spin_unlock(&g_bank_task_lock_data)
104
105extern uint64_t proc_uniqueid(void *p);
106struct proc;
107extern int32_t proc_pid(struct proc *p);
108extern int32_t proc_pidversion(void *p);
109extern uint32_t proc_persona_id(void *p);
110extern uint32_t proc_getuid(void *p);
111extern uint32_t proc_getgid(void *p);
112extern void proc_getexecutableuuid(void *p, unsigned char *uuidbuf, unsigned long size);
113extern int kauth_cred_issuser(void *cred);
114extern void* kauth_cred_get(void);
115extern void* persona_lookup(uint32_t id);
116extern void persona_put(void* persona);
117
118kern_return_t
119bank_release_value(
120 ipc_voucher_attr_manager_t __assert_only manager,
121 mach_voucher_attr_key_t __assert_only key,
122 mach_voucher_attr_value_handle_t value,
123 mach_voucher_attr_value_reference_t sync);
124
125kern_return_t
126bank_get_value(
127 ipc_voucher_attr_manager_t __assert_only manager,
128 mach_voucher_attr_key_t __assert_only key,
129 mach_voucher_attr_recipe_command_t command,
130 mach_voucher_attr_value_handle_array_t prev_values,
131 mach_msg_type_number_t __assert_only prev_value_count,
132 mach_voucher_attr_content_t recipe,
133 mach_voucher_attr_content_size_t recipe_size,
134 mach_voucher_attr_value_handle_t *out_value,
135 mach_voucher_attr_value_flags_t *out_flags,
136 ipc_voucher_t *out_value_voucher);
137
138kern_return_t
139bank_extract_content(
140 ipc_voucher_attr_manager_t __assert_only manager,
141 mach_voucher_attr_key_t __assert_only key,
142 mach_voucher_attr_value_handle_array_t values,
143 mach_msg_type_number_t value_count,
144 mach_voucher_attr_recipe_command_t *out_command,
145 mach_voucher_attr_content_t out_recipe,
146 mach_voucher_attr_content_size_t *in_out_recipe_size);
147
148kern_return_t
149bank_command(
150 ipc_voucher_attr_manager_t __assert_only manager,
151 mach_voucher_attr_key_t __assert_only key,
152 mach_voucher_attr_value_handle_array_t values,
153 mach_msg_type_number_t value_count,
154 mach_voucher_attr_command_t command,
155 mach_voucher_attr_content_t in_content,
156 mach_voucher_attr_content_size_t in_content_size,
157 mach_voucher_attr_content_t out_content,
158 mach_voucher_attr_content_size_t *in_out_content_size);
159
160void
161bank_release(ipc_voucher_attr_manager_t __assert_only manager);
162
163/*
164 * communication channel from voucher system to ATM
165 */
166const struct ipc_voucher_attr_manager bank_manager = {
167 .ivam_release_value = bank_release_value,
168 .ivam_get_value = bank_get_value,
169 .ivam_extract_content = bank_extract_content,
170 .ivam_command = bank_command,
171 .ivam_release = bank_release,
172 .ivam_flags = (IVAM_FLAGS_SUPPORT_SEND_PREPROCESS | IVAM_FLAGS_SUPPORT_RECEIVE_POSTPROCESS),
173};
174
175
176#if DEVELOPMENT || DEBUG
177LCK_GRP_DECLARE(bank_dev_lock_grp, "bank_dev_lock");
178LCK_MTX_DECLARE(bank_tasks_list_lock, &bank_dev_lock_grp);
179LCK_MTX_DECLARE(bank_accounts_list_lock, &bank_dev_lock_grp);
180queue_head_t bank_tasks_list = QUEUE_HEAD_INITIALIZER(bank_tasks_list);
181queue_head_t bank_accounts_list = QUEUE_HEAD_INITIALIZER(bank_accounts_list);
182#endif
183
184/*
185 * Routine: bank_init
186 * Purpose: Initialize the BANK subsystem.
187 * Returns: None.
188 */
189void
190bank_init()
191{
192 kern_return_t kr = KERN_SUCCESS;
193
194 init_bank_ledgers();
195
196 /* Register the bank manager with the Vouchers sub system. */
197 kr = ipc_register_well_known_mach_voucher_attr_manager(
198 &bank_manager,
199 0,
200 MACH_VOUCHER_ATTR_KEY_BANK,
201 &bank_voucher_attr_control);
202 if (kr != KERN_SUCCESS) {
203 panic("BANK subsystem initialization failed");
204 }
205
206
207 kprintf("BANK subsystem is initialized\n");
208}
209
210
211/*
212 * BANK Resource Manager Routines.
213 */
214
215
216/*
217 * Routine: bank_release_value
218 * Purpose: Release a value, if sync matches the sync count in value.
219 * Returns: KERN_SUCCESS: on Successful deletion.
220 * KERN_FAILURE: if sync value does not matches.
221 */
222kern_return_t
223bank_release_value(
224 ipc_voucher_attr_manager_t __assert_only manager,
225 mach_voucher_attr_key_t __assert_only key,
226 mach_voucher_attr_value_handle_t value,
227 mach_voucher_attr_value_reference_t sync)
228{
229 bank_task_t bank_task = BANK_TASK_NULL;
230 bank_element_t bank_element = BANK_ELEMENT_NULL;
231 bank_account_t bank_account = BANK_ACCOUNT_NULL;
232 kern_return_t kr = KERN_SUCCESS;
233
234 assert(MACH_VOUCHER_ATTR_KEY_BANK == key);
235 assert(manager == &bank_manager);
236
237
238 bank_element = HANDLE_TO_BANK_ELEMENT(value);
239 /* Voucher system should never release the default or persistent value */
240 assert(bank_element != BANK_DEFAULT_VALUE && bank_element != BANK_DEFAULT_TASK_VALUE);
241
242 if (bank_element == BANK_DEFAULT_VALUE || bank_element == BANK_DEFAULT_TASK_VALUE) {
243 /* Return success for default and default task value */
244 return KERN_SUCCESS;
245 }
246
247
248 if (bank_element->be_type == BANK_TASK) {
249 bank_task = CAST_TO_BANK_TASK(bank_element);
250
251 /* Checking of the made ref with sync and clearing of voucher ref should be done under a lock */
252 lck_mtx_lock(&bank_task->bt_acc_to_pay_lock);
253 if (bank_task->bt_made != sync) {
254 lck_mtx_unlock(&bank_task->bt_acc_to_pay_lock);
255 return KERN_FAILURE;
256 }
257
258 bank_task_made_release_num(bank_task, sync);
259 assert(bank_task->bt_voucher_ref == 1);
260 bank_task->bt_voucher_ref = 0;
261 lck_mtx_unlock(&bank_task->bt_acc_to_pay_lock);
262
263 bank_task_dealloc(bank_task, 1);
264 } else if (bank_element->be_type == BANK_ACCOUNT) {
265 bank_account = CAST_TO_BANK_ACCOUNT(bank_element);
266 kr = bank_account_dealloc_with_sync(bank_account, sync);
267 } else {
268 panic("Bogus bank type: %d passed in get_value\n", bank_element->be_type);
269 }
270
271 return kr;
272}
273
274
275/*
276 * Routine: bank_get_value
277 *
278 * This function uses the recipe to create a bank attribute for a voucher.
279 */
280kern_return_t
281bank_get_value(
282 ipc_voucher_attr_manager_t __assert_only manager,
283 mach_voucher_attr_key_t __assert_only key,
284 mach_voucher_attr_recipe_command_t command,
285 mach_voucher_attr_value_handle_array_t prev_values,
286 mach_msg_type_number_t prev_value_count,
287 mach_voucher_attr_content_t recipe,
288 mach_voucher_attr_content_size_t recipe_size,
289 mach_voucher_attr_value_handle_t *out_value,
290 mach_voucher_attr_value_flags_t *out_flags,
291 ipc_voucher_t *out_value_voucher)
292{
293 bank_task_t bank_holder = BANK_TASK_NULL;
294 bank_task_t bank_merchant = BANK_TASK_NULL;
295 bank_task_t bank_secureoriginator = BANK_TASK_NULL;
296 bank_task_t bank_proximateprocess = BANK_TASK_NULL;
297 bank_element_t bank_element = BANK_ELEMENT_NULL;
298 bank_account_t bank_account = BANK_ACCOUNT_NULL;
299 bank_account_t old_bank_account = BANK_ACCOUNT_NULL;
300 mach_voucher_attr_value_handle_t bank_handle;
301 task_t task;
302 kern_return_t kr = KERN_SUCCESS;
303 mach_msg_type_number_t i;
304 struct thread_group *thread_group = NULL;
305 struct thread_group *cur_thread_group = NULL;
306 uint32_t persona_id = proc_persona_id(NULL);
307
308 assert(MACH_VOUCHER_ATTR_KEY_BANK == key);
309 assert(manager == &bank_manager);
310
311 /* never an out voucher */
312 *out_value_voucher = IPC_VOUCHER_NULL;
313 *out_flags = MACH_VOUCHER_ATTR_VALUE_FLAGS_NONE;
314
315 switch (command) {
316 case MACH_VOUCHER_ATTR_BANK_CREATE:
317
318 /* It returns the default task value. This value is replaced by
319 * an actual bank task reference, by using a recipe with
320 * MACH_VOUCHER_ATTR_SEND_PREPROCESS command.
321 */
322 *out_value = BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_TASK_VALUE);
323 *out_flags = MACH_VOUCHER_ATTR_VALUE_FLAGS_PERSIST;
324 break;
325
326 case MACH_VOUCHER_ATTR_BANK_MODIFY_PERSONA:
327
328 /* It creates a bank account attribute value with a new persona id
329 * and auto-redeems it on behalf of the bank_holder.
330 */
331 *out_value = BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_VALUE);
332
333 for (i = 0; i < prev_value_count; i++) {
334 bank_handle = prev_values[i];
335 bank_element = HANDLE_TO_BANK_ELEMENT(bank_handle);
336
337 /* Expect a pre-processed attribute value */
338 if (bank_element == BANK_DEFAULT_VALUE || bank_element == BANK_DEFAULT_TASK_VALUE) {
339 continue;
340 }
341
342 if (!bank_task_is_persona_modify_entitled(current_task())) {
343 return KERN_NO_ACCESS;
344 }
345
346 struct persona_modify_info pmi = {};
347 if (recipe_size == sizeof(pmi)) {
348 memcpy((void *)&pmi, recipe, sizeof(pmi));
349 persona_id = pmi.persona_id;
350 } else {
351 return KERN_INVALID_ARGUMENT;
352 }
353
354 /* Verify if the persona id is valid */
355 if (!bank_verify_persona_id(persona_id)) {
356 return KERN_INVALID_ARGUMENT;
357 }
358
359 /* Update the persona id only if the bank element is a bank task.
360 * This ensures that the bank_holder can be trusted.
361 */
362 if (bank_element->be_type == BANK_TASK) {
363 bank_holder = CAST_TO_BANK_TASK(bank_element);
364 /* Ensure that the requestor validated by userspace matches
365 * the bank_holder
366 */
367 if (pmi.unique_pid != bank_holder->bt_unique_pid) {
368 return KERN_INVALID_CAPABILITY;
369 }
370 bank_merchant = bank_holder;
371 bank_secureoriginator = bank_holder;
372 bank_proximateprocess = bank_holder;
373 thread_group = bank_get_bank_task_thread_group(bank_holder);
374 } else if (bank_element->be_type == BANK_ACCOUNT) {
375 return KERN_INVALID_ARGUMENT;
376 } else {
377 panic("Bogus bank type: %d passed in get_value\n", bank_element->be_type);
378 }
379
380 /* Do not replace persona id if the task is not spawned in system persona */
381 if (unique_persona &&
382 bank_merchant->bt_persona_id != persona_get_id(system_persona) &&
383 bank_merchant->bt_persona_id != persona_get_id(proxy_system_persona) &&
384 bank_merchant->bt_persona_id != persona_id) {
385 return KERN_INVALID_ARGUMENT;
386 }
387
388 if (bank_holder->bt_persona_id == persona_id) {
389 lck_mtx_lock(&bank_holder->bt_acc_to_pay_lock);
390 bank_task_made_reference(bank_holder);
391 if (bank_holder->bt_voucher_ref == 0) {
392 /* Take a ref for voucher system, if voucher system does not have a ref */
393 bank_task_reference(bank_holder);
394 bank_holder->bt_voucher_ref = 1;
395 }
396 lck_mtx_unlock(&bank_holder->bt_acc_to_pay_lock);
397
398 *out_value = BANK_ELEMENT_TO_HANDLE(bank_holder);
399 return kr;
400 }
401
402 bank_account = bank_account_alloc_init(bank_holder, bank_merchant,
403 bank_secureoriginator, bank_proximateprocess,
404 thread_group, persona_id);
405 if (bank_account == BANK_ACCOUNT_NULL) {
406 return KERN_RESOURCE_SHORTAGE;
407 }
408
409 *out_value = BANK_ELEMENT_TO_HANDLE(bank_account);
410 return kr;
411 }
412 break;
413
414 case MACH_VOUCHER_ATTR_AUTO_REDEEM:
415
416 /* It creates a bank account with the bank_merchant set to the current task.
417 * A bank attribute voucher needs to be redeemed before it can be adopted by
418 * it's threads.
419 */
420 for (i = 0; i < prev_value_count; i++) {
421 bank_handle = prev_values[i];
422 bank_element = HANDLE_TO_BANK_ELEMENT(bank_handle);
423
424 /* Should not have received default task value from an IPC */
425 if (bank_element == BANK_DEFAULT_VALUE || bank_element == BANK_DEFAULT_TASK_VALUE) {
426 continue;
427 }
428
429 task = current_task();
430 if (bank_element->be_type == BANK_TASK) {
431 bank_holder = CAST_TO_BANK_TASK(bank_element);
432 bank_secureoriginator = bank_holder;
433 bank_proximateprocess = bank_holder;
434 thread_group = bank_get_bank_task_thread_group(bank_holder);
435 persona_id = bank_holder->bt_persona_id;
436 } else if (bank_element->be_type == BANK_ACCOUNT) {
437 old_bank_account = CAST_TO_BANK_ACCOUNT(bank_element);
438 bank_holder = old_bank_account->ba_holder;
439 bank_secureoriginator = old_bank_account->ba_secureoriginator;
440 bank_proximateprocess = old_bank_account->ba_proximateprocess;
441 thread_group = bank_get_bank_account_thread_group(old_bank_account);
442 persona_id = old_bank_account->ba_so_persona_id;
443 } else {
444 panic("Bogus bank type: %d passed in get_value\n", bank_element->be_type);
445 }
446
447 bank_merchant = get_bank_task_context(task, FALSE);
448 if (bank_merchant == BANK_TASK_NULL) {
449 return KERN_RESOURCE_SHORTAGE;
450 }
451
452 cur_thread_group = bank_get_bank_task_thread_group(bank_merchant);
453
454 /* Change voucher thread group to current thread group for Apps */
455 if (task_is_app(task)) {
456 thread_group = cur_thread_group;
457 }
458
459 /* Change the persona-id to current task persona-id if the task is not spawned in system persona */
460 if (unique_persona &&
461 bank_merchant->bt_persona_id != persona_get_id(system_persona) &&
462 bank_merchant->bt_persona_id != persona_get_id(proxy_system_persona)) {
463 persona_id = bank_merchant->bt_persona_id;
464 }
465
466 /* Check if trying to redeem for self task, return the default bank task */
467 if (bank_holder == bank_merchant &&
468 bank_holder == bank_secureoriginator &&
469 bank_holder == bank_proximateprocess &&
470 thread_group == cur_thread_group &&
471 persona_id == bank_holder->bt_persona_id) {
472 *out_value = BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_TASK_VALUE);
473 *out_flags = MACH_VOUCHER_ATTR_VALUE_FLAGS_PERSIST;
474 return kr;
475 }
476
477 bank_account = bank_account_alloc_init(bank_holder, bank_merchant,
478 bank_secureoriginator, bank_proximateprocess,
479 thread_group, persona_id);
480 if (bank_account == BANK_ACCOUNT_NULL) {
481 return KERN_RESOURCE_SHORTAGE;
482 }
483
484 *out_value = BANK_ELEMENT_TO_HANDLE(bank_account);
485 return kr;
486 }
487
488 *out_value = BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_VALUE);
489 break;
490
491 case MACH_VOUCHER_ATTR_SEND_PREPROCESS:
492
493 for (i = 0; i < prev_value_count; i++) {
494 bank_handle = prev_values[i];
495 bank_element = HANDLE_TO_BANK_ELEMENT(bank_handle);
496
497 if (bank_element == BANK_DEFAULT_VALUE) {
498 continue;
499 }
500
501 task = current_task();
502 if (bank_element == BANK_DEFAULT_TASK_VALUE) {
503 bank_element = CAST_TO_BANK_ELEMENT(get_bank_task_context(task, FALSE));
504 }
505
506 if (bank_element->be_type == BANK_TASK) {
507 bank_holder = CAST_TO_BANK_TASK(bank_element);
508 bank_secureoriginator = bank_holder;
509 thread_group = bank_get_bank_task_thread_group(bank_holder);
510 persona_id = bank_holder->bt_persona_id;
511 } else if (bank_element->be_type == BANK_ACCOUNT) {
512 old_bank_account = CAST_TO_BANK_ACCOUNT(bank_element);
513 bank_holder = old_bank_account->ba_holder;
514 bank_secureoriginator = old_bank_account->ba_secureoriginator;
515 thread_group = bank_get_bank_account_thread_group(old_bank_account);
516 persona_id = old_bank_account->ba_so_persona_id;
517 } else {
518 panic("Bogus bank type: %d passed in get_value\n", bank_element->be_type);
519 }
520
521 bank_merchant = get_bank_task_context(task, FALSE);
522 if (bank_merchant == BANK_TASK_NULL) {
523 return KERN_RESOURCE_SHORTAGE;
524 }
525
526 cur_thread_group = bank_get_bank_task_thread_group(bank_merchant);
527
528 /*
529 * If the process doesn't have secure persona entitlement,
530 * then replace the secure originator to current task.
531 * Also update the persona_id to match that of the secure originator.
532 */
533 if (bank_merchant->bt_hasentitlement == 0) {
534 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE,
535 (BANK_CODE(BANK_ACCOUNT_INFO, (BANK_SECURE_ORIGINATOR_CHANGED))) | DBG_FUNC_NONE,
536 bank_secureoriginator->bt_pid, bank_merchant->bt_pid, 0, 0, 0);
537 bank_secureoriginator = bank_merchant;
538 persona_id = bank_merchant->bt_persona_id;
539 }
540
541 bank_proximateprocess = bank_merchant;
542
543 /* Check if trying to pre-process for self task, return the bank task */
544 if (bank_holder == bank_merchant &&
545 bank_holder == bank_secureoriginator &&
546 bank_holder == bank_proximateprocess &&
547 thread_group == cur_thread_group &&
548 persona_id == bank_holder->bt_persona_id) {
549 lck_mtx_lock(&bank_holder->bt_acc_to_pay_lock);
550 bank_task_made_reference(bank_holder);
551 if (bank_holder->bt_voucher_ref == 0) {
552 /* Take a ref for voucher system, if voucher system does not have a ref */
553 bank_task_reference(bank_holder);
554 bank_holder->bt_voucher_ref = 1;
555 }
556 lck_mtx_unlock(&bank_holder->bt_acc_to_pay_lock);
557
558 *out_value = BANK_ELEMENT_TO_HANDLE(bank_holder);
559 return kr;
560 }
561 bank_account = bank_account_alloc_init(bank_holder, bank_merchant,
562 bank_secureoriginator, bank_proximateprocess,
563 thread_group, persona_id);
564 if (bank_account == BANK_ACCOUNT_NULL) {
565 return KERN_RESOURCE_SHORTAGE;
566 }
567
568 *out_value = BANK_ELEMENT_TO_HANDLE(bank_account);
569 return kr;
570 }
571
572 *out_value = BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_VALUE);
573 break;
574
575 case MACH_VOUCHER_ATTR_REDEEM:
576 /* This command expects that the bank attribute has been auto-redeemed
577 * and returns a reference to that bank account value.
578 */
579 for (i = 0; i < prev_value_count; i++) {
580 bank_handle = prev_values[i];
581 bank_element = HANDLE_TO_BANK_ELEMENT(bank_handle);
582
583 if (bank_element == BANK_DEFAULT_VALUE) {
584 continue;
585 }
586
587 if (bank_element == BANK_DEFAULT_TASK_VALUE) {
588 *out_value = BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_TASK_VALUE);
589 *out_flags = MACH_VOUCHER_ATTR_VALUE_FLAGS_PERSIST;
590 return kr;
591 }
592
593 task = current_task();
594 if (bank_element->be_type == BANK_TASK) {
595 bank_holder = CAST_TO_BANK_TASK(bank_element);
596 if (bank_holder == get_bank_task_context(task, FALSE)) {
597 *out_value = BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_TASK_VALUE);
598 *out_flags = MACH_VOUCHER_ATTR_VALUE_FLAGS_PERSIST;
599 } else {
600 kr = KERN_INVALID_CAPABILITY;
601 }
602 return kr;
603 } else if (bank_element->be_type == BANK_ACCOUNT) {
604 bank_account = CAST_TO_BANK_ACCOUNT(bank_element);
605 bank_merchant = bank_account->ba_merchant;
606 if (bank_merchant != get_bank_task_context(task, FALSE)) {
607 /* This error can be used to verify if the task can
608 * adopt the voucher.
609 */
610 kr = KERN_INVALID_CAPABILITY;
611 return kr;
612 }
613 bank_account_made_reference(bank_account);
614 *out_value = BANK_ELEMENT_TO_HANDLE(bank_account);
615 return kr;
616 } else {
617 panic("Bogus bank type: %d passed in get_value\n", bank_element->be_type);
618 }
619 }
620
621 *out_value = BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_VALUE);
622 break;
623
624 default:
625 kr = KERN_INVALID_ARGUMENT;
626 break;
627 }
628
629 return kr;
630}
631
632
633/*
634 * Routine: bank_extract_content
635 * Purpose: Extract a set of aid from an array of voucher values.
636 * Returns: KERN_SUCCESS: on Success.
637 * KERN_FAILURE: one of the value is not present in the hash.
638 * KERN_NO_SPACE: insufficeint buffer provided to fill an array of aid.
639 */
640kern_return_t
641bank_extract_content(
642 ipc_voucher_attr_manager_t __assert_only manager,
643 mach_voucher_attr_key_t __assert_only key,
644 mach_voucher_attr_value_handle_array_t values,
645 mach_msg_type_number_t value_count,
646 mach_voucher_attr_recipe_command_t *out_command,
647 mach_voucher_attr_content_t out_recipe,
648 mach_voucher_attr_content_size_t *in_out_recipe_size)
649{
650 bank_task_t bank_task = BANK_TASK_NULL;
651 bank_element_t bank_element = BANK_ELEMENT_NULL;
652 bank_account_t bank_account = BANK_ACCOUNT_NULL;
653 mach_voucher_attr_value_handle_t bank_handle;
654 char buf[MACH_VOUCHER_BANK_CONTENT_SIZE];
655 mach_msg_type_number_t i;
656
657 assert(MACH_VOUCHER_ATTR_KEY_BANK == key);
658 assert(manager == &bank_manager);
659
660 for (i = 0; i < value_count && *in_out_recipe_size > 0; i++) {
661 bank_handle = values[i];
662 bank_element = HANDLE_TO_BANK_ELEMENT(bank_handle);
663 if (bank_element == BANK_DEFAULT_VALUE) {
664 continue;
665 }
666
667 if (bank_element == BANK_DEFAULT_TASK_VALUE) {
668 bank_element = CAST_TO_BANK_ELEMENT(get_bank_task_context(current_task(), FALSE));
669 }
670
671 if (MACH_VOUCHER_BANK_CONTENT_SIZE > *in_out_recipe_size) {
672 *in_out_recipe_size = 0;
673 return KERN_NO_SPACE;
674 }
675
676 if (bank_element->be_type == BANK_TASK) {
677 bank_task = CAST_TO_BANK_TASK(bank_element);
678 snprintf(buf, MACH_VOUCHER_BANK_CONTENT_SIZE,
679 " Bank Context for a pid %d\n", bank_task->bt_pid);
680 } else if (bank_element->be_type == BANK_ACCOUNT) {
681 bank_account = CAST_TO_BANK_ACCOUNT(bank_element);
682 snprintf(buf, MACH_VOUCHER_BANK_CONTENT_SIZE,
683 " Bank Account linking holder pid %d with merchant pid %d, originator PID/persona: %d, %u and proximate PID/persona: %d, %u\n",
684 bank_account->ba_holder->bt_pid,
685 bank_account->ba_merchant->bt_pid,
686 bank_account->ba_secureoriginator->bt_pid,
687 bank_account->ba_so_persona_id,
688 bank_account->ba_proximateprocess->bt_pid,
689 bank_account->ba_proximateprocess->bt_persona_id);
690 } else {
691 panic("Bogus bank type: %d passed in get_value\n", bank_element->be_type);
692 }
693
694 memcpy(&out_recipe[0], buf, strlen(buf) + 1);
695 *out_command = MACH_VOUCHER_ATTR_BANK_NULL;
696 *in_out_recipe_size = (mach_voucher_attr_content_size_t)strlen(buf) + 1;
697 return KERN_SUCCESS;
698 }
699
700 return KERN_SUCCESS;
701}
702
703/*
704 * Routine: bank_command
705 * Purpose: Execute a command against a set of bank values.
706 * Returns: KERN_SUCCESS: On successful execution of command.
707 * KERN_FAILURE: On failure.
708 */
709kern_return_t
710bank_command(
711 ipc_voucher_attr_manager_t __assert_only manager,
712 mach_voucher_attr_key_t __assert_only key,
713 mach_voucher_attr_value_handle_array_t __unused values,
714 mach_msg_type_number_t __unused value_count,
715 mach_voucher_attr_command_t __unused command,
716 mach_voucher_attr_content_t __unused in_content,
717 mach_voucher_attr_content_size_t __unused in_content_size,
718 mach_voucher_attr_content_t __unused out_content,
719 mach_voucher_attr_content_size_t __unused *out_content_size)
720{
721 bank_task_t bank_task = BANK_TASK_NULL;
722 bank_task_t bank_secureoriginator = BANK_TASK_NULL;
723 bank_task_t bank_proximateprocess = BANK_TASK_NULL;
724 struct persona_token *token = NULL;
725 bank_element_t bank_element = BANK_ELEMENT_NULL;
726 bank_account_t bank_account = BANK_ACCOUNT_NULL;
727 mach_voucher_attr_value_handle_t bank_handle;
728 mach_msg_type_number_t i;
729 int32_t pid;
730 uint32_t persona_id;
731
732 assert(MACH_VOUCHER_ATTR_KEY_BANK == key);
733 assert(manager == &bank_manager);
734
735 switch (command) {
736 case BANK_ORIGINATOR_PID:
737
738 if ((sizeof(pid)) > *out_content_size) {
739 *out_content_size = 0;
740 return KERN_NO_SPACE;
741 }
742
743 for (i = 0; i < value_count; i++) {
744 bank_handle = values[i];
745 bank_element = HANDLE_TO_BANK_ELEMENT(bank_handle);
746 if (bank_element == BANK_DEFAULT_VALUE) {
747 continue;
748 }
749
750 if (bank_element == BANK_DEFAULT_TASK_VALUE) {
751 bank_element = CAST_TO_BANK_ELEMENT(get_bank_task_context(current_task(), FALSE));
752 }
753
754 if (bank_element->be_type == BANK_TASK) {
755 bank_task = CAST_TO_BANK_TASK(bank_element);
756 } else if (bank_element->be_type == BANK_ACCOUNT) {
757 bank_account = CAST_TO_BANK_ACCOUNT(bank_element);
758 bank_task = bank_account->ba_holder;
759 } else {
760 panic("Bogus bank type: %d passed in voucher_command\n", bank_element->be_type);
761 }
762 pid = bank_task->bt_pid;
763
764 memcpy(&out_content[0], &pid, sizeof(pid));
765 *out_content_size = (mach_voucher_attr_content_size_t)sizeof(pid);
766 return KERN_SUCCESS;
767 }
768 /* In the case of no value, return error KERN_INVALID_VALUE */
769 *out_content_size = 0;
770 return KERN_INVALID_VALUE;
771
772 case BANK_PERSONA_TOKEN:
773
774 if ((sizeof(struct persona_token)) > *out_content_size) {
775 *out_content_size = 0;
776 return KERN_NO_SPACE;
777 }
778 for (i = 0; i < value_count; i++) {
779 bank_handle = values[i];
780 bank_element = HANDLE_TO_BANK_ELEMENT(bank_handle);
781 if (bank_element == BANK_DEFAULT_VALUE) {
782 continue;
783 }
784
785 if (bank_element == BANK_DEFAULT_TASK_VALUE) {
786 bank_element = CAST_TO_BANK_ELEMENT(get_bank_task_context(current_task(), FALSE));
787 }
788
789 if (bank_element->be_type == BANK_TASK) {
790 *out_content_size = 0;
791 return KERN_INVALID_OBJECT;
792 } else if (bank_element->be_type == BANK_ACCOUNT) {
793 bank_account = CAST_TO_BANK_ACCOUNT(bank_element);
794 bank_secureoriginator = bank_account->ba_secureoriginator;
795 bank_proximateprocess = bank_account->ba_proximateprocess;
796 } else {
797 panic("Bogus bank type: %d passed in voucher_command\n", bank_element->be_type);
798 }
799 token = (struct persona_token *)(void *)&out_content[0];
800 memcpy(&token->originator, &bank_secureoriginator->bt_proc_persona, sizeof(struct proc_persona_info));
801 memcpy(&token->proximate, &bank_proximateprocess->bt_proc_persona, sizeof(struct proc_persona_info));
802
803 *out_content_size = (mach_voucher_attr_content_size_t)sizeof(*token);
804 return KERN_SUCCESS;
805 }
806 /* In the case of no value, return error KERN_INVALID_VALUE */
807 *out_content_size = 0;
808 return KERN_INVALID_VALUE;
809
810 case BANK_PERSONA_ID:
811
812 if ((sizeof(persona_id)) > *out_content_size) {
813 *out_content_size = 0;
814 return KERN_NO_SPACE;
815 }
816
817 for (i = 0; i < value_count; i++) {
818 bank_handle = values[i];
819 bank_element = HANDLE_TO_BANK_ELEMENT(bank_handle);
820 if (bank_element == BANK_DEFAULT_VALUE) {
821 continue;
822 }
823
824 if (bank_element == BANK_DEFAULT_TASK_VALUE) {
825 bank_element = CAST_TO_BANK_ELEMENT(get_bank_task_context(current_task(), FALSE));
826 }
827
828 if (bank_element->be_type == BANK_TASK) {
829 bank_task = CAST_TO_BANK_TASK(bank_element);
830 persona_id = bank_task->bt_persona_id;
831 } else if (bank_element->be_type == BANK_ACCOUNT) {
832 bank_account = CAST_TO_BANK_ACCOUNT(bank_element);
833 persona_id = bank_account->ba_so_persona_id;
834 } else {
835 panic("Bogus bank type: %d passed in voucher_command\n", bank_element->be_type);
836 }
837
838 memcpy(out_content, &persona_id, sizeof(persona_id));
839 *out_content_size = (mach_voucher_attr_content_size_t)sizeof(persona_id);
840 return KERN_SUCCESS;
841 }
842 /* In the case of no value, return error KERN_INVALID_VALUE */
843 *out_content_size = 0;
844 return KERN_INVALID_VALUE;
845
846 default:
847 return KERN_INVALID_ARGUMENT;
848 }
849 return KERN_SUCCESS;
850}
851
852
853void
854bank_release(
855 ipc_voucher_attr_manager_t __assert_only manager)
856{
857 assert(manager == &bank_manager);
858}
859
860
861
862/*
863 * Bank Internal Routines.
864 */
865
866/*
867 * Routine: bank_task_alloc_init
868 * Purpose: Allocate and initialize a bank task structure.
869 * Returns: bank_task_t on Success.
870 * BANK_TASK_NULL: on Failure.
871 * Notes: Leaves the task and ledger blank and has only 1 ref,
872 * needs to take 1 extra ref after the task field is initialized.
873 */
874static bank_task_t
875bank_task_alloc_init(task_t task)
876{
877 bank_task_t new_bank_task;
878
879 new_bank_task = (bank_task_t) zalloc(bank_task_zone);
880 if (new_bank_task == BANK_TASK_NULL) {
881 return BANK_TASK_NULL;
882 }
883
884 new_bank_task->bt_type = BANK_TASK;
885 new_bank_task->bt_voucher_ref = 0;
886 new_bank_task->bt_refs = 1;
887 new_bank_task->bt_made = 0;
888 new_bank_task->bt_ledger = LEDGER_NULL;
889 new_bank_task->bt_hasentitlement = bank_task_is_propagate_entitled(task);
890 queue_init(&new_bank_task->bt_accounts_to_pay);
891 queue_init(&new_bank_task->bt_accounts_to_charge);
892 lck_mtx_init(&new_bank_task->bt_acc_to_pay_lock, &bank_lock_grp, &bank_lock_attr);
893 lck_mtx_init(&new_bank_task->bt_acc_to_charge_lock, &bank_lock_grp, &bank_lock_attr);
894
895 /*
896 * Initialize the persona_id struct
897 */
898 bzero(&new_bank_task->bt_proc_persona, sizeof(new_bank_task->bt_proc_persona));
899 new_bank_task->bt_flags = 0;
900 new_bank_task->bt_unique_pid = proc_uniqueid(task->bsd_info);
901 new_bank_task->bt_pid = proc_pid(task->bsd_info);
902 new_bank_task->bt_pidversion = proc_pidversion(task->bsd_info);
903 new_bank_task->bt_persona_id = proc_persona_id(task->bsd_info);
904 new_bank_task->bt_uid = proc_getuid(task->bsd_info);
905 new_bank_task->bt_gid = proc_getgid(task->bsd_info);
906#if CONFIG_THREAD_GROUPS
907 new_bank_task->bt_thread_group = thread_group_retain(task_coalition_get_thread_group(task));
908#endif
909 proc_getexecutableuuid(task->bsd_info, new_bank_task->bt_macho_uuid, sizeof(new_bank_task->bt_macho_uuid));
910
911#if DEVELOPMENT || DEBUG
912 new_bank_task->bt_task = NULL;
913 lck_mtx_lock(&bank_tasks_list_lock);
914 queue_enter(&bank_tasks_list, new_bank_task, bank_task_t, bt_global_elt);
915 lck_mtx_unlock(&bank_tasks_list_lock);
916#endif
917 return new_bank_task;
918}
919
920/*
921 * Routine: proc_is_propagate_entitled
922 * Purpose: Check if the process is allowed to propagate secure originator.
923 * Returns: TRUE if entitled.
924 * FALSE if not.
925 */
926static boolean_t
927bank_task_is_propagate_entitled(task_t t)
928{
929 /* Check if it has an entitlement which disallows secure originator propagation */
930 boolean_t entitled = FALSE;
931 entitled = IOTaskHasEntitlement(t, ENTITLEMENT_PERSONA_NO_PROPAGATE);
932 if (entitled) {
933 return FALSE;
934 }
935
936 /* If it's a platform binary, allow propagation by default */
937 if (disable_persona_propagate_check || (t->t_flags & TF_PLATFORM)) {
938 return TRUE;
939 }
940
941 return FALSE;
942}
943
944/*
945 * Routine: proc_is_persona_modify_entitled
946 * Purpose: Check if the process has persona modify entitlement.
947 * Returns: TRUE if entitled.
948 * FALSE if not.
949 */
950static boolean_t
951bank_task_is_persona_modify_entitled(task_t t)
952{
953 boolean_t entitled = FALSE;
954 entitled = IOTaskHasEntitlement(t, ENTITLEMENT_PERSONA_MODIFY);
955 return entitled;
956}
957
958/*
959 * Routine: bank_account_alloc_init
960 * Purpose: Allocate and Initialize the bank account struct.
961 * Returns: bank_account_t : On Success.
962 * BANK_ACCOUNT_NULL: On Failure.
963 */
964static bank_account_t
965bank_account_alloc_init(
966 bank_task_t bank_holder,
967 bank_task_t bank_merchant,
968 bank_task_t bank_secureoriginator,
969 bank_task_t bank_proximateprocess,
970 struct thread_group *thread_group,
971 uint32_t persona_id)
972{
973 bank_account_t new_bank_account;
974 bank_account_t bank_account;
975 boolean_t entry_found = FALSE;
976 ledger_t new_ledger = ledger_instantiate(bank_ledger_template, LEDGER_CREATE_INACTIVE_ENTRIES);
977
978 if (new_ledger == LEDGER_NULL) {
979 return BANK_ACCOUNT_NULL;
980 }
981
982 ledger_entry_setactive(new_ledger, bank_ledgers.cpu_time);
983 ledger_entry_setactive(new_ledger, bank_ledgers.energy);
984 new_bank_account = (bank_account_t) zalloc(bank_account_zone);
985 if (new_bank_account == BANK_ACCOUNT_NULL) {
986 ledger_dereference(new_ledger);
987 return BANK_ACCOUNT_NULL;
988 }
989
990 new_bank_account->ba_type = BANK_ACCOUNT;
991 new_bank_account->ba_voucher_ref = 0;
992 new_bank_account->ba_refs = 1;
993 new_bank_account->ba_made = 1;
994 new_bank_account->ba_bill = new_ledger;
995 new_bank_account->ba_merchant = bank_merchant;
996 new_bank_account->ba_holder = bank_holder;
997 new_bank_account->ba_secureoriginator = bank_secureoriginator;
998 new_bank_account->ba_proximateprocess = bank_proximateprocess;
999#if CONFIG_THREAD_GROUPS
1000 new_bank_account->ba_thread_group = thread_group;
1001#endif
1002 new_bank_account->ba_so_persona_id = persona_id;
1003
1004 /* Iterate through accounts need to pay list to find the existing entry */
1005 lck_mtx_lock(&bank_holder->bt_acc_to_pay_lock);
1006 queue_iterate(&bank_holder->bt_accounts_to_pay, bank_account, bank_account_t, ba_next_acc_to_pay) {
1007 if (bank_account->ba_merchant != bank_merchant ||
1008 bank_account->ba_secureoriginator != bank_secureoriginator ||
1009 bank_account->ba_proximateprocess != bank_proximateprocess ||
1010 bank_get_bank_account_thread_group(bank_account) != thread_group ||
1011 bank_account->ba_so_persona_id != persona_id) {
1012 continue;
1013 }
1014
1015 entry_found = TRUE;
1016 /* Take a made ref, since this value would be returned to voucher system. */
1017 bank_account_made_reference(bank_account);
1018 break;
1019 }
1020
1021 if (!entry_found) {
1022 /* Create a linkage between the holder and the merchant task, Grab both the list locks before adding it to the list. */
1023 lck_mtx_lock(&bank_merchant->bt_acc_to_charge_lock);
1024
1025 /* Add the account entry into Accounts need to pay account link list. */
1026 queue_enter(&bank_holder->bt_accounts_to_pay, new_bank_account, bank_account_t, ba_next_acc_to_pay);
1027
1028 /* Add the account entry into Accounts need to charge account link list. */
1029 queue_enter(&bank_merchant->bt_accounts_to_charge, new_bank_account, bank_account_t, ba_next_acc_to_charge);
1030
1031 lck_mtx_unlock(&bank_merchant->bt_acc_to_charge_lock);
1032 }
1033
1034 lck_mtx_unlock(&bank_holder->bt_acc_to_pay_lock);
1035
1036 if (entry_found) {
1037 ledger_dereference(new_ledger);
1038 zfree(bank_account_zone, new_bank_account);
1039 return bank_account;
1040 }
1041
1042 bank_task_reference(bank_holder);
1043 bank_task_reference(bank_merchant);
1044 bank_task_reference(bank_secureoriginator);
1045 bank_task_reference(bank_proximateprocess);
1046#if CONFIG_THREAD_GROUPS
1047 assert(new_bank_account->ba_thread_group != NULL);
1048 thread_group_retain(new_bank_account->ba_thread_group);
1049#endif
1050
1051#if DEVELOPMENT || DEBUG
1052 new_bank_account->ba_task = NULL;
1053 lck_mtx_lock(&bank_accounts_list_lock);
1054 queue_enter(&bank_accounts_list, new_bank_account, bank_account_t, ba_global_elt);
1055 lck_mtx_unlock(&bank_accounts_list_lock);
1056#endif
1057
1058 return new_bank_account;
1059}
1060
1061/*
1062 * Routine: get_bank_task_context
1063 * Purpose: Get the bank context of the given task
1064 * Returns: bank_task_t on Success.
1065 * BANK_TASK_NULL: on Failure.
1066 * Note: Initialize bank context if NULL.
1067 */
1068static bank_task_t
1069get_bank_task_context
1070(task_t task,
1071 boolean_t initialize)
1072{
1073 bank_task_t bank_task;
1074
1075 if (task->bank_context || !initialize) {
1076 assert(task->bank_context != NULL);
1077 return task->bank_context;
1078 }
1079
1080 bank_task = bank_task_alloc_init(task);
1081
1082 /* Grab the task lock and check if we won the race. */
1083 task_lock(task);
1084 if (task->bank_context) {
1085 task_unlock(task);
1086 if (bank_task != BANK_TASK_NULL) {
1087 bank_task_dealloc(bank_task, 1);
1088 }
1089 return task->bank_context;
1090 } else if (bank_task == BANK_TASK_NULL) {
1091 task_unlock(task);
1092 return BANK_TASK_NULL;
1093 }
1094 /* We won the race. Take a ref on the ledger and initialize bank task. */
1095 bank_task->bt_ledger = task->ledger;
1096#if DEVELOPMENT || DEBUG
1097 bank_task->bt_task = task;
1098#endif
1099 ledger_reference(task->ledger);
1100
1101 /* Grab the global bank task lock before setting the bank context on a task */
1102 global_bank_task_lock();
1103 task->bank_context = bank_task;
1104 global_bank_task_unlock();
1105
1106 task_unlock(task);
1107
1108 return bank_task;
1109}
1110
1111/*
1112 * Routine: bank_task_dealloc
1113 * Purpose: Drops the reference on bank task.
1114 * Returns: None.
1115 */
1116static void
1117bank_task_dealloc(
1118 bank_task_t bank_task,
1119 mach_voucher_attr_value_reference_t sync)
1120{
1121 assert(bank_task->bt_refs >= 0);
1122
1123 if (bank_task_release_num(bank_task, sync) > (int)sync) {
1124 return;
1125 }
1126
1127 assert(bank_task->bt_refs == 0);
1128 assert(queue_empty(&bank_task->bt_accounts_to_pay));
1129 assert(queue_empty(&bank_task->bt_accounts_to_charge));
1130
1131 assert(!LEDGER_VALID(bank_task->bt_ledger));
1132 lck_mtx_destroy(&bank_task->bt_acc_to_pay_lock, &bank_lock_grp);
1133 lck_mtx_destroy(&bank_task->bt_acc_to_charge_lock, &bank_lock_grp);
1134
1135#if CONFIG_THREAD_GROUPS
1136 thread_group_release(bank_task->bt_thread_group);
1137#endif
1138
1139#if DEVELOPMENT || DEBUG
1140 lck_mtx_lock(&bank_tasks_list_lock);
1141 queue_remove(&bank_tasks_list, bank_task, bank_task_t, bt_global_elt);
1142 lck_mtx_unlock(&bank_tasks_list_lock);
1143#endif
1144
1145 zfree(bank_task_zone, bank_task);
1146}
1147
1148/*
1149 * Routine: bank_account_dealloc_with_sync
1150 * Purpose: Drop the reference on bank account if the sync matches.
1151 * Returns: KERN_SUCCESS if sync matches.
1152 * KERN_FAILURE on mismatch.
1153 */
1154static kern_return_t
1155bank_account_dealloc_with_sync(
1156 bank_account_t bank_account,
1157 mach_voucher_attr_value_reference_t sync)
1158{
1159 bank_task_t bank_holder = bank_account->ba_holder;
1160 bank_task_t bank_merchant = bank_account->ba_merchant;
1161 bank_task_t bank_secureoriginator = bank_account->ba_secureoriginator;
1162 bank_task_t bank_proximateprocess = bank_account->ba_proximateprocess;
1163 ledger_t bank_merchant_ledger = LEDGER_NULL;
1164
1165 /*
1166 * Grab a reference on the bank_merchant_ledger, since we would not be able
1167 * to take bt_acc_to_pay_lock for bank_merchant later.
1168 */
1169 bank_merchant_ledger = bank_get_bank_task_ledger_with_ref(bank_merchant);
1170
1171 /* Grab the acc to pay list lock and check the sync value */
1172 lck_mtx_lock(&bank_holder->bt_acc_to_pay_lock);
1173
1174 if (bank_account->ba_made != sync) {
1175 lck_mtx_unlock(&bank_holder->bt_acc_to_pay_lock);
1176 if (bank_merchant_ledger) {
1177 ledger_dereference(bank_merchant_ledger);
1178 }
1179 return KERN_FAILURE;
1180 }
1181
1182 bank_account_made_release_num(bank_account, sync);
1183
1184 if (bank_account_release_num(bank_account, 1) > 1) {
1185 panic("Releasing a non zero ref bank account %p\n", bank_account);
1186 }
1187
1188
1189 /* Grab both the acc to pay and acc to charge locks */
1190 lck_mtx_lock(&bank_merchant->bt_acc_to_charge_lock);
1191
1192 /* No need to take ledger reference for bank_holder ledger since bt_acc_to_pay_lock is locked */
1193 bank_rollup_chit_to_tasks(bank_account->ba_bill, bank_holder->bt_ledger, bank_merchant_ledger,
1194 bank_holder->bt_pid, bank_merchant->bt_pid);
1195
1196 /* Remove the account entry from Accounts need to pay account link list. */
1197 queue_remove(&bank_holder->bt_accounts_to_pay, bank_account, bank_account_t, ba_next_acc_to_pay);
1198
1199 /* Remove the account entry from Accounts need to charge account link list. */
1200 queue_remove(&bank_merchant->bt_accounts_to_charge, bank_account, bank_account_t, ba_next_acc_to_charge);
1201
1202 lck_mtx_unlock(&bank_merchant->bt_acc_to_charge_lock);
1203 lck_mtx_unlock(&bank_holder->bt_acc_to_pay_lock);
1204
1205 if (bank_merchant_ledger) {
1206 ledger_dereference(bank_merchant_ledger);
1207 }
1208 ledger_dereference(bank_account->ba_bill);
1209
1210 /* Drop the reference of bank holder and merchant */
1211 bank_task_dealloc(bank_holder, 1);
1212 bank_task_dealloc(bank_merchant, 1);
1213 bank_task_dealloc(bank_secureoriginator, 1);
1214 bank_task_dealloc(bank_proximateprocess, 1);
1215#if CONFIG_THREAD_GROUPS
1216 assert(bank_account->ba_thread_group != NULL);
1217 thread_group_release(bank_account->ba_thread_group);
1218#endif
1219
1220#if DEVELOPMENT || DEBUG
1221 lck_mtx_lock(&bank_accounts_list_lock);
1222 queue_remove(&bank_accounts_list, bank_account, bank_account_t, ba_global_elt);
1223 lck_mtx_unlock(&bank_accounts_list_lock);
1224#endif
1225
1226 zfree(bank_account_zone, bank_account);
1227 return KERN_SUCCESS;
1228}
1229
1230/*
1231 * Routine: bank_rollup_chit_to_tasks
1232 * Purpose: Debit and Credit holder's and merchant's ledgers.
1233 * Returns: None.
1234 */
1235static void
1236bank_rollup_chit_to_tasks(
1237 ledger_t bill,
1238 ledger_t bank_holder_ledger,
1239 ledger_t bank_merchant_ledger,
1240 int bank_holder_pid,
1241 int bank_merchant_pid)
1242{
1243 ledger_amount_t credit;
1244 ledger_amount_t debit;
1245 kern_return_t ret;
1246
1247 if (bank_holder_ledger == bank_merchant_ledger) {
1248 return;
1249 }
1250
1251 ret = ledger_get_entries(bill, bank_ledgers.cpu_time, &credit, &debit);
1252 if (ret == KERN_SUCCESS) {
1253 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE,
1254 (BANK_CODE(BANK_ACCOUNT_INFO, (BANK_SETTLE_CPU_TIME))) | DBG_FUNC_NONE,
1255 bank_merchant_pid, bank_holder_pid, credit, debit, 0);
1256
1257 if (bank_holder_ledger) {
1258 ledger_credit(bank_holder_ledger, task_ledgers.cpu_time_billed_to_me, credit);
1259 ledger_debit(bank_holder_ledger, task_ledgers.cpu_time_billed_to_me, debit);
1260 }
1261
1262 if (bank_merchant_ledger) {
1263 ledger_credit(bank_merchant_ledger, task_ledgers.cpu_time_billed_to_others, credit);
1264 ledger_debit(bank_merchant_ledger, task_ledgers.cpu_time_billed_to_others, debit);
1265 }
1266 }
1267
1268 ret = ledger_get_entries(bill, bank_ledgers.energy, &credit, &debit);
1269 if (ret == KERN_SUCCESS) {
1270 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE,
1271 (BANK_CODE(BANK_ACCOUNT_INFO, (BANK_SETTLE_ENERGY))) | DBG_FUNC_NONE,
1272 bank_merchant_pid, bank_holder_pid, credit, debit, 0);
1273
1274 if (bank_holder_ledger) {
1275 ledger_credit(bank_holder_ledger, task_ledgers.energy_billed_to_me, credit);
1276 ledger_debit(bank_holder_ledger, task_ledgers.energy_billed_to_me, debit);
1277 }
1278
1279 if (bank_merchant_ledger) {
1280 ledger_credit(bank_merchant_ledger, task_ledgers.energy_billed_to_others, credit);
1281 ledger_debit(bank_merchant_ledger, task_ledgers.energy_billed_to_others, debit);
1282 }
1283 }
1284}
1285
1286
1287
1288/*
1289 * Routine: bank_task_destroy
1290 * Purpose: Drops reference on bank task.
1291 * Returns: None.
1292 */
1293void
1294bank_task_destroy(task_t task)
1295{
1296 bank_task_t bank_task;
1297
1298 /* Grab the global bank task lock before dropping the ref on task bank context */
1299 global_bank_task_lock();
1300 bank_task = task->bank_context;
1301 task->bank_context = NULL;
1302 global_bank_task_unlock();
1303
1304 bank_destroy_bank_task_ledger(bank_task);
1305 bank_task_dealloc(bank_task, 1);
1306}
1307
1308/*
1309 * Routine: bank_task_initialize
1310 * Purpose: Initialize the bank context of a task.
1311 * Returns: None.
1312 */
1313void
1314bank_task_initialize(task_t task)
1315{
1316 get_bank_task_context(task, TRUE);
1317}
1318
1319/*
1320 * Routine: init_bank_ledgers
1321 * Purpose: Initialize template for bank ledgers.
1322 * Returns: None.
1323 */
1324static void
1325init_bank_ledgers(void)
1326{
1327 ledger_template_t t;
1328 int idx;
1329
1330 assert(bank_ledger_template == NULL);
1331
1332 if ((t = ledger_template_create("Bank ledger")) == NULL) {
1333 panic("couldn't create bank ledger template");
1334 }
1335
1336 if ((idx = ledger_entry_add(t, "cpu_time", "sched", "ns")) < 0) {
1337 panic("couldn't create cpu_time entry for bank ledger template");
1338 }
1339 bank_ledgers.cpu_time = idx;
1340
1341 if ((idx = ledger_entry_add(t, "energy", "power", "nj")) < 0) {
1342 panic("couldn't create energy entry for bank ledger template");
1343 }
1344 bank_ledgers.energy = idx;
1345
1346 ledger_template_complete(t);
1347 bank_ledger_template = t;
1348}
1349
1350/* Routine: bank_billed_balance_safe
1351 * Purpose: Walk through all the bank accounts billed to me by other tasks and get the current billing balance.
1352 * Called from another task. It takes global bank task lock to make sure the bank context is
1353 * not deallocated while accesing it.
1354 * Returns: cpu balance and energy balance in out paremeters.
1355 */
1356void
1357bank_billed_balance_safe(task_t task, uint64_t *cpu_time, uint64_t *energy)
1358{
1359 bank_task_t bank_task = BANK_TASK_NULL;
1360 ledger_amount_t credit, debit;
1361 uint64_t cpu_balance = 0;
1362 uint64_t energy_balance = 0;
1363 kern_return_t kr;
1364
1365 /* Task might be in exec, grab the global bank task lock before accessing bank context. */
1366 global_bank_task_lock();
1367 /* Grab a reference on bank context */
1368 if (task->bank_context != NULL) {
1369 bank_task = task->bank_context;
1370 bank_task_reference(bank_task);
1371 }
1372 global_bank_task_unlock();
1373
1374 if (bank_task) {
1375 bank_billed_balance(bank_task, &cpu_balance, &energy_balance);
1376 bank_task_dealloc(bank_task, 1);
1377 } else {
1378 kr = ledger_get_entries(task->ledger, task_ledgers.cpu_time_billed_to_me,
1379 &credit, &debit);
1380 if (kr == KERN_SUCCESS) {
1381 cpu_balance = credit - debit;
1382 }
1383 kr = ledger_get_entries(task->ledger, task_ledgers.energy_billed_to_me,
1384 &credit, &debit);
1385 if (kr == KERN_SUCCESS) {
1386 energy_balance = credit - debit;
1387 }
1388 }
1389
1390 *cpu_time = cpu_balance;
1391 *energy = energy_balance;
1392 return;
1393}
1394
1395/*
1396 * Routine: bank_billed_time
1397 * Purpose: Walk through the Accounts need to pay account list and get the current billing balance.
1398 * Returns: cpu balance and energy balance in out paremeters.
1399 */
1400void
1401bank_billed_balance(bank_task_t bank_task, uint64_t *cpu_time, uint64_t *energy)
1402{
1403 int64_t cpu_balance = 0;
1404 int64_t energy_balance = 0;
1405 bank_account_t bank_account;
1406 int64_t temp = 0;
1407 kern_return_t kr;
1408 if (bank_task == BANK_TASK_NULL) {
1409 *cpu_time = 0;
1410 *energy = 0;
1411 return;
1412 }
1413
1414 lck_mtx_lock(&bank_task->bt_acc_to_pay_lock);
1415
1416 /* bt_acc_to_pay_lock locked, no need to take ledger reference for bt_ledger */
1417 if (bank_task->bt_ledger != LEDGER_NULL) {
1418 kr = ledger_get_balance(bank_task->bt_ledger, task_ledgers.cpu_time_billed_to_me, &temp);
1419 if (kr == KERN_SUCCESS && temp >= 0) {
1420 cpu_balance += temp;
1421 }
1422#if DEVELOPMENT || DEBUG
1423 else {
1424 printf("bank_bill_time: ledger_get_balance failed or negative balance in ledger: %lld\n", temp);
1425 }
1426#endif /* DEVELOPMENT || DEBUG */
1427
1428 kr = ledger_get_balance(bank_task->bt_ledger, task_ledgers.energy_billed_to_me, &temp);
1429 if (kr == KERN_SUCCESS && temp >= 0) {
1430 energy_balance += temp;
1431 }
1432 }
1433
1434 queue_iterate(&bank_task->bt_accounts_to_pay, bank_account, bank_account_t, ba_next_acc_to_pay) {
1435 temp = 0;
1436 kr = ledger_get_balance(bank_account->ba_bill, bank_ledgers.cpu_time, &temp);
1437 if (kr == KERN_SUCCESS && temp >= 0) {
1438 cpu_balance += temp;
1439 }
1440#if DEVELOPMENT || DEBUG
1441 else {
1442 printf("bank_bill_time: ledger_get_balance failed or negative balance in ledger: %lld\n", temp);
1443 }
1444#endif /* DEVELOPMENT || DEBUG */
1445
1446 kr = ledger_get_balance(bank_account->ba_bill, bank_ledgers.energy, &temp);
1447 if (kr == KERN_SUCCESS && temp >= 0) {
1448 energy_balance += temp;
1449 }
1450 }
1451 lck_mtx_unlock(&bank_task->bt_acc_to_pay_lock);
1452 *cpu_time = (uint64_t)cpu_balance;
1453 *energy = (uint64_t)energy_balance;
1454 return;
1455}
1456
1457/* Routine: bank_serviced_balance_safe
1458 * Purpose: Walk through the bank accounts billed to other tasks by me and get the current balance to be charged.
1459 * Called from another task. It takes global bank task lock to make sure the bank context is
1460 * not deallocated while accesing it.
1461 * Returns: cpu balance and energy balance in out paremeters.
1462 */
1463void
1464bank_serviced_balance_safe(task_t task, uint64_t *cpu_time, uint64_t *energy)
1465{
1466 bank_task_t bank_task = BANK_TASK_NULL;
1467 ledger_amount_t credit, debit;
1468 uint64_t cpu_balance = 0;
1469 uint64_t energy_balance = 0;
1470 kern_return_t kr;
1471
1472 /* Task might be in exec, grab the global bank task lock before accessing bank context. */
1473 global_bank_task_lock();
1474 /* Grab a reference on bank context */
1475 if (task->bank_context != NULL) {
1476 bank_task = task->bank_context;
1477 bank_task_reference(bank_task);
1478 }
1479 global_bank_task_unlock();
1480
1481 if (bank_task) {
1482 bank_serviced_balance(bank_task, &cpu_balance, &energy_balance);
1483 bank_task_dealloc(bank_task, 1);
1484 } else {
1485 kr = ledger_get_entries(task->ledger, task_ledgers.cpu_time_billed_to_others,
1486 &credit, &debit);
1487 if (kr == KERN_SUCCESS) {
1488 cpu_balance = credit - debit;
1489 }
1490
1491 kr = ledger_get_entries(task->ledger, task_ledgers.energy_billed_to_others,
1492 &credit, &debit);
1493 if (kr == KERN_SUCCESS) {
1494 energy_balance = credit - debit;
1495 }
1496 }
1497
1498 *cpu_time = cpu_balance;
1499 *energy = energy_balance;
1500 return;
1501}
1502
1503/*
1504 * Routine: bank_serviced_balance
1505 * Purpose: Walk through the Account need to charge account list and get the current balance to be charged.
1506 * Returns: cpu balance and energy balance in out paremeters.
1507 */
1508void
1509bank_serviced_balance(bank_task_t bank_task, uint64_t *cpu_time, uint64_t *energy)
1510{
1511 int64_t cpu_balance = 0;
1512 int64_t energy_balance = 0;
1513 bank_account_t bank_account;
1514 int64_t temp = 0;
1515 kern_return_t kr;
1516 ledger_t ledger = LEDGER_NULL;
1517 if (bank_task == BANK_TASK_NULL) {
1518 *cpu_time = 0;
1519 *energy = 0;
1520 return;
1521 }
1522
1523 /* Grab a ledger reference on bt_ledger for bank_task */
1524 ledger = bank_get_bank_task_ledger_with_ref(bank_task);
1525
1526 lck_mtx_lock(&bank_task->bt_acc_to_charge_lock);
1527
1528 if (ledger) {
1529 kr = ledger_get_balance(ledger, task_ledgers.cpu_time_billed_to_others, &temp);
1530 if (kr == KERN_SUCCESS && temp >= 0) {
1531 cpu_balance += temp;
1532 }
1533#if DEVELOPMENT || DEBUG
1534 else {
1535 printf("bank_serviced_time: ledger_get_balance failed or negative balance in ledger: %lld\n", temp);
1536 }
1537#endif /* DEVELOPMENT || DEBUG */
1538
1539 kr = ledger_get_balance(ledger, task_ledgers.energy_billed_to_others, &temp);
1540 if (kr == KERN_SUCCESS && temp >= 0) {
1541 energy_balance += temp;
1542 }
1543 }
1544
1545 queue_iterate(&bank_task->bt_accounts_to_charge, bank_account, bank_account_t, ba_next_acc_to_charge) {
1546 temp = 0;
1547 kr = ledger_get_balance(bank_account->ba_bill, bank_ledgers.cpu_time, &temp);
1548 if (kr == KERN_SUCCESS && temp >= 0) {
1549 cpu_balance += temp;
1550 }
1551#if DEVELOPMENT || DEBUG
1552 else {
1553 printf("bank_serviced_time: ledger_get_balance failed or negative balance in ledger: %lld\n", temp);
1554 }
1555#endif /* DEVELOPMENT || DEBUG */
1556
1557 kr = ledger_get_balance(bank_account->ba_bill, bank_ledgers.energy, &temp);
1558 if (kr == KERN_SUCCESS && temp >= 0) {
1559 energy_balance += temp;
1560 }
1561 }
1562 lck_mtx_unlock(&bank_task->bt_acc_to_charge_lock);
1563 if (ledger) {
1564 ledger_dereference(ledger);
1565 }
1566 *cpu_time = (uint64_t)cpu_balance;
1567 *energy = (uint64_t)energy_balance;
1568 return;
1569}
1570
1571/*
1572 * Routine: bank_get_voucher_bank_account
1573 * Purpose: Get the bank account from the voucher.
1574 * Returns: bank_account if bank_account attribute present in voucher.
1575 * NULL on no attribute or no bank_element
1576 */
1577static bank_account_t
1578bank_get_voucher_bank_account(ipc_voucher_t voucher)
1579{
1580 bank_element_t bank_element = BANK_ELEMENT_NULL;
1581 bank_account_t bank_account = BANK_ACCOUNT_NULL;
1582 mach_voucher_attr_value_handle_t vals[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED];
1583 mach_voucher_attr_value_handle_array_size_t val_count;
1584 kern_return_t kr;
1585
1586 val_count = MACH_VOUCHER_ATTR_VALUE_MAX_NESTED;
1587 kr = mach_voucher_attr_control_get_values(bank_voucher_attr_control,
1588 voucher,
1589 vals,
1590 &val_count);
1591
1592 if (kr != KERN_SUCCESS || val_count == 0) {
1593 return BANK_ACCOUNT_NULL;
1594 }
1595
1596 bank_element = HANDLE_TO_BANK_ELEMENT(vals[0]);
1597 if (bank_element == BANK_DEFAULT_VALUE) {
1598 return BANK_ACCOUNT_NULL;
1599 }
1600 if (bank_element == BANK_DEFAULT_TASK_VALUE) {
1601 bank_element = CAST_TO_BANK_ELEMENT(get_bank_task_context(current_task(), FALSE));
1602 }
1603
1604 if (bank_element->be_type == BANK_TASK) {
1605 return BANK_ACCOUNT_NULL;
1606 } else if (bank_element->be_type == BANK_ACCOUNT) {
1607 bank_account = CAST_TO_BANK_ACCOUNT(bank_element);
1608 return bank_account;
1609 } else {
1610 panic("Bogus bank type: %d passed in bank_get_voucher_bank_account\n", bank_element->be_type);
1611 }
1612 return BANK_ACCOUNT_NULL;
1613}
1614
1615/*
1616 * Routine: bank_get_bank_task_ledger_with_ref
1617 * Purpose: Get the bank ledger from the bank task and return a reference to it.
1618 */
1619static ledger_t
1620bank_get_bank_task_ledger_with_ref(bank_task_t bank_task)
1621{
1622 ledger_t ledger = LEDGER_NULL;
1623
1624 lck_mtx_lock(&bank_task->bt_acc_to_pay_lock);
1625 ledger = bank_task->bt_ledger;
1626 if (ledger) {
1627 ledger_reference(ledger);
1628 }
1629 lck_mtx_unlock(&bank_task->bt_acc_to_pay_lock);
1630
1631 return ledger;
1632}
1633
1634/*
1635 * Routine: bank_destroy_bank_task_ledger
1636 * Purpose: Drop the bank task reference on the task ledger.
1637 */
1638static void
1639bank_destroy_bank_task_ledger(bank_task_t bank_task)
1640{
1641 ledger_t ledger;
1642
1643 /* Remove the ledger reference from the bank task */
1644 lck_mtx_lock(&bank_task->bt_acc_to_pay_lock);
1645 assert(LEDGER_VALID(bank_task->bt_ledger));
1646 ledger = bank_task->bt_ledger;
1647 bank_task->bt_ledger = LEDGER_NULL;
1648 lck_mtx_unlock(&bank_task->bt_acc_to_pay_lock);
1649
1650 ledger_dereference(ledger);
1651}
1652
1653/*
1654 * Routine: bank_get_bank_account_ledger
1655 * Purpose: Get the bankledger from the bank account if ba_merchant different than ba_holder
1656 */
1657static ledger_t
1658bank_get_bank_account_ledger(bank_account_t bank_account)
1659{
1660 ledger_t bankledger = LEDGER_NULL;
1661
1662 if (bank_account != BANK_ACCOUNT_NULL &&
1663 bank_account->ba_holder != bank_account->ba_merchant) {
1664 bankledger = bank_account->ba_bill;
1665 }
1666
1667 return bankledger;
1668}
1669
1670/*
1671 * Routine: bank_get_bank_task_thread_group
1672 * Purpose: Get the bank task's thread group from the bank task
1673 */
1674static struct thread_group *
1675bank_get_bank_task_thread_group(bank_task_t bank_task __unused)
1676{
1677 struct thread_group *banktg = NULL;
1678
1679#if CONFIG_THREAD_GROUPS
1680 if (bank_task != BANK_TASK_NULL) {
1681 banktg = bank_task->bt_thread_group;
1682 }
1683#endif /* CONFIG_THREAD_GROUPS */
1684
1685 return banktg;
1686}
1687
1688/*
1689 * Routine: bank_get_bank_account_thread_group
1690 * Purpose: Get the bank account's thread group from the bank account
1691 */
1692static struct thread_group *
1693bank_get_bank_account_thread_group(bank_account_t bank_account __unused)
1694{
1695 struct thread_group *banktg = NULL;
1696
1697#if CONFIG_THREAD_GROUPS
1698 if (bank_account != BANK_ACCOUNT_NULL) {
1699 banktg = bank_account->ba_thread_group;
1700 }
1701#endif /* CONFIG_THREAD_GROUPS */
1702
1703 return banktg;
1704}
1705
1706/*
1707 * Routine: bank_get_bank_ledger_thread_group_and_persona
1708 * Purpose: Get the bankledger (chit), thread group and persona id from the voucher.
1709 * Returns: bankledger, thread group if bank_account attribute present in voucher
1710 * and persona_id
1711 */
1712kern_return_t
1713bank_get_bank_ledger_thread_group_and_persona(
1714 ipc_voucher_t voucher,
1715 ledger_t *bankledger,
1716 struct thread_group **banktg,
1717 uint32_t *persona_id)
1718{
1719 bank_account_t bank_account;
1720 bank_task_t bank_task;
1721 struct thread_group *thread_group = NULL;
1722
1723 bank_account = bank_get_voucher_bank_account(voucher);
1724 bank_task = get_bank_task_context(current_task(), FALSE);
1725 if (persona_id != NULL) {
1726 if (bank_account != BANK_ACCOUNT_NULL) {
1727 *persona_id = bank_account->ba_so_persona_id;
1728 } else {
1729 *persona_id = bank_task->bt_persona_id;
1730 }
1731 }
1732 /*
1733 * Use BANK_ACCOUNT_NULL if the ba_holder is same as ba_merchant
1734 * and bank account thread group is same as current thread group
1735 * i.e. ba_merchant's thread group.
1736 *
1737 * The bank account might have ba_holder same as ba_merchant but different
1738 * thread group if daemon sends a voucher to an App and then App sends the
1739 * same voucher back to the daemon (IPC code will replace thread group in the
1740 * voucher to App's thread group when it gets auto redeemed by the App).
1741 */
1742 if ((bank_account != NULL) &&
1743 (bank_account->ba_holder == bank_account->ba_merchant) &&
1744 (bank_get_bank_account_thread_group(bank_account) ==
1745 bank_get_bank_task_thread_group(bank_account->ba_merchant))) {
1746 bank_account = BANK_ACCOUNT_NULL;
1747 }
1748
1749 if (bankledger != NULL) {
1750 *bankledger = bank_get_bank_account_ledger(bank_account);
1751 }
1752
1753 if (banktg != NULL) {
1754 thread_group = bank_get_bank_account_thread_group(bank_account);
1755
1756 /* Return NULL thread group if voucher has current task's thread group */
1757 if (thread_group == bank_get_bank_task_thread_group(bank_task)) {
1758 thread_group = NULL;
1759 }
1760 *banktg = thread_group;
1761 }
1762 return KERN_SUCCESS;
1763}
1764
1765/*
1766 * Routine: bank_swap_thread_bank_ledger
1767 * Purpose: swap the bank ledger on the thread.
1768 * Returns: None.
1769 * Note: Should be only called for current thread or thread which is not started.
1770 */
1771void
1772bank_swap_thread_bank_ledger(thread_t thread __unused, ledger_t new_ledger __unused)
1773{
1774 spl_t s;
1775 processor_t processor;
1776 ledger_t old_ledger = thread->t_bankledger;
1777 int64_t ctime, effective_ledger_time_consumed = 0;
1778 int64_t remainder = 0, consumed = 0;
1779 int64_t effective_energy_consumed = 0;
1780 uint64_t thread_energy;
1781
1782 if (old_ledger == LEDGER_NULL && new_ledger == LEDGER_NULL) {
1783 return;
1784 }
1785
1786 assert((thread == current_thread() || thread->started == 0));
1787
1788 s = splsched();
1789 thread_lock(thread);
1790
1791 /*
1792 * Calculation of time elapsed by the thread before voucher swap.
1793 * Following is the timeline which shows all the variables used in the calculation below.
1794 *
1795 * thread ledger
1796 * cpu_time
1797 * |<- consumed ->|<- remainder ->|
1798 * timeline ----------------------------------------------------------------->
1799 * | | |
1800 * thread_dispatch ctime quantum end
1801 *
1802 * |<-effective_ledger_time -> |
1803 * deduct_bank_ledger_time
1804 */
1805
1806 ctime = mach_absolute_time();
1807 processor = thread->last_processor;
1808 if (processor != NULL) {
1809 if ((int64_t)processor->quantum_end > ctime) {
1810 remainder = (int64_t)processor->quantum_end - ctime;
1811 }
1812
1813 consumed = thread->quantum_remaining - remainder;
1814 effective_ledger_time_consumed = consumed - thread->t_deduct_bank_ledger_time;
1815 }
1816
1817 thread->t_deduct_bank_ledger_time = consumed;
1818
1819 thread_energy = ml_energy_stat(thread);
1820 effective_energy_consumed =
1821 thread_energy - thread->t_deduct_bank_ledger_energy;
1822 assert(effective_energy_consumed >= 0);
1823 thread->t_deduct_bank_ledger_energy = thread_energy;
1824
1825 thread->t_bankledger = new_ledger;
1826
1827 thread_unlock(thread);
1828 splx(s);
1829
1830 if (old_ledger != LEDGER_NULL) {
1831 ledger_credit(old_ledger,
1832 bank_ledgers.cpu_time,
1833 effective_ledger_time_consumed);
1834 ledger_credit(old_ledger,
1835 bank_ledgers.energy,
1836 effective_energy_consumed);
1837 }
1838}
1839
1840/*
1841 * Routine: bank_verify_persona_id
1842 * Purpose: Verifies if the persona id is valid
1843 *
1844 * The caller should check if the task is entitled
1845 * to do the lookup.
1846 */
1847static boolean_t
1848bank_verify_persona_id(uint32_t persona_id)
1849{
1850 /* A successful lookup implies that the persona id is valid */
1851 void *persona = persona_lookup(persona_id);
1852 if (!persona) {
1853 return FALSE;
1854 }
1855 persona_put(persona);
1856
1857 return TRUE;
1858}