Commit fa70f0d2 authored by Thomas Gleixner's avatar Thomas Gleixner

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.
parents 6bf4ca7f 9c1442a9
...@@ -738,6 +738,7 @@ efi_main(struct efi_config *c, struct boot_params *boot_params) ...@@ -738,6 +738,7 @@ efi_main(struct efi_config *c, struct boot_params *boot_params)
struct desc_struct *desc; struct desc_struct *desc;
void *handle; void *handle;
efi_system_table_t *_table; efi_system_table_t *_table;
unsigned long cmdline_paddr;
efi_early = c; efi_early = c;
...@@ -755,6 +756,15 @@ efi_main(struct efi_config *c, struct boot_params *boot_params) ...@@ -755,6 +756,15 @@ efi_main(struct efi_config *c, struct boot_params *boot_params)
else else
setup_boot_services32(efi_early); 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, * If the boot loader gave us a value for secure_boot then we use that,
* otherwise we ask the BIOS. * otherwise we ask the BIOS.
......
...@@ -391,6 +391,13 @@ int main(int argc, char ** argv) ...@@ -391,6 +391,13 @@ int main(int argc, char ** argv)
die("Unable to mmap '%s': %m", argv[2]); die("Unable to mmap '%s': %m", argv[2]);
/* Number of 16-byte paragraphs, including space for a 4-byte CRC */ /* Number of 16-byte paragraphs, including space for a 4-byte CRC */
sys_size = (sz + 15 + 4) / 16; 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 */ /* Patch the setup code with the appropriate size parameters */
buf[0x1f1] = setup_sectors-1; buf[0x1f1] = setup_sectors-1;
......
...@@ -140,6 +140,7 @@ extern void __init efi_apply_memmap_quirks(void); ...@@ -140,6 +140,7 @@ extern void __init efi_apply_memmap_quirks(void);
extern int __init efi_reuse_config(u64 tables, int nr_tables); extern int __init efi_reuse_config(u64 tables, int nr_tables);
extern void efi_delete_dummy_variable(void); extern void efi_delete_dummy_variable(void);
extern void efi_switch_mm(struct mm_struct *mm); extern void efi_switch_mm(struct mm_struct *mm);
extern void efi_recover_from_page_fault(unsigned long phys_addr);
struct efi_setup_data { struct efi_setup_data {
u64 fw_vendor; u64 fw_vendor;
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <linux/prefetch.h> /* prefetchw */ #include <linux/prefetch.h> /* prefetchw */
#include <linux/context_tracking.h> /* exception_enter(), ... */ #include <linux/context_tracking.h> /* exception_enter(), ... */
#include <linux/uaccess.h> /* faulthandler_disabled() */ #include <linux/uaccess.h> /* faulthandler_disabled() */
#include <linux/efi.h> /* efi_recover_from_page_fault()*/
#include <linux/mm_types.h> #include <linux/mm_types.h>
#include <asm/cpufeature.h> /* boot_cpu_has, ... */ #include <asm/cpufeature.h> /* boot_cpu_has, ... */
...@@ -25,6 +26,7 @@ ...@@ -25,6 +26,7 @@
#include <asm/vsyscall.h> /* emulate_vsyscall */ #include <asm/vsyscall.h> /* emulate_vsyscall */
#include <asm/vm86.h> /* struct vm86 */ #include <asm/vm86.h> /* struct vm86 */
#include <asm/mmu_context.h> /* vma_pkey() */ #include <asm/mmu_context.h> /* vma_pkey() */
#include <asm/efi.h> /* efi_recover_from_page_fault()*/
#define CREATE_TRACE_POINTS #define CREATE_TRACE_POINTS
#include <asm/trace/exceptions.h> #include <asm/trace/exceptions.h>
...@@ -788,6 +790,13 @@ no_context(struct pt_regs *regs, unsigned long error_code, ...@@ -788,6 +790,13 @@ no_context(struct pt_regs *regs, unsigned long error_code,
if (is_errata93(regs, address)) if (is_errata93(regs, address))
return; 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 * Oops. The kernel tried to access some bad page. We'll have to
* terminate things with extreme prejudice: * terminate things with extreme prejudice:
......
...@@ -26,12 +26,14 @@ static bool early_efi_keep; ...@@ -26,12 +26,14 @@ static bool early_efi_keep;
*/ */
static __init int early_efi_map_fb(void) static __init int early_efi_map_fb(void)
{ {
unsigned long base, size; u64 base, size;
if (!early_efi_keep) if (!early_efi_keep)
return 0; return 0;
base = boot_params.screen_info.lfb_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;
size = boot_params.screen_info.lfb_size; size = boot_params.screen_info.lfb_size;
efi_fb = ioremap(base, size); efi_fb = ioremap(base, size);
...@@ -46,9 +48,11 @@ early_initcall(early_efi_map_fb); ...@@ -46,9 +48,11 @@ early_initcall(early_efi_map_fb);
*/ */
static __ref void *early_efi_map(unsigned long start, unsigned long len) static __ref void *early_efi_map(unsigned long start, unsigned long len)
{ {
unsigned long base; u64 base;
base = boot_params.screen_info.lfb_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) if (efi_fb)
return (efi_fb + start); return (efi_fb + start);
......
...@@ -619,18 +619,16 @@ void __init efi_dump_pagetable(void) ...@@ -619,18 +619,16 @@ void __init efi_dump_pagetable(void)
/* /*
* Makes the calling thread switch to/from efi_mm context. Can be used * Makes the calling thread switch to/from efi_mm context. Can be used
* for SetVirtualAddressMap() i.e. current->active_mm == init_mm as well * in a kernel thread and user context. Preemption needs to remain disabled
* as during efi runtime calls i.e current->active_mm == current_mm. * while the EFI-mm is borrowed. mmgrab()/mmdrop() is not used because the mm
* We are not mm_dropping()/mm_grabbing() any mm, because we are not * can not change under us.
* losing/creating any references. * It should be ensured that there are no concurent calls to this function.
*/ */
void efi_switch_mm(struct mm_struct *mm) void efi_switch_mm(struct mm_struct *mm)
{ {
task_lock(current);
efi_scratch.prev_mm = current->active_mm; efi_scratch.prev_mm = current->active_mm;
current->active_mm = mm; current->active_mm = mm;
switch_mm(efi_scratch.prev_mm, mm, NULL); switch_mm(efi_scratch.prev_mm, mm, NULL);
task_unlock(current);
} }
#ifdef CONFIG_EFI_MIXED #ifdef CONFIG_EFI_MIXED
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <asm/efi.h> #include <asm/efi.h>
#include <asm/uv/uv.h> #include <asm/uv/uv.h>
#include <asm/cpu_device_id.h> #include <asm/cpu_device_id.h>
#include <asm/reboot.h>
#define EFI_MIN_RESERVE 5120 #define EFI_MIN_RESERVE 5120
...@@ -654,3 +655,80 @@ int efi_capsule_setup_info(struct capsule_info *cap_info, void *kbuff, ...@@ -654,3 +655,80 @@ int efi_capsule_setup_info(struct capsule_info *cap_info, void *kbuff,
} }
#endif #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 = { ...@@ -52,7 +52,8 @@ struct efi __read_mostly efi = {
.properties_table = EFI_INVALID_TABLE_ADDR, .properties_table = EFI_INVALID_TABLE_ADDR,
.mem_attr_table = EFI_INVALID_TABLE_ADDR, .mem_attr_table = EFI_INVALID_TABLE_ADDR,
.rng_seed = 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); EXPORT_SYMBOL(efi);
...@@ -484,6 +485,7 @@ static __initdata efi_config_table_type_t common_tables[] = { ...@@ -484,6 +485,7 @@ static __initdata efi_config_table_type_t common_tables[] = {
{EFI_MEMORY_ATTRIBUTES_TABLE_GUID, "MEMATTR", &efi.mem_attr_table}, {EFI_MEMORY_ATTRIBUTES_TABLE_GUID, "MEMATTR", &efi.mem_attr_table},
{LINUX_EFI_RANDOM_SEED_TABLE_GUID, "RNG", &efi.rng_seed}, {LINUX_EFI_RANDOM_SEED_TABLE_GUID, "RNG", &efi.rng_seed},
{LINUX_EFI_TPM_EVENT_LOG_GUID, "TPMEventLog", &efi.tpm_log}, {LINUX_EFI_TPM_EVENT_LOG_GUID, "TPMEventLog", &efi.tpm_log},
{LINUX_EFI_MEMRESERVE_TABLE_GUID, "MEMRESERVE", &efi.mem_reserve},
{NULL_GUID, NULL, NULL}, {NULL_GUID, NULL, NULL},
}; };
...@@ -591,6 +593,29 @@ int __init efi_config_parse_tables(void *config_tables, int count, int sz, ...@@ -591,6 +593,29 @@ int __init efi_config_parse_tables(void *config_tables, int count, int sz,
early_memunmap(tbl, sizeof(*tbl)); 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; return 0;
} }
...@@ -937,6 +962,38 @@ bool efi_is_table_address(unsigned long phys_addr) ...@@ -937,6 +962,38 @@ bool efi_is_table_address(unsigned long phys_addr)
return false; 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 #ifdef CONFIG_KEXEC
static int update_efi_random_seed(struct notifier_block *nb, static int update_efi_random_seed(struct notifier_block *nb,
unsigned long code, void *unused) unsigned long code, void *unused)
......
...@@ -16,7 +16,8 @@ cflags-$(CONFIG_X86) += -m$(BITS) -D__KERNEL__ -O2 \ ...@@ -16,7 +16,8 @@ cflags-$(CONFIG_X86) += -m$(BITS) -D__KERNEL__ -O2 \
cflags-$(CONFIG_ARM64) := $(subst -pg,,$(KBUILD_CFLAGS)) -fpie \ cflags-$(CONFIG_ARM64) := $(subst -pg,,$(KBUILD_CFLAGS)) -fpie \
$(DISABLE_STACKLEAK_PLUGIN) $(DISABLE_STACKLEAK_PLUGIN)
cflags-$(CONFIG_ARM) := $(subst -pg,,$(KBUILD_CFLAGS)) \ 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 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) ...@@ -69,6 +69,31 @@ static struct screen_info *setup_graphics(efi_system_table_t *sys_table_arg)
return si; 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 * This function handles the architcture specific differences between arm and
* arm64 regarding where the kernel image must be loaded and any memory that * 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, ...@@ -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; new_fdt_addr = fdt_addr;
status = allocate_new_fdt_and_exit_boot(sys_table, handle, status = allocate_new_fdt_and_exit_boot(sys_table, handle,
&new_fdt_addr, efi_get_max_fdt_addr(dram_base), &new_fdt_addr, efi_get_max_fdt_addr(dram_base),
......
...@@ -45,39 +45,7 @@ ...@@ -45,39 +45,7 @@
#define __efi_call_virt(f, args...) \ #define __efi_call_virt(f, args...) \
__efi_call_virt_pointer(efi.systab->runtime, f, args) __efi_call_virt_pointer(efi.systab->runtime, f, args)
/* efi_runtime_service() function identifiers */ struct efi_runtime_work efi_rts_work;
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;
};
/* /*
* efi_queue_work: Queue efi_runtime_service() and wait until it's done * efi_queue_work: Queue efi_runtime_service() and wait until it's done
...@@ -91,9 +59,13 @@ struct efi_runtime_work { ...@@ -91,9 +59,13 @@ struct efi_runtime_work {
*/ */
#define efi_queue_work(_rts, _arg1, _arg2, _arg3, _arg4, _arg5) \ #define efi_queue_work(_rts, _arg1, _arg2, _arg3, _arg4, _arg5) \
({ \ ({ \
struct efi_runtime_work efi_rts_work; \
efi_rts_work.status = EFI_ABORTED; \ 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_completion(&efi_rts_work.efi_rts_comp); \
INIT_WORK_ONSTACK(&efi_rts_work.work, efi_call_rts); \ INIT_WORK_ONSTACK(&efi_rts_work.work, efi_call_rts); \
efi_rts_work.arg1 = _arg1; \ efi_rts_work.arg1 = _arg1; \
...@@ -112,6 +84,8 @@ struct efi_runtime_work { ...@@ -112,6 +84,8 @@ struct efi_runtime_work {
else \ else \
pr_err("Failed to queue work to efi_rts_wq.\n"); \ pr_err("Failed to queue work to efi_rts_wq.\n"); \
\ \
exit: \
efi_rts_work.efi_rts_id = NONE; \
efi_rts_work.status; \ efi_rts_work.status; \
}) })
...@@ -184,18 +158,16 @@ static DEFINE_SEMAPHORE(efi_runtime_lock); ...@@ -184,18 +158,16 @@ static DEFINE_SEMAPHORE(efi_runtime_lock);
*/ */
static void efi_call_rts(struct work_struct *work) static void efi_call_rts(struct work_struct *work)
{ {
struct efi_runtime_work *efi_rts_work;
void *arg1, *arg2, *arg3, *arg4, *arg5; void *arg1, *arg2, *arg3, *arg4, *arg5;
efi_status_t status = EFI_NOT_FOUND; efi_status_t status = EFI_NOT_FOUND;
efi_rts_work = container_of(work, struct efi_runtime_work, work); arg1 = efi_rts_work.arg1;
arg1 = efi_rts_work->arg1; arg2 = efi_rts_work.arg2;
arg2 = efi_rts_work->arg2; arg3 = efi_rts_work.arg3;
arg3 = efi_rts_work->arg3; arg4 = efi_rts_work.arg4;
arg4 = efi_rts_work->arg4; arg5 = efi_rts_work.arg5;
arg5 = efi_rts_work->arg5;
switch (efi_rts_work->efi_rts_id) { switch (efi_rts_work.efi_rts_id) {
case GET_TIME: case GET_TIME:
status = efi_call_virt(get_time, (efi_time_t *)arg1, status = efi_call_virt(get_time, (efi_time_t *)arg1,
(efi_time_cap_t *)arg2); (efi_time_cap_t *)arg2);
...@@ -253,8 +225,8 @@ static void efi_call_rts(struct work_struct *work) ...@@ -253,8 +225,8 @@ static void efi_call_rts(struct work_struct *work)
*/ */
pr_err("Requested executing invalid EFI Runtime Service.\n"); pr_err("Requested executing invalid EFI Runtime Service.\n");
} }
efi_rts_work->status = status; efi_rts_work.status = status;
complete(&efi_rts_work->efi_rts_comp); complete(&efi_rts_work.efi_rts_comp);
} }
static efi_status_t virt_efi_get_time(efi_time_t *tm, efi_time_cap_t *tc) 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, ...@@ -428,6 +400,7 @@ static void virt_efi_reset_system(int reset_type,
"could not get exclusive access to the firmware\n"); "could not get exclusive access to the firmware\n");
return; return;
} }
efi_rts_work.efi_rts_id = RESET_SYSTEM;
__efi_call_virt(reset_system, reset_type, status, data_size, data); __efi_call_virt(reset_system, reset_type, status, data_size, data);
up(&efi_runtime_lock); up(&efi_runtime_lock);
} }
......
...@@ -542,6 +542,30 @@ static long efi_runtime_get_nexthighmonocount(unsigned long arg) ...@@ -542,6 +542,30 @@ static long efi_runtime_get_nexthighmonocount(unsigned long arg)
return 0; 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) static long efi_runtime_query_variableinfo(unsigned long arg)
{ {
struct efi_queryvariableinfo __user *queryvariableinfo_user; struct efi_queryvariableinfo __user *queryvariableinfo_user;
...@@ -682,6 +706,9 @@ static long efi_test_ioctl(struct file *file, unsigned int cmd, ...@@ -682,6 +706,9 @@ static long efi_test_ioctl(struct file *file, unsigned int cmd,
case EFI_RUNTIME_QUERY_CAPSULECAPABILITIES: case EFI_RUNTIME_QUERY_CAPSULECAPABILITIES:
return efi_runtime_query_capsulecaps(arg); return efi_runtime_query_capsulecaps(arg);
case EFI_RUNTIME_RESET_SYSTEM:
return efi_runtime_reset_system(arg);
} }
return -ENOTTY; return -ENOTTY;
......
...@@ -81,6 +81,13 @@ struct efi_querycapsulecapabilities { ...@@ -81,6 +81,13 @@ struct efi_querycapsulecapabilities {
efi_status_t *status; efi_status_t *status;
} __packed; } __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 \ #define EFI_RUNTIME_GET_VARIABLE \
_IOWR('p', 0x01, struct efi_getvariable) _IOWR('p', 0x01, struct efi_getvariable)
#define EFI_RUNTIME_SET_VARIABLE \ #define EFI_RUNTIME_SET_VARIABLE \
...@@ -108,4 +115,7 @@ struct efi_querycapsulecapabilities { ...@@ -108,4 +115,7 @@ struct efi_querycapsulecapabilities {
#define EFI_RUNTIME_QUERY_CAPSULECAPABILITIES \ #define EFI_RUNTIME_QUERY_CAPSULECAPABILITIES \
_IOR('p', 0x0A, struct efi_querycapsulecapabilities) _IOR('p', 0x0A, struct efi_querycapsulecapabilities)
#define EFI_RUNTIME_RESET_SYSTEM \
_IOW('p', 0x0B, struct efi_resetsystem)
#endif /* _DRIVERS_FIRMWARE_EFI_TEST_H_ */ #endif /* _DRIVERS_FIRMWARE_EFI_TEST_H_ */
...@@ -672,6 +672,7 @@ void efi_native_runtime_setup(void); ...@@ -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_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_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_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 { typedef struct {
efi_guid_t guid; efi_guid_t guid;
...@@ -957,6 +958,7 @@ extern struct efi { ...@@ -957,6 +958,7 @@ extern struct efi {
unsigned long mem_attr_table; /* memory attributes table */ unsigned long mem_attr_table; /* memory attributes table */
unsigned long rng_seed; /* UEFI firmware random seed */ unsigned long rng_seed; /* UEFI firmware random seed */
unsigned long tpm_log; /* TPM2 Event Log table */ unsigned long tpm_log; /* TPM2 Event Log table */
unsigned long mem_reserve; /* Linux EFI memreserve table */
efi_get_time_t *get_time; efi_get_time_t *get_time;
efi_set_time_t *set_time; efi_set_time_t *set_time;
efi_get_wakeup_time_t *get_wakeup_time; efi_get_wakeup_time_t *get_wakeup_time;
...@@ -1041,6 +1043,7 @@ extern int __init efi_uart_console_only (void); ...@@ -1041,6 +1043,7 @@ extern int __init efi_uart_console_only (void);
extern u64 efi_mem_desc_end(efi_memory_desc_t *md); 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 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 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, extern void efi_initialize_iomem_resources(struct resource *code_resource,
struct resource *data_resource, struct resource *bss_resource); struct resource *data_resource, struct resource *bss_resource);
extern void efi_reserve_boot_services(void); extern void efi_reserve_boot_services(void);
...@@ -1659,7 +1662,55 @@ struct linux_efi_tpm_eventlog { ...@@ -1659,7 +1662,55 @@ struct linux_efi_tpm_eventlog {
extern int efi_tpm_eventlog_init(void); 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 */ /* Workqueue to queue EFI Runtime Services */
extern struct workqueue_struct *efi_rts_wq; 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 */ #endif /* _LINUX_EFI_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment