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