mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 03:35:32 -04:00
Merge tag 'efi-next-for-v7.3' of git://git.kernel.org/pub/scm/linux/kernel/git/efi/efi
Pull EFI updates from Ard Biesheuvel: - Set a timeout for EFI runtime service completions, and declare the firmware wedged if it is exceeded. Note that this requires special handling in case the firmware does return after all - Rate limit the efivarfs statfs() handler as the QueryVariableInfo() runtime service can be costly - Sanity check the size of struct properties_header on Mac/x86 - Tweak the prototype of efi_guid_to_str() * tag 'efi-next-for-v7.3' of git://git.kernel.org/pub/scm/linux/kernel/git/efi/efi: efivarfs: Rate limit statfs() handler efi: apple-properties: validate setup data header length efi: make efi_guid_to_str() take a const GUID pointer efi/runtime-wrappers: retire the worker if a wedged call ever returns efi/runtime-wrappers: honour EFI_RUNTIME_SERVICES in the non-blocking paths efi/runtime-wrappers: bound the wait for EFI runtime service calls efi/runtime-wrappers: check EFI_RUNTIME_SERVICES before using efi_rts_work efi/runtime-wrappers: handle queue_work() failure with goto exit efi/runtime-wrappers: factor out efi_rts_park_worker() efi: fix stale reference to efi_recover_from_page_fault()
This commit is contained in:
@@ -832,12 +832,5 @@ void efi_crash_gracefully_on_page_fault(unsigned long phys_addr,
|
||||
clear_bit(EFI_RUNTIME_SERVICES, &efi.flags);
|
||||
pr_info("Froze efi_rts_wq and disabled EFI Runtime Services\n");
|
||||
|
||||
/*
|
||||
* Call schedule() in an infinite loop, so that any spurious wake ups
|
||||
* will never run efi_rts_wq again.
|
||||
*/
|
||||
for (;;) {
|
||||
set_current_state(TASK_IDLE);
|
||||
schedule();
|
||||
}
|
||||
efi_rts_park_worker();
|
||||
}
|
||||
|
||||
@@ -207,7 +207,10 @@ static int __init map_properties(void)
|
||||
}
|
||||
|
||||
properties = (struct properties_header *)data->data;
|
||||
if (properties->version != 1) {
|
||||
if (data_len < sizeof(*properties)) {
|
||||
pr_err("truncated properties header\n");
|
||||
ret = -EINVAL;
|
||||
} else if (properties->version != 1) {
|
||||
pr_err("unsupported version:\n");
|
||||
print_hex_dump(KERN_ERR, pr_fmt(), DUMP_PREFIX_OFFSET,
|
||||
16, 1, properties, data_len, true);
|
||||
|
||||
@@ -118,6 +118,14 @@ union efi_rts_args {
|
||||
|
||||
struct efi_runtime_work efi_rts_work;
|
||||
|
||||
/*
|
||||
* Upper bound on how long we wait for a single EFI runtime service
|
||||
* call to finish before declaring firmware wedged. Chosen to be longer
|
||||
* than any plausible legitimate call (including UpdateCapsule on slow
|
||||
* SPI-NOR) while still bounding userspace wait time.
|
||||
*/
|
||||
#define EFI_RTS_TIMEOUT (120 * HZ)
|
||||
|
||||
/*
|
||||
* efi_queue_work: Queue EFI runtime service call and wait for completion
|
||||
* @_rts: EFI runtime service function identifier
|
||||
@@ -202,7 +210,7 @@ void efi_call_virt_check_flags(unsigned long flags, const void *caller)
|
||||
*/
|
||||
static DEFINE_SEMAPHORE(efi_runtime_lock, 1);
|
||||
|
||||
static struct task_struct *efi_runtime_lock_owner;
|
||||
static struct task_struct *efi_runtime_lock_owner __used;
|
||||
|
||||
/*
|
||||
* Expose the EFI runtime lock to the UV platform
|
||||
@@ -211,6 +219,19 @@ static struct task_struct *efi_runtime_lock_owner;
|
||||
extern struct semaphore __efi_uv_runtime_lock __alias(efi_runtime_lock);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Park a worker that must never run efi_rts_wq again: EFI runtime services
|
||||
* have been disabled and its efi_rts_work is abandoned. Loop in schedule()
|
||||
* so a spurious wakeup cannot resume it.
|
||||
*/
|
||||
void __noreturn efi_rts_park_worker(void)
|
||||
{
|
||||
for (;;) {
|
||||
set_current_state(TASK_IDLE);
|
||||
schedule();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Calls the appropriate efi_runtime_service() with the appropriate
|
||||
* arguments.
|
||||
@@ -221,6 +242,9 @@ static void __nocfi efi_call_rts(struct work_struct *work)
|
||||
efi_status_t status = EFI_NOT_FOUND;
|
||||
unsigned long flags;
|
||||
|
||||
if (!efi_enabled(EFI_RUNTIME_SERVICES))
|
||||
efi_rts_park_worker();
|
||||
|
||||
efi_runtime_lock_owner = current;
|
||||
|
||||
arch_efi_call_virt_setup();
|
||||
@@ -312,6 +336,9 @@ static void __nocfi efi_call_rts(struct work_struct *work)
|
||||
efi_call_virt_check_flags(flags, efi_rts_work.caller);
|
||||
arch_efi_call_virt_teardown();
|
||||
|
||||
if (!efi_enabled(EFI_RUNTIME_SERVICES))
|
||||
efi_rts_park_worker();
|
||||
|
||||
efi_rts_work.status = status;
|
||||
complete(&efi_rts_work.efi_rts_comp);
|
||||
efi_runtime_lock_owner = NULL;
|
||||
@@ -320,17 +347,16 @@ static void __nocfi efi_call_rts(struct work_struct *work)
|
||||
static efi_status_t __efi_queue_work(enum efi_rts_ids id,
|
||||
union efi_rts_args *args)
|
||||
{
|
||||
if (!efi_enabled(EFI_RUNTIME_SERVICES)) {
|
||||
pr_warn_once("EFI Runtime Services are disabled!\n");
|
||||
return EFI_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
efi_rts_work.efi_rts_id = id;
|
||||
efi_rts_work.args = args;
|
||||
efi_rts_work.caller = __builtin_return_address(0);
|
||||
efi_rts_work.status = EFI_ABORTED;
|
||||
|
||||
if (!efi_enabled(EFI_RUNTIME_SERVICES)) {
|
||||
pr_warn_once("EFI Runtime Services are disabled!\n");
|
||||
efi_rts_work.status = EFI_DEVICE_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
init_completion(&efi_rts_work.efi_rts_comp);
|
||||
INIT_WORK(&efi_rts_work.work, efi_call_rts);
|
||||
|
||||
@@ -338,10 +364,18 @@ static efi_status_t __efi_queue_work(enum efi_rts_ids id,
|
||||
* 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
|
||||
if (!queue_work(efi_rts_wq, &efi_rts_work.work)) {
|
||||
pr_err("Failed to queue work to efi_rts_wq.\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (!wait_for_completion_timeout(&efi_rts_work.efi_rts_comp,
|
||||
EFI_RTS_TIMEOUT)) {
|
||||
pr_err("EFI runtime service %d wedged in firmware; disabling EFI runtime services\n",
|
||||
id);
|
||||
clear_bit(EFI_RUNTIME_SERVICES, &efi.flags);
|
||||
return EFI_ABORTED;
|
||||
}
|
||||
|
||||
WARN_ON_ONCE(efi_rts_work.status == EFI_ABORTED);
|
||||
exit:
|
||||
@@ -449,6 +483,11 @@ virt_efi_set_variable_nb(efi_char16_t *name, efi_guid_t *vendor, u32 attr,
|
||||
if (down_trylock(&efi_runtime_lock))
|
||||
return EFI_NOT_READY;
|
||||
|
||||
if (!efi_enabled(EFI_RUNTIME_SERVICES)) {
|
||||
up(&efi_runtime_lock);
|
||||
return EFI_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
efi_runtime_lock_owner = current;
|
||||
status = efi_call_virt_pointer(efi.runtime, set_variable, name, vendor,
|
||||
attr, data_size, data);
|
||||
@@ -488,6 +527,11 @@ virt_efi_query_variable_info_nb(u32 attr, u64 *storage_space,
|
||||
if (down_trylock(&efi_runtime_lock))
|
||||
return EFI_NOT_READY;
|
||||
|
||||
if (!efi_enabled(EFI_RUNTIME_SERVICES)) {
|
||||
up(&efi_runtime_lock);
|
||||
return EFI_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
efi_runtime_lock_owner = current;
|
||||
status = efi_call_virt_pointer(efi.runtime, query_variable_info, attr,
|
||||
storage_space, remaining_space,
|
||||
@@ -518,6 +562,12 @@ virt_efi_reset_system(int reset_type, efi_status_t status,
|
||||
return;
|
||||
}
|
||||
|
||||
if (!efi_enabled(EFI_RUNTIME_SERVICES)) {
|
||||
pr_warn("EFI Runtime Services are disabled, not invoking reset_system()\n");
|
||||
up(&efi_runtime_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
efi_runtime_lock_owner = current;
|
||||
arch_efi_call_virt_setup();
|
||||
efi_rts_work.efi_rts_id = EFI_RESET_SYSTEM;
|
||||
|
||||
@@ -89,12 +89,30 @@ static int efivarfs_statfs(struct dentry *dentry, struct kstatfs *buf)
|
||||
/* Some UEFI firmware does not implement QueryVariableInfo() */
|
||||
storage_space = remaining_space = 0;
|
||||
if (efi_rt_services_supported(EFI_RT_SUPPORTED_QUERY_VARIABLE_INFO)) {
|
||||
status = efivar_query_variable_info(attr, &storage_space,
|
||||
&remaining_space,
|
||||
&max_variable_size);
|
||||
if (status != EFI_SUCCESS && status != EFI_UNSUPPORTED)
|
||||
pr_warn_ratelimited("query_variable_info() failed: 0x%lx\n",
|
||||
status);
|
||||
static DEFINE_RATELIMIT_STATE(_rs, 2 * HZ, 5);
|
||||
static u64 storage, remaining;
|
||||
static DEFINE_SPINLOCK(lock);
|
||||
|
||||
if (!__ratelimit(&_rs)) {
|
||||
ratelimit_set_flags(&_rs, RATELIMIT_MSG_ON_RELEASE);
|
||||
|
||||
spin_lock(&lock);
|
||||
storage_space = storage;
|
||||
remaining_space = remaining;
|
||||
spin_unlock(&lock);
|
||||
} else {
|
||||
status = efivar_query_variable_info(attr, &storage_space,
|
||||
&remaining_space,
|
||||
&max_variable_size);
|
||||
if (status != EFI_SUCCESS && status != EFI_UNSUPPORTED)
|
||||
pr_warn("query_variable_info() failed: 0x%lx\n",
|
||||
status);
|
||||
|
||||
spin_lock(&lock);
|
||||
storage = storage_space;
|
||||
remaining = remaining_space;
|
||||
spin_unlock(&lock);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -705,7 +705,7 @@ efi_guidcmp (efi_guid_t left, efi_guid_t right)
|
||||
}
|
||||
|
||||
static inline char *
|
||||
efi_guid_to_str(efi_guid_t *guid, char *out)
|
||||
efi_guid_to_str(const efi_guid_t *guid, char *out)
|
||||
{
|
||||
sprintf(out, "%pUl", guid->b);
|
||||
return out;
|
||||
@@ -1212,8 +1212,8 @@ efi_call_acpi_prm_handler(efi_status_t (__efiapi *handler_addr)(u64, void *),
|
||||
|
||||
/*
|
||||
* efi_runtime_service() function identifiers.
|
||||
* "NONE" is used by efi_recover_from_page_fault() to check if the page
|
||||
* fault happened while executing an efi runtime service.
|
||||
* "NONE" is used by efi_crash_gracefully_on_page_fault() to check if the
|
||||
* page fault happened while executing an efi runtime service.
|
||||
*/
|
||||
enum efi_rts_ids {
|
||||
EFI_NONE,
|
||||
@@ -1256,6 +1256,8 @@ extern struct efi_runtime_work efi_rts_work;
|
||||
/* Workqueue to queue EFI Runtime Services */
|
||||
extern struct workqueue_struct *efi_rts_wq;
|
||||
|
||||
void __noreturn efi_rts_park_worker(void);
|
||||
|
||||
struct linux_efi_memreserve {
|
||||
int size; // allocated size of the array
|
||||
atomic_t count; // number of entries used
|
||||
|
||||
@@ -26,6 +26,7 @@ NORETURN(cpu_startup_entry)
|
||||
NORETURN(do_exit)
|
||||
NORETURN(do_group_exit)
|
||||
NORETURN(do_task_dead)
|
||||
NORETURN(efi_rts_park_worker)
|
||||
NORETURN(ex_handler_msr_mce)
|
||||
NORETURN(hlt_play_dead)
|
||||
NORETURN(hv_ghcb_terminate)
|
||||
|
||||
Reference in New Issue
Block a user