EFI updates for v4.20:
- Add support for enlisting the help of the EFI firmware to create memory reservations that persist across kexec. - Add page fault handling to the runtime services support code on x86 so we can gracefully recover from buggy EFI firmware. - Fix command line handling on x86 for the boot path that omits the stub's PE/COFF entry point. - Other assorted fixes. -----BEGIN PGP SIGNATURE----- iQEzBAABCAAdFiEEnNKg2mrY9zMBdeK7wjcgfpV0+n0FAlurXR8ACgkQwjcgfpV0 +n2CGwf/V4exixXjTDwkqE6gY5bq0Y3AL8tp89wdbJzjgGOIJLKh3CrGr8xEFHrv oYObcvB3SfNEIyGeBjc/8ZMw1P/j98s6ucsMm0u+V52k7xxu/xJoIPw3bX2R8LLc QhedUmKWLFQXxottaqzRFi1m0rP9TlAlc2n2pjIPCywjTPzeT/jBTtnRGRRdpDkN uxwv59eXc6MXuwJGhM9lGIBCu8ra54SiSByJSKoMwNYXQRCLtiBUg5iibWkKigHp 9rQiimQnDOuPiZ6JGFx6pwSu7cqv3d8LYk5EnU3zYfzxAvHRfxuf40joSeZzySby vZ4zRog79DxkSnuvaQ0+phQHiq+yQg== =HZGk -----END PGP SIGNATURE----- Merge tag 'efi-next' of git://git.kernel.org/pub/scm/linux/kernel/git/efi/efi into efi/core Pull EFI updates for v4.20 from Ard Biesheuvel: - Add support for enlisting the help of the EFI firmware to create memory reservations that persist across kexec. - Add page fault handling to the runtime services support code on x86 so we can gracefully recover from buggy EFI firmware. - Fix command line handling on x86 for the boot path that omits the stub's PE/COFF entry point. - Other assorted fixes.
This commit is contained in:
commit
fa70f0d2ce
|
@ -738,6 +738,7 @@ efi_main(struct efi_config *c, struct boot_params *boot_params)
|
|||
struct desc_struct *desc;
|
||||
void *handle;
|
||||
efi_system_table_t *_table;
|
||||
unsigned long cmdline_paddr;
|
||||
|
||||
efi_early = c;
|
||||
|
||||
|
@ -755,6 +756,15 @@ efi_main(struct efi_config *c, struct boot_params *boot_params)
|
|||
else
|
||||
setup_boot_services32(efi_early);
|
||||
|
||||
/*
|
||||
* make_boot_params() may have been called before efi_main(), in which
|
||||
* case this is the second time we parse the cmdline. This is ok,
|
||||
* parsing the cmdline multiple times does not have side-effects.
|
||||
*/
|
||||
cmdline_paddr = ((u64)hdr->cmd_line_ptr |
|
||||
((u64)boot_params->ext_cmd_line_ptr << 32));
|
||||
efi_parse_options((char *)cmdline_paddr);
|
||||
|
||||
/*
|
||||
* If the boot loader gave us a value for secure_boot then we use that,
|
||||
* otherwise we ask the BIOS.
|
||||
|
|
|
@ -391,6 +391,13 @@ int main(int argc, char ** argv)
|
|||
die("Unable to mmap '%s': %m", argv[2]);
|
||||
/* Number of 16-byte paragraphs, including space for a 4-byte CRC */
|
||||
sys_size = (sz + 15 + 4) / 16;
|
||||
#ifdef CONFIG_EFI_STUB
|
||||
/*
|
||||
* COFF requires minimum 32-byte alignment of sections, and
|
||||
* adding a signature is problematic without that alignment.
|
||||
*/
|
||||
sys_size = (sys_size + 1) & ~1;
|
||||
#endif
|
||||
|
||||
/* Patch the setup code with the appropriate size parameters */
|
||||
buf[0x1f1] = setup_sectors-1;
|
||||
|
|
|
@ -140,6 +140,7 @@ extern void __init efi_apply_memmap_quirks(void);
|
|||
extern int __init efi_reuse_config(u64 tables, int nr_tables);
|
||||
extern void efi_delete_dummy_variable(void);
|
||||
extern void efi_switch_mm(struct mm_struct *mm);
|
||||
extern void efi_recover_from_page_fault(unsigned long phys_addr);
|
||||
|
||||
struct efi_setup_data {
|
||||
u64 fw_vendor;
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <linux/prefetch.h> /* prefetchw */
|
||||
#include <linux/context_tracking.h> /* exception_enter(), ... */
|
||||
#include <linux/uaccess.h> /* faulthandler_disabled() */
|
||||
#include <linux/efi.h> /* efi_recover_from_page_fault()*/
|
||||
#include <linux/mm_types.h>
|
||||
|
||||
#include <asm/cpufeature.h> /* boot_cpu_has, ... */
|
||||
|
@ -25,6 +26,7 @@
|
|||
#include <asm/vsyscall.h> /* emulate_vsyscall */
|
||||
#include <asm/vm86.h> /* struct vm86 */
|
||||
#include <asm/mmu_context.h> /* vma_pkey() */
|
||||
#include <asm/efi.h> /* efi_recover_from_page_fault()*/
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include <asm/trace/exceptions.h>
|
||||
|
@ -788,6 +790,13 @@ no_context(struct pt_regs *regs, unsigned long error_code,
|
|||
if (is_errata93(regs, address))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Buggy firmware could access regions which might page fault, try to
|
||||
* recover from such faults.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_EFI))
|
||||
efi_recover_from_page_fault(address);
|
||||
|
||||
/*
|
||||
* Oops. The kernel tried to access some bad page. We'll have to
|
||||
* terminate things with extreme prejudice:
|
||||
|
|
|
@ -26,12 +26,14 @@ static bool early_efi_keep;
|
|||
*/
|
||||
static __init int early_efi_map_fb(void)
|
||||
{
|
||||
unsigned long base, size;
|
||||
u64 base, size;
|
||||
|
||||
if (!early_efi_keep)
|
||||
return 0;
|
||||
|
||||
base = boot_params.screen_info.lfb_base;
|
||||
if (boot_params.screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE)
|
||||
base |= (u64)boot_params.screen_info.ext_lfb_base << 32;
|
||||
size = boot_params.screen_info.lfb_size;
|
||||
efi_fb = ioremap(base, size);
|
||||
|
||||
|
@ -46,9 +48,11 @@ early_initcall(early_efi_map_fb);
|
|||
*/
|
||||
static __ref void *early_efi_map(unsigned long start, unsigned long len)
|
||||
{
|
||||
unsigned long base;
|
||||
u64 base;
|
||||
|
||||
base = boot_params.screen_info.lfb_base;
|
||||
if (boot_params.screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE)
|
||||
base |= (u64)boot_params.screen_info.ext_lfb_base << 32;
|
||||
|
||||
if (efi_fb)
|
||||
return (efi_fb + start);
|
||||
|
|
|
@ -619,18 +619,16 @@ void __init efi_dump_pagetable(void)
|
|||
|
||||
/*
|
||||
* Makes the calling thread switch to/from efi_mm context. Can be used
|
||||
* for SetVirtualAddressMap() i.e. current->active_mm == init_mm as well
|
||||
* as during efi runtime calls i.e current->active_mm == current_mm.
|
||||
* We are not mm_dropping()/mm_grabbing() any mm, because we are not
|
||||
* losing/creating any references.
|
||||
* in a kernel thread and user context. Preemption needs to remain disabled
|
||||
* while the EFI-mm is borrowed. mmgrab()/mmdrop() is not used because the mm
|
||||
* can not change under us.
|
||||
* It should be ensured that there are no concurent calls to this function.
|
||||
*/
|
||||
void efi_switch_mm(struct mm_struct *mm)
|
||||
{
|
||||
task_lock(current);
|
||||
efi_scratch.prev_mm = current->active_mm;
|
||||
current->active_mm = mm;
|
||||
switch_mm(efi_scratch.prev_mm, mm, NULL);
|
||||
task_unlock(current);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_EFI_MIXED
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <asm/efi.h>
|
||||
#include <asm/uv/uv.h>
|
||||
#include <asm/cpu_device_id.h>
|
||||
#include <asm/reboot.h>
|
||||
|
||||
#define EFI_MIN_RESERVE 5120
|
||||
|
||||
|
@ -654,3 +655,80 @@ int efi_capsule_setup_info(struct capsule_info *cap_info, void *kbuff,
|
|||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* If any access by any efi runtime service causes a page fault, then,
|
||||
* 1. If it's efi_reset_system(), reboot through BIOS.
|
||||
* 2. If any other efi runtime service, then
|
||||
* a. Return error status to the efi caller process.
|
||||
* b. Disable EFI Runtime Services forever and
|
||||
* c. Freeze efi_rts_wq and schedule new process.
|
||||
*
|
||||
* @return: Returns, if the page fault is not handled. This function
|
||||
* will never return if the page fault is handled successfully.
|
||||
*/
|
||||
void efi_recover_from_page_fault(unsigned long phys_addr)
|
||||
{
|
||||
if (!IS_ENABLED(CONFIG_X86_64))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Make sure that an efi runtime service caused the page fault.
|
||||
* "efi_mm" cannot be used to check if the page fault had occurred
|
||||
* in the firmware context because efi=old_map doesn't use efi_pgd.
|
||||
*/
|
||||
if (efi_rts_work.efi_rts_id == NONE)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Address range 0x0000 - 0x0fff is always mapped in the efi_pgd, so
|
||||
* page faulting on these addresses isn't expected.
|
||||
*/
|
||||
if (phys_addr >= 0x0000 && phys_addr <= 0x0fff)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Print stack trace as it might be useful to know which EFI Runtime
|
||||
* Service is buggy.
|
||||
*/
|
||||
WARN(1, FW_BUG "Page fault caused by firmware at PA: 0x%lx\n",
|
||||
phys_addr);
|
||||
|
||||
/*
|
||||
* Buggy efi_reset_system() is handled differently from other EFI
|
||||
* Runtime Services as it doesn't use efi_rts_wq. Although,
|
||||
* native_machine_emergency_restart() says that machine_real_restart()
|
||||
* could fail, it's better not to compilcate this fault handler
|
||||
* because this case occurs *very* rarely and hence could be improved
|
||||
* on a need by basis.
|
||||
*/
|
||||
if (efi_rts_work.efi_rts_id == RESET_SYSTEM) {
|
||||
pr_info("efi_reset_system() buggy! Reboot through BIOS\n");
|
||||
machine_real_restart(MRR_BIOS);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Before calling EFI Runtime Service, the kernel has switched the
|
||||
* calling process to efi_mm. Hence, switch back to task_mm.
|
||||
*/
|
||||
arch_efi_call_virt_teardown();
|
||||
|
||||
/* Signal error status to the efi caller process */
|
||||
efi_rts_work.status = EFI_ABORTED;
|
||||
complete(&efi_rts_work.efi_rts_comp);
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -52,7 +52,8 @@ struct efi __read_mostly efi = {
|
|||
.properties_table = EFI_INVALID_TABLE_ADDR,
|
||||
.mem_attr_table = EFI_INVALID_TABLE_ADDR,
|
||||
.rng_seed = EFI_INVALID_TABLE_ADDR,
|
||||
.tpm_log = EFI_INVALID_TABLE_ADDR
|
||||
.tpm_log = EFI_INVALID_TABLE_ADDR,
|
||||
.mem_reserve = EFI_INVALID_TABLE_ADDR,
|
||||
};
|
||||
EXPORT_SYMBOL(efi);
|
||||
|
||||
|
@ -484,6 +485,7 @@ static __initdata efi_config_table_type_t common_tables[] = {
|
|||
{EFI_MEMORY_ATTRIBUTES_TABLE_GUID, "MEMATTR", &efi.mem_attr_table},
|
||||
{LINUX_EFI_RANDOM_SEED_TABLE_GUID, "RNG", &efi.rng_seed},
|
||||
{LINUX_EFI_TPM_EVENT_LOG_GUID, "TPMEventLog", &efi.tpm_log},
|
||||
{LINUX_EFI_MEMRESERVE_TABLE_GUID, "MEMRESERVE", &efi.mem_reserve},
|
||||
{NULL_GUID, NULL, NULL},
|
||||
};
|
||||
|
||||
|
@ -591,6 +593,29 @@ int __init efi_config_parse_tables(void *config_tables, int count, int sz,
|
|||
early_memunmap(tbl, sizeof(*tbl));
|
||||
}
|
||||
|
||||
if (efi.mem_reserve != EFI_INVALID_TABLE_ADDR) {
|
||||
unsigned long prsv = efi.mem_reserve;
|
||||
|
||||
while (prsv) {
|
||||
struct linux_efi_memreserve *rsv;
|
||||
|
||||
/* reserve the entry itself */
|
||||
memblock_reserve(prsv, sizeof(*rsv));
|
||||
|
||||
rsv = early_memremap(prsv, sizeof(*rsv));
|
||||
if (rsv == NULL) {
|
||||
pr_err("Could not map UEFI memreserve entry!\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (rsv->size)
|
||||
memblock_reserve(rsv->base, rsv->size);
|
||||
|
||||
prsv = rsv->next;
|
||||
early_memunmap(rsv, sizeof(*rsv));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -937,6 +962,38 @@ bool efi_is_table_address(unsigned long phys_addr)
|
|||
return false;
|
||||
}
|
||||
|
||||
static DEFINE_SPINLOCK(efi_mem_reserve_persistent_lock);
|
||||
|
||||
int efi_mem_reserve_persistent(phys_addr_t addr, u64 size)
|
||||
{
|
||||
struct linux_efi_memreserve *rsv, *parent;
|
||||
|
||||
if (efi.mem_reserve == EFI_INVALID_TABLE_ADDR)
|
||||
return -ENODEV;
|
||||
|
||||
rsv = kmalloc(sizeof(*rsv), GFP_KERNEL);
|
||||
if (!rsv)
|
||||
return -ENOMEM;
|
||||
|
||||
parent = memremap(efi.mem_reserve, sizeof(*rsv), MEMREMAP_WB);
|
||||
if (!parent) {
|
||||
kfree(rsv);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
rsv->base = addr;
|
||||
rsv->size = size;
|
||||
|
||||
spin_lock(&efi_mem_reserve_persistent_lock);
|
||||
rsv->next = parent->next;
|
||||
parent->next = __pa(rsv);
|
||||
spin_unlock(&efi_mem_reserve_persistent_lock);
|
||||
|
||||
memunmap(parent);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KEXEC
|
||||
static int update_efi_random_seed(struct notifier_block *nb,
|
||||
unsigned long code, void *unused)
|
||||
|
|
|
@ -16,7 +16,8 @@ cflags-$(CONFIG_X86) += -m$(BITS) -D__KERNEL__ -O2 \
|
|||
cflags-$(CONFIG_ARM64) := $(subst -pg,,$(KBUILD_CFLAGS)) -fpie \
|
||||
$(DISABLE_STACKLEAK_PLUGIN)
|
||||
cflags-$(CONFIG_ARM) := $(subst -pg,,$(KBUILD_CFLAGS)) \
|
||||
-fno-builtin -fpic -mno-single-pic-base
|
||||
-fno-builtin -fpic \
|
||||
$(call cc-option,-mno-single-pic-base)
|
||||
|
||||
cflags-$(CONFIG_EFI_ARMSTUB) += -I$(srctree)/scripts/dtc/libfdt
|
||||
|
||||
|
|
|
@ -69,6 +69,31 @@ static struct screen_info *setup_graphics(efi_system_table_t *sys_table_arg)
|
|||
return si;
|
||||
}
|
||||
|
||||
void install_memreserve_table(efi_system_table_t *sys_table_arg)
|
||||
{
|
||||
struct linux_efi_memreserve *rsv;
|
||||
efi_guid_t memreserve_table_guid = LINUX_EFI_MEMRESERVE_TABLE_GUID;
|
||||
efi_status_t status;
|
||||
|
||||
status = efi_call_early(allocate_pool, EFI_LOADER_DATA, sizeof(*rsv),
|
||||
(void **)&rsv);
|
||||
if (status != EFI_SUCCESS) {
|
||||
pr_efi_err(sys_table_arg, "Failed to allocate memreserve entry!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
rsv->next = 0;
|
||||
rsv->base = 0;
|
||||
rsv->size = 0;
|
||||
|
||||
status = efi_call_early(install_configuration_table,
|
||||
&memreserve_table_guid,
|
||||
rsv);
|
||||
if (status != EFI_SUCCESS)
|
||||
pr_efi_err(sys_table_arg, "Failed to install memreserve config table!\n");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This function handles the architcture specific differences between arm and
|
||||
* arm64 regarding where the kernel image must be loaded and any memory that
|
||||
|
@ -235,6 +260,8 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table,
|
|||
}
|
||||
}
|
||||
|
||||
install_memreserve_table(sys_table);
|
||||
|
||||
new_fdt_addr = fdt_addr;
|
||||
status = allocate_new_fdt_and_exit_boot(sys_table, handle,
|
||||
&new_fdt_addr, efi_get_max_fdt_addr(dram_base),
|
||||
|
|
|
@ -45,39 +45,7 @@
|
|||
#define __efi_call_virt(f, args...) \
|
||||
__efi_call_virt_pointer(efi.systab->runtime, f, args)
|
||||
|
||||
/* efi_runtime_service() function identifiers */
|
||||
enum efi_rts_ids {
|
||||
GET_TIME,
|
||||
SET_TIME,
|
||||
GET_WAKEUP_TIME,
|
||||
SET_WAKEUP_TIME,
|
||||
GET_VARIABLE,
|
||||
GET_NEXT_VARIABLE,
|
||||
SET_VARIABLE,
|
||||
QUERY_VARIABLE_INFO,
|
||||
GET_NEXT_HIGH_MONO_COUNT,
|
||||
UPDATE_CAPSULE,
|
||||
QUERY_CAPSULE_CAPS,
|
||||
};
|
||||
|
||||
/*
|
||||
* efi_runtime_work: Details of EFI Runtime Service work
|
||||
* @arg<1-5>: EFI Runtime Service function arguments
|
||||
* @status: Status of executing EFI Runtime Service
|
||||
* @efi_rts_id: EFI Runtime Service function identifier
|
||||
* @efi_rts_comp: Struct used for handling completions
|
||||
*/
|
||||
struct efi_runtime_work {
|
||||
void *arg1;
|
||||
void *arg2;
|
||||
void *arg3;
|
||||
void *arg4;
|
||||
void *arg5;
|
||||
efi_status_t status;
|
||||
struct work_struct work;
|
||||
enum efi_rts_ids efi_rts_id;
|
||||
struct completion efi_rts_comp;
|
||||
};
|
||||
struct efi_runtime_work efi_rts_work;
|
||||
|
||||
/*
|
||||
* efi_queue_work: Queue efi_runtime_service() and wait until it's done
|
||||
|
@ -91,9 +59,13 @@ struct efi_runtime_work {
|
|||
*/
|
||||
#define efi_queue_work(_rts, _arg1, _arg2, _arg3, _arg4, _arg5) \
|
||||
({ \
|
||||
struct efi_runtime_work efi_rts_work; \
|
||||
efi_rts_work.status = EFI_ABORTED; \
|
||||
\
|
||||
if (!efi_enabled(EFI_RUNTIME_SERVICES)) { \
|
||||
pr_warn_once("EFI Runtime Services are disabled!\n"); \
|
||||
goto exit; \
|
||||
} \
|
||||
\
|
||||
init_completion(&efi_rts_work.efi_rts_comp); \
|
||||
INIT_WORK_ONSTACK(&efi_rts_work.work, efi_call_rts); \
|
||||
efi_rts_work.arg1 = _arg1; \
|
||||
|
@ -112,6 +84,8 @@ struct efi_runtime_work {
|
|||
else \
|
||||
pr_err("Failed to queue work to efi_rts_wq.\n"); \
|
||||
\
|
||||
exit: \
|
||||
efi_rts_work.efi_rts_id = NONE; \
|
||||
efi_rts_work.status; \
|
||||
})
|
||||
|
||||
|
@ -184,18 +158,16 @@ static DEFINE_SEMAPHORE(efi_runtime_lock);
|
|||
*/
|
||||
static void efi_call_rts(struct work_struct *work)
|
||||
{
|
||||
struct efi_runtime_work *efi_rts_work;
|
||||
void *arg1, *arg2, *arg3, *arg4, *arg5;
|
||||
efi_status_t status = EFI_NOT_FOUND;
|
||||
|
||||
efi_rts_work = container_of(work, struct efi_runtime_work, work);
|
||||
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;
|
||||
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;
|
||||
|
||||
switch (efi_rts_work->efi_rts_id) {
|
||||
switch (efi_rts_work.efi_rts_id) {
|
||||
case GET_TIME:
|
||||
status = efi_call_virt(get_time, (efi_time_t *)arg1,
|
||||
(efi_time_cap_t *)arg2);
|
||||
|
@ -253,8 +225,8 @@ static void efi_call_rts(struct work_struct *work)
|
|||
*/
|
||||
pr_err("Requested executing invalid EFI Runtime Service.\n");
|
||||
}
|
||||
efi_rts_work->status = status;
|
||||
complete(&efi_rts_work->efi_rts_comp);
|
||||
efi_rts_work.status = status;
|
||||
complete(&efi_rts_work.efi_rts_comp);
|
||||
}
|
||||
|
||||
static efi_status_t virt_efi_get_time(efi_time_t *tm, efi_time_cap_t *tc)
|
||||
|
@ -428,6 +400,7 @@ static void virt_efi_reset_system(int reset_type,
|
|||
"could not get exclusive access to the firmware\n");
|
||||
return;
|
||||
}
|
||||
efi_rts_work.efi_rts_id = RESET_SYSTEM;
|
||||
__efi_call_virt(reset_system, reset_type, status, data_size, data);
|
||||
up(&efi_runtime_lock);
|
||||
}
|
||||
|
|
|
@ -542,6 +542,30 @@ static long efi_runtime_get_nexthighmonocount(unsigned long arg)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static long efi_runtime_reset_system(unsigned long arg)
|
||||
{
|
||||
struct efi_resetsystem __user *resetsystem_user;
|
||||
struct efi_resetsystem resetsystem;
|
||||
void *data = NULL;
|
||||
|
||||
resetsystem_user = (struct efi_resetsystem __user *)arg;
|
||||
if (copy_from_user(&resetsystem, resetsystem_user,
|
||||
sizeof(resetsystem)))
|
||||
return -EFAULT;
|
||||
if (resetsystem.data_size != 0) {
|
||||
data = memdup_user((void *)resetsystem.data,
|
||||
resetsystem.data_size);
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
}
|
||||
|
||||
efi.reset_system(resetsystem.reset_type, resetsystem.status,
|
||||
resetsystem.data_size, (efi_char16_t *)data);
|
||||
|
||||
kfree(data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long efi_runtime_query_variableinfo(unsigned long arg)
|
||||
{
|
||||
struct efi_queryvariableinfo __user *queryvariableinfo_user;
|
||||
|
@ -682,6 +706,9 @@ static long efi_test_ioctl(struct file *file, unsigned int cmd,
|
|||
|
||||
case EFI_RUNTIME_QUERY_CAPSULECAPABILITIES:
|
||||
return efi_runtime_query_capsulecaps(arg);
|
||||
|
||||
case EFI_RUNTIME_RESET_SYSTEM:
|
||||
return efi_runtime_reset_system(arg);
|
||||
}
|
||||
|
||||
return -ENOTTY;
|
||||
|
|
|
@ -81,6 +81,13 @@ struct efi_querycapsulecapabilities {
|
|||
efi_status_t *status;
|
||||
} __packed;
|
||||
|
||||
struct efi_resetsystem {
|
||||
int reset_type;
|
||||
efi_status_t status;
|
||||
unsigned long data_size;
|
||||
efi_char16_t *data;
|
||||
} __packed;
|
||||
|
||||
#define EFI_RUNTIME_GET_VARIABLE \
|
||||
_IOWR('p', 0x01, struct efi_getvariable)
|
||||
#define EFI_RUNTIME_SET_VARIABLE \
|
||||
|
@ -108,4 +115,7 @@ struct efi_querycapsulecapabilities {
|
|||
#define EFI_RUNTIME_QUERY_CAPSULECAPABILITIES \
|
||||
_IOR('p', 0x0A, struct efi_querycapsulecapabilities)
|
||||
|
||||
#define EFI_RUNTIME_RESET_SYSTEM \
|
||||
_IOW('p', 0x0B, struct efi_resetsystem)
|
||||
|
||||
#endif /* _DRIVERS_FIRMWARE_EFI_TEST_H_ */
|
||||
|
|
|
@ -672,6 +672,7 @@ void efi_native_runtime_setup(void);
|
|||
#define LINUX_EFI_LOADER_ENTRY_GUID EFI_GUID(0x4a67b082, 0x0a4c, 0x41cf, 0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f)
|
||||
#define LINUX_EFI_RANDOM_SEED_TABLE_GUID EFI_GUID(0x1ce1e5bc, 0x7ceb, 0x42f2, 0x81, 0xe5, 0x8a, 0xad, 0xf1, 0x80, 0xf5, 0x7b)
|
||||
#define LINUX_EFI_TPM_EVENT_LOG_GUID EFI_GUID(0xb7799cb0, 0xeca2, 0x4943, 0x96, 0x67, 0x1f, 0xae, 0x07, 0xb7, 0x47, 0xfa)
|
||||
#define LINUX_EFI_MEMRESERVE_TABLE_GUID EFI_GUID(0x888eb0c6, 0x8ede, 0x4ff5, 0xa8, 0xf0, 0x9a, 0xee, 0x5c, 0xb9, 0x77, 0xc2)
|
||||
|
||||
typedef struct {
|
||||
efi_guid_t guid;
|
||||
|
@ -957,6 +958,7 @@ extern struct efi {
|
|||
unsigned long mem_attr_table; /* memory attributes table */
|
||||
unsigned long rng_seed; /* UEFI firmware random seed */
|
||||
unsigned long tpm_log; /* TPM2 Event Log table */
|
||||
unsigned long mem_reserve; /* Linux EFI memreserve table */
|
||||
efi_get_time_t *get_time;
|
||||
efi_set_time_t *set_time;
|
||||
efi_get_wakeup_time_t *get_wakeup_time;
|
||||
|
@ -1041,6 +1043,7 @@ extern int __init efi_uart_console_only (void);
|
|||
extern u64 efi_mem_desc_end(efi_memory_desc_t *md);
|
||||
extern int efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md);
|
||||
extern void efi_mem_reserve(phys_addr_t addr, u64 size);
|
||||
extern int efi_mem_reserve_persistent(phys_addr_t addr, u64 size);
|
||||
extern void efi_initialize_iomem_resources(struct resource *code_resource,
|
||||
struct resource *data_resource, struct resource *bss_resource);
|
||||
extern void efi_reserve_boot_services(void);
|
||||
|
@ -1659,7 +1662,55 @@ struct linux_efi_tpm_eventlog {
|
|||
|
||||
extern int efi_tpm_eventlog_init(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.
|
||||
*/
|
||||
enum efi_rts_ids {
|
||||
NONE,
|
||||
GET_TIME,
|
||||
SET_TIME,
|
||||
GET_WAKEUP_TIME,
|
||||
SET_WAKEUP_TIME,
|
||||
GET_VARIABLE,
|
||||
GET_NEXT_VARIABLE,
|
||||
SET_VARIABLE,
|
||||
QUERY_VARIABLE_INFO,
|
||||
GET_NEXT_HIGH_MONO_COUNT,
|
||||
RESET_SYSTEM,
|
||||
UPDATE_CAPSULE,
|
||||
QUERY_CAPSULE_CAPS,
|
||||
};
|
||||
|
||||
/*
|
||||
* efi_runtime_work: Details of EFI Runtime Service work
|
||||
* @arg<1-5>: EFI Runtime Service function arguments
|
||||
* @status: Status of executing EFI Runtime Service
|
||||
* @efi_rts_id: EFI Runtime Service function identifier
|
||||
* @efi_rts_comp: Struct used for handling completions
|
||||
*/
|
||||
struct efi_runtime_work {
|
||||
void *arg1;
|
||||
void *arg2;
|
||||
void *arg3;
|
||||
void *arg4;
|
||||
void *arg5;
|
||||
efi_status_t status;
|
||||
struct work_struct work;
|
||||
enum efi_rts_ids efi_rts_id;
|
||||
struct completion efi_rts_comp;
|
||||
};
|
||||
|
||||
extern struct efi_runtime_work efi_rts_work;
|
||||
|
||||
/* Workqueue to queue EFI Runtime Services */
|
||||
extern struct workqueue_struct *efi_rts_wq;
|
||||
|
||||
struct linux_efi_memreserve {
|
||||
phys_addr_t next;
|
||||
phys_addr_t base;
|
||||
phys_addr_t size;
|
||||
};
|
||||
|
||||
#endif /* _LINUX_EFI_H */
|
||||
|
|
Loading…
Reference in New Issue