[PATCH 2/5] arm64: use fixmap region for permanent FDT mapping

Rob Herring robh at kernel.org
Tue Mar 10 14:37:53 PDT 2015


On Tue, Mar 3, 2015 at 5:03 AM, Ard Biesheuvel
<ard.biesheuvel at linaro.org> wrote:
> Currently, the FDT blob needs to be in the same naturally aligned
> 512 MB region as the kernel, so that it can be mapped into the
> kernel virtual memory space very early on using a minimal set of
> statically allocated translation tables.
>
> Now that we have early fixmap support, we can relax this restriction,
> by moving the permanent FDT mapping to the fixmap region instead.
> This way, the FDT blob may be anywhere in memory.
>
> This also moves the vetting of the FDT to setup.c, since the early
> init code in head.S does not handle mapping of the FDT anymore.
> At the same time, fix up some comments in head.S that have gone stale.
>
> Signed-off-by: Ard Biesheuvel <ard.biesheuvel at linaro.org>
> ---
>  Documentation/arm64/booting.txt |  7 ++---
>  arch/arm64/include/asm/fixmap.h |  9 ++++++
>  arch/arm64/kernel/Makefile      |  1 +
>  arch/arm64/kernel/head.S        | 38 +------------------------
>  arch/arm64/kernel/setup.c       | 62 +++++++++++++++++++++++++++++++++++++----
>  5 files changed, 70 insertions(+), 47 deletions(-)
>
> diff --git a/Documentation/arm64/booting.txt b/Documentation/arm64/booting.txt
> index f3c05b5f9f08..bdc35fc97ac8 100644
> --- a/Documentation/arm64/booting.txt
> +++ b/Documentation/arm64/booting.txt
> @@ -45,10 +45,9 @@ sees fit.)
>
>  Requirement: MANDATORY
>
> -The device tree blob (dtb) must be placed on an 8-byte boundary within
> -the first 512 megabytes from the start of the kernel image and must not
> -cross a 2-megabyte boundary. This is to allow the kernel to map the
> -blob using a single section mapping in the initial page tables.
> +The device tree blob (dtb) must be placed on an 8-byte boundary and must
> +not cross a 2-megabyte boundary. This is to allow the kernel to map the
> +blob using a single section mapping in the fixmap region.
>
>
>  3. Decompress the kernel image
> diff --git a/arch/arm64/include/asm/fixmap.h b/arch/arm64/include/asm/fixmap.h
> index defa0ff98250..4ad240a60898 100644
> --- a/arch/arm64/include/asm/fixmap.h
> +++ b/arch/arm64/include/asm/fixmap.h
> @@ -32,6 +32,15 @@
>   */
>  enum fixed_addresses {
>         FIX_HOLE,
> +
> +       /*
> +        * Reserve 2 MB of virtual space for the FDT at the top of the fixmap
> +        * region. Keep this at the top so it remains 2 MB aligned.
> +        */

We should not fix a location restriction by creating a size
restriction. You could embed firmware images within a DTB (which I
think PPC does).

> +#define FIX_FDT_SIZE           SZ_2M
> +       FIX_FDT_END,
> +       FIX_FDT = FIX_FDT_END + FIX_FDT_SIZE / PAGE_SIZE - 1,
> +
>         FIX_EARLYCON_MEM_BASE,
>         __end_of_permanent_fixed_addresses,
>
> diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
> index 5ee07eee80c2..e60885766936 100644
> --- a/arch/arm64/kernel/Makefile
> +++ b/arch/arm64/kernel/Makefile
> @@ -6,6 +6,7 @@ CPPFLAGS_vmlinux.lds    := -DTEXT_OFFSET=$(TEXT_OFFSET)
>  AFLAGS_head.o          := -DTEXT_OFFSET=$(TEXT_OFFSET)
>  CFLAGS_efi-stub.o      := -DTEXT_OFFSET=$(TEXT_OFFSET)
>  CFLAGS_armv8_deprecated.o := -I$(src)
> +CFLAGS_setup.o         := -I$(srctree)/scripts/dtc/libfdt/
>
>  CFLAGS_REMOVE_ftrace.o = -pg
>  CFLAGS_REMOVE_insn.o = -pg
> diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S
> index 8ce88e08c030..66675d27fea3 100644
> --- a/arch/arm64/kernel/head.S
> +++ b/arch/arm64/kernel/head.S
> @@ -255,7 +255,6 @@ ENTRY(stext)
>         cbnz    x23, 1f                         // invalid processor (x23=0)?
>         b       __error_p
>  1:
> -       bl      __vet_fdt
>         bl      __create_page_tables            // x25=TTBR0, x26=TTBR1
>         /*
>          * The following calls CPU specific code in a position independent
> @@ -274,24 +273,6 @@ ENTRY(stext)
>  ENDPROC(stext)
>
>  /*
> - * Determine validity of the x21 FDT pointer.
> - * The dtb must be 8-byte aligned and live in the first 512M of memory.
> - */
> -__vet_fdt:
> -       tst     x21, #0x7
> -       b.ne    1f
> -       cmp     x21, x24
> -       b.lt    1f
> -       mov     x0, #(1 << 29)
> -       add     x0, x0, x24
> -       cmp     x21, x0
> -       b.ge    1f
> -       ret
> -1:
> -       mov     x21, #0
> -       ret
> -ENDPROC(__vet_fdt)
> -/*
>   * Macro to create a table entry to the next page.
>   *
>   *     tbl:    page table address
> @@ -352,8 +333,7 @@ ENDPROC(__vet_fdt)
>   * required to get the kernel running. The following sections are required:
>   *   - identity mapping to enable the MMU (low address, TTBR0)
>   *   - first few MB of the kernel linear mapping to jump to once the MMU has
> - *     been enabled, including the FDT blob (TTBR1)
> - *   - pgd entry for fixed mappings (TTBR1)
> + *     been enabled
>   */
>  __create_page_tables:
>         pgtbl   x25, x26, x28                   // idmap_pg_dir and swapper_pg_dir addresses
> @@ -404,22 +384,6 @@ __create_page_tables:
>         create_block_map x0, x7, x3, x5, x6
>
>         /*
> -        * Map the FDT blob (maximum 2MB; must be within 512MB of
> -        * PHYS_OFFSET).
> -        */
> -       mov     x3, x21                         // FDT phys address
> -       and     x3, x3, #~((1 << 21) - 1)       // 2MB aligned
> -       mov     x6, #PAGE_OFFSET
> -       sub     x5, x3, x24                     // subtract PHYS_OFFSET
> -       tst     x5, #~((1 << 29) - 1)           // within 512MB?
> -       csel    x21, xzr, x21, ne               // zero the FDT pointer
> -       b.ne    1f
> -       add     x5, x5, x6                      // __va(FDT blob)
> -       add     x6, x5, #1 << 21                // 2MB for the FDT blob
> -       sub     x6, x6, #1                      // inclusive range
> -       create_block_map x0, x7, x3, x5, x6
> -1:
> -       /*
>          * Since the page tables have been populated with non-cacheable
>          * accesses (MMU disabled), invalidate the idmap and swapper page
>          * tables again to remove any speculatively loaded cache lines.
> diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c
> index e8420f635bd4..5c675a09116e 100644
> --- a/arch/arm64/kernel/setup.c
> +++ b/arch/arm64/kernel/setup.c
> @@ -45,6 +45,7 @@
>  #include <linux/of_platform.h>
>  #include <linux/efi.h>
>  #include <linux/personality.h>
> +#include <linux/libfdt.h>
>
>  #include <asm/fixmap.h>
>  #include <asm/cpu.h>
> @@ -307,14 +308,63 @@ static void __init setup_processor(void)
>  #endif
>  }
>
> +static unsigned long const dt_virt_base = __fix_to_virt(FIX_FDT);
> +static phys_addr_t dt_phys_base;
> +
> +phys_addr_t __init fdt_virt_to_phys(void *virt)
> +{
> +       return (phys_addr_t)virt - dt_virt_base + dt_phys_base;
> +}
> +
> +static void *__init fixmap_remap_fdt(phys_addr_t dt_phys)
> +{
> +       dt_phys_base = dt_phys & ~(FIX_FDT_SIZE - 1);
> +
> +       /*
> +        * Make sure that the FDT region can be mapped without the need to
> +        * allocate additional translation table pages, so that it is safe
> +        * to call create_pgd_mapping() this early.
> +        * On 4k pages, we'll use a section mapping for the 2 MB region so we
> +        * only have to be in the same PUD as the rest of the fixmap.
> +        * On 64k pages, we need to be in the same PMD as well, as the region
> +        * will be mapped using PTEs.
> +        */
> +       BUILD_BUG_ON(dt_virt_base & (FIX_FDT_SIZE - 1));
> +
> +       if (IS_ENABLED(CONFIG_ARM64_64K_PAGES))
> +               BUILD_BUG_ON(dt_virt_base >> PMD_SHIFT !=
> +                            __fix_to_virt(FIX_BTMAP_BEGIN) >> PMD_SHIFT);
> +       else
> +               BUILD_BUG_ON(dt_virt_base >> PUD_SHIFT !=
> +                            __fix_to_virt(FIX_BTMAP_BEGIN) >> PUD_SHIFT);
> +
> +       create_pgd_mapping(&init_mm, dt_phys_base, dt_virt_base, FIX_FDT_SIZE,
> +                          PAGE_KERNEL);
> +
> +       return (void *)(dt_virt_base + dt_phys - dt_phys_base);
> +}
> +
>  static void __init setup_machine_fdt(phys_addr_t dt_phys)
>  {
> -       if (!dt_phys || !early_init_dt_scan(phys_to_virt(dt_phys))) {
> +       void *dt_virt = NULL;
> +
> +       if (dt_phys && (dt_phys & 7) == 0)
> +               dt_virt = fixmap_remap_fdt(dt_phys);
> +
> +       /*
> +        * Before passing the dt_virt pointer to early_init_dt_scan(), we have
> +        * to ensure that the FDT size as reported in the FDT itself does not
> +        * exceed the 2 MB window we just mapped for it.
> +        */
> +       if (!dt_virt ||
> +           fdt_check_header(dt_virt) != 0 ||
> +           (dt_phys & (SZ_2M - 1)) + fdt_totalsize(dt_virt) > SZ_2M ||
> +           !early_init_dt_scan(dt_virt)) {
>                 early_print("\n"
>                         "Error: invalid device tree blob at physical address 0x%p (virtual address 0x%p)\n"
> -                       "The dtb must be 8-byte aligned and passed in the first 512MB of memory\n"
> +                       "The dtb must be 8-byte aligned and must not cross a 2 MB alignment boundary\n"
>                         "\nPlease check your bootloader.\n",
> -                       dt_phys, phys_to_virt(dt_phys));
> +                       dt_phys, dt_virt);
>
>                 while (true)
>                         cpu_relax();
> @@ -357,6 +407,9 @@ void __init setup_arch(char **cmdline_p)
>  {
>         setup_processor();
>
> +       early_fixmap_init();
> +       early_ioremap_init();
> +
>         setup_machine_fdt(__fdt_pointer);
>
>         init_mm.start_code = (unsigned long) _text;
> @@ -366,9 +419,6 @@ void __init setup_arch(char **cmdline_p)
>
>         *cmdline_p = boot_command_line;
>
> -       early_fixmap_init();
> -       early_ioremap_init();
> -
>         parse_early_param();
>
>         /*
> --
> 1.8.3.2
>



More information about the linux-arm-kernel mailing list