[PATCH V2 1/2] kexec: arm64: create identity page table to be used in purgatory

Maxim Uvarov muvarov at gmail.com
Sun Dec 18 23:21:40 PST 2016


2016-12-19 10:13 GMT+03:00 Pratyush Anand <panand at redhat.com>:
> Purgatory sha verification is very slow when D-cache is not enabled there.
> We need to enable MMU as well to enable D-Cache.Therefore,we need to an
> identity mapped page table in purgatory.
>
> Since debugging is very difficult in purgatory therefore we prefer to do as
> much work as possible in kexec.
>
> This patch prepares page table for purgatory in advance. We support only 4K
> page table,because it will be available on most of the platform. This page
> table is passed to the purgatory as a new segment.
>
> VA bit is fixed as 48, page table level is 3 where 3rd level page table
> contains 2M block entries.
>
> Signed-off-by: Pratyush Anand <panand at redhat.com>
> ---
>  kexec/arch/arm64/kexec-arm64.c | 152 +++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 152 insertions(+)
>
> diff --git a/kexec/arch/arm64/kexec-arm64.c b/kexec/arch/arm64/kexec-arm64.c
> index 04fd3968bb52..c2c8ff1b6940 100644
> --- a/kexec/arch/arm64/kexec-arm64.c
> +++ b/kexec/arch/arm64/kexec-arm64.c
> @@ -24,6 +24,45 @@
>  #include "kexec-syscall.h"
>  #include "arch/options.h"
>
> +/*
> + * kexec creates identity page table to be used in purgatory so that
> + * dcache verification becomes faster.
> + *
> + * These are the definitions to be used by page table creation routine.
> + *
> + * Only 4K page table, 3 level, 2M block mapping, 48bit VA is supported
> + */
> +#define PGDIR_SHIFT            39
> +#define PUD_SHIFT              30
> +#define PMD_SHIFT              21
> +#define PTRS_PER_PGD           0x1FF
> +#define PTRS_PER_PUD           0x1FF
> +#define PTRS_PER_PMD           0x1FF
> +#define PMD_TYPE_TABLE         (3UL << 0)
> +#define PMD_TYPE_SECT          (1UL << 0)
> +#define PMD_SECT_AF            (1UL << 10)
> +#define PMD_ATTRINDX(t)                ((unsigned long)(t) << 2)
> +#define MT_NORMAL              4
> +#define PMD_FLAGS_NORMAL       (PMD_TYPE_SECT | PMD_SECT_AF)
> +#define MMU_FLAGS_NORMAL       (PMD_ATTRINDX(MT_NORMAL) | PMD_FLAGS_NORMAL)
> +#define SECTION_SIZE           (2 * 1024 * 1024)
> +#define PAGE_SIZE              (4 * 1024)
> +/* Since we are using 3 level of page tables, therefore minimum number of
> + * table will be 3. Most likely we will never need more than 3. Each entry
> + * in level 3 page table can map 2MB memory area. Thus a level 3 page table
> + * indexed by bit 29:21 can map a total of 1G memory area. Therefore, if
> + * any segment crosses 1G boundary, then we will need one more level 3
> + * table. Similarly, level 2 page table indexed by bit 38:30 can map a
> + * total of 512G memory area. If segment addresses are more than 512G apart
> + * then we will need two more table for each such block. We do not expect
> + * any memory segment to cross 512G boundary, however if we will ever wish
> + * to support uart debugging in purgatory then that might cross the
> + * boundary and therefore additional 2 more table space. Thus we will need
> + * maximum of 6 table space.
> + */
> +#define MAX_PGTBLE_SZ  (6 * 4096)
> +static int next_tbl_cnt = 1;
> +
>  /* Global varables the core kexec routines expect. */
>
>  unsigned char reuse_initrd;
> @@ -316,6 +355,117 @@ unsigned long arm64_locate_kernel_segment(struct kexec_info *info)
>         return hole;
>  }
>
> +static unsigned long *create_table_entry(unsigned long *pgtbl_buf,
> +               unsigned long pgtbl_mem, unsigned long *tbl,
> +               unsigned long virt, int shift,
> +               unsigned long ptrs)
> +{
> +       unsigned long index, desc, offset;
> +
> +       index = (virt >> shift) & ptrs;
> +       /* check if we have allocated a table already for this index */
> +       if (tbl[index]) {
> +               /*
> +                * table index will have entry as per purgatory page table
> +                * memory. Find out corresponding buffer address of table.
> +                */
> +               desc = tbl[index] & ~3UL;
> +               offset = desc - pgtbl_mem;
> +               return &pgtbl_buf[offset >> 3];
> +       }
> +
> +       /*
> +        * Always write page table content as per page table memory allocated
> +        * for purgaory area, but return corresponding buffer area alloced
> +        * in kexec
> +        */
> +       if (next_tbl_cnt > 5)
> +               die("%s: No more memory for page table\n", __func__);
> +
> +       tbl[index] = (pgtbl_mem + PAGE_SIZE * next_tbl_cnt) | PMD_TYPE_TABLE;
> +
> +       return &pgtbl_buf[(next_tbl_cnt++ * PAGE_SIZE) >> 3];
> +}
> +
> +static void craete_block_entry(unsigned long *tbl, unsigned long flags,

typo in name

> +               unsigned long phys, unsigned long virt)
> +{
> +       unsigned long index;
> +       unsigned long desc;
> +
> +       index = (virt >> PMD_SHIFT) & PTRS_PER_PMD;
> +       desc = (phys >> PMD_SHIFT) << PMD_SHIFT;
> +       desc |= flags;
> +       tbl[index] = desc;
> +}
> +
> +static void create_identity_entry(unsigned long *pgtbl_buf,
> +               unsigned long pgtbl_mem, unsigned long virt,
> +               unsigned long flags)
> +{
> +       unsigned long *tbl = pgtbl_buf;
> +
> +       tbl = create_table_entry(pgtbl_buf, pgtbl_mem, tbl, virt,
> +                       PGDIR_SHIFT, PTRS_PER_PGD);
> +       tbl = create_table_entry(pgtbl_buf, pgtbl_mem, tbl, virt,
> +                       PUD_SHIFT, PTRS_PER_PUD);
> +       craete_block_entry(tbl, flags, virt, virt);
> +}
> +
> +/**
> + * arm64_create_pgtbl_segment - Create page table segments to be used by
> + * purgatory. Page table will have entries to access memory area of all
> + * those segments which becomes part of sha verification in purgatory.
> + * Additionaly, we also create page table for purgatory segment as well.
> + */
> +
> +static int arm64_create_pgtbl_segment(struct kexec_info *info,
> +               unsigned long hole_min, unsigned long hole_max)
> +{
> +       unsigned long *pgtbl_buf;
> +       int i;
> +       unsigned long mstart, mend, pgtbl_mem;
> +       unsigned long purgatory_base, purgatory_len;
> +
> +       pgtbl_buf = xmalloc(MAX_PGTBLE_SZ);
> +       memset(pgtbl_buf, 0, MAX_PGTBLE_SZ);
> +       pgtbl_mem = add_buffer_phys_virt(info, pgtbl_buf, MAX_PGTBLE_SZ,
> +                       MAX_PGTBLE_SZ, PAGE_SIZE, hole_min, hole_max, 1, 0);
> +       for (i = 0; i < info->nr_segments; i++) {
> +               if (info->segment[i].mem == (void *)info->rhdr.rel_addr) {
> +                       purgatory_base = (unsigned long)info->segment[i].mem;
> +                       purgatory_len = info->segment[i].memsz;
> +               }
> +               mstart = (unsigned long)info->segment[i].mem;
> +               mend = mstart + info->segment[i].memsz;
> +               mstart &= ~(SECTION_SIZE - 1);
> +               while (mstart < mend) {
> +                       create_identity_entry(pgtbl_buf, pgtbl_mem,
> +                                       mstart, MMU_FLAGS_NORMAL);
> +                       mstart += SECTION_SIZE;
> +               }
> +       }
> +
> +       /* we will need pgtble_base in purgatory for enabling d-cache */
> +       elf_rel_set_symbol(&info->rhdr, "pgtble_base", &pgtbl_mem,
> +               sizeof(pgtbl_mem));
> +       /*
> +        * We need to disable d-cache before we exit from purgatory.
> +        * Since, only dcache flush by VAs is recomeneded, therefore we
> +        * will also need memory location of all those area which will be
> +        * accessed in purgatory with enabled d-cache. sha256_regions
> +        * already have start and length for all the segments except
> +        * purgatory. Therefore, we will need to pass start and length of
> +        * purgatory additionaly.
> +        */
> +       elf_rel_set_symbol(&info->rhdr, "purgatory_base", &purgatory_base,
> +               sizeof(purgatory_base));
> +       elf_rel_set_symbol(&info->rhdr, "purgatory_len", &purgatory_len,
> +               sizeof(purgatory_len));
> +
> +       return 0;
> +}
> +
>  /**
>   * arm64_load_other_segments - Prepare the dtb, initrd and purgatory segments.
>   */
> @@ -423,6 +573,8 @@ int arm64_load_other_segments(struct kexec_info *info,
>         elf_rel_set_symbol(&info->rhdr, "arm64_dtb_addr", &dtb_base,
>                 sizeof(dtb_base));
>
> +       arm64_create_pgtbl_segment(info, hole_min, hole_max);
> +
>         return 0;
>  }
>
> --
> 2.7.4
>
>
> _______________________________________________
> kexec mailing list
> kexec at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/kexec



-- 
Best regards,
Maxim Uvarov



More information about the linux-arm-kernel mailing list