// SPDX-License-Identifier: GPL-2.0-only /* * runtime-wrappers.c - Runtime Services function call wrappers * * Implementation summary: * ----------------------- * 1. When user/kernel thread requests to execute efi_runtime_service(), * enqueue work to efi_rts_wq. * 2. Caller thread waits for completion until the work is finished * because it's dependent on the return status and execution of * efi_runtime_service(). * For instance, get_variable() and get_next_variable(). * * Copyright (C) 2014 Linaro Ltd. <ard.biesheuvel@linaro.org> * * Split off from arch/x86/platform/efi/efi.c * * Copyright (C) 1999 VA Linux Systems * Copyright (C) 1999 Walt Drummond <drummond@valinux.com> * Copyright (C) 1999-2002 Hewlett-Packard Co. * Copyright (C) 2005-2008 Intel Co. * Copyright (C) 2013 SuSE Labs
*/
/* * Wrap around the new efi_call_virt_generic() macros so that the * code doesn't get too cluttered:
*/ #define efi_call_virt(f, args...) \
arch_efi_call_virt(efi.runtime, f, args)
/* * efi_queue_work: Queue EFI runtime service call and wait for completion * @_rts: EFI runtime service function identifier * @_args: Arguments to pass to the EFI runtime service * * Accesses to efi_runtime_services() are serialized by a binary * semaphore (efi_runtime_lock) and caller waits until the work is * finished, hence _only_ one work is queued at a time and the caller * thread waits for completion.
*/ #define efi_queue_work(_rts, _args...) \
__efi_queue_work(EFI_ ## _rts, \
&(union efi_rts_args){ ._rts = { _args }})
mismatch = flags ^ cur_flags; if (!WARN_ON_ONCE(mismatch & ARCH_EFI_IRQ_FLAGS_MASK)) return;
add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_NOW_UNRELIABLE);
pr_err_ratelimited(FW_BUG "IRQ flags corrupted (0x%08lx=>0x%08lx) by EFI call from %pS\n",
flags, cur_flags, caller ?: __builtin_return_address(0));
arch_efi_restore_flags(flags);
}
/* * According to section 7.1 of the UEFI spec, Runtime Services are not fully * reentrant, and there are particular combinations of calls that need to be * serialized. (source: UEFI Specification v2.4A) * * Table 31. Rules for Reentry Into Runtime Services * +------------------------------------+-------------------------------+ * | If previous call is busy in | Forbidden to call | * +------------------------------------+-------------------------------+ * | Any | SetVirtualAddressMap() | * +------------------------------------+-------------------------------+ * | ConvertPointer() | ConvertPointer() | * +------------------------------------+-------------------------------+ * | SetVariable() | ResetSystem() | * | UpdateCapsule() | | * | SetTime() | | * | SetWakeupTime() | | * | GetNextHighMonotonicCount() | | * +------------------------------------+-------------------------------+ * | GetVariable() | GetVariable() | * | GetNextVariableName() | GetNextVariableName() | * | SetVariable() | SetVariable() | * | QueryVariableInfo() | QueryVariableInfo() | * | UpdateCapsule() | UpdateCapsule() | * | QueryCapsuleCapabilities() | QueryCapsuleCapabilities() | * | GetNextHighMonotonicCount() | GetNextHighMonotonicCount() | * +------------------------------------+-------------------------------+ * | GetTime() | GetTime() | * | SetTime() | SetTime() | * | GetWakeupTime() | GetWakeupTime() | * | SetWakeupTime() | SetWakeupTime() | * +------------------------------------+-------------------------------+ * * Due to the fact that the EFI pstore may write to the variable store in * interrupt context, we need to use a lock for at least the groups that * contain SetVariable() and QueryVariableInfo(). That leaves little else, as * none of the remaining functions are actually ever called at runtime. * So let's just use a single lock to serialize all Runtime Services calls.
*/ static DEFINE_SEMAPHORE(efi_runtime_lock, 1);
/* * Expose the EFI runtime lock to the UV platform
*/ #ifdef CONFIG_X86_UV externstruct semaphore __efi_uv_runtime_lock __alias(efi_runtime_lock); #endif
/* * Calls the appropriate efi_runtime_service() with the appropriate * arguments.
*/ staticvoid __nocfi efi_call_rts(struct work_struct *work)
{ constunion efi_rts_args *args = efi_rts_work.args;
efi_status_t status = EFI_NOT_FOUND; unsignedlong flags;
switch (efi_rts_work.efi_rts_id) { case EFI_GET_TIME:
status = efi_call_virt(get_time,
args->GET_TIME.time,
args->GET_TIME.capabilities); break; case EFI_SET_TIME:
status = efi_call_virt(set_time,
args->SET_TIME.time); break; case EFI_GET_WAKEUP_TIME:
status = efi_call_virt(get_wakeup_time,
args->GET_WAKEUP_TIME.enabled,
args->GET_WAKEUP_TIME.pending,
args->GET_WAKEUP_TIME.time); break; case EFI_SET_WAKEUP_TIME:
status = efi_call_virt(set_wakeup_time,
args->SET_WAKEUP_TIME.enable,
args->SET_WAKEUP_TIME.time); break; case EFI_GET_VARIABLE:
status = efi_call_virt(get_variable,
args->GET_VARIABLE.name,
args->GET_VARIABLE.vendor,
args->GET_VARIABLE.attr,
args->GET_VARIABLE.data_size,
args->GET_VARIABLE.data); break; case EFI_GET_NEXT_VARIABLE:
status = efi_call_virt(get_next_variable,
args->GET_NEXT_VARIABLE.name_size,
args->GET_NEXT_VARIABLE.name,
args->GET_NEXT_VARIABLE.vendor); break; case EFI_SET_VARIABLE:
status = efi_call_virt(set_variable,
args->SET_VARIABLE.name,
args->SET_VARIABLE.vendor,
args->SET_VARIABLE.attr,
args->SET_VARIABLE.data_size,
args->SET_VARIABLE.data); break; case EFI_QUERY_VARIABLE_INFO:
status = efi_call_virt(query_variable_info,
args->QUERY_VARIABLE_INFO.attr,
args->QUERY_VARIABLE_INFO.storage_space,
args->QUERY_VARIABLE_INFO.remaining_space,
args->QUERY_VARIABLE_INFO.max_variable_size); break; case EFI_GET_NEXT_HIGH_MONO_COUNT:
status = efi_call_virt(get_next_high_mono_count,
args->GET_NEXT_HIGH_MONO_COUNT.high_count); break; case EFI_UPDATE_CAPSULE:
status = efi_call_virt(update_capsule,
args->UPDATE_CAPSULE.capsules,
args->UPDATE_CAPSULE.count,
args->UPDATE_CAPSULE.sg_list); break; case EFI_QUERY_CAPSULE_CAPS:
status = efi_call_virt(query_capsule_caps,
args->QUERY_CAPSULE_CAPS.capsules,
args->QUERY_CAPSULE_CAPS.count,
args->QUERY_CAPSULE_CAPS.max_size,
args->QUERY_CAPSULE_CAPS.reset_type); break; case EFI_ACPI_PRM_HANDLER: #ifdef CONFIG_ACPI_PRMT
status = arch_efi_call_virt(args, ACPI_PRM_HANDLER.acpi_prm_handler,
args->ACPI_PRM_HANDLER.param_buffer_addr,
args->ACPI_PRM_HANDLER.context); break; #endif default: /* * Ideally, we should never reach here because a caller of this * function should have put the right efi_runtime_service() * function identifier into efi_rts_work->efi_rts_id
*/
pr_err("Requested executing invalid EFI Runtime Service.\n");
}
/* * queue_work() returns 0 if work was already on queue, * _ideally_ this should never happen.
*/ if (queue_work(efi_rts_wq, &efi_rts_work.work))
wait_for_completion(&efi_rts_work.efi_rts_comp); else
pr_err("Failed to queue work to efi_rts_wq.\n");
if (down_interruptible(&efi_runtime_lock)) return EFI_ABORTED;
status = efi_queue_work(GET_NEXT_HIGH_MONO_COUNT, count);
up(&efi_runtime_lock); return status;
}
staticvoid __nocfi
virt_efi_reset_system(int reset_type, efi_status_t status, unsignedlong data_size, efi_char16_t *data)
{ if (down_trylock(&efi_runtime_lock)) {
pr_warn("failed to invoke the reset_system() runtime service:\n" "could not get exclusive access to the firmware\n"); return;
}
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.