]> git.saurik.com Git - apple/xnu.git/blame - osfmk/bank/bank.c
xnu-3248.40.184.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
361 /* Check if trying to redeem for self task, return the bank task */
362 if (bank_holder == bank_merchant &&
363 bank_holder == bank_secureoriginator &&
364 bank_holder == bank_proximateprocess) {
365 bank_task_reference(bank_holder);
366 bank_task_made_reference(bank_holder);
367 *out_value = BANK_ELEMENT_TO_HANDLE(bank_holder);
368 return kr;
369 }
370
371 bank_account = bank_account_alloc_init(bank_holder, bank_merchant,
372 bank_secureoriginator, bank_proximateprocess);
373 if (bank_account == BANK_ACCOUNT_NULL)
374 return KERN_RESOURCE_SHORTAGE;
375
376 *out_value = BANK_ELEMENT_TO_HANDLE(bank_account);
377 return kr;
378 }
379
380 *out_value = BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_VALUE);
fe8ab488
A
381 break;
382
490019cf 383 case MACH_VOUCHER_ATTR_SEND_PREPROCESS:
fe8ab488
A
384
385 for (i = 0; i < prev_value_count; i++) {
386 bank_handle = prev_values[i];
387 bank_element = HANDLE_TO_BANK_ELEMENT(bank_handle);
388
389 if (bank_element == BANK_DEFAULT_VALUE)
390 continue;
391
392 task = current_task();
490019cf
A
393 if (bank_element == BANK_DEFAULT_TASK_VALUE) {
394 bank_element = CAST_TO_BANK_ELEMENT(get_bank_task_context(task, FALSE));
395 }
396
fe8ab488
A
397 if (bank_element->be_type == BANK_TASK) {
398 bank_holder = CAST_TO_BANK_TASK(bank_element);
490019cf 399 bank_secureoriginator = bank_holder;
fe8ab488
A
400 } else if (bank_element->be_type == BANK_ACCOUNT) {
401 old_bank_account = CAST_TO_BANK_ACCOUNT(bank_element);
402 bank_holder = old_bank_account->ba_holder;
490019cf 403 bank_secureoriginator = old_bank_account->ba_secureoriginator;
fe8ab488
A
404 } else {
405 panic("Bogus bank type: %d passed in get_value\n", bank_element->be_type);
406 }
407
490019cf 408 bank_merchant = get_bank_task_context(task, FALSE);
fe8ab488
A
409 if (bank_merchant == BANK_TASK_NULL)
410 return KERN_RESOURCE_SHORTAGE;
411
490019cf
A
412 /*
413 * If the process doesn't have secure persona entitlement,
414 * then replace the secure originator to current task.
415 */
416 if (bank_merchant->bt_hasentitlement == 0) {
417 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE,
418 (BANK_CODE(BANK_ACCOUNT_INFO, (BANK_SECURE_ORIGINATOR_CHANGED))) | DBG_FUNC_NONE,
419 bank_secureoriginator->bt_pid, bank_merchant->bt_pid, 0, 0, 0);
420 bank_secureoriginator = bank_merchant;
421 }
422
423 bank_proximateprocess = bank_merchant;
424
fe8ab488 425 /* Check if trying to redeem for self task, return the bank task */
490019cf
A
426 if (bank_holder == bank_merchant &&
427 bank_holder == bank_secureoriginator &&
428 bank_holder == bank_proximateprocess) {
fe8ab488
A
429 bank_task_reference(bank_holder);
430 bank_task_made_reference(bank_holder);
431 *out_value = BANK_ELEMENT_TO_HANDLE(bank_holder);
432 return kr;
433 }
490019cf
A
434 bank_account = bank_account_alloc_init(bank_holder, bank_merchant,
435 bank_secureoriginator, bank_proximateprocess);
fe8ab488
A
436 if (bank_account == BANK_ACCOUNT_NULL)
437 return KERN_RESOURCE_SHORTAGE;
438
439 *out_value = BANK_ELEMENT_TO_HANDLE(bank_account);
440 return kr;
441 }
442
443 *out_value = BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_VALUE);
444 break;
490019cf
A
445
446 case MACH_VOUCHER_ATTR_REDEEM:
447
448 for (i = 0; i < prev_value_count; i++) {
449 bank_handle = prev_values[i];
450 bank_element = HANDLE_TO_BANK_ELEMENT(bank_handle);
451
452 if (bank_element == BANK_DEFAULT_VALUE)
453 continue;
454
455 task = current_task();
456 if (bank_element == BANK_DEFAULT_TASK_VALUE) {
457 *out_value = BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_TASK_VALUE);
458 *out_flags = MACH_VOUCHER_ATTR_VALUE_FLAGS_PERSIST;
459 return kr;
460 }
461 if (bank_element->be_type == BANK_TASK) {
462 bank_task = CAST_TO_BANK_TASK(bank_element);
463 if (bank_task != get_bank_task_context(task, FALSE)) {
464 panic("Found a bank task of another task with bank_context: %p", bank_task);
465 }
466
467 bank_task_reference(bank_task);
468 bank_task_made_reference(bank_task);
469 *out_value = BANK_ELEMENT_TO_HANDLE(bank_task);
470 return kr;
471
472 } else if (bank_element->be_type == BANK_ACCOUNT) {
473 bank_account = CAST_TO_BANK_ACCOUNT(bank_element);
474 bank_merchant = bank_account->ba_merchant;
475 if (bank_merchant != get_bank_task_context(task, FALSE)) {
476 panic("Found another bank task: %p as a bank merchant\n", bank_merchant);
477 }
478
479 bank_account_reference(bank_account);
480 bank_account_made_reference(bank_account);
481 *out_value = BANK_ELEMENT_TO_HANDLE(bank_account);
482 return kr;
483 } else {
484 panic("Bogus bank type: %d passed in get_value\n", bank_element->be_type);
485 }
486 }
487
488 *out_value = BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_VALUE);
489 break;
490
fe8ab488
A
491 default:
492 kr = KERN_INVALID_ARGUMENT;
493 break;
494 }
495
496 return kr;
497}
498
499
500/*
501 * Routine: bank_extract_content
502 * Purpose: Extract a set of aid from an array of voucher values.
503 * Returns: KERN_SUCCESS: on Success.
504 * KERN_FAILURE: one of the value is not present in the hash.
505 * KERN_NO_SPACE: insufficeint buffer provided to fill an array of aid.
506 */
507kern_return_t
508bank_extract_content(
509 ipc_voucher_attr_manager_t __assert_only manager,
510 mach_voucher_attr_key_t __assert_only key,
511 mach_voucher_attr_value_handle_array_t values,
512 mach_msg_type_number_t value_count,
513 mach_voucher_attr_recipe_command_t *out_command,
514 mach_voucher_attr_content_t out_recipe,
515 mach_voucher_attr_content_size_t *in_out_recipe_size)
516{
517 bank_task_t bank_task = BANK_TASK_NULL;
518 bank_element_t bank_element = BANK_ELEMENT_NULL;
519 bank_account_t bank_account = BANK_ACCOUNT_NULL;
520 mach_voucher_attr_value_handle_t bank_handle;
521 char buf[MACH_VOUCHER_BANK_CONTENT_SIZE];
522 mach_msg_type_number_t i;
523
524 assert(MACH_VOUCHER_ATTR_KEY_BANK == key);
525 assert(manager == &bank_manager);
526
527 for (i = 0; i < value_count; i++) {
528 bank_handle = values[i];
529 bank_element = HANDLE_TO_BANK_ELEMENT(bank_handle);
530 if (bank_element == BANK_DEFAULT_VALUE)
531 continue;
532
490019cf
A
533 if (bank_element == BANK_DEFAULT_TASK_VALUE) {
534 bank_element = CAST_TO_BANK_ELEMENT(get_bank_task_context(current_task(), FALSE));
535 }
536
fe8ab488
A
537 if (MACH_VOUCHER_BANK_CONTENT_SIZE > *in_out_recipe_size) {
538 *in_out_recipe_size = 0;
539 return KERN_NO_SPACE;
540 }
541
542 if (bank_element->be_type == BANK_TASK) {
543 bank_task = CAST_TO_BANK_TASK(bank_element);
544 snprintf(buf, MACH_VOUCHER_BANK_CONTENT_SIZE,
545 " Bank Context for a pid %d\n", bank_task->bt_pid);
546 } else if (bank_element->be_type == BANK_ACCOUNT) {
547 bank_account = CAST_TO_BANK_ACCOUNT(bank_element);
548 snprintf(buf, MACH_VOUCHER_BANK_CONTENT_SIZE,
490019cf 549 " Bank Account linking holder pid %d with merchant pid %d, originator PID/persona: %d, %u and proximate PID/persona: %d, %u\n",
fe8ab488 550 bank_account->ba_holder->bt_pid,
490019cf
A
551 bank_account->ba_merchant->bt_pid,
552 bank_account->ba_secureoriginator->bt_pid,
553 bank_account->ba_secureoriginator->bt_persona_id,
554 bank_account->ba_proximateprocess->bt_pid,
555 bank_account->ba_proximateprocess->bt_persona_id);
fe8ab488
A
556 } else {
557 panic("Bogus bank type: %d passed in get_value\n", bank_element->be_type);
558 }
559
560
561 memcpy(&out_recipe[0], buf, strlen(buf) + 1);
562 *out_command = MACH_VOUCHER_ATTR_BANK_NULL;
563 *in_out_recipe_size = (mach_voucher_attr_content_size_t)strlen(buf) + 1;
564 return KERN_SUCCESS;
565 }
566
567 return KERN_SUCCESS;
568}
569
570/*
571 * Routine: bank_command
572 * Purpose: Execute a command against a set of ATM values.
573 * Returns: KERN_SUCCESS: On successful execution of command.
574 KERN_FAILURE: On failure.
575 */
576kern_return_t
577bank_command(
578 ipc_voucher_attr_manager_t __assert_only manager,
579 mach_voucher_attr_key_t __assert_only key,
580 mach_voucher_attr_value_handle_array_t __unused values,
581 mach_msg_type_number_t __unused value_count,
582 mach_voucher_attr_command_t __unused command,
583 mach_voucher_attr_content_t __unused in_content,
584 mach_voucher_attr_content_size_t __unused in_content_size,
585 mach_voucher_attr_content_t __unused out_content,
586 mach_voucher_attr_content_size_t __unused *out_content_size)
587{
588 bank_task_t bank_task = BANK_TASK_NULL;
490019cf
A
589 bank_task_t bank_secureoriginator = BANK_TASK_NULL;
590 bank_task_t bank_proximateprocess = BANK_TASK_NULL;
591 struct persona_token *token = NULL;
fe8ab488
A
592 bank_element_t bank_element = BANK_ELEMENT_NULL;
593 bank_account_t bank_account = BANK_ACCOUNT_NULL;
594 mach_voucher_attr_value_handle_t bank_handle;
595 mach_msg_type_number_t i;
596 int32_t pid;
597
598 assert(MACH_VOUCHER_ATTR_KEY_BANK == key);
599 assert(manager == &bank_manager);
600
601 switch (command) {
602 case BANK_ORIGINATOR_PID:
603
604 if ((sizeof(pid)) > *out_content_size) {
605 *out_content_size = 0;
606 return KERN_NO_SPACE;
607 }
608
609 for (i = 0; i < value_count; i++) {
610 bank_handle = values[i];
611 bank_element = HANDLE_TO_BANK_ELEMENT(bank_handle);
612 if (bank_element == BANK_DEFAULT_VALUE)
613 continue;
614
490019cf
A
615 if (bank_element == BANK_DEFAULT_TASK_VALUE) {
616 bank_element = CAST_TO_BANK_ELEMENT(get_bank_task_context(current_task(), FALSE));
617 }
618
fe8ab488
A
619 if (bank_element->be_type == BANK_TASK) {
620 bank_task = CAST_TO_BANK_TASK(bank_element);
621 } else if (bank_element->be_type == BANK_ACCOUNT) {
622 bank_account = CAST_TO_BANK_ACCOUNT(bank_element);
623 bank_task = bank_account->ba_holder;
624 } else {
625 panic("Bogus bank type: %d passed in voucher_command\n", bank_element->be_type);
626 }
627 pid = bank_task->bt_pid;
628
629 memcpy(&out_content[0], &pid, sizeof(pid));
630 *out_content_size = (mach_voucher_attr_content_size_t)sizeof(pid);
631 return KERN_SUCCESS;
632 }
633 /* In the case of no value, return error KERN_INVALID_VALUE */
634 *out_content_size = 0;
635 return KERN_INVALID_VALUE;
636
637 break;
490019cf
A
638
639 case BANK_PERSONA_TOKEN:
640
641 if ((sizeof(struct persona_token)) > *out_content_size) {
642 *out_content_size = 0;
643 return KERN_NO_SPACE;
644 }
645 for (i = 0; i < value_count; i++) {
646 bank_handle = values[i];
647 bank_element = HANDLE_TO_BANK_ELEMENT(bank_handle);
648 if (bank_element == BANK_DEFAULT_VALUE)
649 continue;
650
651 if (bank_element == BANK_DEFAULT_TASK_VALUE) {
652 bank_element = CAST_TO_BANK_ELEMENT(get_bank_task_context(current_task(), FALSE));
653 }
654
655 if (bank_element->be_type == BANK_TASK) {
656 *out_content_size = 0;
657 return KERN_INVALID_OBJECT;
658 } else if (bank_element->be_type == BANK_ACCOUNT) {
659 bank_account = CAST_TO_BANK_ACCOUNT(bank_element);
660 bank_secureoriginator = bank_account->ba_secureoriginator;
661 bank_proximateprocess = bank_account->ba_proximateprocess;
662 } else {
663 panic("Bogus bank type: %d passed in voucher_command\n", bank_element->be_type);
664 }
665 token = (struct persona_token *)(void *)&out_content[0];
666 memcpy(&token->originator, &bank_secureoriginator->bt_proc_persona, sizeof(struct proc_persona_info));
667 memcpy(&token->proximate, &bank_proximateprocess->bt_proc_persona, sizeof(struct proc_persona_info));
668
669 *out_content_size = (mach_voucher_attr_content_size_t)sizeof(*token);
670 return KERN_SUCCESS;
671 }
672 /* In the case of no value, return error KERN_INVALID_VALUE */
673 *out_content_size = 0;
674 return KERN_INVALID_VALUE;
675
676 break;
677
fe8ab488
A
678 default:
679 return KERN_INVALID_ARGUMENT;
680 }
681 return KERN_SUCCESS;
682}
683
684
685void
686bank_release(
687 ipc_voucher_attr_manager_t __assert_only manager)
688{
689 assert(manager == &bank_manager);
690}
691
692
693
694/*
695 * Bank Internal Routines.
696 */
697
698/*
699 * Routine: bank_task_alloc_init
700 * Purpose: Allocate and initialize a bank task structure.
701 * Returns: bank_task_t on Success.
702 * BANK_TASK_NULL: on Failure.
703 * Notes: Leaves the task and creditcard blank and has only 1 ref,
704 needs to take 1 extra ref after the task field is initialized.
705 */
706static bank_task_t
490019cf 707bank_task_alloc_init(task_t task)
fe8ab488
A
708{
709 bank_task_t new_bank_task;
710
711 new_bank_task = (bank_task_t) zalloc(bank_task_zone);
712 if (new_bank_task == BANK_TASK_NULL)
713 return BANK_TASK_NULL;
714
715 new_bank_task->bt_type = BANK_TASK;
716 new_bank_task->bt_refs = 1;
717 new_bank_task->bt_made = 0;
fe8ab488 718 new_bank_task->bt_creditcard = NULL;
490019cf 719 new_bank_task->bt_hasentitlement = bank_task_is_propagate_entitled(task);
fe8ab488
A
720 queue_init(&new_bank_task->bt_accounts_to_pay);
721 queue_init(&new_bank_task->bt_accounts_to_charge);
722 lck_mtx_init(&new_bank_task->bt_acc_to_pay_lock, &bank_lock_grp, &bank_lock_attr);
723 lck_mtx_init(&new_bank_task->bt_acc_to_charge_lock, &bank_lock_grp, &bank_lock_attr);
724
490019cf
A
725 /*
726 * Initialize the persona_id struct
727 */
728 bzero(&new_bank_task->bt_proc_persona, sizeof(new_bank_task->bt_proc_persona));
729 new_bank_task->bt_flags = 0;
730 new_bank_task->bt_unique_pid = proc_uniqueid(task->bsd_info);
731 new_bank_task->bt_pid = proc_pid(task->bsd_info);
732 new_bank_task->bt_pidversion = proc_pidversion(task->bsd_info);
733 new_bank_task->bt_persona_id = proc_persona_id(task->bsd_info);
734 new_bank_task->bt_uid = proc_getuid(task->bsd_info);
735 new_bank_task->bt_gid = proc_getgid(task->bsd_info);
736 proc_getexecutableuuid(task->bsd_info, new_bank_task->bt_macho_uuid, sizeof(new_bank_task->bt_macho_uuid));
737
fe8ab488
A
738#if DEVELOPMENT || DEBUG
739 new_bank_task->bt_task = NULL;
740 lck_mtx_lock(&bank_tasks_list_lock);
741 queue_enter(&bank_tasks_list, new_bank_task, bank_task_t, bt_global_elt);
742 lck_mtx_unlock(&bank_tasks_list_lock);
743#endif
744 return (new_bank_task);
745}
746
490019cf
A
747/*
748 * Routine: proc_is_propagate_entitled
749 * Purpose: Check if the process has persona propagate entitlement.
750 * Returns: TRUE if entitled.
751 * FALSE if not.
752 */
753static boolean_t
754bank_task_is_propagate_entitled(task_t t)
755{
756 /* Return TRUE if root process */
757 if (0 == kauth_cred_issuser(kauth_cred_get())) {
758 /* If it's a non-root process, it needs to have the entitlement for secure originator propagation */
759 boolean_t entitled = FALSE;
760 entitled = IOTaskHasEntitlement(t, ENTITLEMENT_PERSONA_PROPAGATE);
761 return entitled;
762 } else {
763 return TRUE;
764 }
765}
766
fe8ab488
A
767/*
768 * Routine: bank_account_alloc_init
769 * Purpose: Allocate and Initialize the bank account struct.
770 * Returns: bank_account_t : On Success.
771 * BANK_ACCOUNT_NULL: On Failure.
772 */
773static bank_account_t
774bank_account_alloc_init(
775 bank_task_t bank_holder,
490019cf
A
776 bank_task_t bank_merchant,
777 bank_task_t bank_secureoriginator,
778 bank_task_t bank_proximateprocess)
fe8ab488
A
779{
780 bank_account_t new_bank_account;
781 bank_account_t bank_account;
782 boolean_t entry_found = FALSE;
783 ledger_t new_ledger = ledger_instantiate(bank_ledger_template, LEDGER_CREATE_INACTIVE_ENTRIES);
784
785 if (new_ledger == NULL)
786 return BANK_ACCOUNT_NULL;
787
788 ledger_entry_setactive(new_ledger, bank_ledgers.cpu_time);
789 new_bank_account = (bank_account_t) zalloc(bank_account_zone);
790 if (new_bank_account == BANK_ACCOUNT_NULL) {
791 ledger_dereference(new_ledger);
792 return BANK_ACCOUNT_NULL;
793 }
794
795 new_bank_account->ba_type = BANK_ACCOUNT;
796 new_bank_account->ba_refs = 1;
797 new_bank_account->ba_made = 1;
fe8ab488
A
798 new_bank_account->ba_bill = new_ledger;
799 new_bank_account->ba_merchant = bank_merchant;
800 new_bank_account->ba_holder = bank_holder;
490019cf
A
801 new_bank_account->ba_secureoriginator = bank_secureoriginator;
802 new_bank_account->ba_proximateprocess = bank_proximateprocess;
fe8ab488
A
803
804 /* Iterate through accounts need to pay list to find the existing entry */
805 lck_mtx_lock(&bank_holder->bt_acc_to_pay_lock);
806 queue_iterate(&bank_holder->bt_accounts_to_pay, bank_account, bank_account_t, ba_next_acc_to_pay) {
490019cf
A
807 if (bank_account->ba_merchant != bank_merchant ||
808 bank_account->ba_secureoriginator != bank_secureoriginator ||
809 bank_account->ba_proximateprocess != bank_proximateprocess)
fe8ab488
A
810 continue;
811
812 entry_found = TRUE;
813 /* Take a made ref, since this value would be returned to voucher system. */
814 bank_account_reference(bank_account);
815 bank_account_made_reference(bank_account);
816 break;
817 }
818
819 if (!entry_found) {
820
821 /* Create a linkage between the holder and the merchant task, Grab both the list locks before adding it to the list. */
822 lck_mtx_lock(&bank_merchant->bt_acc_to_charge_lock);
823
824 /* Add the account entry into Accounts need to pay account link list. */
825 queue_enter(&bank_holder->bt_accounts_to_pay, new_bank_account, bank_account_t, ba_next_acc_to_pay);
826
827 /* Add the account entry into Accounts need to charge account link list. */
828 queue_enter(&bank_merchant->bt_accounts_to_charge, new_bank_account, bank_account_t, ba_next_acc_to_charge);
829
830 lck_mtx_unlock(&bank_merchant->bt_acc_to_charge_lock);
831 }
832
833 lck_mtx_unlock(&bank_holder->bt_acc_to_pay_lock);
834
835 if (entry_found) {
836 ledger_dereference(new_ledger);
837 zfree(bank_account_zone, new_bank_account);
838 return bank_account;
839 }
840
841 bank_task_reference(bank_holder);
842 bank_task_reference(bank_merchant);
490019cf
A
843 bank_task_reference(bank_secureoriginator);
844 bank_task_reference(bank_proximateprocess);
fe8ab488
A
845
846#if DEVELOPMENT || DEBUG
847 new_bank_account->ba_task = NULL;
848 lck_mtx_lock(&bank_accounts_list_lock);
849 queue_enter(&bank_accounts_list, new_bank_account, bank_account_t, ba_global_elt);
850 lck_mtx_unlock(&bank_accounts_list_lock);
851#endif
852
853 return (new_bank_account);
854}
855
856/*
857 * Routine: get_bank_task_context
858 * Purpose: Get the bank context of the given task
859 * Returns: bank_task_t on Success.
860 * BANK_TASK_NULL: on Failure.
861 * Note: Initialize bank context if NULL.
862 */
863static bank_task_t
490019cf
A
864get_bank_task_context
865 (task_t task,
866 boolean_t initialize)
fe8ab488
A
867{
868 bank_task_t bank_task;
869
490019cf
A
870 if (task->bank_context || !initialize) {
871 assert(task->bank_context != NULL);
fe8ab488 872 return (task->bank_context);
490019cf 873 }
fe8ab488 874
490019cf 875 bank_task = bank_task_alloc_init(task);
fe8ab488
A
876
877 /* Grab the task lock and check if we won the race. */
878 task_lock(task);
879 if (task->bank_context) {
880 task_unlock(task);
881 if (bank_task != BANK_TASK_NULL)
882 bank_task_dealloc(bank_task, 1);
883 return (task->bank_context);
884 } else if (bank_task == BANK_TASK_NULL) {
885 task_unlock(task);
886 return BANK_TASK_NULL;
887 }
888 /* We won the race. Take a ref on the ledger and initialize bank task. */
889 bank_task->bt_creditcard = task->ledger;
fe8ab488
A
890#if DEVELOPMENT || DEBUG
891 bank_task->bt_task = task;
892#endif
893 ledger_reference(task->ledger);
894
490019cf
A
895 /* Grab the global bank task lock before setting the bank context on a task */
896 global_bank_task_lock();
fe8ab488 897 task->bank_context = bank_task;
490019cf
A
898 global_bank_task_unlock();
899
fe8ab488
A
900 task_unlock(task);
901
902 return (bank_task);
903}
904
905/*
906 * Routine: bank_task_dealloc
907 * Purpose: Drops the reference on bank task.
908 * Returns: None.
909 */
910static void
911bank_task_dealloc(
912 bank_task_t bank_task,
913 mach_voucher_attr_value_reference_t sync)
914{
915 assert(bank_task->bt_refs >= 0);
916
917 if (bank_task_release_num(bank_task, sync) > (int)sync)
918 return;
919
920 assert(bank_task->bt_refs == 0);
921 assert(queue_empty(&bank_task->bt_accounts_to_pay));
922 assert(queue_empty(&bank_task->bt_accounts_to_charge));
923
924 ledger_dereference(bank_task->bt_creditcard);
925 lck_mtx_destroy(&bank_task->bt_acc_to_pay_lock, &bank_lock_grp);
926 lck_mtx_destroy(&bank_task->bt_acc_to_charge_lock, &bank_lock_grp);
927
928#if DEVELOPMENT || DEBUG
929 lck_mtx_lock(&bank_tasks_list_lock);
930 queue_remove(&bank_tasks_list, bank_task, bank_task_t, bt_global_elt);
931 lck_mtx_unlock(&bank_tasks_list_lock);
932#endif
933
934 zfree(bank_task_zone, bank_task);
935}
936
937/*
938 * Routine: bank_account_dealloc_with_sync
939 * Purpose: Drop the reference on bank account if the sync matches.
940 * Returns: KERN_SUCCESS if sync matches.
941 * KERN_FAILURE on mismatch.
942 */
943static kern_return_t
944bank_account_dealloc_with_sync(
945 bank_account_t bank_account,
946 mach_voucher_attr_value_reference_t sync)
947{
948 bank_task_t bank_holder = bank_account->ba_holder;
949 bank_task_t bank_merchant = bank_account->ba_merchant;
490019cf
A
950 bank_task_t bank_secureoriginator = bank_account->ba_secureoriginator;
951 bank_task_t bank_proximateprocess = bank_account->ba_proximateprocess;
fe8ab488
A
952
953 /* Grab the acc to pay list lock and check the sync value */
954 lck_mtx_lock(&bank_holder->bt_acc_to_pay_lock);
955
956 if (bank_account->ba_made != (int)sync) {
957 lck_mtx_unlock(&bank_holder->bt_acc_to_pay_lock);
958 return KERN_FAILURE;
959 }
960
961 bank_account_made_release_num(bank_account, sync);
962
963 if (bank_account_release_num(bank_account, sync) > (int)sync)
964 panic("Sync and ref value did not match for bank account %p\n", bank_account);
965
966
967 /* Grab both the acc to pay and acc to charge locks */
968 lck_mtx_lock(&bank_merchant->bt_acc_to_charge_lock);
969
970 bank_rollup_chit_to_tasks(bank_account->ba_bill, bank_holder, bank_merchant);
971
972 /* Remove the account entry from Accounts need to pay account link list. */
973 queue_remove(&bank_holder->bt_accounts_to_pay, bank_account, bank_account_t, ba_next_acc_to_pay);
974
975 /* Remove the account entry from Accounts need to charge account link list. */
976 queue_remove(&bank_merchant->bt_accounts_to_charge, bank_account, bank_account_t, ba_next_acc_to_charge);
977
978 lck_mtx_unlock(&bank_merchant->bt_acc_to_charge_lock);
979 lck_mtx_unlock(&bank_holder->bt_acc_to_pay_lock);
980
981 ledger_dereference(bank_account->ba_bill);
982
983 /* Drop the reference of bank holder and merchant */
984 bank_task_dealloc(bank_holder, 1);
985 bank_task_dealloc(bank_merchant, 1);
490019cf
A
986 bank_task_dealloc(bank_secureoriginator, 1);
987 bank_task_dealloc(bank_proximateprocess, 1);
fe8ab488
A
988
989#if DEVELOPMENT || DEBUG
990 lck_mtx_lock(&bank_accounts_list_lock);
991 queue_remove(&bank_accounts_list, bank_account, bank_account_t, ba_global_elt);
992 lck_mtx_unlock(&bank_accounts_list_lock);
993#endif
994
995 zfree(bank_account_zone, bank_account);
996 return KERN_SUCCESS;
997}
998
999/*
1000 * Routine: bank_rollup_chit_to_tasks
1001 * Purpose: Debit and Credit holder's and merchant's ledgers.
1002 * Returns: None.
1003 */
1004static void
1005bank_rollup_chit_to_tasks(
1006 ledger_t bill,
1007 bank_task_t bank_holder,
1008 bank_task_t bank_merchant)
1009{
1010 ledger_amount_t credit;
1011 ledger_amount_t debit;
1012 kern_return_t ret;
1013
490019cf
A
1014 if (bank_holder == bank_merchant)
1015 return;
1016
fe8ab488
A
1017 ret = ledger_get_entries(bill, bank_ledgers.cpu_time, &credit, &debit);
1018 if (ret != KERN_SUCCESS) {
1019 return;
1020 }
1021
3e170ce0
A
1022#if DEVELOPMENT || DEBUG
1023 if (debit != 0) {
1024 panic("bank_rollup: debit: %lld non zero\n", debit);
1025 }
1026#endif
1027
fe8ab488
A
1028 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, (BANK_CODE(BANK_ACCOUNT_INFO, (BANK_SETTLE_CPU_TIME))) | DBG_FUNC_NONE,
1029 bank_merchant->bt_pid, bank_holder->bt_pid, credit, debit, 0);
1030#if CONFIG_BANK
1031 ledger_credit(bank_holder->bt_creditcard, task_ledgers.cpu_time_billed_to_me, credit);
1032 ledger_debit(bank_holder->bt_creditcard, task_ledgers.cpu_time_billed_to_me, debit);
1033
1034 ledger_credit(bank_merchant->bt_creditcard, task_ledgers.cpu_time_billed_to_others, credit);
1035 ledger_debit(bank_merchant->bt_creditcard, task_ledgers.cpu_time_billed_to_others, debit);
1036#endif
1037}
1038
1039
1040
1041/*
1042 * Routine: bank_task_destroy
1043 * Purpose: Drops reference on bank task.
1044 * Returns: None.
1045 */
1046void
490019cf 1047bank_task_destroy(task_t task)
fe8ab488 1048{
490019cf
A
1049 bank_task_t bank_task;
1050
1051 /* Grab the global bank task lock before dropping the ref on task bank context */
1052 global_bank_task_lock();
1053 bank_task = task->bank_context;
1054 task->bank_context = NULL;
1055 global_bank_task_unlock();
1056
fe8ab488
A
1057 bank_task_dealloc(bank_task, 1);
1058}
1059
490019cf
A
1060/*
1061 * Routine: bank_task_initialize
1062 * Purpose: Initialize the bank context of a task.
1063 * Returns: None.
1064 */
1065void
1066bank_task_initialize(task_t task)
1067{
1068 get_bank_task_context(task, TRUE);
1069}
1070
fe8ab488
A
1071/*
1072 * Routine: init_bank_ledgers
1073 * Purpose: Initialize template for bank ledgers.
1074 * Returns: None.
1075 */
1076static void
1077init_bank_ledgers(void) {
1078 ledger_template_t t;
1079 int idx;
1080
1081 assert(bank_ledger_template == NULL);
1082
1083 if ((t = ledger_template_create("Bank ledger")) == NULL)
1084 panic("couldn't create bank ledger template");
1085
1086 if ((idx = ledger_entry_add(t, "cpu_time", "sched", "ns")) < 0) {
1087 panic("couldn't create cpu_time entry for bank ledger template");
1088 }
1089
1090 bank_ledgers.cpu_time = idx;
1091 bank_ledger_template = t;
1092}
1093
490019cf
A
1094/* Routine: bank_billed_time_safe
1095 * Purpose: Walk through all the bank accounts billed to me by other tasks and get the current billing balance.
1096 * Called from another task. It takes global bank task lock to make sure the bank context is
1097 not deallocated while accesing it.
1098 * Returns: balance.
1099 */
1100uint64_t
1101bank_billed_time_safe(task_t task)
1102{
1103 bank_task_t bank_task = BANK_TASK_NULL;
1104 ledger_amount_t credit, debit;
1105 uint64_t balance = 0;
1106 kern_return_t kr;
1107
1108 /* Task might be in exec, grab the global bank task lock before accessing bank context. */
1109 global_bank_task_lock();
1110 /* Grab a reference on bank context */
1111 if (task->bank_context != NULL) {
1112 bank_task = task->bank_context;
1113 bank_task_reference(bank_task);
1114 }
1115 global_bank_task_unlock();
1116
1117 if (bank_task) {
1118 balance = bank_billed_time(bank_task);
1119 bank_task_dealloc(bank_task, 1);
1120 } else {
1121 kr = ledger_get_entries(task->ledger, task_ledgers.cpu_time_billed_to_me,
1122 &credit, &debit);
1123 if (kr == KERN_SUCCESS) {
1124 balance = credit - debit;
1125 }
1126 }
1127
1128 return balance;
1129}
1130
fe8ab488
A
1131/*
1132 * Routine: bank_billed_time
490019cf 1133 * Purpose: Walk through the Accounts need to pay account list and get the current billing balance.
fe8ab488
A
1134 * Returns: balance.
1135 */
1136uint64_t
1137bank_billed_time(bank_task_t bank_task)
1138{
1139 int64_t balance = 0;
1140#ifdef CONFIG_BANK
1141 bank_account_t bank_account;
1142 int64_t temp = 0;
3e170ce0 1143 kern_return_t kr;
fe8ab488
A
1144#endif
1145 if (bank_task == BANK_TASK_NULL) {
1146 return balance;
1147 }
1148
1149#ifdef CONFIG_BANK
1150 lck_mtx_lock(&bank_task->bt_acc_to_pay_lock);
1151
3e170ce0
A
1152 kr = ledger_get_balance(bank_task->bt_creditcard, task_ledgers.cpu_time_billed_to_me, &temp);
1153 if (kr == KERN_SUCCESS && temp >= 0) {
1154 balance += temp;
1155 }
1156#if DEVELOPMENT || DEBUG
1157 else {
1158 printf("bank_bill_time: ledger_get_balance failed or negative balance in ledger: %lld\n", temp);
1159 }
1160#endif /* DEVELOPMENT || DEBUG */
fe8ab488
A
1161
1162 queue_iterate(&bank_task->bt_accounts_to_pay, bank_account, bank_account_t, ba_next_acc_to_pay) {
1163 temp = 0;
3e170ce0
A
1164 kr = ledger_get_balance(bank_account->ba_bill, bank_ledgers.cpu_time, &temp);
1165 if (kr == KERN_SUCCESS && temp >= 0) {
1166 balance += temp;
1167 }
1168#if DEVELOPMENT || DEBUG
1169 else {
1170 printf("bank_bill_time: ledger_get_balance failed or negative balance in ledger: %lld\n", temp);
1171 }
1172#endif /* DEVELOPMENT || DEBUG */
fe8ab488
A
1173 }
1174 lck_mtx_unlock(&bank_task->bt_acc_to_pay_lock);
1175#endif
1176 return (uint64_t)balance;
1177}
1178
490019cf
A
1179/* Routine: bank_serviced_time_safe
1180 * Purpose: Walk through the bank accounts billed to other tasks by me and get the current balance to be charged.
1181 * Called from another task. It takes global bank task lock to make sure the bank context is
1182 not deallocated while accesing it.
1183 * Returns: balance.
1184 */
1185uint64_t
1186bank_serviced_time_safe(task_t task)
1187{
1188 bank_task_t bank_task = BANK_TASK_NULL;
1189 ledger_amount_t credit, debit;
1190 uint64_t balance = 0;
1191 kern_return_t kr;
1192
1193 /* Task might be in exec, grab the global bank task lock before accessing bank context. */
1194 global_bank_task_lock();
1195 /* Grab a reference on bank context */
1196 if (task->bank_context != NULL) {
1197 bank_task = task->bank_context;
1198 bank_task_reference(bank_task);
1199 }
1200 global_bank_task_unlock();
1201
1202 if (bank_task) {
1203 balance = bank_serviced_time(bank_task);
1204 bank_task_dealloc(bank_task, 1);
1205 } else {
1206 kr = ledger_get_entries(task->ledger, task_ledgers.cpu_time_billed_to_others,
1207 &credit, &debit);
1208 if (kr == KERN_SUCCESS) {
1209 balance = credit - debit;
1210 }
1211 }
1212
1213 return balance;
1214}
1215
fe8ab488
A
1216/*
1217 * Routine: bank_serviced_time
490019cf 1218 * Purpose: Walk through the Account need to charge account list and get the current balance to be charged.
fe8ab488
A
1219 * Returns: balance.
1220 */
1221uint64_t
1222bank_serviced_time(bank_task_t bank_task)
1223{
1224 int64_t balance = 0;
1225#ifdef CONFIG_BANK
1226 bank_account_t bank_account;
1227 int64_t temp = 0;
3e170ce0 1228 kern_return_t kr;
fe8ab488
A
1229#endif
1230 if (bank_task == BANK_TASK_NULL) {
1231 return balance;
1232 }
1233
1234#ifdef CONFIG_BANK
1235 lck_mtx_lock(&bank_task->bt_acc_to_charge_lock);
1236
3e170ce0
A
1237 kr = ledger_get_balance(bank_task->bt_creditcard, task_ledgers.cpu_time_billed_to_others, &temp);
1238 if (kr == KERN_SUCCESS && temp >= 0) {
1239 balance += temp;
1240 }
1241#if DEVELOPMENT || DEBUG
1242 else {
1243 printf("bank_serviced_time: ledger_get_balance failed or negative balance in ledger: %lld\n", temp);
1244 }
1245#endif /* DEVELOPMENT || DEBUG */
fe8ab488
A
1246
1247 queue_iterate(&bank_task->bt_accounts_to_charge, bank_account, bank_account_t, ba_next_acc_to_charge) {
1248 temp = 0;
3e170ce0
A
1249 kr = ledger_get_balance(bank_account->ba_bill, bank_ledgers.cpu_time, &temp);
1250 if (kr == KERN_SUCCESS && temp >= 0) {
1251 balance += temp;
1252 }
1253#if DEVELOPMENT || DEBUG
1254 else {
1255 printf("bank_serviced_time: ledger_get_balance failed or negative balance in ledger: %lld\n", temp);
1256 }
1257#endif /* DEVELOPMENT || DEBUG */
fe8ab488
A
1258 }
1259 lck_mtx_unlock(&bank_task->bt_acc_to_charge_lock);
1260#endif
1261 return (uint64_t)balance;
1262}
1263
1264/*
1265 * Routine: bank_get_voucher_ledger
1266 * Purpose: Get the bankledger (chit) from the voucher.
1267 * Returns: bank_ledger if bank_account attribute present in voucher.
1268 * NULL on no attribute ot bank_task attribute.
1269 */
1270ledger_t
1271bank_get_voucher_ledger(ipc_voucher_t voucher)
1272{
1273 bank_element_t bank_element = BANK_ELEMENT_NULL;
1274 bank_account_t bank_account = BANK_ACCOUNT_NULL;
1275 mach_voucher_attr_value_handle_t vals[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED];
1276 mach_voucher_attr_value_handle_array_size_t val_count;
1277 ledger_t bankledger = NULL;
490019cf 1278 bank_task_t bank_merchant;
fe8ab488
A
1279 kern_return_t kr;
1280
1281 val_count = MACH_VOUCHER_ATTR_VALUE_MAX_NESTED;
1282 kr = mach_voucher_attr_control_get_values(bank_voucher_attr_control,
1283 voucher,
1284 vals,
1285 &val_count);
1286
1287 if (kr != KERN_SUCCESS)
1288 return NULL;
1289
1290 if (val_count == 0)
1291 return NULL;
1292
1293 bank_element = HANDLE_TO_BANK_ELEMENT(vals[0]);
1294 if (bank_element == BANK_DEFAULT_VALUE)
1295 return NULL;
1296
490019cf
A
1297 if (bank_element == BANK_DEFAULT_TASK_VALUE) {
1298 bank_element = CAST_TO_BANK_ELEMENT(get_bank_task_context(current_task(), FALSE));
1299 }
1300
fe8ab488
A
1301 if (bank_element->be_type == BANK_TASK) {
1302 bankledger = NULL;
1303 } else if (bank_element->be_type == BANK_ACCOUNT) {
1304 bank_account = CAST_TO_BANK_ACCOUNT(bank_element);
490019cf
A
1305 if (bank_account->ba_holder != bank_account->ba_merchant) {
1306 /* Return the ledger, if the voucher is redeemed by currrent process. */
1307 bank_merchant = get_bank_task_context(current_task(), FALSE);
1308 if (bank_account->ba_merchant == bank_merchant) {
1309 bankledger = bank_account->ba_bill;
1310 }
1311 }
fe8ab488
A
1312 } else {
1313 panic("Bogus bank type: %d passed in bank_get_voucher_ledger\n", bank_element->be_type);
1314 }
1315
1316 return (bankledger);
1317}
1318
1319/*
1320 * Routine: bank_swap_thread_bank_ledger
1321 * Purpose: swap the bank ledger on the thread.
1322 * Retunrs: None.
1323 * Note: Should be only called for current thread or thread which is not started.
1324 */
1325void
1326bank_swap_thread_bank_ledger(thread_t thread __unused, ledger_t new_ledger __unused)
1327{
1328#ifdef CONFIG_BANK
1329 spl_t s;
1330 processor_t processor;
1331 ledger_t old_ledger = thread->t_bankledger;
1332 int64_t ctime, effective_ledger_time_consumed = 0;
1333 int64_t remainder = 0, consumed = 0;
1334
1335 if (old_ledger == NULL && new_ledger == NULL)
1336 return;
1337
1338 assert((thread == current_thread() || thread->started == 0));
1339
1340 s = splsched();
1341 thread_lock(thread);
1342
1343 /*
1344 * Calculation of time elapsed by the thread before voucher swap.
1345 * Following is the timeline which shows all the variables used in the calculation below.
1346 *
1347 * thread ledger
1348 * cpu_time
1349 * |<- consumed ->|<- remainder ->|
1350 * timeline ----------------------------------------------------------------->
1351 * | | |
1352 * thread_dispatch ctime quantum end
1353 *
1354 * |<-effective_ledger_time -> |
1355 * deduct_bank_ledger_time
1356 */
1357
1358 ctime = mach_absolute_time();
1359 processor = thread->last_processor;
1360 if (processor != NULL) {
1361 if ((int64_t)processor->quantum_end > ctime)
1362 remainder = (int64_t)processor->quantum_end - ctime;
1363
1364 consumed = thread->quantum_remaining - remainder;
1365 effective_ledger_time_consumed = consumed - thread->t_deduct_bank_ledger_time;
1366 }
1367
1368 thread->t_deduct_bank_ledger_time = consumed;
1369
1370 thread->t_bankledger = new_ledger;
1371
1372 thread_unlock(thread);
1373 splx(s);
1374
1375 if (old_ledger != NULL)
1376 ledger_credit(old_ledger,
1377 bank_ledgers.cpu_time,
1378 effective_ledger_time_consumed);
1379#endif
1380}
1381