1/* 2 * Copyright (c) 2012-2013 Apple Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28 29#include <bank/bank_internal.h> 30#include <bank/bank_types.h> 31#include <mach/mach_types.h> 32#include <mach/kern_return.h> 33#include <ipc/ipc_port.h> 34#include <mach/mach_vm.h> 35#include <mach/vm_map.h> 36#include <vm/vm_map.h> 37#include <mach/host_priv.h> 38#include <mach/host_special_ports.h> 39#include <kern/host.h> 40#include <kern/kalloc.h> 41#include <kern/ledger.h> 42#include <sys/kdebug.h> 43 44#include <mach/mach_voucher_attr_control.h> 45 46static zone_t bank_task_zone, bank_account_zone; 47#define MAX_BANK_TASK (CONFIG_TASK_MAX) 48#define MAX_BANK_ACCOUNT (CONFIG_TASK_MAX + CONFIG_THREAD_MAX) 49 50#define BANK_ELEMENT_TO_HANDLE(x) (CAST_DOWN(bank_handle_t, (x))) 51#define HANDLE_TO_BANK_ELEMENT(x) (CAST_DOWN(bank_element_t, (x))) 52 53/* Need macro since bank_element_t is 4 byte aligned on release kernel and direct type case gives compilation error */ 54#define CAST_TO_BANK_TASK(x) ((bank_task_t)((void *)(x))) 55#define CAST_TO_BANK_ACCOUNT(x) ((bank_account_t)((void *)(x))) 56 57ipc_voucher_attr_control_t bank_voucher_attr_control; /* communication channel from ATM to voucher system */ 58 59#if DEVELOPMENT || DEBUG 60queue_head_t bank_tasks_list; 61queue_head_t bank_accounts_list; 62#endif 63 64static ledger_template_t bank_ledger_template = NULL; 65struct _bank_ledger_indices bank_ledgers = { -1 }; 66 67static bank_task_t bank_task_alloc_init(void); 68static bank_account_t bank_account_alloc_init(bank_task_t bank_holder, bank_task_t bank_merchant); 69static bank_task_t get_bank_task_context(task_t task); 70static void bank_task_dealloc(bank_task_t bank_task, mach_voucher_attr_value_reference_t sync); 71static kern_return_t bank_account_dealloc_with_sync(bank_account_t bank_account, mach_voucher_attr_value_reference_t sync); 72static void bank_rollup_chit_to_tasks(ledger_t bill, bank_task_t bank_holder, bank_task_t bank_merchant); 73static void init_bank_ledgers(void); 74 75kern_return_t 76bank_release_value( 77 ipc_voucher_attr_manager_t __assert_only manager, 78 mach_voucher_attr_key_t __assert_only key, 79 mach_voucher_attr_value_handle_t value, 80 mach_voucher_attr_value_reference_t sync); 81 82kern_return_t 83bank_get_value( 84 ipc_voucher_attr_manager_t __assert_only manager, 85 mach_voucher_attr_key_t __assert_only key, 86 mach_voucher_attr_recipe_command_t command, 87 mach_voucher_attr_value_handle_array_t prev_values, 88 mach_msg_type_number_t __assert_only prev_value_count, 89 mach_voucher_attr_content_t recipe, 90 mach_voucher_attr_content_size_t recipe_size, 91 mach_voucher_attr_value_handle_t *out_value, 92 ipc_voucher_t *out_value_voucher); 93 94kern_return_t 95bank_extract_content( 96 ipc_voucher_attr_manager_t __assert_only manager, 97 mach_voucher_attr_key_t __assert_only key, 98 mach_voucher_attr_value_handle_array_t values, 99 mach_msg_type_number_t value_count, 100 mach_voucher_attr_recipe_command_t *out_command, 101 mach_voucher_attr_content_t out_recipe, 102 mach_voucher_attr_content_size_t *in_out_recipe_size); 103 104kern_return_t 105bank_command( 106 ipc_voucher_attr_manager_t __assert_only manager, 107 mach_voucher_attr_key_t __assert_only key, 108 mach_voucher_attr_value_handle_array_t values, 109 mach_msg_type_number_t value_count, 110 mach_voucher_attr_command_t command, 111 mach_voucher_attr_content_t in_content, 112 mach_voucher_attr_content_size_t in_content_size, 113 mach_voucher_attr_content_t out_content, 114 mach_voucher_attr_content_size_t *in_out_content_size); 115 116void 117bank_release(ipc_voucher_attr_manager_t __assert_only manager); 118 119/* 120 * communication channel from voucher system to ATM 121 */ 122struct ipc_voucher_attr_manager bank_manager = { 123 .ivam_release_value = bank_release_value, 124 .ivam_get_value = bank_get_value, 125 .ivam_extract_content = bank_extract_content, 126 .ivam_command = bank_command, 127 .ivam_release = bank_release, 128}; 129 130 131#if DEVELOPMENT || DEBUG 132decl_lck_mtx_data(, bank_tasks_list_lock); 133decl_lck_mtx_data(, bank_accounts_list_lock); 134 135lck_grp_t bank_dev_lock_grp; 136lck_attr_t bank_dev_lock_attr; 137lck_grp_attr_t bank_dev_lock_grp_attr; 138#endif 139 140/* 141 * Lock group attributes for bank sub system. 142 */ 143lck_grp_t bank_lock_grp; 144lck_attr_t bank_lock_attr; 145lck_grp_attr_t bank_lock_grp_attr; 146 147/* 148 * Routine: bank_init 149 * Purpose: Initialize the BANK subsystem. 150 * Returns: None. 151 */ 152void 153bank_init() 154{ 155 kern_return_t kr = KERN_SUCCESS; 156 /* setup zones for bank_task and bank_account objects */ 157 bank_task_zone = zinit(sizeof(struct bank_task), 158 MAX_BANK_TASK * sizeof(struct bank_task), 159 sizeof(struct bank_task), 160 "bank_task"); 161 162 bank_account_zone = zinit(sizeof(struct bank_account), 163 MAX_BANK_ACCOUNT * sizeof(struct bank_account), 164 sizeof(struct bank_account), 165 "bank_account"); 166 167 init_bank_ledgers(); 168 169 /* Initialize bank lock group and lock attributes. */ 170 lck_grp_attr_setdefault(&bank_lock_grp_attr); 171 lck_grp_init(&bank_lock_grp, "bank_lock", &bank_lock_grp_attr); 172 lck_attr_setdefault(&bank_lock_attr); 173 174#if DEVELOPMENT || DEBUG 175 /* Initialize global bank development lock group and lock attributes. */ 176 lck_grp_attr_setdefault(&bank_dev_lock_grp_attr); 177 lck_grp_init(&bank_dev_lock_grp, "bank_dev_lock", &bank_dev_lock_grp_attr); 178 lck_attr_setdefault(&bank_dev_lock_attr); 179 180 lck_mtx_init(&bank_tasks_list_lock, &bank_dev_lock_grp, &bank_dev_lock_attr); 181 lck_mtx_init(&bank_accounts_list_lock, &bank_dev_lock_grp, &bank_dev_lock_attr); 182 183 queue_init(&bank_tasks_list); 184 queue_init(&bank_accounts_list); 185#endif 186 187 /* Register the bank manager with the Vouchers sub system. */ 188 kr = ipc_register_well_known_mach_voucher_attr_manager( 189 &bank_manager, 190 0, 191 MACH_VOUCHER_ATTR_KEY_BANK, 192 &bank_voucher_attr_control); 193 if (kr != KERN_SUCCESS ) 194 panic("BANK subsystem initialization failed"); 195 196 kprintf("BANK subsystem is initialized\n"); 197 return ; 198} 199 200 201/* 202 * BANK Resource Manager Routines. 203 */ 204 205 206/* 207 * Routine: bank_release_value 208 * Purpose: Release a value, if sync matches the sync count in value. 209 * Returns: KERN_SUCCESS: on Successful deletion. 210 * KERN_FAILURE: if sync value does not matches. 211 */ 212kern_return_t 213bank_release_value( 214 ipc_voucher_attr_manager_t __assert_only manager, 215 mach_voucher_attr_key_t __assert_only key, 216 mach_voucher_attr_value_handle_t value, 217 mach_voucher_attr_value_reference_t sync) 218{ 219 bank_task_t bank_task = BANK_TASK_NULL; 220 bank_element_t bank_element = BANK_ELEMENT_NULL; 221 bank_account_t bank_account = BANK_ACCOUNT_NULL; 222 kern_return_t kr = KERN_SUCCESS; 223 224 assert(MACH_VOUCHER_ATTR_KEY_BANK == key); 225 assert(manager == &bank_manager); 226 227 228 bank_element = HANDLE_TO_BANK_ELEMENT(value); 229 if (bank_element == BANK_DEFAULT_VALUE) { 230 /* Return success for default value */ 231 return KERN_SUCCESS; 232 } 233 234 235 if (bank_element->be_type == BANK_TASK) { 236 bank_task = CAST_TO_BANK_TASK(bank_element); 237 238 if (bank_task->bt_made != (int)sync) { 239 return KERN_FAILURE; 240 } 241 242 bank_task_made_release_num(bank_task, sync); 243 bank_task_dealloc(bank_task, sync); 244 } else if (bank_element->be_type == BANK_ACCOUNT) { 245 bank_account = CAST_TO_BANK_ACCOUNT(bank_element); 246 kr = bank_account_dealloc_with_sync(bank_account, sync); 247 } else { 248 panic("Bogus bank type: %d passed in get_value\n", bank_element->be_type); 249 } 250 251 return kr; 252} 253 254 255/* 256 * Routine: bank_get_value 257 */ 258kern_return_t 259bank_get_value( 260 ipc_voucher_attr_manager_t __assert_only manager, 261 mach_voucher_attr_key_t __assert_only key, 262 mach_voucher_attr_recipe_command_t command, 263 mach_voucher_attr_value_handle_array_t prev_values, 264 mach_msg_type_number_t prev_value_count, 265 mach_voucher_attr_content_t __unused recipe, 266 mach_voucher_attr_content_size_t __unused recipe_size, 267 mach_voucher_attr_value_handle_t *out_value, 268 ipc_voucher_t *out_value_voucher) 269{ 270 bank_task_t bank_task = BANK_TASK_NULL; 271 bank_task_t bank_holder = BANK_TASK_NULL; 272 bank_task_t bank_merchant = BANK_TASK_NULL; 273 bank_element_t bank_element = BANK_ELEMENT_NULL; 274 bank_account_t bank_account = BANK_ACCOUNT_NULL; 275 bank_account_t old_bank_account = BANK_ACCOUNT_NULL; 276 mach_voucher_attr_value_handle_t bank_handle; 277 task_t task; 278 kern_return_t kr = KERN_SUCCESS; 279 mach_msg_type_number_t i; 280 281 assert(MACH_VOUCHER_ATTR_KEY_BANK == key); 282 assert(manager == &bank_manager); 283 284 /* never an out voucher */ 285 *out_value_voucher = IPC_VOUCHER_NULL; 286 287 switch (command) { 288 289 case MACH_VOUCHER_ATTR_BANK_CREATE: 290 291 /* Get the bank context from the current task and take a reference on it. */ 292 task = current_task(); 293 bank_task = get_bank_task_context(task); 294 if (bank_task == BANK_TASK_NULL) 295 return KERN_RESOURCE_SHORTAGE; 296 297 bank_task_reference(bank_task); 298 bank_task_made_reference(bank_task); 299 300 *out_value = BANK_ELEMENT_TO_HANDLE(bank_task); 301 break; 302 303 case MACH_VOUCHER_ATTR_REDEEM: 304 305 for (i = 0; i < prev_value_count; i++) { 306 bank_handle = prev_values[i]; 307 bank_element = HANDLE_TO_BANK_ELEMENT(bank_handle); 308 309 if (bank_element == BANK_DEFAULT_VALUE) 310 continue; 311 312 task = current_task(); 313 if (bank_element->be_type == BANK_TASK) { 314 bank_holder = CAST_TO_BANK_TASK(bank_element); 315 } else if (bank_element->be_type == BANK_ACCOUNT) { 316 old_bank_account = CAST_TO_BANK_ACCOUNT(bank_element); 317 bank_holder = old_bank_account->ba_holder; 318 } else { 319 panic("Bogus bank type: %d passed in get_value\n", bank_element->be_type); 320 } 321 322 bank_merchant = get_bank_task_context(task); 323 if (bank_merchant == BANK_TASK_NULL) 324 return KERN_RESOURCE_SHORTAGE; 325 326 /* Check if trying to redeem for self task, return the bank task */ 327 if (bank_holder == bank_merchant) { 328 bank_task_reference(bank_holder); 329 bank_task_made_reference(bank_holder); 330 *out_value = BANK_ELEMENT_TO_HANDLE(bank_holder); 331 return kr; 332 } 333 334 bank_account = bank_account_alloc_init(bank_holder, bank_merchant); 335 if (bank_account == BANK_ACCOUNT_NULL) 336 return KERN_RESOURCE_SHORTAGE; 337 338 *out_value = BANK_ELEMENT_TO_HANDLE(bank_account); 339 return kr; 340 } 341 342 *out_value = BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_VALUE); 343 break; 344 default: 345 kr = KERN_INVALID_ARGUMENT; 346 break; 347 } 348 349 return kr; 350} 351 352 353/* 354 * Routine: bank_extract_content 355 * Purpose: Extract a set of aid from an array of voucher values. 356 * Returns: KERN_SUCCESS: on Success. 357 * KERN_FAILURE: one of the value is not present in the hash. 358 * KERN_NO_SPACE: insufficeint buffer provided to fill an array of aid. 359 */ 360kern_return_t 361bank_extract_content( 362 ipc_voucher_attr_manager_t __assert_only manager, 363 mach_voucher_attr_key_t __assert_only key, 364 mach_voucher_attr_value_handle_array_t values, 365 mach_msg_type_number_t value_count, 366 mach_voucher_attr_recipe_command_t *out_command, 367 mach_voucher_attr_content_t out_recipe, 368 mach_voucher_attr_content_size_t *in_out_recipe_size) 369{ 370 bank_task_t bank_task = BANK_TASK_NULL; 371 bank_element_t bank_element = BANK_ELEMENT_NULL; 372 bank_account_t bank_account = BANK_ACCOUNT_NULL; 373 mach_voucher_attr_value_handle_t bank_handle; 374 char buf[MACH_VOUCHER_BANK_CONTENT_SIZE]; 375 mach_msg_type_number_t i; 376 377 assert(MACH_VOUCHER_ATTR_KEY_BANK == key); 378 assert(manager == &bank_manager); 379 380 for (i = 0; i < value_count; i++) { 381 bank_handle = values[i]; 382 bank_element = HANDLE_TO_BANK_ELEMENT(bank_handle); 383 if (bank_element == BANK_DEFAULT_VALUE) 384 continue; 385 386 if (MACH_VOUCHER_BANK_CONTENT_SIZE > *in_out_recipe_size) { 387 *in_out_recipe_size = 0; 388 return KERN_NO_SPACE; 389 } 390 391 if (bank_element->be_type == BANK_TASK) { 392 bank_task = CAST_TO_BANK_TASK(bank_element); 393 snprintf(buf, MACH_VOUCHER_BANK_CONTENT_SIZE, 394 " Bank Context for a pid %d\n", bank_task->bt_pid); 395 } else if (bank_element->be_type == BANK_ACCOUNT) { 396 bank_account = CAST_TO_BANK_ACCOUNT(bank_element); 397 snprintf(buf, MACH_VOUCHER_BANK_CONTENT_SIZE, 398 " Bank Account linking holder pid %d with merchant pid %d\n", 399 bank_account->ba_holder->bt_pid, 400 bank_account->ba_merchant->bt_pid); 401 } else { 402 panic("Bogus bank type: %d passed in get_value\n", bank_element->be_type); 403 } 404 405 406 memcpy(&out_recipe[0], buf, strlen(buf) + 1); 407 *out_command = MACH_VOUCHER_ATTR_BANK_NULL; 408 *in_out_recipe_size = (mach_voucher_attr_content_size_t)strlen(buf) + 1; 409 return KERN_SUCCESS; 410 } 411 412 return KERN_SUCCESS; 413} 414 415/* 416 * Routine: bank_command 417 * Purpose: Execute a command against a set of ATM values. 418 * Returns: KERN_SUCCESS: On successful execution of command. 419 KERN_FAILURE: On failure. 420 */ 421kern_return_t 422bank_command( 423 ipc_voucher_attr_manager_t __assert_only manager, 424 mach_voucher_attr_key_t __assert_only key, 425 mach_voucher_attr_value_handle_array_t __unused values, 426 mach_msg_type_number_t __unused value_count, 427 mach_voucher_attr_command_t __unused command, 428 mach_voucher_attr_content_t __unused in_content, 429 mach_voucher_attr_content_size_t __unused in_content_size, 430 mach_voucher_attr_content_t __unused out_content, 431 mach_voucher_attr_content_size_t __unused *out_content_size) 432{ 433 bank_task_t bank_task = BANK_TASK_NULL; 434 bank_element_t bank_element = BANK_ELEMENT_NULL; 435 bank_account_t bank_account = BANK_ACCOUNT_NULL; 436 mach_voucher_attr_value_handle_t bank_handle; 437 mach_msg_type_number_t i; 438 int32_t pid; 439 440 assert(MACH_VOUCHER_ATTR_KEY_BANK == key); 441 assert(manager == &bank_manager); 442 443 switch (command) { 444 case BANK_ORIGINATOR_PID: 445 446 if ((sizeof(pid)) > *out_content_size) { 447 *out_content_size = 0; 448 return KERN_NO_SPACE; 449 } 450 451 for (i = 0; i < value_count; i++) { 452 bank_handle = values[i]; 453 bank_element = HANDLE_TO_BANK_ELEMENT(bank_handle); 454 if (bank_element == BANK_DEFAULT_VALUE) 455 continue; 456 457 if (bank_element->be_type == BANK_TASK) { 458 bank_task = CAST_TO_BANK_TASK(bank_element); 459 } else if (bank_element->be_type == BANK_ACCOUNT) { 460 bank_account = CAST_TO_BANK_ACCOUNT(bank_element); 461 bank_task = bank_account->ba_holder; 462 } else { 463 panic("Bogus bank type: %d passed in voucher_command\n", bank_element->be_type); 464 } 465 pid = bank_task->bt_pid; 466 467 memcpy(&out_content[0], &pid, sizeof(pid)); 468 *out_content_size = (mach_voucher_attr_content_size_t)sizeof(pid); 469 return KERN_SUCCESS; 470 } 471 /* In the case of no value, return error KERN_INVALID_VALUE */ 472 *out_content_size = 0; 473 return KERN_INVALID_VALUE; 474 475 break; 476 default: 477 return KERN_INVALID_ARGUMENT; 478 } 479 return KERN_SUCCESS; 480} 481 482 483void 484bank_release( 485 ipc_voucher_attr_manager_t __assert_only manager) 486{ 487 assert(manager == &bank_manager); 488} 489 490 491 492/* 493 * Bank Internal Routines. 494 */ 495 496/* 497 * Routine: bank_task_alloc_init 498 * Purpose: Allocate and initialize a bank task structure. 499 * Returns: bank_task_t on Success. 500 * BANK_TASK_NULL: on Failure. 501 * Notes: Leaves the task and creditcard blank and has only 1 ref, 502 needs to take 1 extra ref after the task field is initialized. 503 */ 504static bank_task_t 505bank_task_alloc_init(void) 506{ 507 bank_task_t new_bank_task; 508 509 new_bank_task = (bank_task_t) zalloc(bank_task_zone); 510 if (new_bank_task == BANK_TASK_NULL) 511 return BANK_TASK_NULL; 512 513 new_bank_task->bt_type = BANK_TASK; 514 new_bank_task->bt_refs = 1; 515 new_bank_task->bt_made = 0; 516 new_bank_task->bt_pid = 0; 517 new_bank_task->bt_creditcard = NULL; 518 queue_init(&new_bank_task->bt_accounts_to_pay); 519 queue_init(&new_bank_task->bt_accounts_to_charge); 520 lck_mtx_init(&new_bank_task->bt_acc_to_pay_lock, &bank_lock_grp, &bank_lock_attr); 521 lck_mtx_init(&new_bank_task->bt_acc_to_charge_lock, &bank_lock_grp, &bank_lock_attr); 522 523#if DEVELOPMENT || DEBUG 524 new_bank_task->bt_task = NULL; 525 lck_mtx_lock(&bank_tasks_list_lock); 526 queue_enter(&bank_tasks_list, new_bank_task, bank_task_t, bt_global_elt); 527 lck_mtx_unlock(&bank_tasks_list_lock); 528#endif 529 return (new_bank_task); 530} 531 532/* 533 * Routine: bank_account_alloc_init 534 * Purpose: Allocate and Initialize the bank account struct. 535 * Returns: bank_account_t : On Success. 536 * BANK_ACCOUNT_NULL: On Failure. 537 */ 538static bank_account_t 539bank_account_alloc_init( 540 bank_task_t bank_holder, 541 bank_task_t bank_merchant) 542{ 543 bank_account_t new_bank_account; 544 bank_account_t bank_account; 545 boolean_t entry_found = FALSE; 546 ledger_t new_ledger = ledger_instantiate(bank_ledger_template, LEDGER_CREATE_INACTIVE_ENTRIES); 547 548 if (new_ledger == NULL) 549 return BANK_ACCOUNT_NULL; 550 551 ledger_entry_setactive(new_ledger, bank_ledgers.cpu_time); 552 new_bank_account = (bank_account_t) zalloc(bank_account_zone); 553 if (new_bank_account == BANK_ACCOUNT_NULL) { 554 ledger_dereference(new_ledger); 555 return BANK_ACCOUNT_NULL; 556 } 557 558 new_bank_account->ba_type = BANK_ACCOUNT; 559 new_bank_account->ba_refs = 1; 560 new_bank_account->ba_made = 1; 561 new_bank_account->ba_pid = 0; 562 new_bank_account->ba_bill = new_ledger; 563 new_bank_account->ba_merchant = bank_merchant; 564 new_bank_account->ba_holder = bank_holder; 565 566 /* Iterate through accounts need to pay list to find the existing entry */ 567 lck_mtx_lock(&bank_holder->bt_acc_to_pay_lock); 568 queue_iterate(&bank_holder->bt_accounts_to_pay, bank_account, bank_account_t, ba_next_acc_to_pay) { 569 if (bank_account->ba_merchant != bank_merchant) 570 continue; 571 572 entry_found = TRUE; 573 /* Take a made ref, since this value would be returned to voucher system. */ 574 bank_account_reference(bank_account); 575 bank_account_made_reference(bank_account); 576 break; 577 } 578 579 if (!entry_found) { 580 581 /* Create a linkage between the holder and the merchant task, Grab both the list locks before adding it to the list. */ 582 lck_mtx_lock(&bank_merchant->bt_acc_to_charge_lock); 583 584 /* Add the account entry into Accounts need to pay account link list. */ 585 queue_enter(&bank_holder->bt_accounts_to_pay, new_bank_account, bank_account_t, ba_next_acc_to_pay); 586 587 /* Add the account entry into Accounts need to charge account link list. */ 588 queue_enter(&bank_merchant->bt_accounts_to_charge, new_bank_account, bank_account_t, ba_next_acc_to_charge); 589 590 lck_mtx_unlock(&bank_merchant->bt_acc_to_charge_lock); 591 } 592 593 lck_mtx_unlock(&bank_holder->bt_acc_to_pay_lock); 594 595 if (entry_found) { 596 ledger_dereference(new_ledger); 597 zfree(bank_account_zone, new_bank_account); 598 return bank_account; 599 } 600 601 bank_task_reference(bank_holder); 602 bank_task_reference(bank_merchant); 603 604#if DEVELOPMENT || DEBUG 605 new_bank_account->ba_task = NULL; 606 lck_mtx_lock(&bank_accounts_list_lock); 607 queue_enter(&bank_accounts_list, new_bank_account, bank_account_t, ba_global_elt); 608 lck_mtx_unlock(&bank_accounts_list_lock); 609#endif 610 611 return (new_bank_account); 612} 613 614/* 615 * Routine: get_bank_task_context 616 * Purpose: Get the bank context of the given task 617 * Returns: bank_task_t on Success. 618 * BANK_TASK_NULL: on Failure. 619 * Note: Initialize bank context if NULL. 620 */ 621static bank_task_t 622get_bank_task_context(task_t task) 623{ 624 bank_task_t bank_task; 625 626 if (task->bank_context) 627 return (task->bank_context); 628 629 bank_task = bank_task_alloc_init(); 630 631 /* Grab the task lock and check if we won the race. */ 632 task_lock(task); 633 if (task->bank_context) { 634 task_unlock(task); 635 if (bank_task != BANK_TASK_NULL) 636 bank_task_dealloc(bank_task, 1); 637 return (task->bank_context); 638 } else if (bank_task == BANK_TASK_NULL) { 639 task_unlock(task); 640 return BANK_TASK_NULL; 641 } 642 /* We won the race. Take a ref on the ledger and initialize bank task. */ 643 bank_task->bt_creditcard = task->ledger; 644 bank_task->bt_pid = audit_token_pid_from_task(task); 645#if DEVELOPMENT || DEBUG 646 bank_task->bt_task = task; 647#endif 648 ledger_reference(task->ledger); 649 650 task->bank_context = bank_task; 651 task_unlock(task); 652 653 return (bank_task); 654} 655 656/* 657 * Routine: bank_task_dealloc 658 * Purpose: Drops the reference on bank task. 659 * Returns: None. 660 */ 661static void 662bank_task_dealloc( 663 bank_task_t bank_task, 664 mach_voucher_attr_value_reference_t sync) 665{ 666 assert(bank_task->bt_refs >= 0); 667 668 if (bank_task_release_num(bank_task, sync) > (int)sync) 669 return; 670 671 assert(bank_task->bt_refs == 0); 672 assert(queue_empty(&bank_task->bt_accounts_to_pay)); 673 assert(queue_empty(&bank_task->bt_accounts_to_charge)); 674 675 ledger_dereference(bank_task->bt_creditcard); 676 lck_mtx_destroy(&bank_task->bt_acc_to_pay_lock, &bank_lock_grp); 677 lck_mtx_destroy(&bank_task->bt_acc_to_charge_lock, &bank_lock_grp); 678 679#if DEVELOPMENT || DEBUG 680 lck_mtx_lock(&bank_tasks_list_lock); 681 queue_remove(&bank_tasks_list, bank_task, bank_task_t, bt_global_elt); 682 lck_mtx_unlock(&bank_tasks_list_lock); 683#endif 684 685 zfree(bank_task_zone, bank_task); 686} 687 688/* 689 * Routine: bank_account_dealloc_with_sync 690 * Purpose: Drop the reference on bank account if the sync matches. 691 * Returns: KERN_SUCCESS if sync matches. 692 * KERN_FAILURE on mismatch. 693 */ 694static kern_return_t 695bank_account_dealloc_with_sync( 696 bank_account_t bank_account, 697 mach_voucher_attr_value_reference_t sync) 698{ 699 bank_task_t bank_holder = bank_account->ba_holder; 700 bank_task_t bank_merchant = bank_account->ba_merchant; 701 702 /* Grab the acc to pay list lock and check the sync value */ 703 lck_mtx_lock(&bank_holder->bt_acc_to_pay_lock); 704 705 if (bank_account->ba_made != (int)sync) { 706 lck_mtx_unlock(&bank_holder->bt_acc_to_pay_lock); 707 return KERN_FAILURE; 708 } 709 710 bank_account_made_release_num(bank_account, sync); 711 712 if (bank_account_release_num(bank_account, sync) > (int)sync) 713 panic("Sync and ref value did not match for bank account %p\n", bank_account); 714 715 716 /* Grab both the acc to pay and acc to charge locks */ 717 lck_mtx_lock(&bank_merchant->bt_acc_to_charge_lock); 718 719 bank_rollup_chit_to_tasks(bank_account->ba_bill, bank_holder, bank_merchant); 720 721 /* Remove the account entry from Accounts need to pay account link list. */ 722 queue_remove(&bank_holder->bt_accounts_to_pay, bank_account, bank_account_t, ba_next_acc_to_pay); 723 724 /* Remove the account entry from Accounts need to charge account link list. */ 725 queue_remove(&bank_merchant->bt_accounts_to_charge, bank_account, bank_account_t, ba_next_acc_to_charge); 726 727 lck_mtx_unlock(&bank_merchant->bt_acc_to_charge_lock); 728 lck_mtx_unlock(&bank_holder->bt_acc_to_pay_lock); 729 730 ledger_dereference(bank_account->ba_bill); 731 732 /* Drop the reference of bank holder and merchant */ 733 bank_task_dealloc(bank_holder, 1); 734 bank_task_dealloc(bank_merchant, 1); 735 736#if DEVELOPMENT || DEBUG 737 lck_mtx_lock(&bank_accounts_list_lock); 738 queue_remove(&bank_accounts_list, bank_account, bank_account_t, ba_global_elt); 739 lck_mtx_unlock(&bank_accounts_list_lock); 740#endif 741 742 zfree(bank_account_zone, bank_account); 743 return KERN_SUCCESS; 744} 745 746/* 747 * Routine: bank_rollup_chit_to_tasks 748 * Purpose: Debit and Credit holder's and merchant's ledgers. 749 * Returns: None. 750 */ 751static void 752bank_rollup_chit_to_tasks( 753 ledger_t bill, 754 bank_task_t bank_holder, 755 bank_task_t bank_merchant) 756{ 757 ledger_amount_t credit; 758 ledger_amount_t debit; 759 kern_return_t ret; 760 761 ret = ledger_get_entries(bill, bank_ledgers.cpu_time, &credit, &debit); 762 if (ret != KERN_SUCCESS) { 763 return; 764 } 765 766 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, (BANK_CODE(BANK_ACCOUNT_INFO, (BANK_SETTLE_CPU_TIME))) | DBG_FUNC_NONE, 767 bank_merchant->bt_pid, bank_holder->bt_pid, credit, debit, 0); 768#if CONFIG_BANK 769 ledger_credit(bank_holder->bt_creditcard, task_ledgers.cpu_time_billed_to_me, credit); 770 ledger_debit(bank_holder->bt_creditcard, task_ledgers.cpu_time_billed_to_me, debit); 771 772 ledger_credit(bank_merchant->bt_creditcard, task_ledgers.cpu_time_billed_to_others, credit); 773 ledger_debit(bank_merchant->bt_creditcard, task_ledgers.cpu_time_billed_to_others, debit); 774#endif 775} 776 777 778 779/* 780 * Routine: bank_task_destroy 781 * Purpose: Drops reference on bank task. 782 * Returns: None. 783 */ 784void 785bank_task_destroy(bank_task_t bank_task) 786{ 787 bank_task_dealloc(bank_task, 1); 788} 789 790/* 791 * Routine: init_bank_ledgers 792 * Purpose: Initialize template for bank ledgers. 793 * Returns: None. 794 */ 795static void 796init_bank_ledgers(void) { 797 ledger_template_t t; 798 int idx; 799 800 assert(bank_ledger_template == NULL); 801 802 if ((t = ledger_template_create("Bank ledger")) == NULL) 803 panic("couldn't create bank ledger template"); 804 805 if ((idx = ledger_entry_add(t, "cpu_time", "sched", "ns")) < 0) { 806 panic("couldn't create cpu_time entry for bank ledger template"); 807 } 808 809 bank_ledgers.cpu_time = idx; 810 bank_ledger_template = t; 811} 812 813/* 814 * Routine: bank_billed_time 815 * Purpose: Walk throught the Accounts need to pay account list and get the current billing balance. 816 * Returns: balance. 817 */ 818uint64_t 819bank_billed_time(bank_task_t bank_task) 820{ 821 int64_t balance = 0; 822#ifdef CONFIG_BANK 823 bank_account_t bank_account; 824 int64_t temp = 0; 825#endif 826 if (bank_task == BANK_TASK_NULL) { 827 return balance; 828 } 829 830#ifdef CONFIG_BANK 831 lck_mtx_lock(&bank_task->bt_acc_to_pay_lock); 832 833 ledger_get_balance(bank_task->bt_creditcard, task_ledgers.cpu_time_billed_to_me, &temp); 834 balance +=temp; 835 836 queue_iterate(&bank_task->bt_accounts_to_pay, bank_account, bank_account_t, ba_next_acc_to_pay) { 837 temp = 0; 838 ledger_get_balance(bank_account->ba_bill, bank_ledgers.cpu_time, &temp); 839 balance += temp; 840 } 841 lck_mtx_unlock(&bank_task->bt_acc_to_pay_lock); 842#endif 843 return (uint64_t)balance; 844} 845 846/* 847 * Routine: bank_serviced_time 848 * Purpose: Walk throught the Account need to charge account list and get the current balance to be charged. 849 * Returns: balance. 850 */ 851uint64_t 852bank_serviced_time(bank_task_t bank_task) 853{ 854 int64_t balance = 0; 855#ifdef CONFIG_BANK 856 bank_account_t bank_account; 857 int64_t temp = 0; 858#endif 859 if (bank_task == BANK_TASK_NULL) { 860 return balance; 861 } 862 863#ifdef CONFIG_BANK 864 lck_mtx_lock(&bank_task->bt_acc_to_charge_lock); 865 866 ledger_get_balance(bank_task->bt_creditcard, task_ledgers.cpu_time_billed_to_others, &temp); 867 balance +=temp; 868 869 queue_iterate(&bank_task->bt_accounts_to_charge, bank_account, bank_account_t, ba_next_acc_to_charge) { 870 temp = 0; 871 ledger_get_balance(bank_account->ba_bill, bank_ledgers.cpu_time, &temp); 872 balance += temp; 873 } 874 lck_mtx_unlock(&bank_task->bt_acc_to_charge_lock); 875#endif 876 return (uint64_t)balance; 877} 878 879/* 880 * Routine: bank_get_voucher_ledger 881 * Purpose: Get the bankledger (chit) from the voucher. 882 * Returns: bank_ledger if bank_account attribute present in voucher. 883 * NULL on no attribute ot bank_task attribute. 884 */ 885ledger_t 886bank_get_voucher_ledger(ipc_voucher_t voucher) 887{ 888 bank_element_t bank_element = BANK_ELEMENT_NULL; 889 bank_account_t bank_account = BANK_ACCOUNT_NULL; 890 mach_voucher_attr_value_handle_t vals[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED]; 891 mach_voucher_attr_value_handle_array_size_t val_count; 892 ledger_t bankledger = NULL; 893 kern_return_t kr; 894 895 val_count = MACH_VOUCHER_ATTR_VALUE_MAX_NESTED; 896 kr = mach_voucher_attr_control_get_values(bank_voucher_attr_control, 897 voucher, 898 vals, 899 &val_count); 900 901 if (kr != KERN_SUCCESS) 902 return NULL; 903 904 if (val_count == 0) 905 return NULL; 906 907 bank_element = HANDLE_TO_BANK_ELEMENT(vals[0]); 908 if (bank_element == BANK_DEFAULT_VALUE) 909 return NULL; 910 911 if (bank_element->be_type == BANK_TASK) { 912 bankledger = NULL; 913 } else if (bank_element->be_type == BANK_ACCOUNT) { 914 bank_account = CAST_TO_BANK_ACCOUNT(bank_element); 915 bankledger = bank_account->ba_bill; 916 } else { 917 panic("Bogus bank type: %d passed in bank_get_voucher_ledger\n", bank_element->be_type); 918 } 919 920 return (bankledger); 921} 922 923/* 924 * Routine: bank_swap_thread_bank_ledger 925 * Purpose: swap the bank ledger on the thread. 926 * Retunrs: None. 927 * Note: Should be only called for current thread or thread which is not started. 928 */ 929void 930bank_swap_thread_bank_ledger(thread_t thread __unused, ledger_t new_ledger __unused) 931{ 932#ifdef CONFIG_BANK 933 spl_t s; 934 processor_t processor; 935 ledger_t old_ledger = thread->t_bankledger; 936 int64_t ctime, effective_ledger_time_consumed = 0; 937 int64_t remainder = 0, consumed = 0; 938 939 if (old_ledger == NULL && new_ledger == NULL) 940 return; 941 942 assert((thread == current_thread() || thread->started == 0)); 943 944 s = splsched(); 945 thread_lock(thread); 946 947 /* 948 * Calculation of time elapsed by the thread before voucher swap. 949 * Following is the timeline which shows all the variables used in the calculation below. 950 * 951 * thread ledger 952 * cpu_time 953 * |<- consumed ->|<- remainder ->| 954 * timeline -----------------------------------------------------------------> 955 * | | | 956 * thread_dispatch ctime quantum end 957 * 958 * |<-effective_ledger_time -> | 959 * deduct_bank_ledger_time 960 */ 961 962 ctime = mach_absolute_time(); 963 processor = thread->last_processor; 964 if (processor != NULL) { 965 if ((int64_t)processor->quantum_end > ctime) 966 remainder = (int64_t)processor->quantum_end - ctime; 967 968 consumed = thread->quantum_remaining - remainder; 969 effective_ledger_time_consumed = consumed - thread->t_deduct_bank_ledger_time; 970 } 971 972 thread->t_deduct_bank_ledger_time = consumed; 973 974 thread->t_bankledger = new_ledger; 975 976 thread_unlock(thread); 977 splx(s); 978 979 if (old_ledger != NULL) 980 ledger_credit(old_ledger, 981 bank_ledgers.cpu_time, 982 effective_ledger_time_consumed); 983#endif 984} 985 986