diff mbox

[v5,02/14] ARM: Section based HYP idmap

Message ID 20130108183848.46302.77369.stgit@ubuntu (mailing list archive)
State New, archived
Headers show

Commit Message

Christoffer Dall Jan. 8, 2013, 6:38 p.m. UTC
Add a method (hyp_idmap_setup) to populate a hyp pgd with an
identity mapping of the code contained in the .hyp.idmap.text
section.

Offer a method to drop this identity mapping through
hyp_idmap_teardown.

Make all the above depend on CONFIG_ARM_VIRT_EXT and CONFIG_ARM_LPAE.

Cc: Will Deacon <will.deacon@arm.com>
Reviewed-by: Marcelo Tosatti <mtosatti@redhat.com>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Signed-off-by: Christoffer Dall <c.dall@virtualopensystems.com>
---
 arch/arm/include/asm/idmap.h                |    1 +
 arch/arm/include/asm/pgtable-3level-hwdef.h |    1 +
 arch/arm/kernel/vmlinux.lds.S               |    6 +++
 arch/arm/mm/idmap.c                         |   54 ++++++++++++++++++++++-----
 4 files changed, 50 insertions(+), 12 deletions(-)

Comments

Gleb Natapov Jan. 14, 2013, 10:27 a.m. UTC | #1
On Tue, Jan 08, 2013 at 01:38:48PM -0500, Christoffer Dall wrote:
> Add a method (hyp_idmap_setup) to populate a hyp pgd with an
> identity mapping of the code contained in the .hyp.idmap.text
> section.
> 
> Offer a method to drop this identity mapping through
> hyp_idmap_teardown.
> 
> Make all the above depend on CONFIG_ARM_VIRT_EXT and CONFIG_ARM_LPAE.
> 
> Cc: Will Deacon <will.deacon@arm.com>
> Reviewed-by: Marcelo Tosatti <mtosatti@redhat.com>
> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
> Signed-off-by: Christoffer Dall <c.dall@virtualopensystems.com>
> ---
>  arch/arm/include/asm/idmap.h                |    1 +
>  arch/arm/include/asm/pgtable-3level-hwdef.h |    1 +
>  arch/arm/kernel/vmlinux.lds.S               |    6 +++
>  arch/arm/mm/idmap.c                         |   54 ++++++++++++++++++++++-----
>  4 files changed, 50 insertions(+), 12 deletions(-)
> 
> diff --git a/arch/arm/include/asm/idmap.h b/arch/arm/include/asm/idmap.h
> index bf863ed..1a66f907 100644
> --- a/arch/arm/include/asm/idmap.h
> +++ b/arch/arm/include/asm/idmap.h
> @@ -8,6 +8,7 @@
>  #define __idmap __section(.idmap.text) noinline notrace
>  
>  extern pgd_t *idmap_pgd;
> +extern pgd_t *hyp_pgd;
>  
>  void setup_mm_for_reboot(void);
>  
> diff --git a/arch/arm/include/asm/pgtable-3level-hwdef.h b/arch/arm/include/asm/pgtable-3level-hwdef.h
> index d795282..a2d404e 100644
> --- a/arch/arm/include/asm/pgtable-3level-hwdef.h
> +++ b/arch/arm/include/asm/pgtable-3level-hwdef.h
> @@ -44,6 +44,7 @@
>  #define PMD_SECT_XN		(_AT(pmdval_t, 1) << 54)
>  #define PMD_SECT_AP_WRITE	(_AT(pmdval_t, 0))
>  #define PMD_SECT_AP_READ	(_AT(pmdval_t, 0))
> +#define PMD_SECT_AP1		(_AT(pmdval_t, 1) << 6)
>  #define PMD_SECT_TEX(x)		(_AT(pmdval_t, 0))
>  
>  /*
> diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S
> index 11c1785..b571484 100644
> --- a/arch/arm/kernel/vmlinux.lds.S
> +++ b/arch/arm/kernel/vmlinux.lds.S
> @@ -19,7 +19,11 @@
>  	ALIGN_FUNCTION();						\
>  	VMLINUX_SYMBOL(__idmap_text_start) = .;				\
>  	*(.idmap.text)							\
> -	VMLINUX_SYMBOL(__idmap_text_end) = .;
> +	VMLINUX_SYMBOL(__idmap_text_end) = .;				\
> +	ALIGN_FUNCTION();						\
> +	VMLINUX_SYMBOL(__hyp_idmap_text_start) = .;			\
> +	*(.hyp.idmap.text)						\
> +	VMLINUX_SYMBOL(__hyp_idmap_text_end) = .;
>  
>  #ifdef CONFIG_HOTPLUG_CPU
>  #define ARM_CPU_DISCARD(x)
> diff --git a/arch/arm/mm/idmap.c b/arch/arm/mm/idmap.c
> index 99db769..d9213a5 100644
> --- a/arch/arm/mm/idmap.c
> +++ b/arch/arm/mm/idmap.c
> @@ -1,4 +1,6 @@
> +#include <linux/module.h>
>  #include <linux/kernel.h>
> +#include <linux/slab.h>
>  
>  #include <asm/cputype.h>
>  #include <asm/idmap.h>
> @@ -6,6 +8,7 @@
>  #include <asm/pgtable.h>
>  #include <asm/sections.h>
>  #include <asm/system_info.h>
> +#include <asm/virt.h>
>  
>  pgd_t *idmap_pgd;
>  
> @@ -59,11 +62,20 @@ static void idmap_add_pud(pgd_t *pgd, unsigned long addr, unsigned long end,
>  	} while (pud++, addr = next, addr != end);
>  }
>  
> -static void identity_mapping_add(pgd_t *pgd, unsigned long addr, unsigned long end)
> +static void identity_mapping_add(pgd_t *pgd, const char *text_start,
> +				 const char *text_end, unsigned long prot)
>  {
> -	unsigned long prot, next;
> +	unsigned long addr, end;
> +	unsigned long next;
> +
> +	addr = virt_to_phys(text_start);
> +	end = virt_to_phys(text_end);
How does this work with phys addresses greater than 32bit (with
LPAE)? This was the same before the patch too, but I am still
curious. Since __virt_to_phys() returns unsigned long kernel cannot be
put in high memory, right?


> +
> +	pr_info("Setting up static %sidentity map for 0x%llx - 0x%llx\n",
> +		prot ? "HYP " : "",
> +		(long long)addr, (long long)end);
> +	prot |= PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_SECT_AF;
>  
> -	prot = PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_SECT_AF;
>  	if (cpu_architecture() <= CPU_ARCH_ARMv5TEJ && !cpu_is_xscale())
>  		prot |= PMD_BIT4;
>  
> @@ -74,28 +86,48 @@ static void identity_mapping_add(pgd_t *pgd, unsigned long addr, unsigned long e
>  	} while (pgd++, addr = next, addr != end);
>  }
>  
> +#ifdef CONFIG_ARM_VIRT_EXT
> +pgd_t *hyp_pgd;
> +
> +extern char  __hyp_idmap_text_start[], __hyp_idmap_text_end[];
> +
> +static int __init init_static_idmap_hyp(void)
> +{
> +	hyp_pgd = kzalloc(PTRS_PER_PGD * sizeof(pgd_t), GFP_KERNEL);
> +	if (!hyp_pgd)
> +		return -ENOMEM;
> +
> +	identity_mapping_add(hyp_pgd, __hyp_idmap_text_start,
> +			     __hyp_idmap_text_end, PMD_SECT_AP1);
> +
> +	return 0;
> +}
> +#else
> +static int __init init_static_idmap_hyp(void)
> +{
> +	return 0;
> +}
> +#endif
> +
>  extern char  __idmap_text_start[], __idmap_text_end[];
>  
>  static int __init init_static_idmap(void)
>  {
> -	phys_addr_t idmap_start, idmap_end;
> +	int ret;
>  
>  	idmap_pgd = pgd_alloc(&init_mm);
>  	if (!idmap_pgd)
>  		return -ENOMEM;
>  
> -	/* Add an identity mapping for the physical address of the section. */
> -	idmap_start = virt_to_phys((void *)__idmap_text_start);
> -	idmap_end = virt_to_phys((void *)__idmap_text_end);
> +	identity_mapping_add(idmap_pgd, __idmap_text_start,
> +			     __idmap_text_end, 0);
>  
> -	pr_info("Setting up static identity map for 0x%llx - 0x%llx\n",
> -		(long long)idmap_start, (long long)idmap_end);
> -	identity_mapping_add(idmap_pgd, idmap_start, idmap_end);
> +	ret = init_static_idmap_hyp();
>  
>  	/* Flush L1 for the hardware to see this page table content */
>  	flush_cache_louis();
>  
> -	return 0;
> +	return ret;
>  }
>  early_initcall(init_static_idmap);
>  
> 
> --
> To unsubscribe from this list: send the line "unsubscribe kvm" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

--
			Gleb.
Will Deacon Jan. 14, 2013, 10:49 a.m. UTC | #2
On Mon, Jan 14, 2013 at 10:27:21AM +0000, Gleb Natapov wrote:
> On Tue, Jan 08, 2013 at 01:38:48PM -0500, Christoffer Dall wrote:
> > Add a method (hyp_idmap_setup) to populate a hyp pgd with an
> > identity mapping of the code contained in the .hyp.idmap.text
> > section.
> > 
> > Offer a method to drop this identity mapping through
> > hyp_idmap_teardown.
> > 
> > Make all the above depend on CONFIG_ARM_VIRT_EXT and CONFIG_ARM_LPAE.
> > 
> > Cc: Will Deacon <will.deacon@arm.com>
> > Reviewed-by: Marcelo Tosatti <mtosatti@redhat.com>
> > Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
> > Signed-off-by: Christoffer Dall <c.dall@virtualopensystems.com>
> > ---
> >  arch/arm/include/asm/idmap.h                |    1 +
> >  arch/arm/include/asm/pgtable-3level-hwdef.h |    1 +
> >  arch/arm/kernel/vmlinux.lds.S               |    6 +++
> >  arch/arm/mm/idmap.c                         |   54 ++++++++++++++++++++++-----
> >  4 files changed, 50 insertions(+), 12 deletions(-)

[...]

> > -static void identity_mapping_add(pgd_t *pgd, unsigned long addr, unsigned long end)
> > +static void identity_mapping_add(pgd_t *pgd, const char *text_start,
> > +				 const char *text_end, unsigned long prot)
> >  {
> > -	unsigned long prot, next;
> > +	unsigned long addr, end;
> > +	unsigned long next;
> > +
> > +	addr = virt_to_phys(text_start);
> > +	end = virt_to_phys(text_end);
> How does this work with phys addresses greater than 32bit (with
> LPAE)? This was the same before the patch too, but I am still
> curious. Since __virt_to_phys() returns unsigned long kernel cannot be
> put in high memory, right?

Well, AArch32 (arch/arm/) only supports 32-bit virtual addresses by virtue
of the fact that our registers are only 32 bits wide, so we can't
identity-map physical addresses above the 4GB boundary.

You may want to look at the keystone patches from TI for insight about
kernels at high (>32-bit) addresses, although I've not seen any activity
around that for some time now (which is a pity, because the code-patching
stuff was in a good shape).

Will
Gleb Natapov Jan. 14, 2013, 11:07 a.m. UTC | #3
On Mon, Jan 14, 2013 at 10:49:53AM +0000, Will Deacon wrote:
> On Mon, Jan 14, 2013 at 10:27:21AM +0000, Gleb Natapov wrote:
> > On Tue, Jan 08, 2013 at 01:38:48PM -0500, Christoffer Dall wrote:
> > > Add a method (hyp_idmap_setup) to populate a hyp pgd with an
> > > identity mapping of the code contained in the .hyp.idmap.text
> > > section.
> > > 
> > > Offer a method to drop this identity mapping through
> > > hyp_idmap_teardown.
> > > 
> > > Make all the above depend on CONFIG_ARM_VIRT_EXT and CONFIG_ARM_LPAE.
> > > 
> > > Cc: Will Deacon <will.deacon@arm.com>
> > > Reviewed-by: Marcelo Tosatti <mtosatti@redhat.com>
> > > Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
> > > Signed-off-by: Christoffer Dall <c.dall@virtualopensystems.com>
> > > ---
> > >  arch/arm/include/asm/idmap.h                |    1 +
> > >  arch/arm/include/asm/pgtable-3level-hwdef.h |    1 +
> > >  arch/arm/kernel/vmlinux.lds.S               |    6 +++
> > >  arch/arm/mm/idmap.c                         |   54 ++++++++++++++++++++++-----
> > >  4 files changed, 50 insertions(+), 12 deletions(-)
> 
> [...]
> 
> > > -static void identity_mapping_add(pgd_t *pgd, unsigned long addr, unsigned long end)
> > > +static void identity_mapping_add(pgd_t *pgd, const char *text_start,
> > > +				 const char *text_end, unsigned long prot)
> > >  {
> > > -	unsigned long prot, next;
> > > +	unsigned long addr, end;
> > > +	unsigned long next;
> > > +
> > > +	addr = virt_to_phys(text_start);
> > > +	end = virt_to_phys(text_end);
> > How does this work with phys addresses greater than 32bit (with
> > LPAE)? This was the same before the patch too, but I am still
> > curious. Since __virt_to_phys() returns unsigned long kernel cannot be
> > put in high memory, right?
> 
> Well, AArch32 (arch/arm/) only supports 32-bit virtual addresses by virtue
> of the fact that our registers are only 32 bits wide, so we can't
> identity-map physical addresses above the 4GB boundary.
> 
Ah, of course. This is ident map so by definition it cannot map phys
addresses above 4G. And since __virt_to_phys() suppose to work only on
ident map it's OK to returns unsigned long.


> You may want to look at the keystone patches from TI for insight about
> kernels at high (>32-bit) addresses, although I've not seen any activity
> around that for some time now (which is a pity, because the code-patching
> stuff was in a good shape).
> 
> Will

--
			Gleb.
Russell King - ARM Linux Jan. 14, 2013, 1:07 p.m. UTC | #4
On Mon, Jan 14, 2013 at 01:07:56PM +0200, Gleb Natapov wrote:
> Ah, of course. This is ident map so by definition it cannot map phys
> addresses above 4G. And since __virt_to_phys() suppose to work only on
> ident map it's OK to returns unsigned long.

Let's get this right... the lack of correct definition in these
comments needs correction.

Firstly, __virt_to_phys() is an ARM-arch private function.  Only
ARM-arch private code should be using it - it must not be used outside
that context.

Secondly, it's "public" counterpart, virt_to_phys(), and itself are
valid _only_ for what we call the "kernel direct map", which are the
addresses corresponding with the lowmem pages mapped from PAGE_OFFSET
up to the _start_ of vmalloc space.  No other mapping is valid for this.

That means that addresses in the identity mapping, if the identity
mapping is outside of the range {PAGE_OFFSET..vmalloc start}, are _not_
valid for virt_to_phys().

The same is true of their counterparts, __phys_to_virt() and
phys_to_virt().  These are _only_ valid for physical addresses
corresponding to the pages mapped in as "lowmem" and they will return
addresses for that mapping of the pages.

Both these functions are invalid when used on highmem pages.
*virt_to_phys() is invalid when used on pointers returned from
ioremap(), vmalloc(), vmap(), dma_alloc_coherent(), and any other
interface which remaps memory.
Russell King - ARM Linux Jan. 14, 2013, 4:13 p.m. UTC | #5
On Tue, Jan 08, 2013 at 01:38:48PM -0500, Christoffer Dall wrote:
> +	pr_info("Setting up static %sidentity map for 0x%llx - 0x%llx\n",
> +		prot ? "HYP " : "",
> +		(long long)addr, (long long)end);

There's no point using 0x%llx and casting to 64-bit longs if the arguments
are always going to be 32-bit.
diff mbox

Patch

diff --git a/arch/arm/include/asm/idmap.h b/arch/arm/include/asm/idmap.h
index bf863ed..1a66f907 100644
--- a/arch/arm/include/asm/idmap.h
+++ b/arch/arm/include/asm/idmap.h
@@ -8,6 +8,7 @@ 
 #define __idmap __section(.idmap.text) noinline notrace
 
 extern pgd_t *idmap_pgd;
+extern pgd_t *hyp_pgd;
 
 void setup_mm_for_reboot(void);
 
diff --git a/arch/arm/include/asm/pgtable-3level-hwdef.h b/arch/arm/include/asm/pgtable-3level-hwdef.h
index d795282..a2d404e 100644
--- a/arch/arm/include/asm/pgtable-3level-hwdef.h
+++ b/arch/arm/include/asm/pgtable-3level-hwdef.h
@@ -44,6 +44,7 @@ 
 #define PMD_SECT_XN		(_AT(pmdval_t, 1) << 54)
 #define PMD_SECT_AP_WRITE	(_AT(pmdval_t, 0))
 #define PMD_SECT_AP_READ	(_AT(pmdval_t, 0))
+#define PMD_SECT_AP1		(_AT(pmdval_t, 1) << 6)
 #define PMD_SECT_TEX(x)		(_AT(pmdval_t, 0))
 
 /*
diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S
index 11c1785..b571484 100644
--- a/arch/arm/kernel/vmlinux.lds.S
+++ b/arch/arm/kernel/vmlinux.lds.S
@@ -19,7 +19,11 @@ 
 	ALIGN_FUNCTION();						\
 	VMLINUX_SYMBOL(__idmap_text_start) = .;				\
 	*(.idmap.text)							\
-	VMLINUX_SYMBOL(__idmap_text_end) = .;
+	VMLINUX_SYMBOL(__idmap_text_end) = .;				\
+	ALIGN_FUNCTION();						\
+	VMLINUX_SYMBOL(__hyp_idmap_text_start) = .;			\
+	*(.hyp.idmap.text)						\
+	VMLINUX_SYMBOL(__hyp_idmap_text_end) = .;
 
 #ifdef CONFIG_HOTPLUG_CPU
 #define ARM_CPU_DISCARD(x)
diff --git a/arch/arm/mm/idmap.c b/arch/arm/mm/idmap.c
index 99db769..d9213a5 100644
--- a/arch/arm/mm/idmap.c
+++ b/arch/arm/mm/idmap.c
@@ -1,4 +1,6 @@ 
+#include <linux/module.h>
 #include <linux/kernel.h>
+#include <linux/slab.h>
 
 #include <asm/cputype.h>
 #include <asm/idmap.h>
@@ -6,6 +8,7 @@ 
 #include <asm/pgtable.h>
 #include <asm/sections.h>
 #include <asm/system_info.h>
+#include <asm/virt.h>
 
 pgd_t *idmap_pgd;
 
@@ -59,11 +62,20 @@  static void idmap_add_pud(pgd_t *pgd, unsigned long addr, unsigned long end,
 	} while (pud++, addr = next, addr != end);
 }
 
-static void identity_mapping_add(pgd_t *pgd, unsigned long addr, unsigned long end)
+static void identity_mapping_add(pgd_t *pgd, const char *text_start,
+				 const char *text_end, unsigned long prot)
 {
-	unsigned long prot, next;
+	unsigned long addr, end;
+	unsigned long next;
+
+	addr = virt_to_phys(text_start);
+	end = virt_to_phys(text_end);
+
+	pr_info("Setting up static %sidentity map for 0x%llx - 0x%llx\n",
+		prot ? "HYP " : "",
+		(long long)addr, (long long)end);
+	prot |= PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_SECT_AF;
 
-	prot = PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_SECT_AF;
 	if (cpu_architecture() <= CPU_ARCH_ARMv5TEJ && !cpu_is_xscale())
 		prot |= PMD_BIT4;
 
@@ -74,28 +86,48 @@  static void identity_mapping_add(pgd_t *pgd, unsigned long addr, unsigned long e
 	} while (pgd++, addr = next, addr != end);
 }
 
+#ifdef CONFIG_ARM_VIRT_EXT
+pgd_t *hyp_pgd;
+
+extern char  __hyp_idmap_text_start[], __hyp_idmap_text_end[];
+
+static int __init init_static_idmap_hyp(void)
+{
+	hyp_pgd = kzalloc(PTRS_PER_PGD * sizeof(pgd_t), GFP_KERNEL);
+	if (!hyp_pgd)
+		return -ENOMEM;
+
+	identity_mapping_add(hyp_pgd, __hyp_idmap_text_start,
+			     __hyp_idmap_text_end, PMD_SECT_AP1);
+
+	return 0;
+}
+#else
+static int __init init_static_idmap_hyp(void)
+{
+	return 0;
+}
+#endif
+
 extern char  __idmap_text_start[], __idmap_text_end[];
 
 static int __init init_static_idmap(void)
 {
-	phys_addr_t idmap_start, idmap_end;
+	int ret;
 
 	idmap_pgd = pgd_alloc(&init_mm);
 	if (!idmap_pgd)
 		return -ENOMEM;
 
-	/* Add an identity mapping for the physical address of the section. */
-	idmap_start = virt_to_phys((void *)__idmap_text_start);
-	idmap_end = virt_to_phys((void *)__idmap_text_end);
+	identity_mapping_add(idmap_pgd, __idmap_text_start,
+			     __idmap_text_end, 0);
 
-	pr_info("Setting up static identity map for 0x%llx - 0x%llx\n",
-		(long long)idmap_start, (long long)idmap_end);
-	identity_mapping_add(idmap_pgd, idmap_start, idmap_end);
+	ret = init_static_idmap_hyp();
 
 	/* Flush L1 for the hardware to see this page table content */
 	flush_cache_louis();
 
-	return 0;
+	return ret;
 }
 early_initcall(init_static_idmap);