[PATCH] arm64: support ACPI tables outside of kernel RAM

Ard Biesheuvel ard.biesheuvel at linaro.org
Thu May 14 07:50:54 PDT 2015


On 14 May 2015 at 16:22, Mark Salter <msalter at redhat.com> wrote:
> There is no guarantee that ACPI tables will be located in RAM linearly
> mapped by the kernel. This could be because UEFI placed them below the
> kernel image or because mem= places them beyond the reach of the linear
> kernel mapping. Even though these tables are outside the linear mapped
> RAM, they still need to be accessed as normal memory in order to support
> unaligned accesses from ACPI code. In this case, the page_is_ram() test
> in acpi_os_ioremap() is not sufficient. Additionally, if the table spans
> multiple pages, it may fall partially within the linear map and partially
> without. If the table overlaps the end of the linear map, the test for
> whether or not to use the existing mapping in ioremap_cache() could lead
> to a panic when ACPI code tries to access the part beyond the end of the
> linear map. This patch attempts to address these problems.
>

I would strongly prefer memblock_remove()'ing all UEFI reserved
regions entirely, and keeping track of which areas are backed my RAM
using a table rather than string matching on the iomem resource table.
When I looked into this a while ago [1], I ended up with a separate
physmem memblock table (borrowed from another arch) that tracks the
regions which are memory while removing all the EFI reserved region
from the 'memory' memblock table. That way, page_is_ram() could be
reimplemented as memblock_is_physmem(), and ioremap_cache() would
always do the right thing automagically.

I kind of held off with this series until the ACPI stuff had landed,
which is obviously the case now. Would you mind having a look at these
patches and sharing your opinion?

[1] http://thread.gmane.org/gmane.linux.kernel.efi/5133

-- 
Ard.


> Cc: Hanjun Guo <hanjun.guo at linaro.org>
> Cc: Ard Biesheuvel <ard.biesheuvel at linaro.org>
> Cc: Matt Fleming <matt.fleming at intel.com>
> Signed-off-by: Mark Salter <msalter at redhat.com>
> ---
>  arch/arm64/include/asm/acpi.h |  6 ++-
>  arch/arm64/include/asm/efi.h  |  2 +
>  arch/arm64/kernel/acpi.c      | 13 ++++++
>  arch/arm64/kernel/efi.c       | 95 +++++++++++++++++++++++++++++++++++++++++++
>  arch/arm64/kernel/setup.c     |  1 +
>  arch/arm64/mm/ioremap.c       |  3 +-
>  6 files changed, 118 insertions(+), 2 deletions(-)
>
> diff --git a/arch/arm64/include/asm/acpi.h b/arch/arm64/include/asm/acpi.h
> index 59c05d8..0849533 100644
> --- a/arch/arm64/include/asm/acpi.h
> +++ b/arch/arm64/include/asm/acpi.h
> @@ -20,11 +20,15 @@
>
>  /* Basic configuration for ACPI */
>  #ifdef CONFIG_ACPI
> +extern int page_is_acpi_ram(unsigned long pfn);
> +
>  /* ACPI table mapping after acpi_gbl_permanent_mmap is set */
>  static inline void __iomem *acpi_os_ioremap(acpi_physical_address phys,
>                                             acpi_size size)
>  {
> -       if (!page_is_ram(phys >> PAGE_SHIFT))
> +       unsigned long pfn = phys >> PAGE_SHIFT;
> +
> +       if (!page_is_ram(pfn) && !page_is_acpi_ram(pfn))
>                 return ioremap(phys, size);
>
>         return ioremap_cache(phys, size);
> diff --git a/arch/arm64/include/asm/efi.h b/arch/arm64/include/asm/efi.h
> index ef57220..3d5c12a 100644
> --- a/arch/arm64/include/asm/efi.h
> +++ b/arch/arm64/include/asm/efi.h
> @@ -6,8 +6,10 @@
>
>  #ifdef CONFIG_EFI
>  extern void efi_init(void);
> +extern void efi_request_acpi_resources(void);
>  #else
>  #define efi_init()
> +#define efi_request_acpi_resources()
>  #endif
>
>  #define efi_call_virt(f, ...)                                          \
> diff --git a/arch/arm64/kernel/acpi.c b/arch/arm64/kernel/acpi.c
> index 8b83955..2c85ca0 100644
> --- a/arch/arm64/kernel/acpi.c
> +++ b/arch/arm64/kernel/acpi.c
> @@ -343,3 +343,16 @@ void __init acpi_gic_init(void)
>
>         early_acpi_os_unmap_memory((char *)table, tbl_size);
>  }
> +
> +static int __is_acpi_ram(u64 start, u64 end, void *arg)
> +{
> +       return 1;
> +}
> +
> +int page_is_acpi_ram(unsigned long pfn)
> +{
> +       u64 addr = (u64) pfn << PAGE_SHIFT;
> +
> +       return walk_iomem_res("ACPI RAM", IORESOURCE_MEM | IORESOURCE_BUSY,
> +                             addr, addr, NULL, __is_acpi_ram) == 1;
> +}
> diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c
> index ab21e0d..2d914d7 100644
> --- a/arch/arm64/kernel/efi.c
> +++ b/arch/arm64/kernel/efi.c
> @@ -204,6 +204,101 @@ static __init void reserve_regions(void)
>         set_bit(EFI_MEMMAP, &efi.flags);
>  }
>
> +#ifdef CONFIG_ACPI
> +static void __init __insert_resource(struct resource *res)
> +{
> +       struct resource *r, *conflict;
> +
> +       r = alloc_bootmem_low(sizeof(*r));
> +       *r = *res;
> +
> +       /*
> +        * When inserting a resource, there will be a conflict
> +        * only if the resource being inserted partially overlaps
> +        * an existing resource. If the resource being inserted
> +        * is entirely within an existing resource, it becomes
> +        * a child of that resource with no conflict. So if we
> +        * do get a conflict, split the one resource into two
> +        * resources: one inside the conflict and one outside.
> +        */
> +       conflict = insert_resource_conflict(&iomem_resource, r);
> +       if (conflict) {
> +               struct resource *tmp;
> +
> +               tmp = alloc_bootmem_low(sizeof(*tmp));
> +               *tmp = *r;
> +
> +               if (r->start >= conflict->start) {
> +                       r->start = conflict->end + 1;
> +                       tmp->end = conflict->end;
> +               } else {
> +                       r->end = conflict->start - 1;
> +                       tmp->start = conflict->start;
> +               }
> +               insert_resource(&iomem_resource, tmp);
> +               insert_resource(&iomem_resource, r);
> +       }
> +}
> +
> +/*
> + * Create /proc/iomem resources for any ACPI regions in RAM.
> + */
> +void __init efi_request_acpi_resources(void)
> +{
> +       struct resource res;
> +       efi_memory_desc_t *md;
> +       u64 start, end, npages;
> +       unsigned long mapsize = memmap.map_end - memmap.map;
> +       void *map;
> +
> +       map = early_memremap((resource_size_t)memmap.phys_map, mapsize);
> +       if (map == NULL)
> +               return;
> +       memmap.map = map;
> +       memmap.map_end = map + mapsize;
> +
> +       memset(&res, 0, sizeof(res));
> +       res.name  = "ACPI RAM";
> +       res.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
> +
> +       for_each_efi_memory_desc(&memmap, md) {
> +               if (!is_normal_ram(md) ||
> +                   !(md->type == EFI_ACPI_RECLAIM_MEMORY ||
> +                     md->type == EFI_ACPI_MEMORY_NVS))
> +                       continue;
> +
> +               start = md->phys_addr;
> +               npages = md->num_pages;
> +               memrange_efi_to_native(&start, &npages);
> +               end = start + (npages << PAGE_SHIFT) - 1;
> +
> +               if (res.end == 0) {
> +                       res.start = start;
> +                       res.end = end;
> +                       continue;
> +               }
> +
> +               if (start >= res.start && (start - 1) <= res.end) {
> +                       /* overlaps (or adjacent to) end of old region */
> +                       if (end > res.end)
> +                               res.end = end;
> +               } else if (end >= (res.start - 1) && end <= res.end) {
> +                       /* overlaps (or adjacent to) start of old region */
> +                       if (start  < res.start)
> +                               res.start = start;
> +               } else {
> +                       __insert_resource(&res);
> +                       res.start = start;
> +                       res.end = end;
> +               }
> +       }
> +       if (res.end)
> +               __insert_resource(&res);
> +
> +       early_memunmap(memmap.map, mapsize);
> +}
> +#endif
> +
>  void __init efi_init(void)
>  {
>         struct efi_fdt_params params;
> diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c
> index 7475313..a438a0c 100644
> --- a/arch/arm64/kernel/setup.c
> +++ b/arch/arm64/kernel/setup.c
> @@ -413,6 +413,7 @@ void __init setup_arch(char **cmdline_p)
>                 of_smp_init_cpus();
>  #endif
>         } else {
> +               efi_request_acpi_resources();
>                 psci_acpi_init();
>                 acpi_init_cpus();
>         }
> diff --git a/arch/arm64/mm/ioremap.c b/arch/arm64/mm/ioremap.c
> index 01e88c8..d62e701 100644
> --- a/arch/arm64/mm/ioremap.c
> +++ b/arch/arm64/mm/ioremap.c
> @@ -96,7 +96,8 @@ EXPORT_SYMBOL(__iounmap);
>  void __iomem *ioremap_cache(phys_addr_t phys_addr, size_t size)
>  {
>         /* For normal memory we already have a cacheable mapping. */
> -       if (pfn_valid(__phys_to_pfn(phys_addr)))
> +       if (pfn_valid(__phys_to_pfn(phys_addr)) &&
> +           pfn_valid(__phys_to_pfn(phys_addr + size - 1)))
>                 return (void __iomem *)__phys_to_virt(phys_addr);
>
>         return __ioremap_caller(phys_addr, size, __pgprot(PROT_NORMAL),
> --
> 1.9.3
>



More information about the linux-arm-kernel mailing list