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:
@@ -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_ */
|
||||
|
Reference in New Issue
Block a user