Merge branch 'efi-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 3 Oct 2016 18:33:18 +0000 (11:33 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 3 Oct 2016 18:33:18 +0000 (11:33 -0700)
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
  ...

31 files changed:
MAINTAINERS
arch/x86/boot/compressed/eboot.c
arch/x86/boot/compressed/head_32.S
arch/x86/boot/compressed/head_64.S
arch/x86/include/asm/efi.h
arch/x86/kernel/setup.c
arch/x86/platform/efi/efi-bgrt.c
arch/x86/platform/efi/efi.c
arch/x86/platform/efi/efi_64.c
arch/x86/platform/efi/quirks.c
drivers/firmware/efi/Kconfig
drivers/firmware/efi/Makefile
drivers/firmware/efi/arm-init.c
drivers/firmware/efi/arm-runtime.c
drivers/firmware/efi/efi-pstore.c
drivers/firmware/efi/efi.c
drivers/firmware/efi/efivars.c
drivers/firmware/efi/esrt.c
drivers/firmware/efi/fake_mem.c
drivers/firmware/efi/memmap.c [new file with mode: 0644]
drivers/firmware/efi/runtime-map.c
drivers/firmware/efi/runtime-wrappers.c
drivers/firmware/efi/test/Makefile [new file with mode: 0644]
drivers/firmware/efi/test/efi_test.c [new file with mode: 0644]
drivers/firmware/efi/test/efi_test.h [new file with mode: 0644]
drivers/firmware/efi/vars.c
drivers/firmware/google/gsmi.c
fs/efivarfs/inode.c
fs/efivarfs/super.c
include/linux/efi.h
lib/ucs2_string.c

index f2ae3a4..413b0da 100644 (file)
@@ -4587,6 +4587,13 @@ M:       Peter Jones <pjones@redhat.com>
 S:     Maintained
 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
 W:     http://aeschi.ch.eu.org/efs/
 S:     Orphan
index 94dd4a3..cc69e37 100644 (file)
@@ -29,22 +29,11 @@ __pure const struct efi_config *__efi_early(void)
 static void setup_boot_services##bits(struct efi_config *c)            \
 {                                                                      \
        efi_system_table_##bits##_t *table;                             \
-       efi_boot_services_##bits##_t *bt;                               \
                                                                        \
        table = (typeof(table))sys_table;                               \
                                                                        \
+       c->boot_services = table->boottime;                             \
        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(64);
@@ -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
 __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)
        efi_guid_t uga_proto = EFI_UGA_PROTOCOL_GUID;
        unsigned long nr_ugas;
        u32 *handles = (u32 *)uga_handle;;
-       efi_status_t status;
+       efi_status_t status = EFI_INVALID_PARAMETER;
        int i;
 
        first_uga = NULL;
@@ -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;
        unsigned long nr_ugas;
        u64 *handles = (u64 *)uga_handle;;
-       efi_status_t status;
+       efi_status_t status = EFI_INVALID_PARAMETER;
        int i;
 
        first_uga = NULL;
index 1038524..fd0b6a2 100644 (file)
@@ -82,7 +82,7 @@ ENTRY(efi_pe_entry)
 
        /* Relocate efi_config->call() */
        leal    efi32_config(%esi), %eax
-       add     %esi, 88(%eax)
+       add     %esi, 32(%eax)
        pushl   %eax
 
        call    make_boot_params
@@ -108,7 +108,7 @@ ENTRY(efi32_stub_entry)
 
        /* Relocate efi_config->call() */
        leal    efi32_config(%esi), %eax
-       add     %esi, 88(%eax)
+       add     %esi, 32(%eax)
        pushl   %eax
 2:
        call    efi_main
@@ -264,7 +264,7 @@ relocated:
 #ifdef CONFIG_EFI_STUB
        .data
 efi32_config:
-       .fill 11,8,0
+       .fill 4,8,0
        .long efi_call_phys
        .long 0
        .byte 0
index 0d80a7a..efdfba2 100644 (file)
@@ -265,7 +265,7 @@ ENTRY(efi_pe_entry)
        /*
         * Relocate efi_config->call().
         */
-       addq    %rbp, efi64_config+88(%rip)
+       addq    %rbp, efi64_config+32(%rip)
 
        movq    %rax, %rdi
        call    make_boot_params
@@ -285,7 +285,7 @@ handover_entry:
         * Relocate efi_config->call().
         */
        movq    efi_config(%rip), %rax
-       addq    %rbp, 88(%rax)
+       addq    %rbp, 32(%rax)
 2:
        movq    efi_config(%rip), %rdi
        call    efi_main
@@ -457,14 +457,14 @@ efi_config:
 #ifdef CONFIG_EFI_MIXED
        .global efi32_config
 efi32_config:
-       .fill   11,8,0
+       .fill   4,8,0
        .quad   efi64_thunk
        .byte   0
 #endif
 
        .global efi64_config
 efi64_config:
-       .fill   11,8,0
+       .fill   4,8,0
        .quad   efi_call
        .byte   1
 #endif /* CONFIG_EFI_STUB */
index d0bb76d..389d700 100644 (file)
@@ -117,7 +117,6 @@ extern int __init efi_memblock_x86_reserve_range(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_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_map_region(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(
 struct efi_config {
        u64 image_handle;
        u64 table;
-       u64 allocate_pool;
-       u64 allocate_pages;
-       u64 get_memory_map;
-       u64 free_pool;
-       u64 free_pages;
-       u64 locate_handle;
-       u64 handle_protocol;
-       u64 exit_boot_services;
+       u64 boot_services;
        u64 text_output;
        efi_status_t (*call)(unsigned long, ...);
        bool is64;
@@ -207,14 +199,27 @@ struct efi_config {
 
 __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, ...)                                         \
-       __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, ...)                                       \
        __efi_early()->call((unsigned long)f, __VA_ARGS__);
 
-#define efi_is_64bit()         __efi_early()->is64
-
 extern bool efi_reboot_required(void);
 
 #else
index 98c9cd6..7e54491 100644 (file)
@@ -1096,19 +1096,19 @@ void __init setup_arch(char **cmdline_p)
        memblock_set_current_limit(ISA_END_ADDRESS);
        memblock_x86_fill();
 
-       if (efi_enabled(EFI_BOOT)) {
+       reserve_bios_regions();
+
+       if (efi_enabled(EFI_MEMMAP)) {
                efi_fake_memmap();
                efi_find_mirror();
-       }
-
-       reserve_bios_regions();
+               efi_esrt_init();
 
-       /*
-        * The EFI specification says that boot service code won't be called
-        * after ExitBootServices(). This is, in fact, a lie.
-        */
-       if (efi_enabled(EFI_MEMMAP))
+               /*
+                * The EFI specification says that boot service code won't be
+                * called after ExitBootServices(). This is, in fact, a lie.
+                */
                efi_reserve_boot_services();
+       }
 
        /* preallocate 4k for mptable mpc */
        early_reserve_e820_mpc_new();
index 6a2f569..6aad870 100644 (file)
@@ -82,21 +82,12 @@ void __init efi_bgrt_init(void)
        }
        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) {
-               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");
-               kfree(bgrt_image);
                bgrt_image = NULL;
                return;
        }
 
-       memcpy(bgrt_image, image, bgrt_image_size);
-       memunmap(image);
+       efi_mem_reserve(bgrt_tab->image_address, bgrt_image_size);
 }
index 1fbb408..0955c70 100644 (file)
@@ -172,7 +172,9 @@ static void __init do_add_efi_memmap(void)
 int __init efi_memblock_x86_reserve_range(void)
 {
        struct efi_info *e = &boot_params.efi_info;
+       struct efi_memory_map_data data;
        phys_addr_t pmap;
+       int rv;
 
        if (efi_enabled(EFI_PARAVIRT))
                return 0;
@@ -187,11 +189,17 @@ int __init efi_memblock_x86_reserve_range(void)
 #else
        pmap = (e->efi_memmap | ((__u64)e->efi_memmap_hi << 32));
 #endif
-       efi.memmap.phys_map     = pmap;
-       efi.memmap.nr_map       = e->efi_memmap_size /
-                                 e->efi_memdesc_size;
-       efi.memmap.desc_size    = e->efi_memdesc_size;
-       efi.memmap.desc_version = e->efi_memdesc_version;
+       data.phys_map           = pmap;
+       data.size               = e->efi_memmap_size;
+       data.desc_size          = e->efi_memdesc_size;
+       data.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,
             "Unexpected EFI_MEMORY_DESCRIPTOR version %ld",
@@ -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)
 {
        if (efi_enabled(EFI_64BIT)) {
@@ -414,33 +409,6 @@ static int __init efi_runtime_init(void)
        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)
 {
        efi_char16_t *c16;
@@ -498,16 +466,14 @@ void __init efi_init(void)
        if (!efi_runtime_supported())
                pr_info("No EFI runtime due to 32/64-bit mismatch with kernel\n");
        else {
-               if (efi_runtime_disabled() || efi_runtime_init())
+               if (efi_runtime_disabled() || efi_runtime_init()) {
+                       efi_memmap_unmap();
                        return;
+               }
        }
-       if (efi_memmap_init())
-               return;
 
        if (efi_enabled(EFI_DBG))
                efi_print_memmap();
-
-       efi_esrt_init();
 }
 
 void __init efi_late_init(void)
@@ -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)
 {
        void *ret;
@@ -745,6 +675,46 @@ static void *efi_map_next_entry(void *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
  * virtual addresses.
@@ -761,13 +731,9 @@ static void * __init efi_map_regions(int *count, int *pg_shift)
        p = NULL;
        while ((p = efi_map_next_entry(p))) {
                md = p;
-               if (!(md->attribute & EFI_MEMORY_RUNTIME)) {
-#ifdef CONFIG_X86_64
-                       if (md->type != EFI_BOOT_SERVICES_CODE &&
-                           md->type != EFI_BOOT_SERVICES_DATA)
-#endif
-                               continue;
-               }
+
+               if (!should_map_region(md))
+                       continue;
 
                efi_map_region(md);
                get_systab_virt_addr(md);
@@ -803,7 +769,7 @@ static void __init kexec_enter_virtual_mode(void)
         * non-native EFI
         */
        if (!efi_is_native()) {
-               efi_unmap_memmap();
+               efi_memmap_unmap();
                clear_bit(EFI_RUNTIME_SERVICES, &efi.flags);
                return;
        }
@@ -823,7 +789,18 @@ static void __init kexec_enter_virtual_mode(void)
                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);
 
@@ -884,6 +861,7 @@ static void __init __efi_enter_virtual_mode(void)
        int count = 0, pg_shift = 0;
        void *new_memmap = NULL;
        efi_status_t status;
+       phys_addr_t pa;
 
        efi.systab = NULL;
 
@@ -901,11 +879,24 @@ static void __init __efi_enter_virtual_mode(void)
                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);
 
-       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);
                return;
        }
@@ -917,14 +908,14 @@ static void __init __efi_enter_virtual_mode(void)
                                efi.memmap.desc_size * count,
                                efi.memmap.desc_size,
                                efi.memmap.desc_version,
-                               (efi_memory_desc_t *)__pa(new_memmap));
+                               (efi_memory_desc_t *)pa);
        } else {
                status = efi_thunk_set_virtual_address_map(
                                efi_phys.set_virtual_address_map,
                                efi.memmap.desc_size * count,
                                efi.memmap.desc_size,
                                efi.memmap.desc_version,
-                               (efi_memory_desc_t *)__pa(new_memmap));
+                               (efi_memory_desc_t *)pa);
        }
 
        if (status != EFI_SUCCESS) {
@@ -956,15 +947,6 @@ static void __init __efi_enter_virtual_mode(void)
        efi_runtime_update_mappings();
        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 */
        efi_delete_dummy_variable();
 }
index 8dd3784..58b0f80 100644 (file)
@@ -85,7 +85,7 @@ pgd_t * __init efi_call_phys_prolog(void)
        early_code_mapping_set_exec(1);
 
        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++) {
                save_pgd[pgd] = *pgd_offset_k(pgd * PGDIR_SIZE);
@@ -214,7 +214,6 @@ void efi_sync_low_kernel_mappings(void)
 int __init efi_setup_page_tables(unsigned long pa_memmap, unsigned num_pages)
 {
        unsigned long pfn, text;
-       efi_memory_desc_t *md;
        struct page *page;
        unsigned npages;
        pgd_t *pgd;
@@ -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())
                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);
        if (!page)
                panic("Unable to allocate EFI runtime stack < 4GB\n");
@@ -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)
 {
+       __map_region(md, md->phys_addr);
        __map_region(md, md->virt_addr);
 }
 
index 89d1146..10aca63 100644 (file)
@@ -163,6 +163,75 @@ efi_status_t efi_query_variable_store(u32 attributes, unsigned long size,
 }
 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
  * can free regions in efi_free_boot_services().
@@ -184,15 +253,6 @@ static bool can_free_region(u64 start, u64 size)
        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)
 {
        efi_memory_desc_t *md;
@@ -249,7 +309,10 @@ void __init efi_reserve_boot_services(void)
 
 void __init efi_free_boot_services(void)
 {
+       phys_addr_t new_phys, new_size;
        efi_memory_desc_t *md;
+       int num_entries = 0;
+       void *new, *new_md;
 
        for_each_efi_memory_desc(md) {
                unsigned long long start = md->phys_addr;
@@ -257,12 +320,16 @@ void __init efi_free_boot_services(void)
                size_t rm_size;
 
                if (md->type != EFI_BOOT_SERVICES_CODE &&
-                   md->type != EFI_BOOT_SERVICES_DATA)
+                   md->type != EFI_BOOT_SERVICES_DATA) {
+                       num_entries++;
                        continue;
+               }
 
                /* Do not free, someone else owns it: */
-               if (md->attribute & EFI_MEMORY_RUNTIME)
+               if (md->attribute & EFI_MEMORY_RUNTIME) {
+                       num_entries++;
                        continue;
+               }
 
                /*
                 * Nasty quirk: if all sub-1MB memory is used for boot
@@ -287,7 +354,41 @@ void __init efi_free_boot_services(void)
                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)
         */
        if (!efi_runtime_supported()) {
                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. */
index 6394152..c981be1 100644 (file)
@@ -112,6 +112,23 @@ config EFI_CAPSULE_LOADER
 
          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
 
 config UEFI_CPER
index a219640..c8a439f 100644 (file)
@@ -10,7 +10,7 @@
 KASAN_SANITIZE_runtime-wrappers.o      := n
 
 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_ESRT)                 += esrt.o
 obj-$(CONFIG_EFI_VARS_PSTORE)          += efi-pstore.o
@@ -20,6 +20,7 @@ obj-$(CONFIG_EFI_RUNTIME_WRAPPERS)    += runtime-wrappers.o
 obj-$(CONFIG_EFI_STUB)                 += libstub/
 obj-$(CONFIG_EFI_FAKE_MEMMAP)          += fake_mem.o
 obj-$(CONFIG_EFI_BOOTLOADER_CONTROL)   += efibc.o
+obj-$(CONFIG_EFI_TEST)                 += test/
 
 arm-obj-$(CONFIG_EFI)                  := arm-init.o arm-runtime.o
 obj-$(CONFIG_ARM)                      += $(arm-obj-y)
index c49d50e..8efe130 100644 (file)
@@ -26,9 +26,9 @@
 
 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 0;
 }
@@ -152,9 +152,9 @@ out:
 }
 
 /*
- * 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) {
        case EFI_LOADER_CODE:
@@ -163,18 +163,22 @@ static __init int is_reserve_region(efi_memory_desc_t *md)
        case EFI_BOOT_SERVICES_DATA:
        case EFI_CONVENTIONAL_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:
                break;
        }
-       return is_normal_ram(md);
+       return false;
 }
 
 static __init void reserve_regions(void)
 {
        efi_memory_desc_t *md;
        u64 paddr, npages, size;
-       int resv;
 
        if (efi_enabled(EFI_DBG))
                pr_info("Processing EFI memory map:\n");
@@ -191,32 +195,29 @@ static __init void reserve_regions(void)
                paddr = md->phys_addr;
                npages = md->num_pages;
 
-               resv = is_reserve_region(md);
                if (efi_enabled(EFI_DBG)) {
                        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,
-                               efi_md_typeattr_format(buf, sizeof(buf), md),
-                               resv ? "*" : "");
+                               efi_md_typeattr_format(buf, sizeof(buf), md));
                }
 
                memrange_efi_to_native(&paddr, &npages);
                size = npages << PAGE_SHIFT;
 
-               if (is_normal_ram(md))
+               if (is_memory(md)) {
                        early_init_dt_add_memory_arch(paddr, size);
 
-               if (resv)
-                       memblock_mark_nomap(paddr, size);
-
+                       if (!is_usable_memory(md))
+                               memblock_mark_nomap(paddr, size);
+               }
        }
-
-       set_bit(EFI_MEMMAP, &efi.flags);
 }
 
 void __init efi_init(void)
 {
+       struct efi_memory_map_data data;
        struct efi_fdt_params params;
 
        /* Grab UEFI information placed in FDT by stub */
@@ -225,9 +226,12 @@ void __init efi_init(void)
 
        efi_system_table = params.system_table;
 
-       efi.memmap.phys_map = params.mmap;
-       efi.memmap.map = early_memremap_ro(params.mmap, params.mmap_size);
-       if (efi.memmap.map == NULL) {
+       data.desc_version = params.desc_ver;
+       data.desc_size = params.desc_size;
+       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
                * description of memory we have, so there is little point in
@@ -235,9 +239,6 @@ void __init efi_init(void)
                */
                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,
             "Unexpected EFI_MEMORY_DESCRIPTOR version %ld",
@@ -248,7 +249,8 @@ void __init efi_init(void)
 
        reserve_regions();
        efi_memattr_init();
-       early_memunmap(efi.memmap.map, params.mmap_size);
+       efi_esrt_init();
+       efi_memmap_unmap();
 
        memblock_reserve(params.mmap & PAGE_MASK,
                         PAGE_ALIGN(params.mmap_size +
index c394b81..7c75a8d 100644 (file)
@@ -39,6 +39,26 @@ static struct mm_struct efi_mm = {
        .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)
 {
        efi_memory_desc_t *md;
@@ -114,14 +134,12 @@ static int __init arm_enable_runtime_services(void)
 
        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.map) {
+       if (efi_memmap_init_late(efi.memmap.phys_map, mapsize)) {
                pr_err("Failed to remap EFI memory map\n");
                return -ENOMEM;
        }
-       efi.memmap.map_end = efi.memmap.map + mapsize;
 
        if (!efi_virtmap_init()) {
                pr_err("UEFI virtual mapping missing or invalid -- runtime services will not be available\n");
index 30a24d0..1c33d74 100644 (file)
@@ -125,16 +125,19 @@ static void efi_pstore_scan_sysfs_enter(struct efivar_entry *pos,
  * @entry: deleting entry
  * @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)
 {
        if (entry->deleting) {
                list_del(&entry->list);
                efivar_entry_iter_end();
                efivar_unregister(entry);
-               efivar_entry_iter_begin();
+               if (efivar_entry_iter_begin())
+                       return -EINTR;
        } else if (turn_off_scanning)
                entry->scanning = false;
+
+       return 0;
 }
 
 /**
@@ -144,13 +147,18 @@ static inline void __efi_pstore_scan_sysfs_exit(struct efivar_entry *entry,
  * @head: list head
  * @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 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)
-               __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)
        struct efivar_entry *entry, *n;
        struct list_head *head = &efivar_sysfs_list;
        int size = 0;
+       int ret;
 
        if (!*pos) {
                list_for_each_entry_safe(entry, n, head, list) {
                        efi_pstore_scan_sysfs_enter(entry, n, head);
 
                        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)
                                break;
                }
@@ -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);
 
                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)
                        break;
        }
@@ -232,7 +246,10 @@ static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
        if (!*data.buf)
                return -ENOMEM;
 
-       efivar_entry_iter_begin();
+       if (efivar_entry_iter_begin()) {
+               kfree(*data.buf);
+               return -EINTR;
+       }
        size = efi_pstore_sysfs_entry_iter(&data,
                                           (struct efivar_entry **)&psi->data);
        efivar_entry_iter_end();
@@ -347,7 +364,8 @@ static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count,
        edata.time = time;
        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);
 
        if (found && !entry->scanning) {
index 7dd2e2d..1ac199c 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/slab.h>
 #include <linux/acpi.h>
 #include <linux/ucs2_string.h>
+#include <linux/memblock.h>
 
 #include <asm/early_ioremap.h>
 
@@ -347,56 +348,31 @@ subsys_initcall(efisubsys_init);
 
 /*
  * 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
  * data.
  */
 int __init efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md)
 {
-       struct efi_memory_map *map = &efi.memmap;
-       phys_addr_t p, e;
+       efi_memory_desc_t *md;
 
        if (!efi_enabled(EFI_MEMMAP)) {
                pr_err_once("EFI_MEMMAP is not enabled.\n");
                return -EINVAL;
        }
 
-       if (!map) {
-               pr_err_once("efi.memmap is not set.\n");
-               return -EINVAL;
-       }
        if (!out_md) {
                pr_err_once("out_md is null.\n");
                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 (p = map->phys_map; p < e; p += map->desc_size) {
-               efi_memory_desc_t *md;
+       for_each_efi_memory_desc(md) {
                u64 size;
                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) &&
                    md->type != EFI_BOOT_SERVICES_DATA &&
                    md->type != EFI_RUNTIME_SERVICES_DATA) {
-                       early_memunmap(md, sizeof (*md));
                        continue;
                }
 
@@ -404,11 +380,8 @@ int __init efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md)
                end = md->phys_addr + size;
                if (phys_addr >= md->phys_addr && phys_addr < end) {
                        memcpy(out_md, md, sizeof(*out_md));
-                       early_memunmap(md, sizeof (*md));
                        return 0;
                }
-
-               early_memunmap(md, sizeof (*md));
        }
        pr_err_once("requested map not found.\n");
        return -ENOENT;
@@ -424,6 +397,35 @@ u64 __init efi_mem_desc_end(efi_memory_desc_t *md)
        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[] = {
        {ACPI_20_TABLE_GUID, "ACPI 2.0", &efi.acpi20},
        {ACPI_TABLE_GUID, "ACPI", &efi.acpi},
@@ -811,6 +813,9 @@ int efi_status_to_err(efi_status_t status)
        case EFI_NOT_FOUND:
                err = -ENOENT;
                break;
+       case EFI_ABORTED:
+               err = -EINTR;
+               break;
        default:
                err = -EINVAL;
        }
index 116b244..3e626fd 100644 (file)
@@ -510,7 +510,8 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
                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);
        if (!entry)
                err = -EINVAL;
@@ -575,7 +576,10 @@ efivar_create_sysfs_entry(struct efivar_entry *new_var)
                return ret;
 
        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;
 }
@@ -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)
 {
-       efivar_entry_remove(entry);
+       int err = efivar_entry_remove(entry);
+
+       if (err)
+               return err;
        efivar_unregister(entry);
        return 0;
 }
@@ -698,7 +705,14 @@ static int efivar_sysfs_destroy(struct efivar_entry *entry, void *data)
 static void efivars_sysfs_exit(void)
 {
        /* 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)
                sysfs_remove_bin_file(&efivars_kset->kobj, efivars_new_var);
index 75feb3f..1491407 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/device.h>
 #include <linux/efi.h>
 #include <linux/init.h>
+#include <linux/io.h>
 #include <linux/kernel.h>
 #include <linux/kobject.h>
 #include <linux/list.h>
@@ -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)
 {
@@ -335,7 +336,7 @@ void __init efi_esrt_init(void)
 
        end = esrt_data + size;
        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");
 err_memunmap:
@@ -382,28 +383,18 @@ static void cleanup_entry_list(void)
 static int __init esrt_sysfs_init(void)
 {
        int error;
-       struct efi_system_resource_table __iomem *ioesrt;
 
        pr_debug("esrt-sysfs: loading.\n");
        if (!esrt_data || !esrt_data_size)
                return -ENOSYS;
 
-       ioesrt = ioremap(esrt_data, esrt_data_size);
-       if (!ioesrt) {
-               pr_err("ioremap(%pa, %zu) failed.\n", &esrt_data,
-                      esrt_data_size);
-               return -ENOMEM;
-       }
-
-       esrt = kmalloc(esrt_data_size, GFP_KERNEL);
+       esrt = memremap(esrt_data, esrt_data_size, MEMREMAP_WB);
        if (!esrt) {
-               pr_err("kmalloc failed. (wanted %zu bytes)\n", esrt_data_size);
-               iounmap(ioesrt);
+               pr_err("memremap(%pa, %zu) failed.\n", &esrt_data,
+                      esrt_data_size);
                return -ENOMEM;
        }
 
-       memcpy_fromio(esrt, ioesrt, esrt_data_size);
-
        esrt_kobj = kobject_create_and_add("esrt", efi_kobj);
        if (!esrt_kobj) {
                pr_err("Firmware table registration failed.\n");
@@ -429,8 +420,6 @@ static int __init esrt_sysfs_init(void)
        if (error)
                goto err_cleanup_list;
 
-       memblock_remove(esrt_data, esrt_data_size);
-
        pr_debug("esrt-sysfs: loaded.\n");
 
        return 0;
index 48430ab..520a40e 100644 (file)
 
 #define EFI_MAX_FAKEMEM CONFIG_EFI_MAX_FAKE_MEM
 
-struct fake_mem {
-       struct range range;
-       u64 attribute;
-};
-static struct fake_mem fake_mems[EFI_MAX_FAKEMEM];
+static struct efi_mem_range fake_mems[EFI_MAX_FAKEMEM];
 static int nr_fake_mem;
 
 static int __init cmp_fake_mem(const void *x1, const void *x2)
 {
-       const struct fake_mem *m1 = x1;
-       const struct fake_mem *m2 = x2;
+       const struct efi_mem_range *m1 = x1;
+       const struct efi_mem_range *m2 = x2;
 
        if (m1->range.start < m2->range.start)
                return -1;
@@ -56,40 +52,21 @@ static int __init cmp_fake_mem(const void *x1, const void *x2)
 
 void __init efi_fake_memmap(void)
 {
-       u64 start, end, m_start, m_end, m_attr;
        int new_nr_map = efi.memmap.nr_map;
        efi_memory_desc_t *md;
        phys_addr_t new_memmap_phy;
        void *new_memmap;
-       void *old, *new;
        int i;
 
-       if (!nr_fake_mem || !efi_enabled(EFI_MEMMAP))
+       if (!nr_fake_mem)
                return;
 
        /* count up the number of EFI memory descriptor */
-       for_each_efi_memory_desc(md) {
-               start = md->phys_addr;
-               end = start + (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;
-
-                       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++;
-                       }
+       for (i = 0; i < nr_fake_mem; i++) {
+               for_each_efi_memory_desc(md) {
+                       struct range *r = &fake_mems[i].range;
+
+                       new_nr_map += efi_memmap_split_count(md, r);
                }
        }
 
@@ -107,85 +84,13 @@ void __init efi_fake_memmap(void)
                return;
        }
 
-       for (old = efi.memmap.map, new = new_memmap;
-            old < efi.memmap.map_end;
-            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;
-                       }
-               }
-       }
+       for (i = 0; i < nr_fake_mem; i++)
+               efi_memmap_insert(&efi.memmap, new_memmap, &fake_mems[i]);
 
        /* swap into new EFI memmap */
-       efi_unmap_memmap();
-       efi.memmap.map = new_memmap;
-       efi.memmap.phys_map = new_memmap_phy;
-       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);
+       early_memunmap(new_memmap, efi.memmap.desc_size * new_nr_map);
+
+       efi_memmap_install(new_memmap_phy, new_nr_map);
 
        /* print new EFI memmap */
        efi_print_memmap();
@@ -223,7 +128,7 @@ static int __init setup_fake_mem(char *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);
 
        for (i = 0; i < nr_fake_mem; i++)
diff --git a/drivers/firmware/efi/memmap.c b/drivers/firmware/efi/memmap.c
new file mode 100644 (file)
index 0000000..f03ddec
--- /dev/null
@@ -0,0 +1,303 @@
+/*
+ * 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;
+               }
+       }
+}
index 5c55227..8e64b77 100644 (file)
 
 #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 {
        efi_memory_desc_t md;
        struct kobject kobj;   /* kobject for each entry */
@@ -106,7 +102,8 @@ static struct kobj_type __refdata map_ktype = {
 static struct kset *map_kset;
 
 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;
        struct efi_runtime_map_entry *entry;
@@ -124,8 +121,7 @@ add_sysfs_runtime_map_entry(struct kobject *kobj, int nr)
                return ERR_PTR(-ENOMEM);
        }
 
-       memcpy(&entry->md, efi_runtime_map + nr * efi_memdesc_size,
-              sizeof(efi_memory_desc_t));
+       memcpy(&entry->md, md, sizeof(efi_memory_desc_t));
 
        kobject_init(&entry->kobj, &map_ktype);
        entry->kobj.kset = map_kset;
@@ -142,12 +138,12 @@ add_sysfs_runtime_map_entry(struct kobject *kobj, int nr)
 
 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)
 {
-       return efi_memdesc_size;
+       return efi.memmap.desc_size;
 }
 
 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)
                sz = bufsz;
 
-       memcpy(buf, efi_runtime_map, sz);
+       memcpy(buf, efi.memmap.map, sz);
        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 i, j, ret = 0;
        struct efi_runtime_map_entry *entry;
+       efi_memory_desc_t *md;
 
-       if (!efi_runtime_map)
+       if (!efi_enabled(EFI_MEMMAP))
                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) {
                ret = -ENOMEM;
                goto out;
        }
 
-       for (i = 0; i < nr_efi_runtime_map; i++) {
-               entry = add_sysfs_runtime_map_entry(efi_kobj, i);
+       i = 0;
+       for_each_efi_memory_desc(md) {
+               entry = add_sysfs_runtime_map_entry(efi_kobj, i, md);
                if (IS_ERR(entry)) {
                        ret = PTR_ERR(entry);
                        goto out_add_entry;
                }
-               *(map_entries + i) = entry;
+               *(map_entries + i++) = entry;
        }
 
        return 0;
index 4195877..ae54870 100644 (file)
  * This file is released under the GPLv2.
  */
 
+#define pr_fmt(fmt)    "efi: " fmt
+
 #include <linux/bug.h>
 #include <linux/efi.h>
 #include <linux/irqflags.h>
 #include <linux/mutex.h>
-#include <linux/spinlock.h>
+#include <linux/semaphore.h>
 #include <linux/stringify.h>
 #include <asm/efi.h>
 
@@ -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
- * 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
  * 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)
 {
        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);
-       spin_unlock(&efi_runtime_lock);
+       up(&efi_runtime_lock);
        return status;
 }
 
@@ -102,9 +105,10 @@ static efi_status_t virt_efi_set_time(efi_time_t *tm)
 {
        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);
-       spin_unlock(&efi_runtime_lock);
+       up(&efi_runtime_lock);
        return status;
 }
 
@@ -114,9 +118,10 @@ static efi_status_t virt_efi_get_wakeup_time(efi_bool_t *enabled,
 {
        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);
-       spin_unlock(&efi_runtime_lock);
+       up(&efi_runtime_lock);
        return status;
 }
 
@@ -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;
 
-       spin_lock(&efi_runtime_lock);
+       if (down_interruptible(&efi_runtime_lock))
+               return EFI_ABORTED;
        status = efi_call_virt(set_wakeup_time, enabled, tm);
-       spin_unlock(&efi_runtime_lock);
+       up(&efi_runtime_lock);
        return status;
 }
 
@@ -138,10 +144,11 @@ static efi_status_t virt_efi_get_variable(efi_char16_t *name,
 {
        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,
                               data);
-       spin_unlock(&efi_runtime_lock);
+       up(&efi_runtime_lock);
        return status;
 }
 
@@ -151,9 +158,10 @@ static efi_status_t virt_efi_get_next_variable(unsigned long *name_size,
 {
        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);
-       spin_unlock(&efi_runtime_lock);
+       up(&efi_runtime_lock);
        return status;
 }
 
@@ -165,10 +173,11 @@ static efi_status_t virt_efi_set_variable(efi_char16_t *name,
 {
        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,
                               data);
-       spin_unlock(&efi_runtime_lock);
+       up(&efi_runtime_lock);
        return status;
 }
 
@@ -179,12 +188,12 @@ virt_efi_set_variable_nonblocking(efi_char16_t *name, efi_guid_t *vendor,
 {
        efi_status_t status;
 
-       if (!spin_trylock(&efi_runtime_lock))
+       if (down_trylock(&efi_runtime_lock))
                return EFI_NOT_READY;
 
        status = efi_call_virt(set_variable, name, vendor, attr, data_size,
                               data);
-       spin_unlock(&efi_runtime_lock);
+       up(&efi_runtime_lock);
        return status;
 }
 
@@ -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)
                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,
                               remaining_space, max_variable_size);
-       spin_unlock(&efi_runtime_lock);
+       up(&efi_runtime_lock);
        return status;
 }
 
@@ -217,12 +227,12 @@ virt_efi_query_variable_info_nonblocking(u32 attr,
        if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION)
                return EFI_UNSUPPORTED;
 
-       if (!spin_trylock(&efi_runtime_lock))
+       if (down_trylock(&efi_runtime_lock))
                return EFI_NOT_READY;
 
        status = efi_call_virt(query_variable_info, attr, storage_space,
                               remaining_space, max_variable_size);
-       spin_unlock(&efi_runtime_lock);
+       up(&efi_runtime_lock);
        return status;
 }
 
@@ -230,9 +240,10 @@ static efi_status_t virt_efi_get_next_high_mono_count(u32 *count)
 {
        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);
-       spin_unlock(&efi_runtime_lock);
+       up(&efi_runtime_lock);
        return status;
 }
 
@@ -241,9 +252,13 @@ static void virt_efi_reset_system(int reset_type,
                                  unsigned long data_size,
                                  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);
-       spin_unlock(&efi_runtime_lock);
+       up(&efi_runtime_lock);
 }
 
 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)
                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);
-       spin_unlock(&efi_runtime_lock);
+       up(&efi_runtime_lock);
        return status;
 }
 
@@ -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)
                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,
                               reset_type);
-       spin_unlock(&efi_runtime_lock);
+       up(&efi_runtime_lock);
        return status;
 }
 
diff --git a/drivers/firmware/efi/test/Makefile b/drivers/firmware/efi/test/Makefile
new file mode 100644 (file)
index 0000000..bcd4577
--- /dev/null
@@ -0,0 +1 @@
+obj-$(CONFIG_EFI_TEST)                 += efi_test.o
diff --git a/drivers/firmware/efi/test/efi_test.c b/drivers/firmware/efi/test/efi_test.c
new file mode 100644 (file)
index 0000000..f61bb52
--- /dev/null
@@ -0,0 +1,749 @@
+/*
+ * EFI Test Driver for Runtime Services
+ *
+ * Copyright(C) 2012-2016 Canonical Ltd.
+ *
+ * This driver exports EFI runtime services interfaces into userspace, which
+ * allow to use and test UEFI runtime services provided by firmware.
+ *
+ */
+
+#include <linux/version.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/efi.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#include "efi_test.h"
+
+MODULE_AUTHOR("Ivan Hu <ivan.hu@canonical.com>");
+MODULE_DESCRIPTION("EFI Test Driver");
+MODULE_LICENSE("GPL");
+
+/*
+ * Count the bytes in 'str', including the terminating NULL.
+ *
+ * Note this function returns the number of *bytes*, not the number of
+ * ucs2 characters.
+ */
+static inline size_t user_ucs2_strsize(efi_char16_t  __user *str)
+{
+       efi_char16_t *s = str, c;
+       size_t len;
+
+       if (!str)
+               return 0;
+
+       /* Include terminating NULL */
+       len = sizeof(efi_char16_t);
+
+       if (get_user(c, s++)) {
+               /* Can't read userspace memory for size */
+               return 0;
+       }
+
+       while (c != 0) {
+               if (get_user(c, s++)) {
+                       /* Can't read userspace memory for size */
+                       return 0;
+               }
+               len += sizeof(efi_char16_t);
+       }
+       return len;
+}
+
+/*
+ * Allocate a buffer and copy a ucs2 string from user space into it.
+ */
+static inline int
+copy_ucs2_from_user_len(efi_char16_t **dst, efi_char16_t __user *src,
+                       size_t len)
+{
+       efi_char16_t *buf;
+
+       if (!src) {
+               *dst = NULL;
+               return 0;
+       }
+
+       if (!access_ok(VERIFY_READ, src, 1))
+               return -EFAULT;
+
+       buf = kmalloc(len, GFP_KERNEL);
+       if (!buf) {
+               *dst = NULL;
+               return -ENOMEM;
+       }
+       *dst = buf;
+
+       if (copy_from_user(*dst, src, len)) {
+               kfree(buf);
+               return -EFAULT;
+       }
+
+       return 0;
+}
+
+/*
+ * Count the bytes in 'str', including the terminating NULL.
+ *
+ * Just a wrap for user_ucs2_strsize
+ */
+static inline int
+get_ucs2_strsize_from_user(efi_char16_t __user *src, size_t *len)
+{
+       if (!access_ok(VERIFY_READ, src, 1))
+               return -EFAULT;
+
+       *len = user_ucs2_strsize(src);
+       if (*len == 0)
+               return -EFAULT;
+
+       return 0;
+}
+
+/*
+ * Calculate the required buffer allocation size and copy a ucs2 string
+ * from user space into it.
+ *
+ * This function differs from copy_ucs2_from_user_len() because it
+ * calculates the size of the buffer to allocate by taking the length of
+ * the string 'src'.
+ *
+ * If a non-zero value is returned, the caller MUST NOT access 'dst'.
+ *
+ * It is the caller's responsibility to free 'dst'.
+ */
+static inline int
+copy_ucs2_from_user(efi_char16_t **dst, efi_char16_t __user *src)
+{
+       size_t len;
+
+       if (!access_ok(VERIFY_READ, src, 1))
+               return -EFAULT;
+
+       len = user_ucs2_strsize(src);
+       if (len == 0)
+               return -EFAULT;
+       return copy_ucs2_from_user_len(dst, src, len);
+}
+
+/*
+ * Copy a ucs2 string to a user buffer.
+ *
+ * This function is a simple wrapper around copy_to_user() that does
+ * nothing if 'src' is NULL, which is useful for reducing the amount of
+ * NULL checking the caller has to do.
+ *
+ * 'len' specifies the number of bytes to copy.
+ */
+static inline int
+copy_ucs2_to_user_len(efi_char16_t __user *dst, efi_char16_t *src, size_t len)
+{
+       if (!src)
+               return 0;
+
+       if (!access_ok(VERIFY_WRITE, dst, 1))
+               return -EFAULT;
+
+       return copy_to_user(dst, src, len);
+}
+
+static long efi_runtime_get_variable(unsigned long arg)
+{
+       struct efi_getvariable __user *getvariable_user;
+       struct efi_getvariable getvariable;
+       unsigned long datasize, prev_datasize, *dz;
+       efi_guid_t vendor_guid, *vd = NULL;
+       efi_status_t status;
+       efi_char16_t *name = NULL;
+       u32 attr, *at;
+       void *data = NULL;
+       int rv = 0;
+
+       getvariable_user = (struct efi_getvariable __user *)arg;
+
+       if (copy_from_user(&getvariable, getvariable_user,
+                          sizeof(getvariable)))
+               return -EFAULT;
+       if (getvariable.data_size &&
+           get_user(datasize, getvariable.data_size))
+               return -EFAULT;
+       if (getvariable.vendor_guid) {
+               if (copy_from_user(&vendor_guid, getvariable.vendor_guid,
+                                       sizeof(vendor_guid)))
+                       return -EFAULT;
+               vd = &vendor_guid;
+       }
+
+       if (getvariable.variable_name) {
+               rv = copy_ucs2_from_user(&name, getvariable.variable_name);
+               if (rv)
+                       return rv;
+       }
+
+       at = getvariable.attributes ? &attr : NULL;
+       dz = getvariable.data_size ? &datasize : NULL;
+
+       if (getvariable.data_size && getvariable.data) {
+               data = kmalloc(datasize, GFP_KERNEL);
+               if (!data) {
+                       kfree(name);
+                       return -ENOMEM;
+               }
+       }
+
+       prev_datasize = datasize;
+       status = efi.get_variable(name, vd, at, dz, data);
+       kfree(name);
+
+       if (put_user(status, getvariable.status)) {
+               rv = -EFAULT;
+               goto out;
+       }
+
+       if (status != EFI_SUCCESS) {
+               if (status == EFI_BUFFER_TOO_SMALL) {
+                       if (dz && put_user(datasize, getvariable.data_size)) {
+                               rv = -EFAULT;
+                               goto out;
+                       }
+               }
+               rv = -EINVAL;
+               goto out;
+       }
+
+       if (prev_datasize < datasize) {
+               rv = -EINVAL;
+               goto out;
+       }
+
+       if (data) {
+               if (copy_to_user(getvariable.data, data, datasize)) {
+                       rv = -EFAULT;
+                       goto out;
+               }
+       }
+
+       if (at && put_user(attr, getvariable.attributes)) {
+               rv = -EFAULT;
+               goto out;
+       }
+
+       if (dz && put_user(datasize, getvariable.data_size))
+               rv = -EFAULT;
+
+out:
+       kfree(data);
+       return rv;
+
+}
+
+static long efi_runtime_set_variable(unsigned long arg)
+{
+       struct efi_setvariable __user *setvariable_user;
+       struct efi_setvariable setvariable;
+       efi_guid_t vendor_guid;
+       efi_status_t status;
+       efi_char16_t *name = NULL;
+       void *data;
+       int rv = 0;
+
+       setvariable_user = (struct efi_setvariable __user *)arg;
+
+       if (copy_from_user(&setvariable, setvariable_user, sizeof(setvariable)))
+               return -EFAULT;
+       if (copy_from_user(&vendor_guid, setvariable.vendor_guid,
+                               sizeof(vendor_guid)))
+               return -EFAULT;
+
+       if (setvariable.variable_name) {
+               rv = copy_ucs2_from_user(&name, setvariable.variable_name);
+               if (rv)
+                       return rv;
+       }
+
+       data = kmalloc(setvariable.data_size, GFP_KERNEL);
+       if (!data) {
+               kfree(name);
+               return -ENOMEM;
+       }
+       if (copy_from_user(data, setvariable.data, setvariable.data_size)) {
+               rv = -EFAULT;
+               goto out;
+       }
+
+       status = efi.set_variable(name, &vendor_guid,
+                               setvariable.attributes,
+                               setvariable.data_size, data);
+
+       if (put_user(status, setvariable.status)) {
+               rv = -EFAULT;
+               goto out;
+       }
+
+       rv = status == EFI_SUCCESS ? 0 : -EINVAL;
+
+out:
+       kfree(data);
+       kfree(name);
+
+       return rv;
+}
+
+static long efi_runtime_get_time(unsigned long arg)
+{
+       struct efi_gettime __user *gettime_user;
+       struct efi_gettime  gettime;
+       efi_status_t status;
+       efi_time_cap_t cap;
+       efi_time_t efi_time;
+
+       gettime_user = (struct efi_gettime __user *)arg;
+       if (copy_from_user(&gettime, gettime_user, sizeof(gettime)))
+               return -EFAULT;
+
+       status = efi.get_time(gettime.time ? &efi_time : NULL,
+                             gettime.capabilities ? &cap : NULL);
+
+       if (put_user(status, gettime.status))
+               return -EFAULT;
+
+       if (status != EFI_SUCCESS)
+               return -EINVAL;
+
+       if (gettime.capabilities) {
+               efi_time_cap_t __user *cap_local;
+
+               cap_local = (efi_time_cap_t *)gettime.capabilities;
+               if (put_user(cap.resolution, &(cap_local->resolution)) ||
+                       put_user(cap.accuracy, &(cap_local->accuracy)) ||
+                       put_user(cap.sets_to_zero, &(cap_local->sets_to_zero)))
+                       return -EFAULT;
+       }
+       if (gettime.time) {
+               if (copy_to_user(gettime.time, &efi_time, sizeof(efi_time_t)))
+                       return -EFAULT;
+       }
+
+       return 0;
+}
+
+static long efi_runtime_set_time(unsigned long arg)
+{
+       struct efi_settime __user *settime_user;
+       struct efi_settime settime;
+       efi_status_t status;
+       efi_time_t efi_time;
+
+       settime_user = (struct efi_settime __user *)arg;
+       if (copy_from_user(&settime, settime_user, sizeof(settime)))
+               return -EFAULT;
+       if (copy_from_user(&efi_time, settime.time,
+                                       sizeof(efi_time_t)))
+               return -EFAULT;
+       status = efi.set_time(&efi_time);
+
+       if (put_user(status, settime.status))
+               return -EFAULT;
+
+       return status == EFI_SUCCESS ? 0 : -EINVAL;
+}
+
+static long efi_runtime_get_waketime(unsigned long arg)
+{
+       struct efi_getwakeuptime __user *getwakeuptime_user;
+       struct efi_getwakeuptime getwakeuptime;
+       efi_bool_t enabled, pending;
+       efi_status_t status;
+       efi_time_t efi_time;
+
+       getwakeuptime_user = (struct efi_getwakeuptime __user *)arg;
+       if (copy_from_user(&getwakeuptime, getwakeuptime_user,
+                               sizeof(getwakeuptime)))
+               return -EFAULT;
+
+       status = efi.get_wakeup_time(
+               getwakeuptime.enabled ? (efi_bool_t *)&enabled : NULL,
+               getwakeuptime.pending ? (efi_bool_t *)&pending : NULL,
+               getwakeuptime.time ? &efi_time : NULL);
+
+       if (put_user(status, getwakeuptime.status))
+               return -EFAULT;
+
+       if (status != EFI_SUCCESS)
+               return -EINVAL;
+
+       if (getwakeuptime.enabled && put_user(enabled,
+                                               getwakeuptime.enabled))
+               return -EFAULT;
+
+       if (getwakeuptime.time) {
+               if (copy_to_user(getwakeuptime.time, &efi_time,
+                               sizeof(efi_time_t)))
+                       return -EFAULT;
+       }
+
+       return 0;
+}
+
+static long efi_runtime_set_waketime(unsigned long arg)
+{
+       struct efi_setwakeuptime __user *setwakeuptime_user;
+       struct efi_setwakeuptime setwakeuptime;
+       efi_bool_t enabled;
+       efi_status_t status;
+       efi_time_t efi_time;
+
+       setwakeuptime_user = (struct efi_setwakeuptime __user *)arg;
+
+       if (copy_from_user(&setwakeuptime, setwakeuptime_user,
+                               sizeof(setwakeuptime)))
+               return -EFAULT;
+
+       enabled = setwakeuptime.enabled;
+       if (setwakeuptime.time) {
+               if (copy_from_user(&efi_time, setwakeuptime.time,
+                                       sizeof(efi_time_t)))
+                       return -EFAULT;
+
+               status = efi.set_wakeup_time(enabled, &efi_time);
+       } else
+               status = efi.set_wakeup_time(enabled, NULL);
+
+       if (put_user(status, setwakeuptime.status))
+               return -EFAULT;
+
+       return status == EFI_SUCCESS ? 0 : -EINVAL;
+}
+
+static long efi_runtime_get_nextvariablename(unsigned long arg)
+{
+       struct efi_getnextvariablename __user *getnextvariablename_user;
+       struct efi_getnextvariablename getnextvariablename;
+       unsigned long name_size, prev_name_size = 0, *ns = NULL;
+       efi_status_t status;
+       efi_guid_t *vd = NULL;
+       efi_guid_t vendor_guid;
+       efi_char16_t *name = NULL;
+       int rv;
+
+       getnextvariablename_user = (struct efi_getnextvariablename __user *)arg;
+
+       if (copy_from_user(&getnextvariablename, getnextvariablename_user,
+                          sizeof(getnextvariablename)))
+               return -EFAULT;
+
+       if (getnextvariablename.variable_name_size) {
+               if (get_user(name_size, getnextvariablename.variable_name_size))
+                       return -EFAULT;
+               ns = &name_size;
+               prev_name_size = name_size;
+       }
+
+       if (getnextvariablename.vendor_guid) {
+               if (copy_from_user(&vendor_guid,
+                               getnextvariablename.vendor_guid,
+                               sizeof(vendor_guid)))
+                       return -EFAULT;
+               vd = &vendor_guid;
+       }
+
+       if (getnextvariablename.variable_name) {
+               size_t name_string_size = 0;
+
+               rv = get_ucs2_strsize_from_user(
+                               getnextvariablename.variable_name,
+                               &name_string_size);
+               if (rv)
+                       return rv;
+               /*
+                * The name_size may be smaller than the real buffer size where
+                * variable name located in some use cases. The most typical
+                * case is passing a 0 to get the required buffer size for the
+                * 1st time call. So we need to copy the content from user
+                * space for at least the string size of variable name, or else
+                * the name passed to UEFI may not be terminated as we expected.
+                */
+               rv = copy_ucs2_from_user_len(&name,
+                               getnextvariablename.variable_name,
+                               prev_name_size > name_string_size ?
+                               prev_name_size : name_string_size);
+               if (rv)
+                       return rv;
+       }
+
+       status = efi.get_next_variable(ns, name, vd);
+
+       if (put_user(status, getnextvariablename.status)) {
+               rv = -EFAULT;
+               goto out;
+       }
+
+       if (status != EFI_SUCCESS) {
+               if (status == EFI_BUFFER_TOO_SMALL) {
+                       if (ns && put_user(*ns,
+                               getnextvariablename.variable_name_size)) {
+                               rv = -EFAULT;
+                               goto out;
+                       }
+               }
+               rv = -EINVAL;
+               goto out;
+       }
+
+       if (name) {
+               if (copy_ucs2_to_user_len(getnextvariablename.variable_name,
+                                               name, prev_name_size)) {
+                       rv = -EFAULT;
+                       goto out;
+               }
+       }
+
+       if (ns) {
+               if (put_user(*ns, getnextvariablename.variable_name_size)) {
+                       rv = -EFAULT;
+                       goto out;
+               }
+       }
+
+       if (vd) {
+               if (copy_to_user(getnextvariablename.vendor_guid, vd,
+                                                       sizeof(efi_guid_t)))
+                       rv = -EFAULT;
+       }
+
+out:
+       kfree(name);
+       return rv;
+}
+
+static long efi_runtime_get_nexthighmonocount(unsigned long arg)
+{
+       struct efi_getnexthighmonotoniccount __user *getnexthighmonocount_user;
+       struct efi_getnexthighmonotoniccount getnexthighmonocount;
+       efi_status_t status;
+       u32 count;
+
+       getnexthighmonocount_user = (struct
+                       efi_getnexthighmonotoniccount __user *)arg;
+
+       if (copy_from_user(&getnexthighmonocount,
+                          getnexthighmonocount_user,
+                          sizeof(getnexthighmonocount)))
+               return -EFAULT;
+
+       status = efi.get_next_high_mono_count(
+               getnexthighmonocount.high_count ? &count : NULL);
+
+       if (put_user(status, getnexthighmonocount.status))
+               return -EFAULT;
+
+       if (status != EFI_SUCCESS)
+               return -EINVAL;
+
+       if (getnexthighmonocount.high_count &&
+           put_user(count, getnexthighmonocount.high_count))
+               return -EFAULT;
+
+       return 0;
+}
+
+static long efi_runtime_query_variableinfo(unsigned long arg)
+{
+       struct efi_queryvariableinfo __user *queryvariableinfo_user;
+       struct efi_queryvariableinfo queryvariableinfo;
+       efi_status_t status;
+       u64 max_storage, remaining, max_size;
+
+       queryvariableinfo_user = (struct efi_queryvariableinfo __user *)arg;
+
+       if (copy_from_user(&queryvariableinfo, queryvariableinfo_user,
+                          sizeof(queryvariableinfo)))
+               return -EFAULT;
+
+       status = efi.query_variable_info(queryvariableinfo.attributes,
+                                        &max_storage, &remaining, &max_size);
+
+       if (put_user(status, queryvariableinfo.status))
+               return -EFAULT;
+
+       if (status != EFI_SUCCESS)
+               return -EINVAL;
+
+       if (put_user(max_storage,
+                    queryvariableinfo.maximum_variable_storage_size))
+               return -EFAULT;
+
+       if (put_user(remaining,
+                    queryvariableinfo.remaining_variable_storage_size))
+               return -EFAULT;
+
+       if (put_user(max_size, queryvariableinfo.maximum_variable_size))
+               return -EFAULT;
+
+       return 0;
+}
+
+static long efi_runtime_query_capsulecaps(unsigned long arg)
+{
+       struct efi_querycapsulecapabilities __user *qcaps_user;
+       struct efi_querycapsulecapabilities qcaps;
+       efi_capsule_header_t *capsules;
+       efi_status_t status;
+       u64 max_size;
+       int i, reset_type;
+       int rv = 0;
+
+       qcaps_user = (struct efi_querycapsulecapabilities __user *)arg;
+
+       if (copy_from_user(&qcaps, qcaps_user, sizeof(qcaps)))
+               return -EFAULT;
+
+       capsules = kcalloc(qcaps.capsule_count + 1,
+                          sizeof(efi_capsule_header_t), GFP_KERNEL);
+       if (!capsules)
+               return -ENOMEM;
+
+       for (i = 0; i < qcaps.capsule_count; i++) {
+               efi_capsule_header_t *c;
+               /*
+                * We cannot dereference qcaps.capsule_header_array directly to
+                * obtain the address of the capsule as it resides in the
+                * user space
+                */
+               if (get_user(c, qcaps.capsule_header_array + i)) {
+                       rv = -EFAULT;
+                       goto out;
+               }
+               if (copy_from_user(&capsules[i], c,
+                               sizeof(efi_capsule_header_t))) {
+                       rv = -EFAULT;
+                       goto out;
+               }
+       }
+
+       qcaps.capsule_header_array = &capsules;
+
+       status = efi.query_capsule_caps((efi_capsule_header_t **)
+                                       qcaps.capsule_header_array,
+                                       qcaps.capsule_count,
+                                       &max_size, &reset_type);
+
+       if (put_user(status, qcaps.status)) {
+               rv = -EFAULT;
+               goto out;
+       }
+
+       if (status != EFI_SUCCESS) {
+               rv = -EINVAL;
+               goto out;
+       }
+
+       if (put_user(max_size, qcaps.maximum_capsule_size)) {
+               rv = -EFAULT;
+               goto out;
+       }
+
+       if (put_user(reset_type, qcaps.reset_type))
+               rv = -EFAULT;
+
+out:
+       kfree(capsules);
+       return rv;
+}
+
+static long efi_test_ioctl(struct file *file, unsigned int cmd,
+                                                       unsigned long arg)
+{
+       switch (cmd) {
+       case EFI_RUNTIME_GET_VARIABLE:
+               return efi_runtime_get_variable(arg);
+
+       case EFI_RUNTIME_SET_VARIABLE:
+               return efi_runtime_set_variable(arg);
+
+       case EFI_RUNTIME_GET_TIME:
+               return efi_runtime_get_time(arg);
+
+       case EFI_RUNTIME_SET_TIME:
+               return efi_runtime_set_time(arg);
+
+       case EFI_RUNTIME_GET_WAKETIME:
+               return efi_runtime_get_waketime(arg);
+
+       case EFI_RUNTIME_SET_WAKETIME:
+               return efi_runtime_set_waketime(arg);
+
+       case EFI_RUNTIME_GET_NEXTVARIABLENAME:
+               return efi_runtime_get_nextvariablename(arg);
+
+       case EFI_RUNTIME_GET_NEXTHIGHMONOTONICCOUNT:
+               return efi_runtime_get_nexthighmonocount(arg);
+
+       case EFI_RUNTIME_QUERY_VARIABLEINFO:
+               return efi_runtime_query_variableinfo(arg);
+
+       case EFI_RUNTIME_QUERY_CAPSULECAPABILITIES:
+               return efi_runtime_query_capsulecaps(arg);
+       }
+
+       return -ENOTTY;
+}
+
+static int efi_test_open(struct inode *inode, struct file *file)
+{
+       /*
+        * nothing special to do here
+        * We do accept multiple open files at the same time as we
+        * synchronize on the per call operation.
+        */
+       return 0;
+}
+
+static int efi_test_close(struct inode *inode, struct file *file)
+{
+       return 0;
+}
+
+/*
+ *     The various file operations we support.
+ */
+static const struct file_operations efi_test_fops = {
+       .owner          = THIS_MODULE,
+       .unlocked_ioctl = efi_test_ioctl,
+       .open           = efi_test_open,
+       .release        = efi_test_close,
+       .llseek         = no_llseek,
+};
+
+static struct miscdevice efi_test_dev = {
+       MISC_DYNAMIC_MINOR,
+       "efi_test",
+       &efi_test_fops
+};
+
+static int __init efi_test_init(void)
+{
+       int ret;
+
+       ret = misc_register(&efi_test_dev);
+       if (ret) {
+               pr_err("efi_test: can't misc_register on minor=%d\n",
+                       MISC_DYNAMIC_MINOR);
+               return ret;
+       }
+
+       return 0;
+}
+
+static void __exit efi_test_exit(void)
+{
+       misc_deregister(&efi_test_dev);
+}
+
+module_init(efi_test_init);
+module_exit(efi_test_exit);
diff --git a/drivers/firmware/efi/test/efi_test.h b/drivers/firmware/efi/test/efi_test.h
new file mode 100644 (file)
index 0000000..a33a6c6
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * 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_ */
index d3b7513..9336ffd 100644 (file)
 /* Private pointer to registered efivars */
 static struct efivars *__efivars;
 
+/*
+ * efivars_lock protects three things:
+ * 1) efivarfs_list and efivars_sysfs_list
+ * 2) ->ops calls
+ * 3) (un)registration of __efivars
+ */
+static DEFINE_SEMAPHORE(efivars_lock);
+
 static bool efivar_wq_enabled = true;
 DECLARE_WORK(efivar_work, NULL);
 EXPORT_SYMBOL_GPL(efivar_work);
@@ -434,7 +442,10 @@ int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
                return -ENOMEM;
        }
 
-       spin_lock_irq(&__efivars->lock);
+       if (down_interruptible(&efivars_lock)) {
+               err = -EINTR;
+               goto free;
+       }
 
        /*
         * Per EFI spec, the maximum storage allocated for both
@@ -450,7 +461,7 @@ int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
                switch (status) {
                case EFI_SUCCESS:
                        if (duplicates)
-                               spin_unlock_irq(&__efivars->lock);
+                               up(&efivars_lock);
 
                        variable_name_size = var_name_strnsize(variable_name,
                                                               variable_name_size);
@@ -476,8 +487,12 @@ int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
                                        status = EFI_NOT_FOUND;
                        }
 
-                       if (duplicates)
-                               spin_lock_irq(&__efivars->lock);
+                       if (duplicates) {
+                               if (down_interruptible(&efivars_lock)) {
+                                       err = -EINTR;
+                                       goto free;
+                               }
+                       }
 
                        break;
                case EFI_NOT_FOUND:
@@ -491,8 +506,8 @@ int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
 
        } while (status != EFI_NOT_FOUND);
 
-       spin_unlock_irq(&__efivars->lock);
-
+       up(&efivars_lock);
+free:
        kfree(variable_name);
 
        return err;
@@ -503,24 +518,34 @@ EXPORT_SYMBOL_GPL(efivar_init);
  * efivar_entry_add - add entry to variable list
  * @entry: entry to add to list
  * @head: list head
+ *
+ * Returns 0 on success, or a kernel error code on failure.
  */
-void efivar_entry_add(struct efivar_entry *entry, struct list_head *head)
+int efivar_entry_add(struct efivar_entry *entry, struct list_head *head)
 {
-       spin_lock_irq(&__efivars->lock);
+       if (down_interruptible(&efivars_lock))
+               return -EINTR;
        list_add(&entry->list, head);
-       spin_unlock_irq(&__efivars->lock);
+       up(&efivars_lock);
+
+       return 0;
 }
 EXPORT_SYMBOL_GPL(efivar_entry_add);
 
 /**
  * efivar_entry_remove - remove entry from variable list
  * @entry: entry to remove from list
+ *
+ * Returns 0 on success, or a kernel error code on failure.
  */
-void efivar_entry_remove(struct efivar_entry *entry)
+int efivar_entry_remove(struct efivar_entry *entry)
 {
-       spin_lock_irq(&__efivars->lock);
+       if (down_interruptible(&efivars_lock))
+               return -EINTR;
        list_del(&entry->list);
-       spin_unlock_irq(&__efivars->lock);
+       up(&efivars_lock);
+
+       return 0;
 }
 EXPORT_SYMBOL_GPL(efivar_entry_remove);
 
@@ -537,10 +562,8 @@ EXPORT_SYMBOL_GPL(efivar_entry_remove);
  */
 static void efivar_entry_list_del_unlock(struct efivar_entry *entry)
 {
-       lockdep_assert_held(&__efivars->lock);
-
        list_del(&entry->list);
-       spin_unlock_irq(&__efivars->lock);
+       up(&efivars_lock);
 }
 
 /**
@@ -563,8 +586,6 @@ int __efivar_entry_delete(struct efivar_entry *entry)
        const struct efivar_operations *ops = __efivars->ops;
        efi_status_t status;
 
-       lockdep_assert_held(&__efivars->lock);
-
        status = ops->set_variable(entry->var.VariableName,
                                   &entry->var.VendorGuid,
                                   0, 0, NULL);
@@ -581,20 +602,22 @@ EXPORT_SYMBOL_GPL(__efivar_entry_delete);
  * variable list. It is the caller's responsibility to free @entry
  * once we return.
  *
- * Returns 0 on success, or a converted EFI status code if
- * set_variable() fails.
+ * Returns 0 on success, -EINTR if we can't grab the semaphore,
+ * converted EFI status code if set_variable() fails.
  */
 int efivar_entry_delete(struct efivar_entry *entry)
 {
        const struct efivar_operations *ops = __efivars->ops;
        efi_status_t status;
 
-       spin_lock_irq(&__efivars->lock);
+       if (down_interruptible(&efivars_lock))
+               return -EINTR;
+
        status = ops->set_variable(entry->var.VariableName,
                                   &entry->var.VendorGuid,
                                   0, 0, NULL);
        if (!(status == EFI_SUCCESS || status == EFI_NOT_FOUND)) {
-               spin_unlock_irq(&__efivars->lock);
+               up(&efivars_lock);
                return efi_status_to_err(status);
        }
 
@@ -620,9 +643,9 @@ EXPORT_SYMBOL_GPL(efivar_entry_delete);
  * If @head is not NULL a lookup is performed to determine whether
  * the entry is already on the list.
  *
- * Returns 0 on success, -EEXIST if a lookup is performed and the entry
- * already exists on the list, or a converted EFI status code if
- * set_variable() fails.
+ * Returns 0 on success, -EINTR if we can't grab the semaphore,
+ * -EEXIST if a lookup is performed and the entry already exists on
+ * the list, or a converted EFI status code if set_variable() fails.
  */
 int efivar_entry_set(struct efivar_entry *entry, u32 attributes,
                     unsigned long size, void *data, struct list_head *head)
@@ -632,10 +655,10 @@ int efivar_entry_set(struct efivar_entry *entry, u32 attributes,
        efi_char16_t *name = entry->var.VariableName;
        efi_guid_t vendor = entry->var.VendorGuid;
 
-       spin_lock_irq(&__efivars->lock);
-
+       if (down_interruptible(&efivars_lock))
+               return -EINTR;
        if (head && efivar_entry_find(name, vendor, head, false)) {
-               spin_unlock_irq(&__efivars->lock);
+               up(&efivars_lock);
                return -EEXIST;
        }
 
@@ -644,7 +667,7 @@ int efivar_entry_set(struct efivar_entry *entry, u32 attributes,
                status = ops->set_variable(name, &vendor,
                                           attributes, size, data);
 
-       spin_unlock_irq(&__efivars->lock);
+       up(&efivars_lock);
 
        return efi_status_to_err(status);
 
@@ -658,30 +681,29 @@ EXPORT_SYMBOL_GPL(efivar_entry_set);
  * from crash/panic handlers.
  *
  * Crucially, this function will not block if it cannot acquire
- * __efivars->lock. Instead, it returns -EBUSY.
+ * 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))
+       if (down_trylock(&efivars_lock))
                return -EBUSY;
 
        status = check_var_size_nonblocking(attributes,
                                            size + ucs2_strsize(name, 1024));
        if (status != EFI_SUCCESS) {
-               spin_unlock_irqrestore(&__efivars->lock, flags);
+               up(&efivars_lock);
                return -ENOSPC;
        }
 
        status = ops->set_variable_nonblocking(name, &vendor, attributes,
                                               size, data);
 
-       spin_unlock_irqrestore(&__efivars->lock, flags);
+       up(&efivars_lock);
        return efi_status_to_err(status);
 }
 
@@ -706,7 +728,6 @@ int efivar_entry_set_safe(efi_char16_t *name, efi_guid_t vendor, u32 attributes,
                          bool block, unsigned long size, void *data)
 {
        const struct efivar_operations *ops = __efivars->ops;
-       unsigned long flags;
        efi_status_t status;
 
        if (!ops->query_variable_store)
@@ -727,21 +748,22 @@ int efivar_entry_set_safe(efi_char16_t *name, efi_guid_t vendor, u32 attributes,
                                                    size, data);
 
        if (!block) {
-               if (!spin_trylock_irqsave(&__efivars->lock, flags))
+               if (down_trylock(&efivars_lock))
                        return -EBUSY;
        } else {
-               spin_lock_irqsave(&__efivars->lock, flags);
+               if (down_interruptible(&efivars_lock))
+                       return -EINTR;
        }
 
        status = check_var_size(attributes, size + ucs2_strsize(name, 1024));
        if (status != EFI_SUCCESS) {
-               spin_unlock_irqrestore(&__efivars->lock, flags);
+               up(&efivars_lock);
                return -ENOSPC;
        }
 
        status = ops->set_variable(name, &vendor, attributes, size, data);
 
-       spin_unlock_irqrestore(&__efivars->lock, flags);
+       up(&efivars_lock);
 
        return efi_status_to_err(status);
 }
@@ -771,8 +793,6 @@ struct efivar_entry *efivar_entry_find(efi_char16_t *name, efi_guid_t guid,
        int strsize1, strsize2;
        bool found = false;
 
-       lockdep_assert_held(&__efivars->lock);
-
        list_for_each_entry_safe(entry, n, head, list) {
                strsize1 = ucs2_strsize(name, 1024);
                strsize2 = ucs2_strsize(entry->var.VariableName, 1024);
@@ -814,10 +834,11 @@ int efivar_entry_size(struct efivar_entry *entry, unsigned long *size)
 
        *size = 0;
 
-       spin_lock_irq(&__efivars->lock);
+       if (down_interruptible(&efivars_lock))
+               return -EINTR;
        status = ops->get_variable(entry->var.VariableName,
                                   &entry->var.VendorGuid, NULL, size, NULL);
-       spin_unlock_irq(&__efivars->lock);
+       up(&efivars_lock);
 
        if (status != EFI_BUFFER_TOO_SMALL)
                return efi_status_to_err(status);
@@ -843,8 +864,6 @@ int __efivar_entry_get(struct efivar_entry *entry, u32 *attributes,
        const struct efivar_operations *ops = __efivars->ops;
        efi_status_t status;
 
-       lockdep_assert_held(&__efivars->lock);
-
        status = ops->get_variable(entry->var.VariableName,
                                   &entry->var.VendorGuid,
                                   attributes, size, data);
@@ -866,11 +885,12 @@ int efivar_entry_get(struct efivar_entry *entry, u32 *attributes,
        const struct efivar_operations *ops = __efivars->ops;
        efi_status_t status;
 
-       spin_lock_irq(&__efivars->lock);
+       if (down_interruptible(&efivars_lock))
+               return -EINTR;
        status = ops->get_variable(entry->var.VariableName,
                                   &entry->var.VendorGuid,
                                   attributes, size, data);
-       spin_unlock_irq(&__efivars->lock);
+       up(&efivars_lock);
 
        return efi_status_to_err(status);
 }
@@ -917,7 +937,8 @@ int efivar_entry_set_get_size(struct efivar_entry *entry, u32 attributes,
         * set_variable call, and removal of the variable from the efivars
         * list (in the case of an authenticated delete).
         */
-       spin_lock_irq(&__efivars->lock);
+       if (down_interruptible(&efivars_lock))
+               return -EINTR;
 
        /*
         * Ensure that the available space hasn't shrunk below the safe level
@@ -957,7 +978,7 @@ int efivar_entry_set_get_size(struct efivar_entry *entry, u32 attributes,
        if (status == EFI_NOT_FOUND)
                efivar_entry_list_del_unlock(entry);
        else
-               spin_unlock_irq(&__efivars->lock);
+               up(&efivars_lock);
 
        if (status && status != EFI_BUFFER_TOO_SMALL)
                return efi_status_to_err(status);
@@ -965,7 +986,7 @@ int efivar_entry_set_get_size(struct efivar_entry *entry, u32 attributes,
        return 0;
 
 out:
-       spin_unlock_irq(&__efivars->lock);
+       up(&efivars_lock);
        return err;
 
 }
@@ -978,9 +999,9 @@ EXPORT_SYMBOL_GPL(efivar_entry_set_get_size);
  * efivar_entry_iter_end() is called. This function is usually used in
  * conjunction with __efivar_entry_iter() or efivar_entry_iter().
  */
-void efivar_entry_iter_begin(void)
+int efivar_entry_iter_begin(void)
 {
-       spin_lock_irq(&__efivars->lock);
+       return down_interruptible(&efivars_lock);
 }
 EXPORT_SYMBOL_GPL(efivar_entry_iter_begin);
 
@@ -991,7 +1012,7 @@ EXPORT_SYMBOL_GPL(efivar_entry_iter_begin);
  */
 void efivar_entry_iter_end(void)
 {
-       spin_unlock_irq(&__efivars->lock);
+       up(&efivars_lock);
 }
 EXPORT_SYMBOL_GPL(efivar_entry_iter_end);
 
@@ -1067,7 +1088,9 @@ int efivar_entry_iter(int (*func)(struct efivar_entry *, void *),
 {
        int err = 0;
 
-       efivar_entry_iter_begin();
+       err = efivar_entry_iter_begin();
+       if (err)
+               return err;
        err = __efivar_entry_iter(func, head, data, NULL);
        efivar_entry_iter_end();
 
@@ -1112,12 +1135,18 @@ int efivars_register(struct efivars *efivars,
                     const struct efivar_operations *ops,
                     struct kobject *kobject)
 {
-       spin_lock_init(&efivars->lock);
+       if (down_interruptible(&efivars_lock))
+               return -EINTR;
+
        efivars->ops = ops;
        efivars->kobject = kobject;
 
        __efivars = efivars;
 
+       pr_info("Registered efivars operations\n");
+
+       up(&efivars_lock);
+
        return 0;
 }
 EXPORT_SYMBOL_GPL(efivars_register);
@@ -1133,6 +1162,9 @@ int efivars_unregister(struct efivars *efivars)
 {
        int rv;
 
+       if (down_interruptible(&efivars_lock))
+               return -EINTR;
+
        if (!__efivars) {
                printk(KERN_ERR "efivars not registered\n");
                rv = -EINVAL;
@@ -1144,10 +1176,12 @@ int efivars_unregister(struct efivars *efivars)
                goto out;
        }
 
+       pr_info("Unregistered efivars operations\n");
        __efivars = NULL;
 
        rv = 0;
 out:
+       up(&efivars_lock);
        return rv;
 }
 EXPORT_SYMBOL_GPL(efivars_unregister);
index f1ab05e..c463871 100644 (file)
@@ -910,8 +910,7 @@ out_err:
        gsmi_buf_free(gsmi_dev.param_buf);
        gsmi_buf_free(gsmi_dev.data_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);
        pr_info("gsmi: failed to load: %d\n", ret);
        return ret;
index 1d73fc6..cbb50ca 100644 (file)
@@ -105,7 +105,10 @@ static int efivarfs_create(struct inode *dir, struct dentry *dentry,
 
        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);
        dget(dentry);
 out:
index 688ccc1..d7a7c53 100644 (file)
@@ -157,12 +157,14 @@ static int efivarfs_callback(efi_char16_t *name16, efi_guid_t vendor,
                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. */
        kfree(name);
 
-       efivar_entry_size(entry, &size);
-       efivar_entry_add(entry, &efivarfs_list);
-
        inode_lock(inode);
        inode->i_private = entry;
        i_size_write(inode, size + sizeof(entry->var.Attributes));
@@ -182,7 +184,10 @@ fail:
 
 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);
        return 0;
 }
index 0148a30..2d08948 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/ioport.h>
 #include <linux/pfn.h>
 #include <linux/pstore.h>
+#include <linux/range.h>
 #include <linux/reboot.h>
 #include <linux/uuid.h>
 #include <linux/screen_info.h>
@@ -37,6 +38,7 @@
 #define EFI_WRITE_PROTECTED    ( 8 | (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_ABORTED            (21 | (1UL << (BITS_PER_LONG-1)))
 #define EFI_SECURITY_VIOLATION (26 | (1UL << (BITS_PER_LONG-1)))
 
 typedef unsigned long efi_status_t;
@@ -678,6 +680,18 @@ typedef struct {
        unsigned long tables;
 } 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 {
        phys_addr_t phys_map;
        void *map;
@@ -685,6 +699,12 @@ struct efi_memory_map {
        int nr_map;
        unsigned long desc_version;
        unsigned long desc_size;
+       bool late;
+};
+
+struct efi_mem_range {
+       struct range range;
+       u64 attribute;
 };
 
 struct efi_fdt_params {
@@ -909,6 +929,16 @@ static inline efi_status_t efi_query_variable_store(u32 attributes,
 }
 #endif
 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);
 #ifdef CONFIG_EFI_ESRT
 extern void __init efi_esrt_init(void);
@@ -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 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 void efi_mem_reserve(phys_addr_t addr, u64 size);
 extern void efi_initialize_iomem_resources(struct resource *code_resource,
                struct resource *data_resource, struct resource *bss_resource);
 extern void efi_reserve_boot_services(void);
@@ -1136,12 +1167,6 @@ struct efivar_operations {
 };
 
 struct efivars {
-       /*
-        * ->lock protects two things:
-        * 1) efivarfs_list and efivars_sysfs_list
-        * 2) ->ops calls
-        */
-       spinlock_t lock;
        struct kset *kset;
        struct kobject *kobject;
        const struct efivar_operations *ops;
@@ -1282,8 +1307,8 @@ struct kobject *efivars_kobject(void);
 int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
                void *data, bool duplicates, struct list_head *head);
 
-void efivar_entry_add(struct efivar_entry *entry, struct list_head *head);
-void efivar_entry_remove(struct efivar_entry *entry);
+int efivar_entry_add(struct efivar_entry *entry, struct list_head *head);
+int efivar_entry_remove(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,
 int efivar_entry_set_safe(efi_char16_t *name, efi_guid_t vendor, u32 attributes,
                          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);
 
 int __efivar_entry_iter(int (*func)(struct efivar_entry *, void *),
@@ -1336,7 +1361,6 @@ extern int efi_capsule_update(efi_capsule_header_t *capsule,
 
 #ifdef CONFIG_EFI_RUNTIME_MAP
 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_desc_size(void);
 int efi_runtime_map_copy(void *buf, size_t bufsz);
@@ -1346,9 +1370,6 @@ static inline int efi_runtime_map_init(struct kobject *kobj)
        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)
 {
        return 0;
index f0b323a..ae8d249 100644 (file)
@@ -56,7 +56,7 @@ ucs2_utf8size(const ucs2_char_t *src)
        unsigned long i;
        unsigned long j = 0;
 
-       for (i = 0; i < ucs2_strlen(src); i++) {
+       for (i = 0; src[i]; i++) {
                u16 c = src[i];
 
                if (c >= 0x800)