Re: [PATCH 3/3] arm64: add EFI runtime services

Mark Salter
2013-12-06T14:35:24Z

On Thu, 2013-12-05 at 15:25 +0000, Catalin Marinas wrote:
> On Fri, Nov 29, 2013 at 10:05:12PM +0000, Mark Salter wrote:
> > +
> > +#define efi_remap(cookie, size) __ioremap((cookie), (size), PAGE_KERNEL_EXEC)
> > +#define efi_ioremap(cookie, size) __ioremap((cookie), (size), \
> > + __pgprot(PROT_DEVICE_nGnRE))
> > +#define efi_unmap(cookie) __iounmap((cookie))
> > +#define efi_iounmap(cookie) __iounmap((cookie))
>
> I prefer to use the ioremap_*() functions rather than the lower-level
> __ioremap(). The ioremap_cache() should give us executable memory.

okay

>
> Looking at the code, I think we have a bug with ioremap_cache() using
> MT_NORMAL since it doesn't have the shareability bit (Device memory does
> not require this on AArch64). PROT_DEFAULT should change to
> pgprot_default | PTE_DIRTY.
>

ah, yes.

> > + if (depth != 1 ||
> > + (strcmp(uname, "chosen") != 0 && strcmp(uname, "chosen@0") != 0))
> > + return 0;
> > +
> > + pr_info("Getting EFI parameters from FDT.\n");
> > +
> > + prop = of_get_flat_dt_prop(node, "linux,uefi-system-table", &len);
> > + if (!prop) {
> > + pr_err("No EFI system table in FDT\n");
> > + return 0;
> > + }
> > + efi_system_table = of_read_ulong(prop, len/4);
> > +
> > + prop = of_get_flat_dt_prop(node, "linux,uefi-mmap-start", &len);
> > + if (!prop) {
> > + pr_err("No EFI memmap in FDT\n");
> > + return 0;
> > + }
> > + memmap.phys_map = (void *)of_read_ulong(prop, len/4);
> > +
> > + prop = of_get_flat_dt_prop(node, "linux,uefi-mmap-size", &len);
> > + if (!prop) {
> > + pr_err("No EFI memmap size in FDT\n");
> > + return 0;
> > + }
> > + size = of_read_ulong(prop, len/4);
> > + memmap.map_end = memmap.phys_map + size;
> > +
> > + /* reserve memmap */
> > + memblock_reserve((u64)memmap.phys_map, size);
> > +
> > + prop = of_get_flat_dt_prop(node, "linux,uefi-mmap-desc-size", &len);
> > + if (!prop) {
> > + pr_err("No EFI memmap descriptor size in FDT\n");
> > + return 0;
> > + }
> > + memmap.desc_size = of_read_ulong(prop, len/4);
> > +
> > + memmap.nr_map = size / memmap.desc_size;
> > +
> > + prop = of_get_flat_dt_prop(node, "linux,uefi-mmap-desc-ver", &len);
> > + if (!prop) {
> > + pr_err("No EFI memmap descriptor version in FDT\n");
> > + return 0;
> > + }
> > + memmap.desc_version = of_read_ulong(prop, len/4);
> > +
> > + if (uefi_debug) {
> > + pr_info(" EFI system table @ %p\n", (void *)efi_system_table);
> > + pr_info(" EFI mmap @ %p-%p\n", memmap.phys_map,
> > + memmap.map_end);
> > + pr_info(" EFI mmap descriptor size = 0x%lx\n",
> > + memmap.desc_size);
> > + pr_info(" EFI mmap descriptor version = 0x%lx\n",
> > + memmap.desc_version);
> > + }
> > +
> > + return 1;
> > +}
>
> Are these checks generic to other architectures? We may do with some
> helpers to avoid duplication.

That whole function could probably be shared.

>
> > +
> > +#define PGD_END (&idmap_pg_dir[sizeof(idmap_pg_dir)/sizeof(pgd_t)])
>
> Just &idmap_pg_dir[PTRS_PER_PGD] would do (or idmap_pg_dir +
> ARRAY_SIZE(idmap_pg_dir)).

Should have been ARRAY_SIZE. I didn't want to use PTRS_PER_PGD in case
that changed.

>
> > +#ifndef CONFIG_SMP
> > +#define PMD_FLAGS (PMD_ATTRINDX(MT_NORMAL) | PMD_TYPE_SECT | PMD_SECT_AF)
> > +#else
> > +#define PMD_FLAGS (PMD_ATTRINDX(MT_NORMAL) | PMD_TYPE_SECT | PMD_SECT_AF | \
> > + PMD_SECT_S)
> > +#endif
> > +
> > +static void __init create_idmap(unsigned long addr, unsigned long len)
>
> I would change this to use the existing create_mapping() function which
> takes care of allocating pud/pmd/ptes. We shouldn't duplicate this
> functionality in two places. create_mapping() may need a slight change
> since it assumes swapper_pg_dir but it's not much. It also uses
> memblock_alloc() for early allocations.

I'll take a look at this.

>
> > +static __init int memmap_walk(struct efi_memory_map *memmap,
> > + int (*func)(efi_memory_desc_t *, void *),
> > + void *private_data, int early)
>
> Is this generic enough as a common helper between arm and arm64 (and
> maybe x86)?

Probably not as-is, but one could be made.
>
> > +static __init int reserve_region(efi_memory_desc_t *md, void *priv)
> [...]
> > +static __init void reserve_regions(void)
> [...]
> > +static int __init remap_region(efi_memory_desc_t *md, void *priv)
> [...]
> > +static int __init remap_regions(efi_memory_desc_t *map)
>
> Same here (I haven't looked at the other implementations).

Only in arm/arm64 patches and they are different.

>
> > +/*
> > + * Called from setup_arch with interrupts disabled.
> > + */
> > +void __init efi_enter_virtual_mode(void)
> > +{
> > + void *newmap;
> > + efi_status_t status;
> > + u64 mapsize, total_freed = 0;
> > + int count;
> > +
> > + if (!efi_enabled(EFI_BOOT)) {
> > + pr_info("EFI services will not be available.\n");
> > + return;
> > + }
> > +
> > + pr_info("Remapping and enabling EFI services.\n");
> > +
> > + mapsize = memmap.map_end - memmap.phys_map;
> > + memmap.map = (__force void *)ioremap_cache((phys_addr_t)memmap.phys_map,
> > + mapsize);
> > + memmap.map_end = memmap.map + mapsize;
> > +
> > + /* Map the regions we reserved earlier */
> > + newmap = alloc_bootmem(mapsize);
>
> memblock_alloc() (also check the other bootmem uses in this patch, arm64
> is using memblock).

Okay, the bootmem function do use the memblock interface with some leak
detection added, but I can use memblock directly.

>
> > + if (newmap == NULL) {
> > + pr_err("Failed to allocate new EFI memmap\n");
> > + return;
> > + }
> > +
> > + count = remap_regions(newmap);
> > + if (count <= 0) {
> > + pr_err("Failed to remap EFI regions.\n");
> > + return;
> > + }
> > +
> > + efi.memmap = &memmap;
> > +
> > + efi.systab = (__force void *)efi_lookup_mapped_addr(efi_system_table);
> > + if (efi.systab)
> > + set_bit(EFI_SYSTEM_TABLES, &arm_efi_facility);
> > +
> > + /*
> > + * efi.systab->runtime is a pointer to something guaranteed by
> > + * the UEFI specification to be 1:1 mapped.
> > + */
> > + runtime = (__force void *)efi_lookup_mapped_addr((u64)efi.systab->runtime);
> > +
> > + /* Call SetVirtualAddressMap with the physical address of the map */
> > + efi.set_virtual_address_map = runtime->set_virtual_address_map;
> > +
> > + /* boot time idmap_pg_dir is incomplete, so fill in missing parts */
> > + efi_setup_idmap();
> > +
> > + cpu_switch_mm(idmap_pg_dir, &init_mm);
> > + flush_tlb_all();
> > + flush_cache_all();
> > +
> > + status = efi.set_virtual_address_map(count * memmap.desc_size,
> > + memmap.desc_size,
> > + memmap.desc_version,
> > + newmap);
> > + cpu_set_reserved_ttbr0();
> > + flush_tlb_all();
> > + flush_cache_all();
>
> What is the point of cache flusing here?

Paranoia based on not knowing what the firmware will be caching.
If it doesn't matter and there's nothing to worry about I can drop
it.