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