Re: [PATCH] x86/efi: Return error status if mapping EFI regions fail

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri, 28 Dec 2018 at 04:18, Sai Praneeth Prakhya
<sai.praneeth.prakhya@xxxxxxxxx> wrote:
>
> efi_map_region() creates VA mappings for an given EFI region using any one
> of the two helper functions (namely __map_region() and old_map_region()).
> These helper functions *could* fail while creating mappings and presently
> their return value is not checked. Not checking for the return value of
> these functions might create issues because after these functions return
> "md->virt_addr" is set to the requested virtual address (so it's assumed
> that these functions always succeed which is not quite true). This
> assumption leads to "md->virt_addr" having invalid mapping should any of
> __map_region() or old_map_region() fail.
>
> Hence, check for the return value of these functions and if indeed they
> fail, turn off EFI Runtime Services forever because kernel cannot
> prioritize among EFI regions.
>
> This also fixes the comment "FIXME: add error handling" in
> kexec_enter_virtual_mode().
>
> Signed-off-by: Sai Praneeth Prakhya <sai.praneeth.prakhya@xxxxxxxxx>
> Cc: Ard Biesheuvel <ard.biesheuvel@xxxxxxxxxx>
> Cc: Borislav Petkov <bp@xxxxxxxxx>
> Cc: Ingo Molnar <mingo@xxxxxxxxxx>

Added to efi/next

> ---
>  arch/x86/include/asm/efi.h     |  6 +++---
>  arch/x86/platform/efi/efi.c    | 21 +++++++++++++-----
>  arch/x86/platform/efi/efi_32.c |  6 +++---
>  arch/x86/platform/efi/efi_64.c | 39 ++++++++++++++++++++++------------
>  4 files changed, 48 insertions(+), 24 deletions(-)
>
> diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h
> index d1e64ac80b9c..44b187ec32ea 100644
> --- a/arch/x86/include/asm/efi.h
> +++ b/arch/x86/include/asm/efi.h
> @@ -127,12 +127,12 @@ 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_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);
> +extern int __init efi_map_region(efi_memory_desc_t *md);
> +extern int __init efi_map_region_fixed(efi_memory_desc_t *md);
>  extern void efi_sync_low_kernel_mappings(void);
>  extern int __init efi_alloc_page_tables(void);
>  extern int __init efi_setup_page_tables(unsigned long pa_memmap, unsigned num_pages);
> -extern void __init old_map_region(efi_memory_desc_t *md);
> +extern int __init old_map_region(efi_memory_desc_t *md);
>  extern void __init runtime_code_page_mkexec(void);
>  extern void __init efi_runtime_update_mappings(void);
>  extern void __init efi_dump_pagetable(void);
> diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c
> index e1cb01a22fa8..3d43ec58775b 100644
> --- a/arch/x86/platform/efi/efi.c
> +++ b/arch/x86/platform/efi/efi.c
> @@ -581,7 +581,7 @@ void __init efi_memory_uc(u64 addr, unsigned long size)
>         set_memory_uc(addr, npages);
>  }
>
> -void __init old_map_region(efi_memory_desc_t *md)
> +int __init old_map_region(efi_memory_desc_t *md)
>  {
>         u64 start_pfn, end_pfn, end;
>         unsigned long size;
> @@ -601,10 +601,14 @@ void __init old_map_region(efi_memory_desc_t *md)
>                 va = efi_ioremap(md->phys_addr, size,
>                                  md->type, md->attribute);
>
> -       md->virt_addr = (u64) (unsigned long) va;
> -       if (!va)
> +       if (!va) {
>                 pr_err("ioremap of 0x%llX failed!\n",
>                        (unsigned long long)md->phys_addr);
> +               return -ENOMEM;
> +       }
> +
> +       md->virt_addr = (u64)(unsigned long)va;
> +       return 0;
>  }
>
>  /* Merge contiguous regions of the same type and attribute */
> @@ -797,7 +801,9 @@ static void * __init efi_map_regions(int *count, int *pg_shift)
>                 if (!should_map_region(md))
>                         continue;
>
> -               efi_map_region(md);
> +               if (efi_map_region(md))
> +                       return NULL;
> +
>                 get_systab_virt_addr(md);
>
>                 if (left < desc_size) {
> @@ -849,7 +855,12 @@ static void __init kexec_enter_virtual_mode(void)
>         * fixed addr which was used in first kernel of a kexec boot.
>         */
>         for_each_efi_memory_desc(md) {
> -               efi_map_region_fixed(md); /* FIXME: add error handling */
> +               if (efi_map_region_fixed(md)) {
> +                       pr_err("Error mapping EFI regions, EFI runtime non-functional!\n");
> +                       clear_bit(EFI_RUNTIME_SERVICES, &efi.flags);
> +                       return;
> +               }
> +
>                 get_systab_virt_addr(md);
>         }
>
> diff --git a/arch/x86/platform/efi/efi_32.c b/arch/x86/platform/efi/efi_32.c
> index 9959657127f4..4d369118391a 100644
> --- a/arch/x86/platform/efi/efi_32.c
> +++ b/arch/x86/platform/efi/efi_32.c
> @@ -58,12 +58,12 @@ int __init efi_setup_page_tables(unsigned long pa_memmap, unsigned num_pages)
>         return 0;
>  }
>
> -void __init efi_map_region(efi_memory_desc_t *md)
> +int __init efi_map_region(efi_memory_desc_t *md)
>  {
> -       old_map_region(md);
> +       return old_map_region(md);
>  }
>
> -void __init efi_map_region_fixed(efi_memory_desc_t *md) {}
> +int __init efi_map_region_fixed(efi_memory_desc_t *md) { return 0; }
>  void __init parse_efi_setup(u64 phys_addr, u32 data_len) {}
>
>  pgd_t * __init efi_call_phys_prolog(void)
> diff --git a/arch/x86/platform/efi/efi_64.c b/arch/x86/platform/efi/efi_64.c
> index cf0347f61b21..ba83e2e2664b 100644
> --- a/arch/x86/platform/efi/efi_64.c
> +++ b/arch/x86/platform/efi/efi_64.c
> @@ -408,7 +408,7 @@ int __init efi_setup_page_tables(unsigned long pa_memmap, unsigned num_pages)
>         return 0;
>  }
>
> -static void __init __map_region(efi_memory_desc_t *md, u64 va)
> +static int __init __map_region(efi_memory_desc_t *md, u64 va)
>  {
>         unsigned long flags = _PAGE_RW;
>         unsigned long pfn;
> @@ -421,12 +421,16 @@ static void __init __map_region(efi_memory_desc_t *md, u64 va)
>                 flags |= _PAGE_ENC;
>
>         pfn = md->phys_addr >> PAGE_SHIFT;
> -       if (kernel_map_pages_in_pgd(pgd, pfn, va, md->num_pages, flags))
> -               pr_warn("Error mapping PA 0x%llx -> VA 0x%llx!\n",
> -                          md->phys_addr, va);
> +       if (kernel_map_pages_in_pgd(pgd, pfn, va, md->num_pages, flags)) {
> +               pr_err("Error mapping PA 0x%llx -> VA 0x%llx!\n",
> +                      md->phys_addr, va);
> +               return -ENOMEM;
> +       }
> +
> +       return 0;
>  }
>
> -void __init efi_map_region(efi_memory_desc_t *md)
> +int __init efi_map_region(efi_memory_desc_t *md)
>  {
>         unsigned long size = md->num_pages << PAGE_SHIFT;
>         u64 pa = md->phys_addr;
> @@ -439,7 +443,8 @@ void __init efi_map_region(efi_memory_desc_t *md)
>          * firmware which doesn't update all internal pointers after switching
>          * to virtual mode and would otherwise crap on us.
>          */
> -       __map_region(md, md->phys_addr);
> +       if (__map_region(md, md->phys_addr))
> +               return -ENOMEM;
>
>         /*
>          * Enforce the 1:1 mapping as the default virtual address when
> @@ -448,7 +453,7 @@ void __init efi_map_region(efi_memory_desc_t *md)
>          */
>         if (!efi_is_native () && IS_ENABLED(CONFIG_EFI_MIXED)) {
>                 md->virt_addr = md->phys_addr;
> -               return;
> +               return 0;
>         }
>
>         efi_va -= size;
> @@ -468,13 +473,16 @@ void __init efi_map_region(efi_memory_desc_t *md)
>         }
>
>         if (efi_va < EFI_VA_END) {
> -               pr_warn(FW_WARN "VA address range overflow!\n");
> -               return;
> +               pr_err(FW_WARN "VA address range overflow!\n");
> +               return -ENOMEM;
>         }
>
>         /* Do the VA map */
> -       __map_region(md, efi_va);
> +       if (__map_region(md, efi_va))
> +               return -ENOMEM;
> +
>         md->virt_addr = efi_va;
> +       return 0;
>  }
>
>  /*
> @@ -482,10 +490,15 @@ void __init efi_map_region(efi_memory_desc_t *md)
>   * md->virt_addr is the original virtual address which had been mapped in kexec
>   * 1st kernel.
>   */
> -void __init efi_map_region_fixed(efi_memory_desc_t *md)
> +int __init efi_map_region_fixed(efi_memory_desc_t *md)
>  {
> -       __map_region(md, md->phys_addr);
> -       __map_region(md, md->virt_addr);
> +       if (__map_region(md, md->phys_addr))
> +               return -ENOMEM;
> +
> +       if (__map_region(md, md->virt_addr))
> +               return -ENOMEM;
> +
> +       return 0;
>  }
>
>  void __iomem *__init efi_ioremap(unsigned long phys_addr, unsigned long size,
> --
> 2.19.1
>



[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux