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:
Linus Torvalds
2026-08-23 09:24:34 -07:00
6 changed files with 95 additions and 28 deletions

View File

@@ -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();
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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);
}
}
/*

View File

@@ -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

View File

@@ -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)