mirror of
https://github.com/darlinghq/darling-xnu.git
synced 2024-11-23 12:39:55 +00:00
1859 lines
59 KiB
C
1859 lines
59 KiB
C
/*
|
|
* Copyright (c) 2012-2020 Apple Inc. All rights reserved.
|
|
*
|
|
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
|
|
*
|
|
* This file contains Original Code and/or Modifications of Original Code
|
|
* as defined in and that are subject to the Apple Public Source License
|
|
* Version 2.0 (the 'License'). You may not use this file except in
|
|
* compliance with the License. The rights granted to you under the License
|
|
* may not be used to create, or enable the creation or redistribution of,
|
|
* unlawful or unlicensed copies of an Apple operating system, or to
|
|
* circumvent, violate, or enable the circumvention or violation of, any
|
|
* terms of an Apple operating system software license agreement.
|
|
*
|
|
* Please obtain a copy of the License at
|
|
* http://www.opensource.apple.com/apsl/ and read it before using this file.
|
|
*
|
|
* The Original Code and all software distributed under the License are
|
|
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
|
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
|
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
|
|
* Please see the License for the specific language governing rights and
|
|
* limitations under the License.
|
|
*
|
|
* @APPLE_OSREFERENCE_LICENSE_HEADER_END@
|
|
*/
|
|
|
|
#include <bank/bank_internal.h>
|
|
#include <bank/bank_types.h>
|
|
#include <mach/mach_types.h>
|
|
#include <mach/kern_return.h>
|
|
#include <ipc/ipc_port.h>
|
|
#include <mach/mach_vm.h>
|
|
#include <mach/vm_map.h>
|
|
#include <vm/vm_map.h>
|
|
#include <mach/host_priv.h>
|
|
#include <mach/host_special_ports.h>
|
|
#include <kern/host.h>
|
|
#include <kern/ledger.h>
|
|
#include <kern/coalition.h>
|
|
#include <kern/thread_group.h>
|
|
#include <sys/kdebug.h>
|
|
#include <IOKit/IOBSD.h>
|
|
#include <mach/mach_voucher_attr_control.h>
|
|
#include <kern/policy_internal.h>
|
|
|
|
static ZONE_DECLARE(bank_task_zone, "bank_task",
|
|
sizeof(struct bank_task), ZC_NONE);
|
|
static ZONE_DECLARE(bank_account_zone, "bank_account",
|
|
sizeof(struct bank_account), ZC_NONE);
|
|
|
|
#define MAX_BANK_TASK (CONFIG_TASK_MAX)
|
|
#define MAX_BANK_ACCOUNT (CONFIG_TASK_MAX + CONFIG_THREAD_MAX)
|
|
|
|
#define BANK_ELEMENT_TO_HANDLE(x) (CAST_DOWN(bank_handle_t, (x)))
|
|
#define HANDLE_TO_BANK_ELEMENT(x) (CAST_DOWN(bank_element_t, (x)))
|
|
|
|
/* Need macro since bank_element_t is 4 byte aligned on release kernel and direct type case gives compilation error */
|
|
#define CAST_TO_BANK_ELEMENT(x) ((bank_element_t)((void *)(x)))
|
|
#define CAST_TO_BANK_TASK(x) ((bank_task_t)((void *)(x)))
|
|
#define CAST_TO_BANK_ACCOUNT(x) ((bank_account_t)((void *)(x)))
|
|
|
|
ipc_voucher_attr_control_t bank_voucher_attr_control; /* communication channel from ATM to voucher system */
|
|
struct persona;
|
|
extern struct persona *system_persona, *proxy_system_persona;
|
|
uint32_t persona_get_id(struct persona *persona);
|
|
extern int unique_persona;
|
|
|
|
static ledger_template_t bank_ledger_template = NULL;
|
|
struct _bank_ledger_indices bank_ledgers = { .cpu_time = -1, .energy = -1 };
|
|
|
|
static bank_task_t bank_task_alloc_init(task_t task);
|
|
static bank_account_t bank_account_alloc_init(bank_task_t bank_holder, bank_task_t bank_merchant,
|
|
bank_task_t bank_secureoriginator, bank_task_t bank_proximateprocess, struct thread_group* banktg, uint32_t persona_id);
|
|
static bank_task_t get_bank_task_context(task_t task, boolean_t initialize);
|
|
static void bank_task_dealloc(bank_task_t bank_task, mach_voucher_attr_value_reference_t sync);
|
|
static kern_return_t bank_account_dealloc_with_sync(bank_account_t bank_account, mach_voucher_attr_value_reference_t sync);
|
|
static void bank_rollup_chit_to_tasks(ledger_t bill, ledger_t bank_holder_ledger, ledger_t bank_merchant_ledger,
|
|
int bank_holder_pid, int bank_merchant_pid);
|
|
static ledger_t bank_get_bank_task_ledger_with_ref(bank_task_t bank_task);
|
|
static void bank_destroy_bank_task_ledger(bank_task_t bank_task);
|
|
static void init_bank_ledgers(void);
|
|
static boolean_t bank_task_is_propagate_entitled(task_t t);
|
|
static boolean_t bank_task_is_persona_modify_entitled(task_t t);
|
|
static struct thread_group *bank_get_bank_task_thread_group(bank_task_t bank_task __unused);
|
|
static struct thread_group *bank_get_bank_account_thread_group(bank_account_t bank_account __unused);
|
|
static boolean_t bank_verify_persona_id(uint32_t persona_id);
|
|
|
|
/* lock to protect task->bank_context transition */
|
|
static LCK_GRP_DECLARE(bank_lock_grp, "bank_lock");
|
|
static LCK_ATTR_DECLARE(bank_lock_attr, 0, 0);
|
|
static LCK_SPIN_DECLARE_ATTR(g_bank_task_lock_data, &bank_lock_grp, &bank_lock_attr);
|
|
|
|
static TUNABLE(bool, disable_persona_propagate_check,
|
|
"disable_persona_propagate_check", false);
|
|
|
|
#define global_bank_task_lock() \
|
|
lck_spin_lock_grp(&g_bank_task_lock_data, &bank_lock_grp)
|
|
#define global_bank_task_lock_try() \
|
|
lck_spin_try_lock_grp(&g_bank_task_lock_data, &bank_lock_grp)
|
|
#define global_bank_task_unlock() \
|
|
lck_spin_unlock(&g_bank_task_lock_data)
|
|
|
|
extern uint64_t proc_uniqueid(void *p);
|
|
struct proc;
|
|
extern int32_t proc_pid(struct proc *p);
|
|
extern int32_t proc_pidversion(void *p);
|
|
extern uint32_t proc_persona_id(void *p);
|
|
extern uint32_t proc_getuid(void *p);
|
|
extern uint32_t proc_getgid(void *p);
|
|
extern void proc_getexecutableuuid(void *p, unsigned char *uuidbuf, unsigned long size);
|
|
extern int kauth_cred_issuser(void *cred);
|
|
extern void* kauth_cred_get(void);
|
|
extern void* persona_lookup(uint32_t id);
|
|
extern void persona_put(void* persona);
|
|
|
|
kern_return_t
|
|
bank_release_value(
|
|
ipc_voucher_attr_manager_t __assert_only manager,
|
|
mach_voucher_attr_key_t __assert_only key,
|
|
mach_voucher_attr_value_handle_t value,
|
|
mach_voucher_attr_value_reference_t sync);
|
|
|
|
kern_return_t
|
|
bank_get_value(
|
|
ipc_voucher_attr_manager_t __assert_only manager,
|
|
mach_voucher_attr_key_t __assert_only key,
|
|
mach_voucher_attr_recipe_command_t command,
|
|
mach_voucher_attr_value_handle_array_t prev_values,
|
|
mach_msg_type_number_t __assert_only prev_value_count,
|
|
mach_voucher_attr_content_t recipe,
|
|
mach_voucher_attr_content_size_t recipe_size,
|
|
mach_voucher_attr_value_handle_t *out_value,
|
|
mach_voucher_attr_value_flags_t *out_flags,
|
|
ipc_voucher_t *out_value_voucher);
|
|
|
|
kern_return_t
|
|
bank_extract_content(
|
|
ipc_voucher_attr_manager_t __assert_only manager,
|
|
mach_voucher_attr_key_t __assert_only key,
|
|
mach_voucher_attr_value_handle_array_t values,
|
|
mach_msg_type_number_t value_count,
|
|
mach_voucher_attr_recipe_command_t *out_command,
|
|
mach_voucher_attr_content_t out_recipe,
|
|
mach_voucher_attr_content_size_t *in_out_recipe_size);
|
|
|
|
kern_return_t
|
|
bank_command(
|
|
ipc_voucher_attr_manager_t __assert_only manager,
|
|
mach_voucher_attr_key_t __assert_only key,
|
|
mach_voucher_attr_value_handle_array_t values,
|
|
mach_msg_type_number_t value_count,
|
|
mach_voucher_attr_command_t command,
|
|
mach_voucher_attr_content_t in_content,
|
|
mach_voucher_attr_content_size_t in_content_size,
|
|
mach_voucher_attr_content_t out_content,
|
|
mach_voucher_attr_content_size_t *in_out_content_size);
|
|
|
|
void
|
|
bank_release(ipc_voucher_attr_manager_t __assert_only manager);
|
|
|
|
/*
|
|
* communication channel from voucher system to ATM
|
|
*/
|
|
const struct ipc_voucher_attr_manager bank_manager = {
|
|
.ivam_release_value = bank_release_value,
|
|
.ivam_get_value = bank_get_value,
|
|
.ivam_extract_content = bank_extract_content,
|
|
.ivam_command = bank_command,
|
|
.ivam_release = bank_release,
|
|
.ivam_flags = (IVAM_FLAGS_SUPPORT_SEND_PREPROCESS | IVAM_FLAGS_SUPPORT_RECEIVE_POSTPROCESS),
|
|
};
|
|
|
|
|
|
#if DEVELOPMENT || DEBUG
|
|
LCK_GRP_DECLARE(bank_dev_lock_grp, "bank_dev_lock");
|
|
LCK_MTX_DECLARE(bank_tasks_list_lock, &bank_dev_lock_grp);
|
|
LCK_MTX_DECLARE(bank_accounts_list_lock, &bank_dev_lock_grp);
|
|
queue_head_t bank_tasks_list = QUEUE_HEAD_INITIALIZER(bank_tasks_list);
|
|
queue_head_t bank_accounts_list = QUEUE_HEAD_INITIALIZER(bank_accounts_list);
|
|
#endif
|
|
|
|
/*
|
|
* Routine: bank_init
|
|
* Purpose: Initialize the BANK subsystem.
|
|
* Returns: None.
|
|
*/
|
|
void
|
|
bank_init()
|
|
{
|
|
kern_return_t kr = KERN_SUCCESS;
|
|
|
|
init_bank_ledgers();
|
|
|
|
/* Register the bank manager with the Vouchers sub system. */
|
|
kr = ipc_register_well_known_mach_voucher_attr_manager(
|
|
&bank_manager,
|
|
0,
|
|
MACH_VOUCHER_ATTR_KEY_BANK,
|
|
&bank_voucher_attr_control);
|
|
if (kr != KERN_SUCCESS) {
|
|
panic("BANK subsystem initialization failed");
|
|
}
|
|
|
|
|
|
kprintf("BANK subsystem is initialized\n");
|
|
}
|
|
|
|
|
|
/*
|
|
* BANK Resource Manager Routines.
|
|
*/
|
|
|
|
|
|
/*
|
|
* Routine: bank_release_value
|
|
* Purpose: Release a value, if sync matches the sync count in value.
|
|
* Returns: KERN_SUCCESS: on Successful deletion.
|
|
* KERN_FAILURE: if sync value does not matches.
|
|
*/
|
|
kern_return_t
|
|
bank_release_value(
|
|
ipc_voucher_attr_manager_t __assert_only manager,
|
|
mach_voucher_attr_key_t __assert_only key,
|
|
mach_voucher_attr_value_handle_t value,
|
|
mach_voucher_attr_value_reference_t sync)
|
|
{
|
|
bank_task_t bank_task = BANK_TASK_NULL;
|
|
bank_element_t bank_element = BANK_ELEMENT_NULL;
|
|
bank_account_t bank_account = BANK_ACCOUNT_NULL;
|
|
kern_return_t kr = KERN_SUCCESS;
|
|
|
|
assert(MACH_VOUCHER_ATTR_KEY_BANK == key);
|
|
assert(manager == &bank_manager);
|
|
|
|
|
|
bank_element = HANDLE_TO_BANK_ELEMENT(value);
|
|
/* Voucher system should never release the default or persistent value */
|
|
assert(bank_element != BANK_DEFAULT_VALUE && bank_element != BANK_DEFAULT_TASK_VALUE);
|
|
|
|
if (bank_element == BANK_DEFAULT_VALUE || bank_element == BANK_DEFAULT_TASK_VALUE) {
|
|
/* Return success for default and default task value */
|
|
return KERN_SUCCESS;
|
|
}
|
|
|
|
|
|
if (bank_element->be_type == BANK_TASK) {
|
|
bank_task = CAST_TO_BANK_TASK(bank_element);
|
|
|
|
/* Checking of the made ref with sync and clearing of voucher ref should be done under a lock */
|
|
lck_mtx_lock(&bank_task->bt_acc_to_pay_lock);
|
|
if (bank_task->bt_made != sync) {
|
|
lck_mtx_unlock(&bank_task->bt_acc_to_pay_lock);
|
|
return KERN_FAILURE;
|
|
}
|
|
|
|
bank_task_made_release_num(bank_task, sync);
|
|
assert(bank_task->bt_voucher_ref == 1);
|
|
bank_task->bt_voucher_ref = 0;
|
|
lck_mtx_unlock(&bank_task->bt_acc_to_pay_lock);
|
|
|
|
bank_task_dealloc(bank_task, 1);
|
|
} else if (bank_element->be_type == BANK_ACCOUNT) {
|
|
bank_account = CAST_TO_BANK_ACCOUNT(bank_element);
|
|
kr = bank_account_dealloc_with_sync(bank_account, sync);
|
|
} else {
|
|
panic("Bogus bank type: %d passed in get_value\n", bank_element->be_type);
|
|
}
|
|
|
|
return kr;
|
|
}
|
|
|
|
|
|
/*
|
|
* Routine: bank_get_value
|
|
*
|
|
* This function uses the recipe to create a bank attribute for a voucher.
|
|
*/
|
|
kern_return_t
|
|
bank_get_value(
|
|
ipc_voucher_attr_manager_t __assert_only manager,
|
|
mach_voucher_attr_key_t __assert_only key,
|
|
mach_voucher_attr_recipe_command_t command,
|
|
mach_voucher_attr_value_handle_array_t prev_values,
|
|
mach_msg_type_number_t prev_value_count,
|
|
mach_voucher_attr_content_t recipe,
|
|
mach_voucher_attr_content_size_t recipe_size,
|
|
mach_voucher_attr_value_handle_t *out_value,
|
|
mach_voucher_attr_value_flags_t *out_flags,
|
|
ipc_voucher_t *out_value_voucher)
|
|
{
|
|
bank_task_t bank_holder = BANK_TASK_NULL;
|
|
bank_task_t bank_merchant = BANK_TASK_NULL;
|
|
bank_task_t bank_secureoriginator = BANK_TASK_NULL;
|
|
bank_task_t bank_proximateprocess = BANK_TASK_NULL;
|
|
bank_element_t bank_element = BANK_ELEMENT_NULL;
|
|
bank_account_t bank_account = BANK_ACCOUNT_NULL;
|
|
bank_account_t old_bank_account = BANK_ACCOUNT_NULL;
|
|
mach_voucher_attr_value_handle_t bank_handle;
|
|
task_t task;
|
|
kern_return_t kr = KERN_SUCCESS;
|
|
mach_msg_type_number_t i;
|
|
struct thread_group *thread_group = NULL;
|
|
struct thread_group *cur_thread_group = NULL;
|
|
uint32_t persona_id = proc_persona_id(NULL);
|
|
|
|
assert(MACH_VOUCHER_ATTR_KEY_BANK == key);
|
|
assert(manager == &bank_manager);
|
|
|
|
/* never an out voucher */
|
|
*out_value_voucher = IPC_VOUCHER_NULL;
|
|
*out_flags = MACH_VOUCHER_ATTR_VALUE_FLAGS_NONE;
|
|
|
|
switch (command) {
|
|
case MACH_VOUCHER_ATTR_BANK_CREATE:
|
|
|
|
/* It returns the default task value. This value is replaced by
|
|
* an actual bank task reference, by using a recipe with
|
|
* MACH_VOUCHER_ATTR_SEND_PREPROCESS command.
|
|
*/
|
|
*out_value = BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_TASK_VALUE);
|
|
*out_flags = MACH_VOUCHER_ATTR_VALUE_FLAGS_PERSIST;
|
|
break;
|
|
|
|
case MACH_VOUCHER_ATTR_BANK_MODIFY_PERSONA:
|
|
|
|
/* It creates a bank account attribute value with a new persona id
|
|
* and auto-redeems it on behalf of the bank_holder.
|
|
*/
|
|
*out_value = BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_VALUE);
|
|
|
|
for (i = 0; i < prev_value_count; i++) {
|
|
bank_handle = prev_values[i];
|
|
bank_element = HANDLE_TO_BANK_ELEMENT(bank_handle);
|
|
|
|
/* Expect a pre-processed attribute value */
|
|
if (bank_element == BANK_DEFAULT_VALUE || bank_element == BANK_DEFAULT_TASK_VALUE) {
|
|
continue;
|
|
}
|
|
|
|
if (!bank_task_is_persona_modify_entitled(current_task())) {
|
|
return KERN_NO_ACCESS;
|
|
}
|
|
|
|
struct persona_modify_info pmi = {};
|
|
if (recipe_size == sizeof(pmi)) {
|
|
memcpy((void *)&pmi, recipe, sizeof(pmi));
|
|
persona_id = pmi.persona_id;
|
|
} else {
|
|
return KERN_INVALID_ARGUMENT;
|
|
}
|
|
|
|
/* Verify if the persona id is valid */
|
|
if (!bank_verify_persona_id(persona_id)) {
|
|
return KERN_INVALID_ARGUMENT;
|
|
}
|
|
|
|
/* Update the persona id only if the bank element is a bank task.
|
|
* This ensures that the bank_holder can be trusted.
|
|
*/
|
|
if (bank_element->be_type == BANK_TASK) {
|
|
bank_holder = CAST_TO_BANK_TASK(bank_element);
|
|
/* Ensure that the requestor validated by userspace matches
|
|
* the bank_holder
|
|
*/
|
|
if (pmi.unique_pid != bank_holder->bt_unique_pid) {
|
|
return KERN_INVALID_CAPABILITY;
|
|
}
|
|
bank_merchant = bank_holder;
|
|
bank_secureoriginator = bank_holder;
|
|
bank_proximateprocess = bank_holder;
|
|
thread_group = bank_get_bank_task_thread_group(bank_holder);
|
|
} else if (bank_element->be_type == BANK_ACCOUNT) {
|
|
return KERN_INVALID_ARGUMENT;
|
|
} else {
|
|
panic("Bogus bank type: %d passed in get_value\n", bank_element->be_type);
|
|
}
|
|
|
|
/* Do not replace persona id if the task is not spawned in system persona */
|
|
if (unique_persona &&
|
|
bank_merchant->bt_persona_id != persona_get_id(system_persona) &&
|
|
bank_merchant->bt_persona_id != persona_get_id(proxy_system_persona) &&
|
|
bank_merchant->bt_persona_id != persona_id) {
|
|
return KERN_INVALID_ARGUMENT;
|
|
}
|
|
|
|
if (bank_holder->bt_persona_id == persona_id) {
|
|
lck_mtx_lock(&bank_holder->bt_acc_to_pay_lock);
|
|
bank_task_made_reference(bank_holder);
|
|
if (bank_holder->bt_voucher_ref == 0) {
|
|
/* Take a ref for voucher system, if voucher system does not have a ref */
|
|
bank_task_reference(bank_holder);
|
|
bank_holder->bt_voucher_ref = 1;
|
|
}
|
|
lck_mtx_unlock(&bank_holder->bt_acc_to_pay_lock);
|
|
|
|
*out_value = BANK_ELEMENT_TO_HANDLE(bank_holder);
|
|
return kr;
|
|
}
|
|
|
|
bank_account = bank_account_alloc_init(bank_holder, bank_merchant,
|
|
bank_secureoriginator, bank_proximateprocess,
|
|
thread_group, persona_id);
|
|
if (bank_account == BANK_ACCOUNT_NULL) {
|
|
return KERN_RESOURCE_SHORTAGE;
|
|
}
|
|
|
|
*out_value = BANK_ELEMENT_TO_HANDLE(bank_account);
|
|
return kr;
|
|
}
|
|
break;
|
|
|
|
case MACH_VOUCHER_ATTR_AUTO_REDEEM:
|
|
|
|
/* It creates a bank account with the bank_merchant set to the current task.
|
|
* A bank attribute voucher needs to be redeemed before it can be adopted by
|
|
* it's threads.
|
|
*/
|
|
for (i = 0; i < prev_value_count; i++) {
|
|
bank_handle = prev_values[i];
|
|
bank_element = HANDLE_TO_BANK_ELEMENT(bank_handle);
|
|
|
|
/* Should not have received default task value from an IPC */
|
|
if (bank_element == BANK_DEFAULT_VALUE || bank_element == BANK_DEFAULT_TASK_VALUE) {
|
|
continue;
|
|
}
|
|
|
|
task = current_task();
|
|
if (bank_element->be_type == BANK_TASK) {
|
|
bank_holder = CAST_TO_BANK_TASK(bank_element);
|
|
bank_secureoriginator = bank_holder;
|
|
bank_proximateprocess = bank_holder;
|
|
thread_group = bank_get_bank_task_thread_group(bank_holder);
|
|
persona_id = bank_holder->bt_persona_id;
|
|
} else if (bank_element->be_type == BANK_ACCOUNT) {
|
|
old_bank_account = CAST_TO_BANK_ACCOUNT(bank_element);
|
|
bank_holder = old_bank_account->ba_holder;
|
|
bank_secureoriginator = old_bank_account->ba_secureoriginator;
|
|
bank_proximateprocess = old_bank_account->ba_proximateprocess;
|
|
thread_group = bank_get_bank_account_thread_group(old_bank_account);
|
|
persona_id = old_bank_account->ba_so_persona_id;
|
|
} else {
|
|
panic("Bogus bank type: %d passed in get_value\n", bank_element->be_type);
|
|
}
|
|
|
|
bank_merchant = get_bank_task_context(task, FALSE);
|
|
if (bank_merchant == BANK_TASK_NULL) {
|
|
return KERN_RESOURCE_SHORTAGE;
|
|
}
|
|
|
|
cur_thread_group = bank_get_bank_task_thread_group(bank_merchant);
|
|
|
|
/* Change voucher thread group to current thread group for Apps */
|
|
if (task_is_app(task)) {
|
|
thread_group = cur_thread_group;
|
|
}
|
|
|
|
/* Change the persona-id to current task persona-id if the task is not spawned in system persona */
|
|
if (unique_persona &&
|
|
bank_merchant->bt_persona_id != persona_get_id(system_persona) &&
|
|
bank_merchant->bt_persona_id != persona_get_id(proxy_system_persona)) {
|
|
persona_id = bank_merchant->bt_persona_id;
|
|
}
|
|
|
|
/* Check if trying to redeem for self task, return the default bank task */
|
|
if (bank_holder == bank_merchant &&
|
|
bank_holder == bank_secureoriginator &&
|
|
bank_holder == bank_proximateprocess &&
|
|
thread_group == cur_thread_group &&
|
|
persona_id == bank_holder->bt_persona_id) {
|
|
*out_value = BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_TASK_VALUE);
|
|
*out_flags = MACH_VOUCHER_ATTR_VALUE_FLAGS_PERSIST;
|
|
return kr;
|
|
}
|
|
|
|
bank_account = bank_account_alloc_init(bank_holder, bank_merchant,
|
|
bank_secureoriginator, bank_proximateprocess,
|
|
thread_group, persona_id);
|
|
if (bank_account == BANK_ACCOUNT_NULL) {
|
|
return KERN_RESOURCE_SHORTAGE;
|
|
}
|
|
|
|
*out_value = BANK_ELEMENT_TO_HANDLE(bank_account);
|
|
return kr;
|
|
}
|
|
|
|
*out_value = BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_VALUE);
|
|
break;
|
|
|
|
case MACH_VOUCHER_ATTR_SEND_PREPROCESS:
|
|
|
|
for (i = 0; i < prev_value_count; i++) {
|
|
bank_handle = prev_values[i];
|
|
bank_element = HANDLE_TO_BANK_ELEMENT(bank_handle);
|
|
|
|
if (bank_element == BANK_DEFAULT_VALUE) {
|
|
continue;
|
|
}
|
|
|
|
task = current_task();
|
|
if (bank_element == BANK_DEFAULT_TASK_VALUE) {
|
|
bank_element = CAST_TO_BANK_ELEMENT(get_bank_task_context(task, FALSE));
|
|
}
|
|
|
|
if (bank_element->be_type == BANK_TASK) {
|
|
bank_holder = CAST_TO_BANK_TASK(bank_element);
|
|
bank_secureoriginator = bank_holder;
|
|
thread_group = bank_get_bank_task_thread_group(bank_holder);
|
|
persona_id = bank_holder->bt_persona_id;
|
|
} else if (bank_element->be_type == BANK_ACCOUNT) {
|
|
old_bank_account = CAST_TO_BANK_ACCOUNT(bank_element);
|
|
bank_holder = old_bank_account->ba_holder;
|
|
bank_secureoriginator = old_bank_account->ba_secureoriginator;
|
|
thread_group = bank_get_bank_account_thread_group(old_bank_account);
|
|
persona_id = old_bank_account->ba_so_persona_id;
|
|
} else {
|
|
panic("Bogus bank type: %d passed in get_value\n", bank_element->be_type);
|
|
}
|
|
|
|
bank_merchant = get_bank_task_context(task, FALSE);
|
|
if (bank_merchant == BANK_TASK_NULL) {
|
|
return KERN_RESOURCE_SHORTAGE;
|
|
}
|
|
|
|
cur_thread_group = bank_get_bank_task_thread_group(bank_merchant);
|
|
|
|
/*
|
|
* If the process doesn't have secure persona entitlement,
|
|
* then replace the secure originator to current task.
|
|
* Also update the persona_id to match that of the secure originator.
|
|
*/
|
|
if (bank_merchant->bt_hasentitlement == 0) {
|
|
KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE,
|
|
(BANK_CODE(BANK_ACCOUNT_INFO, (BANK_SECURE_ORIGINATOR_CHANGED))) | DBG_FUNC_NONE,
|
|
bank_secureoriginator->bt_pid, bank_merchant->bt_pid, 0, 0, 0);
|
|
bank_secureoriginator = bank_merchant;
|
|
persona_id = bank_merchant->bt_persona_id;
|
|
}
|
|
|
|
bank_proximateprocess = bank_merchant;
|
|
|
|
/* Check if trying to pre-process for self task, return the bank task */
|
|
if (bank_holder == bank_merchant &&
|
|
bank_holder == bank_secureoriginator &&
|
|
bank_holder == bank_proximateprocess &&
|
|
thread_group == cur_thread_group &&
|
|
persona_id == bank_holder->bt_persona_id) {
|
|
lck_mtx_lock(&bank_holder->bt_acc_to_pay_lock);
|
|
bank_task_made_reference(bank_holder);
|
|
if (bank_holder->bt_voucher_ref == 0) {
|
|
/* Take a ref for voucher system, if voucher system does not have a ref */
|
|
bank_task_reference(bank_holder);
|
|
bank_holder->bt_voucher_ref = 1;
|
|
}
|
|
lck_mtx_unlock(&bank_holder->bt_acc_to_pay_lock);
|
|
|
|
*out_value = BANK_ELEMENT_TO_HANDLE(bank_holder);
|
|
return kr;
|
|
}
|
|
bank_account = bank_account_alloc_init(bank_holder, bank_merchant,
|
|
bank_secureoriginator, bank_proximateprocess,
|
|
thread_group, persona_id);
|
|
if (bank_account == BANK_ACCOUNT_NULL) {
|
|
return KERN_RESOURCE_SHORTAGE;
|
|
}
|
|
|
|
*out_value = BANK_ELEMENT_TO_HANDLE(bank_account);
|
|
return kr;
|
|
}
|
|
|
|
*out_value = BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_VALUE);
|
|
break;
|
|
|
|
case MACH_VOUCHER_ATTR_REDEEM:
|
|
/* This command expects that the bank attribute has been auto-redeemed
|
|
* and returns a reference to that bank account value.
|
|
*/
|
|
for (i = 0; i < prev_value_count; i++) {
|
|
bank_handle = prev_values[i];
|
|
bank_element = HANDLE_TO_BANK_ELEMENT(bank_handle);
|
|
|
|
if (bank_element == BANK_DEFAULT_VALUE) {
|
|
continue;
|
|
}
|
|
|
|
if (bank_element == BANK_DEFAULT_TASK_VALUE) {
|
|
*out_value = BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_TASK_VALUE);
|
|
*out_flags = MACH_VOUCHER_ATTR_VALUE_FLAGS_PERSIST;
|
|
return kr;
|
|
}
|
|
|
|
task = current_task();
|
|
if (bank_element->be_type == BANK_TASK) {
|
|
bank_holder = CAST_TO_BANK_TASK(bank_element);
|
|
if (bank_holder == get_bank_task_context(task, FALSE)) {
|
|
*out_value = BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_TASK_VALUE);
|
|
*out_flags = MACH_VOUCHER_ATTR_VALUE_FLAGS_PERSIST;
|
|
} else {
|
|
kr = KERN_INVALID_CAPABILITY;
|
|
}
|
|
return kr;
|
|
} else if (bank_element->be_type == BANK_ACCOUNT) {
|
|
bank_account = CAST_TO_BANK_ACCOUNT(bank_element);
|
|
bank_merchant = bank_account->ba_merchant;
|
|
if (bank_merchant != get_bank_task_context(task, FALSE)) {
|
|
/* This error can be used to verify if the task can
|
|
* adopt the voucher.
|
|
*/
|
|
kr = KERN_INVALID_CAPABILITY;
|
|
return kr;
|
|
}
|
|
bank_account_made_reference(bank_account);
|
|
*out_value = BANK_ELEMENT_TO_HANDLE(bank_account);
|
|
return kr;
|
|
} else {
|
|
panic("Bogus bank type: %d passed in get_value\n", bank_element->be_type);
|
|
}
|
|
}
|
|
|
|
*out_value = BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_VALUE);
|
|
break;
|
|
|
|
default:
|
|
kr = KERN_INVALID_ARGUMENT;
|
|
break;
|
|
}
|
|
|
|
return kr;
|
|
}
|
|
|
|
|
|
/*
|
|
* Routine: bank_extract_content
|
|
* Purpose: Extract a set of aid from an array of voucher values.
|
|
* Returns: KERN_SUCCESS: on Success.
|
|
* KERN_FAILURE: one of the value is not present in the hash.
|
|
* KERN_NO_SPACE: insufficeint buffer provided to fill an array of aid.
|
|
*/
|
|
kern_return_t
|
|
bank_extract_content(
|
|
ipc_voucher_attr_manager_t __assert_only manager,
|
|
mach_voucher_attr_key_t __assert_only key,
|
|
mach_voucher_attr_value_handle_array_t values,
|
|
mach_msg_type_number_t value_count,
|
|
mach_voucher_attr_recipe_command_t *out_command,
|
|
mach_voucher_attr_content_t out_recipe,
|
|
mach_voucher_attr_content_size_t *in_out_recipe_size)
|
|
{
|
|
bank_task_t bank_task = BANK_TASK_NULL;
|
|
bank_element_t bank_element = BANK_ELEMENT_NULL;
|
|
bank_account_t bank_account = BANK_ACCOUNT_NULL;
|
|
mach_voucher_attr_value_handle_t bank_handle;
|
|
char buf[MACH_VOUCHER_BANK_CONTENT_SIZE];
|
|
mach_msg_type_number_t i;
|
|
|
|
assert(MACH_VOUCHER_ATTR_KEY_BANK == key);
|
|
assert(manager == &bank_manager);
|
|
|
|
for (i = 0; i < value_count && *in_out_recipe_size > 0; i++) {
|
|
bank_handle = values[i];
|
|
bank_element = HANDLE_TO_BANK_ELEMENT(bank_handle);
|
|
if (bank_element == BANK_DEFAULT_VALUE) {
|
|
continue;
|
|
}
|
|
|
|
if (bank_element == BANK_DEFAULT_TASK_VALUE) {
|
|
bank_element = CAST_TO_BANK_ELEMENT(get_bank_task_context(current_task(), FALSE));
|
|
}
|
|
|
|
if (MACH_VOUCHER_BANK_CONTENT_SIZE > *in_out_recipe_size) {
|
|
*in_out_recipe_size = 0;
|
|
return KERN_NO_SPACE;
|
|
}
|
|
|
|
if (bank_element->be_type == BANK_TASK) {
|
|
bank_task = CAST_TO_BANK_TASK(bank_element);
|
|
snprintf(buf, MACH_VOUCHER_BANK_CONTENT_SIZE,
|
|
" Bank Context for a pid %d\n", bank_task->bt_pid);
|
|
} else if (bank_element->be_type == BANK_ACCOUNT) {
|
|
bank_account = CAST_TO_BANK_ACCOUNT(bank_element);
|
|
snprintf(buf, MACH_VOUCHER_BANK_CONTENT_SIZE,
|
|
" Bank Account linking holder pid %d with merchant pid %d, originator PID/persona: %d, %u and proximate PID/persona: %d, %u\n",
|
|
bank_account->ba_holder->bt_pid,
|
|
bank_account->ba_merchant->bt_pid,
|
|
bank_account->ba_secureoriginator->bt_pid,
|
|
bank_account->ba_so_persona_id,
|
|
bank_account->ba_proximateprocess->bt_pid,
|
|
bank_account->ba_proximateprocess->bt_persona_id);
|
|
} else {
|
|
panic("Bogus bank type: %d passed in get_value\n", bank_element->be_type);
|
|
}
|
|
|
|
memcpy(&out_recipe[0], buf, strlen(buf) + 1);
|
|
*out_command = MACH_VOUCHER_ATTR_BANK_NULL;
|
|
*in_out_recipe_size = (mach_voucher_attr_content_size_t)strlen(buf) + 1;
|
|
return KERN_SUCCESS;
|
|
}
|
|
|
|
return KERN_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* Routine: bank_command
|
|
* Purpose: Execute a command against a set of bank values.
|
|
* Returns: KERN_SUCCESS: On successful execution of command.
|
|
* KERN_FAILURE: On failure.
|
|
*/
|
|
kern_return_t
|
|
bank_command(
|
|
ipc_voucher_attr_manager_t __assert_only manager,
|
|
mach_voucher_attr_key_t __assert_only key,
|
|
mach_voucher_attr_value_handle_array_t __unused values,
|
|
mach_msg_type_number_t __unused value_count,
|
|
mach_voucher_attr_command_t __unused command,
|
|
mach_voucher_attr_content_t __unused in_content,
|
|
mach_voucher_attr_content_size_t __unused in_content_size,
|
|
mach_voucher_attr_content_t __unused out_content,
|
|
mach_voucher_attr_content_size_t __unused *out_content_size)
|
|
{
|
|
bank_task_t bank_task = BANK_TASK_NULL;
|
|
bank_task_t bank_secureoriginator = BANK_TASK_NULL;
|
|
bank_task_t bank_proximateprocess = BANK_TASK_NULL;
|
|
struct persona_token *token = NULL;
|
|
bank_element_t bank_element = BANK_ELEMENT_NULL;
|
|
bank_account_t bank_account = BANK_ACCOUNT_NULL;
|
|
mach_voucher_attr_value_handle_t bank_handle;
|
|
mach_msg_type_number_t i;
|
|
int32_t pid;
|
|
uint32_t persona_id;
|
|
|
|
assert(MACH_VOUCHER_ATTR_KEY_BANK == key);
|
|
assert(manager == &bank_manager);
|
|
|
|
switch (command) {
|
|
case BANK_ORIGINATOR_PID:
|
|
|
|
if ((sizeof(pid)) > *out_content_size) {
|
|
*out_content_size = 0;
|
|
return KERN_NO_SPACE;
|
|
}
|
|
|
|
for (i = 0; i < value_count; i++) {
|
|
bank_handle = values[i];
|
|
bank_element = HANDLE_TO_BANK_ELEMENT(bank_handle);
|
|
if (bank_element == BANK_DEFAULT_VALUE) {
|
|
continue;
|
|
}
|
|
|
|
if (bank_element == BANK_DEFAULT_TASK_VALUE) {
|
|
bank_element = CAST_TO_BANK_ELEMENT(get_bank_task_context(current_task(), FALSE));
|
|
}
|
|
|
|
if (bank_element->be_type == BANK_TASK) {
|
|
bank_task = CAST_TO_BANK_TASK(bank_element);
|
|
} else if (bank_element->be_type == BANK_ACCOUNT) {
|
|
bank_account = CAST_TO_BANK_ACCOUNT(bank_element);
|
|
bank_task = bank_account->ba_holder;
|
|
} else {
|
|
panic("Bogus bank type: %d passed in voucher_command\n", bank_element->be_type);
|
|
}
|
|
pid = bank_task->bt_pid;
|
|
|
|
memcpy(&out_content[0], &pid, sizeof(pid));
|
|
*out_content_size = (mach_voucher_attr_content_size_t)sizeof(pid);
|
|
return KERN_SUCCESS;
|
|
}
|
|
/* In the case of no value, return error KERN_INVALID_VALUE */
|
|
*out_content_size = 0;
|
|
return KERN_INVALID_VALUE;
|
|
|
|
case BANK_PERSONA_TOKEN:
|
|
|
|
if ((sizeof(struct persona_token)) > *out_content_size) {
|
|
*out_content_size = 0;
|
|
return KERN_NO_SPACE;
|
|
}
|
|
for (i = 0; i < value_count; i++) {
|
|
bank_handle = values[i];
|
|
bank_element = HANDLE_TO_BANK_ELEMENT(bank_handle);
|
|
if (bank_element == BANK_DEFAULT_VALUE) {
|
|
continue;
|
|
}
|
|
|
|
if (bank_element == BANK_DEFAULT_TASK_VALUE) {
|
|
bank_element = CAST_TO_BANK_ELEMENT(get_bank_task_context(current_task(), FALSE));
|
|
}
|
|
|
|
if (bank_element->be_type == BANK_TASK) {
|
|
*out_content_size = 0;
|
|
return KERN_INVALID_OBJECT;
|
|
} else if (bank_element->be_type == BANK_ACCOUNT) {
|
|
bank_account = CAST_TO_BANK_ACCOUNT(bank_element);
|
|
bank_secureoriginator = bank_account->ba_secureoriginator;
|
|
bank_proximateprocess = bank_account->ba_proximateprocess;
|
|
} else {
|
|
panic("Bogus bank type: %d passed in voucher_command\n", bank_element->be_type);
|
|
}
|
|
token = (struct persona_token *)(void *)&out_content[0];
|
|
memcpy(&token->originator, &bank_secureoriginator->bt_proc_persona, sizeof(struct proc_persona_info));
|
|
memcpy(&token->proximate, &bank_proximateprocess->bt_proc_persona, sizeof(struct proc_persona_info));
|
|
|
|
*out_content_size = (mach_voucher_attr_content_size_t)sizeof(*token);
|
|
return KERN_SUCCESS;
|
|
}
|
|
/* In the case of no value, return error KERN_INVALID_VALUE */
|
|
*out_content_size = 0;
|
|
return KERN_INVALID_VALUE;
|
|
|
|
case BANK_PERSONA_ID:
|
|
|
|
if ((sizeof(persona_id)) > *out_content_size) {
|
|
*out_content_size = 0;
|
|
return KERN_NO_SPACE;
|
|
}
|
|
|
|
for (i = 0; i < value_count; i++) {
|
|
bank_handle = values[i];
|
|
bank_element = HANDLE_TO_BANK_ELEMENT(bank_handle);
|
|
if (bank_element == BANK_DEFAULT_VALUE) {
|
|
continue;
|
|
}
|
|
|
|
if (bank_element == BANK_DEFAULT_TASK_VALUE) {
|
|
bank_element = CAST_TO_BANK_ELEMENT(get_bank_task_context(current_task(), FALSE));
|
|
}
|
|
|
|
if (bank_element->be_type == BANK_TASK) {
|
|
bank_task = CAST_TO_BANK_TASK(bank_element);
|
|
persona_id = bank_task->bt_persona_id;
|
|
} else if (bank_element->be_type == BANK_ACCOUNT) {
|
|
bank_account = CAST_TO_BANK_ACCOUNT(bank_element);
|
|
persona_id = bank_account->ba_so_persona_id;
|
|
} else {
|
|
panic("Bogus bank type: %d passed in voucher_command\n", bank_element->be_type);
|
|
}
|
|
|
|
memcpy(out_content, &persona_id, sizeof(persona_id));
|
|
*out_content_size = (mach_voucher_attr_content_size_t)sizeof(persona_id);
|
|
return KERN_SUCCESS;
|
|
}
|
|
/* In the case of no value, return error KERN_INVALID_VALUE */
|
|
*out_content_size = 0;
|
|
return KERN_INVALID_VALUE;
|
|
|
|
default:
|
|
return KERN_INVALID_ARGUMENT;
|
|
}
|
|
return KERN_SUCCESS;
|
|
}
|
|
|
|
|
|
void
|
|
bank_release(
|
|
ipc_voucher_attr_manager_t __assert_only manager)
|
|
{
|
|
assert(manager == &bank_manager);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Bank Internal Routines.
|
|
*/
|
|
|
|
/*
|
|
* Routine: bank_task_alloc_init
|
|
* Purpose: Allocate and initialize a bank task structure.
|
|
* Returns: bank_task_t on Success.
|
|
* BANK_TASK_NULL: on Failure.
|
|
* Notes: Leaves the task and ledger blank and has only 1 ref,
|
|
* needs to take 1 extra ref after the task field is initialized.
|
|
*/
|
|
static bank_task_t
|
|
bank_task_alloc_init(task_t task)
|
|
{
|
|
bank_task_t new_bank_task;
|
|
|
|
new_bank_task = (bank_task_t) zalloc(bank_task_zone);
|
|
if (new_bank_task == BANK_TASK_NULL) {
|
|
return BANK_TASK_NULL;
|
|
}
|
|
|
|
new_bank_task->bt_type = BANK_TASK;
|
|
new_bank_task->bt_voucher_ref = 0;
|
|
new_bank_task->bt_refs = 1;
|
|
new_bank_task->bt_made = 0;
|
|
new_bank_task->bt_ledger = LEDGER_NULL;
|
|
new_bank_task->bt_hasentitlement = bank_task_is_propagate_entitled(task);
|
|
queue_init(&new_bank_task->bt_accounts_to_pay);
|
|
queue_init(&new_bank_task->bt_accounts_to_charge);
|
|
lck_mtx_init(&new_bank_task->bt_acc_to_pay_lock, &bank_lock_grp, &bank_lock_attr);
|
|
lck_mtx_init(&new_bank_task->bt_acc_to_charge_lock, &bank_lock_grp, &bank_lock_attr);
|
|
|
|
/*
|
|
* Initialize the persona_id struct
|
|
*/
|
|
bzero(&new_bank_task->bt_proc_persona, sizeof(new_bank_task->bt_proc_persona));
|
|
new_bank_task->bt_flags = 0;
|
|
new_bank_task->bt_unique_pid = proc_uniqueid(task->bsd_info);
|
|
new_bank_task->bt_pid = proc_pid(task->bsd_info);
|
|
new_bank_task->bt_pidversion = proc_pidversion(task->bsd_info);
|
|
new_bank_task->bt_persona_id = proc_persona_id(task->bsd_info);
|
|
new_bank_task->bt_uid = proc_getuid(task->bsd_info);
|
|
new_bank_task->bt_gid = proc_getgid(task->bsd_info);
|
|
#if CONFIG_THREAD_GROUPS
|
|
new_bank_task->bt_thread_group = thread_group_retain(task_coalition_get_thread_group(task));
|
|
#endif
|
|
proc_getexecutableuuid(task->bsd_info, new_bank_task->bt_macho_uuid, sizeof(new_bank_task->bt_macho_uuid));
|
|
|
|
#if DEVELOPMENT || DEBUG
|
|
new_bank_task->bt_task = NULL;
|
|
lck_mtx_lock(&bank_tasks_list_lock);
|
|
queue_enter(&bank_tasks_list, new_bank_task, bank_task_t, bt_global_elt);
|
|
lck_mtx_unlock(&bank_tasks_list_lock);
|
|
#endif
|
|
return new_bank_task;
|
|
}
|
|
|
|
/*
|
|
* Routine: proc_is_propagate_entitled
|
|
* Purpose: Check if the process is allowed to propagate secure originator.
|
|
* Returns: TRUE if entitled.
|
|
* FALSE if not.
|
|
*/
|
|
static boolean_t
|
|
bank_task_is_propagate_entitled(task_t t)
|
|
{
|
|
/* Check if it has an entitlement which disallows secure originator propagation */
|
|
boolean_t entitled = FALSE;
|
|
entitled = IOTaskHasEntitlement(t, ENTITLEMENT_PERSONA_NO_PROPAGATE);
|
|
if (entitled) {
|
|
return FALSE;
|
|
}
|
|
|
|
/* If it's a platform binary, allow propagation by default */
|
|
if (disable_persona_propagate_check || (t->t_flags & TF_PLATFORM)) {
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Routine: proc_is_persona_modify_entitled
|
|
* Purpose: Check if the process has persona modify entitlement.
|
|
* Returns: TRUE if entitled.
|
|
* FALSE if not.
|
|
*/
|
|
static boolean_t
|
|
bank_task_is_persona_modify_entitled(task_t t)
|
|
{
|
|
boolean_t entitled = FALSE;
|
|
entitled = IOTaskHasEntitlement(t, ENTITLEMENT_PERSONA_MODIFY);
|
|
return entitled;
|
|
}
|
|
|
|
/*
|
|
* Routine: bank_account_alloc_init
|
|
* Purpose: Allocate and Initialize the bank account struct.
|
|
* Returns: bank_account_t : On Success.
|
|
* BANK_ACCOUNT_NULL: On Failure.
|
|
*/
|
|
static bank_account_t
|
|
bank_account_alloc_init(
|
|
bank_task_t bank_holder,
|
|
bank_task_t bank_merchant,
|
|
bank_task_t bank_secureoriginator,
|
|
bank_task_t bank_proximateprocess,
|
|
struct thread_group *thread_group,
|
|
uint32_t persona_id)
|
|
{
|
|
bank_account_t new_bank_account;
|
|
bank_account_t bank_account;
|
|
boolean_t entry_found = FALSE;
|
|
ledger_t new_ledger = ledger_instantiate(bank_ledger_template, LEDGER_CREATE_INACTIVE_ENTRIES);
|
|
|
|
if (new_ledger == LEDGER_NULL) {
|
|
return BANK_ACCOUNT_NULL;
|
|
}
|
|
|
|
ledger_entry_setactive(new_ledger, bank_ledgers.cpu_time);
|
|
ledger_entry_setactive(new_ledger, bank_ledgers.energy);
|
|
new_bank_account = (bank_account_t) zalloc(bank_account_zone);
|
|
if (new_bank_account == BANK_ACCOUNT_NULL) {
|
|
ledger_dereference(new_ledger);
|
|
return BANK_ACCOUNT_NULL;
|
|
}
|
|
|
|
new_bank_account->ba_type = BANK_ACCOUNT;
|
|
new_bank_account->ba_voucher_ref = 0;
|
|
new_bank_account->ba_refs = 1;
|
|
new_bank_account->ba_made = 1;
|
|
new_bank_account->ba_bill = new_ledger;
|
|
new_bank_account->ba_merchant = bank_merchant;
|
|
new_bank_account->ba_holder = bank_holder;
|
|
new_bank_account->ba_secureoriginator = bank_secureoriginator;
|
|
new_bank_account->ba_proximateprocess = bank_proximateprocess;
|
|
#if CONFIG_THREAD_GROUPS
|
|
new_bank_account->ba_thread_group = thread_group;
|
|
#endif
|
|
new_bank_account->ba_so_persona_id = persona_id;
|
|
|
|
/* Iterate through accounts need to pay list to find the existing entry */
|
|
lck_mtx_lock(&bank_holder->bt_acc_to_pay_lock);
|
|
queue_iterate(&bank_holder->bt_accounts_to_pay, bank_account, bank_account_t, ba_next_acc_to_pay) {
|
|
if (bank_account->ba_merchant != bank_merchant ||
|
|
bank_account->ba_secureoriginator != bank_secureoriginator ||
|
|
bank_account->ba_proximateprocess != bank_proximateprocess ||
|
|
bank_get_bank_account_thread_group(bank_account) != thread_group ||
|
|
bank_account->ba_so_persona_id != persona_id) {
|
|
continue;
|
|
}
|
|
|
|
entry_found = TRUE;
|
|
/* Take a made ref, since this value would be returned to voucher system. */
|
|
bank_account_made_reference(bank_account);
|
|
break;
|
|
}
|
|
|
|
if (!entry_found) {
|
|
/* Create a linkage between the holder and the merchant task, Grab both the list locks before adding it to the list. */
|
|
lck_mtx_lock(&bank_merchant->bt_acc_to_charge_lock);
|
|
|
|
/* Add the account entry into Accounts need to pay account link list. */
|
|
queue_enter(&bank_holder->bt_accounts_to_pay, new_bank_account, bank_account_t, ba_next_acc_to_pay);
|
|
|
|
/* Add the account entry into Accounts need to charge account link list. */
|
|
queue_enter(&bank_merchant->bt_accounts_to_charge, new_bank_account, bank_account_t, ba_next_acc_to_charge);
|
|
|
|
lck_mtx_unlock(&bank_merchant->bt_acc_to_charge_lock);
|
|
}
|
|
|
|
lck_mtx_unlock(&bank_holder->bt_acc_to_pay_lock);
|
|
|
|
if (entry_found) {
|
|
ledger_dereference(new_ledger);
|
|
zfree(bank_account_zone, new_bank_account);
|
|
return bank_account;
|
|
}
|
|
|
|
bank_task_reference(bank_holder);
|
|
bank_task_reference(bank_merchant);
|
|
bank_task_reference(bank_secureoriginator);
|
|
bank_task_reference(bank_proximateprocess);
|
|
#if CONFIG_THREAD_GROUPS
|
|
assert(new_bank_account->ba_thread_group != NULL);
|
|
thread_group_retain(new_bank_account->ba_thread_group);
|
|
#endif
|
|
|
|
#if DEVELOPMENT || DEBUG
|
|
new_bank_account->ba_task = NULL;
|
|
lck_mtx_lock(&bank_accounts_list_lock);
|
|
queue_enter(&bank_accounts_list, new_bank_account, bank_account_t, ba_global_elt);
|
|
lck_mtx_unlock(&bank_accounts_list_lock);
|
|
#endif
|
|
|
|
return new_bank_account;
|
|
}
|
|
|
|
/*
|
|
* Routine: get_bank_task_context
|
|
* Purpose: Get the bank context of the given task
|
|
* Returns: bank_task_t on Success.
|
|
* BANK_TASK_NULL: on Failure.
|
|
* Note: Initialize bank context if NULL.
|
|
*/
|
|
static bank_task_t
|
|
get_bank_task_context
|
|
(task_t task,
|
|
boolean_t initialize)
|
|
{
|
|
bank_task_t bank_task;
|
|
|
|
if (task->bank_context || !initialize) {
|
|
assert(task->bank_context != NULL);
|
|
return task->bank_context;
|
|
}
|
|
|
|
bank_task = bank_task_alloc_init(task);
|
|
|
|
/* Grab the task lock and check if we won the race. */
|
|
task_lock(task);
|
|
if (task->bank_context) {
|
|
task_unlock(task);
|
|
if (bank_task != BANK_TASK_NULL) {
|
|
bank_task_dealloc(bank_task, 1);
|
|
}
|
|
return task->bank_context;
|
|
} else if (bank_task == BANK_TASK_NULL) {
|
|
task_unlock(task);
|
|
return BANK_TASK_NULL;
|
|
}
|
|
/* We won the race. Take a ref on the ledger and initialize bank task. */
|
|
bank_task->bt_ledger = task->ledger;
|
|
#if DEVELOPMENT || DEBUG
|
|
bank_task->bt_task = task;
|
|
#endif
|
|
ledger_reference(task->ledger);
|
|
|
|
/* Grab the global bank task lock before setting the bank context on a task */
|
|
global_bank_task_lock();
|
|
task->bank_context = bank_task;
|
|
global_bank_task_unlock();
|
|
|
|
task_unlock(task);
|
|
|
|
return bank_task;
|
|
}
|
|
|
|
/*
|
|
* Routine: bank_task_dealloc
|
|
* Purpose: Drops the reference on bank task.
|
|
* Returns: None.
|
|
*/
|
|
static void
|
|
bank_task_dealloc(
|
|
bank_task_t bank_task,
|
|
mach_voucher_attr_value_reference_t sync)
|
|
{
|
|
assert(bank_task->bt_refs >= 0);
|
|
|
|
if (bank_task_release_num(bank_task, sync) > (int)sync) {
|
|
return;
|
|
}
|
|
|
|
assert(bank_task->bt_refs == 0);
|
|
assert(queue_empty(&bank_task->bt_accounts_to_pay));
|
|
assert(queue_empty(&bank_task->bt_accounts_to_charge));
|
|
|
|
assert(!LEDGER_VALID(bank_task->bt_ledger));
|
|
lck_mtx_destroy(&bank_task->bt_acc_to_pay_lock, &bank_lock_grp);
|
|
lck_mtx_destroy(&bank_task->bt_acc_to_charge_lock, &bank_lock_grp);
|
|
|
|
#if CONFIG_THREAD_GROUPS
|
|
thread_group_release(bank_task->bt_thread_group);
|
|
#endif
|
|
|
|
#if DEVELOPMENT || DEBUG
|
|
lck_mtx_lock(&bank_tasks_list_lock);
|
|
queue_remove(&bank_tasks_list, bank_task, bank_task_t, bt_global_elt);
|
|
lck_mtx_unlock(&bank_tasks_list_lock);
|
|
#endif
|
|
|
|
zfree(bank_task_zone, bank_task);
|
|
}
|
|
|
|
/*
|
|
* Routine: bank_account_dealloc_with_sync
|
|
* Purpose: Drop the reference on bank account if the sync matches.
|
|
* Returns: KERN_SUCCESS if sync matches.
|
|
* KERN_FAILURE on mismatch.
|
|
*/
|
|
static kern_return_t
|
|
bank_account_dealloc_with_sync(
|
|
bank_account_t bank_account,
|
|
mach_voucher_attr_value_reference_t sync)
|
|
{
|
|
bank_task_t bank_holder = bank_account->ba_holder;
|
|
bank_task_t bank_merchant = bank_account->ba_merchant;
|
|
bank_task_t bank_secureoriginator = bank_account->ba_secureoriginator;
|
|
bank_task_t bank_proximateprocess = bank_account->ba_proximateprocess;
|
|
ledger_t bank_merchant_ledger = LEDGER_NULL;
|
|
|
|
/*
|
|
* Grab a reference on the bank_merchant_ledger, since we would not be able
|
|
* to take bt_acc_to_pay_lock for bank_merchant later.
|
|
*/
|
|
bank_merchant_ledger = bank_get_bank_task_ledger_with_ref(bank_merchant);
|
|
|
|
/* Grab the acc to pay list lock and check the sync value */
|
|
lck_mtx_lock(&bank_holder->bt_acc_to_pay_lock);
|
|
|
|
if (bank_account->ba_made != sync) {
|
|
lck_mtx_unlock(&bank_holder->bt_acc_to_pay_lock);
|
|
if (bank_merchant_ledger) {
|
|
ledger_dereference(bank_merchant_ledger);
|
|
}
|
|
return KERN_FAILURE;
|
|
}
|
|
|
|
bank_account_made_release_num(bank_account, sync);
|
|
|
|
if (bank_account_release_num(bank_account, 1) > 1) {
|
|
panic("Releasing a non zero ref bank account %p\n", bank_account);
|
|
}
|
|
|
|
|
|
/* Grab both the acc to pay and acc to charge locks */
|
|
lck_mtx_lock(&bank_merchant->bt_acc_to_charge_lock);
|
|
|
|
/* No need to take ledger reference for bank_holder ledger since bt_acc_to_pay_lock is locked */
|
|
bank_rollup_chit_to_tasks(bank_account->ba_bill, bank_holder->bt_ledger, bank_merchant_ledger,
|
|
bank_holder->bt_pid, bank_merchant->bt_pid);
|
|
|
|
/* Remove the account entry from Accounts need to pay account link list. */
|
|
queue_remove(&bank_holder->bt_accounts_to_pay, bank_account, bank_account_t, ba_next_acc_to_pay);
|
|
|
|
/* Remove the account entry from Accounts need to charge account link list. */
|
|
queue_remove(&bank_merchant->bt_accounts_to_charge, bank_account, bank_account_t, ba_next_acc_to_charge);
|
|
|
|
lck_mtx_unlock(&bank_merchant->bt_acc_to_charge_lock);
|
|
lck_mtx_unlock(&bank_holder->bt_acc_to_pay_lock);
|
|
|
|
if (bank_merchant_ledger) {
|
|
ledger_dereference(bank_merchant_ledger);
|
|
}
|
|
ledger_dereference(bank_account->ba_bill);
|
|
|
|
/* Drop the reference of bank holder and merchant */
|
|
bank_task_dealloc(bank_holder, 1);
|
|
bank_task_dealloc(bank_merchant, 1);
|
|
bank_task_dealloc(bank_secureoriginator, 1);
|
|
bank_task_dealloc(bank_proximateprocess, 1);
|
|
#if CONFIG_THREAD_GROUPS
|
|
assert(bank_account->ba_thread_group != NULL);
|
|
thread_group_release(bank_account->ba_thread_group);
|
|
#endif
|
|
|
|
#if DEVELOPMENT || DEBUG
|
|
lck_mtx_lock(&bank_accounts_list_lock);
|
|
queue_remove(&bank_accounts_list, bank_account, bank_account_t, ba_global_elt);
|
|
lck_mtx_unlock(&bank_accounts_list_lock);
|
|
#endif
|
|
|
|
zfree(bank_account_zone, bank_account);
|
|
return KERN_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* Routine: bank_rollup_chit_to_tasks
|
|
* Purpose: Debit and Credit holder's and merchant's ledgers.
|
|
* Returns: None.
|
|
*/
|
|
static void
|
|
bank_rollup_chit_to_tasks(
|
|
ledger_t bill,
|
|
ledger_t bank_holder_ledger,
|
|
ledger_t bank_merchant_ledger,
|
|
int bank_holder_pid,
|
|
int bank_merchant_pid)
|
|
{
|
|
ledger_amount_t credit;
|
|
ledger_amount_t debit;
|
|
kern_return_t ret;
|
|
|
|
if (bank_holder_ledger == bank_merchant_ledger) {
|
|
return;
|
|
}
|
|
|
|
ret = ledger_get_entries(bill, bank_ledgers.cpu_time, &credit, &debit);
|
|
if (ret == KERN_SUCCESS) {
|
|
KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE,
|
|
(BANK_CODE(BANK_ACCOUNT_INFO, (BANK_SETTLE_CPU_TIME))) | DBG_FUNC_NONE,
|
|
bank_merchant_pid, bank_holder_pid, credit, debit, 0);
|
|
|
|
if (bank_holder_ledger) {
|
|
ledger_credit(bank_holder_ledger, task_ledgers.cpu_time_billed_to_me, credit);
|
|
ledger_debit(bank_holder_ledger, task_ledgers.cpu_time_billed_to_me, debit);
|
|
}
|
|
|
|
if (bank_merchant_ledger) {
|
|
ledger_credit(bank_merchant_ledger, task_ledgers.cpu_time_billed_to_others, credit);
|
|
ledger_debit(bank_merchant_ledger, task_ledgers.cpu_time_billed_to_others, debit);
|
|
}
|
|
}
|
|
|
|
ret = ledger_get_entries(bill, bank_ledgers.energy, &credit, &debit);
|
|
if (ret == KERN_SUCCESS) {
|
|
KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE,
|
|
(BANK_CODE(BANK_ACCOUNT_INFO, (BANK_SETTLE_ENERGY))) | DBG_FUNC_NONE,
|
|
bank_merchant_pid, bank_holder_pid, credit, debit, 0);
|
|
|
|
if (bank_holder_ledger) {
|
|
ledger_credit(bank_holder_ledger, task_ledgers.energy_billed_to_me, credit);
|
|
ledger_debit(bank_holder_ledger, task_ledgers.energy_billed_to_me, debit);
|
|
}
|
|
|
|
if (bank_merchant_ledger) {
|
|
ledger_credit(bank_merchant_ledger, task_ledgers.energy_billed_to_others, credit);
|
|
ledger_debit(bank_merchant_ledger, task_ledgers.energy_billed_to_others, debit);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Routine: bank_task_destroy
|
|
* Purpose: Drops reference on bank task.
|
|
* Returns: None.
|
|
*/
|
|
void
|
|
bank_task_destroy(task_t task)
|
|
{
|
|
bank_task_t bank_task;
|
|
|
|
/* Grab the global bank task lock before dropping the ref on task bank context */
|
|
global_bank_task_lock();
|
|
bank_task = task->bank_context;
|
|
task->bank_context = NULL;
|
|
global_bank_task_unlock();
|
|
|
|
bank_destroy_bank_task_ledger(bank_task);
|
|
bank_task_dealloc(bank_task, 1);
|
|
}
|
|
|
|
/*
|
|
* Routine: bank_task_initialize
|
|
* Purpose: Initialize the bank context of a task.
|
|
* Returns: None.
|
|
*/
|
|
void
|
|
bank_task_initialize(task_t task)
|
|
{
|
|
get_bank_task_context(task, TRUE);
|
|
}
|
|
|
|
/*
|
|
* Routine: init_bank_ledgers
|
|
* Purpose: Initialize template for bank ledgers.
|
|
* Returns: None.
|
|
*/
|
|
static void
|
|
init_bank_ledgers(void)
|
|
{
|
|
ledger_template_t t;
|
|
int idx;
|
|
|
|
assert(bank_ledger_template == NULL);
|
|
|
|
if ((t = ledger_template_create("Bank ledger")) == NULL) {
|
|
panic("couldn't create bank ledger template");
|
|
}
|
|
|
|
if ((idx = ledger_entry_add(t, "cpu_time", "sched", "ns")) < 0) {
|
|
panic("couldn't create cpu_time entry for bank ledger template");
|
|
}
|
|
bank_ledgers.cpu_time = idx;
|
|
|
|
if ((idx = ledger_entry_add(t, "energy", "power", "nj")) < 0) {
|
|
panic("couldn't create energy entry for bank ledger template");
|
|
}
|
|
bank_ledgers.energy = idx;
|
|
|
|
ledger_template_complete(t);
|
|
bank_ledger_template = t;
|
|
}
|
|
|
|
/* Routine: bank_billed_balance_safe
|
|
* Purpose: Walk through all the bank accounts billed to me by other tasks and get the current billing balance.
|
|
* Called from another task. It takes global bank task lock to make sure the bank context is
|
|
* not deallocated while accesing it.
|
|
* Returns: cpu balance and energy balance in out paremeters.
|
|
*/
|
|
void
|
|
bank_billed_balance_safe(task_t task, uint64_t *cpu_time, uint64_t *energy)
|
|
{
|
|
bank_task_t bank_task = BANK_TASK_NULL;
|
|
ledger_amount_t credit, debit;
|
|
uint64_t cpu_balance = 0;
|
|
uint64_t energy_balance = 0;
|
|
kern_return_t kr;
|
|
|
|
/* Task might be in exec, grab the global bank task lock before accessing bank context. */
|
|
global_bank_task_lock();
|
|
/* Grab a reference on bank context */
|
|
if (task->bank_context != NULL) {
|
|
bank_task = task->bank_context;
|
|
bank_task_reference(bank_task);
|
|
}
|
|
global_bank_task_unlock();
|
|
|
|
if (bank_task) {
|
|
bank_billed_balance(bank_task, &cpu_balance, &energy_balance);
|
|
bank_task_dealloc(bank_task, 1);
|
|
} else {
|
|
kr = ledger_get_entries(task->ledger, task_ledgers.cpu_time_billed_to_me,
|
|
&credit, &debit);
|
|
if (kr == KERN_SUCCESS) {
|
|
cpu_balance = credit - debit;
|
|
}
|
|
kr = ledger_get_entries(task->ledger, task_ledgers.energy_billed_to_me,
|
|
&credit, &debit);
|
|
if (kr == KERN_SUCCESS) {
|
|
energy_balance = credit - debit;
|
|
}
|
|
}
|
|
|
|
*cpu_time = cpu_balance;
|
|
*energy = energy_balance;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Routine: bank_billed_time
|
|
* Purpose: Walk through the Accounts need to pay account list and get the current billing balance.
|
|
* Returns: cpu balance and energy balance in out paremeters.
|
|
*/
|
|
void
|
|
bank_billed_balance(bank_task_t bank_task, uint64_t *cpu_time, uint64_t *energy)
|
|
{
|
|
int64_t cpu_balance = 0;
|
|
int64_t energy_balance = 0;
|
|
bank_account_t bank_account;
|
|
int64_t temp = 0;
|
|
kern_return_t kr;
|
|
if (bank_task == BANK_TASK_NULL) {
|
|
*cpu_time = 0;
|
|
*energy = 0;
|
|
return;
|
|
}
|
|
|
|
lck_mtx_lock(&bank_task->bt_acc_to_pay_lock);
|
|
|
|
/* bt_acc_to_pay_lock locked, no need to take ledger reference for bt_ledger */
|
|
if (bank_task->bt_ledger != LEDGER_NULL) {
|
|
kr = ledger_get_balance(bank_task->bt_ledger, task_ledgers.cpu_time_billed_to_me, &temp);
|
|
if (kr == KERN_SUCCESS && temp >= 0) {
|
|
cpu_balance += temp;
|
|
}
|
|
#if DEVELOPMENT || DEBUG
|
|
else {
|
|
printf("bank_bill_time: ledger_get_balance failed or negative balance in ledger: %lld\n", temp);
|
|
}
|
|
#endif /* DEVELOPMENT || DEBUG */
|
|
|
|
kr = ledger_get_balance(bank_task->bt_ledger, task_ledgers.energy_billed_to_me, &temp);
|
|
if (kr == KERN_SUCCESS && temp >= 0) {
|
|
energy_balance += temp;
|
|
}
|
|
}
|
|
|
|
queue_iterate(&bank_task->bt_accounts_to_pay, bank_account, bank_account_t, ba_next_acc_to_pay) {
|
|
temp = 0;
|
|
kr = ledger_get_balance(bank_account->ba_bill, bank_ledgers.cpu_time, &temp);
|
|
if (kr == KERN_SUCCESS && temp >= 0) {
|
|
cpu_balance += temp;
|
|
}
|
|
#if DEVELOPMENT || DEBUG
|
|
else {
|
|
printf("bank_bill_time: ledger_get_balance failed or negative balance in ledger: %lld\n", temp);
|
|
}
|
|
#endif /* DEVELOPMENT || DEBUG */
|
|
|
|
kr = ledger_get_balance(bank_account->ba_bill, bank_ledgers.energy, &temp);
|
|
if (kr == KERN_SUCCESS && temp >= 0) {
|
|
energy_balance += temp;
|
|
}
|
|
}
|
|
lck_mtx_unlock(&bank_task->bt_acc_to_pay_lock);
|
|
*cpu_time = (uint64_t)cpu_balance;
|
|
*energy = (uint64_t)energy_balance;
|
|
return;
|
|
}
|
|
|
|
/* Routine: bank_serviced_balance_safe
|
|
* Purpose: Walk through the bank accounts billed to other tasks by me and get the current balance to be charged.
|
|
* Called from another task. It takes global bank task lock to make sure the bank context is
|
|
* not deallocated while accesing it.
|
|
* Returns: cpu balance and energy balance in out paremeters.
|
|
*/
|
|
void
|
|
bank_serviced_balance_safe(task_t task, uint64_t *cpu_time, uint64_t *energy)
|
|
{
|
|
bank_task_t bank_task = BANK_TASK_NULL;
|
|
ledger_amount_t credit, debit;
|
|
uint64_t cpu_balance = 0;
|
|
uint64_t energy_balance = 0;
|
|
kern_return_t kr;
|
|
|
|
/* Task might be in exec, grab the global bank task lock before accessing bank context. */
|
|
global_bank_task_lock();
|
|
/* Grab a reference on bank context */
|
|
if (task->bank_context != NULL) {
|
|
bank_task = task->bank_context;
|
|
bank_task_reference(bank_task);
|
|
}
|
|
global_bank_task_unlock();
|
|
|
|
if (bank_task) {
|
|
bank_serviced_balance(bank_task, &cpu_balance, &energy_balance);
|
|
bank_task_dealloc(bank_task, 1);
|
|
} else {
|
|
kr = ledger_get_entries(task->ledger, task_ledgers.cpu_time_billed_to_others,
|
|
&credit, &debit);
|
|
if (kr == KERN_SUCCESS) {
|
|
cpu_balance = credit - debit;
|
|
}
|
|
|
|
kr = ledger_get_entries(task->ledger, task_ledgers.energy_billed_to_others,
|
|
&credit, &debit);
|
|
if (kr == KERN_SUCCESS) {
|
|
energy_balance = credit - debit;
|
|
}
|
|
}
|
|
|
|
*cpu_time = cpu_balance;
|
|
*energy = energy_balance;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Routine: bank_serviced_balance
|
|
* Purpose: Walk through the Account need to charge account list and get the current balance to be charged.
|
|
* Returns: cpu balance and energy balance in out paremeters.
|
|
*/
|
|
void
|
|
bank_serviced_balance(bank_task_t bank_task, uint64_t *cpu_time, uint64_t *energy)
|
|
{
|
|
int64_t cpu_balance = 0;
|
|
int64_t energy_balance = 0;
|
|
bank_account_t bank_account;
|
|
int64_t temp = 0;
|
|
kern_return_t kr;
|
|
ledger_t ledger = LEDGER_NULL;
|
|
if (bank_task == BANK_TASK_NULL) {
|
|
*cpu_time = 0;
|
|
*energy = 0;
|
|
return;
|
|
}
|
|
|
|
/* Grab a ledger reference on bt_ledger for bank_task */
|
|
ledger = bank_get_bank_task_ledger_with_ref(bank_task);
|
|
|
|
lck_mtx_lock(&bank_task->bt_acc_to_charge_lock);
|
|
|
|
if (ledger) {
|
|
kr = ledger_get_balance(ledger, task_ledgers.cpu_time_billed_to_others, &temp);
|
|
if (kr == KERN_SUCCESS && temp >= 0) {
|
|
cpu_balance += temp;
|
|
}
|
|
#if DEVELOPMENT || DEBUG
|
|
else {
|
|
printf("bank_serviced_time: ledger_get_balance failed or negative balance in ledger: %lld\n", temp);
|
|
}
|
|
#endif /* DEVELOPMENT || DEBUG */
|
|
|
|
kr = ledger_get_balance(ledger, task_ledgers.energy_billed_to_others, &temp);
|
|
if (kr == KERN_SUCCESS && temp >= 0) {
|
|
energy_balance += temp;
|
|
}
|
|
}
|
|
|
|
queue_iterate(&bank_task->bt_accounts_to_charge, bank_account, bank_account_t, ba_next_acc_to_charge) {
|
|
temp = 0;
|
|
kr = ledger_get_balance(bank_account->ba_bill, bank_ledgers.cpu_time, &temp);
|
|
if (kr == KERN_SUCCESS && temp >= 0) {
|
|
cpu_balance += temp;
|
|
}
|
|
#if DEVELOPMENT || DEBUG
|
|
else {
|
|
printf("bank_serviced_time: ledger_get_balance failed or negative balance in ledger: %lld\n", temp);
|
|
}
|
|
#endif /* DEVELOPMENT || DEBUG */
|
|
|
|
kr = ledger_get_balance(bank_account->ba_bill, bank_ledgers.energy, &temp);
|
|
if (kr == KERN_SUCCESS && temp >= 0) {
|
|
energy_balance += temp;
|
|
}
|
|
}
|
|
lck_mtx_unlock(&bank_task->bt_acc_to_charge_lock);
|
|
if (ledger) {
|
|
ledger_dereference(ledger);
|
|
}
|
|
*cpu_time = (uint64_t)cpu_balance;
|
|
*energy = (uint64_t)energy_balance;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Routine: bank_get_voucher_bank_account
|
|
* Purpose: Get the bank account from the voucher.
|
|
* Returns: bank_account if bank_account attribute present in voucher.
|
|
* NULL on no attribute or no bank_element
|
|
*/
|
|
static bank_account_t
|
|
bank_get_voucher_bank_account(ipc_voucher_t voucher)
|
|
{
|
|
bank_element_t bank_element = BANK_ELEMENT_NULL;
|
|
bank_account_t bank_account = BANK_ACCOUNT_NULL;
|
|
mach_voucher_attr_value_handle_t vals[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED];
|
|
mach_voucher_attr_value_handle_array_size_t val_count;
|
|
kern_return_t kr;
|
|
|
|
val_count = MACH_VOUCHER_ATTR_VALUE_MAX_NESTED;
|
|
kr = mach_voucher_attr_control_get_values(bank_voucher_attr_control,
|
|
voucher,
|
|
vals,
|
|
&val_count);
|
|
|
|
if (kr != KERN_SUCCESS || val_count == 0) {
|
|
return BANK_ACCOUNT_NULL;
|
|
}
|
|
|
|
bank_element = HANDLE_TO_BANK_ELEMENT(vals[0]);
|
|
if (bank_element == BANK_DEFAULT_VALUE) {
|
|
return BANK_ACCOUNT_NULL;
|
|
}
|
|
if (bank_element == BANK_DEFAULT_TASK_VALUE) {
|
|
bank_element = CAST_TO_BANK_ELEMENT(get_bank_task_context(current_task(), FALSE));
|
|
}
|
|
|
|
if (bank_element->be_type == BANK_TASK) {
|
|
return BANK_ACCOUNT_NULL;
|
|
} else if (bank_element->be_type == BANK_ACCOUNT) {
|
|
bank_account = CAST_TO_BANK_ACCOUNT(bank_element);
|
|
return bank_account;
|
|
} else {
|
|
panic("Bogus bank type: %d passed in bank_get_voucher_bank_account\n", bank_element->be_type);
|
|
}
|
|
return BANK_ACCOUNT_NULL;
|
|
}
|
|
|
|
/*
|
|
* Routine: bank_get_bank_task_ledger_with_ref
|
|
* Purpose: Get the bank ledger from the bank task and return a reference to it.
|
|
*/
|
|
static ledger_t
|
|
bank_get_bank_task_ledger_with_ref(bank_task_t bank_task)
|
|
{
|
|
ledger_t ledger = LEDGER_NULL;
|
|
|
|
lck_mtx_lock(&bank_task->bt_acc_to_pay_lock);
|
|
ledger = bank_task->bt_ledger;
|
|
if (ledger) {
|
|
ledger_reference(ledger);
|
|
}
|
|
lck_mtx_unlock(&bank_task->bt_acc_to_pay_lock);
|
|
|
|
return ledger;
|
|
}
|
|
|
|
/*
|
|
* Routine: bank_destroy_bank_task_ledger
|
|
* Purpose: Drop the bank task reference on the task ledger.
|
|
*/
|
|
static void
|
|
bank_destroy_bank_task_ledger(bank_task_t bank_task)
|
|
{
|
|
ledger_t ledger;
|
|
|
|
/* Remove the ledger reference from the bank task */
|
|
lck_mtx_lock(&bank_task->bt_acc_to_pay_lock);
|
|
assert(LEDGER_VALID(bank_task->bt_ledger));
|
|
ledger = bank_task->bt_ledger;
|
|
bank_task->bt_ledger = LEDGER_NULL;
|
|
lck_mtx_unlock(&bank_task->bt_acc_to_pay_lock);
|
|
|
|
ledger_dereference(ledger);
|
|
}
|
|
|
|
/*
|
|
* Routine: bank_get_bank_account_ledger
|
|
* Purpose: Get the bankledger from the bank account if ba_merchant different than ba_holder
|
|
*/
|
|
static ledger_t
|
|
bank_get_bank_account_ledger(bank_account_t bank_account)
|
|
{
|
|
ledger_t bankledger = LEDGER_NULL;
|
|
|
|
if (bank_account != BANK_ACCOUNT_NULL &&
|
|
bank_account->ba_holder != bank_account->ba_merchant) {
|
|
bankledger = bank_account->ba_bill;
|
|
}
|
|
|
|
return bankledger;
|
|
}
|
|
|
|
/*
|
|
* Routine: bank_get_bank_task_thread_group
|
|
* Purpose: Get the bank task's thread group from the bank task
|
|
*/
|
|
static struct thread_group *
|
|
bank_get_bank_task_thread_group(bank_task_t bank_task __unused)
|
|
{
|
|
struct thread_group *banktg = NULL;
|
|
|
|
#if CONFIG_THREAD_GROUPS
|
|
if (bank_task != BANK_TASK_NULL) {
|
|
banktg = bank_task->bt_thread_group;
|
|
}
|
|
#endif /* CONFIG_THREAD_GROUPS */
|
|
|
|
return banktg;
|
|
}
|
|
|
|
/*
|
|
* Routine: bank_get_bank_account_thread_group
|
|
* Purpose: Get the bank account's thread group from the bank account
|
|
*/
|
|
static struct thread_group *
|
|
bank_get_bank_account_thread_group(bank_account_t bank_account __unused)
|
|
{
|
|
struct thread_group *banktg = NULL;
|
|
|
|
#if CONFIG_THREAD_GROUPS
|
|
if (bank_account != BANK_ACCOUNT_NULL) {
|
|
banktg = bank_account->ba_thread_group;
|
|
}
|
|
#endif /* CONFIG_THREAD_GROUPS */
|
|
|
|
return banktg;
|
|
}
|
|
|
|
/*
|
|
* Routine: bank_get_bank_ledger_thread_group_and_persona
|
|
* Purpose: Get the bankledger (chit), thread group and persona id from the voucher.
|
|
* Returns: bankledger, thread group if bank_account attribute present in voucher
|
|
* and persona_id
|
|
*/
|
|
kern_return_t
|
|
bank_get_bank_ledger_thread_group_and_persona(
|
|
ipc_voucher_t voucher,
|
|
ledger_t *bankledger,
|
|
struct thread_group **banktg,
|
|
uint32_t *persona_id)
|
|
{
|
|
bank_account_t bank_account;
|
|
bank_task_t bank_task;
|
|
struct thread_group *thread_group = NULL;
|
|
|
|
bank_account = bank_get_voucher_bank_account(voucher);
|
|
bank_task = get_bank_task_context(current_task(), FALSE);
|
|
if (persona_id != NULL) {
|
|
if (bank_account != BANK_ACCOUNT_NULL) {
|
|
*persona_id = bank_account->ba_so_persona_id;
|
|
} else {
|
|
*persona_id = bank_task->bt_persona_id;
|
|
}
|
|
}
|
|
/*
|
|
* Use BANK_ACCOUNT_NULL if the ba_holder is same as ba_merchant
|
|
* and bank account thread group is same as current thread group
|
|
* i.e. ba_merchant's thread group.
|
|
*
|
|
* The bank account might have ba_holder same as ba_merchant but different
|
|
* thread group if daemon sends a voucher to an App and then App sends the
|
|
* same voucher back to the daemon (IPC code will replace thread group in the
|
|
* voucher to App's thread group when it gets auto redeemed by the App).
|
|
*/
|
|
if ((bank_account != NULL) &&
|
|
(bank_account->ba_holder == bank_account->ba_merchant) &&
|
|
(bank_get_bank_account_thread_group(bank_account) ==
|
|
bank_get_bank_task_thread_group(bank_account->ba_merchant))) {
|
|
bank_account = BANK_ACCOUNT_NULL;
|
|
}
|
|
|
|
if (bankledger != NULL) {
|
|
*bankledger = bank_get_bank_account_ledger(bank_account);
|
|
}
|
|
|
|
if (banktg != NULL) {
|
|
thread_group = bank_get_bank_account_thread_group(bank_account);
|
|
|
|
/* Return NULL thread group if voucher has current task's thread group */
|
|
if (thread_group == bank_get_bank_task_thread_group(bank_task)) {
|
|
thread_group = NULL;
|
|
}
|
|
*banktg = thread_group;
|
|
}
|
|
return KERN_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* Routine: bank_swap_thread_bank_ledger
|
|
* Purpose: swap the bank ledger on the thread.
|
|
* Returns: None.
|
|
* Note: Should be only called for current thread or thread which is not started.
|
|
*/
|
|
void
|
|
bank_swap_thread_bank_ledger(thread_t thread __unused, ledger_t new_ledger __unused)
|
|
{
|
|
spl_t s;
|
|
processor_t processor;
|
|
ledger_t old_ledger = thread->t_bankledger;
|
|
int64_t ctime, effective_ledger_time_consumed = 0;
|
|
int64_t remainder = 0, consumed = 0;
|
|
int64_t effective_energy_consumed = 0;
|
|
uint64_t thread_energy;
|
|
|
|
if (old_ledger == LEDGER_NULL && new_ledger == LEDGER_NULL) {
|
|
return;
|
|
}
|
|
|
|
assert((thread == current_thread() || thread->started == 0));
|
|
|
|
s = splsched();
|
|
thread_lock(thread);
|
|
|
|
/*
|
|
* Calculation of time elapsed by the thread before voucher swap.
|
|
* Following is the timeline which shows all the variables used in the calculation below.
|
|
*
|
|
* thread ledger
|
|
* cpu_time
|
|
* |<- consumed ->|<- remainder ->|
|
|
* timeline ----------------------------------------------------------------->
|
|
* | | |
|
|
* thread_dispatch ctime quantum end
|
|
*
|
|
* |<-effective_ledger_time -> |
|
|
* deduct_bank_ledger_time
|
|
*/
|
|
|
|
ctime = mach_absolute_time();
|
|
processor = thread->last_processor;
|
|
if (processor != NULL) {
|
|
if ((int64_t)processor->quantum_end > ctime) {
|
|
remainder = (int64_t)processor->quantum_end - ctime;
|
|
}
|
|
|
|
consumed = thread->quantum_remaining - remainder;
|
|
effective_ledger_time_consumed = consumed - thread->t_deduct_bank_ledger_time;
|
|
}
|
|
|
|
thread->t_deduct_bank_ledger_time = consumed;
|
|
|
|
thread_energy = ml_energy_stat(thread);
|
|
effective_energy_consumed =
|
|
thread_energy - thread->t_deduct_bank_ledger_energy;
|
|
assert(effective_energy_consumed >= 0);
|
|
thread->t_deduct_bank_ledger_energy = thread_energy;
|
|
|
|
thread->t_bankledger = new_ledger;
|
|
|
|
thread_unlock(thread);
|
|
splx(s);
|
|
|
|
if (old_ledger != LEDGER_NULL) {
|
|
ledger_credit(old_ledger,
|
|
bank_ledgers.cpu_time,
|
|
effective_ledger_time_consumed);
|
|
ledger_credit(old_ledger,
|
|
bank_ledgers.energy,
|
|
effective_energy_consumed);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Routine: bank_verify_persona_id
|
|
* Purpose: Verifies if the persona id is valid
|
|
*
|
|
* The caller should check if the task is entitled
|
|
* to do the lookup.
|
|
*/
|
|
static boolean_t
|
|
bank_verify_persona_id(uint32_t persona_id)
|
|
{
|
|
/* A successful lookup implies that the persona id is valid */
|
|
void *persona = persona_lookup(persona_id);
|
|
if (!persona) {
|
|
return FALSE;
|
|
}
|
|
persona_put(persona);
|
|
|
|
return TRUE;
|
|
}
|