Patchwork [U-Boot,v3,4/7] ARM: switch to non-secure state during bootm execution

login
register
mail settings
Submitter Andre Przywara
Date July 9, 2013, 11:54 p.m.
Message ID <1373414059-22779-5-git-send-email-andre.przywara@linaro.org>
Download mbox | patch
Permalink /patch/257920/
State Superseded
Delegated to: Albert ARIBAUD
Headers show

Comments

Andre Przywara - July 9, 2013, 11:54 p.m.
To actually trigger the non-secure switch we just implemented, call
the switching routine from within the bootm command implementation.
This way we automatically enable this feature without further user
intervention.

The core specific part of the work is done in the assembly routine
in nonsec_virt.S, introduced with the previous patch, but for the full
glory we need to setup the GIC distributor interface once for the
whole system, which is done in C here.
The routine is placed in arch/arm/cpu/armv7 to allow easy access from
other ARMv7 boards.

We check the availability of the security extensions first.

Since we need a safe way to access the GIC, we use the PERIPHBASE
registers on Cortex-A15 and A7 CPUs and do some sanity checks.
Board not implementing the CBAR can override this value via a
configuration file variable.

Then we actually do the GIC enablement:
a) enable the GIC distributor, both for non-secure and secure state
   (GICD_CTLR[1:0] = 11b)
b) allow all interrupts to be handled from non-secure state
   (GICD_IGROUPRn = 0xFFFFFFFF)

The core specific GIC setup is then done in the assembly routine.

The actual bootm trigger is pretty small: calling the routine and
doing some error reporting.

Signed-off-by: Andre Przywara <andre.przywara@linaro.org>
---
 arch/arm/cpu/armv7/Makefile  |   1 +
 arch/arm/cpu/armv7/virt-v7.c | 117 +++++++++++++++++++++++++++++++++++++++++++
 arch/arm/include/asm/armv7.h |  10 ++++
 arch/arm/lib/bootm.c         |  28 +++++++++++
 4 files changed, 156 insertions(+)
 create mode 100644 arch/arm/cpu/armv7/virt-v7.c
Nikolay Nikolaev - July 10, 2013, 12:49 p.m.
Hello Andre,


On Wed, Jul 10, 2013 at 2:54 AM, Andre Przywara
<andre.przywara@linaro.org>wrote:

> To actually trigger the non-secure switch we just implemented, call
> the switching routine from within the bootm command implementation.
> This way we automatically enable this feature without further user
> intervention.
>
> The core specific part of the work is done in the assembly routine
> in nonsec_virt.S, introduced with the previous patch, but for the full
> glory we need to setup the GIC distributor interface once for the
> whole system, which is done in C here.
> The routine is placed in arch/arm/cpu/armv7 to allow easy access from
> other ARMv7 boards.
>
> We check the availability of the security extensions first.
>
> Since we need a safe way to access the GIC, we use the PERIPHBASE
> registers on Cortex-A15 and A7 CPUs and do some sanity checks.
> Board not implementing the CBAR can override this value via a
> configuration file variable.
>
> Then we actually do the GIC enablement:
> a) enable the GIC distributor, both for non-secure and secure state
>    (GICD_CTLR[1:0] = 11b)
> b) allow all interrupts to be handled from non-secure state
>    (GICD_IGROUPRn = 0xFFFFFFFF)
>
> The core specific GIC setup is then done in the assembly routine.
>
> The actual bootm trigger is pretty small: calling the routine and
> doing some error reporting.
>
> Signed-off-by: Andre Przywara <andre.przywara@linaro.org>
> ---
>  arch/arm/cpu/armv7/Makefile  |   1 +
>  arch/arm/cpu/armv7/virt-v7.c | 117
> +++++++++++++++++++++++++++++++++++++++++++
>  arch/arm/include/asm/armv7.h |  10 ++++
>  arch/arm/lib/bootm.c         |  28 +++++++++++
>  4 files changed, 156 insertions(+)
>  create mode 100644 arch/arm/cpu/armv7/virt-v7.c
>
> diff --git a/arch/arm/cpu/armv7/Makefile b/arch/arm/cpu/armv7/Makefile
> index 5d75077..b59f59e 100644
> --- a/arch/arm/cpu/armv7/Makefile
> +++ b/arch/arm/cpu/armv7/Makefile
> @@ -38,6 +38,7 @@ endif
>
>  ifneq ($(CONFIG_ARMV7_NONSEC),)
>  SOBJS   += nonsec_virt.o
> +COBJS  += virt-v7.o
>  endif
>
>  SRCS   := $(START:.o=.S) $(COBJS:.o=.c)
> diff --git a/arch/arm/cpu/armv7/virt-v7.c b/arch/arm/cpu/armv7/virt-v7.c
> new file mode 100644
> index 0000000..54f9746
> --- /dev/null
> +++ b/arch/arm/cpu/armv7/virt-v7.c
> @@ -0,0 +1,117 @@
> +/*
> + * (C) Copyright 2013
> + * Andre Przywara, Linaro
> + *
> + * Routines to transition ARMv7 processors from secure into non-secure
> state
> + * needed to enable ARMv7 virtualization for current hypervisors
> + *
> + * See file CREDITS for list of people who contributed to this
> + * project.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
> + * MA 02111-1307 USA
> + */
> +
> +#include <common.h>
> +#include <asm/armv7.h>
> +#include <asm/gic.h>
> +#include <asm/io.h>
> +
> +static unsigned int read_id_pfr1(void)
> +{
> +       unsigned int reg;
> +
> +       asm("mrc p15, 0, %0, c0, c1, 1\n" : "=r"(reg));
> +       return reg;
> +}
> +
> +static int get_gicd_base_address(unsigned int *gicdaddr)
> +{
> +#ifdef CONFIG_ARM_GIC_BASE_ADDRESS
> +       *gicdaddr = CONFIG_ARM_GIC_BASE_ADDRESS + GIC_DIST_OFFSET;
> +       return 0;
> +#else
> +       unsigned midr;
> +       unsigned periphbase;
> +
> +       /* check whether we are an Cortex-A15 or A7.
> +        * The actual HYP switch should work with all CPUs supporting
> +        * the virtualization extension, but we need the GIC address,
> +        * which we know only for sure for those two CPUs.
> +        */
> +       asm("mrc p15, 0, %0, c0, c0, 0\n" : "=r"(midr));
> +       switch (midr & MIDR_PRIMARY_PART_MASK) {
> +       case MIDR_CORTEX_A9_R0P1:
> +       case MIDR_CORTEX_A15_R0P0:
> +       case MIDR_CORTEX_A7_R0P0:
> +               break;
> +       default:
> +               return NONSEC_ERR_NO_GIC_ADDRESS;
> +       }
> +
> +       /* get the GIC base address from the CBAR register */
> +       asm("mrc p15, 4, %0, c15, c0, 0\n" : "=r" (periphbase));
> +
> +       /* the PERIPHBASE can be mapped above 4 GB (lower 8 bits used to
> +        * encode this). Bail out here since we cannot access this without
> +        * enabling paging.
> +        */
> +       if ((periphbase & 0xff) != 0)
> +               return NONSEC_ERR_GIC_ADDRESS_ABOVE_4GB;
> +
> +       *gicdaddr = periphbase + GIC_DIST_OFFSET;
>

The same as in _nonsec_init

periphbase &= PERIPHBASE_MASK; // 0xffff8000


> +
> +       return 0;
> +#endif
> +}
> +
> +enum nonsec_virt_errors armv7_switch_nonsec(void)
> +{
> +       unsigned int reg, ret;
> +       unsigned int gicdaddr = 0;
> +       unsigned itlinesnr, i;
> +
> +       /* check whether the CPU supports the security extensions */
> +       reg = read_id_pfr1();
> +       if ((reg & 0xF0) == 0)
> +               return NONSEC_ERR_NO_SEC_EXT;
> +
> +       /* the SCR register will be set directly in the monitor mode
> handler,
> +        * according to the spec one should not tinker with it in secure
> state
> +        * in SVC mode. Do not try to read it once in non-secure state,
> +        * any access to it will trap.
> +        */
> +
> +       ret = get_gicd_base_address(&gicdaddr);
> +       if (ret != 0)
> +               return ret;
> +
> +       /* enable the GIC distributor */
> +       writel(readl(gicdaddr + GICD_CTLR) | 0x03, gicdaddr + GICD_CTLR);
> +
> +       /* TYPER[4:0] contains an encoded number of available interrupts */
> +       itlinesnr = readl(gicdaddr + GICD_TYPER) & 0x1f;
> +
> +       /* set all bits in the GIC group registers to one to allow access
> +        * from non-secure state
> +        */
> +       for (i = 0; i <= itlinesnr; i++)
>

you can start from i = 1 here, since in _nonsec_init you already have:

       mvn     r1, #0                          @ all bits to 1
       str     r1, [r3, #GICD_IGROUPRn]        @ allow private interrupts

+               writel((unsigned)-1, gicdaddr + GICD_IGROUPRn + 4 * i);
> +
> +       /* call the non-sec switching code on this CPU */
> +       _nonsec_init();
> +
> +       return NONSEC_VIRT_SUCCESS;
> +}
> diff --git a/arch/arm/include/asm/armv7.h b/arch/arm/include/asm/armv7.h
> index ab9fa58..e5c0279 100644
> --- a/arch/arm/include/asm/armv7.h
> +++ b/arch/arm/include/asm/armv7.h
> @@ -90,6 +90,16 @@ void v7_outer_cache_flush_range(u32 start, u32 end);
>  void v7_outer_cache_inval_range(u32 start, u32 end);
>
>  #ifdef CONFIG_ARMV7_NONSEC
> +
> +enum nonsec_virt_errors {
> +       NONSEC_VIRT_SUCCESS,
> +       NONSEC_ERR_NO_SEC_EXT,
> +       NONSEC_ERR_NO_GIC_ADDRESS,
> +       NONSEC_ERR_GIC_ADDRESS_ABOVE_4GB,
> +};
> +
> +enum nonsec_virt_errors armv7_switch_nonsec(void);
> +
>  /* defined in assembly file */
>  unsigned int _nonsec_init(void);
>  #endif /* CONFIG_ARMV7_NONSEC */
> diff --git a/arch/arm/lib/bootm.c b/arch/arm/lib/bootm.c
> index 1b6e0ac..7b0619e 100644
> --- a/arch/arm/lib/bootm.c
> +++ b/arch/arm/lib/bootm.c
> @@ -34,6 +34,10 @@
>  #include <asm/bootm.h>
>  #include <linux/compiler.h>
>
> +#ifdef CONFIG_ARMV7_NONSEC
> +#include <asm/armv7.h>
> +#endif
> +
>  DECLARE_GLOBAL_DATA_PTR;
>
>  static struct tag *params;
> @@ -186,6 +190,29 @@ static void setup_end_tag(bd_t *bd)
>
>  __weak void setup_board_tags(struct tag **in_params) {}
>
> +static void do_nonsec_virt_switch(void)
> +{
> +#ifdef CONFIG_ARMV7_NONSEC
> +       int ret;
> +
> +       ret = armv7_switch_nonsec();
> +       switch (ret) {
> +       case NONSEC_VIRT_SUCCESS:
> +               debug("entered non-secure state\n");
> +               break;
> +       case NONSEC_ERR_NO_SEC_EXT:
> +               printf("nonsec: Security extensions not implemented.\n");
> +               break;
> +       case NONSEC_ERR_NO_GIC_ADDRESS:
> +               printf("nonsec: could not determine GIC address.\n");
> +               break;
> +       case NONSEC_ERR_GIC_ADDRESS_ABOVE_4GB:
> +               printf("nonsec: PERIPHBASE is above 4 GB, no access.\n");
> +               break;
> +       }
> +#endif
> +}
> +
>  /* Subcommand: PREP */
>  static void boot_prep_linux(bootm_headers_t *images)
>  {
> @@ -222,6 +249,7 @@ static void boot_prep_linux(bootm_headers_t *images)
>                 printf("FDT and ATAGS support not compiled in -
> hanging\n");
>                 hang();
>         }
> +       do_nonsec_virt_switch();
>  }
>
>  /* Subcommand: GO */
> --
> 1.7.12.1
>
>
regards,
Nikolay Nikolaev
Christoffer Dall - July 29, 2013, 10:02 p.m.
On Wed, Jul 10, 2013 at 01:54:16AM +0200, Andre Przywara wrote:

[...]

> diff --git a/arch/arm/lib/bootm.c b/arch/arm/lib/bootm.c
> index 1b6e0ac..7b0619e 100644
> --- a/arch/arm/lib/bootm.c
> +++ b/arch/arm/lib/bootm.c
> @@ -34,6 +34,10 @@
>  #include <asm/bootm.h>
>  #include <linux/compiler.h>
>  
> +#ifdef CONFIG_ARMV7_NONSEC
> +#include <asm/armv7.h>
> +#endif
> +
>  DECLARE_GLOBAL_DATA_PTR;
>  
>  static struct tag *params;
> @@ -186,6 +190,29 @@ static void setup_end_tag(bd_t *bd)
>  
>  __weak void setup_board_tags(struct tag **in_params) {}
>  
> +static void do_nonsec_virt_switch(void)
> +{
> +#ifdef CONFIG_ARMV7_NONSEC
> +	int ret;
> +
> +	ret = armv7_switch_nonsec();
> +	switch (ret) {
> +	case NONSEC_VIRT_SUCCESS:
> +		debug("entered non-secure state\n");
> +		break;
> +	case NONSEC_ERR_NO_SEC_EXT:
> +		printf("nonsec: Security extensions not implemented.\n");
> +		break;
> +	case NONSEC_ERR_NO_GIC_ADDRESS:
> +		printf("nonsec: could not determine GIC address.\n");
> +		break;
> +	case NONSEC_ERR_GIC_ADDRESS_ABOVE_4GB:
> +		printf("nonsec: PERIPHBASE is above 4 GB, no access.\n");
> +		break;
> +	}
> +#endif
> +}

I still don't get why you just don't make armv7_switch_nonsec a void and
print the error when they occur... ???

-Christoffer
Andre Przywara - July 30, 2013, 11:32 a.m.
On 07/30/2013 12:02 AM, Christoffer Dall wrote:
> On Wed, Jul 10, 2013 at 01:54:16AM +0200, Andre Przywara wrote:
>
> [...]
>
>> diff --git a/arch/arm/lib/bootm.c b/arch/arm/lib/bootm.c
>> index 1b6e0ac..7b0619e 100644
>> --- a/arch/arm/lib/bootm.c
>> +++ b/arch/arm/lib/bootm.c
>> @@ -34,6 +34,10 @@
>>   #include <asm/bootm.h>
>>   #include <linux/compiler.h>
>>
>> +#ifdef CONFIG_ARMV7_NONSEC
>> +#include <asm/armv7.h>
>> +#endif
>> +
>>   DECLARE_GLOBAL_DATA_PTR;
>>
>>   static struct tag *params;
>> @@ -186,6 +190,29 @@ static void setup_end_tag(bd_t *bd)
>>
>>   __weak void setup_board_tags(struct tag **in_params) {}
>>
>> +static void do_nonsec_virt_switch(void)
>> +{
>> +#ifdef CONFIG_ARMV7_NONSEC
>> +	int ret;
>> +
>> +	ret = armv7_switch_nonsec();
>> +	switch (ret) {
>> +	case NONSEC_VIRT_SUCCESS:
>> +		debug("entered non-secure state\n");
>> +		break;
>> +	case NONSEC_ERR_NO_SEC_EXT:
>> +		printf("nonsec: Security extensions not implemented.\n");
>> +		break;
>> +	case NONSEC_ERR_NO_GIC_ADDRESS:
>> +		printf("nonsec: could not determine GIC address.\n");
>> +		break;
>> +	case NONSEC_ERR_GIC_ADDRESS_ABOVE_4GB:
>> +		printf("nonsec: PERIPHBASE is above 4 GB, no access.\n");
>> +		break;
>> +	}
>> +#endif
>> +}
>
> I still don't get why you just don't make armv7_switch_nonsec a void and
> print the error when they occur... ???

My apologies for not elaborating on these comments I didn't incorporate:

So, I don't like the idea of marrying a low-level routine with high 
level output. I don't want to constraint the usage of the routine by 
requiring an output channel. Also some parts may not be fatal for all 
users - someone could just try to switch and then behave differently if 
that failed - without bothering the user.
May seem a bit over-engineered, but I like it better this way ;-)

If that is a show-stopper for you, I can change it, of course.

Regards,
Andre.
Christoffer Dall - July 30, 2013, 2:23 p.m.
On Tue, Jul 30, 2013 at 01:32:14PM +0200, Andre Przywara wrote:
> On 07/30/2013 12:02 AM, Christoffer Dall wrote:
> >On Wed, Jul 10, 2013 at 01:54:16AM +0200, Andre Przywara wrote:
> >
> >[...]
> >
> >>diff --git a/arch/arm/lib/bootm.c b/arch/arm/lib/bootm.c
> >>index 1b6e0ac..7b0619e 100644
> >>--- a/arch/arm/lib/bootm.c
> >>+++ b/arch/arm/lib/bootm.c
> >>@@ -34,6 +34,10 @@
> >>  #include <asm/bootm.h>
> >>  #include <linux/compiler.h>
> >>
> >>+#ifdef CONFIG_ARMV7_NONSEC
> >>+#include <asm/armv7.h>
> >>+#endif
> >>+
> >>  DECLARE_GLOBAL_DATA_PTR;
> >>
> >>  static struct tag *params;
> >>@@ -186,6 +190,29 @@ static void setup_end_tag(bd_t *bd)
> >>
> >>  __weak void setup_board_tags(struct tag **in_params) {}
> >>
> >>+static void do_nonsec_virt_switch(void)
> >>+{
> >>+#ifdef CONFIG_ARMV7_NONSEC
> >>+	int ret;
> >>+
> >>+	ret = armv7_switch_nonsec();
> >>+	switch (ret) {
> >>+	case NONSEC_VIRT_SUCCESS:
> >>+		debug("entered non-secure state\n");
> >>+		break;
> >>+	case NONSEC_ERR_NO_SEC_EXT:
> >>+		printf("nonsec: Security extensions not implemented.\n");
> >>+		break;
> >>+	case NONSEC_ERR_NO_GIC_ADDRESS:
> >>+		printf("nonsec: could not determine GIC address.\n");
> >>+		break;
> >>+	case NONSEC_ERR_GIC_ADDRESS_ABOVE_4GB:
> >>+		printf("nonsec: PERIPHBASE is above 4 GB, no access.\n");
> >>+		break;
> >>+	}
> >>+#endif
> >>+}
> >
> >I still don't get why you just don't make armv7_switch_nonsec a void and
> >print the error when they occur... ???
> 
> My apologies for not elaborating on these comments I didn't incorporate:
> 
> So, I don't like the idea of marrying a low-level routine with high
> level output. I don't want to constraint the usage of the routine by
> requiring an output channel. Also some parts may not be fatal for
> all users - someone could just try to switch and then behave
> differently if that failed - without bothering the user.
> May seem a bit over-engineered, but I like it better this way ;-)
> 
> If that is a show-stopper for you, I can change it, of course.
> 
I won't hold back my ack for the patch series based on this, but I do
think it's over-engineered.  I think at least just returning -1 for
error and 0 for success (or even make it a bool) and just printing a
generic error message is cleaner - the level of details as to why the
switch to hyp/nonsec didn't work could then be debug statements that a
board developer could enable with a "#define DEBUG 1" in the
corresponding file.

But ok, we've had the conversation, if you still feel this is better and
necessary, then I'll let it be.

-Christoffer

Patch

diff --git a/arch/arm/cpu/armv7/Makefile b/arch/arm/cpu/armv7/Makefile
index 5d75077..b59f59e 100644
--- a/arch/arm/cpu/armv7/Makefile
+++ b/arch/arm/cpu/armv7/Makefile
@@ -38,6 +38,7 @@  endif
 
 ifneq ($(CONFIG_ARMV7_NONSEC),)
 SOBJS   += nonsec_virt.o
+COBJS	+= virt-v7.o
 endif
 
 SRCS	:= $(START:.o=.S) $(COBJS:.o=.c)
diff --git a/arch/arm/cpu/armv7/virt-v7.c b/arch/arm/cpu/armv7/virt-v7.c
new file mode 100644
index 0000000..54f9746
--- /dev/null
+++ b/arch/arm/cpu/armv7/virt-v7.c
@@ -0,0 +1,117 @@ 
+/*
+ * (C) Copyright 2013
+ * Andre Przywara, Linaro
+ *
+ * Routines to transition ARMv7 processors from secure into non-secure state
+ * needed to enable ARMv7 virtualization for current hypervisors
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <asm/armv7.h>
+#include <asm/gic.h>
+#include <asm/io.h>
+
+static unsigned int read_id_pfr1(void)
+{
+	unsigned int reg;
+
+	asm("mrc p15, 0, %0, c0, c1, 1\n" : "=r"(reg));
+	return reg;
+}
+
+static int get_gicd_base_address(unsigned int *gicdaddr)
+{
+#ifdef CONFIG_ARM_GIC_BASE_ADDRESS
+	*gicdaddr = CONFIG_ARM_GIC_BASE_ADDRESS + GIC_DIST_OFFSET;
+	return 0;
+#else
+	unsigned midr;
+	unsigned periphbase;
+
+	/* check whether we are an Cortex-A15 or A7.
+	 * The actual HYP switch should work with all CPUs supporting
+	 * the virtualization extension, but we need the GIC address,
+	 * which we know only for sure for those two CPUs.
+	 */
+	asm("mrc p15, 0, %0, c0, c0, 0\n" : "=r"(midr));
+	switch (midr & MIDR_PRIMARY_PART_MASK) {
+	case MIDR_CORTEX_A9_R0P1:
+	case MIDR_CORTEX_A15_R0P0:
+	case MIDR_CORTEX_A7_R0P0:
+		break;
+	default:
+		return NONSEC_ERR_NO_GIC_ADDRESS;
+	}
+
+	/* get the GIC base address from the CBAR register */
+	asm("mrc p15, 4, %0, c15, c0, 0\n" : "=r" (periphbase));
+
+	/* the PERIPHBASE can be mapped above 4 GB (lower 8 bits used to
+	 * encode this). Bail out here since we cannot access this without
+	 * enabling paging.
+	 */
+	if ((periphbase & 0xff) != 0)
+		return NONSEC_ERR_GIC_ADDRESS_ABOVE_4GB;
+
+	*gicdaddr = periphbase + GIC_DIST_OFFSET;
+
+	return 0;
+#endif
+}
+
+enum nonsec_virt_errors armv7_switch_nonsec(void)
+{
+	unsigned int reg, ret;
+	unsigned int gicdaddr = 0;
+	unsigned itlinesnr, i;
+
+	/* check whether the CPU supports the security extensions */
+	reg = read_id_pfr1();
+	if ((reg & 0xF0) == 0)
+		return NONSEC_ERR_NO_SEC_EXT;
+
+	/* the SCR register will be set directly in the monitor mode handler,
+	 * according to the spec one should not tinker with it in secure state
+	 * in SVC mode. Do not try to read it once in non-secure state,
+	 * any access to it will trap.
+	 */
+
+	ret = get_gicd_base_address(&gicdaddr);
+	if (ret != 0)
+		return ret;
+
+	/* enable the GIC distributor */
+	writel(readl(gicdaddr + GICD_CTLR) | 0x03, gicdaddr + GICD_CTLR);
+
+	/* TYPER[4:0] contains an encoded number of available interrupts */
+	itlinesnr = readl(gicdaddr + GICD_TYPER) & 0x1f;
+
+	/* set all bits in the GIC group registers to one to allow access
+	 * from non-secure state
+	 */
+	for (i = 0; i <= itlinesnr; i++)
+		writel((unsigned)-1, gicdaddr + GICD_IGROUPRn + 4 * i);
+
+	/* call the non-sec switching code on this CPU */
+	_nonsec_init();
+
+	return NONSEC_VIRT_SUCCESS;
+}
diff --git a/arch/arm/include/asm/armv7.h b/arch/arm/include/asm/armv7.h
index ab9fa58..e5c0279 100644
--- a/arch/arm/include/asm/armv7.h
+++ b/arch/arm/include/asm/armv7.h
@@ -90,6 +90,16 @@  void v7_outer_cache_flush_range(u32 start, u32 end);
 void v7_outer_cache_inval_range(u32 start, u32 end);
 
 #ifdef CONFIG_ARMV7_NONSEC
+
+enum nonsec_virt_errors {
+	NONSEC_VIRT_SUCCESS,
+	NONSEC_ERR_NO_SEC_EXT,
+	NONSEC_ERR_NO_GIC_ADDRESS,
+	NONSEC_ERR_GIC_ADDRESS_ABOVE_4GB,
+};
+
+enum nonsec_virt_errors armv7_switch_nonsec(void);
+
 /* defined in assembly file */
 unsigned int _nonsec_init(void);
 #endif /* CONFIG_ARMV7_NONSEC */
diff --git a/arch/arm/lib/bootm.c b/arch/arm/lib/bootm.c
index 1b6e0ac..7b0619e 100644
--- a/arch/arm/lib/bootm.c
+++ b/arch/arm/lib/bootm.c
@@ -34,6 +34,10 @@ 
 #include <asm/bootm.h>
 #include <linux/compiler.h>
 
+#ifdef CONFIG_ARMV7_NONSEC
+#include <asm/armv7.h>
+#endif
+
 DECLARE_GLOBAL_DATA_PTR;
 
 static struct tag *params;
@@ -186,6 +190,29 @@  static void setup_end_tag(bd_t *bd)
 
 __weak void setup_board_tags(struct tag **in_params) {}
 
+static void do_nonsec_virt_switch(void)
+{
+#ifdef CONFIG_ARMV7_NONSEC
+	int ret;
+
+	ret = armv7_switch_nonsec();
+	switch (ret) {
+	case NONSEC_VIRT_SUCCESS:
+		debug("entered non-secure state\n");
+		break;
+	case NONSEC_ERR_NO_SEC_EXT:
+		printf("nonsec: Security extensions not implemented.\n");
+		break;
+	case NONSEC_ERR_NO_GIC_ADDRESS:
+		printf("nonsec: could not determine GIC address.\n");
+		break;
+	case NONSEC_ERR_GIC_ADDRESS_ABOVE_4GB:
+		printf("nonsec: PERIPHBASE is above 4 GB, no access.\n");
+		break;
+	}
+#endif
+}
+
 /* Subcommand: PREP */
 static void boot_prep_linux(bootm_headers_t *images)
 {
@@ -222,6 +249,7 @@  static void boot_prep_linux(bootm_headers_t *images)
 		printf("FDT and ATAGS support not compiled in - hanging\n");
 		hang();
 	}
+	do_nonsec_virt_switch();
 }
 
 /* Subcommand: GO */