diff mbox

[16/22] arm64: Add function to create identity mappings

Message ID 1391619853-10601-17-git-send-email-leif.lindholm@linaro.org (mailing list archive)
State New, archived
Headers show

Commit Message

Leif Lindholm Feb. 5, 2014, 5:04 p.m. UTC
From: Mark Salter <msalter@redhat.com>

At boot time, before switching to a virtual UEFI memory map, firmware
expects UEFI memory and IO regions to be identity mapped whenever
kernel makes runtime services calls. The exisitng early boot code
creates an identity map of kernel text/data but this is not sufficient
for UEFI. This patch adds a create_id_mapping() function which reuses
the core code of the existing create_mapping().

Signed-off-by: Mark Salter <msalter@redhat.com>
Signed-off-by: Leif Lindholm <leif.lindholm@linaro.org>
---
 arch/arm64/include/asm/mmu.h |    2 ++
 arch/arm64/mm/mmu.c          |   66 ++++++++++++++++++++++++++++++------------
 2 files changed, 50 insertions(+), 18 deletions(-)

Comments

Catalin Marinas Feb. 14, 2014, 5:42 p.m. UTC | #1
On Wed, Feb 05, 2014 at 05:04:07PM +0000, Leif Lindholm wrote:
> +void __init create_id_mapping(phys_addr_t addr, phys_addr_t size, int map_io)
> +{
> +	pgd_t *pgd = &idmap_pg_dir[pgd_index(addr)];
> +
> +	if (pgd >= &idmap_pg_dir[ARRAY_SIZE(idmap_pg_dir)]) {
> +		pr_warn("BUG: not creating id mapping for 0x%016llx\n", addr);
> +		return;
> +	}
> +	__create_mapping(pgd, addr, addr, size, map_io);
> +}

I'm a bit worried because I gave some feedback in the past but it didn't
make to this patch.

What I said on 22nd of January:

  The condition above is always false since pgd_index() already ands the
  index with (PTRS_PER_PGD - 1). Better check addr against something like
  (PTRS_PER_PGD * PGDIR_SIZE) (for clarity, you could do other shifts,
  doesn't really matter).
Mark Salter Feb. 17, 2014, 1:29 p.m. UTC | #2
On Fri, 2014-02-14 at 17:42 +0000, Catalin Marinas wrote:
> On Wed, Feb 05, 2014 at 05:04:07PM +0000, Leif Lindholm wrote:
> > +void __init create_id_mapping(phys_addr_t addr, phys_addr_t size, int map_io)
> > +{
> > +	pgd_t *pgd = &idmap_pg_dir[pgd_index(addr)];
> > +
> > +	if (pgd >= &idmap_pg_dir[ARRAY_SIZE(idmap_pg_dir)]) {
> > +		pr_warn("BUG: not creating id mapping for 0x%016llx\n", addr);
> > +		return;
> > +	}
> > +	__create_mapping(pgd, addr, addr, size, map_io);
> > +}
> 
> I'm a bit worried because I gave some feedback in the past but it didn't
> make to this patch.
> 
> What I said on 22nd of January:
> 
>   The condition above is always false since pgd_index() already ands the
>   index with (PTRS_PER_PGD - 1). Better check addr against something like
>   (PTRS_PER_PGD * PGDIR_SIZE) (for clarity, you could do other shifts,
>   doesn't really matter).
> 
Sorry about that. I remember making this change but I don't know what
happened to it.
diff mbox

Patch

diff --git a/arch/arm64/include/asm/mmu.h b/arch/arm64/include/asm/mmu.h
index f600d40..29ed1d8 100644
--- a/arch/arm64/include/asm/mmu.h
+++ b/arch/arm64/include/asm/mmu.h
@@ -28,5 +28,7 @@  extern void paging_init(void);
 extern void setup_mm_for_reboot(void);
 extern void __iomem *early_io_map(phys_addr_t phys, unsigned long virt);
 extern void init_mem_pgprot(void);
+/* create an identity mapping for memory (or io if map_io is true) */
+extern void create_id_mapping(phys_addr_t addr, phys_addr_t size, int map_io);
 
 #endif
diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
index 7b345e3..2eeebd1 100644
--- a/arch/arm64/mm/mmu.c
+++ b/arch/arm64/mm/mmu.c
@@ -168,7 +168,8 @@  static void __init *early_alloc(unsigned long sz)
 }
 
 static void __init alloc_init_pte(pmd_t *pmd, unsigned long addr,
-				  unsigned long end, unsigned long pfn)
+				  unsigned long end, unsigned long pfn,
+				  pgprot_t prot)
 {
 	pte_t *pte;
 
@@ -180,16 +181,28 @@  static void __init alloc_init_pte(pmd_t *pmd, unsigned long addr,
 
 	pte = pte_offset_kernel(pmd, addr);
 	do {
-		set_pte(pte, pfn_pte(pfn, PAGE_KERNEL_EXEC));
+		set_pte(pte, pfn_pte(pfn, prot));
 		pfn++;
 	} while (pte++, addr += PAGE_SIZE, addr != end);
 }
 
 static void __init alloc_init_pmd(pud_t *pud, unsigned long addr,
-				  unsigned long end, phys_addr_t phys)
+				  unsigned long end, phys_addr_t phys,
+				  int map_io)
 {
 	pmd_t *pmd;
 	unsigned long next;
+	pmdval_t prot_sect;
+	pgprot_t prot_pte;
+
+	if (map_io) {
+		prot_sect = PMD_TYPE_SECT | PMD_SECT_AF |
+			    PMD_ATTRINDX(MT_DEVICE_nGnRE);
+		prot_pte = __pgprot(PROT_DEVICE_nGnRE);
+	} else {
+		prot_sect = prot_sect_kernel;
+		prot_pte = PAGE_KERNEL_EXEC;
+	}
 
 	/*
 	 * Check for initial section mappings in the pgd/pud and remove them.
@@ -204,22 +217,24 @@  static void __init alloc_init_pmd(pud_t *pud, unsigned long addr,
 		next = pmd_addr_end(addr, end);
 		/* try section mapping first */
 		if (((addr | next | phys) & ~SECTION_MASK) == 0)
-			set_pmd(pmd, __pmd(phys | prot_sect_kernel));
+			set_pmd(pmd, __pmd(phys | prot_sect));
 		else
-			alloc_init_pte(pmd, addr, next, __phys_to_pfn(phys));
+			alloc_init_pte(pmd, addr, next, __phys_to_pfn(phys),
+				       prot_pte);
 		phys += next - addr;
 	} while (pmd++, addr = next, addr != end);
 }
 
 static void __init alloc_init_pud(pgd_t *pgd, unsigned long addr,
-				  unsigned long end, unsigned long phys)
+				  unsigned long end, unsigned long phys,
+				  int map_io)
 {
 	pud_t *pud = pud_offset(pgd, addr);
 	unsigned long next;
 
 	do {
 		next = pud_addr_end(addr, end);
-		alloc_init_pmd(pud, addr, next, phys);
+		alloc_init_pmd(pud, addr, next, phys, map_io);
 		phys += next - addr;
 	} while (pud++, addr = next, addr != end);
 }
@@ -228,30 +243,45 @@  static void __init alloc_init_pud(pgd_t *pgd, unsigned long addr,
  * Create the page directory entries and any necessary page tables for the
  * mapping specified by 'md'.
  */
-static void __init create_mapping(phys_addr_t phys, unsigned long virt,
-				  phys_addr_t size)
+static void __init __create_mapping(pgd_t *pgd, phys_addr_t phys,
+				    unsigned long virt, phys_addr_t size,
+				    int map_io)
 {
 	unsigned long addr, length, end, next;
-	pgd_t *pgd;
-
-	if (virt < VMALLOC_START) {
-		pr_warning("BUG: not creating mapping for 0x%016llx at 0x%016lx - outside kernel range\n",
-			   phys, virt);
-		return;
-	}
 
 	addr = virt & PAGE_MASK;
 	length = PAGE_ALIGN(size + (virt & ~PAGE_MASK));
 
-	pgd = pgd_offset_k(addr);
 	end = addr + length;
 	do {
 		next = pgd_addr_end(addr, end);
-		alloc_init_pud(pgd, addr, next, phys);
+		alloc_init_pud(pgd, addr, next, phys, map_io);
 		phys += next - addr;
 	} while (pgd++, addr = next, addr != end);
 }
 
+static void __init create_mapping(phys_addr_t phys, unsigned long virt,
+				  phys_addr_t size)
+{
+	if (virt < VMALLOC_START) {
+		pr_warn("BUG: not creating mapping for 0x%016llx at 0x%016lx - outside kernel range\n",
+			phys, virt);
+		return;
+	}
+	__create_mapping(pgd_offset_k(virt & PAGE_MASK), phys, virt, size, 0);
+}
+
+void __init create_id_mapping(phys_addr_t addr, phys_addr_t size, int map_io)
+{
+	pgd_t *pgd = &idmap_pg_dir[pgd_index(addr)];
+
+	if (pgd >= &idmap_pg_dir[ARRAY_SIZE(idmap_pg_dir)]) {
+		pr_warn("BUG: not creating id mapping for 0x%016llx\n", addr);
+		return;
+	}
+	__create_mapping(pgd, addr, addr, size, map_io);
+}
+
 static void __init map_mem(void)
 {
 	struct memblock_region *reg;