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