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