提交 8c81f48e 编写于 作者: L Linus Torvalds

Merge branch 'x86-efi-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull x86 EFI updates from Peter Anvin:
 "This patchset falls under the "maintainers that grovel" clause in the
  v3.18-rc1 announcement.  We had intended to push it late in the merge
  window since we got it into the -tip tree relatively late.

  Many of these are relatively simple things, but there are a couple of
  key bits, especially Ard's and Matt's patches"

* 'x86-efi-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (24 commits)
  rtc: Disable EFI rtc for x86
  efi: rtc-efi: Export platform:rtc-efi as module alias
  efi: Delete the in_nmi() conditional runtime locking
  efi: Provide a non-blocking SetVariable() operation
  x86/efi: Adding efi_printks on memory allocationa and pci.reads
  x86/efi: Mark initialization code as such
  x86/efi: Update comment regarding required phys mapped EFI services
  x86/efi: Unexport add_efi_memmap variable
  x86/efi: Remove unused efi_call* macros
  efi: Resolve some shadow warnings
  arm64: efi: Format EFI memory type & attrs with efi_md_typeattr_format()
  ia64: efi: Format EFI memory type & attrs with efi_md_typeattr_format()
  x86: efi: Format EFI memory type & attrs with efi_md_typeattr_format()
  efi: Introduce efi_md_typeattr_format()
  efi: Add macro for EFI_MEMORY_UCE memory attribute
  x86/efi: Clear EFI_RUNTIME_SERVICES if failing to enter virtual mode
  arm64/efi: Do not enter virtual mode if booting with efi=noruntime or noefi
  arm64/efi: uefi_init error handling fix
  efi: Add kernel param efi=noruntime
  lib: Add a generic cmdline parse function parse_option_str
  ...
...@@ -1015,10 +1015,14 @@ bytes respectively. Such letter suffixes can also be entirely omitted. ...@@ -1015,10 +1015,14 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
Format: {"off" | "on" | "skip[mbr]"} Format: {"off" | "on" | "skip[mbr]"}
efi= [EFI] efi= [EFI]
Format: { "old_map" } Format: { "old_map", "nochunk", "noruntime" }
old_map [X86-64]: switch to the old ioremap-based EFI old_map [X86-64]: switch to the old ioremap-based EFI
runtime services mapping. 32-bit still uses this one by runtime services mapping. 32-bit still uses this one by
default. default.
nochunk: disable reading files in "chunks" in the EFI
boot stub, as chunking can cause problems with some
firmware implementations.
noruntime : disable EFI runtime services support
efi_no_storage_paranoia [EFI; X86] efi_no_storage_paranoia [EFI; X86]
Using this parameter you can use more than 50% of Using this parameter you can use more than 50% of
...@@ -2232,7 +2236,7 @@ bytes respectively. Such letter suffixes can also be entirely omitted. ...@@ -2232,7 +2236,7 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
nodsp [SH] Disable hardware DSP at boot time. nodsp [SH] Disable hardware DSP at boot time.
noefi [X86] Disable EFI runtime services support. noefi Disable EFI runtime services support.
noexec [IA-64] noexec [IA-64]
......
...@@ -89,7 +89,8 @@ static int __init uefi_init(void) ...@@ -89,7 +89,8 @@ static int __init uefi_init(void)
*/ */
if (efi.systab->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) { if (efi.systab->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) {
pr_err("System table signature incorrect\n"); pr_err("System table signature incorrect\n");
return -EINVAL; retval = -EINVAL;
goto out;
} }
if ((efi.systab->hdr.revision >> 16) < 2) if ((efi.systab->hdr.revision >> 16) < 2)
pr_warn("Warning: EFI system table version %d.%02d, expected 2.00 or greater\n", pr_warn("Warning: EFI system table version %d.%02d, expected 2.00 or greater\n",
...@@ -103,6 +104,7 @@ static int __init uefi_init(void) ...@@ -103,6 +104,7 @@ static int __init uefi_init(void)
for (i = 0; i < (int) sizeof(vendor) - 1 && *c16; ++i) for (i = 0; i < (int) sizeof(vendor) - 1 && *c16; ++i)
vendor[i] = c16[i]; vendor[i] = c16[i];
vendor[i] = '\0'; vendor[i] = '\0';
early_memunmap(c16, sizeof(vendor));
} }
pr_info("EFI v%u.%.02u by %s\n", pr_info("EFI v%u.%.02u by %s\n",
...@@ -113,29 +115,11 @@ static int __init uefi_init(void) ...@@ -113,29 +115,11 @@ static int __init uefi_init(void)
if (retval == 0) if (retval == 0)
set_bit(EFI_CONFIG_TABLES, &efi.flags); set_bit(EFI_CONFIG_TABLES, &efi.flags);
early_memunmap(c16, sizeof(vendor)); out:
early_memunmap(efi.systab, sizeof(efi_system_table_t)); early_memunmap(efi.systab, sizeof(efi_system_table_t));
return retval; return retval;
} }
static __initdata char memory_type_name[][32] = {
{"Reserved"},
{"Loader Code"},
{"Loader Data"},
{"Boot Code"},
{"Boot Data"},
{"Runtime Code"},
{"Runtime Data"},
{"Conventional Memory"},
{"Unusable Memory"},
{"ACPI Reclaim Memory"},
{"ACPI Memory NVS"},
{"Memory Mapped I/O"},
{"MMIO Port Space"},
{"PAL Code"},
};
/* /*
* Return true for RAM regions we want to permanently reserve. * Return true for RAM regions we want to permanently reserve.
*/ */
...@@ -166,10 +150,13 @@ static __init void reserve_regions(void) ...@@ -166,10 +150,13 @@ static __init void reserve_regions(void)
paddr = md->phys_addr; paddr = md->phys_addr;
npages = md->num_pages; npages = md->num_pages;
if (uefi_debug) if (uefi_debug) {
pr_info(" 0x%012llx-0x%012llx [%s]", char buf[64];
pr_info(" 0x%012llx-0x%012llx %s",
paddr, paddr + (npages << EFI_PAGE_SHIFT) - 1, paddr, paddr + (npages << EFI_PAGE_SHIFT) - 1,
memory_type_name[md->type]); efi_md_typeattr_format(buf, sizeof(buf), md));
}
memrange_efi_to_native(&paddr, &npages); memrange_efi_to_native(&paddr, &npages);
size = npages << PAGE_SHIFT; size = npages << PAGE_SHIFT;
...@@ -393,11 +380,16 @@ static int __init arm64_enter_virtual_mode(void) ...@@ -393,11 +380,16 @@ static int __init arm64_enter_virtual_mode(void)
return -1; return -1;
} }
pr_info("Remapping and enabling EFI services.\n");
/* replace early memmap mapping with permanent mapping */
mapsize = memmap.map_end - memmap.map; mapsize = memmap.map_end - memmap.map;
early_memunmap(memmap.map, mapsize); early_memunmap(memmap.map, mapsize);
if (efi_runtime_disabled()) {
pr_info("EFI runtime services will be disabled.\n");
return -1;
}
pr_info("Remapping and enabling EFI services.\n");
/* replace early memmap mapping with permanent mapping */
memmap.map = (__force void *)ioremap_cache((phys_addr_t)memmap.phys_map, memmap.map = (__force void *)ioremap_cache((phys_addr_t)memmap.phys_map,
mapsize); mapsize);
memmap.map_end = memmap.map + mapsize; memmap.map_end = memmap.map + mapsize;
......
...@@ -568,6 +568,7 @@ efi_init (void) ...@@ -568,6 +568,7 @@ efi_init (void)
{ {
const char *unit; const char *unit;
unsigned long size; unsigned long size;
char buf[64];
md = p; md = p;
size = md->num_pages << EFI_PAGE_SHIFT; size = md->num_pages << EFI_PAGE_SHIFT;
...@@ -586,9 +587,10 @@ efi_init (void) ...@@ -586,9 +587,10 @@ efi_init (void)
unit = "KB"; unit = "KB";
} }
printk("mem%02d: type=%2u, attr=0x%016lx, " printk("mem%02d: %s "
"range=[0x%016lx-0x%016lx) (%4lu%s)\n", "range=[0x%016lx-0x%016lx) (%4lu%s)\n",
i, md->type, md->attribute, md->phys_addr, i, efi_md_typeattr_format(buf, sizeof(buf), md),
md->phys_addr,
md->phys_addr + efi_md_size(md), size, unit); md->phys_addr + efi_md_size(md), size, unit);
} }
} }
......
...@@ -330,8 +330,10 @@ __setup_efi_pci32(efi_pci_io_protocol_32 *pci, struct pci_setup_rom **__rom) ...@@ -330,8 +330,10 @@ __setup_efi_pci32(efi_pci_io_protocol_32 *pci, struct pci_setup_rom **__rom)
size = pci->romsize + sizeof(*rom); size = pci->romsize + sizeof(*rom);
status = efi_call_early(allocate_pool, EFI_LOADER_DATA, size, &rom); status = efi_call_early(allocate_pool, EFI_LOADER_DATA, size, &rom);
if (status != EFI_SUCCESS) if (status != EFI_SUCCESS) {
efi_printk(sys_table, "Failed to alloc mem for rom\n");
return status; return status;
}
memset(rom, 0, sizeof(*rom)); memset(rom, 0, sizeof(*rom));
...@@ -344,14 +346,18 @@ __setup_efi_pci32(efi_pci_io_protocol_32 *pci, struct pci_setup_rom **__rom) ...@@ -344,14 +346,18 @@ __setup_efi_pci32(efi_pci_io_protocol_32 *pci, struct pci_setup_rom **__rom)
status = efi_early->call(pci->pci.read, pci, EfiPciIoWidthUint16, status = efi_early->call(pci->pci.read, pci, EfiPciIoWidthUint16,
PCI_VENDOR_ID, 1, &(rom->vendor)); PCI_VENDOR_ID, 1, &(rom->vendor));
if (status != EFI_SUCCESS) if (status != EFI_SUCCESS) {
efi_printk(sys_table, "Failed to read rom->vendor\n");
goto free_struct; goto free_struct;
}
status = efi_early->call(pci->pci.read, pci, EfiPciIoWidthUint16, status = efi_early->call(pci->pci.read, pci, EfiPciIoWidthUint16,
PCI_DEVICE_ID, 1, &(rom->devid)); PCI_DEVICE_ID, 1, &(rom->devid));
if (status != EFI_SUCCESS) if (status != EFI_SUCCESS) {
efi_printk(sys_table, "Failed to read rom->devid\n");
goto free_struct; goto free_struct;
}
status = efi_early->call(pci->get_location, pci, &(rom->segment), status = efi_early->call(pci->get_location, pci, &(rom->segment),
&(rom->bus), &(rom->device), &(rom->function)); &(rom->bus), &(rom->device), &(rom->function));
...@@ -432,8 +438,10 @@ __setup_efi_pci64(efi_pci_io_protocol_64 *pci, struct pci_setup_rom **__rom) ...@@ -432,8 +438,10 @@ __setup_efi_pci64(efi_pci_io_protocol_64 *pci, struct pci_setup_rom **__rom)
size = pci->romsize + sizeof(*rom); size = pci->romsize + sizeof(*rom);
status = efi_call_early(allocate_pool, EFI_LOADER_DATA, size, &rom); status = efi_call_early(allocate_pool, EFI_LOADER_DATA, size, &rom);
if (status != EFI_SUCCESS) if (status != EFI_SUCCESS) {
efi_printk(sys_table, "Failed to alloc mem for rom\n");
return status; return status;
}
rom->data.type = SETUP_PCI; rom->data.type = SETUP_PCI;
rom->data.len = size - sizeof(struct setup_data); rom->data.len = size - sizeof(struct setup_data);
...@@ -444,14 +452,18 @@ __setup_efi_pci64(efi_pci_io_protocol_64 *pci, struct pci_setup_rom **__rom) ...@@ -444,14 +452,18 @@ __setup_efi_pci64(efi_pci_io_protocol_64 *pci, struct pci_setup_rom **__rom)
status = efi_early->call(pci->pci.read, pci, EfiPciIoWidthUint16, status = efi_early->call(pci->pci.read, pci, EfiPciIoWidthUint16,
PCI_VENDOR_ID, 1, &(rom->vendor)); PCI_VENDOR_ID, 1, &(rom->vendor));
if (status != EFI_SUCCESS) if (status != EFI_SUCCESS) {
efi_printk(sys_table, "Failed to read rom->vendor\n");
goto free_struct; goto free_struct;
}
status = efi_early->call(pci->pci.read, pci, EfiPciIoWidthUint16, status = efi_early->call(pci->pci.read, pci, EfiPciIoWidthUint16,
PCI_DEVICE_ID, 1, &(rom->devid)); PCI_DEVICE_ID, 1, &(rom->devid));
if (status != EFI_SUCCESS) if (status != EFI_SUCCESS) {
efi_printk(sys_table, "Failed to read rom->devid\n");
goto free_struct; goto free_struct;
}
status = efi_early->call(pci->get_location, pci, &(rom->segment), status = efi_early->call(pci->get_location, pci, &(rom->segment),
&(rom->bus), &(rom->device), &(rom->function)); &(rom->bus), &(rom->device), &(rom->function));
...@@ -538,8 +550,10 @@ static void setup_efi_pci(struct boot_params *params) ...@@ -538,8 +550,10 @@ static void setup_efi_pci(struct boot_params *params)
EFI_LOADER_DATA, EFI_LOADER_DATA,
size, (void **)&pci_handle); size, (void **)&pci_handle);
if (status != EFI_SUCCESS) if (status != EFI_SUCCESS) {
efi_printk(sys_table, "Failed to alloc mem for pci_handle\n");
return; return;
}
status = efi_call_early(locate_handle, status = efi_call_early(locate_handle,
EFI_LOCATE_BY_PROTOCOL, &pci_proto, EFI_LOCATE_BY_PROTOCOL, &pci_proto,
...@@ -1105,6 +1119,10 @@ struct boot_params *make_boot_params(struct efi_config *c) ...@@ -1105,6 +1119,10 @@ struct boot_params *make_boot_params(struct efi_config *c)
memset(sdt, 0, sizeof(*sdt)); memset(sdt, 0, sizeof(*sdt));
status = efi_parse_options(cmdline_ptr);
if (status != EFI_SUCCESS)
goto fail2;
status = handle_cmdline_files(sys_table, image, status = handle_cmdline_files(sys_table, image,
(char *)(unsigned long)hdr->cmd_line_ptr, (char *)(unsigned long)hdr->cmd_line_ptr,
"initrd=", hdr->initrd_addr_max, "initrd=", hdr->initrd_addr_max,
......
...@@ -81,24 +81,23 @@ extern u64 asmlinkage efi_call(void *fp, ...); ...@@ -81,24 +81,23 @@ extern u64 asmlinkage efi_call(void *fp, ...);
*/ */
#define __efi_call_virt(f, args...) efi_call_virt(f, args) #define __efi_call_virt(f, args...) efi_call_virt(f, args)
extern void __iomem *efi_ioremap(unsigned long addr, unsigned long size, extern void __iomem *__init efi_ioremap(unsigned long addr, unsigned long size,
u32 type, u64 attribute); u32 type, u64 attribute);
#endif /* CONFIG_X86_32 */ #endif /* CONFIG_X86_32 */
extern int add_efi_memmap;
extern struct efi_scratch efi_scratch; extern struct efi_scratch efi_scratch;
extern void efi_set_executable(efi_memory_desc_t *md, bool executable); extern void __init efi_set_executable(efi_memory_desc_t *md, bool executable);
extern int efi_memblock_x86_reserve_range(void); extern int __init efi_memblock_x86_reserve_range(void);
extern void efi_call_phys_prelog(void); extern void __init efi_call_phys_prolog(void);
extern void efi_call_phys_epilog(void); extern void __init efi_call_phys_epilog(void);
extern void efi_unmap_memmap(void); extern void __init efi_unmap_memmap(void);
extern void efi_memory_uc(u64 addr, unsigned long size); extern void __init efi_memory_uc(u64 addr, unsigned long size);
extern void __init efi_map_region(efi_memory_desc_t *md); extern void __init efi_map_region(efi_memory_desc_t *md);
extern void __init efi_map_region_fixed(efi_memory_desc_t *md); extern void __init efi_map_region_fixed(efi_memory_desc_t *md);
extern void efi_sync_low_kernel_mappings(void); extern void efi_sync_low_kernel_mappings(void);
extern int efi_setup_page_tables(unsigned long pa_memmap, unsigned num_pages); extern int __init efi_setup_page_tables(unsigned long pa_memmap, unsigned num_pages);
extern void efi_cleanup_page_tables(unsigned long pa_memmap, unsigned num_pages); extern void __init efi_cleanup_page_tables(unsigned long pa_memmap, unsigned num_pages);
extern void __init old_map_region(efi_memory_desc_t *md); extern void __init old_map_region(efi_memory_desc_t *md);
extern void __init runtime_code_page_mkexec(void); extern void __init runtime_code_page_mkexec(void);
extern void __init efi_runtime_mkexec(void); extern void __init efi_runtime_mkexec(void);
...@@ -162,16 +161,6 @@ static inline efi_status_t efi_thunk_set_virtual_address_map( ...@@ -162,16 +161,6 @@ static inline efi_status_t efi_thunk_set_virtual_address_map(
extern bool efi_reboot_required(void); extern bool efi_reboot_required(void);
#else #else
/*
* IF EFI is not configured, have the EFI calls return -ENOSYS.
*/
#define efi_call0(_f) (-ENOSYS)
#define efi_call1(_f, _a1) (-ENOSYS)
#define efi_call2(_f, _a1, _a2) (-ENOSYS)
#define efi_call3(_f, _a1, _a2, _a3) (-ENOSYS)
#define efi_call4(_f, _a1, _a2, _a3, _a4) (-ENOSYS)
#define efi_call5(_f, _a1, _a2, _a3, _a4, _a5) (-ENOSYS)
#define efi_call6(_f, _a1, _a2, _a3, _a4, _a5, _a6) (-ENOSYS)
static inline void parse_efi_setup(u64 phys_addr, u32 data_len) {} static inline void parse_efi_setup(u64 phys_addr, u32 data_len) {}
static inline bool efi_reboot_required(void) static inline bool efi_reboot_required(void)
{ {
......
...@@ -40,35 +40,59 @@ void __init efi_bgrt_init(void) ...@@ -40,35 +40,59 @@ void __init efi_bgrt_init(void)
if (ACPI_FAILURE(status)) if (ACPI_FAILURE(status))
return; return;
if (bgrt_tab->header.length < sizeof(*bgrt_tab)) if (bgrt_tab->header.length < sizeof(*bgrt_tab)) {
pr_err("Ignoring BGRT: invalid length %u (expected %zu)\n",
bgrt_tab->header.length, sizeof(*bgrt_tab));
return; return;
if (bgrt_tab->version != 1 || bgrt_tab->status != 1) }
if (bgrt_tab->version != 1) {
pr_err("Ignoring BGRT: invalid version %u (expected 1)\n",
bgrt_tab->version);
return;
}
if (bgrt_tab->status != 1) {
pr_err("Ignoring BGRT: invalid status %u (expected 1)\n",
bgrt_tab->status);
return;
}
if (bgrt_tab->image_type != 0) {
pr_err("Ignoring BGRT: invalid image type %u (expected 0)\n",
bgrt_tab->image_type);
return; return;
if (bgrt_tab->image_type != 0 || !bgrt_tab->image_address) }
if (!bgrt_tab->image_address) {
pr_err("Ignoring BGRT: null image address\n");
return; return;
}
image = efi_lookup_mapped_addr(bgrt_tab->image_address); image = efi_lookup_mapped_addr(bgrt_tab->image_address);
if (!image) { if (!image) {
image = early_memremap(bgrt_tab->image_address, image = early_memremap(bgrt_tab->image_address,
sizeof(bmp_header)); sizeof(bmp_header));
ioremapped = true; ioremapped = true;
if (!image) if (!image) {
pr_err("Ignoring BGRT: failed to map image header memory\n");
return; return;
} }
}
memcpy_fromio(&bmp_header, image, sizeof(bmp_header)); memcpy_fromio(&bmp_header, image, sizeof(bmp_header));
if (ioremapped) if (ioremapped)
early_iounmap(image, sizeof(bmp_header)); early_iounmap(image, sizeof(bmp_header));
bgrt_image_size = bmp_header.size; bgrt_image_size = bmp_header.size;
bgrt_image = kmalloc(bgrt_image_size, GFP_KERNEL); bgrt_image = kmalloc(bgrt_image_size, GFP_KERNEL | __GFP_NOWARN);
if (!bgrt_image) if (!bgrt_image) {
pr_err("Ignoring BGRT: failed to allocate memory for image (wanted %zu bytes)\n",
bgrt_image_size);
return; return;
}
if (ioremapped) { if (ioremapped) {
image = early_memremap(bgrt_tab->image_address, image = early_memremap(bgrt_tab->image_address,
bmp_header.size); bmp_header.size);
if (!image) { if (!image) {
pr_err("Ignoring BGRT: failed to map image memory\n");
kfree(bgrt_image); kfree(bgrt_image);
bgrt_image = NULL; bgrt_image = NULL;
return; return;
......
...@@ -70,17 +70,7 @@ static efi_config_table_type_t arch_tables[] __initdata = { ...@@ -70,17 +70,7 @@ static efi_config_table_type_t arch_tables[] __initdata = {
u64 efi_setup; /* efi setup_data physical address */ u64 efi_setup; /* efi setup_data physical address */
static bool disable_runtime __initdata = false; static int add_efi_memmap __initdata;
static int __init setup_noefi(char *arg)
{
disable_runtime = true;
return 0;
}
early_param("noefi", setup_noefi);
int add_efi_memmap;
EXPORT_SYMBOL(add_efi_memmap);
static int __init setup_add_efi_memmap(char *arg) static int __init setup_add_efi_memmap(char *arg)
{ {
add_efi_memmap = 1; add_efi_memmap = 1;
...@@ -96,7 +86,7 @@ static efi_status_t __init phys_efi_set_virtual_address_map( ...@@ -96,7 +86,7 @@ static efi_status_t __init phys_efi_set_virtual_address_map(
{ {
efi_status_t status; efi_status_t status;
efi_call_phys_prelog(); efi_call_phys_prolog();
status = efi_call_phys(efi_phys.set_virtual_address_map, status = efi_call_phys(efi_phys.set_virtual_address_map,
memory_map_size, descriptor_size, memory_map_size, descriptor_size,
descriptor_version, virtual_map); descriptor_version, virtual_map);
...@@ -210,9 +200,12 @@ static void __init print_efi_memmap(void) ...@@ -210,9 +200,12 @@ static void __init print_efi_memmap(void)
for (p = memmap.map, i = 0; for (p = memmap.map, i = 0;
p < memmap.map_end; p < memmap.map_end;
p += memmap.desc_size, i++) { p += memmap.desc_size, i++) {
char buf[64];
md = p; md = p;
pr_info("mem%02u: type=%u, attr=0x%llx, range=[0x%016llx-0x%016llx) (%lluMB)\n", pr_info("mem%02u: %s range=[0x%016llx-0x%016llx) (%lluMB)\n",
i, md->type, md->attribute, md->phys_addr, i, efi_md_typeattr_format(buf, sizeof(buf), md),
md->phys_addr,
md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT), md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT),
(md->num_pages >> (20 - EFI_PAGE_SHIFT))); (md->num_pages >> (20 - EFI_PAGE_SHIFT)));
} }
...@@ -344,9 +337,9 @@ static int __init efi_runtime_init32(void) ...@@ -344,9 +337,9 @@ static int __init efi_runtime_init32(void)
} }
/* /*
* We will only need *early* access to the following two * We will only need *early* access to the SetVirtualAddressMap
* EFI runtime services before set_virtual_address_map * EFI runtime service. All other runtime services will be called
* is invoked. * via the virtual mapping.
*/ */
efi_phys.set_virtual_address_map = efi_phys.set_virtual_address_map =
(efi_set_virtual_address_map_t *) (efi_set_virtual_address_map_t *)
...@@ -368,9 +361,9 @@ static int __init efi_runtime_init64(void) ...@@ -368,9 +361,9 @@ static int __init efi_runtime_init64(void)
} }
/* /*
* We will only need *early* access to the following two * We will only need *early* access to the SetVirtualAddressMap
* EFI runtime services before set_virtual_address_map * EFI runtime service. All other runtime services will be called
* is invoked. * via the virtual mapping.
*/ */
efi_phys.set_virtual_address_map = efi_phys.set_virtual_address_map =
(efi_set_virtual_address_map_t *) (efi_set_virtual_address_map_t *)
...@@ -492,7 +485,7 @@ void __init efi_init(void) ...@@ -492,7 +485,7 @@ void __init efi_init(void)
if (!efi_runtime_supported()) if (!efi_runtime_supported())
pr_info("No EFI runtime due to 32/64-bit mismatch with kernel\n"); pr_info("No EFI runtime due to 32/64-bit mismatch with kernel\n");
else { else {
if (disable_runtime || efi_runtime_init()) if (efi_runtime_disabled() || efi_runtime_init())
return; return;
} }
if (efi_memmap_init()) if (efi_memmap_init())
...@@ -537,7 +530,7 @@ void __init runtime_code_page_mkexec(void) ...@@ -537,7 +530,7 @@ void __init runtime_code_page_mkexec(void)
} }
} }
void efi_memory_uc(u64 addr, unsigned long size) void __init efi_memory_uc(u64 addr, unsigned long size)
{ {
unsigned long page_shift = 1UL << EFI_PAGE_SHIFT; unsigned long page_shift = 1UL << EFI_PAGE_SHIFT;
u64 npages; u64 npages;
...@@ -732,6 +725,7 @@ static void __init kexec_enter_virtual_mode(void) ...@@ -732,6 +725,7 @@ static void __init kexec_enter_virtual_mode(void)
*/ */
if (!efi_is_native()) { if (!efi_is_native()) {
efi_unmap_memmap(); efi_unmap_memmap();
clear_bit(EFI_RUNTIME_SERVICES, &efi.flags);
return; return;
} }
...@@ -805,6 +799,7 @@ static void __init __efi_enter_virtual_mode(void) ...@@ -805,6 +799,7 @@ static void __init __efi_enter_virtual_mode(void)
new_memmap = efi_map_regions(&count, &pg_shift); new_memmap = efi_map_regions(&count, &pg_shift);
if (!new_memmap) { if (!new_memmap) {
pr_err("Error reallocating memory, EFI runtime non-functional!\n"); pr_err("Error reallocating memory, EFI runtime non-functional!\n");
clear_bit(EFI_RUNTIME_SERVICES, &efi.flags);
return; return;
} }
...@@ -812,8 +807,10 @@ static void __init __efi_enter_virtual_mode(void) ...@@ -812,8 +807,10 @@ static void __init __efi_enter_virtual_mode(void)
BUG_ON(!efi.systab); BUG_ON(!efi.systab);
if (efi_setup_page_tables(__pa(new_memmap), 1 << pg_shift)) if (efi_setup_page_tables(__pa(new_memmap), 1 << pg_shift)) {
clear_bit(EFI_RUNTIME_SERVICES, &efi.flags);
return; return;
}
efi_sync_low_kernel_mappings(); efi_sync_low_kernel_mappings();
efi_dump_pagetable(); efi_dump_pagetable();
...@@ -938,14 +935,11 @@ u64 efi_mem_attributes(unsigned long phys_addr) ...@@ -938,14 +935,11 @@ u64 efi_mem_attributes(unsigned long phys_addr)
return 0; return 0;
} }
static int __init parse_efi_cmdline(char *str) static int __init arch_parse_efi_cmdline(char *str)
{ {
if (*str == '=') if (parse_option_str(str, "old_map"))
str++;
if (!strncmp(str, "old_map", 7))
set_bit(EFI_OLD_MEMMAP, &efi.flags); set_bit(EFI_OLD_MEMMAP, &efi.flags);
return 0; return 0;
} }
early_param("efi", parse_efi_cmdline); early_param("efi", arch_parse_efi_cmdline);
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
/* /*
* To make EFI call EFI runtime service in physical addressing mode we need * To make EFI call EFI runtime service in physical addressing mode we need
* prelog/epilog before/after the invocation to disable interrupt, to * prolog/epilog before/after the invocation to disable interrupt, to
* claim EFI runtime service handler exclusively and to duplicate a memory in * claim EFI runtime service handler exclusively and to duplicate a memory in
* low memory space say 0 - 3G. * low memory space say 0 - 3G.
*/ */
...@@ -41,11 +41,13 @@ static unsigned long efi_rt_eflags; ...@@ -41,11 +41,13 @@ static unsigned long efi_rt_eflags;
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) {}
int 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;
} }
void efi_cleanup_page_tables(unsigned long pa_memmap, unsigned num_pages) {} void __init efi_cleanup_page_tables(unsigned long pa_memmap, unsigned num_pages)
{
}
void __init efi_map_region(efi_memory_desc_t *md) void __init efi_map_region(efi_memory_desc_t *md)
{ {
...@@ -55,7 +57,7 @@ void __init efi_map_region(efi_memory_desc_t *md) ...@@ -55,7 +57,7 @@ void __init efi_map_region(efi_memory_desc_t *md)
void __init efi_map_region_fixed(efi_memory_desc_t *md) {} void __init efi_map_region_fixed(efi_memory_desc_t *md) {}
void __init parse_efi_setup(u64 phys_addr, u32 data_len) {} void __init parse_efi_setup(u64 phys_addr, u32 data_len) {}
void efi_call_phys_prelog(void) void __init efi_call_phys_prolog(void)
{ {
struct desc_ptr gdt_descr; struct desc_ptr gdt_descr;
...@@ -69,7 +71,7 @@ void efi_call_phys_prelog(void) ...@@ -69,7 +71,7 @@ void efi_call_phys_prelog(void)
load_gdt(&gdt_descr); load_gdt(&gdt_descr);
} }
void efi_call_phys_epilog(void) void __init efi_call_phys_epilog(void)
{ {
struct desc_ptr gdt_descr; struct desc_ptr gdt_descr;
......
...@@ -79,7 +79,7 @@ static void __init early_code_mapping_set_exec(int executable) ...@@ -79,7 +79,7 @@ static void __init early_code_mapping_set_exec(int executable)
} }
} }
void __init efi_call_phys_prelog(void) void __init efi_call_phys_prolog(void)
{ {
unsigned long vaddress; unsigned long vaddress;
int pgd; int pgd;
...@@ -139,7 +139,7 @@ void efi_sync_low_kernel_mappings(void) ...@@ -139,7 +139,7 @@ void efi_sync_low_kernel_mappings(void)
sizeof(pgd_t) * num_pgds); sizeof(pgd_t) * num_pgds);
} }
int efi_setup_page_tables(unsigned long pa_memmap, unsigned num_pages) int __init efi_setup_page_tables(unsigned long pa_memmap, unsigned num_pages)
{ {
unsigned long text; unsigned long text;
struct page *page; struct page *page;
...@@ -192,7 +192,7 @@ int efi_setup_page_tables(unsigned long pa_memmap, unsigned num_pages) ...@@ -192,7 +192,7 @@ int efi_setup_page_tables(unsigned long pa_memmap, unsigned num_pages)
return 0; return 0;
} }
void efi_cleanup_page_tables(unsigned long pa_memmap, unsigned num_pages) void __init efi_cleanup_page_tables(unsigned long pa_memmap, unsigned num_pages)
{ {
pgd_t *pgd = (pgd_t *)__va(real_mode_header->trampoline_pgd); pgd_t *pgd = (pgd_t *)__va(real_mode_header->trampoline_pgd);
......
...@@ -27,13 +27,13 @@ ENTRY(efi_call_phys) ...@@ -27,13 +27,13 @@ ENTRY(efi_call_phys)
* set to 0x0010, DS and SS have been set to 0x0018. In EFI, I found * set to 0x0010, DS and SS have been set to 0x0018. In EFI, I found
* the values of these registers are the same. And, the corresponding * the values of these registers are the same. And, the corresponding
* GDT entries are identical. So I will do nothing about segment reg * GDT entries are identical. So I will do nothing about segment reg
* and GDT, but change GDT base register in prelog and epilog. * and GDT, but change GDT base register in prolog and epilog.
*/ */
/* /*
* 1. Now I am running with EIP = <physical address> + PAGE_OFFSET. * 1. Now I am running with EIP = <physical address> + PAGE_OFFSET.
* But to make it smoothly switch from virtual mode to flat mode. * But to make it smoothly switch from virtual mode to flat mode.
* The mapping of lower virtual memory has been created in prelog and * The mapping of lower virtual memory has been created in prolog and
* epilog. * epilog.
*/ */
movl $1f, %edx movl $1f, %edx
......
...@@ -41,6 +41,28 @@ struct efi __read_mostly efi = { ...@@ -41,6 +41,28 @@ struct efi __read_mostly efi = {
}; };
EXPORT_SYMBOL(efi); EXPORT_SYMBOL(efi);
static bool disable_runtime;
static int __init setup_noefi(char *arg)
{
disable_runtime = true;
return 0;
}
early_param("noefi", setup_noefi);
bool efi_runtime_disabled(void)
{
return disable_runtime;
}
static int __init parse_efi_cmdline(char *str)
{
if (parse_option_str(str, "noruntime"))
disable_runtime = true;
return 0;
}
early_param("efi", parse_efi_cmdline);
static struct kobject *efi_kobj; static struct kobject *efi_kobj;
static struct kobject *efivars_kobj; static struct kobject *efivars_kobj;
...@@ -423,3 +445,60 @@ int __init efi_get_fdt_params(struct efi_fdt_params *params, int verbose) ...@@ -423,3 +445,60 @@ int __init efi_get_fdt_params(struct efi_fdt_params *params, int verbose)
return ret; return ret;
} }
#endif /* CONFIG_EFI_PARAMS_FROM_FDT */ #endif /* CONFIG_EFI_PARAMS_FROM_FDT */
static __initdata char memory_type_name[][20] = {
"Reserved",
"Loader Code",
"Loader Data",
"Boot Code",
"Boot Data",
"Runtime Code",
"Runtime Data",
"Conventional Memory",
"Unusable Memory",
"ACPI Reclaim Memory",
"ACPI Memory NVS",
"Memory Mapped I/O",
"MMIO Port Space",
"PAL Code"
};
char * __init efi_md_typeattr_format(char *buf, size_t size,
const efi_memory_desc_t *md)
{
char *pos;
int type_len;
u64 attr;
pos = buf;
if (md->type >= ARRAY_SIZE(memory_type_name))
type_len = snprintf(pos, size, "[type=%u", md->type);
else
type_len = snprintf(pos, size, "[%-*s",
(int)(sizeof(memory_type_name[0]) - 1),
memory_type_name[md->type]);
if (type_len >= size)
return buf;
pos += type_len;
size -= type_len;
attr = md->attribute;
if (attr & ~(EFI_MEMORY_UC | EFI_MEMORY_WC | EFI_MEMORY_WT |
EFI_MEMORY_WB | EFI_MEMORY_UCE | EFI_MEMORY_WP |
EFI_MEMORY_RP | EFI_MEMORY_XP | EFI_MEMORY_RUNTIME))
snprintf(pos, size, "|attr=0x%016llx]",
(unsigned long long)attr);
else
snprintf(pos, size, "|%3s|%2s|%2s|%2s|%3s|%2s|%2s|%2s|%2s]",
attr & EFI_MEMORY_RUNTIME ? "RUN" : "",
attr & EFI_MEMORY_XP ? "XP" : "",
attr & EFI_MEMORY_RP ? "RP" : "",
attr & EFI_MEMORY_WP ? "WP" : "",
attr & EFI_MEMORY_UCE ? "UCE" : "",
attr & EFI_MEMORY_WB ? "WB" : "",
attr & EFI_MEMORY_WT ? "WT" : "",
attr & EFI_MEMORY_WC ? "WC" : "",
attr & EFI_MEMORY_UC ? "UC" : "");
return buf;
}
...@@ -226,6 +226,10 @@ unsigned long __init efi_entry(void *handle, efi_system_table_t *sys_table, ...@@ -226,6 +226,10 @@ unsigned long __init efi_entry(void *handle, efi_system_table_t *sys_table,
goto fail_free_image; goto fail_free_image;
} }
status = efi_parse_options(cmdline_ptr);
if (status != EFI_SUCCESS)
pr_efi_err(sys_table, "Failed to parse EFI cmdline options\n");
/* /*
* Unauthenticated device tree data is a security hazard, so * Unauthenticated device tree data is a security hazard, so
* ignore 'dtb=' unless UEFI Secure Boot is disabled. * ignore 'dtb=' unless UEFI Secure Boot is disabled.
......
...@@ -15,8 +15,23 @@ ...@@ -15,8 +15,23 @@
#include "efistub.h" #include "efistub.h"
/*
* Some firmware implementations have problems reading files in one go.
* A read chunk size of 1MB seems to work for most platforms.
*
* Unfortunately, reading files in chunks triggers *other* bugs on some
* platforms, so we provide a way to disable this workaround, which can
* be done by passing "efi=nochunk" on the EFI boot stub command line.
*
* If you experience issues with initrd images being corrupt it's worth
* trying efi=nochunk, but chunking is enabled by default because there
* are far more machines that require the workaround than those that
* break with it enabled.
*/
#define EFI_READ_CHUNK_SIZE (1024 * 1024) #define EFI_READ_CHUNK_SIZE (1024 * 1024)
static unsigned long __chunk_size = EFI_READ_CHUNK_SIZE;
struct file_info { struct file_info {
efi_file_handle_t *handle; efi_file_handle_t *handle;
u64 size; u64 size;
...@@ -281,6 +296,49 @@ void efi_free(efi_system_table_t *sys_table_arg, unsigned long size, ...@@ -281,6 +296,49 @@ void efi_free(efi_system_table_t *sys_table_arg, unsigned long size,
efi_call_early(free_pages, addr, nr_pages); efi_call_early(free_pages, addr, nr_pages);
} }
/*
* Parse the ASCII string 'cmdline' for EFI options, denoted by the efi=
* option, e.g. efi=nochunk.
*
* It should be noted that efi= is parsed in two very different
* environments, first in the early boot environment of the EFI boot
* stub, and subsequently during the kernel boot.
*/
efi_status_t efi_parse_options(char *cmdline)
{
char *str;
/*
* If no EFI parameters were specified on the cmdline we've got
* nothing to do.
*/
str = strstr(cmdline, "efi=");
if (!str)
return EFI_SUCCESS;
/* Skip ahead to first argument */
str += strlen("efi=");
/*
* Remember, because efi= is also used by the kernel we need to
* skip over arguments we don't understand.
*/
while (*str) {
if (!strncmp(str, "nochunk", 7)) {
str += strlen("nochunk");
__chunk_size = -1UL;
}
/* Group words together, delimited by "," */
while (*str && *str != ',')
str++;
if (*str == ',')
str++;
}
return EFI_SUCCESS;
}
/* /*
* Check the cmdline for a LILO-style file= arguments. * Check the cmdline for a LILO-style file= arguments.
...@@ -423,8 +481,8 @@ efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg, ...@@ -423,8 +481,8 @@ efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
size = files[j].size; size = files[j].size;
while (size) { while (size) {
unsigned long chunksize; unsigned long chunksize;
if (size > EFI_READ_CHUNK_SIZE) if (size > __chunk_size)
chunksize = EFI_READ_CHUNK_SIZE; chunksize = __chunk_size;
else else
chunksize = size; chunksize = size;
......
...@@ -14,10 +14,79 @@ ...@@ -14,10 +14,79 @@
* This file is released under the GPLv2. * This file is released under the GPLv2.
*/ */
#include <linux/bug.h>
#include <linux/efi.h> #include <linux/efi.h>
#include <linux/spinlock.h> /* spinlock_t */ #include <linux/mutex.h>
#include <linux/spinlock.h>
#include <asm/efi.h> #include <asm/efi.h>
/*
* According to section 7.1 of the UEFI spec, Runtime Services are not fully
* reentrant, and there are particular combinations of calls that need to be
* serialized. (source: UEFI Specification v2.4A)
*
* Table 31. Rules for Reentry Into Runtime Services
* +------------------------------------+-------------------------------+
* | If previous call is busy in | Forbidden to call |
* +------------------------------------+-------------------------------+
* | Any | SetVirtualAddressMap() |
* +------------------------------------+-------------------------------+
* | ConvertPointer() | ConvertPointer() |
* +------------------------------------+-------------------------------+
* | SetVariable() | ResetSystem() |
* | UpdateCapsule() | |
* | SetTime() | |
* | SetWakeupTime() | |
* | GetNextHighMonotonicCount() | |
* +------------------------------------+-------------------------------+
* | GetVariable() | GetVariable() |
* | GetNextVariableName() | GetNextVariableName() |
* | SetVariable() | SetVariable() |
* | QueryVariableInfo() | QueryVariableInfo() |
* | UpdateCapsule() | UpdateCapsule() |
* | QueryCapsuleCapabilities() | QueryCapsuleCapabilities() |
* | GetNextHighMonotonicCount() | GetNextHighMonotonicCount() |
* +------------------------------------+-------------------------------+
* | GetTime() | GetTime() |
* | SetTime() | SetTime() |
* | GetWakeupTime() | GetWakeupTime() |
* | SetWakeupTime() | SetWakeupTime() |
* +------------------------------------+-------------------------------+
*
* Due to the fact that the EFI pstore may write to the variable store in
* interrupt context, we need to use a spinlock for at least the groups that
* contain SetVariable() and QueryVariableInfo(). That leaves little else, as
* none of the remaining functions are actually ever called at runtime.
* So let's just use a single spinlock to serialize all Runtime Services calls.
*/
static DEFINE_SPINLOCK(efi_runtime_lock);
/*
* Some runtime services calls can be reentrant under NMI, even if the table
* above says they are not. (source: UEFI Specification v2.4A)
*
* Table 32. Functions that may be called after Machine Check, INIT and NMI
* +----------------------------+------------------------------------------+
* | Function | Called after Machine Check, INIT and NMI |
* +----------------------------+------------------------------------------+
* | GetTime() | Yes, even if previously busy. |
* | GetVariable() | Yes, even if previously busy |
* | GetNextVariableName() | Yes, even if previously busy |
* | QueryVariableInfo() | Yes, even if previously busy |
* | SetVariable() | Yes, even if previously busy |
* | UpdateCapsule() | Yes, even if previously busy |
* | QueryCapsuleCapabilities() | Yes, even if previously busy |
* | ResetSystem() | Yes, even if previously busy |
* +----------------------------+------------------------------------------+
*
* In order to prevent deadlocks under NMI, the wrappers for these functions
* may only grab the efi_runtime_lock or rtc_lock spinlocks if !efi_in_nmi().
* However, not all of the services listed are reachable through NMI code paths,
* so the the special handling as suggested by the UEFI spec is only implemented
* for QueryVariableInfo() and SetVariable(), as these can be reached in NMI
* context through efi_pstore_write().
*/
/* /*
* As per commit ef68c8f87ed1 ("x86: Serialize EFI time accesses on rtc_lock"), * As per commit ef68c8f87ed1 ("x86: Serialize EFI time accesses on rtc_lock"),
* the EFI specification requires that callers of the time related runtime * the EFI specification requires that callers of the time related runtime
...@@ -32,7 +101,9 @@ static efi_status_t virt_efi_get_time(efi_time_t *tm, efi_time_cap_t *tc) ...@@ -32,7 +101,9 @@ static efi_status_t virt_efi_get_time(efi_time_t *tm, efi_time_cap_t *tc)
efi_status_t status; efi_status_t status;
spin_lock_irqsave(&rtc_lock, flags); spin_lock_irqsave(&rtc_lock, flags);
spin_lock(&efi_runtime_lock);
status = efi_call_virt(get_time, tm, tc); status = efi_call_virt(get_time, tm, tc);
spin_unlock(&efi_runtime_lock);
spin_unlock_irqrestore(&rtc_lock, flags); spin_unlock_irqrestore(&rtc_lock, flags);
return status; return status;
} }
...@@ -43,7 +114,9 @@ static efi_status_t virt_efi_set_time(efi_time_t *tm) ...@@ -43,7 +114,9 @@ static efi_status_t virt_efi_set_time(efi_time_t *tm)
efi_status_t status; efi_status_t status;
spin_lock_irqsave(&rtc_lock, flags); spin_lock_irqsave(&rtc_lock, flags);
spin_lock(&efi_runtime_lock);
status = efi_call_virt(set_time, tm); status = efi_call_virt(set_time, tm);
spin_unlock(&efi_runtime_lock);
spin_unlock_irqrestore(&rtc_lock, flags); spin_unlock_irqrestore(&rtc_lock, flags);
return status; return status;
} }
...@@ -56,7 +129,9 @@ static efi_status_t virt_efi_get_wakeup_time(efi_bool_t *enabled, ...@@ -56,7 +129,9 @@ static efi_status_t virt_efi_get_wakeup_time(efi_bool_t *enabled,
efi_status_t status; efi_status_t status;
spin_lock_irqsave(&rtc_lock, flags); spin_lock_irqsave(&rtc_lock, flags);
spin_lock(&efi_runtime_lock);
status = efi_call_virt(get_wakeup_time, enabled, pending, tm); status = efi_call_virt(get_wakeup_time, enabled, pending, tm);
spin_unlock(&efi_runtime_lock);
spin_unlock_irqrestore(&rtc_lock, flags); spin_unlock_irqrestore(&rtc_lock, flags);
return status; return status;
} }
...@@ -67,7 +142,9 @@ static efi_status_t virt_efi_set_wakeup_time(efi_bool_t enabled, efi_time_t *tm) ...@@ -67,7 +142,9 @@ static efi_status_t virt_efi_set_wakeup_time(efi_bool_t enabled, efi_time_t *tm)
efi_status_t status; efi_status_t status;
spin_lock_irqsave(&rtc_lock, flags); spin_lock_irqsave(&rtc_lock, flags);
spin_lock(&efi_runtime_lock);
status = efi_call_virt(set_wakeup_time, enabled, tm); status = efi_call_virt(set_wakeup_time, enabled, tm);
spin_unlock(&efi_runtime_lock);
spin_unlock_irqrestore(&rtc_lock, flags); spin_unlock_irqrestore(&rtc_lock, flags);
return status; return status;
} }
...@@ -78,14 +155,27 @@ static efi_status_t virt_efi_get_variable(efi_char16_t *name, ...@@ -78,14 +155,27 @@ static efi_status_t virt_efi_get_variable(efi_char16_t *name,
unsigned long *data_size, unsigned long *data_size,
void *data) void *data)
{ {
return efi_call_virt(get_variable, name, vendor, attr, data_size, data); unsigned long flags;
efi_status_t status;
spin_lock_irqsave(&efi_runtime_lock, flags);
status = efi_call_virt(get_variable, name, vendor, attr, data_size,
data);
spin_unlock_irqrestore(&efi_runtime_lock, flags);
return status;
} }
static efi_status_t virt_efi_get_next_variable(unsigned long *name_size, static efi_status_t virt_efi_get_next_variable(unsigned long *name_size,
efi_char16_t *name, efi_char16_t *name,
efi_guid_t *vendor) efi_guid_t *vendor)
{ {
return efi_call_virt(get_next_variable, name_size, name, vendor); unsigned long flags;
efi_status_t status;
spin_lock_irqsave(&efi_runtime_lock, flags);
status = efi_call_virt(get_next_variable, name_size, name, vendor);
spin_unlock_irqrestore(&efi_runtime_lock, flags);
return status;
} }
static efi_status_t virt_efi_set_variable(efi_char16_t *name, static efi_status_t virt_efi_set_variable(efi_char16_t *name,
...@@ -94,24 +184,61 @@ static efi_status_t virt_efi_set_variable(efi_char16_t *name, ...@@ -94,24 +184,61 @@ static efi_status_t virt_efi_set_variable(efi_char16_t *name,
unsigned long data_size, unsigned long data_size,
void *data) void *data)
{ {
return efi_call_virt(set_variable, name, vendor, attr, data_size, data); unsigned long flags;
efi_status_t status;
spin_lock_irqsave(&efi_runtime_lock, flags);
status = efi_call_virt(set_variable, name, vendor, attr, data_size,
data);
spin_unlock_irqrestore(&efi_runtime_lock, flags);
return status;
}
static efi_status_t
virt_efi_set_variable_nonblocking(efi_char16_t *name, efi_guid_t *vendor,
u32 attr, unsigned long data_size,
void *data)
{
unsigned long flags;
efi_status_t status;
if (!spin_trylock_irqsave(&efi_runtime_lock, flags))
return EFI_NOT_READY;
status = efi_call_virt(set_variable, name, vendor, attr, data_size,
data);
spin_unlock_irqrestore(&efi_runtime_lock, flags);
return status;
} }
static efi_status_t virt_efi_query_variable_info(u32 attr, static efi_status_t virt_efi_query_variable_info(u32 attr,
u64 *storage_space, u64 *storage_space,
u64 *remaining_space, u64 *remaining_space,
u64 *max_variable_size) u64 *max_variable_size)
{ {
unsigned long flags;
efi_status_t status;
if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION)
return EFI_UNSUPPORTED; return EFI_UNSUPPORTED;
return efi_call_virt(query_variable_info, attr, storage_space, spin_lock_irqsave(&efi_runtime_lock, flags);
status = efi_call_virt(query_variable_info, attr, storage_space,
remaining_space, max_variable_size); remaining_space, max_variable_size);
spin_unlock_irqrestore(&efi_runtime_lock, flags);
return status;
} }
static efi_status_t virt_efi_get_next_high_mono_count(u32 *count) static efi_status_t virt_efi_get_next_high_mono_count(u32 *count)
{ {
return efi_call_virt(get_next_high_mono_count, count); unsigned long flags;
efi_status_t status;
spin_lock_irqsave(&efi_runtime_lock, flags);
status = efi_call_virt(get_next_high_mono_count, count);
spin_unlock_irqrestore(&efi_runtime_lock, flags);
return status;
} }
static void virt_efi_reset_system(int reset_type, static void virt_efi_reset_system(int reset_type,
...@@ -119,17 +246,27 @@ static void virt_efi_reset_system(int reset_type, ...@@ -119,17 +246,27 @@ static void virt_efi_reset_system(int reset_type,
unsigned long data_size, unsigned long data_size,
efi_char16_t *data) efi_char16_t *data)
{ {
unsigned long flags;
spin_lock_irqsave(&efi_runtime_lock, flags);
__efi_call_virt(reset_system, reset_type, status, data_size, data); __efi_call_virt(reset_system, reset_type, status, data_size, data);
spin_unlock_irqrestore(&efi_runtime_lock, flags);
} }
static efi_status_t virt_efi_update_capsule(efi_capsule_header_t **capsules, static efi_status_t virt_efi_update_capsule(efi_capsule_header_t **capsules,
unsigned long count, unsigned long count,
unsigned long sg_list) unsigned long sg_list)
{ {
unsigned long flags;
efi_status_t status;
if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION)
return EFI_UNSUPPORTED; return EFI_UNSUPPORTED;
return efi_call_virt(update_capsule, capsules, count, sg_list); spin_lock_irqsave(&efi_runtime_lock, flags);
status = efi_call_virt(update_capsule, capsules, count, sg_list);
spin_unlock_irqrestore(&efi_runtime_lock, flags);
return status;
} }
static efi_status_t virt_efi_query_capsule_caps(efi_capsule_header_t **capsules, static efi_status_t virt_efi_query_capsule_caps(efi_capsule_header_t **capsules,
...@@ -137,11 +274,17 @@ static efi_status_t virt_efi_query_capsule_caps(efi_capsule_header_t **capsules, ...@@ -137,11 +274,17 @@ static efi_status_t virt_efi_query_capsule_caps(efi_capsule_header_t **capsules,
u64 *max_size, u64 *max_size,
int *reset_type) int *reset_type)
{ {
unsigned long flags;
efi_status_t status;
if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION)
return EFI_UNSUPPORTED; return EFI_UNSUPPORTED;
return efi_call_virt(query_capsule_caps, capsules, count, max_size, spin_lock_irqsave(&efi_runtime_lock, flags);
status = efi_call_virt(query_capsule_caps, capsules, count, max_size,
reset_type); reset_type);
spin_unlock_irqrestore(&efi_runtime_lock, flags);
return status;
} }
void efi_native_runtime_setup(void) void efi_native_runtime_setup(void)
...@@ -153,6 +296,7 @@ void efi_native_runtime_setup(void) ...@@ -153,6 +296,7 @@ void efi_native_runtime_setup(void)
efi.get_variable = virt_efi_get_variable; efi.get_variable = virt_efi_get_variable;
efi.get_next_variable = virt_efi_get_next_variable; efi.get_next_variable = virt_efi_get_next_variable;
efi.set_variable = virt_efi_set_variable; efi.set_variable = virt_efi_set_variable;
efi.set_variable_nonblocking = virt_efi_set_variable_nonblocking;
efi.get_next_high_mono_count = virt_efi_get_next_high_mono_count; efi.get_next_high_mono_count = virt_efi_get_next_high_mono_count;
efi.reset_system = virt_efi_reset_system; efi.reset_system = virt_efi_reset_system;
efi.query_variable_info = virt_efi_query_variable_info; efi.query_variable_info = virt_efi_query_variable_info;
......
...@@ -321,11 +321,11 @@ static unsigned long var_name_strnsize(efi_char16_t *variable_name, ...@@ -321,11 +321,11 @@ static unsigned long var_name_strnsize(efi_char16_t *variable_name,
* Print a warning when duplicate EFI variables are encountered and * Print a warning when duplicate EFI variables are encountered and
* disable the sysfs workqueue since the firmware is buggy. * disable the sysfs workqueue since the firmware is buggy.
*/ */
static void dup_variable_bug(efi_char16_t *s16, efi_guid_t *vendor_guid, static void dup_variable_bug(efi_char16_t *str16, efi_guid_t *vendor_guid,
unsigned long len16) unsigned long len16)
{ {
size_t i, len8 = len16 / sizeof(efi_char16_t); size_t i, len8 = len16 / sizeof(efi_char16_t);
char *s8; char *str8;
/* /*
* Disable the workqueue since the algorithm it uses for * Disable the workqueue since the algorithm it uses for
...@@ -334,16 +334,16 @@ static void dup_variable_bug(efi_char16_t *s16, efi_guid_t *vendor_guid, ...@@ -334,16 +334,16 @@ static void dup_variable_bug(efi_char16_t *s16, efi_guid_t *vendor_guid,
*/ */
efivar_wq_enabled = false; efivar_wq_enabled = false;
s8 = kzalloc(len8, GFP_KERNEL); str8 = kzalloc(len8, GFP_KERNEL);
if (!s8) if (!str8)
return; return;
for (i = 0; i < len8; i++) for (i = 0; i < len8; i++)
s8[i] = s16[i]; str8[i] = str16[i];
printk(KERN_WARNING "efivars: duplicate variable: %s-%pUl\n", printk(KERN_WARNING "efivars: duplicate variable: %s-%pUl\n",
s8, vendor_guid); str8, vendor_guid);
kfree(s8); kfree(str8);
} }
/** /**
...@@ -595,6 +595,39 @@ int efivar_entry_set(struct efivar_entry *entry, u32 attributes, ...@@ -595,6 +595,39 @@ int efivar_entry_set(struct efivar_entry *entry, u32 attributes,
} }
EXPORT_SYMBOL_GPL(efivar_entry_set); EXPORT_SYMBOL_GPL(efivar_entry_set);
/*
* efivar_entry_set_nonblocking - call set_variable_nonblocking()
*
* This function is guaranteed to not block and is suitable for calling
* from crash/panic handlers.
*
* Crucially, this function will not block if it cannot acquire
* __efivars->lock. Instead, it returns -EBUSY.
*/
static int
efivar_entry_set_nonblocking(efi_char16_t *name, efi_guid_t vendor,
u32 attributes, unsigned long size, void *data)
{
const struct efivar_operations *ops = __efivars->ops;
unsigned long flags;
efi_status_t status;
if (!spin_trylock_irqsave(&__efivars->lock, flags))
return -EBUSY;
status = check_var_size(attributes, size + ucs2_strsize(name, 1024));
if (status != EFI_SUCCESS) {
spin_unlock_irqrestore(&__efivars->lock, flags);
return -ENOSPC;
}
status = ops->set_variable_nonblocking(name, &vendor, attributes,
size, data);
spin_unlock_irqrestore(&__efivars->lock, flags);
return efi_status_to_err(status);
}
/** /**
* efivar_entry_set_safe - call set_variable() if enough space in firmware * efivar_entry_set_safe - call set_variable() if enough space in firmware
* @name: buffer containing the variable name * @name: buffer containing the variable name
...@@ -622,6 +655,20 @@ int efivar_entry_set_safe(efi_char16_t *name, efi_guid_t vendor, u32 attributes, ...@@ -622,6 +655,20 @@ int efivar_entry_set_safe(efi_char16_t *name, efi_guid_t vendor, u32 attributes,
if (!ops->query_variable_store) if (!ops->query_variable_store)
return -ENOSYS; return -ENOSYS;
/*
* If the EFI variable backend provides a non-blocking
* ->set_variable() operation and we're in a context where we
* cannot block, then we need to use it to avoid live-locks,
* since the implication is that the regular ->set_variable()
* will block.
*
* If no ->set_variable_nonblocking() is provided then
* ->set_variable() is assumed to be non-blocking.
*/
if (!block && ops->set_variable_nonblocking)
return efivar_entry_set_nonblocking(name, vendor, attributes,
size, data);
if (!block) { if (!block) {
if (!spin_trylock_irqsave(&__efivars->lock, flags)) if (!spin_trylock_irqsave(&__efivars->lock, flags))
return -EBUSY; return -EBUSY;
......
...@@ -830,7 +830,7 @@ config RTC_DRV_DA9063 ...@@ -830,7 +830,7 @@ config RTC_DRV_DA9063
config RTC_DRV_EFI config RTC_DRV_EFI
tristate "EFI RTC" tristate "EFI RTC"
depends on EFI depends on EFI && !X86
help help
If you say yes here you will get support for the EFI If you say yes here you will get support for the EFI
Real Time Clock. Real Time Clock.
......
...@@ -236,3 +236,4 @@ MODULE_ALIAS("platform:rtc-efi"); ...@@ -236,3 +236,4 @@ MODULE_ALIAS("platform:rtc-efi");
MODULE_AUTHOR("dann frazier <dannf@hp.com>"); MODULE_AUTHOR("dann frazier <dannf@hp.com>");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("EFI RTC driver"); MODULE_DESCRIPTION("EFI RTC driver");
MODULE_ALIAS("platform:rtc-efi");
...@@ -92,6 +92,7 @@ typedef struct { ...@@ -92,6 +92,7 @@ typedef struct {
#define EFI_MEMORY_WC ((u64)0x0000000000000002ULL) /* write-coalescing */ #define EFI_MEMORY_WC ((u64)0x0000000000000002ULL) /* write-coalescing */
#define EFI_MEMORY_WT ((u64)0x0000000000000004ULL) /* write-through */ #define EFI_MEMORY_WT ((u64)0x0000000000000004ULL) /* write-through */
#define EFI_MEMORY_WB ((u64)0x0000000000000008ULL) /* write-back */ #define EFI_MEMORY_WB ((u64)0x0000000000000008ULL) /* write-back */
#define EFI_MEMORY_UCE ((u64)0x0000000000000010ULL) /* uncached, exported */
#define EFI_MEMORY_WP ((u64)0x0000000000001000ULL) /* write-protect */ #define EFI_MEMORY_WP ((u64)0x0000000000001000ULL) /* write-protect */
#define EFI_MEMORY_RP ((u64)0x0000000000002000ULL) /* read-protect */ #define EFI_MEMORY_RP ((u64)0x0000000000002000ULL) /* read-protect */
#define EFI_MEMORY_XP ((u64)0x0000000000004000ULL) /* execute-protect */ #define EFI_MEMORY_XP ((u64)0x0000000000004000ULL) /* execute-protect */
...@@ -502,6 +503,10 @@ typedef efi_status_t efi_get_next_variable_t (unsigned long *name_size, efi_char ...@@ -502,6 +503,10 @@ typedef efi_status_t efi_get_next_variable_t (unsigned long *name_size, efi_char
typedef efi_status_t efi_set_variable_t (efi_char16_t *name, efi_guid_t *vendor, typedef efi_status_t efi_set_variable_t (efi_char16_t *name, efi_guid_t *vendor,
u32 attr, unsigned long data_size, u32 attr, unsigned long data_size,
void *data); void *data);
typedef efi_status_t
efi_set_variable_nonblocking_t(efi_char16_t *name, efi_guid_t *vendor,
u32 attr, unsigned long data_size, void *data);
typedef efi_status_t efi_get_next_high_mono_count_t (u32 *count); typedef efi_status_t efi_get_next_high_mono_count_t (u32 *count);
typedef void efi_reset_system_t (int reset_type, efi_status_t status, typedef void efi_reset_system_t (int reset_type, efi_status_t status,
unsigned long data_size, efi_char16_t *data); unsigned long data_size, efi_char16_t *data);
...@@ -821,6 +826,7 @@ extern struct efi { ...@@ -821,6 +826,7 @@ extern struct efi {
efi_get_variable_t *get_variable; efi_get_variable_t *get_variable;
efi_get_next_variable_t *get_next_variable; efi_get_next_variable_t *get_next_variable;
efi_set_variable_t *set_variable; efi_set_variable_t *set_variable;
efi_set_variable_nonblocking_t *set_variable_nonblocking;
efi_query_variable_info_t *query_variable_info; efi_query_variable_info_t *query_variable_info;
efi_update_capsule_t *update_capsule; efi_update_capsule_t *update_capsule;
efi_query_capsule_caps_t *query_capsule_caps; efi_query_capsule_caps_t *query_capsule_caps;
...@@ -886,6 +892,13 @@ extern bool efi_poweroff_required(void); ...@@ -886,6 +892,13 @@ extern bool efi_poweroff_required(void);
(md) <= (efi_memory_desc_t *)((m)->map_end - (m)->desc_size); \ (md) <= (efi_memory_desc_t *)((m)->map_end - (m)->desc_size); \
(md) = (void *)(md) + (m)->desc_size) (md) = (void *)(md) + (m)->desc_size)
/*
* Format an EFI memory descriptor's type and attributes to a user-provided
* character buffer, as per snprintf(), and return the buffer.
*/
char * __init efi_md_typeattr_format(char *buf, size_t size,
const efi_memory_desc_t *md);
/** /**
* efi_range_is_wc - check the WC bit on an address range * efi_range_is_wc - check the WC bit on an address range
* @start: starting kvirt address * @start: starting kvirt address
...@@ -1034,6 +1047,7 @@ struct efivar_operations { ...@@ -1034,6 +1047,7 @@ struct efivar_operations {
efi_get_variable_t *get_variable; efi_get_variable_t *get_variable;
efi_get_next_variable_t *get_next_variable; efi_get_next_variable_t *get_next_variable;
efi_set_variable_t *set_variable; efi_set_variable_t *set_variable;
efi_set_variable_nonblocking_t *set_variable_nonblocking;
efi_query_variable_store_t *query_variable_store; efi_query_variable_store_t *query_variable_store;
}; };
...@@ -1227,4 +1241,7 @@ efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg, ...@@ -1227,4 +1241,7 @@ efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
unsigned long *load_addr, unsigned long *load_addr,
unsigned long *load_size); unsigned long *load_size);
efi_status_t efi_parse_options(char *cmdline);
bool efi_runtime_disabled(void);
#endif /* _LINUX_EFI_H */ #endif /* _LINUX_EFI_H */
...@@ -403,6 +403,7 @@ int vsscanf(const char *, const char *, va_list); ...@@ -403,6 +403,7 @@ int vsscanf(const char *, const char *, va_list);
extern int get_option(char **str, int *pint); extern int get_option(char **str, int *pint);
extern char *get_options(const char *str, int nints, int *ints); extern char *get_options(const char *str, int nints, int *ints);
extern unsigned long long memparse(const char *ptr, char **retptr); extern unsigned long long memparse(const char *ptr, char **retptr);
extern bool parse_option_str(const char *str, const char *option);
extern int core_kernel_text(unsigned long addr); extern int core_kernel_text(unsigned long addr);
extern int core_kernel_data(unsigned long addr); extern int core_kernel_data(unsigned long addr);
......
...@@ -160,3 +160,32 @@ unsigned long long memparse(const char *ptr, char **retptr) ...@@ -160,3 +160,32 @@ unsigned long long memparse(const char *ptr, char **retptr)
return ret; return ret;
} }
EXPORT_SYMBOL(memparse); EXPORT_SYMBOL(memparse);
/**
* parse_option_str - Parse a string and check an option is set or not
* @str: String to be parsed
* @option: option name
*
* This function parses a string containing a comma-separated list of
* strings like a=b,c.
*
* Return true if there's such option in the string, or return false.
*/
bool parse_option_str(const char *str, const char *option)
{
while (*str) {
if (!strncmp(str, option, strlen(option))) {
str += strlen(option);
if (!*str || *str == ',')
return true;
}
while (*str && *str != ',')
str++;
if (*str == ',')
str++;
}
return false;
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册