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