/* Per-VM stdcall state */ struct sm_std_call_vm_state { struct list_node queue_node; struct list_node active_node; struct obj_ref self_ref; struct smc32_args args; long ret; bool done; int active_cpu; /* cpu that expects stdcall result */ int initial_cpu; /* Debug info: cpu that started stdcall */ int last_cpu; /* Debug info: most recent cpu expecting stdcall result */ int restart_count;
};
long smc_sm_api_version(struct smc32_args* args) {
uint32_t api_version = args->params[0];
spin_lock(&sm_api_version_lock);
LTRACEF("request api version %d\n", api_version); if (api_version > sm_api_version_max) {
api_version = sm_api_version_max;
}
if (api_version < sm_api_version_min) {
TRACEF("ERROR: Tried to select incompatible api version %d < %d, current version %d\n",
api_version, sm_api_version_min, sm_api_version);
api_version = sm_api_version;
} else { /* Update and lock the version to prevent downgrade */
sm_api_version = api_version;
sm_api_version_min = api_version;
}
spin_unlock(&sm_api_version_lock);
LTRACEF("return api version %d\n", api_version); return api_version;
}
long smc_get_smp_max_cpus(struct smc32_args* args) { return SMP_MAX_CPUS;
}
/* must be called with irqs disabled */ staticlong sm_queue_stdcall(struct smc32_args* args) { long ret;
uint cpu = arch_curr_cpu_num(); struct sm_vm* vm = NULL; struct obj_ref vm_ref = OBJ_REF_INITIAL_VALUE(vm_ref);
spin_lock(&sm_vm_lock);
ret = sm_vm_get_locked(args->client_id, &vm_ref, &vm); if (ret) {
dprintf(CRITICAL, "%s: could not get VM %" PRIx64 " (%ld)\n",
__func__, args->client_id, ret); goto err;
}
if (list_in_list(&vm->stdcall.queue_node) || vm->stdcall.done) { if (args->smc_nr == SMC_SC_RESTART_LAST) {
DEBUG_ASSERT(vm->stdcall.args.client_id == args->client_id); if (vm->stdcall.active_cpu == -1) {
vm->stdcall.restart_count++;
LTRACEF_LEVEL(3, "cpu %d, restart std call, restart_count %d\n",
cpu, vm->stdcall.restart_count); goto restart_stdcall;
}
}
dprintf(CRITICAL, "%s: cpu %d, std call busy\n", __func__, cpu);
ret = SM_ERR_BUSY; goto err;
} else { if (args->smc_nr == SMC_SC_RESTART_LAST) {
dprintf(CRITICAL, "%s: cpu %d, unexpected restart, no std call active\n",
__func__, arch_curr_cpu_num());
ret = SM_ERR_UNEXPECTED_RESTART; goto err;
}
}
switch (regs->r3) { case TRUSTY_FFA_MSG_RUN_FASTCALL: if (SMC_IS_SMC64(regs->r4)) { return SM_ERR_NOT_SUPPORTED;
} if (!SMC_IS_FASTCALL(regs->r4)) {
dprintf(CRITICAL, "Synchronous message is not a fastcall: %lx\n",
regs->r4); return SM_ERR_INVALID_PARAMETERS;
}
case TRUSTY_FFA_MSG_QUEUE_STDCALL: if (SMC_IS_SMC64(regs->r4)) { return SM_ERR_NOT_SUPPORTED;
} if (SMC_IS_FASTCALL(regs->r4)) {
dprintf(CRITICAL, "Asynchronous message is a fastcall: %lx\n",
regs->r4); return SM_ERR_INVALID_PARAMETERS;
}
ret = sm_queue_stdcall(&args); if (!ret) { /* Ring the doorbell on the host so it queues a Trusty NOP */
sm_intc_raise_doorbell_irq();
} return ret;
case TRUSTY_FFA_MSG_GET_STDCALL_RET:
spin_lock(&sm_vm_lock);
ret = sm_vm_get_locked(client_id, &vm_ref, &vm); if (ret) {
dprintf(CRITICAL, "%s: could not get VM %" PRIx16 " (%ld)\n",
__func__, client_id, ret);
} else {
ret = sm_get_stdcall_ret_locked(vm);
sm_vm_del_ref_locked(vm, &vm_ref);
}
spin_unlock(&sm_vm_lock); return ret;
#if !ARM_MERGE_FIQ_IRQ #error"FF-A libsm requires ARM_MERGE_FIQ_IRQ" #endif
ret = sm_nopcall_table[SMC_ENTITY(args.params[0])](&args); if (!ret) { /* Ring the doorbell on the host so it queues a Trusty NOP */
sm_intc_raise_doorbell_irq();
} return ret;
case TRUSTY_FFA_MSG_IS_IDLE: return get_current_thread() == nsidlethreads[cpu];
if (!bst_is_empty(&sm_vm_tree)) { /* *ThereisalreadyaVMinthetree,sowedon'tneed *toaddthecompatibilityVMexplicitly.
*/ return;
} if (sm_vm_to_create != -1) { /* The tree is empty but we have a pending VM queued up for creation */ return;
}
spin_lock_irqsave(&sm_vm_lock, state); /* *Checkthenodewiththelockheldtoavoid *itgettingremovedduringthecheck
*/ if (!list_in_list(¬if->node)) {
ret = ERR_NOT_FOUND; goto err_notif_not_in_list;
} if ((uintptr_t)notif == atomic_load(&sm_vm_active_notifier)) {
spin_unlock_irqrestore(&sm_vm_lock, state);
/* The callback is currently running, wait for it to finish */ do { /* *Ifsm_vm_active_notifierisnotif,thatmeansthatour *notifieriscurrentlyrunning;retrytheevent_wait *untilthenotifieractuallycompletesinordertoavoid *leftoverwakeups.Weuseaglobalvariablebecausethe *notifiermighthavebeendestroyedbythehandlerby *thetimeitreturns.
*/
event_wait(&sm_vm_notifier_done_event);
} while ((uintptr_t)notif == atomic_load(&sm_vm_active_notifier));
/* Nothing else to do here, the notifier is already out of the list */ return NO_ERROR;
}
vm = bst_search_key_type(&sm_vm_tree, ¬if->client_id, sm_vm_compare_key, struct sm_vm, node); if (!vm) {
ret = ERR_NOT_FOUND; goto err_no_vm;
}
case SM_VM_STATE_DESTROY_NOTIFYING:
ret = FFA_ERROR_RETRY; break;
case SM_VM_STATE_DESTROY_NOTIFIED: /* Mark the VM for freeing since we're done with it */
vm->state = SM_VM_STATE_READY_TO_FREE;
bst_delete(&sm_vm_tree, &vm->node);
inserted =
bst_insert(&sm_vm_free_tree, &vm->node, sm_vm_compare);
DEBUG_ASSERT(inserted);
ret = 0; /* *SignaltheeventsotheVMisfreedlater;wedonot *needtoringthedoorbellbecausethisisnoturgent, *sothefreeingcanhappenwheneverTrustygetscyclesnext.
*/
event_signal(&sm_vm_event, false); break;
default:
panic("Invalid VM state: %d\n", vm->state);
}
}
spin_unlock(&sm_vm_lock);
/* Create the new VM if a message came in */ while (true) {
int32_t vm_id; struct sm_vm* vm; bool inserted;
spin_lock_irqsave(&sm_vm_lock, state); if (sm_vm_to_create != -1 &&
sm_vm_compat_vm.state == SM_VM_STATE_AVAILABLE) { /* We got an actual VM, tear down the compatibility one */
sm_vm_compat_vm.state = SM_VM_STATE_DESTROY_NOTIFYING; /* *Signaltheeventsowecontinuetheouterloopbecause *theremainderofthecurrentiterationwillhandlethe *newNOTIFYINGstateforthecompatibilityVM.Theevent *willbeusedatthestartofthenextiterationtoget *backhereandcreatethenewVM.
*/
event_signal(&sm_vm_event, false); /* Defer creation of the new VM until compat_vm is gone */
vm_id = -1;
} else {
vm_id = sm_vm_to_create;
}
spin_unlock_irqrestore(&sm_vm_lock, state);
LTRACEF_LEVEL(2, "Creating fresh VM %d\n", vm_id); if (vm_id == -1) { break;
}
vm = calloc(1, sizeof(struct sm_vm)); if (!vm) {
dprintf(CRITICAL, "Out of memory for VMs\n"); continue;
}
/* Destroy all VMs on the free list */ while (true) {
spin_lock_irqsave(&sm_vm_lock, state);
vm = bst_next_type(&sm_vm_free_tree, NULL, struct sm_vm, node); if (vm) {
bst_delete(&sm_vm_free_tree, &vm->node);
}
spin_unlock_irqrestore(&sm_vm_lock, state);
if (atomic_load(&platform_halted)) {
regs = arm_ffa_call_error(FFA_ERROR_ABORTED);
} elseif (ffa_init_state == ARM_FFA_INIT_UNINIT) {
panic("FF-A not initialized before main loop\n");
} elseif (ffa_init_state == ARM_FFA_INIT_FAILED) {
TRACEF("FF-A failed to initialize, " "falling back to legacy SPD SMCs\n");
sm_use_ffa = false; return;
} else { /* *Linuxwillchecktheshadowprioritynextand *giveusmorecyclesifit'sanythingotherthanIDLE
*/
LTRACEF_LEVEL(5, "Calling FFA_MSG_WAIT (%ld)\n", ret); /* *Updatethememlognotificationstatewithoutraisingthedoorbell *becausetrusty_log_call_notifywillchecktheloginthehostanyway
*/
sm_memlog_notify_clients(false);
regs = arm_ffa_call_msg_wait();
}
while (true) {
LTRACEF_LEVEL(5, "Incoming FF-A SMC (%lx)\n", regs.r0); switch ((uint32_t)regs.r0) { case SMC_FC_FFA_MSG_SEND_DIRECT_REQ: case SMC_FC64_FFA_MSG_SEND_DIRECT_REQ: if (atomic_load(&platform_halted)) { /* Return to NS since we have nothing to do */
regs = arm_ffa_call_error(FFA_ERROR_ABORTED); break;
}
atomic_store(&sm_ffa_valid_call, true);
if (regs.r2 & FFA_FRAMEWORK_MSG_FLAG) {
ret = sm_ffa_handle_framework_msg(®s);
} else {
ret = sm_ffa_handle_direct_req(ret, ®s);
}
case SMC_FC_FFA_RUN: if (atomic_load(&platform_halted)) { /* Return to NS since we have nothing to do */
regs = arm_ffa_call_error(FFA_ERROR_ABORTED); break;
}
atomic_store(&sm_ffa_valid_call, true);
case SMC_FC_FFA_ERROR: if (atomic_load(&platform_halted)) { /* *Loopforeverifwehaltedand *gotbackherefromFFA_ERROR_ABORTED, *thereisnotmuchelsewecando
*/ break;
} if ((int32_t)regs.r2 == FFA_ERROR_NOT_SUPPORTED &&
!atomic_load(&sm_ffa_valid_call)) {
TRACEF("Using legacy SPD SMCs\n");
sm_use_ffa = false; return;
}
panic("Received FFA_ERROR from SPMC: (%lx, %lx)\n", regs.r1,
regs.r2);
case SMC_UNKNOWN: if (atomic_load(&sm_ffa_valid_call)) { /* We already got a valid FF-A call earlier */
panic("Received SMC_UNKNOWN from SPMC\n");
}
TRACEF("Using legacy SPD SMCs\n");
sm_use_ffa = false; return;
staticvoid sm_sched_nonsecure_fiq_loop(long ret, struct smc32_args* args) { #if LIB_SM_WITH_FFA_LOOP if (sm_use_ffa) {
sm_ffa_loop(ret, args); /* Check again in case we switched to the legacy SPD SMCs */ if (sm_use_ffa) { return;
}
} #endif
while (true) { if (atomic_load(&platform_halted)) {
ret = SM_ERR_PANIC;
}
sm_sched_nonsecure(ret, args); if (atomic_load(&platform_halted) && args->smc_nr != SMC_FC_FIQ_ENTER) { continue;
} if (SMC_IS_SMC64(args->smc_nr)) {
ret = SM_ERR_NOT_SUPPORTED; continue;
} if (!SMC_IS_FASTCALL(args->smc_nr)) { break;
}
ret = sm_fastcall_table[SMC_ENTITY(args->smc_nr)](args);
}
}
/* must be called with irqs disabled */ staticenum handler_return sm_return_and_wait_for_next_stdcall(long ret, int cpu) { struct smc32_args args = SMC32_ARGS_INITIAL_VALUE(args);
staticvoid sm_irq_return_ns(void) { long ret; int cpu; struct sm_vm* vm;
cpu = arch_curr_cpu_num();
spin_lock(&sm_vm_lock); /* TODO: remove? */
vm = sm_vm_find_by_stdcall_active_cpu_locked(cpu);
LTRACEF_LEVEL(2, "got irq on cpu %d, stdcallcpu %d\n", cpu,
vm ? vm->stdcall.active_cpu : -1); if (vm) {
vm->stdcall.last_cpu = vm->stdcall.active_cpu;
vm->stdcall.active_cpu = -1; if (list_in_list(&vm->stdcall.active_node)) {
list_delete(&vm->stdcall.active_node);
}
ret = SM_ERR_INTERRUPTED;
} else {
ret = SM_ERR_NOP_INTERRUPTED;
}
LTRACEF_LEVEL(2, "got irq on cpu %d, return %ld\n", cpu, ret);
spin_unlock(&sm_vm_lock);
sm_return_and_wait_for_next_stdcall(ret, cpu);
}
staticint __NO_RETURN sm_irq_loop(void* arg) { int cpu; /* cpu that requested this thread, the current cpu could be different */ int eventcpu = (uintptr_t)arg;
cpu = arch_curr_cpu_num();
LTRACEF("wait for irqs for cpu %d, on cpu %d\n", eventcpu, cpu); while (true) {
event_wait(&nsirqevent[eventcpu]);
sm_irq_return_ns();
}
}
/* must be called with irqs disabled and sm_vm_lock held */ staticlong sm_get_stdcall_ret_locked(struct sm_vm* vm) { long ret;
uint cpu = arch_curr_cpu_num();
DEBUG_ASSERT(spin_lock_held(&sm_vm_lock));
if (!sm_use_ffa && vm->stdcall.active_cpu != (int)cpu) {
dprintf(CRITICAL, "%s: stdcallcpu, a%d != curr-cpu %d, l%d, i%d\n",
__func__, vm->stdcall.active_cpu, cpu, vm->stdcall.last_cpu,
vm->stdcall.initial_cpu);
ret = SM_ERR_INTERNAL_FAILURE; goto err;
} if (!list_in_list(&vm->stdcall.queue_node) && !vm->stdcall.done) {
dprintf(CRITICAL, "%s: stdcall bad state: neither queued nor done\n",
__func__);
ret = SM_ERR_NOT_ALLOWED; goto err;
}
vm->stdcall.last_cpu = (int)cpu;
vm->stdcall.active_cpu = -1; if (list_in_list(&vm->stdcall.active_node)) {
list_delete(&vm->stdcall.active_node);
}
if (vm->stdcall.done) {
vm->stdcall.done = false;
ret = vm->stdcall.ret;
LTRACEF("cpu %d, return stdcall result, %ld, initial cpu %d\n", cpu,
vm->stdcall.ret, vm->stdcall.initial_cpu);
} else { if (sm_check_and_lock_api_version(TRUSTY_API_VERSION_SMP))
ret = SM_ERR_CPU_IDLE; /* ns using smp api */ elseif (vm->stdcall.restart_count)
ret = SM_ERR_BUSY; else
ret = SM_ERR_INTERRUPTED;
LTRACEF("cpu %d, initial cpu %d, restart_count %d, std call not finished, return %ld\n",
cpu, vm->stdcall.initial_cpu, vm->stdcall.restart_count, ret);
}
err: return ret;
}
staticvoid sm_release_boot_args(uint level) { if (boot_args) {
sm_put_boot_args();
} else { /* we need to resume the ns-switcher here if *thebootloaderdidn'tpassbootargs
*/
resume_nsthreads();
}
if (boot_args)
TRACEF("WARNING: outstanding reference to boot args" "at the end of initialzation!\n");
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.