diff options
Diffstat (limited to 'drivers/firmware')
-rw-r--r-- | drivers/firmware/efi/libstub/Makefile | 2 | ||||
-rw-r--r-- | drivers/firmware/efi/libstub/x86-stub.c | 2 | ||||
-rw-r--r-- | drivers/firmware/efi/riscv-runtime.c | 15 | ||||
-rw-r--r-- | drivers/firmware/efi/runtime-wrappers.c | 358 |
4 files changed, 249 insertions, 128 deletions
diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index ae8874401a9f..92389a5481ff 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -147,7 +147,7 @@ STUBCOPY_RELOC-$(CONFIG_ARM64) := R_AARCH64_ABS # For RISC-V, we don't need anything special other than arm64. Keep all the # symbols in .init section and make sure that no absolute symbols references -# doesn't exist. +# exist. STUBCOPY_FLAGS-$(CONFIG_RISCV) += --prefix-alloc-sections=.init \ --prefix-symbols=__efistub_ STUBCOPY_RELOC-$(CONFIG_RISCV) := R_RISCV_HI20 diff --git a/drivers/firmware/efi/libstub/x86-stub.c b/drivers/firmware/efi/libstub/x86-stub.c index e976288728e9..2fee52ed335d 100644 --- a/drivers/firmware/efi/libstub/x86-stub.c +++ b/drivers/firmware/efi/libstub/x86-stub.c @@ -72,7 +72,7 @@ preserve_pci_rom_image(efi_pci_io_protocol_t *pci, struct pci_setup_rom **__rom) rom->data.type = SETUP_PCI; rom->data.len = size - sizeof(struct setup_data); rom->data.next = 0; - rom->pcilen = pci->romsize; + rom->pcilen = romsize; *__rom = rom; status = efi_call_proto(pci, pci.read, EfiPciIoWidthUint16, diff --git a/drivers/firmware/efi/riscv-runtime.c b/drivers/firmware/efi/riscv-runtime.c index d0daacd2c903..09525fb5c240 100644 --- a/drivers/firmware/efi/riscv-runtime.c +++ b/drivers/firmware/efi/riscv-runtime.c @@ -130,14 +130,25 @@ static int __init riscv_enable_runtime_services(void) } early_initcall(riscv_enable_runtime_services); -void efi_virtmap_load(void) +static void efi_virtmap_load(void) { preempt_disable(); switch_mm(current->active_mm, &efi_mm, NULL); } -void efi_virtmap_unload(void) +static void efi_virtmap_unload(void) { switch_mm(&efi_mm, current->active_mm, NULL); preempt_enable(); } + +void arch_efi_call_virt_setup(void) +{ + sync_kernel_mappings(efi_mm.pgd); + efi_virtmap_load(); +} + +void arch_efi_call_virt_teardown(void) +{ + efi_virtmap_unload(); +} diff --git a/drivers/firmware/efi/runtime-wrappers.c b/drivers/firmware/efi/runtime-wrappers.c index a400c4312c82..5d56bc40a79d 100644 --- a/drivers/firmware/efi/runtime-wrappers.c +++ b/drivers/firmware/efi/runtime-wrappers.c @@ -40,55 +40,97 @@ * code doesn't get too cluttered: */ #define efi_call_virt(f, args...) \ - efi_call_virt_pointer(efi.runtime, f, args) -#define __efi_call_virt(f, args...) \ - __efi_call_virt_pointer(efi.runtime, f, args) + arch_efi_call_virt(efi.runtime, f, args) + +union efi_rts_args { + struct { + efi_time_t *time; + efi_time_cap_t *capabilities; + } GET_TIME; + + struct { + efi_time_t *time; + } SET_TIME; + + struct { + efi_bool_t *enabled; + efi_bool_t *pending; + efi_time_t *time; + } GET_WAKEUP_TIME; + + struct { + efi_bool_t enable; + efi_time_t *time; + } SET_WAKEUP_TIME; + + struct { + efi_char16_t *name; + efi_guid_t *vendor; + u32 *attr; + unsigned long *data_size; + void *data; + } GET_VARIABLE; + + struct { + unsigned long *name_size; + efi_char16_t *name; + efi_guid_t *vendor; + } GET_NEXT_VARIABLE; + + struct { + efi_char16_t *name; + efi_guid_t *vendor; + u32 attr; + unsigned long data_size; + void *data; + } SET_VARIABLE; + + struct { + u32 attr; + u64 *storage_space; + u64 *remaining_space; + u64 *max_variable_size; + } QUERY_VARIABLE_INFO; + + struct { + u32 *high_count; + } GET_NEXT_HIGH_MONO_COUNT; + + struct { + efi_capsule_header_t **capsules; + unsigned long count; + unsigned long sg_list; + } UPDATE_CAPSULE; + + struct { + efi_capsule_header_t **capsules; + unsigned long count; + u64 *max_size; + int *reset_type; + } QUERY_CAPSULE_CAPS; + + struct { + efi_status_t (__efiapi *acpi_prm_handler)(u64, void *); + u64 param_buffer_addr; + void *context; + } ACPI_PRM_HANDLER; +}; struct efi_runtime_work efi_rts_work; /* - * efi_queue_work: Queue efi_runtime_service() and wait until it's done - * @rts: efi_runtime_service() function identifier - * @rts_arg<1-5>: efi_runtime_service() function arguments + * 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, _arg1, _arg2, _arg3, _arg4, _arg5) \ -({ \ - 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); \ - efi_rts_work.arg1 = _arg1; \ - efi_rts_work.arg2 = _arg2; \ - efi_rts_work.arg3 = _arg3; \ - efi_rts_work.arg4 = _arg4; \ - efi_rts_work.arg5 = _arg5; \ - efi_rts_work.efi_rts_id = _rts; \ - \ - /* \ - * 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"); \ - \ - WARN_ON_ONCE(efi_rts_work.status == EFI_ABORTED); \ -exit: \ - efi_rts_work.efi_rts_id = EFI_NONE; \ - efi_rts_work.status; \ -}) +#define efi_queue_work(_rts, _args...) \ + __efi_queue_work(EFI_ ## _rts, \ + &(union efi_rts_args){ ._rts = { _args }}) #ifndef arch_efi_save_flags #define arch_efi_save_flags(state_flags) local_save_flags(state_flags) @@ -103,7 +145,7 @@ unsigned long efi_call_virt_save_flags(void) return flags; } -void efi_call_virt_check_flags(unsigned long flags, const char *call) +void efi_call_virt_check_flags(unsigned long flags, const void *caller) { unsigned long cur_flags, mismatch; @@ -114,8 +156,8 @@ void efi_call_virt_check_flags(unsigned long flags, const char *call) return; add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_NOW_UNRELIABLE); - pr_err_ratelimited(FW_BUG "IRQ flags corrupted (0x%08lx=>0x%08lx) by EFI %s\n", - flags, cur_flags, call); + 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); } @@ -170,74 +212,90 @@ extern struct semaphore __efi_uv_runtime_lock __alias(efi_runtime_lock); /* * Calls the appropriate efi_runtime_service() with the appropriate * arguments. - * - * Semantics followed by efi_call_rts() to understand efi_runtime_work: - * 1. If argument was a pointer, recast it from void pointer to original - * pointer type. - * 2. If argument was a value, recast it from void pointer to original - * pointer type and dereference it. */ static void efi_call_rts(struct work_struct *work) { - void *arg1, *arg2, *arg3, *arg4, *arg5; + const union efi_rts_args *args = efi_rts_work.args; efi_status_t status = EFI_NOT_FOUND; + unsigned long flags; - arg1 = efi_rts_work.arg1; - arg2 = efi_rts_work.arg2; - arg3 = efi_rts_work.arg3; - arg4 = efi_rts_work.arg4; - arg5 = efi_rts_work.arg5; + arch_efi_call_virt_setup(); + flags = efi_call_virt_save_flags(); switch (efi_rts_work.efi_rts_id) { case EFI_GET_TIME: - status = efi_call_virt(get_time, (efi_time_t *)arg1, - (efi_time_cap_t *)arg2); + 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, (efi_time_t *)arg1); + status = efi_call_virt(set_time, + args->SET_TIME.time); break; case EFI_GET_WAKEUP_TIME: - status = efi_call_virt(get_wakeup_time, (efi_bool_t *)arg1, - (efi_bool_t *)arg2, (efi_time_t *)arg3); + 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, *(efi_bool_t *)arg1, - (efi_time_t *)arg2); + 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, (efi_char16_t *)arg1, - (efi_guid_t *)arg2, (u32 *)arg3, - (unsigned long *)arg4, (void *)arg5); + 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, (unsigned long *)arg1, - (efi_char16_t *)arg2, - (efi_guid_t *)arg3); + 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, (efi_char16_t *)arg1, - (efi_guid_t *)arg2, *(u32 *)arg3, - *(unsigned long *)arg4, (void *)arg5); + 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, *(u32 *)arg1, - (u64 *)arg2, (u64 *)arg3, (u64 *)arg4); + 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, (u32 *)arg1); + 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, - (efi_capsule_header_t **)arg1, - *(unsigned long *)arg2, - *(unsigned long *)arg3); + 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, - (efi_capsule_header_t **)arg1, - *(unsigned long *)arg2, (u64 *)arg3, - (int *)arg4); + 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 @@ -246,17 +304,53 @@ static void efi_call_rts(struct work_struct *work) */ pr_err("Requested executing invalid EFI Runtime Service.\n"); } + + efi_call_virt_check_flags(flags, efi_rts_work.caller); + arch_efi_call_virt_teardown(); + efi_rts_work.status = status; complete(&efi_rts_work.efi_rts_comp); } +static efi_status_t __efi_queue_work(enum efi_rts_ids id, + union efi_rts_args *args) +{ + 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); + + /* + * 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"); + + WARN_ON_ONCE(efi_rts_work.status == EFI_ABORTED); +exit: + efi_rts_work.efi_rts_id = EFI_NONE; + return efi_rts_work.status; +} + static efi_status_t virt_efi_get_time(efi_time_t *tm, efi_time_cap_t *tc) { efi_status_t status; if (down_interruptible(&efi_runtime_lock)) return EFI_ABORTED; - status = efi_queue_work(EFI_GET_TIME, tm, tc, NULL, NULL, NULL); + status = efi_queue_work(GET_TIME, tm, tc); up(&efi_runtime_lock); return status; } @@ -267,7 +361,7 @@ static efi_status_t virt_efi_set_time(efi_time_t *tm) if (down_interruptible(&efi_runtime_lock)) return EFI_ABORTED; - status = efi_queue_work(EFI_SET_TIME, tm, NULL, NULL, NULL, NULL); + status = efi_queue_work(SET_TIME, tm); up(&efi_runtime_lock); return status; } @@ -280,8 +374,7 @@ static efi_status_t virt_efi_get_wakeup_time(efi_bool_t *enabled, if (down_interruptible(&efi_runtime_lock)) return EFI_ABORTED; - status = efi_queue_work(EFI_GET_WAKEUP_TIME, enabled, pending, tm, NULL, - NULL); + status = efi_queue_work(GET_WAKEUP_TIME, enabled, pending, tm); up(&efi_runtime_lock); return status; } @@ -292,8 +385,7 @@ static efi_status_t virt_efi_set_wakeup_time(efi_bool_t enabled, efi_time_t *tm) if (down_interruptible(&efi_runtime_lock)) return EFI_ABORTED; - status = efi_queue_work(EFI_SET_WAKEUP_TIME, &enabled, tm, NULL, NULL, - NULL); + status = efi_queue_work(SET_WAKEUP_TIME, enabled, tm); up(&efi_runtime_lock); return status; } @@ -308,7 +400,7 @@ static efi_status_t virt_efi_get_variable(efi_char16_t *name, if (down_interruptible(&efi_runtime_lock)) return EFI_ABORTED; - status = efi_queue_work(EFI_GET_VARIABLE, name, vendor, attr, data_size, + status = efi_queue_work(GET_VARIABLE, name, vendor, attr, data_size, data); up(&efi_runtime_lock); return status; @@ -322,8 +414,7 @@ static efi_status_t virt_efi_get_next_variable(unsigned long *name_size, if (down_interruptible(&efi_runtime_lock)) return EFI_ABORTED; - status = efi_queue_work(EFI_GET_NEXT_VARIABLE, name_size, name, vendor, - NULL, NULL); + status = efi_queue_work(GET_NEXT_VARIABLE, name_size, name, vendor); up(&efi_runtime_lock); return status; } @@ -338,24 +429,23 @@ static efi_status_t virt_efi_set_variable(efi_char16_t *name, if (down_interruptible(&efi_runtime_lock)) return EFI_ABORTED; - status = efi_queue_work(EFI_SET_VARIABLE, name, vendor, &attr, &data_size, + status = efi_queue_work(SET_VARIABLE, name, vendor, attr, data_size, data); up(&efi_runtime_lock); return status; } static efi_status_t -virt_efi_set_variable_nonblocking(efi_char16_t *name, efi_guid_t *vendor, - u32 attr, unsigned long data_size, - void *data) +virt_efi_set_variable_nb(efi_char16_t *name, efi_guid_t *vendor, u32 attr, + unsigned long data_size, void *data) { efi_status_t status; if (down_trylock(&efi_runtime_lock)) return EFI_NOT_READY; - status = efi_call_virt(set_variable, name, vendor, attr, data_size, - data); + status = efi_call_virt_pointer(efi.runtime, set_variable, name, vendor, + attr, data_size, data); up(&efi_runtime_lock); return status; } @@ -373,17 +463,15 @@ static efi_status_t virt_efi_query_variable_info(u32 attr, if (down_interruptible(&efi_runtime_lock)) return EFI_ABORTED; - status = efi_queue_work(EFI_QUERY_VARIABLE_INFO, &attr, storage_space, - remaining_space, max_variable_size, NULL); + status = efi_queue_work(QUERY_VARIABLE_INFO, attr, storage_space, + remaining_space, max_variable_size); up(&efi_runtime_lock); return status; } static efi_status_t -virt_efi_query_variable_info_nonblocking(u32 attr, - u64 *storage_space, - u64 *remaining_space, - u64 *max_variable_size) +virt_efi_query_variable_info_nb(u32 attr, u64 *storage_space, + u64 *remaining_space, u64 *max_variable_size) { efi_status_t status; @@ -393,8 +481,9 @@ virt_efi_query_variable_info_nonblocking(u32 attr, if (down_trylock(&efi_runtime_lock)) return EFI_NOT_READY; - status = efi_call_virt(query_variable_info, attr, storage_space, - remaining_space, max_variable_size); + status = efi_call_virt_pointer(efi.runtime, query_variable_info, attr, + storage_space, remaining_space, + max_variable_size); up(&efi_runtime_lock); return status; } @@ -405,8 +494,7 @@ static efi_status_t virt_efi_get_next_high_mono_count(u32 *count) if (down_interruptible(&efi_runtime_lock)) return EFI_ABORTED; - status = efi_queue_work(EFI_GET_NEXT_HIGH_MONO_COUNT, count, NULL, NULL, - NULL, NULL); + status = efi_queue_work(GET_NEXT_HIGH_MONO_COUNT, count); up(&efi_runtime_lock); return status; } @@ -421,8 +509,13 @@ static void virt_efi_reset_system(int reset_type, "could not get exclusive access to the firmware\n"); return; } + + arch_efi_call_virt_setup(); efi_rts_work.efi_rts_id = EFI_RESET_SYSTEM; - __efi_call_virt(reset_system, reset_type, status, data_size, data); + arch_efi_call_virt(efi.runtime, reset_system, reset_type, status, + data_size, data); + arch_efi_call_virt_teardown(); + up(&efi_runtime_lock); } @@ -437,8 +530,7 @@ static efi_status_t virt_efi_update_capsule(efi_capsule_header_t **capsules, if (down_interruptible(&efi_runtime_lock)) return EFI_ABORTED; - status = efi_queue_work(EFI_UPDATE_CAPSULE, capsules, &count, &sg_list, - NULL, NULL); + status = efi_queue_work(UPDATE_CAPSULE, capsules, count, sg_list); up(&efi_runtime_lock); return status; } @@ -455,26 +547,44 @@ static efi_status_t virt_efi_query_capsule_caps(efi_capsule_header_t **capsules, if (down_interruptible(&efi_runtime_lock)) return EFI_ABORTED; - status = efi_queue_work(EFI_QUERY_CAPSULE_CAPS, capsules, &count, - max_size, reset_type, NULL); + status = efi_queue_work(QUERY_CAPSULE_CAPS, capsules, count, + max_size, reset_type); up(&efi_runtime_lock); return status; } -void efi_native_runtime_setup(void) +void __init efi_native_runtime_setup(void) { - efi.get_time = virt_efi_get_time; - efi.set_time = virt_efi_set_time; - efi.get_wakeup_time = virt_efi_get_wakeup_time; - efi.set_wakeup_time = virt_efi_set_wakeup_time; - efi.get_variable = virt_efi_get_variable; - efi.get_next_variable = virt_efi_get_next_variable; - efi.set_variable = virt_efi_set_variable; - efi.set_variable_nonblocking = virt_efi_set_variable_nonblocking; - efi.get_next_high_mono_count = virt_efi_get_next_high_mono_count; - efi.reset_system = virt_efi_reset_system; - efi.query_variable_info = virt_efi_query_variable_info; - efi.query_variable_info_nonblocking = virt_efi_query_variable_info_nonblocking; - efi.update_capsule = virt_efi_update_capsule; - efi.query_capsule_caps = virt_efi_query_capsule_caps; + efi.get_time = virt_efi_get_time; + efi.set_time = virt_efi_set_time; + efi.get_wakeup_time = virt_efi_get_wakeup_time; + efi.set_wakeup_time = virt_efi_set_wakeup_time; + efi.get_variable = virt_efi_get_variable; + efi.get_next_variable = virt_efi_get_next_variable; + efi.set_variable = virt_efi_set_variable; + efi.set_variable_nonblocking = virt_efi_set_variable_nb; + efi.get_next_high_mono_count = virt_efi_get_next_high_mono_count; + efi.reset_system = virt_efi_reset_system; + efi.query_variable_info = virt_efi_query_variable_info; + efi.query_variable_info_nonblocking = virt_efi_query_variable_info_nb; + efi.update_capsule = virt_efi_update_capsule; + efi.query_capsule_caps = virt_efi_query_capsule_caps; } + +#ifdef CONFIG_ACPI_PRMT + +efi_status_t +efi_call_acpi_prm_handler(efi_status_t (__efiapi *handler_addr)(u64, void *), + u64 param_buffer_addr, void *context) +{ + efi_status_t status; + + if (down_interruptible(&efi_runtime_lock)) + return EFI_ABORTED; + status = efi_queue_work(ACPI_PRM_HANDLER, handler_addr, + param_buffer_addr, context); + up(&efi_runtime_lock); + return status; +} + +#endif |