Merge branch 'efi-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull EFI updates from Ingo Molnar: "The main changes in this cycle were: - Rework the EFI capsule loader to allow for workarounds for non-compliant firmware (Ard Biesheuvel) - Implement a capsule loader quirk for Quark X102x (Jan Kiszka) - Enable SMBIOS/DMI support for the ARM architecture (Ard Biesheuvel) - Add CONFIG_EFI_PGT_DUMP=y support for x86-32 and kexec (Sai Praneeth) - Fixes for EFI support for Xen dom0 guests running under x86-64 hosts (Daniel Kiper)" * 'efi-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: x86/xen/efi: Initialize only the EFI struct members used by Xen efi: Process the MEMATTR table only if EFI_MEMMAP is enabled efi/arm: Enable DMI/SMBIOS x86/efi: Extend CONFIG_EFI_PGT_DUMP support to x86_32 and kexec as well efi/efi_test: Use memdup_user() helper efi/capsule: Add support for Quark security header efi/capsule-loader: Use page addresses rather than struct page pointers efi/capsule-loader: Redirect calls to efi_capsule_setup_info() via weak alias efi/capsule: Remove NULL test on kmap() efi/capsule-loader: Use a cached copy of the capsule header efi/capsule: Adjust return type of efi_capsule_setup_info() efi/capsule: Clean up pr_err/_info() messages efi/capsule: Remove pr_debug() on ENOMEM or EFAULT efi/capsule: Fix return code on failing kmap/vmap
This commit is contained in:
commit
162b246eb4
|
@ -2062,6 +2062,23 @@ config EFI
|
||||||
is only useful for kernels that may run on systems that have
|
is only useful for kernels that may run on systems that have
|
||||||
UEFI firmware.
|
UEFI firmware.
|
||||||
|
|
||||||
|
config DMI
|
||||||
|
bool "Enable support for SMBIOS (DMI) tables"
|
||||||
|
depends on EFI
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
This enables SMBIOS/DMI feature for systems.
|
||||||
|
|
||||||
|
This option is only useful on systems that have UEFI firmware.
|
||||||
|
However, even with this option, the resultant kernel should
|
||||||
|
continue to boot on existing non-UEFI platforms.
|
||||||
|
|
||||||
|
NOTE: This does *NOT* enable or encourage the use of DMI quirks,
|
||||||
|
i.e., the the practice of identifying the platform via DMI to
|
||||||
|
decide whether certain workarounds for buggy hardware and/or
|
||||||
|
firmware need to be enabled. This would require the DMI subsystem
|
||||||
|
to be enabled much earlier than we do on ARM, which is non-trivial.
|
||||||
|
|
||||||
endmenu
|
endmenu
|
||||||
|
|
||||||
menu "CPU Power Management"
|
menu "CPU Power Management"
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __ASM_DMI_H
|
||||||
|
#define __ASM_DMI_H
|
||||||
|
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
|
||||||
|
#define dmi_early_remap(x, l) memremap(x, l, MEMREMAP_WB)
|
||||||
|
#define dmi_early_unmap(x, l) memunmap(x)
|
||||||
|
#define dmi_remap(x, l) memremap(x, l, MEMREMAP_WB)
|
||||||
|
#define dmi_unmap(x) memunmap(x)
|
||||||
|
#define dmi_alloc(l) kzalloc(l, GFP_KERNEL)
|
||||||
|
|
||||||
|
#endif
|
|
@ -11,7 +11,6 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/dmi.h>
|
|
||||||
#include <linux/efi.h>
|
#include <linux/efi.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
|
|
||||||
|
@ -117,20 +116,6 @@ int __init efi_set_mapping_permissions(struct mm_struct *mm,
|
||||||
set_permissions, md);
|
set_permissions, md);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __init arm64_dmi_init(void)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* On arm64, DMI depends on UEFI, and dmi_scan_machine() needs to
|
|
||||||
* be called early because dmi_id_init(), which is an arch_initcall
|
|
||||||
* itself, depends on dmi_scan_machine() having been called already.
|
|
||||||
*/
|
|
||||||
dmi_scan_machine();
|
|
||||||
if (dmi_available)
|
|
||||||
dmi_set_dump_stack_arch_desc();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
core_initcall(arm64_dmi_init);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* UpdateCapsule() depends on the system being shutdown via
|
* UpdateCapsule() depends on the system being shutdown via
|
||||||
* ResetSystem().
|
* ResetSystem().
|
||||||
|
|
|
@ -1014,7 +1014,6 @@ static void __init __efi_enter_virtual_mode(void)
|
||||||
* necessary relocation fixups for the new virtual addresses.
|
* necessary relocation fixups for the new virtual addresses.
|
||||||
*/
|
*/
|
||||||
efi_runtime_update_mappings();
|
efi_runtime_update_mappings();
|
||||||
efi_dump_pagetable();
|
|
||||||
|
|
||||||
/* clean DUMMY object */
|
/* clean DUMMY object */
|
||||||
efi_delete_dummy_variable();
|
efi_delete_dummy_variable();
|
||||||
|
@ -1029,6 +1028,8 @@ void __init efi_enter_virtual_mode(void)
|
||||||
kexec_enter_virtual_mode();
|
kexec_enter_virtual_mode();
|
||||||
else
|
else
|
||||||
__efi_enter_virtual_mode();
|
__efi_enter_virtual_mode();
|
||||||
|
|
||||||
|
efi_dump_pagetable();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -44,7 +44,14 @@ int __init efi_alloc_page_tables(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
void efi_sync_low_kernel_mappings(void) {}
|
void efi_sync_low_kernel_mappings(void) {}
|
||||||
void __init efi_dump_pagetable(void) {}
|
|
||||||
|
void __init efi_dump_pagetable(void)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_EFI_PGT_DUMP
|
||||||
|
ptdump_walk_pgd_level(NULL, swapper_pg_dir);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
int __init efi_setup_page_tables(unsigned long pa_memmap, unsigned num_pages)
|
int __init efi_setup_page_tables(unsigned long pa_memmap, unsigned num_pages)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -589,7 +589,10 @@ void __init efi_runtime_update_mappings(void)
|
||||||
void __init efi_dump_pagetable(void)
|
void __init efi_dump_pagetable(void)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_EFI_PGT_DUMP
|
#ifdef CONFIG_EFI_PGT_DUMP
|
||||||
ptdump_walk_pgd_level(NULL, efi_pgd);
|
if (efi_enabled(EFI_OLD_MEMMAP))
|
||||||
|
ptdump_walk_pgd_level(NULL, swapper_pg_dir);
|
||||||
|
else
|
||||||
|
ptdump_walk_pgd_level(NULL, efi_pgd);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,12 +15,66 @@
|
||||||
#include <asm/e820/api.h>
|
#include <asm/e820/api.h>
|
||||||
#include <asm/efi.h>
|
#include <asm/efi.h>
|
||||||
#include <asm/uv/uv.h>
|
#include <asm/uv/uv.h>
|
||||||
|
#include <asm/cpu_device_id.h>
|
||||||
|
|
||||||
#define EFI_MIN_RESERVE 5120
|
#define EFI_MIN_RESERVE 5120
|
||||||
|
|
||||||
#define EFI_DUMMY_GUID \
|
#define EFI_DUMMY_GUID \
|
||||||
EFI_GUID(0x4424ac57, 0xbe4b, 0x47dd, 0x9e, 0x97, 0xed, 0x50, 0xf0, 0x9f, 0x92, 0xa9)
|
EFI_GUID(0x4424ac57, 0xbe4b, 0x47dd, 0x9e, 0x97, 0xed, 0x50, 0xf0, 0x9f, 0x92, 0xa9)
|
||||||
|
|
||||||
|
#define QUARK_CSH_SIGNATURE 0x5f435348 /* _CSH */
|
||||||
|
#define QUARK_SECURITY_HEADER_SIZE 0x400
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Header prepended to the standard EFI capsule on Quark systems the are based
|
||||||
|
* on Intel firmware BSP.
|
||||||
|
* @csh_signature: Unique identifier to sanity check signed module
|
||||||
|
* presence ("_CSH").
|
||||||
|
* @version: Current version of CSH used. Should be one for Quark A0.
|
||||||
|
* @modulesize: Size of the entire module including the module header
|
||||||
|
* and payload.
|
||||||
|
* @security_version_number_index: Index of SVN to use for validation of signed
|
||||||
|
* module.
|
||||||
|
* @security_version_number: Used to prevent against roll back of modules.
|
||||||
|
* @rsvd_module_id: Currently unused for Clanton (Quark).
|
||||||
|
* @rsvd_module_vendor: Vendor Identifier. For Intel products value is
|
||||||
|
* 0x00008086.
|
||||||
|
* @rsvd_date: BCD representation of build date as yyyymmdd, where
|
||||||
|
* yyyy=4 digit year, mm=1-12, dd=1-31.
|
||||||
|
* @headersize: Total length of the header including including any
|
||||||
|
* padding optionally added by the signing tool.
|
||||||
|
* @hash_algo: What Hash is used in the module signing.
|
||||||
|
* @cryp_algo: What Crypto is used in the module signing.
|
||||||
|
* @keysize: Total length of the key data including including any
|
||||||
|
* padding optionally added by the signing tool.
|
||||||
|
* @signaturesize: Total length of the signature including including any
|
||||||
|
* padding optionally added by the signing tool.
|
||||||
|
* @rsvd_next_header: 32-bit pointer to the next Secure Boot Module in the
|
||||||
|
* chain, if there is a next header.
|
||||||
|
* @rsvd: Reserved, padding structure to required size.
|
||||||
|
*
|
||||||
|
* See also QuartSecurityHeader_t in
|
||||||
|
* Quark_EDKII_v1.2.1.1/QuarkPlatformPkg/Include/QuarkBootRom.h
|
||||||
|
* from https://downloadcenter.intel.com/download/23197/Intel-Quark-SoC-X1000-Board-Support-Package-BSP
|
||||||
|
*/
|
||||||
|
struct quark_security_header {
|
||||||
|
u32 csh_signature;
|
||||||
|
u32 version;
|
||||||
|
u32 modulesize;
|
||||||
|
u32 security_version_number_index;
|
||||||
|
u32 security_version_number;
|
||||||
|
u32 rsvd_module_id;
|
||||||
|
u32 rsvd_module_vendor;
|
||||||
|
u32 rsvd_date;
|
||||||
|
u32 headersize;
|
||||||
|
u32 hash_algo;
|
||||||
|
u32 cryp_algo;
|
||||||
|
u32 keysize;
|
||||||
|
u32 signaturesize;
|
||||||
|
u32 rsvd_next_header;
|
||||||
|
u32 rsvd[2];
|
||||||
|
};
|
||||||
|
|
||||||
static efi_char16_t efi_dummy_name[6] = { 'D', 'U', 'M', 'M', 'Y', 0 };
|
static efi_char16_t efi_dummy_name[6] = { 'D', 'U', 'M', 'M', 'Y', 0 };
|
||||||
|
|
||||||
static bool efi_no_storage_paranoia;
|
static bool efi_no_storage_paranoia;
|
||||||
|
@ -504,3 +558,86 @@ bool efi_poweroff_required(void)
|
||||||
{
|
{
|
||||||
return acpi_gbl_reduced_hardware || acpi_no_s5;
|
return acpi_gbl_reduced_hardware || acpi_no_s5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_EFI_CAPSULE_QUIRK_QUARK_CSH
|
||||||
|
|
||||||
|
static int qrk_capsule_setup_info(struct capsule_info *cap_info, void **pkbuff,
|
||||||
|
size_t hdr_bytes)
|
||||||
|
{
|
||||||
|
struct quark_security_header *csh = *pkbuff;
|
||||||
|
|
||||||
|
/* Only process data block that is larger than the security header */
|
||||||
|
if (hdr_bytes < sizeof(struct quark_security_header))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (csh->csh_signature != QUARK_CSH_SIGNATURE ||
|
||||||
|
csh->headersize != QUARK_SECURITY_HEADER_SIZE)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/* Only process data block if EFI header is included */
|
||||||
|
if (hdr_bytes < QUARK_SECURITY_HEADER_SIZE +
|
||||||
|
sizeof(efi_capsule_header_t))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
pr_debug("Quark security header detected\n");
|
||||||
|
|
||||||
|
if (csh->rsvd_next_header != 0) {
|
||||||
|
pr_err("multiple Quark security headers not supported\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
*pkbuff += csh->headersize;
|
||||||
|
cap_info->total_size = csh->headersize;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update the first page pointer to skip over the CSH header.
|
||||||
|
*/
|
||||||
|
cap_info->pages[0] += csh->headersize;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ICPU(family, model, quirk_handler) \
|
||||||
|
{ X86_VENDOR_INTEL, family, model, X86_FEATURE_ANY, \
|
||||||
|
(unsigned long)&quirk_handler }
|
||||||
|
|
||||||
|
static const struct x86_cpu_id efi_capsule_quirk_ids[] = {
|
||||||
|
ICPU(5, 9, qrk_capsule_setup_info), /* Intel Quark X1000 */
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
int efi_capsule_setup_info(struct capsule_info *cap_info, void *kbuff,
|
||||||
|
size_t hdr_bytes)
|
||||||
|
{
|
||||||
|
int (*quirk_handler)(struct capsule_info *, void **, size_t);
|
||||||
|
const struct x86_cpu_id *id;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (hdr_bytes < sizeof(efi_capsule_header_t))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
cap_info->total_size = 0;
|
||||||
|
|
||||||
|
id = x86_match_cpu(efi_capsule_quirk_ids);
|
||||||
|
if (id) {
|
||||||
|
/*
|
||||||
|
* The quirk handler is supposed to return
|
||||||
|
* - a value > 0 if the setup should continue, after advancing
|
||||||
|
* kbuff as needed
|
||||||
|
* - 0 if not enough hdr_bytes are available yet
|
||||||
|
* - a negative error code otherwise
|
||||||
|
*/
|
||||||
|
quirk_handler = (typeof(quirk_handler))id->driver_data;
|
||||||
|
ret = quirk_handler(cap_info, &kbuff, hdr_bytes);
|
||||||
|
if (ret <= 0)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(&cap_info->header, kbuff, sizeof(cap_info->header));
|
||||||
|
|
||||||
|
cap_info->total_size += cap_info->header.imagesize;
|
||||||
|
|
||||||
|
return __efi_capsule_setup_info(cap_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -54,38 +54,6 @@ static efi_system_table_t efi_systab_xen __initdata = {
|
||||||
.tables = EFI_INVALID_TABLE_ADDR /* Initialized later. */
|
.tables = EFI_INVALID_TABLE_ADDR /* Initialized later. */
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct efi efi_xen __initconst = {
|
|
||||||
.systab = NULL, /* Initialized later. */
|
|
||||||
.runtime_version = 0, /* Initialized later. */
|
|
||||||
.mps = EFI_INVALID_TABLE_ADDR,
|
|
||||||
.acpi = EFI_INVALID_TABLE_ADDR,
|
|
||||||
.acpi20 = EFI_INVALID_TABLE_ADDR,
|
|
||||||
.smbios = EFI_INVALID_TABLE_ADDR,
|
|
||||||
.smbios3 = EFI_INVALID_TABLE_ADDR,
|
|
||||||
.sal_systab = EFI_INVALID_TABLE_ADDR,
|
|
||||||
.boot_info = EFI_INVALID_TABLE_ADDR,
|
|
||||||
.hcdp = EFI_INVALID_TABLE_ADDR,
|
|
||||||
.uga = EFI_INVALID_TABLE_ADDR,
|
|
||||||
.uv_systab = EFI_INVALID_TABLE_ADDR,
|
|
||||||
.fw_vendor = EFI_INVALID_TABLE_ADDR,
|
|
||||||
.runtime = EFI_INVALID_TABLE_ADDR,
|
|
||||||
.config_table = EFI_INVALID_TABLE_ADDR,
|
|
||||||
.get_time = xen_efi_get_time,
|
|
||||||
.set_time = xen_efi_set_time,
|
|
||||||
.get_wakeup_time = xen_efi_get_wakeup_time,
|
|
||||||
.set_wakeup_time = xen_efi_set_wakeup_time,
|
|
||||||
.get_variable = xen_efi_get_variable,
|
|
||||||
.get_next_variable = xen_efi_get_next_variable,
|
|
||||||
.set_variable = xen_efi_set_variable,
|
|
||||||
.query_variable_info = xen_efi_query_variable_info,
|
|
||||||
.update_capsule = xen_efi_update_capsule,
|
|
||||||
.query_capsule_caps = xen_efi_query_capsule_caps,
|
|
||||||
.get_next_high_mono_count = xen_efi_get_next_high_mono_count,
|
|
||||||
.reset_system = xen_efi_reset_system,
|
|
||||||
.set_virtual_address_map = NULL, /* Not used under Xen. */
|
|
||||||
.flags = 0 /* Initialized later. */
|
|
||||||
};
|
|
||||||
|
|
||||||
static efi_system_table_t __init *xen_efi_probe(void)
|
static efi_system_table_t __init *xen_efi_probe(void)
|
||||||
{
|
{
|
||||||
struct xen_platform_op op = {
|
struct xen_platform_op op = {
|
||||||
|
@ -102,7 +70,18 @@ static efi_system_table_t __init *xen_efi_probe(void)
|
||||||
|
|
||||||
/* Here we know that Xen runs on EFI platform. */
|
/* Here we know that Xen runs on EFI platform. */
|
||||||
|
|
||||||
efi = efi_xen;
|
efi.get_time = xen_efi_get_time;
|
||||||
|
efi.set_time = xen_efi_set_time;
|
||||||
|
efi.get_wakeup_time = xen_efi_get_wakeup_time;
|
||||||
|
efi.set_wakeup_time = xen_efi_set_wakeup_time;
|
||||||
|
efi.get_variable = xen_efi_get_variable;
|
||||||
|
efi.get_next_variable = xen_efi_get_next_variable;
|
||||||
|
efi.set_variable = xen_efi_set_variable;
|
||||||
|
efi.query_variable_info = xen_efi_query_variable_info;
|
||||||
|
efi.update_capsule = xen_efi_update_capsule;
|
||||||
|
efi.query_capsule_caps = xen_efi_query_capsule_caps;
|
||||||
|
efi.get_next_high_mono_count = xen_efi_get_next_high_mono_count;
|
||||||
|
efi.reset_system = xen_efi_reset_system;
|
||||||
|
|
||||||
efi_systab_xen.tables = info->cfg.addr;
|
efi_systab_xen.tables = info->cfg.addr;
|
||||||
efi_systab_xen.nr_tables = info->cfg.nent;
|
efi_systab_xen.nr_tables = info->cfg.nent;
|
||||||
|
|
|
@ -112,6 +112,15 @@ config EFI_CAPSULE_LOADER
|
||||||
|
|
||||||
Most users should say N.
|
Most users should say N.
|
||||||
|
|
||||||
|
config EFI_CAPSULE_QUIRK_QUARK_CSH
|
||||||
|
boolean "Add support for Quark capsules with non-standard headers"
|
||||||
|
depends on X86 && !64BIT
|
||||||
|
select EFI_CAPSULE_LOADER
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
Add support for processing Quark X1000 EFI capsules, whose header
|
||||||
|
layout deviates from the layout mandated by the UEFI specification.
|
||||||
|
|
||||||
config EFI_TEST
|
config EFI_TEST
|
||||||
tristate "EFI Runtime Service Tests Support"
|
tristate "EFI Runtime Service Tests Support"
|
||||||
depends on EFI
|
depends on EFI
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/dmi.h>
|
||||||
#include <linux/efi.h>
|
#include <linux/efi.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/memblock.h>
|
#include <linux/memblock.h>
|
||||||
|
@ -166,3 +167,18 @@ void efi_virtmap_unload(void)
|
||||||
efi_set_pgd(current->active_mm);
|
efi_set_pgd(current->active_mm);
|
||||||
preempt_enable();
|
preempt_enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int __init arm_dmi_init(void)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* On arm64/ARM, DMI depends on UEFI, and dmi_scan_machine() needs to
|
||||||
|
* be called early because dmi_id_init(), which is an arch_initcall
|
||||||
|
* itself, depends on dmi_scan_machine() having been called already.
|
||||||
|
*/
|
||||||
|
dmi_scan_machine();
|
||||||
|
if (dmi_available)
|
||||||
|
dmi_set_dump_stack_arch_desc();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
core_initcall(arm_dmi_init);
|
||||||
|
|
|
@ -20,15 +20,9 @@
|
||||||
|
|
||||||
#define NO_FURTHER_WRITE_ACTION -1
|
#define NO_FURTHER_WRITE_ACTION -1
|
||||||
|
|
||||||
struct capsule_info {
|
#ifndef phys_to_page
|
||||||
bool header_obtained;
|
#define phys_to_page(x) pfn_to_page((x) >> PAGE_SHIFT)
|
||||||
int reset_type;
|
#endif
|
||||||
long index;
|
|
||||||
size_t count;
|
|
||||||
size_t total_size;
|
|
||||||
struct page **pages;
|
|
||||||
size_t page_bytes_remain;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* efi_free_all_buff_pages - free all previous allocated buffer pages
|
* efi_free_all_buff_pages - free all previous allocated buffer pages
|
||||||
|
@ -41,62 +35,67 @@ struct capsule_info {
|
||||||
static void efi_free_all_buff_pages(struct capsule_info *cap_info)
|
static void efi_free_all_buff_pages(struct capsule_info *cap_info)
|
||||||
{
|
{
|
||||||
while (cap_info->index > 0)
|
while (cap_info->index > 0)
|
||||||
__free_page(cap_info->pages[--cap_info->index]);
|
__free_page(phys_to_page(cap_info->pages[--cap_info->index]));
|
||||||
|
|
||||||
cap_info->index = NO_FURTHER_WRITE_ACTION;
|
cap_info->index = NO_FURTHER_WRITE_ACTION;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int __efi_capsule_setup_info(struct capsule_info *cap_info)
|
||||||
|
{
|
||||||
|
size_t pages_needed;
|
||||||
|
int ret;
|
||||||
|
void *temp_page;
|
||||||
|
|
||||||
|
pages_needed = ALIGN(cap_info->total_size, PAGE_SIZE) / PAGE_SIZE;
|
||||||
|
|
||||||
|
if (pages_needed == 0) {
|
||||||
|
pr_err("invalid capsule size");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if the capsule binary supported */
|
||||||
|
ret = efi_capsule_supported(cap_info->header.guid,
|
||||||
|
cap_info->header.flags,
|
||||||
|
cap_info->header.imagesize,
|
||||||
|
&cap_info->reset_type);
|
||||||
|
if (ret) {
|
||||||
|
pr_err("capsule not supported\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
temp_page = krealloc(cap_info->pages,
|
||||||
|
pages_needed * sizeof(void *),
|
||||||
|
GFP_KERNEL | __GFP_ZERO);
|
||||||
|
if (!temp_page)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
cap_info->pages = temp_page;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* efi_capsule_setup_info - obtain the efi capsule header in the binary and
|
* efi_capsule_setup_info - obtain the efi capsule header in the binary and
|
||||||
* setup capsule_info structure
|
* setup capsule_info structure
|
||||||
* @cap_info: pointer to current instance of capsule_info structure
|
* @cap_info: pointer to current instance of capsule_info structure
|
||||||
* @kbuff: a mapped first page buffer pointer
|
* @kbuff: a mapped first page buffer pointer
|
||||||
* @hdr_bytes: the total received number of bytes for efi header
|
* @hdr_bytes: the total received number of bytes for efi header
|
||||||
|
*
|
||||||
|
* Platforms with non-standard capsule update mechanisms can override
|
||||||
|
* this __weak function so they can perform any required capsule
|
||||||
|
* image munging. See quark_quirk_function() for an example.
|
||||||
**/
|
**/
|
||||||
static ssize_t efi_capsule_setup_info(struct capsule_info *cap_info,
|
int __weak efi_capsule_setup_info(struct capsule_info *cap_info, void *kbuff,
|
||||||
void *kbuff, size_t hdr_bytes)
|
size_t hdr_bytes)
|
||||||
{
|
{
|
||||||
efi_capsule_header_t *cap_hdr;
|
|
||||||
size_t pages_needed;
|
|
||||||
int ret;
|
|
||||||
void *temp_page;
|
|
||||||
|
|
||||||
/* Only process data block that is larger than efi header size */
|
/* Only process data block that is larger than efi header size */
|
||||||
if (hdr_bytes < sizeof(efi_capsule_header_t))
|
if (hdr_bytes < sizeof(efi_capsule_header_t))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* Reset back to the correct offset of header */
|
memcpy(&cap_info->header, kbuff, sizeof(cap_info->header));
|
||||||
cap_hdr = kbuff - cap_info->count;
|
cap_info->total_size = cap_info->header.imagesize;
|
||||||
pages_needed = ALIGN(cap_hdr->imagesize, PAGE_SIZE) >> PAGE_SHIFT;
|
|
||||||
|
|
||||||
if (pages_needed == 0) {
|
return __efi_capsule_setup_info(cap_info);
|
||||||
pr_err("%s: pages count invalid\n", __func__);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if the capsule binary supported */
|
|
||||||
ret = efi_capsule_supported(cap_hdr->guid, cap_hdr->flags,
|
|
||||||
cap_hdr->imagesize,
|
|
||||||
&cap_info->reset_type);
|
|
||||||
if (ret) {
|
|
||||||
pr_err("%s: efi_capsule_supported() failed\n",
|
|
||||||
__func__);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
cap_info->total_size = cap_hdr->imagesize;
|
|
||||||
temp_page = krealloc(cap_info->pages,
|
|
||||||
pages_needed * sizeof(void *),
|
|
||||||
GFP_KERNEL | __GFP_ZERO);
|
|
||||||
if (!temp_page) {
|
|
||||||
pr_debug("%s: krealloc() failed\n", __func__);
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
cap_info->pages = temp_page;
|
|
||||||
cap_info->header_obtained = true;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -107,26 +106,17 @@ static ssize_t efi_capsule_setup_info(struct capsule_info *cap_info,
|
||||||
static ssize_t efi_capsule_submit_update(struct capsule_info *cap_info)
|
static ssize_t efi_capsule_submit_update(struct capsule_info *cap_info)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
void *cap_hdr_temp;
|
|
||||||
|
|
||||||
cap_hdr_temp = vmap(cap_info->pages, cap_info->index,
|
ret = efi_capsule_update(&cap_info->header, cap_info->pages);
|
||||||
VM_MAP, PAGE_KERNEL);
|
|
||||||
if (!cap_hdr_temp) {
|
|
||||||
pr_debug("%s: vmap() failed\n", __func__);
|
|
||||||
return -EFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = efi_capsule_update(cap_hdr_temp, cap_info->pages);
|
|
||||||
vunmap(cap_hdr_temp);
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
pr_err("%s: efi_capsule_update() failed\n", __func__);
|
pr_err("capsule update failed\n");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Indicate capsule binary uploading is done */
|
/* Indicate capsule binary uploading is done */
|
||||||
cap_info->index = NO_FURTHER_WRITE_ACTION;
|
cap_info->index = NO_FURTHER_WRITE_ACTION;
|
||||||
pr_info("%s: Successfully upload capsule file with reboot type '%s'\n",
|
pr_info("Successfully upload capsule file with reboot type '%s'\n",
|
||||||
__func__, !cap_info->reset_type ? "RESET_COLD" :
|
!cap_info->reset_type ? "RESET_COLD" :
|
||||||
cap_info->reset_type == 1 ? "RESET_WARM" :
|
cap_info->reset_type == 1 ? "RESET_WARM" :
|
||||||
"RESET_SHUTDOWN");
|
"RESET_SHUTDOWN");
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -171,37 +161,30 @@ static ssize_t efi_capsule_write(struct file *file, const char __user *buff,
|
||||||
if (!cap_info->page_bytes_remain) {
|
if (!cap_info->page_bytes_remain) {
|
||||||
page = alloc_page(GFP_KERNEL);
|
page = alloc_page(GFP_KERNEL);
|
||||||
if (!page) {
|
if (!page) {
|
||||||
pr_debug("%s: alloc_page() failed\n", __func__);
|
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto failed;
|
goto failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
cap_info->pages[cap_info->index++] = page;
|
cap_info->pages[cap_info->index++] = page_to_phys(page);
|
||||||
cap_info->page_bytes_remain = PAGE_SIZE;
|
cap_info->page_bytes_remain = PAGE_SIZE;
|
||||||
|
} else {
|
||||||
|
page = phys_to_page(cap_info->pages[cap_info->index - 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
page = cap_info->pages[cap_info->index - 1];
|
|
||||||
|
|
||||||
kbuff = kmap(page);
|
kbuff = kmap(page);
|
||||||
if (!kbuff) {
|
|
||||||
pr_debug("%s: kmap() failed\n", __func__);
|
|
||||||
ret = -EFAULT;
|
|
||||||
goto failed;
|
|
||||||
}
|
|
||||||
kbuff += PAGE_SIZE - cap_info->page_bytes_remain;
|
kbuff += PAGE_SIZE - cap_info->page_bytes_remain;
|
||||||
|
|
||||||
/* Copy capsule binary data from user space to kernel space buffer */
|
/* Copy capsule binary data from user space to kernel space buffer */
|
||||||
write_byte = min_t(size_t, count, cap_info->page_bytes_remain);
|
write_byte = min_t(size_t, count, cap_info->page_bytes_remain);
|
||||||
if (copy_from_user(kbuff, buff, write_byte)) {
|
if (copy_from_user(kbuff, buff, write_byte)) {
|
||||||
pr_debug("%s: copy_from_user() failed\n", __func__);
|
|
||||||
ret = -EFAULT;
|
ret = -EFAULT;
|
||||||
goto fail_unmap;
|
goto fail_unmap;
|
||||||
}
|
}
|
||||||
cap_info->page_bytes_remain -= write_byte;
|
cap_info->page_bytes_remain -= write_byte;
|
||||||
|
|
||||||
/* Setup capsule binary info structure */
|
/* Setup capsule binary info structure */
|
||||||
if (!cap_info->header_obtained) {
|
if (cap_info->header.headersize == 0) {
|
||||||
ret = efi_capsule_setup_info(cap_info, kbuff,
|
ret = efi_capsule_setup_info(cap_info, kbuff - cap_info->count,
|
||||||
cap_info->count + write_byte);
|
cap_info->count + write_byte);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto fail_unmap;
|
goto fail_unmap;
|
||||||
|
@ -211,11 +194,10 @@ static ssize_t efi_capsule_write(struct file *file, const char __user *buff,
|
||||||
kunmap(page);
|
kunmap(page);
|
||||||
|
|
||||||
/* Submit the full binary to efi_capsule_update() API */
|
/* Submit the full binary to efi_capsule_update() API */
|
||||||
if (cap_info->header_obtained &&
|
if (cap_info->header.headersize > 0 &&
|
||||||
cap_info->count >= cap_info->total_size) {
|
cap_info->count >= cap_info->total_size) {
|
||||||
if (cap_info->count > cap_info->total_size) {
|
if (cap_info->count > cap_info->total_size) {
|
||||||
pr_err("%s: upload size exceeded header defined size\n",
|
pr_err("capsule upload size exceeded header defined size\n");
|
||||||
__func__);
|
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto failed;
|
goto failed;
|
||||||
}
|
}
|
||||||
|
@ -249,7 +231,7 @@ static int efi_capsule_flush(struct file *file, fl_owner_t id)
|
||||||
struct capsule_info *cap_info = file->private_data;
|
struct capsule_info *cap_info = file->private_data;
|
||||||
|
|
||||||
if (cap_info->index > 0) {
|
if (cap_info->index > 0) {
|
||||||
pr_err("%s: capsule upload not complete\n", __func__);
|
pr_err("capsule upload not complete\n");
|
||||||
efi_free_all_buff_pages(cap_info);
|
efi_free_all_buff_pages(cap_info);
|
||||||
ret = -ECANCELED;
|
ret = -ECANCELED;
|
||||||
}
|
}
|
||||||
|
@ -328,8 +310,7 @@ static int __init efi_capsule_loader_init(void)
|
||||||
|
|
||||||
ret = misc_register(&efi_capsule_misc);
|
ret = misc_register(&efi_capsule_misc);
|
||||||
if (ret)
|
if (ret)
|
||||||
pr_err("%s: Failed to register misc char file note\n",
|
pr_err("Unable to register capsule loader device\n");
|
||||||
__func__);
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -214,7 +214,7 @@ efi_capsule_update_locked(efi_capsule_header_t *capsule,
|
||||||
*
|
*
|
||||||
* Return 0 on success, a converted EFI status code on failure.
|
* Return 0 on success, a converted EFI status code on failure.
|
||||||
*/
|
*/
|
||||||
int efi_capsule_update(efi_capsule_header_t *capsule, struct page **pages)
|
int efi_capsule_update(efi_capsule_header_t *capsule, phys_addr_t *pages)
|
||||||
{
|
{
|
||||||
u32 imagesize = capsule->imagesize;
|
u32 imagesize = capsule->imagesize;
|
||||||
efi_guid_t guid = capsule->guid;
|
efi_guid_t guid = capsule->guid;
|
||||||
|
@ -247,16 +247,13 @@ int efi_capsule_update(efi_capsule_header_t *capsule, struct page **pages)
|
||||||
efi_capsule_block_desc_t *sglist;
|
efi_capsule_block_desc_t *sglist;
|
||||||
|
|
||||||
sglist = kmap(sg_pages[i]);
|
sglist = kmap(sg_pages[i]);
|
||||||
if (!sglist) {
|
|
||||||
rv = -ENOMEM;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (j = 0; j < SGLIST_PER_PAGE && count > 0; j++) {
|
for (j = 0; j < SGLIST_PER_PAGE && count > 0; j++) {
|
||||||
u64 sz = min_t(u64, imagesize, PAGE_SIZE);
|
u64 sz = min_t(u64, imagesize,
|
||||||
|
PAGE_SIZE - (u64)*pages % PAGE_SIZE);
|
||||||
|
|
||||||
sglist[j].length = sz;
|
sglist[j].length = sz;
|
||||||
sglist[j].data = page_to_phys(*pages++);
|
sglist[j].data = *pages++;
|
||||||
|
|
||||||
imagesize -= sz;
|
imagesize -= sz;
|
||||||
count--;
|
count--;
|
||||||
|
|
|
@ -528,7 +528,8 @@ int __init efi_config_parse_tables(void *config_tables, int count, int sz,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
efi_memattr_init();
|
if (efi_enabled(EFI_MEMMAP))
|
||||||
|
efi_memattr_init();
|
||||||
|
|
||||||
/* Parse the EFI Properties table if it exists */
|
/* Parse the EFI Properties table if it exists */
|
||||||
if (efi.properties_table != EFI_INVALID_TABLE_ADDR) {
|
if (efi.properties_table != EFI_INVALID_TABLE_ADDR) {
|
||||||
|
|
|
@ -71,18 +71,13 @@ copy_ucs2_from_user_len(efi_char16_t **dst, efi_char16_t __user *src,
|
||||||
if (!access_ok(VERIFY_READ, src, 1))
|
if (!access_ok(VERIFY_READ, src, 1))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
buf = kmalloc(len, GFP_KERNEL);
|
buf = memdup_user(src, len);
|
||||||
if (!buf) {
|
if (IS_ERR(buf)) {
|
||||||
*dst = NULL;
|
*dst = NULL;
|
||||||
return -ENOMEM;
|
return PTR_ERR(buf);
|
||||||
}
|
}
|
||||||
*dst = buf;
|
*dst = buf;
|
||||||
|
|
||||||
if (copy_from_user(*dst, src, len)) {
|
|
||||||
kfree(buf);
|
|
||||||
return -EFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -137,6 +137,18 @@ struct efi_boot_memmap {
|
||||||
#define EFI_CAPSULE_POPULATE_SYSTEM_TABLE 0x00020000
|
#define EFI_CAPSULE_POPULATE_SYSTEM_TABLE 0x00020000
|
||||||
#define EFI_CAPSULE_INITIATE_RESET 0x00040000
|
#define EFI_CAPSULE_INITIATE_RESET 0x00040000
|
||||||
|
|
||||||
|
struct capsule_info {
|
||||||
|
efi_capsule_header_t header;
|
||||||
|
int reset_type;
|
||||||
|
long index;
|
||||||
|
size_t count;
|
||||||
|
size_t total_size;
|
||||||
|
phys_addr_t *pages;
|
||||||
|
size_t page_bytes_remain;
|
||||||
|
};
|
||||||
|
|
||||||
|
int __efi_capsule_setup_info(struct capsule_info *cap_info);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Allocation types for calls to boottime->allocate_pages.
|
* Allocation types for calls to boottime->allocate_pages.
|
||||||
*/
|
*/
|
||||||
|
@ -1403,7 +1415,7 @@ extern int efi_capsule_supported(efi_guid_t guid, u32 flags,
|
||||||
size_t size, int *reset);
|
size_t size, int *reset);
|
||||||
|
|
||||||
extern int efi_capsule_update(efi_capsule_header_t *capsule,
|
extern int efi_capsule_update(efi_capsule_header_t *capsule,
|
||||||
struct page **pages);
|
phys_addr_t *pages);
|
||||||
|
|
||||||
#ifdef CONFIG_EFI_RUNTIME_MAP
|
#ifdef CONFIG_EFI_RUNTIME_MAP
|
||||||
int efi_runtime_map_init(struct kobject *);
|
int efi_runtime_map_init(struct kobject *);
|
||||||
|
|
Loading…
Reference in New Issue