Commit de956b8f authored by Linus Torvalds's avatar Linus Torvalds

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

Pull EFI updates from Ingo Molnar:
 "Main changes in this cycle were:

   - Refactor the EFI memory map code into architecture neutral files
     and allow drivers to permanently reserve EFI boot services regions
     on x86, as well as ARM/arm64. (Matt Fleming)

   - Add ARM support for the EFI ESRT driver. (Ard Biesheuvel)

   - Make the EFI runtime services and efivar API interruptible by
     swapping spinlocks for semaphores. (Sylvain Chouleur)

   - Provide the EFI identity mapping for kexec which allows kexec to
     work on SGI/UV platforms with requiring the "noefi" kernel command
     line parameter. (Alex Thorlton)

   - Add debugfs node to dump EFI page tables on arm64. (Ard Biesheuvel)

   - Merge the EFI test driver being carried out of tree until now in
     the FWTS project. (Ivan Hu)

   - Expand the list of flags for classifying EFI regions as "RAM" on
     arm64 so we align with the UEFI spec. (Ard Biesheuvel)

   - Optimise out the EFI mixed mode if it's unsupported (CONFIG_X86_32)
     or disabled (CONFIG_EFI_MIXED=n) and switch the early EFI boot
     services function table for direct calls, alleviating us from
     having to maintain the custom function table. (Lukas Wunner)

   - Miscellaneous cleanups and fixes"

* 'efi-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (30 commits)
  x86/efi: Round EFI memmap reservations to EFI_PAGE_SIZE
  x86/efi: Allow invocation of arbitrary boot services
  x86/efi: Optimize away setup_gop32/64 if unused
  x86/efi: Use kmalloc_array() in efi_call_phys_prolog()
  efi/arm64: Treat regions with WT/WC set but WB cleared as memory
  efi: Add efi_test driver for exporting UEFI runtime service interfaces
  x86/efi: Defer efi_esrt_init until after memblock_x86_fill
  efi/arm64: Add debugfs node to dump UEFI runtime page tables
  x86/efi: Remove unused find_bits() function
  fs/efivarfs: Fix double kfree() in error path
  x86/efi: Map in physical addresses in efi_map_region_fixed
  lib/ucs2_string: Speed up ucs2_utf8size()
  firmware-gsmi: Delete an unnecessary check before the function call "dma_pool_destroy"
  x86/efi: Initialize status to ensure garbage is not returned on small size
  efi: Replace runtime services spinlock with semaphore
  efi: Don't use spinlocks for efi vars
  efi: Use a file local lock for efivars
  efi/arm*: esrt: Add missing call to efi_esrt_init()
  efi/esrt: Use memremap not ioremap to access ESRT table in memory
  x86/efi-bgrt: Use efi_mem_reserve() to avoid copying image data
  ...
parents d7a0dab8 2ab78a72
...@@ -4587,6 +4587,13 @@ M: Peter Jones <pjones@redhat.com> ...@@ -4587,6 +4587,13 @@ M: Peter Jones <pjones@redhat.com>
S: Maintained S: Maintained
F: drivers/video/fbdev/efifb.c F: drivers/video/fbdev/efifb.c
EFI TEST DRIVER
L: linux-efi@vger.kernel.org
M: Ivan Hu <ivan.hu@canonical.com>
M: Matt Fleming <matt@codeblueprint.co.uk>
S: Maintained
F: drivers/firmware/efi/test/
EFS FILESYSTEM EFS FILESYSTEM
W: http://aeschi.ch.eu.org/efs/ W: http://aeschi.ch.eu.org/efs/
S: Orphan S: Orphan
......
...@@ -29,22 +29,11 @@ __pure const struct efi_config *__efi_early(void) ...@@ -29,22 +29,11 @@ __pure const struct efi_config *__efi_early(void)
static void setup_boot_services##bits(struct efi_config *c) \ static void setup_boot_services##bits(struct efi_config *c) \
{ \ { \
efi_system_table_##bits##_t *table; \ efi_system_table_##bits##_t *table; \
efi_boot_services_##bits##_t *bt; \
\ \
table = (typeof(table))sys_table; \ table = (typeof(table))sys_table; \
\ \
c->boot_services = table->boottime; \
c->text_output = table->con_out; \ c->text_output = table->con_out; \
\
bt = (typeof(bt))(unsigned long)(table->boottime); \
\
c->allocate_pool = bt->allocate_pool; \
c->allocate_pages = bt->allocate_pages; \
c->get_memory_map = bt->get_memory_map; \
c->free_pool = bt->free_pool; \
c->free_pages = bt->free_pages; \
c->locate_handle = bt->locate_handle; \
c->handle_protocol = bt->handle_protocol; \
c->exit_boot_services = bt->exit_boot_services; \
} }
BOOT_SERVICES(32); BOOT_SERVICES(32);
BOOT_SERVICES(64); BOOT_SERVICES(64);
...@@ -286,29 +275,6 @@ void efi_char16_printk(efi_system_table_t *table, efi_char16_t *str) ...@@ -286,29 +275,6 @@ void efi_char16_printk(efi_system_table_t *table, efi_char16_t *str)
} }
} }
static void find_bits(unsigned long mask, u8 *pos, u8 *size)
{
u8 first, len;
first = 0;
len = 0;
if (mask) {
while (!(mask & 0x1)) {
mask = mask >> 1;
first++;
}
while (mask & 0x1) {
mask = mask >> 1;
len++;
}
}
*pos = first;
*size = len;
}
static efi_status_t static efi_status_t
__setup_efi_pci32(efi_pci_io_protocol_32 *pci, struct pci_setup_rom **__rom) __setup_efi_pci32(efi_pci_io_protocol_32 *pci, struct pci_setup_rom **__rom)
{ {
...@@ -578,7 +544,7 @@ setup_uga32(void **uga_handle, unsigned long size, u32 *width, u32 *height) ...@@ -578,7 +544,7 @@ setup_uga32(void **uga_handle, unsigned long size, u32 *width, u32 *height)
efi_guid_t uga_proto = EFI_UGA_PROTOCOL_GUID; efi_guid_t uga_proto = EFI_UGA_PROTOCOL_GUID;
unsigned long nr_ugas; unsigned long nr_ugas;
u32 *handles = (u32 *)uga_handle;; u32 *handles = (u32 *)uga_handle;;
efi_status_t status; efi_status_t status = EFI_INVALID_PARAMETER;
int i; int i;
first_uga = NULL; first_uga = NULL;
...@@ -623,7 +589,7 @@ setup_uga64(void **uga_handle, unsigned long size, u32 *width, u32 *height) ...@@ -623,7 +589,7 @@ setup_uga64(void **uga_handle, unsigned long size, u32 *width, u32 *height)
efi_guid_t uga_proto = EFI_UGA_PROTOCOL_GUID; efi_guid_t uga_proto = EFI_UGA_PROTOCOL_GUID;
unsigned long nr_ugas; unsigned long nr_ugas;
u64 *handles = (u64 *)uga_handle;; u64 *handles = (u64 *)uga_handle;;
efi_status_t status; efi_status_t status = EFI_INVALID_PARAMETER;
int i; int i;
first_uga = NULL; first_uga = NULL;
......
...@@ -82,7 +82,7 @@ ENTRY(efi_pe_entry) ...@@ -82,7 +82,7 @@ ENTRY(efi_pe_entry)
/* Relocate efi_config->call() */ /* Relocate efi_config->call() */
leal efi32_config(%esi), %eax leal efi32_config(%esi), %eax
add %esi, 88(%eax) add %esi, 32(%eax)
pushl %eax pushl %eax
call make_boot_params call make_boot_params
...@@ -108,7 +108,7 @@ ENTRY(efi32_stub_entry) ...@@ -108,7 +108,7 @@ ENTRY(efi32_stub_entry)
/* Relocate efi_config->call() */ /* Relocate efi_config->call() */
leal efi32_config(%esi), %eax leal efi32_config(%esi), %eax
add %esi, 88(%eax) add %esi, 32(%eax)
pushl %eax pushl %eax
2: 2:
call efi_main call efi_main
...@@ -264,7 +264,7 @@ relocated: ...@@ -264,7 +264,7 @@ relocated:
#ifdef CONFIG_EFI_STUB #ifdef CONFIG_EFI_STUB
.data .data
efi32_config: efi32_config:
.fill 11,8,0 .fill 4,8,0
.long efi_call_phys .long efi_call_phys
.long 0 .long 0
.byte 0 .byte 0
......
...@@ -265,7 +265,7 @@ ENTRY(efi_pe_entry) ...@@ -265,7 +265,7 @@ ENTRY(efi_pe_entry)
/* /*
* Relocate efi_config->call(). * Relocate efi_config->call().
*/ */
addq %rbp, efi64_config+88(%rip) addq %rbp, efi64_config+32(%rip)
movq %rax, %rdi movq %rax, %rdi
call make_boot_params call make_boot_params
...@@ -285,7 +285,7 @@ handover_entry: ...@@ -285,7 +285,7 @@ handover_entry:
* Relocate efi_config->call(). * Relocate efi_config->call().
*/ */
movq efi_config(%rip), %rax movq efi_config(%rip), %rax
addq %rbp, 88(%rax) addq %rbp, 32(%rax)
2: 2:
movq efi_config(%rip), %rdi movq efi_config(%rip), %rdi
call efi_main call efi_main
...@@ -457,14 +457,14 @@ efi_config: ...@@ -457,14 +457,14 @@ efi_config:
#ifdef CONFIG_EFI_MIXED #ifdef CONFIG_EFI_MIXED
.global efi32_config .global efi32_config
efi32_config: efi32_config:
.fill 11,8,0 .fill 4,8,0
.quad efi64_thunk .quad efi64_thunk
.byte 0 .byte 0
#endif #endif
.global efi64_config .global efi64_config
efi64_config: efi64_config:
.fill 11,8,0 .fill 4,8,0
.quad efi_call .quad efi_call
.byte 1 .byte 1
#endif /* CONFIG_EFI_STUB */ #endif /* CONFIG_EFI_STUB */
......
...@@ -117,7 +117,6 @@ extern int __init efi_memblock_x86_reserve_range(void); ...@@ -117,7 +117,6 @@ extern int __init efi_memblock_x86_reserve_range(void);
extern pgd_t * __init efi_call_phys_prolog(void); extern pgd_t * __init efi_call_phys_prolog(void);
extern void __init efi_call_phys_epilog(pgd_t *save_pgd); extern void __init efi_call_phys_epilog(pgd_t *save_pgd);
extern void __init efi_print_memmap(void); extern void __init efi_print_memmap(void);
extern void __init efi_unmap_memmap(void);
extern void __init 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);
...@@ -192,14 +191,7 @@ static inline efi_status_t efi_thunk_set_virtual_address_map( ...@@ -192,14 +191,7 @@ static inline efi_status_t efi_thunk_set_virtual_address_map(
struct efi_config { struct efi_config {
u64 image_handle; u64 image_handle;
u64 table; u64 table;
u64 allocate_pool; u64 boot_services;
u64 allocate_pages;
u64 get_memory_map;
u64 free_pool;
u64 free_pages;
u64 locate_handle;
u64 handle_protocol;
u64 exit_boot_services;
u64 text_output; u64 text_output;
efi_status_t (*call)(unsigned long, ...); efi_status_t (*call)(unsigned long, ...);
bool is64; bool is64;
...@@ -207,14 +199,27 @@ struct efi_config { ...@@ -207,14 +199,27 @@ struct efi_config {
__pure const struct efi_config *__efi_early(void); __pure const struct efi_config *__efi_early(void);
static inline bool efi_is_64bit(void)
{
if (!IS_ENABLED(CONFIG_X86_64))
return false;
if (!IS_ENABLED(CONFIG_EFI_MIXED))
return true;
return __efi_early()->is64;
}
#define efi_call_early(f, ...) \ #define efi_call_early(f, ...) \
__efi_early()->call(__efi_early()->f, __VA_ARGS__); __efi_early()->call(efi_is_64bit() ? \
((efi_boot_services_64_t *)(unsigned long) \
__efi_early()->boot_services)->f : \
((efi_boot_services_32_t *)(unsigned long) \
__efi_early()->boot_services)->f, __VA_ARGS__)
#define __efi_call_early(f, ...) \ #define __efi_call_early(f, ...) \
__efi_early()->call((unsigned long)f, __VA_ARGS__); __efi_early()->call((unsigned long)f, __VA_ARGS__);
#define efi_is_64bit() __efi_early()->is64
extern bool efi_reboot_required(void); extern bool efi_reboot_required(void);
#else #else
......
...@@ -1096,19 +1096,19 @@ void __init setup_arch(char **cmdline_p) ...@@ -1096,19 +1096,19 @@ void __init setup_arch(char **cmdline_p)
memblock_set_current_limit(ISA_END_ADDRESS); memblock_set_current_limit(ISA_END_ADDRESS);
memblock_x86_fill(); memblock_x86_fill();
if (efi_enabled(EFI_BOOT)) { reserve_bios_regions();
if (efi_enabled(EFI_MEMMAP)) {
efi_fake_memmap(); efi_fake_memmap();
efi_find_mirror(); efi_find_mirror();
} efi_esrt_init();
reserve_bios_regions();
/* /*
* The EFI specification says that boot service code won't be called * The EFI specification says that boot service code won't be
* after ExitBootServices(). This is, in fact, a lie. * called after ExitBootServices(). This is, in fact, a lie.
*/ */
if (efi_enabled(EFI_MEMMAP))
efi_reserve_boot_services(); efi_reserve_boot_services();
}
/* preallocate 4k for mptable mpc */ /* preallocate 4k for mptable mpc */
early_reserve_e820_mpc_new(); early_reserve_e820_mpc_new();
......
...@@ -82,21 +82,12 @@ void __init efi_bgrt_init(void) ...@@ -82,21 +82,12 @@ void __init efi_bgrt_init(void)
} }
bgrt_image_size = bmp_header.size; bgrt_image_size = bmp_header.size;
bgrt_image = kmalloc(bgrt_image_size, GFP_KERNEL | __GFP_NOWARN); bgrt_image = memremap(bgrt_tab->image_address, bmp_header.size, MEMREMAP_WB);
if (!bgrt_image) { if (!bgrt_image) {
pr_notice("Ignoring BGRT: failed to allocate memory for image (wanted %zu bytes)\n",
bgrt_image_size);
return;
}
image = memremap(bgrt_tab->image_address, bmp_header.size, MEMREMAP_WB);
if (!image) {
pr_notice("Ignoring BGRT: failed to map image memory\n"); pr_notice("Ignoring BGRT: failed to map image memory\n");
kfree(bgrt_image);
bgrt_image = NULL; bgrt_image = NULL;
return; return;
} }
memcpy(bgrt_image, image, bgrt_image_size); efi_mem_reserve(bgrt_tab->image_address, bgrt_image_size);
memunmap(image);
} }
...@@ -172,7 +172,9 @@ static void __init do_add_efi_memmap(void) ...@@ -172,7 +172,9 @@ static void __init do_add_efi_memmap(void)
int __init efi_memblock_x86_reserve_range(void) int __init efi_memblock_x86_reserve_range(void)
{ {
struct efi_info *e = &boot_params.efi_info; struct efi_info *e = &boot_params.efi_info;
struct efi_memory_map_data data;
phys_addr_t pmap; phys_addr_t pmap;
int rv;
if (efi_enabled(EFI_PARAVIRT)) if (efi_enabled(EFI_PARAVIRT))
return 0; return 0;
...@@ -187,11 +189,17 @@ int __init efi_memblock_x86_reserve_range(void) ...@@ -187,11 +189,17 @@ int __init efi_memblock_x86_reserve_range(void)
#else #else
pmap = (e->efi_memmap | ((__u64)e->efi_memmap_hi << 32)); pmap = (e->efi_memmap | ((__u64)e->efi_memmap_hi << 32));
#endif #endif
efi.memmap.phys_map = pmap; data.phys_map = pmap;
efi.memmap.nr_map = e->efi_memmap_size / data.size = e->efi_memmap_size;
e->efi_memdesc_size; data.desc_size = e->efi_memdesc_size;
efi.memmap.desc_size = e->efi_memdesc_size; data.desc_version = e->efi_memdesc_version;
efi.memmap.desc_version = e->efi_memdesc_version;
rv = efi_memmap_init_early(&data);
if (rv)
return rv;
if (add_efi_memmap)
do_add_efi_memmap();
WARN(efi.memmap.desc_version != 1, WARN(efi.memmap.desc_version != 1,
"Unexpected EFI_MEMORY_DESCRIPTOR version %ld", "Unexpected EFI_MEMORY_DESCRIPTOR version %ld",
...@@ -218,19 +226,6 @@ void __init efi_print_memmap(void) ...@@ -218,19 +226,6 @@ void __init efi_print_memmap(void)
} }
} }
void __init efi_unmap_memmap(void)
{
unsigned long size;
clear_bit(EFI_MEMMAP, &efi.flags);
size = efi.memmap.nr_map * efi.memmap.desc_size;
if (efi.memmap.map) {
early_memunmap(efi.memmap.map, size);
efi.memmap.map = NULL;
}
}
static int __init efi_systab_init(void *phys) static int __init efi_systab_init(void *phys)
{ {
if (efi_enabled(EFI_64BIT)) { if (efi_enabled(EFI_64BIT)) {
...@@ -414,33 +409,6 @@ static int __init efi_runtime_init(void) ...@@ -414,33 +409,6 @@ static int __init efi_runtime_init(void)
return 0; return 0;
} }
static int __init efi_memmap_init(void)
{
unsigned long addr, size;
if (efi_enabled(EFI_PARAVIRT))
return 0;
/* Map the EFI memory map */
size = efi.memmap.nr_map * efi.memmap.desc_size;
addr = (unsigned long)efi.memmap.phys_map;
efi.memmap.map = early_memremap(addr, size);
if (efi.memmap.map == NULL) {
pr_err("Could not map the memory map!\n");
return -ENOMEM;
}
efi.memmap.map_end = efi.memmap.map + size;
if (add_efi_memmap)
do_add_efi_memmap();
set_bit(EFI_MEMMAP, &efi.flags);
return 0;
}
void __init efi_init(void) void __init efi_init(void)
{ {
efi_char16_t *c16; efi_char16_t *c16;
...@@ -498,16 +466,14 @@ void __init efi_init(void) ...@@ -498,16 +466,14 @@ 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 (efi_runtime_disabled() || efi_runtime_init()) if (efi_runtime_disabled() || efi_runtime_init()) {
efi_memmap_unmap();
return; return;
} }
if (efi_memmap_init()) }
return;
if (efi_enabled(EFI_DBG)) if (efi_enabled(EFI_DBG))
efi_print_memmap(); efi_print_memmap();
efi_esrt_init();
} }
void __init efi_late_init(void) void __init efi_late_init(void)
...@@ -624,42 +590,6 @@ static void __init get_systab_virt_addr(efi_memory_desc_t *md) ...@@ -624,42 +590,6 @@ static void __init get_systab_virt_addr(efi_memory_desc_t *md)
} }
} }
static void __init save_runtime_map(void)
{
#ifdef CONFIG_KEXEC_CORE
unsigned long desc_size;
efi_memory_desc_t *md;
void *tmp, *q = NULL;
int count = 0;
if (efi_enabled(EFI_OLD_MEMMAP))
return;
desc_size = efi.memmap.desc_size;
for_each_efi_memory_desc(md) {
if (!(md->attribute & EFI_MEMORY_RUNTIME) ||
(md->type == EFI_BOOT_SERVICES_CODE) ||
(md->type == EFI_BOOT_SERVICES_DATA))
continue;
tmp = krealloc(q, (count + 1) * desc_size, GFP_KERNEL);
if (!tmp)
goto out;
q = tmp;
memcpy(q + count * desc_size, md, desc_size);
count++;
}
efi_runtime_map_setup(q, count, desc_size);
return;
out:
kfree(q);
pr_err("Error saving runtime map, efi runtime on kexec non-functional!!\n");
#endif
}
static void *realloc_pages(void *old_memmap, int old_shift) static void *realloc_pages(void *old_memmap, int old_shift)
{ {
void *ret; void *ret;
...@@ -745,6 +675,46 @@ static void *efi_map_next_entry(void *entry) ...@@ -745,6 +675,46 @@ static void *efi_map_next_entry(void *entry)
return entry; return entry;
} }
static bool should_map_region(efi_memory_desc_t *md)
{
/*
* Runtime regions always require runtime mappings (obviously).
*/
if (md->attribute & EFI_MEMORY_RUNTIME)
return true;
/*
* 32-bit EFI doesn't suffer from the bug that requires us to
* reserve boot services regions, and mixed mode support
* doesn't exist for 32-bit kernels.
*/
if (IS_ENABLED(CONFIG_X86_32))
return false;
/*
* Map all of RAM so that we can access arguments in the 1:1
* mapping when making EFI runtime calls.
*/
if (IS_ENABLED(CONFIG_EFI_MIXED) && !efi_is_native()) {
if (md->type == EFI_CONVENTIONAL_MEMORY ||
md->type == EFI_LOADER_DATA ||
md->type == EFI_LOADER_CODE)
return true;
}
/*
* Map boot services regions as a workaround for buggy
* firmware that accesses them even when they shouldn't.
*
* See efi_{reserve,free}_boot_services().
*/
if (md->type == EFI_BOOT_SERVICES_CODE ||
md->type == EFI_BOOT_SERVICES_DATA)
return true;
return false;
}
/* /*
* Map the efi memory ranges of the runtime services and update new_mmap with * Map the efi memory ranges of the runtime services and update new_mmap with
* virtual addresses. * virtual addresses.
...@@ -761,13 +731,9 @@ static void * __init efi_map_regions(int *count, int *pg_shift) ...@@ -761,13 +731,9 @@ static void * __init efi_map_regions(int *count, int *pg_shift)
p = NULL; p = NULL;
while ((p = efi_map_next_entry(p))) { while ((p = efi_map_next_entry(p))) {
md = p; md = p;
if (!(md->attribute & EFI_MEMORY_RUNTIME)) {
#ifdef CONFIG_X86_64 if (!should_map_region(md))
if (md->type != EFI_BOOT_SERVICES_CODE &&
md->type != EFI_BOOT_SERVICES_DATA)
#endif
continue; continue;
}
efi_map_region(md); efi_map_region(md);
get_systab_virt_addr(md); get_systab_virt_addr(md);
...@@ -803,7 +769,7 @@ static void __init kexec_enter_virtual_mode(void) ...@@ -803,7 +769,7 @@ static void __init kexec_enter_virtual_mode(void)
* non-native EFI * non-native EFI
*/ */
if (!efi_is_native()) { if (!efi_is_native()) {
efi_unmap_memmap(); efi_memmap_unmap();
clear_bit(EFI_RUNTIME_SERVICES, &efi.flags); clear_bit(EFI_RUNTIME_SERVICES, &efi.flags);
return; return;
} }
...@@ -823,7 +789,18 @@ static void __init kexec_enter_virtual_mode(void) ...@@ -823,7 +789,18 @@ static void __init kexec_enter_virtual_mode(void)
get_systab_virt_addr(md); get_systab_virt_addr(md);
} }
save_runtime_map(); /*
* Unregister the early EFI memmap from efi_init() and install
* the new EFI memory map.
*/
efi_memmap_unmap();
if (efi_memmap_init_late(efi.memmap.phys_map,
efi.memmap.desc_size * efi.memmap.nr_map)) {
pr_err("Failed to remap late EFI memory map\n");
clear_bit(EFI_RUNTIME_SERVICES, &efi.flags);
return;
}
BUG_ON(!efi.systab); BUG_ON(!efi.systab);
...@@ -884,6 +861,7 @@ static void __init __efi_enter_virtual_mode(void) ...@@ -884,6 +861,7 @@ static void __init __efi_enter_virtual_mode(void)
int count = 0, pg_shift = 0; int count = 0, pg_shift = 0;
void *new_memmap = NULL; void *new_memmap = NULL;
efi_status_t status; efi_status_t status;
phys_addr_t pa;
efi.systab = NULL; efi.systab = NULL;
...@@ -901,11 +879,24 @@ static void __init __efi_enter_virtual_mode(void) ...@@ -901,11 +879,24 @@ static void __init __efi_enter_virtual_mode(void)
return; return;
} }
save_runtime_map(); pa = __pa(new_memmap);
/*
* Unregister the early EFI memmap from efi_init() and install
* the new EFI memory map that we are about to pass to the
* firmware via SetVirtualAddressMap().
*/
efi_memmap_unmap();
if (efi_memmap_init_late(pa, efi.memmap.desc_size * count)) {
pr_err("Failed to remap late EFI memory map\n");
clear_bit(EFI_RUNTIME_SERVICES, &efi.flags);
return;
}
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, 1 << pg_shift)) {
clear_bit(EFI_RUNTIME_SERVICES, &efi.flags); clear_bit(EFI_RUNTIME_SERVICES, &efi.flags);
return; return;
} }
...@@ -917,14 +908,14 @@ static void __init __efi_enter_virtual_mode(void) ...@@ -917,14 +908,14 @@ static void __init __efi_enter_virtual_mode(void)
efi.memmap.desc_size * count, efi.memmap.desc_size * count,
efi.memmap.desc_size, efi.memmap.desc_size,
efi.memmap.desc_version, efi.memmap.desc_version,
(efi_memory_desc_t *)__pa(new_memmap)); (efi_memory_desc_t *)pa);
} else { } else {
status = efi_thunk_set_virtual_address_map( status = efi_thunk_set_virtual_address_map(
efi_phys.set_virtual_address_map, efi_phys.set_virtual_address_map,
efi.memmap.desc_size * count, efi.memmap.desc_size * count,
efi.memmap.desc_size, efi.memmap.desc_size,
efi.memmap.desc_version, efi.memmap.desc_version,
(efi_memory_desc_t *)__pa(new_memmap)); (efi_memory_desc_t *)pa);
} }
if (status != EFI_SUCCESS) { if (status != EFI_SUCCESS) {
...@@ -956,15 +947,6 @@ static void __init __efi_enter_virtual_mode(void) ...@@ -956,15 +947,6 @@ static void __init __efi_enter_virtual_mode(void)
efi_runtime_update_mappings(); efi_runtime_update_mappings();
efi_dump_pagetable(); efi_dump_pagetable();
/*
* We mapped the descriptor array into the EFI pagetable above
* but we're not unmapping it here because if we're running in
* EFI mixed mode we need all of memory to be accessible when
* we pass parameters to the EFI runtime services in the
* thunking code.
*/
free_pages((unsigned long)new_memmap, pg_shift);
/* clean DUMMY object */ /* clean DUMMY object */
efi_delete_dummy_variable(); efi_delete_dummy_variable();
} }
......
...@@ -85,7 +85,7 @@ pgd_t * __init efi_call_phys_prolog(void) ...@@ -85,7 +85,7 @@ pgd_t * __init efi_call_phys_prolog(void)
early_code_mapping_set_exec(1); early_code_mapping_set_exec(1);
n_pgds = DIV_ROUND_UP((max_pfn << PAGE_SHIFT), PGDIR_SIZE); n_pgds = DIV_ROUND_UP((max_pfn << PAGE_SHIFT), PGDIR_SIZE);
save_pgd = kmalloc(n_pgds * sizeof(pgd_t), GFP_KERNEL); save_pgd = kmalloc_array(n_pgds, sizeof(*save_pgd), GFP_KERNEL);
for (pgd = 0; pgd < n_pgds; pgd++) { for (pgd = 0; pgd < n_pgds; pgd++) {
save_pgd[pgd] = *pgd_offset_k(pgd * PGDIR_SIZE); save_pgd[pgd] = *pgd_offset_k(pgd * PGDIR_SIZE);
...@@ -214,7 +214,6 @@ void efi_sync_low_kernel_mappings(void) ...@@ -214,7 +214,6 @@ void efi_sync_low_kernel_mappings(void)
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)
{ {
unsigned long pfn, text; unsigned long pfn, text;
efi_memory_desc_t *md;
struct page *page; struct page *page;
unsigned npages; unsigned npages;
pgd_t *pgd; pgd_t *pgd;
...@@ -248,25 +247,6 @@ int __init efi_setup_page_tables(unsigned long pa_memmap, unsigned num_pages) ...@@ -248,25 +247,6 @@ int __init efi_setup_page_tables(unsigned long pa_memmap, unsigned num_pages)
if (!IS_ENABLED(CONFIG_EFI_MIXED) || efi_is_native()) if (!IS_ENABLED(CONFIG_EFI_MIXED) || efi_is_native())
return 0; return 0;
/*
* Map all of RAM so that we can access arguments in the 1:1
* mapping when making EFI runtime calls.
*/
for_each_efi_memory_desc(md) {
if (md->type != EFI_CONVENTIONAL_MEMORY &&
md->type != EFI_LOADER_DATA &&
md->type != EFI_LOADER_CODE)
continue;
pfn = md->phys_addr >> PAGE_SHIFT;
npages = md->num_pages;
if (kernel_map_pages_in_pgd(pgd, pfn, md->phys_addr, npages, _PAGE_RW)) {
pr_err("Failed to map 1:1 memory\n");
return 1;
}
}
page = alloc_page(GFP_KERNEL|__GFP_DMA32); page = alloc_page(GFP_KERNEL|__GFP_DMA32);
if (!page) if (!page)
panic("Unable to allocate EFI runtime stack < 4GB\n"); panic("Unable to allocate EFI runtime stack < 4GB\n");
...@@ -359,6 +339,7 @@ void __init efi_map_region(efi_memory_desc_t *md) ...@@ -359,6 +339,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)
{ {
__map_region(md, md->phys_addr);
__map_region(md, md->virt_addr); __map_region(md, md->virt_addr);
} }
......
...@@ -163,6 +163,75 @@ efi_status_t efi_query_variable_store(u32 attributes, unsigned long size, ...@@ -163,6 +163,75 @@ efi_status_t efi_query_variable_store(u32 attributes, unsigned long size,
} }
EXPORT_SYMBOL_GPL(efi_query_variable_store); EXPORT_SYMBOL_GPL(efi_query_variable_store);
/*
* The UEFI specification makes it clear that the operating system is
* free to do whatever it wants with boot services code after
* ExitBootServices() has been called. Ignoring this recommendation a
* significant bunch of EFI implementations continue calling into boot
* services code (SetVirtualAddressMap). In order to work around such
* buggy implementations we reserve boot services region during EFI
* init and make sure it stays executable. Then, after
* SetVirtualAddressMap(), it is discarded.
*
* However, some boot services regions contain data that is required
* by drivers, so we need to track which memory ranges can never be
* freed. This is done by tagging those regions with the
* EFI_MEMORY_RUNTIME attribute.
*
* Any driver that wants to mark a region as reserved must use
* efi_mem_reserve() which will insert a new EFI memory descriptor
* into efi.memmap (splitting existing regions if necessary) and tag
* it with EFI_MEMORY_RUNTIME.
*/
void __init efi_arch_mem_reserve(phys_addr_t addr, u64 size)
{
phys_addr_t new_phys, new_size;
struct efi_mem_range mr;
efi_memory_desc_t md;
int num_entries;
void *new;
if (efi_mem_desc_lookup(addr, &md)) {
pr_err("Failed to lookup EFI memory descriptor for %pa\n", &addr);
return;
}
if (addr + size > md.phys_addr + (md.num_pages << EFI_PAGE_SHIFT)) {
pr_err("Region spans EFI memory descriptors, %pa\n", &addr);
return;
}
size += addr % EFI_PAGE_SIZE;
size = round_up(size, EFI_PAGE_SIZE);
addr = round_down(addr, EFI_PAGE_SIZE);
mr.range.start = addr;
mr.range.end = addr + size - 1;
mr.attribute = md.attribute | EFI_MEMORY_RUNTIME;
num_entries = efi_memmap_split_count(&md, &mr.range);
num_entries += efi.memmap.nr_map;
new_size = efi.memmap.desc_size * num_entries;
new_phys = memblock_alloc(new_size, 0);
if (!new_phys) {
pr_err("Could not allocate boot services memmap\n");
return;
}
new = early_memremap(new_phys, new_size);
if (!new) {
pr_err("Failed to map new boot services memmap\n");
return;
}
efi_memmap_insert(&efi.memmap, new, &mr);
early_memunmap(new, new_size);
efi_memmap_install(new_phys, num_entries);
}
/* /*
* Helper function for efi_reserve_boot_services() to figure out if we * Helper function for efi_reserve_boot_services() to figure out if we
* can free regions in efi_free_boot_services(). * can free regions in efi_free_boot_services().
...@@ -184,15 +253,6 @@ static bool can_free_region(u64 start, u64 size) ...@@ -184,15 +253,6 @@ static bool can_free_region(u64 start, u64 size)
return true; return true;
} }
/*
* The UEFI specification makes it clear that the operating system is free to do
* whatever it wants with boot services code after ExitBootServices() has been
* called. Ignoring this recommendation a significant bunch of EFI implementations
* continue calling into boot services code (SetVirtualAddressMap). In order to
* work around such buggy implementations we reserve boot services region during
* EFI init and make sure it stays executable. Then, after SetVirtualAddressMap(), it
* is discarded.
*/
void __init efi_reserve_boot_services(void) void __init efi_reserve_boot_services(void)
{ {
efi_memory_desc_t *md; efi_memory_desc_t *md;
...@@ -249,7 +309,10 @@ void __init efi_reserve_boot_services(void) ...@@ -249,7 +309,10 @@ void __init efi_reserve_boot_services(void)
void __init efi_free_boot_services(void) void __init efi_free_boot_services(void)
{ {
phys_addr_t new_phys, new_size;
efi_memory_desc_t *md; efi_memory_desc_t *md;
int num_entries = 0;
void *new, *new_md;
for_each_efi_memory_desc(md) { for_each_efi_memory_desc(md) {
unsigned long long start = md->phys_addr; unsigned long long start = md->phys_addr;
...@@ -257,12 +320,16 @@ void __init efi_free_boot_services(void) ...@@ -257,12 +320,16 @@ void __init efi_free_boot_services(void)
size_t rm_size; size_t rm_size;
if (md->type != EFI_BOOT_SERVICES_CODE && if (md->type != EFI_BOOT_SERVICES_CODE &&
md->type != EFI_BOOT_SERVICES_DATA) md->type != EFI_BOOT_SERVICES_DATA) {
num_entries++;
continue; continue;
}
/* Do not free, someone else owns it: */ /* Do not free, someone else owns it: */
if (md->attribute & EFI_MEMORY_RUNTIME) if (md->attribute & EFI_MEMORY_RUNTIME) {
num_entries++;
continue; continue;
}
/* /*
* Nasty quirk: if all sub-1MB memory is used for boot * Nasty quirk: if all sub-1MB memory is used for boot
...@@ -287,7 +354,41 @@ void __init efi_free_boot_services(void) ...@@ -287,7 +354,41 @@ void __init efi_free_boot_services(void)
free_bootmem_late(start, size); free_bootmem_late(start, size);
} }
efi_unmap_memmap(); new_size = efi.memmap.desc_size * num_entries;
new_phys = memblock_alloc(new_size, 0);
if (!new_phys) {
pr_err("Failed to allocate new EFI memmap\n");
return;
}
new = memremap(new_phys, new_size, MEMREMAP_WB);
if (!new) {
pr_err("Failed to map new EFI memmap\n");
return;
}
/*
* Build a new EFI memmap that excludes any boot services
* regions that are not tagged EFI_MEMORY_RUNTIME, since those
* regions have now been freed.
*/
new_md = new;
for_each_efi_memory_desc(md) {
if (!(md->attribute & EFI_MEMORY_RUNTIME) &&
(md->type == EFI_BOOT_SERVICES_CODE ||
md->type == EFI_BOOT_SERVICES_DATA))
continue;
memcpy(new_md, md, efi.memmap.desc_size);
new_md += efi.memmap.desc_size;
}
memunmap(new);
if (efi_memmap_install(new_phys, num_entries)) {
pr_err("Could not install new EFI memmap\n");
return;
}
} }
/* /*
...@@ -365,7 +466,7 @@ void __init efi_apply_memmap_quirks(void) ...@@ -365,7 +466,7 @@ void __init efi_apply_memmap_quirks(void)
*/ */
if (!efi_runtime_supported()) { if (!efi_runtime_supported()) {
pr_info("Setup done, disabling due to 32/64-bit mismatch\n"); pr_info("Setup done, disabling due to 32/64-bit mismatch\n");
efi_unmap_memmap(); efi_memmap_unmap();
} }
/* UV2+ BIOS has a fix for this issue. UV1 still needs the quirk. */ /* UV2+ BIOS has a fix for this issue. UV1 still needs the quirk. */
......
...@@ -112,6 +112,23 @@ config EFI_CAPSULE_LOADER ...@@ -112,6 +112,23 @@ config EFI_CAPSULE_LOADER
Most users should say N. Most users should say N.
config EFI_TEST
tristate "EFI Runtime Service Tests Support"
depends on EFI
default n
help
This driver uses the efi.<service> function pointers directly instead
of going through the efivar API, because it is not trying to test the
kernel subsystem, just for testing the UEFI runtime service
interfaces which are provided by the firmware. This driver is used
by the Firmware Test Suite (FWTS) for testing the UEFI runtime
interfaces readiness of the firmware.
Details for FWTS are available from:
<https://wiki.ubuntu.com/FirmwareTestSuite>
Say Y here to enable the runtime services support via /dev/efi_test.
If unsure, say N.
endmenu endmenu
config UEFI_CPER config UEFI_CPER
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
KASAN_SANITIZE_runtime-wrappers.o := n KASAN_SANITIZE_runtime-wrappers.o := n
obj-$(CONFIG_EFI) += efi.o vars.o reboot.o memattr.o obj-$(CONFIG_EFI) += efi.o vars.o reboot.o memattr.o
obj-$(CONFIG_EFI) += capsule.o obj-$(CONFIG_EFI) += capsule.o memmap.o
obj-$(CONFIG_EFI_VARS) += efivars.o obj-$(CONFIG_EFI_VARS) += efivars.o
obj-$(CONFIG_EFI_ESRT) += esrt.o obj-$(CONFIG_EFI_ESRT) += esrt.o
obj-$(CONFIG_EFI_VARS_PSTORE) += efi-pstore.o obj-$(CONFIG_EFI_VARS_PSTORE) += efi-pstore.o
...@@ -20,6 +20,7 @@ obj-$(CONFIG_EFI_RUNTIME_WRAPPERS) += runtime-wrappers.o ...@@ -20,6 +20,7 @@ obj-$(CONFIG_EFI_RUNTIME_WRAPPERS) += runtime-wrappers.o
obj-$(CONFIG_EFI_STUB) += libstub/ obj-$(CONFIG_EFI_STUB) += libstub/
obj-$(CONFIG_EFI_FAKE_MEMMAP) += fake_mem.o obj-$(CONFIG_EFI_FAKE_MEMMAP) += fake_mem.o
obj-$(CONFIG_EFI_BOOTLOADER_CONTROL) += efibc.o obj-$(CONFIG_EFI_BOOTLOADER_CONTROL) += efibc.o
obj-$(CONFIG_EFI_TEST) += test/
arm-obj-$(CONFIG_EFI) := arm-init.o arm-runtime.o arm-obj-$(CONFIG_EFI) := arm-init.o arm-runtime.o
obj-$(CONFIG_ARM) += $(arm-obj-y) obj-$(CONFIG_ARM) += $(arm-obj-y)
......
...@@ -26,9 +26,9 @@ ...@@ -26,9 +26,9 @@
u64 efi_system_table; u64 efi_system_table;
static int __init is_normal_ram(efi_memory_desc_t *md) static int __init is_memory(efi_memory_desc_t *md)
{ {
if (md->attribute & EFI_MEMORY_WB) if (md->attribute & (EFI_MEMORY_WB|EFI_MEMORY_WT|EFI_MEMORY_WC))
return 1; return 1;
return 0; return 0;
} }
...@@ -152,9 +152,9 @@ static int __init uefi_init(void) ...@@ -152,9 +152,9 @@ static int __init uefi_init(void)
} }
/* /*
* Return true for RAM regions we want to permanently reserve. * Return true for regions that can be used as System RAM.
*/ */
static __init int is_reserve_region(efi_memory_desc_t *md) static __init int is_usable_memory(efi_memory_desc_t *md)
{ {
switch (md->type) { switch (md->type) {
case EFI_LOADER_CODE: case EFI_LOADER_CODE:
...@@ -163,18 +163,22 @@ static __init int is_reserve_region(efi_memory_desc_t *md) ...@@ -163,18 +163,22 @@ static __init int is_reserve_region(efi_memory_desc_t *md)
case EFI_BOOT_SERVICES_DATA: case EFI_BOOT_SERVICES_DATA:
case EFI_CONVENTIONAL_MEMORY: case EFI_CONVENTIONAL_MEMORY:
case EFI_PERSISTENT_MEMORY: case EFI_PERSISTENT_MEMORY:
return 0; /*
* According to the spec, these regions are no longer reserved
* after calling ExitBootServices(). However, we can only use
* them as System RAM if they can be mapped writeback cacheable.
*/
return (md->attribute & EFI_MEMORY_WB);
default: default:
break; break;
} }
return is_normal_ram(md); return false;
} }
static __init void reserve_regions(void) static __init void reserve_regions(void)
{ {
efi_memory_desc_t *md; efi_memory_desc_t *md;
u64 paddr, npages, size; u64 paddr, npages, size;
int resv;
if (efi_enabled(EFI_DBG)) if (efi_enabled(EFI_DBG))
pr_info("Processing EFI memory map:\n"); pr_info("Processing EFI memory map:\n");
...@@ -191,32 +195,29 @@ static __init void reserve_regions(void) ...@@ -191,32 +195,29 @@ static __init void reserve_regions(void)
paddr = md->phys_addr; paddr = md->phys_addr;
npages = md->num_pages; npages = md->num_pages;
resv = is_reserve_region(md);
if (efi_enabled(EFI_DBG)) { if (efi_enabled(EFI_DBG)) {
char buf[64]; char buf[64];
pr_info(" 0x%012llx-0x%012llx %s%s\n", pr_info(" 0x%012llx-0x%012llx %s\n",
paddr, paddr + (npages << EFI_PAGE_SHIFT) - 1, paddr, paddr + (npages << EFI_PAGE_SHIFT) - 1,
efi_md_typeattr_format(buf, sizeof(buf), md), efi_md_typeattr_format(buf, sizeof(buf), md));
resv ? "*" : "");
} }
memrange_efi_to_native(&paddr, &npages); memrange_efi_to_native(&paddr, &npages);
size = npages << PAGE_SHIFT; size = npages << PAGE_SHIFT;
if (is_normal_ram(md)) if (is_memory(md)) {
early_init_dt_add_memory_arch(paddr, size); early_init_dt_add_memory_arch(paddr, size);
if (resv) if (!is_usable_memory(md))
memblock_mark_nomap(paddr, size); memblock_mark_nomap(paddr, size);
} }
}
set_bit(EFI_MEMMAP, &efi.flags);
} }
void __init efi_init(void) void __init efi_init(void)
{ {
struct efi_memory_map_data data;
struct efi_fdt_params params; struct efi_fdt_params params;
/* Grab UEFI information placed in FDT by stub */ /* Grab UEFI information placed in FDT by stub */
...@@ -225,9 +226,12 @@ void __init efi_init(void) ...@@ -225,9 +226,12 @@ void __init efi_init(void)
efi_system_table = params.system_table; efi_system_table = params.system_table;
efi.memmap.phys_map = params.mmap; data.desc_version = params.desc_ver;
efi.memmap.map = early_memremap_ro(params.mmap, params.mmap_size); data.desc_size = params.desc_size;
if (efi.memmap.map == NULL) { data.size = params.mmap_size;
data.phys_map = params.mmap;
if (efi_memmap_init_early(&data) < 0) {
/* /*
* If we are booting via UEFI, the UEFI memory map is the only * If we are booting via UEFI, the UEFI memory map is the only
* description of memory we have, so there is little point in * description of memory we have, so there is little point in
...@@ -235,9 +239,6 @@ void __init efi_init(void) ...@@ -235,9 +239,6 @@ void __init efi_init(void)
*/ */
panic("Unable to map EFI memory map.\n"); panic("Unable to map EFI memory map.\n");
} }
efi.memmap.map_end = efi.memmap.map + params.mmap_size;
efi.memmap.desc_size = params.desc_size;
efi.memmap.desc_version = params.desc_ver;
WARN(efi.memmap.desc_version != 1, WARN(efi.memmap.desc_version != 1,
"Unexpected EFI_MEMORY_DESCRIPTOR version %ld", "Unexpected EFI_MEMORY_DESCRIPTOR version %ld",
...@@ -248,7 +249,8 @@ void __init efi_init(void) ...@@ -248,7 +249,8 @@ void __init efi_init(void)
reserve_regions(); reserve_regions();
efi_memattr_init(); efi_memattr_init();
early_memunmap(efi.memmap.map, params.mmap_size); efi_esrt_init();
efi_memmap_unmap();
memblock_reserve(params.mmap & PAGE_MASK, memblock_reserve(params.mmap & PAGE_MASK,
PAGE_ALIGN(params.mmap_size + PAGE_ALIGN(params.mmap_size +
......
...@@ -39,6 +39,26 @@ static struct mm_struct efi_mm = { ...@@ -39,6 +39,26 @@ static struct mm_struct efi_mm = {
.mmlist = LIST_HEAD_INIT(efi_mm.mmlist), .mmlist = LIST_HEAD_INIT(efi_mm.mmlist),
}; };
#ifdef CONFIG_ARM64_PTDUMP
#include <asm/ptdump.h>
static struct ptdump_info efi_ptdump_info = {
.mm = &efi_mm,
.markers = (struct addr_marker[]){
{ 0, "UEFI runtime start" },
{ TASK_SIZE_64, "UEFI runtime end" }
},
.base_addr = 0,
};
static int __init ptdump_init(void)
{
return ptdump_register(&efi_ptdump_info, "efi_page_tables");
}
device_initcall(ptdump_init);
#endif
static bool __init efi_virtmap_init(void) static bool __init efi_virtmap_init(void)
{ {
efi_memory_desc_t *md; efi_memory_desc_t *md;
...@@ -114,14 +134,12 @@ static int __init arm_enable_runtime_services(void) ...@@ -114,14 +134,12 @@ static int __init arm_enable_runtime_services(void)
pr_info("Remapping and enabling EFI services.\n"); pr_info("Remapping and enabling EFI services.\n");
mapsize = efi.memmap.map_end - efi.memmap.map; mapsize = efi.memmap.desc_size * efi.memmap.nr_map;
efi.memmap.map = memremap(efi.memmap.phys_map, mapsize, MEMREMAP_WB); if (efi_memmap_init_late(efi.memmap.phys_map, mapsize)) {
if (!efi.memmap.map) {
pr_err("Failed to remap EFI memory map\n"); pr_err("Failed to remap EFI memory map\n");
return -ENOMEM; return -ENOMEM;
} }
efi.memmap.map_end = efi.memmap.map + mapsize;
if (!efi_virtmap_init()) { if (!efi_virtmap_init()) {
pr_err("UEFI virtual mapping missing or invalid -- runtime services will not be available\n"); pr_err("UEFI virtual mapping missing or invalid -- runtime services will not be available\n");
......
...@@ -125,16 +125,19 @@ static void efi_pstore_scan_sysfs_enter(struct efivar_entry *pos, ...@@ -125,16 +125,19 @@ static void efi_pstore_scan_sysfs_enter(struct efivar_entry *pos,
* @entry: deleting entry * @entry: deleting entry
* @turn_off_scanning: Check if a scanning flag should be turned off * @turn_off_scanning: Check if a scanning flag should be turned off
*/ */
static inline void __efi_pstore_scan_sysfs_exit(struct efivar_entry *entry, static inline int __efi_pstore_scan_sysfs_exit(struct efivar_entry *entry,
bool turn_off_scanning) bool turn_off_scanning)
{ {
if (entry->deleting) { if (entry->deleting) {
list_del(&entry->list); list_del(&entry->list);
efivar_entry_iter_end(); efivar_entry_iter_end();
efivar_unregister(entry); efivar_unregister(entry);
efivar_entry_iter_begin(); if (efivar_entry_iter_begin())
return -EINTR;
} else if (turn_off_scanning) } else if (turn_off_scanning)
entry->scanning = false; entry->scanning = false;
return 0;
} }
/** /**
...@@ -144,13 +147,18 @@ static inline void __efi_pstore_scan_sysfs_exit(struct efivar_entry *entry, ...@@ -144,13 +147,18 @@ static inline void __efi_pstore_scan_sysfs_exit(struct efivar_entry *entry,
* @head: list head * @head: list head
* @stop: a flag checking if scanning will stop * @stop: a flag checking if scanning will stop
*/ */
static void efi_pstore_scan_sysfs_exit(struct efivar_entry *pos, static int efi_pstore_scan_sysfs_exit(struct efivar_entry *pos,
struct efivar_entry *next, struct efivar_entry *next,
struct list_head *head, bool stop) struct list_head *head, bool stop)
{ {
__efi_pstore_scan_sysfs_exit(pos, true); int ret = __efi_pstore_scan_sysfs_exit(pos, true);
if (ret)
return ret;
if (stop) if (stop)
__efi_pstore_scan_sysfs_exit(next, &next->list != head); ret = __efi_pstore_scan_sysfs_exit(next, &next->list != head);
return ret;
} }
/** /**
...@@ -172,13 +180,17 @@ static int efi_pstore_sysfs_entry_iter(void *data, struct efivar_entry **pos) ...@@ -172,13 +180,17 @@ static int efi_pstore_sysfs_entry_iter(void *data, struct efivar_entry **pos)
struct efivar_entry *entry, *n; struct efivar_entry *entry, *n;
struct list_head *head = &efivar_sysfs_list; struct list_head *head = &efivar_sysfs_list;
int size = 0; int size = 0;
int ret;
if (!*pos) { if (!*pos) {
list_for_each_entry_safe(entry, n, head, list) { list_for_each_entry_safe(entry, n, head, list) {
efi_pstore_scan_sysfs_enter(entry, n, head); efi_pstore_scan_sysfs_enter(entry, n, head);
size = efi_pstore_read_func(entry, data); size = efi_pstore_read_func(entry, data);
efi_pstore_scan_sysfs_exit(entry, n, head, size < 0); ret = efi_pstore_scan_sysfs_exit(entry, n, head,
size < 0);
if (ret)
return ret;
if (size) if (size)
break; break;
} }
...@@ -190,7 +202,9 @@ static int efi_pstore_sysfs_entry_iter(void *data, struct efivar_entry **pos) ...@@ -190,7 +202,9 @@ static int efi_pstore_sysfs_entry_iter(void *data, struct efivar_entry **pos)
efi_pstore_scan_sysfs_enter((*pos), n, head); efi_pstore_scan_sysfs_enter((*pos), n, head);
size = efi_pstore_read_func((*pos), data); size = efi_pstore_read_func((*pos), data);
efi_pstore_scan_sysfs_exit((*pos), n, head, size < 0); ret = efi_pstore_scan_sysfs_exit((*pos), n, head, size < 0);
if (ret)
return ret;
if (size) if (size)
break; break;
} }
...@@ -232,7 +246,10 @@ static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type, ...@@ -232,7 +246,10 @@ static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
if (!*data.buf) if (!*data.buf)
return -ENOMEM; return -ENOMEM;
efivar_entry_iter_begin(); if (efivar_entry_iter_begin()) {
kfree(*data.buf);
return -EINTR;
}
size = efi_pstore_sysfs_entry_iter(&data, size = efi_pstore_sysfs_entry_iter(&data,
(struct efivar_entry **)&psi->data); (struct efivar_entry **)&psi->data);
efivar_entry_iter_end(); efivar_entry_iter_end();
...@@ -347,7 +364,8 @@ static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count, ...@@ -347,7 +364,8 @@ static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count,
edata.time = time; edata.time = time;
edata.name = efi_name; edata.name = efi_name;
efivar_entry_iter_begin(); if (efivar_entry_iter_begin())
return -EINTR;
found = __efivar_entry_iter(efi_pstore_erase_func, &efivar_sysfs_list, &edata, &entry); found = __efivar_entry_iter(efi_pstore_erase_func, &efivar_sysfs_list, &edata, &entry);
if (found && !entry->scanning) { if (found && !entry->scanning) {
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/ucs2_string.h> #include <linux/ucs2_string.h>
#include <linux/memblock.h>
#include <asm/early_ioremap.h> #include <asm/early_ioremap.h>
...@@ -347,56 +348,31 @@ subsys_initcall(efisubsys_init); ...@@ -347,56 +348,31 @@ subsys_initcall(efisubsys_init);
/* /*
* Find the efi memory descriptor for a given physical address. Given a * Find the efi memory descriptor for a given physical address. Given a
* physicall address, determine if it exists within an EFI Memory Map entry, * physical address, determine if it exists within an EFI Memory Map entry,
* and if so, populate the supplied memory descriptor with the appropriate * and if so, populate the supplied memory descriptor with the appropriate
* data. * data.
*/ */
int __init efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md) int __init efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md)
{ {
struct efi_memory_map *map = &efi.memmap; efi_memory_desc_t *md;
phys_addr_t p, e;
if (!efi_enabled(EFI_MEMMAP)) { if (!efi_enabled(EFI_MEMMAP)) {
pr_err_once("EFI_MEMMAP is not enabled.\n"); pr_err_once("EFI_MEMMAP is not enabled.\n");
return -EINVAL; return -EINVAL;
} }
if (!map) {
pr_err_once("efi.memmap is not set.\n");
return -EINVAL;
}
if (!out_md) { if (!out_md) {
pr_err_once("out_md is null.\n"); pr_err_once("out_md is null.\n");
return -EINVAL; return -EINVAL;
} }
if (WARN_ON_ONCE(!map->phys_map))
return -EINVAL;
if (WARN_ON_ONCE(map->nr_map == 0) || WARN_ON_ONCE(map->desc_size == 0))
return -EINVAL;
e = map->phys_map + map->nr_map * map->desc_size; for_each_efi_memory_desc(md) {
for (p = map->phys_map; p < e; p += map->desc_size) {
efi_memory_desc_t *md;
u64 size; u64 size;
u64 end; u64 end;
/*
* If a driver calls this after efi_free_boot_services,
* ->map will be NULL, and the target may also not be mapped.
* So just always get our own virtual map on the CPU.
*
*/
md = early_memremap(p, sizeof (*md));
if (!md) {
pr_err_once("early_memremap(%pa, %zu) failed.\n",
&p, sizeof (*md));
return -ENOMEM;
}
if (!(md->attribute & EFI_MEMORY_RUNTIME) && if (!(md->attribute & EFI_MEMORY_RUNTIME) &&
md->type != EFI_BOOT_SERVICES_DATA && md->type != EFI_BOOT_SERVICES_DATA &&
md->type != EFI_RUNTIME_SERVICES_DATA) { md->type != EFI_RUNTIME_SERVICES_DATA) {
early_memunmap(md, sizeof (*md));
continue; continue;
} }
...@@ -404,11 +380,8 @@ int __init efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md) ...@@ -404,11 +380,8 @@ int __init efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md)
end = md->phys_addr + size; end = md->phys_addr + size;
if (phys_addr >= md->phys_addr && phys_addr < end) { if (phys_addr >= md->phys_addr && phys_addr < end) {
memcpy(out_md, md, sizeof(*out_md)); memcpy(out_md, md, sizeof(*out_md));
early_memunmap(md, sizeof (*md));
return 0; return 0;
} }
early_memunmap(md, sizeof (*md));
} }
pr_err_once("requested map not found.\n"); pr_err_once("requested map not found.\n");
return -ENOENT; return -ENOENT;
...@@ -424,6 +397,35 @@ u64 __init efi_mem_desc_end(efi_memory_desc_t *md) ...@@ -424,6 +397,35 @@ u64 __init efi_mem_desc_end(efi_memory_desc_t *md)
return end; return end;
} }
void __init __weak efi_arch_mem_reserve(phys_addr_t addr, u64 size) {}
/**
* efi_mem_reserve - Reserve an EFI memory region
* @addr: Physical address to reserve
* @size: Size of reservation
*
* Mark a region as reserved from general kernel allocation and
* prevent it being released by efi_free_boot_services().
*
* This function should be called drivers once they've parsed EFI
* configuration tables to figure out where their data lives, e.g.
* efi_esrt_init().
*/
void __init efi_mem_reserve(phys_addr_t addr, u64 size)
{
if (!memblock_is_region_reserved(addr, size))
memblock_reserve(addr, size);
/*
* Some architectures (x86) reserve all boot services ranges
* until efi_free_boot_services() because of buggy firmware
* implementations. This means the above memblock_reserve() is
* superfluous on x86 and instead what it needs to do is
* ensure the @start, @size is not freed.
*/
efi_arch_mem_reserve(addr, size);
}
static __initdata efi_config_table_type_t common_tables[] = { static __initdata efi_config_table_type_t common_tables[] = {
{ACPI_20_TABLE_GUID, "ACPI 2.0", &efi.acpi20}, {ACPI_20_TABLE_GUID, "ACPI 2.0", &efi.acpi20},
{ACPI_TABLE_GUID, "ACPI", &efi.acpi}, {ACPI_TABLE_GUID, "ACPI", &efi.acpi},
...@@ -811,6 +813,9 @@ int efi_status_to_err(efi_status_t status) ...@@ -811,6 +813,9 @@ int efi_status_to_err(efi_status_t status)
case EFI_NOT_FOUND: case EFI_NOT_FOUND:
err = -ENOENT; err = -ENOENT;
break; break;
case EFI_ABORTED:
err = -EINTR;
break;
default: default:
err = -EINVAL; err = -EINVAL;
} }
......
...@@ -510,7 +510,8 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj, ...@@ -510,7 +510,8 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
vendor = del_var->VendorGuid; vendor = del_var->VendorGuid;
} }
efivar_entry_iter_begin(); if (efivar_entry_iter_begin())
return -EINTR;
entry = efivar_entry_find(name, vendor, &efivar_sysfs_list, true); entry = efivar_entry_find(name, vendor, &efivar_sysfs_list, true);
if (!entry) if (!entry)
err = -EINVAL; err = -EINVAL;
...@@ -575,7 +576,10 @@ efivar_create_sysfs_entry(struct efivar_entry *new_var) ...@@ -575,7 +576,10 @@ efivar_create_sysfs_entry(struct efivar_entry *new_var)
return ret; return ret;
kobject_uevent(&new_var->kobj, KOBJ_ADD); kobject_uevent(&new_var->kobj, KOBJ_ADD);
efivar_entry_add(new_var, &efivar_sysfs_list); if (efivar_entry_add(new_var, &efivar_sysfs_list)) {
efivar_unregister(new_var);
return -EINTR;
}
return 0; return 0;
} }
...@@ -690,7 +694,10 @@ static int efivars_sysfs_callback(efi_char16_t *name, efi_guid_t vendor, ...@@ -690,7 +694,10 @@ static int efivars_sysfs_callback(efi_char16_t *name, efi_guid_t vendor,
static int efivar_sysfs_destroy(struct efivar_entry *entry, void *data) static int efivar_sysfs_destroy(struct efivar_entry *entry, void *data)
{ {
efivar_entry_remove(entry); int err = efivar_entry_remove(entry);
if (err)
return err;
efivar_unregister(entry); efivar_unregister(entry);
return 0; return 0;
} }
...@@ -698,7 +705,14 @@ static int efivar_sysfs_destroy(struct efivar_entry *entry, void *data) ...@@ -698,7 +705,14 @@ static int efivar_sysfs_destroy(struct efivar_entry *entry, void *data)
static void efivars_sysfs_exit(void) static void efivars_sysfs_exit(void)
{ {
/* Remove all entries and destroy */ /* Remove all entries and destroy */
__efivar_entry_iter(efivar_sysfs_destroy, &efivar_sysfs_list, NULL, NULL); int err;
err = __efivar_entry_iter(efivar_sysfs_destroy, &efivar_sysfs_list,
NULL, NULL);
if (err) {
pr_err("efivars: Failed to destroy sysfs entries\n");
return;
}
if (efivars_new_var) if (efivars_new_var)
sysfs_remove_bin_file(&efivars_kset->kobj, efivars_new_var); sysfs_remove_bin_file(&efivars_kset->kobj, efivars_new_var);
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <linux/device.h> #include <linux/device.h>
#include <linux/efi.h> #include <linux/efi.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/kobject.h> #include <linux/kobject.h>
#include <linux/list.h> #include <linux/list.h>
...@@ -235,7 +236,7 @@ static struct attribute_group esrt_attr_group = { ...@@ -235,7 +236,7 @@ static struct attribute_group esrt_attr_group = {
}; };
/* /*
* remap the table, copy it to kmalloced pages, and unmap it. * remap the table, validate it, mark it reserved and unmap it.
*/ */
void __init efi_esrt_init(void) void __init efi_esrt_init(void)
{ {
...@@ -335,7 +336,7 @@ void __init efi_esrt_init(void) ...@@ -335,7 +336,7 @@ void __init efi_esrt_init(void)
end = esrt_data + size; end = esrt_data + size;
pr_info("Reserving ESRT space from %pa to %pa.\n", &esrt_data, &end); pr_info("Reserving ESRT space from %pa to %pa.\n", &esrt_data, &end);
memblock_reserve(esrt_data, esrt_data_size); efi_mem_reserve(esrt_data, esrt_data_size);
pr_debug("esrt-init: loaded.\n"); pr_debug("esrt-init: loaded.\n");
err_memunmap: err_memunmap:
...@@ -382,28 +383,18 @@ static void cleanup_entry_list(void) ...@@ -382,28 +383,18 @@ static void cleanup_entry_list(void)
static int __init esrt_sysfs_init(void) static int __init esrt_sysfs_init(void)
{ {
int error; int error;
struct efi_system_resource_table __iomem *ioesrt;
pr_debug("esrt-sysfs: loading.\n"); pr_debug("esrt-sysfs: loading.\n");
if (!esrt_data || !esrt_data_size) if (!esrt_data || !esrt_data_size)
return -ENOSYS; return -ENOSYS;
ioesrt = ioremap(esrt_data, esrt_data_size); esrt = memremap(esrt_data, esrt_data_size, MEMREMAP_WB);
if (!ioesrt) {
pr_err("ioremap(%pa, %zu) failed.\n", &esrt_data,
esrt_data_size);
return -ENOMEM;
}
esrt = kmalloc(esrt_data_size, GFP_KERNEL);
if (!esrt) { if (!esrt) {
pr_err("kmalloc failed. (wanted %zu bytes)\n", esrt_data_size); pr_err("memremap(%pa, %zu) failed.\n", &esrt_data,
iounmap(ioesrt); esrt_data_size);
return -ENOMEM; return -ENOMEM;
} }
memcpy_fromio(esrt, ioesrt, esrt_data_size);
esrt_kobj = kobject_create_and_add("esrt", efi_kobj); esrt_kobj = kobject_create_and_add("esrt", efi_kobj);
if (!esrt_kobj) { if (!esrt_kobj) {
pr_err("Firmware table registration failed.\n"); pr_err("Firmware table registration failed.\n");
...@@ -429,8 +420,6 @@ static int __init esrt_sysfs_init(void) ...@@ -429,8 +420,6 @@ static int __init esrt_sysfs_init(void)
if (error) if (error)
goto err_cleanup_list; goto err_cleanup_list;
memblock_remove(esrt_data, esrt_data_size);
pr_debug("esrt-sysfs: loaded.\n"); pr_debug("esrt-sysfs: loaded.\n");
return 0; return 0;
......
...@@ -35,17 +35,13 @@ ...@@ -35,17 +35,13 @@
#define EFI_MAX_FAKEMEM CONFIG_EFI_MAX_FAKE_MEM #define EFI_MAX_FAKEMEM CONFIG_EFI_MAX_FAKE_MEM
struct fake_mem { static struct efi_mem_range fake_mems[EFI_MAX_FAKEMEM];
struct range range;
u64 attribute;
};
static struct fake_mem fake_mems[EFI_MAX_FAKEMEM];
static int nr_fake_mem; static int nr_fake_mem;
static int __init cmp_fake_mem(const void *x1, const void *x2) static int __init cmp_fake_mem(const void *x1, const void *x2)
{ {
const struct fake_mem *m1 = x1; const struct efi_mem_range *m1 = x1;
const struct fake_mem *m2 = x2; const struct efi_mem_range *m2 = x2;
if (m1->range.start < m2->range.start) if (m1->range.start < m2->range.start)
return -1; return -1;
...@@ -56,40 +52,21 @@ static int __init cmp_fake_mem(const void *x1, const void *x2) ...@@ -56,40 +52,21 @@ static int __init cmp_fake_mem(const void *x1, const void *x2)
void __init efi_fake_memmap(void) void __init efi_fake_memmap(void)
{ {
u64 start, end, m_start, m_end, m_attr;
int new_nr_map = efi.memmap.nr_map; int new_nr_map = efi.memmap.nr_map;
efi_memory_desc_t *md; efi_memory_desc_t *md;
phys_addr_t new_memmap_phy; phys_addr_t new_memmap_phy;
void *new_memmap; void *new_memmap;
void *old, *new;
int i; int i;
if (!nr_fake_mem || !efi_enabled(EFI_MEMMAP)) if (!nr_fake_mem)
return; return;
/* count up the number of EFI memory descriptor */ /* count up the number of EFI memory descriptor */
for (i = 0; i < nr_fake_mem; i++) {
for_each_efi_memory_desc(md) { for_each_efi_memory_desc(md) {
start = md->phys_addr; struct range *r = &fake_mems[i].range;
end = start + (md->num_pages << EFI_PAGE_SHIFT) - 1;
for (i = 0; i < nr_fake_mem; i++) { new_nr_map += efi_memmap_split_count(md, r);
/* modifying range */
m_start = fake_mems[i].range.start;
m_end = fake_mems[i].range.end;
if (m_start <= start) {
/* split into 2 parts */
if (start < m_end && m_end < end)
new_nr_map++;
}
if (start < m_start && m_start < end) {
/* split into 3 parts */
if (m_end < end)
new_nr_map += 2;
/* split into 2 parts */
if (end <= m_end)
new_nr_map++;
}
} }
} }
...@@ -107,85 +84,13 @@ void __init efi_fake_memmap(void) ...@@ -107,85 +84,13 @@ void __init efi_fake_memmap(void)
return; return;
} }
for (old = efi.memmap.map, new = new_memmap; for (i = 0; i < nr_fake_mem; i++)
old < efi.memmap.map_end; efi_memmap_insert(&efi.memmap, new_memmap, &fake_mems[i]);
old += efi.memmap.desc_size, new += efi.memmap.desc_size) {
/* copy original EFI memory descriptor */
memcpy(new, old, efi.memmap.desc_size);
md = new;
start = md->phys_addr;
end = md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) - 1;
for (i = 0; i < nr_fake_mem; i++) {
/* modifying range */
m_start = fake_mems[i].range.start;
m_end = fake_mems[i].range.end;
m_attr = fake_mems[i].attribute;
if (m_start <= start && end <= m_end)
md->attribute |= m_attr;
if (m_start <= start &&
(start < m_end && m_end < end)) {
/* first part */
md->attribute |= m_attr;
md->num_pages = (m_end - md->phys_addr + 1) >>
EFI_PAGE_SHIFT;
/* latter part */
new += efi.memmap.desc_size;
memcpy(new, old, efi.memmap.desc_size);
md = new;
md->phys_addr = m_end + 1;
md->num_pages = (end - md->phys_addr + 1) >>
EFI_PAGE_SHIFT;
}
if ((start < m_start && m_start < end) && m_end < end) {
/* first part */
md->num_pages = (m_start - md->phys_addr) >>
EFI_PAGE_SHIFT;
/* middle part */
new += efi.memmap.desc_size;
memcpy(new, old, efi.memmap.desc_size);
md = new;
md->attribute |= m_attr;
md->phys_addr = m_start;
md->num_pages = (m_end - m_start + 1) >>
EFI_PAGE_SHIFT;
/* last part */
new += efi.memmap.desc_size;
memcpy(new, old, efi.memmap.desc_size);
md = new;
md->phys_addr = m_end + 1;
md->num_pages = (end - m_end) >>
EFI_PAGE_SHIFT;
}
if ((start < m_start && m_start < end) &&
(end <= m_end)) {
/* first part */
md->num_pages = (m_start - md->phys_addr) >>
EFI_PAGE_SHIFT;
/* latter part */
new += efi.memmap.desc_size;
memcpy(new, old, efi.memmap.desc_size);
md = new;
md->phys_addr = m_start;
md->num_pages = (end - md->phys_addr + 1) >>
EFI_PAGE_SHIFT;
md->attribute |= m_attr;
}
}
}
/* swap into new EFI memmap */ /* swap into new EFI memmap */
efi_unmap_memmap(); early_memunmap(new_memmap, efi.memmap.desc_size * new_nr_map);
efi.memmap.map = new_memmap;
efi.memmap.phys_map = new_memmap_phy; efi_memmap_install(new_memmap_phy, new_nr_map);
efi.memmap.nr_map = new_nr_map;
efi.memmap.map_end = efi.memmap.map + efi.memmap.nr_map * efi.memmap.desc_size;
set_bit(EFI_MEMMAP, &efi.flags);
/* print new EFI memmap */ /* print new EFI memmap */
efi_print_memmap(); efi_print_memmap();
...@@ -223,7 +128,7 @@ static int __init setup_fake_mem(char *p) ...@@ -223,7 +128,7 @@ static int __init setup_fake_mem(char *p)
p++; p++;
} }
sort(fake_mems, nr_fake_mem, sizeof(struct fake_mem), sort(fake_mems, nr_fake_mem, sizeof(struct efi_mem_range),
cmp_fake_mem, NULL); cmp_fake_mem, NULL);
for (i = 0; i < nr_fake_mem; i++) for (i = 0; i < nr_fake_mem; i++)
......
/*
* Common EFI memory map functions.
*/
#define pr_fmt(fmt) "efi: " fmt
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/efi.h>
#include <linux/io.h>
#include <asm/early_ioremap.h>
/**
* __efi_memmap_init - Common code for mapping the EFI memory map
* @data: EFI memory map data
* @late: Use early or late mapping function?
*
* This function takes care of figuring out which function to use to
* map the EFI memory map in efi.memmap based on how far into the boot
* we are.
*
* During bootup @late should be %false since we only have access to
* the early_memremap*() functions as the vmalloc space isn't setup.
* Once the kernel is fully booted we can fallback to the more robust
* memremap*() API.
*
* Returns zero on success, a negative error code on failure.
*/
static int __init
__efi_memmap_init(struct efi_memory_map_data *data, bool late)
{
struct efi_memory_map map;
phys_addr_t phys_map;
if (efi_enabled(EFI_PARAVIRT))
return 0;
phys_map = data->phys_map;
if (late)
map.map = memremap(phys_map, data->size, MEMREMAP_WB);
else
map.map = early_memremap(phys_map, data->size);
if (!map.map) {
pr_err("Could not map the memory map!\n");
return -ENOMEM;
}
map.phys_map = data->phys_map;
map.nr_map = data->size / data->desc_size;
map.map_end = map.map + data->size;
map.desc_version = data->desc_version;
map.desc_size = data->desc_size;
map.late = late;
set_bit(EFI_MEMMAP, &efi.flags);
efi.memmap = map;
return 0;
}
/**
* efi_memmap_init_early - Map the EFI memory map data structure
* @data: EFI memory map data
*
* Use early_memremap() to map the passed in EFI memory map and assign
* it to efi.memmap.
*/
int __init efi_memmap_init_early(struct efi_memory_map_data *data)
{
/* Cannot go backwards */
WARN_ON(efi.memmap.late);
return __efi_memmap_init(data, false);
}
void __init efi_memmap_unmap(void)
{
if (!efi.memmap.late) {
unsigned long size;
size = efi.memmap.desc_size * efi.memmap.nr_map;
early_memunmap(efi.memmap.map, size);
} else {
memunmap(efi.memmap.map);
}
efi.memmap.map = NULL;
clear_bit(EFI_MEMMAP, &efi.flags);
}
/**
* efi_memmap_init_late - Map efi.memmap with memremap()
* @phys_addr: Physical address of the new EFI memory map
* @size: Size in bytes of the new EFI memory map
*
* Setup a mapping of the EFI memory map using ioremap_cache(). This
* function should only be called once the vmalloc space has been
* setup and is therefore not suitable for calling during early EFI
* initialise, e.g. in efi_init(). Additionally, it expects
* efi_memmap_init_early() to have already been called.
*
* The reason there are two EFI memmap initialisation
* (efi_memmap_init_early() and this late version) is because the
* early EFI memmap should be explicitly unmapped once EFI
* initialisation is complete as the fixmap space used to map the EFI
* memmap (via early_memremap()) is a scarce resource.
*
* This late mapping is intended to persist for the duration of
* runtime so that things like efi_mem_desc_lookup() and
* efi_mem_attributes() always work.
*
* Returns zero on success, a negative error code on failure.
*/
int __init efi_memmap_init_late(phys_addr_t addr, unsigned long size)
{
struct efi_memory_map_data data = {
.phys_map = addr,
.size = size,
};
/* Did we forget to unmap the early EFI memmap? */
WARN_ON(efi.memmap.map);
/* Were we already called? */
WARN_ON(efi.memmap.late);
/*
* It makes no sense to allow callers to register different
* values for the following fields. Copy them out of the
* existing early EFI memmap.
*/
data.desc_version = efi.memmap.desc_version;
data.desc_size = efi.memmap.desc_size;
return __efi_memmap_init(&data, true);
}
/**
* efi_memmap_install - Install a new EFI memory map in efi.memmap
* @addr: Physical address of the memory map
* @nr_map: Number of entries in the memory map
*
* Unlike efi_memmap_init_*(), this function does not allow the caller
* to switch from early to late mappings. It simply uses the existing
* mapping function and installs the new memmap.
*
* Returns zero on success, a negative error code on failure.
*/
int __init efi_memmap_install(phys_addr_t addr, unsigned int nr_map)
{
struct efi_memory_map_data data;
efi_memmap_unmap();
data.phys_map = addr;
data.size = efi.memmap.desc_size * nr_map;
data.desc_version = efi.memmap.desc_version;
data.desc_size = efi.memmap.desc_size;
return __efi_memmap_init(&data, efi.memmap.late);
}
/**
* efi_memmap_split_count - Count number of additional EFI memmap entries
* @md: EFI memory descriptor to split
* @range: Address range (start, end) to split around
*
* Returns the number of additional EFI memmap entries required to
* accomodate @range.
*/
int __init efi_memmap_split_count(efi_memory_desc_t *md, struct range *range)
{
u64 m_start, m_end;
u64 start, end;
int count = 0;
start = md->phys_addr;
end = start + (md->num_pages << EFI_PAGE_SHIFT) - 1;
/* modifying range */
m_start = range->start;
m_end = range->end;
if (m_start <= start) {
/* split into 2 parts */
if (start < m_end && m_end < end)
count++;
}
if (start < m_start && m_start < end) {
/* split into 3 parts */
if (m_end < end)
count += 2;
/* split into 2 parts */
if (end <= m_end)
count++;
}
return count;
}
/**
* efi_memmap_insert - Insert a memory region in an EFI memmap
* @old_memmap: The existing EFI memory map structure
* @buf: Address of buffer to store new map
* @mem: Memory map entry to insert
*
* It is suggested that you call efi_memmap_split_count() first
* to see how large @buf needs to be.
*/
void __init efi_memmap_insert(struct efi_memory_map *old_memmap, void *buf,
struct efi_mem_range *mem)
{
u64 m_start, m_end, m_attr;
efi_memory_desc_t *md;
u64 start, end;
void *old, *new;
/* modifying range */
m_start = mem->range.start;
m_end = mem->range.end;
m_attr = mem->attribute;
/*
* The EFI memory map deals with regions in EFI_PAGE_SIZE
* units. Ensure that the region described by 'mem' is aligned
* correctly.
*/
if (!IS_ALIGNED(m_start, EFI_PAGE_SIZE) ||
!IS_ALIGNED(m_end + 1, EFI_PAGE_SIZE)) {
WARN_ON(1);
return;
}
for (old = old_memmap->map, new = buf;
old < old_memmap->map_end;
old += old_memmap->desc_size, new += old_memmap->desc_size) {
/* copy original EFI memory descriptor */
memcpy(new, old, old_memmap->desc_size);
md = new;
start = md->phys_addr;
end = md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) - 1;
if (m_start <= start && end <= m_end)
md->attribute |= m_attr;
if (m_start <= start &&
(start < m_end && m_end < end)) {
/* first part */
md->attribute |= m_attr;
md->num_pages = (m_end - md->phys_addr + 1) >>
EFI_PAGE_SHIFT;
/* latter part */
new += old_memmap->desc_size;
memcpy(new, old, old_memmap->desc_size);
md = new;
md->phys_addr = m_end + 1;
md->num_pages = (end - md->phys_addr + 1) >>
EFI_PAGE_SHIFT;
}
if ((start < m_start && m_start < end) && m_end < end) {
/* first part */
md->num_pages = (m_start - md->phys_addr) >>
EFI_PAGE_SHIFT;
/* middle part */
new += old_memmap->desc_size;
memcpy(new, old, old_memmap->desc_size);
md = new;
md->attribute |= m_attr;
md->phys_addr = m_start;
md->num_pages = (m_end - m_start + 1) >>
EFI_PAGE_SHIFT;
/* last part */
new += old_memmap->desc_size;
memcpy(new, old, old_memmap->desc_size);
md = new;
md->phys_addr = m_end + 1;
md->num_pages = (end - m_end) >>
EFI_PAGE_SHIFT;
}
if ((start < m_start && m_start < end) &&
(end <= m_end)) {
/* first part */
md->num_pages = (m_start - md->phys_addr) >>
EFI_PAGE_SHIFT;
/* latter part */
new += old_memmap->desc_size;
memcpy(new, old, old_memmap->desc_size);
md = new;
md->phys_addr = m_start;
md->num_pages = (end - md->phys_addr + 1) >>
EFI_PAGE_SHIFT;
md->attribute |= m_attr;
}
}
}
...@@ -14,10 +14,6 @@ ...@@ -14,10 +14,6 @@
#include <asm/setup.h> #include <asm/setup.h>
static void *efi_runtime_map;
static int nr_efi_runtime_map;
static u32 efi_memdesc_size;
struct efi_runtime_map_entry { struct efi_runtime_map_entry {
efi_memory_desc_t md; efi_memory_desc_t md;
struct kobject kobj; /* kobject for each entry */ struct kobject kobj; /* kobject for each entry */
...@@ -106,7 +102,8 @@ static struct kobj_type __refdata map_ktype = { ...@@ -106,7 +102,8 @@ static struct kobj_type __refdata map_ktype = {
static struct kset *map_kset; static struct kset *map_kset;
static struct efi_runtime_map_entry * static struct efi_runtime_map_entry *
add_sysfs_runtime_map_entry(struct kobject *kobj, int nr) add_sysfs_runtime_map_entry(struct kobject *kobj, int nr,
efi_memory_desc_t *md)
{ {
int ret; int ret;
struct efi_runtime_map_entry *entry; struct efi_runtime_map_entry *entry;
...@@ -124,8 +121,7 @@ add_sysfs_runtime_map_entry(struct kobject *kobj, int nr) ...@@ -124,8 +121,7 @@ add_sysfs_runtime_map_entry(struct kobject *kobj, int nr)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
} }
memcpy(&entry->md, efi_runtime_map + nr * efi_memdesc_size, memcpy(&entry->md, md, sizeof(efi_memory_desc_t));
sizeof(efi_memory_desc_t));
kobject_init(&entry->kobj, &map_ktype); kobject_init(&entry->kobj, &map_ktype);
entry->kobj.kset = map_kset; entry->kobj.kset = map_kset;
...@@ -142,12 +138,12 @@ add_sysfs_runtime_map_entry(struct kobject *kobj, int nr) ...@@ -142,12 +138,12 @@ add_sysfs_runtime_map_entry(struct kobject *kobj, int nr)
int efi_get_runtime_map_size(void) int efi_get_runtime_map_size(void)
{ {
return nr_efi_runtime_map * efi_memdesc_size; return efi.memmap.nr_map * efi.memmap.desc_size;
} }
int efi_get_runtime_map_desc_size(void) int efi_get_runtime_map_desc_size(void)
{ {
return efi_memdesc_size; return efi.memmap.desc_size;
} }
int efi_runtime_map_copy(void *buf, size_t bufsz) int efi_runtime_map_copy(void *buf, size_t bufsz)
...@@ -157,38 +153,33 @@ int efi_runtime_map_copy(void *buf, size_t bufsz) ...@@ -157,38 +153,33 @@ int efi_runtime_map_copy(void *buf, size_t bufsz)
if (sz > bufsz) if (sz > bufsz)
sz = bufsz; sz = bufsz;
memcpy(buf, efi_runtime_map, sz); memcpy(buf, efi.memmap.map, sz);
return 0; return 0;
} }
void efi_runtime_map_setup(void *map, int nr_entries, u32 desc_size)
{
efi_runtime_map = map;
nr_efi_runtime_map = nr_entries;
efi_memdesc_size = desc_size;
}
int __init efi_runtime_map_init(struct kobject *efi_kobj) int __init efi_runtime_map_init(struct kobject *efi_kobj)
{ {
int i, j, ret = 0; int i, j, ret = 0;
struct efi_runtime_map_entry *entry; struct efi_runtime_map_entry *entry;
efi_memory_desc_t *md;
if (!efi_runtime_map) if (!efi_enabled(EFI_MEMMAP))
return 0; return 0;
map_entries = kzalloc(nr_efi_runtime_map * sizeof(entry), GFP_KERNEL); map_entries = kzalloc(efi.memmap.nr_map * sizeof(entry), GFP_KERNEL);
if (!map_entries) { if (!map_entries) {
ret = -ENOMEM; ret = -ENOMEM;
goto out; goto out;
} }
for (i = 0; i < nr_efi_runtime_map; i++) { i = 0;
entry = add_sysfs_runtime_map_entry(efi_kobj, i); for_each_efi_memory_desc(md) {
entry = add_sysfs_runtime_map_entry(efi_kobj, i, md);
if (IS_ERR(entry)) { if (IS_ERR(entry)) {
ret = PTR_ERR(entry); ret = PTR_ERR(entry);
goto out_add_entry; goto out_add_entry;
} }
*(map_entries + i) = entry; *(map_entries + i++) = entry;
} }
return 0; return 0;
......
...@@ -14,11 +14,13 @@ ...@@ -14,11 +14,13 @@
* This file is released under the GPLv2. * This file is released under the GPLv2.
*/ */
#define pr_fmt(fmt) "efi: " fmt
#include <linux/bug.h> #include <linux/bug.h>
#include <linux/efi.h> #include <linux/efi.h>
#include <linux/irqflags.h> #include <linux/irqflags.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/spinlock.h> #include <linux/semaphore.h>
#include <linux/stringify.h> #include <linux/stringify.h>
#include <asm/efi.h> #include <asm/efi.h>
...@@ -81,20 +83,21 @@ void efi_call_virt_check_flags(unsigned long flags, const char *call) ...@@ -81,20 +83,21 @@ void efi_call_virt_check_flags(unsigned long flags, const char *call)
* +------------------------------------+-------------------------------+ * +------------------------------------+-------------------------------+
* *
* Due to the fact that the EFI pstore may write to the variable store in * 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 * interrupt context, we need to use a lock for at least the groups that
* contain SetVariable() and QueryVariableInfo(). That leaves little else, as * contain SetVariable() and QueryVariableInfo(). That leaves little else, as
* none of the remaining functions are actually ever called at runtime. * 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. * So let's just use a single lock to serialize all Runtime Services calls.
*/ */
static DEFINE_SPINLOCK(efi_runtime_lock); static DEFINE_SEMAPHORE(efi_runtime_lock);
static efi_status_t virt_efi_get_time(efi_time_t *tm, efi_time_cap_t *tc) static efi_status_t virt_efi_get_time(efi_time_t *tm, efi_time_cap_t *tc)
{ {
efi_status_t status; efi_status_t status;
spin_lock(&efi_runtime_lock); if (down_interruptible(&efi_runtime_lock))
return EFI_ABORTED;
status = efi_call_virt(get_time, tm, tc); status = efi_call_virt(get_time, tm, tc);
spin_unlock(&efi_runtime_lock); up(&efi_runtime_lock);
return status; return status;
} }
...@@ -102,9 +105,10 @@ static efi_status_t virt_efi_set_time(efi_time_t *tm) ...@@ -102,9 +105,10 @@ static efi_status_t virt_efi_set_time(efi_time_t *tm)
{ {
efi_status_t status; efi_status_t status;
spin_lock(&efi_runtime_lock); if (down_interruptible(&efi_runtime_lock))
return EFI_ABORTED;
status = efi_call_virt(set_time, tm); status = efi_call_virt(set_time, tm);
spin_unlock(&efi_runtime_lock); up(&efi_runtime_lock);
return status; return status;
} }
...@@ -114,9 +118,10 @@ static efi_status_t virt_efi_get_wakeup_time(efi_bool_t *enabled, ...@@ -114,9 +118,10 @@ static efi_status_t virt_efi_get_wakeup_time(efi_bool_t *enabled,
{ {
efi_status_t status; efi_status_t status;
spin_lock(&efi_runtime_lock); if (down_interruptible(&efi_runtime_lock))
return EFI_ABORTED;
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); up(&efi_runtime_lock);
return status; return status;
} }
...@@ -124,9 +129,10 @@ static efi_status_t virt_efi_set_wakeup_time(efi_bool_t enabled, efi_time_t *tm) ...@@ -124,9 +129,10 @@ 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(&efi_runtime_lock); if (down_interruptible(&efi_runtime_lock))
return EFI_ABORTED;
status = efi_call_virt(set_wakeup_time, enabled, tm); status = efi_call_virt(set_wakeup_time, enabled, tm);
spin_unlock(&efi_runtime_lock); up(&efi_runtime_lock);
return status; return status;
} }
...@@ -138,10 +144,11 @@ static efi_status_t virt_efi_get_variable(efi_char16_t *name, ...@@ -138,10 +144,11 @@ static efi_status_t virt_efi_get_variable(efi_char16_t *name,
{ {
efi_status_t status; efi_status_t status;
spin_lock(&efi_runtime_lock); if (down_interruptible(&efi_runtime_lock))
return EFI_ABORTED;
status = efi_call_virt(get_variable, name, vendor, attr, data_size, status = efi_call_virt(get_variable, name, vendor, attr, data_size,
data); data);
spin_unlock(&efi_runtime_lock); up(&efi_runtime_lock);
return status; return status;
} }
...@@ -151,9 +158,10 @@ static efi_status_t virt_efi_get_next_variable(unsigned long *name_size, ...@@ -151,9 +158,10 @@ static efi_status_t virt_efi_get_next_variable(unsigned long *name_size,
{ {
efi_status_t status; efi_status_t status;
spin_lock(&efi_runtime_lock); if (down_interruptible(&efi_runtime_lock))
return EFI_ABORTED;
status = efi_call_virt(get_next_variable, name_size, name, vendor); status = efi_call_virt(get_next_variable, name_size, name, vendor);
spin_unlock(&efi_runtime_lock); up(&efi_runtime_lock);
return status; return status;
} }
...@@ -165,10 +173,11 @@ static efi_status_t virt_efi_set_variable(efi_char16_t *name, ...@@ -165,10 +173,11 @@ static efi_status_t virt_efi_set_variable(efi_char16_t *name,
{ {
efi_status_t status; efi_status_t status;
spin_lock(&efi_runtime_lock); if (down_interruptible(&efi_runtime_lock))
return EFI_ABORTED;
status = efi_call_virt(set_variable, name, vendor, attr, data_size, status = efi_call_virt(set_variable, name, vendor, attr, data_size,
data); data);
spin_unlock(&efi_runtime_lock); up(&efi_runtime_lock);
return status; return status;
} }
...@@ -179,12 +188,12 @@ virt_efi_set_variable_nonblocking(efi_char16_t *name, efi_guid_t *vendor, ...@@ -179,12 +188,12 @@ virt_efi_set_variable_nonblocking(efi_char16_t *name, efi_guid_t *vendor,
{ {
efi_status_t status; efi_status_t status;
if (!spin_trylock(&efi_runtime_lock)) if (down_trylock(&efi_runtime_lock))
return EFI_NOT_READY; return EFI_NOT_READY;
status = efi_call_virt(set_variable, name, vendor, attr, data_size, status = efi_call_virt(set_variable, name, vendor, attr, data_size,
data); data);
spin_unlock(&efi_runtime_lock); up(&efi_runtime_lock);
return status; return status;
} }
...@@ -199,10 +208,11 @@ static efi_status_t virt_efi_query_variable_info(u32 attr, ...@@ -199,10 +208,11 @@ static efi_status_t virt_efi_query_variable_info(u32 attr,
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;
spin_lock(&efi_runtime_lock); if (down_interruptible(&efi_runtime_lock))
return EFI_ABORTED;
status = efi_call_virt(query_variable_info, attr, storage_space, status = efi_call_virt(query_variable_info, attr, storage_space,
remaining_space, max_variable_size); remaining_space, max_variable_size);
spin_unlock(&efi_runtime_lock); up(&efi_runtime_lock);
return status; return status;
} }
...@@ -217,12 +227,12 @@ virt_efi_query_variable_info_nonblocking(u32 attr, ...@@ -217,12 +227,12 @@ virt_efi_query_variable_info_nonblocking(u32 attr,
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;
if (!spin_trylock(&efi_runtime_lock)) if (down_trylock(&efi_runtime_lock))
return EFI_NOT_READY; return EFI_NOT_READY;
status = efi_call_virt(query_variable_info, attr, storage_space, status = efi_call_virt(query_variable_info, attr, storage_space,
remaining_space, max_variable_size); remaining_space, max_variable_size);
spin_unlock(&efi_runtime_lock); up(&efi_runtime_lock);
return status; return status;
} }
...@@ -230,9 +240,10 @@ static efi_status_t virt_efi_get_next_high_mono_count(u32 *count) ...@@ -230,9 +240,10 @@ static efi_status_t virt_efi_get_next_high_mono_count(u32 *count)
{ {
efi_status_t status; efi_status_t status;
spin_lock(&efi_runtime_lock); if (down_interruptible(&efi_runtime_lock))
return EFI_ABORTED;
status = efi_call_virt(get_next_high_mono_count, count); status = efi_call_virt(get_next_high_mono_count, count);
spin_unlock(&efi_runtime_lock); up(&efi_runtime_lock);
return status; return status;
} }
...@@ -241,9 +252,13 @@ static void virt_efi_reset_system(int reset_type, ...@@ -241,9 +252,13 @@ 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)
{ {
spin_lock(&efi_runtime_lock); if (down_interruptible(&efi_runtime_lock)) {
pr_warn("failed to invoke the reset_system() runtime service:\n"
"could not get exclusive access to the firmware\n");
return;
}
__efi_call_virt(reset_system, reset_type, status, data_size, data); __efi_call_virt(reset_system, reset_type, status, data_size, data);
spin_unlock(&efi_runtime_lock); up(&efi_runtime_lock);
} }
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,
...@@ -255,9 +270,10 @@ static efi_status_t virt_efi_update_capsule(efi_capsule_header_t **capsules, ...@@ -255,9 +270,10 @@ static efi_status_t virt_efi_update_capsule(efi_capsule_header_t **capsules,
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;
spin_lock(&efi_runtime_lock); if (down_interruptible(&efi_runtime_lock))
return EFI_ABORTED;
status = efi_call_virt(update_capsule, capsules, count, sg_list); status = efi_call_virt(update_capsule, capsules, count, sg_list);
spin_unlock(&efi_runtime_lock); up(&efi_runtime_lock);
return status; return status;
} }
...@@ -271,10 +287,11 @@ static efi_status_t virt_efi_query_capsule_caps(efi_capsule_header_t **capsules, ...@@ -271,10 +287,11 @@ static efi_status_t virt_efi_query_capsule_caps(efi_capsule_header_t **capsules,
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;
spin_lock(&efi_runtime_lock); if (down_interruptible(&efi_runtime_lock))
return EFI_ABORTED;
status = efi_call_virt(query_capsule_caps, capsules, count, max_size, status = efi_call_virt(query_capsule_caps, capsules, count, max_size,
reset_type); reset_type);
spin_unlock(&efi_runtime_lock); up(&efi_runtime_lock);
return status; return status;
} }
......
obj-$(CONFIG_EFI_TEST) += efi_test.o
This diff is collapsed.
/*
* EFI Test driver Header
*
* Copyright(C) 2012-2016 Canonical Ltd.
*
*/
#ifndef _DRIVERS_FIRMWARE_EFI_TEST_H_
#define _DRIVERS_FIRMWARE_EFI_TEST_H_
#include <linux/efi.h>
struct efi_getvariable {
efi_char16_t *variable_name;
efi_guid_t *vendor_guid;
u32 *attributes;
unsigned long *data_size;
void *data;
efi_status_t *status;
} __packed;
struct efi_setvariable {
efi_char16_t *variable_name;
efi_guid_t *vendor_guid;
u32 attributes;
unsigned long data_size;
void *data;
efi_status_t *status;
} __packed;
struct efi_getnextvariablename {
unsigned long *variable_name_size;
efi_char16_t *variable_name;
efi_guid_t *vendor_guid;
efi_status_t *status;
} __packed;
struct efi_queryvariableinfo {
u32 attributes;
u64 *maximum_variable_storage_size;
u64 *remaining_variable_storage_size;
u64 *maximum_variable_size;
efi_status_t *status;
} __packed;
struct efi_gettime {
efi_time_t *time;
efi_time_cap_t *capabilities;
efi_status_t *status;
} __packed;
struct efi_settime {
efi_time_t *time;
efi_status_t *status;
} __packed;
struct efi_getwakeuptime {
efi_bool_t *enabled;
efi_bool_t *pending;
efi_time_t *time;
efi_status_t *status;
} __packed;
struct efi_setwakeuptime {
efi_bool_t enabled;
efi_time_t *time;
efi_status_t *status;
} __packed;
struct efi_getnexthighmonotoniccount {
u32 *high_count;
efi_status_t *status;
} __packed;
struct efi_querycapsulecapabilities {
efi_capsule_header_t **capsule_header_array;
unsigned long capsule_count;
u64 *maximum_capsule_size;
int *reset_type;
efi_status_t *status;
} __packed;
#define EFI_RUNTIME_GET_VARIABLE \
_IOWR('p', 0x01, struct efi_getvariable)
#define EFI_RUNTIME_SET_VARIABLE \
_IOW('p', 0x02, struct efi_setvariable)
#define EFI_RUNTIME_GET_TIME \
_IOR('p', 0x03, struct efi_gettime)
#define EFI_RUNTIME_SET_TIME \
_IOW('p', 0x04, struct efi_settime)
#define EFI_RUNTIME_GET_WAKETIME \
_IOR('p', 0x05, struct efi_getwakeuptime)
#define EFI_RUNTIME_SET_WAKETIME \
_IOW('p', 0x06, struct efi_setwakeuptime)
#define EFI_RUNTIME_GET_NEXTVARIABLENAME \
_IOWR('p', 0x07, struct efi_getnextvariablename)
#define EFI_RUNTIME_QUERY_VARIABLEINFO \
_IOR('p', 0x08, struct efi_queryvariableinfo)
#define EFI_RUNTIME_GET_NEXTHIGHMONOTONICCOUNT \
_IOR('p', 0x09, struct efi_getnexthighmonotoniccount)
#define EFI_RUNTIME_QUERY_CAPSULECAPABILITIES \
_IOR('p', 0x0A, struct efi_querycapsulecapabilities)
#endif /* _DRIVERS_FIRMWARE_EFI_TEST_H_ */
This diff is collapsed.
...@@ -910,7 +910,6 @@ static __init int gsmi_init(void) ...@@ -910,7 +910,6 @@ static __init int gsmi_init(void)
gsmi_buf_free(gsmi_dev.param_buf); gsmi_buf_free(gsmi_dev.param_buf);
gsmi_buf_free(gsmi_dev.data_buf); gsmi_buf_free(gsmi_dev.data_buf);
gsmi_buf_free(gsmi_dev.name_buf); gsmi_buf_free(gsmi_dev.name_buf);
if (gsmi_dev.dma_pool)
dma_pool_destroy(gsmi_dev.dma_pool); dma_pool_destroy(gsmi_dev.dma_pool);
platform_device_unregister(gsmi_dev.pdev); platform_device_unregister(gsmi_dev.pdev);
pr_info("gsmi: failed to load: %d\n", ret); pr_info("gsmi: failed to load: %d\n", ret);
......
...@@ -105,7 +105,10 @@ static int efivarfs_create(struct inode *dir, struct dentry *dentry, ...@@ -105,7 +105,10 @@ static int efivarfs_create(struct inode *dir, struct dentry *dentry,
inode->i_private = var; inode->i_private = var;
efivar_entry_add(var, &efivarfs_list); err = efivar_entry_add(var, &efivarfs_list);
if (err)
goto out;
d_instantiate(dentry, inode); d_instantiate(dentry, inode);
dget(dentry); dget(dentry);
out: out:
......
...@@ -157,12 +157,14 @@ static int efivarfs_callback(efi_char16_t *name16, efi_guid_t vendor, ...@@ -157,12 +157,14 @@ static int efivarfs_callback(efi_char16_t *name16, efi_guid_t vendor,
goto fail_inode; goto fail_inode;
} }
efivar_entry_size(entry, &size);
err = efivar_entry_add(entry, &efivarfs_list);
if (err)
goto fail_inode;
/* copied by the above to local storage in the dentry. */ /* copied by the above to local storage in the dentry. */
kfree(name); kfree(name);
efivar_entry_size(entry, &size);
efivar_entry_add(entry, &efivarfs_list);
inode_lock(inode); inode_lock(inode);
inode->i_private = entry; inode->i_private = entry;
i_size_write(inode, size + sizeof(entry->var.Attributes)); i_size_write(inode, size + sizeof(entry->var.Attributes));
...@@ -182,7 +184,10 @@ static int efivarfs_callback(efi_char16_t *name16, efi_guid_t vendor, ...@@ -182,7 +184,10 @@ static int efivarfs_callback(efi_char16_t *name16, efi_guid_t vendor,
static int efivarfs_destroy(struct efivar_entry *entry, void *data) static int efivarfs_destroy(struct efivar_entry *entry, void *data)
{ {
efivar_entry_remove(entry); int err = efivar_entry_remove(entry);
if (err)
return err;
kfree(entry); kfree(entry);
return 0; return 0;
} }
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/pfn.h> #include <linux/pfn.h>
#include <linux/pstore.h> #include <linux/pstore.h>
#include <linux/range.h>
#include <linux/reboot.h> #include <linux/reboot.h>
#include <linux/uuid.h> #include <linux/uuid.h>
#include <linux/screen_info.h> #include <linux/screen_info.h>
...@@ -37,6 +38,7 @@ ...@@ -37,6 +38,7 @@
#define EFI_WRITE_PROTECTED ( 8 | (1UL << (BITS_PER_LONG-1))) #define EFI_WRITE_PROTECTED ( 8 | (1UL << (BITS_PER_LONG-1)))
#define EFI_OUT_OF_RESOURCES ( 9 | (1UL << (BITS_PER_LONG-1))) #define EFI_OUT_OF_RESOURCES ( 9 | (1UL << (BITS_PER_LONG-1)))
#define EFI_NOT_FOUND (14 | (1UL << (BITS_PER_LONG-1))) #define EFI_NOT_FOUND (14 | (1UL << (BITS_PER_LONG-1)))
#define EFI_ABORTED (21 | (1UL << (BITS_PER_LONG-1)))
#define EFI_SECURITY_VIOLATION (26 | (1UL << (BITS_PER_LONG-1))) #define EFI_SECURITY_VIOLATION (26 | (1UL << (BITS_PER_LONG-1)))
typedef unsigned long efi_status_t; typedef unsigned long efi_status_t;
...@@ -678,6 +680,18 @@ typedef struct { ...@@ -678,6 +680,18 @@ typedef struct {
unsigned long tables; unsigned long tables;
} efi_system_table_t; } efi_system_table_t;
/*
* Architecture independent structure for describing a memory map for the
* benefit of efi_memmap_init_early(), saving us the need to pass four
* parameters.
*/
struct efi_memory_map_data {
phys_addr_t phys_map;
unsigned long size;
unsigned long desc_version;
unsigned long desc_size;
};
struct efi_memory_map { struct efi_memory_map {
phys_addr_t phys_map; phys_addr_t phys_map;
void *map; void *map;
...@@ -685,6 +699,12 @@ struct efi_memory_map { ...@@ -685,6 +699,12 @@ struct efi_memory_map {
int nr_map; int nr_map;
unsigned long desc_version; unsigned long desc_version;
unsigned long desc_size; unsigned long desc_size;
bool late;
};
struct efi_mem_range {
struct range range;
u64 attribute;
}; };
struct efi_fdt_params { struct efi_fdt_params {
...@@ -909,6 +929,16 @@ static inline efi_status_t efi_query_variable_store(u32 attributes, ...@@ -909,6 +929,16 @@ static inline efi_status_t efi_query_variable_store(u32 attributes,
} }
#endif #endif
extern void __iomem *efi_lookup_mapped_addr(u64 phys_addr); extern void __iomem *efi_lookup_mapped_addr(u64 phys_addr);
extern int __init efi_memmap_init_early(struct efi_memory_map_data *data);
extern int __init efi_memmap_init_late(phys_addr_t addr, unsigned long size);
extern void __init efi_memmap_unmap(void);
extern int __init efi_memmap_install(phys_addr_t addr, unsigned int nr_map);
extern int __init efi_memmap_split_count(efi_memory_desc_t *md,
struct range *range);
extern void __init efi_memmap_insert(struct efi_memory_map *old_memmap,
void *buf, struct efi_mem_range *mem);
extern int efi_config_init(efi_config_table_type_t *arch_tables); extern int efi_config_init(efi_config_table_type_t *arch_tables);
#ifdef CONFIG_EFI_ESRT #ifdef CONFIG_EFI_ESRT
extern void __init efi_esrt_init(void); extern void __init efi_esrt_init(void);
...@@ -924,6 +954,7 @@ extern u64 efi_mem_attribute (unsigned long phys_addr, unsigned long size); ...@@ -924,6 +954,7 @@ extern u64 efi_mem_attribute (unsigned long phys_addr, unsigned long size);
extern int __init efi_uart_console_only (void); extern int __init efi_uart_console_only (void);
extern u64 efi_mem_desc_end(efi_memory_desc_t *md); extern u64 efi_mem_desc_end(efi_memory_desc_t *md);
extern int efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md); extern int efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md);
extern void efi_mem_reserve(phys_addr_t addr, u64 size);
extern void efi_initialize_iomem_resources(struct resource *code_resource, extern void efi_initialize_iomem_resources(struct resource *code_resource,
struct resource *data_resource, struct resource *bss_resource); struct resource *data_resource, struct resource *bss_resource);
extern void efi_reserve_boot_services(void); extern void efi_reserve_boot_services(void);
...@@ -1136,12 +1167,6 @@ struct efivar_operations { ...@@ -1136,12 +1167,6 @@ struct efivar_operations {
}; };
struct efivars { struct efivars {
/*
* ->lock protects two things:
* 1) efivarfs_list and efivars_sysfs_list
* 2) ->ops calls
*/
spinlock_t lock;
struct kset *kset; struct kset *kset;
struct kobject *kobject; struct kobject *kobject;
const struct efivar_operations *ops; const struct efivar_operations *ops;
...@@ -1282,8 +1307,8 @@ struct kobject *efivars_kobject(void); ...@@ -1282,8 +1307,8 @@ struct kobject *efivars_kobject(void);
int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *), int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
void *data, bool duplicates, struct list_head *head); void *data, bool duplicates, struct list_head *head);
void efivar_entry_add(struct efivar_entry *entry, struct list_head *head); int efivar_entry_add(struct efivar_entry *entry, struct list_head *head);
void efivar_entry_remove(struct efivar_entry *entry); int efivar_entry_remove(struct efivar_entry *entry);
int __efivar_entry_delete(struct efivar_entry *entry); int __efivar_entry_delete(struct efivar_entry *entry);
int efivar_entry_delete(struct efivar_entry *entry); int efivar_entry_delete(struct efivar_entry *entry);
...@@ -1300,7 +1325,7 @@ int efivar_entry_set_get_size(struct efivar_entry *entry, u32 attributes, ...@@ -1300,7 +1325,7 @@ int efivar_entry_set_get_size(struct efivar_entry *entry, u32 attributes,
int efivar_entry_set_safe(efi_char16_t *name, efi_guid_t vendor, u32 attributes, int efivar_entry_set_safe(efi_char16_t *name, efi_guid_t vendor, u32 attributes,
bool block, unsigned long size, void *data); bool block, unsigned long size, void *data);
void efivar_entry_iter_begin(void); int efivar_entry_iter_begin(void);
void efivar_entry_iter_end(void); void efivar_entry_iter_end(void);
int __efivar_entry_iter(int (*func)(struct efivar_entry *, void *), int __efivar_entry_iter(int (*func)(struct efivar_entry *, void *),
...@@ -1336,7 +1361,6 @@ extern int efi_capsule_update(efi_capsule_header_t *capsule, ...@@ -1336,7 +1361,6 @@ extern int efi_capsule_update(efi_capsule_header_t *capsule,
#ifdef CONFIG_EFI_RUNTIME_MAP #ifdef CONFIG_EFI_RUNTIME_MAP
int efi_runtime_map_init(struct kobject *); int efi_runtime_map_init(struct kobject *);
void efi_runtime_map_setup(void *, int, u32);
int efi_get_runtime_map_size(void); int efi_get_runtime_map_size(void);
int efi_get_runtime_map_desc_size(void); int efi_get_runtime_map_desc_size(void);
int efi_runtime_map_copy(void *buf, size_t bufsz); int efi_runtime_map_copy(void *buf, size_t bufsz);
...@@ -1346,9 +1370,6 @@ static inline int efi_runtime_map_init(struct kobject *kobj) ...@@ -1346,9 +1370,6 @@ static inline int efi_runtime_map_init(struct kobject *kobj)
return 0; return 0;
} }
static inline void
efi_runtime_map_setup(void *map, int nr_entries, u32 desc_size) {}
static inline int efi_get_runtime_map_size(void) static inline int efi_get_runtime_map_size(void)
{ {
return 0; return 0;
......
...@@ -56,7 +56,7 @@ ucs2_utf8size(const ucs2_char_t *src) ...@@ -56,7 +56,7 @@ ucs2_utf8size(const ucs2_char_t *src)
unsigned long i; unsigned long i;
unsigned long j = 0; unsigned long j = 0;
for (i = 0; i < ucs2_strlen(src); i++) { for (i = 0; src[i]; i++) {
u16 c = src[i]; u16 c = src[i];
if (c >= 0x800) if (c >= 0x800)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment