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