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