diff mbox

arm: tcm: Don't crash when TCM banks are protected by TrustZone

Message ID 1432805760-4590-1-git-send-email-michael@smart-africa.com (mailing list archive)
State New, archived
Headers show

Commit Message

Michael van der Westhuizen May 28, 2015, 9:36 a.m. UTC
Fixes the TCM initialisation code to handle TCM banks that are
present but inaccessible due to TrustZone configuration.  This is
the default case when enabling the non-secure world.  It may also
be the case that that the user decided to use TCM for TrustZone.

This change has exposed a bug in handling of TCM where no TCM bank
was usable (the 0 size TCM case).  This change addresses the
resulting hang.

Signed-off-by: Michael van der Westhuizen <michael@smart-africa.com>
Cc: Linus Walleij <linus.walleij@linaro.org>
Cc: Russell King <rmk+kernel@arm.linux.org.uk>
---
 arch/arm/kernel/tcm.c | 35 ++++++++++++++++++++++++++++++++---
 1 file changed, 32 insertions(+), 3 deletions(-)

Comments

Dave Martin May 28, 2015, 10:16 a.m. UTC | #1
On Thu, May 28, 2015 at 11:36:00AM +0200, Michael van der Westhuizen wrote:
> Fixes the TCM initialisation code to handle TCM banks that are
> present but inaccessible due to TrustZone configuration.  This is
> the default case when enabling the non-secure world.  It may also
> be the case that that the user decided to use TCM for TrustZone.
> 
> This change has exposed a bug in handling of TCM where no TCM bank
> was usable (the 0 size TCM case).  This change addresses the
> resulting hang.

The TCM registers in CP15 are not part of the architecture -- behaviour
is IMP DEF in v7.

This is a problem for multiplatform kernels in particular.  In a v6/v7
multiplatform kernel with a TCM-enabled platform built in, it is
unsafe to probe for TCM by accessing these registers if we are running
on v7.  No Undef exception is guaranteed -- anything might happen.

We could add a DT binding for TCM, but it should only describe whether
the CP15 TCM registers are accessible (node present in DT) or not (node
present, but disabled).

For backwards compatiblity with old DTs we could maybe fall back to
unconditional probing for single-platform kernels only, when no tcm
node is present in the DT.

Cheers
---Dave

[...]
Michael van der Westhuizen May 28, 2015, 11:32 a.m. UTC | #2
> On 28 May 2015, at 12:16 PM, Dave Martin <Dave.Martin@arm.com> wrote:
> 
> On Thu, May 28, 2015 at 11:36:00AM +0200, Michael van der Westhuizen wrote:
>> Fixes the TCM initialisation code to handle TCM banks that are
>> present but inaccessible due to TrustZone configuration.  This is
>> the default case when enabling the non-secure world.  It may also
>> be the case that that the user decided to use TCM for TrustZone.
>> 
>> This change has exposed a bug in handling of TCM where no TCM bank
>> was usable (the 0 size TCM case).  This change addresses the
>> resulting hang.
> 
> The TCM registers in CP15 are not part of the architecture -- behaviour
> is IMP DEF in v7.

My reading of DDI0406C_C is that the register is defined (CP15, c0, c0, 2), but the format is either v6 format or implementation defined.

The manual explicitly states that in v7 the register must be implemented and that when v7 format is used that the meaning of bits 28:0 is implementation defined (this is all in B4.1.132).

The ARM goes on to state that when no TCMs are implemented the TCMTR register must be implemented in ARMv6 format, indicating no TCM banks (i.e. all defined bits must be 0).

So, since this code assumes v6 format should I just add a check that bits 31:29 or 0b000?  If I do this, then my reading is that this will continue to work reliably in the face of v7 implementations that use v7 (implementation defined) format.

Michael

> 
> This is a problem for multiplatform kernels in particular.  In a v6/v7
> multiplatform kernel with a TCM-enabled platform built in, it is
> unsafe to probe for TCM by accessing these registers if we are running
> on v7.  No Undef exception is guaranteed -- anything might happen.
> 
> We could add a DT binding for TCM, but it should only describe whether
> the CP15 TCM registers are accessible (node present in DT) or not (node
> present, but disabled).
> 
> For backwards compatiblity with old DTs we could maybe fall back to
> unconditional probing for single-platform kernels only, when no tcm
> node is present in the DT.
> 
> Cheers
> ---Dave
> 
> [...]
>
Dave Martin May 28, 2015, 1:32 p.m. UTC | #3
On Thu, May 28, 2015 at 01:32:10PM +0200, Michael van der Westhuizen wrote:
> 
> > On 28 May 2015, at 12:16 PM, Dave Martin <Dave.Martin@arm.com> wrote:
> > 
> > On Thu, May 28, 2015 at 11:36:00AM +0200, Michael van der Westhuizen wrote:
> >> Fixes the TCM initialisation code to handle TCM banks that are
> >> present but inaccessible due to TrustZone configuration.  This is
> >> the default case when enabling the non-secure world.  It may also
> >> be the case that that the user decided to use TCM for TrustZone.
> >> 
> >> This change has exposed a bug in handling of TCM where no TCM bank
> >> was usable (the 0 size TCM case).  This change addresses the
> >> resulting hang.
> > 
> > The TCM registers in CP15 are not part of the architecture -- behaviour
> > is IMP DEF in v7.
> 
> My reading of DDI0406C_C is that the register is defined (CP15, c0,
> c0, 2), but the format is either v6 format or implementation defined.
> 
> The manual explicitly states that in v7 the register must be
> implemented and that when v7 format is used that the meaning of bits
> 28:0 is implementation defined (this is all in B4.1.132).
> 
> The ARM goes on to state that when no TCMs are implemented the TCMTR
> register must be implemented in ARMv6 format, indicating no TCM banks
> (i.e. all defined bits must be 0).
> 
> So, since this code assumes v6 format should I just add a check that
> bits 31:29 or 0b000?  If I do this, then my reading is that this will
> continue to work reliably in the face of v7 implementations that use
> v7 (implementation defined) format.

You're right, that looks sound.

Providing that TCMTR is read first and reports v6 format, then access
to the region registers will either succeed safely, or Undef (when
disallowed by the Secure World).

TCMTR itself is guaranteed to be readable even in the ARMv6 base
architecture.

ARMv8 gives mixed messages on this point[1], but it appears[2] that
the intention is for the above check to continue to work.

[1] DDI0487A.e, G6.2.126 (TCMTR, TCM Type Register)
[2] DDI0487A.e, G3.5 (System register support for IMPLEMENTATION DEFINED
memory features)


It would be good to see testing of this in a multiplatform kernel,
assuming you haven't tried it already.

[...]

Cheers
---Dave
Michael van der Westhuizen May 28, 2015, 1:37 p.m. UTC | #4
> On 28 May 2015, at 3:32 PM, Dave Martin <Dave.Martin@arm.com> wrote:
> 
> On Thu, May 28, 2015 at 01:32:10PM +0200, Michael van der Westhuizen wrote:
>> 
>>> On 28 May 2015, at 12:16 PM, Dave Martin <Dave.Martin@arm.com> wrote:
>>> 
>>> On Thu, May 28, 2015 at 11:36:00AM +0200, Michael van der Westhuizen wrote:
>>>> Fixes the TCM initialisation code to handle TCM banks that are
>>>> present but inaccessible due to TrustZone configuration.  This is
>>>> the default case when enabling the non-secure world.  It may also
>>>> be the case that that the user decided to use TCM for TrustZone.
>>>> 
>>>> This change has exposed a bug in handling of TCM where no TCM bank
>>>> was usable (the 0 size TCM case).  This change addresses the
>>>> resulting hang.
>>> 
>>> The TCM registers in CP15 are not part of the architecture -- behaviour
>>> is IMP DEF in v7.
>> 
>> My reading of DDI0406C_C is that the register is defined (CP15, c0,
>> c0, 2), but the format is either v6 format or implementation defined.
>> 
>> The manual explicitly states that in v7 the register must be
>> implemented and that when v7 format is used that the meaning of bits
>> 28:0 is implementation defined (this is all in B4.1.132).
>> 
>> The ARM goes on to state that when no TCMs are implemented the TCMTR
>> register must be implemented in ARMv6 format, indicating no TCM banks
>> (i.e. all defined bits must be 0).
>> 
>> So, since this code assumes v6 format should I just add a check that
>> bits 31:29 or 0b000?  If I do this, then my reading is that this will
>> continue to work reliably in the face of v7 implementations that use
>> v7 (implementation defined) format.
> 
> You're right, that looks sound.

I’ll resend with this check added.

> 
> Providing that TCMTR is read first and reports v6 format, then access
> to the region registers will either succeed safely, or Undef (when
> disallowed by the Secure World).
> 
> TCMTR itself is guaranteed to be readable even in the ARMv6 base
> architecture.
> 
> ARMv8 gives mixed messages on this point[1], but it appears[2] that
> the intention is for the above check to continue to work.
> 
> [1] DDI0487A.e, G6.2.126 (TCMTR, TCM Type Register)
> [2] DDI0487A.e, G3.5 (System register support for IMPLEMENTATION DEFINED
> memory features)
> 
> 
> It would be good to see testing of this in a multiplatform kernel,
> assuming you haven't tried it already.

I’m afraid that I only have one (V6K) board to test against.

Michael

> 
> [...]
> 
> Cheers
> ---Dave
>
diff mbox

Patch

diff --git a/arch/arm/kernel/tcm.c b/arch/arm/kernel/tcm.c
index 7a3be1d..af1dd5a 100644
--- a/arch/arm/kernel/tcm.c
+++ b/arch/arm/kernel/tcm.c
@@ -17,6 +17,7 @@ 
 #include <asm/mach/map.h>
 #include <asm/memory.h>
 #include <asm/system_info.h>
+#include <asm/traps.h>
 
 static struct gen_pool *tcm_pool;
 static bool dtcm_present;
@@ -175,6 +176,21 @@  static int __init setup_tcm_bank(u8 type, u8 bank, u8 banks,
 	return 0;
 }
 
+static int __init tcm_handler(struct pt_regs *regs, unsigned int instr)
+{
+	regs->uregs[(instr >> 12) & 0xf] = 0;
+	regs->ARM_pc += 4;
+	return 0;
+}
+
+static const struct undef_hook tcm_hook __initconst = {
+	.instr_mask	= 0xffff0fdf,
+	.instr_val	= 0xee190f11,
+	.cpsr_mask	= MODE_MASK,
+	.cpsr_val	= SVC_MODE,
+	.fn		= tcm_handler
+};
+
 /*
  * This initializes the TCM memory
  */
@@ -207,6 +223,8 @@  void __init tcm_init(void)
 	dtcm_banks = (tcm_status >> 16) & 0x03;
 	itcm_banks = (tcm_status & 0x03);
 
+	register_undef_hook(&tcm_hook);
+
 	/* Values greater than 2 for D/ITCM banks are "reserved" */
 	if (dtcm_banks > 2)
 		dtcm_banks = 0;
@@ -218,7 +236,7 @@  void __init tcm_init(void)
 		for (i = 0; i < dtcm_banks; i++) {
 			ret = setup_tcm_bank(0, i, dtcm_banks, &dtcm_end);
 			if (ret)
-				return;
+				goto unregister;
 		}
 		/* This means you compiled more code than fits into DTCM */
 		if (dtcm_code_sz > (dtcm_end - DTCM_OFFSET)) {
@@ -227,6 +245,10 @@  void __init tcm_init(void)
 				dtcm_code_sz, (dtcm_end - DTCM_OFFSET));
 			goto no_dtcm;
 		}
+		/* This means that the DTCM sizes were 0 or the DTCM banks
+		 * were inaccessible due to TrustZone configuration */
+		if (!(dtcm_end - DTCM_OFFSET))
+			goto no_dtcm;
 		dtcm_res.end = dtcm_end - 1;
 		request_resource(&iomem_resource, &dtcm_res);
 		dtcm_iomap[0].length = dtcm_end - DTCM_OFFSET;
@@ -250,15 +272,19 @@  no_dtcm:
 		for (i = 0; i < itcm_banks; i++) {
 			ret = setup_tcm_bank(1, i, itcm_banks, &itcm_end);
 			if (ret)
-				return;
+				goto unregister;
 		}
 		/* This means you compiled more code than fits into ITCM */
 		if (itcm_code_sz > (itcm_end - ITCM_OFFSET)) {
 			pr_info("CPU ITCM: %u bytes of code compiled to "
 				"ITCM but only %lu bytes of ITCM present\n",
 				itcm_code_sz, (itcm_end - ITCM_OFFSET));
-			return;
+			goto unregister;
 		}
+		/* This means that the ITCM sizes were 0 or the ITCM banks
+		 * were inaccessible due to TrustZone configuration */
+		if (!(itcm_end - ITCM_OFFSET))
+			goto unregister;
 		itcm_res.end = itcm_end - 1;
 		request_resource(&iomem_resource, &itcm_res);
 		itcm_iomap[0].length = itcm_end - ITCM_OFFSET;
@@ -275,6 +301,9 @@  no_dtcm:
 		pr_info("CPU ITCM: %u bytes of code compiled to ITCM but no "
 			"ITCM banks present in CPU\n", itcm_code_sz);
 	}
+
+unregister:
+	unregister_undef_hook(&tcm_hook);
 }
 
 /*