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