diff mbox series

[v2] ARM: Fix Keystone 2 kernel mapping regression

Message ID 20210804192759.750112-1-linus.walleij@linaro.org (mailing list archive)
State New, archived
Headers show
Series [v2] ARM: Fix Keystone 2 kernel mapping regression | expand

Commit Message

Linus Walleij Aug. 4, 2021, 7:27 p.m. UTC
This fixes a Keystone 2 regression discovered as a side effect of
defining an passing the physical start/end sections of the kernel
to the MMU remapping code.

As the Keystone applies an offset to all physical addresses,
including those identified and patches by phys2virt, we fail to
account for this offset in the kernel_sec_start and kernel_sec_end
variables.

Further these offsets can extend into the 64bit range on LPAE
systems such as the Keystone 2.

Fix it like this:
- Extend kernel_sec_start and kernel_sec_end to be 64bit
- Add the offset also to kernel_sec_start and kernel_sec_end

As passing kernel_sec_start and kernel_sec_end as 64bit invariably
incurs BE8 endianness issues I have attempted to dry-code around
these.

Tested on the Vexpress QEMU model both with and without LPAE
enabled.

Fixes: 6e121df14ccd ("ARM: 9090/1: Map the lowmem and kernel separately")
Reported-by: Nishanth Menon <nmenon@kernel.org>
Suggested-by: Russell King <linux@armlinux.org.uk>
Tested-by: Grygorii Strashko <grygorii.strashko@ti.com>
Tested-by: Nishanth Menon <nmenon@kernel.org>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
ChangeLog v1->v2:
- Better assembly, pass the #4 addition along in the str instruction
  str r8, [r5, #4]
- Collect Tested-by tags from successful boot tests on Keystone 2.
- I will put this into Russell's patch tracker immediately.
---
 arch/arm/include/asm/memory.h |  7 ++++---
 arch/arm/kernel/head.S        | 17 ++++++++++++++---
 arch/arm/mm/mmu.c             |  9 ++++++++-
 arch/arm/mm/pv-fixup-asm.S    |  2 +-
 4 files changed, 27 insertions(+), 8 deletions(-)

Comments

Nishanth Menon Aug. 4, 2021, 10:19 p.m. UTC | #1
On 21:27-20210804, Linus Walleij wrote:
> This fixes a Keystone 2 regression discovered as a side effect of
> defining an passing the physical start/end sections of the kernel
> to the MMU remapping code.
> 
> As the Keystone applies an offset to all physical addresses,
> including those identified and patches by phys2virt, we fail to
> account for this offset in the kernel_sec_start and kernel_sec_end
> variables.
> 
> Further these offsets can extend into the 64bit range on LPAE
> systems such as the Keystone 2.
> 
> Fix it like this:
> - Extend kernel_sec_start and kernel_sec_end to be 64bit
> - Add the offset also to kernel_sec_start and kernel_sec_end
> 
> As passing kernel_sec_start and kernel_sec_end as 64bit invariably
> incurs BE8 endianness issues I have attempted to dry-code around
> these.
> 
> Tested on the Vexpress QEMU model both with and without LPAE
> enabled.
> 
> Fixes: 6e121df14ccd ("ARM: 9090/1: Map the lowmem and kernel separately")
> Reported-by: Nishanth Menon <nmenon@kernel.org>
> Suggested-by: Russell King <linux@armlinux.org.uk>
> Tested-by: Grygorii Strashko <grygorii.strashko@ti.com>
> Tested-by: Nishanth Menon <nmenon@kernel.org>
> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
> Signed-off-by: Nishanth Menon <nm@ti.com>
> Link: https://lore.kernel.org/r/20210804192759.750112-1-linus.walleij@linaro.org
> ---
> ChangeLog v1->v2:
> - Better assembly, pass the #4 addition along in the str instruction
>   str r8, [r5, #4]
> - Collect Tested-by tags from successful boot tests on Keystone 2.
> - I will put this into Russell's patch tracker immediately.
> ---

Recheck still looks good:
k2g-ice: https://pastebin.ubuntu.com/p/xchg8F6T8z/
k2g-evm: https://pastebin.ubuntu.com/p/zrJCQhVdrk/
k2hk-evm: https://pastebin.ubuntu.com/p/zj5qwkNppv/


Thanks.
Robin Murphy Aug. 5, 2021, 12:25 p.m. UTC | #2
On 2021-08-04 20:27, Linus Walleij wrote:
> This fixes a Keystone 2 regression discovered as a side effect of
> defining an passing the physical start/end sections of the kernel
> to the MMU remapping code.
> 
> As the Keystone applies an offset to all physical addresses,
> including those identified and patches by phys2virt, we fail to
> account for this offset in the kernel_sec_start and kernel_sec_end
> variables.
> 
> Further these offsets can extend into the 64bit range on LPAE
> systems such as the Keystone 2.
> 
> Fix it like this:
> - Extend kernel_sec_start and kernel_sec_end to be 64bit
> - Add the offset also to kernel_sec_start and kernel_sec_end
> 
> As passing kernel_sec_start and kernel_sec_end as 64bit invariably
> incurs BE8 endianness issues I have attempted to dry-code around
> these.
> 
> Tested on the Vexpress QEMU model both with and without LPAE
> enabled.
> 
> Fixes: 6e121df14ccd ("ARM: 9090/1: Map the lowmem and kernel separately")
> Reported-by: Nishanth Menon <nmenon@kernel.org>
> Suggested-by: Russell King <linux@armlinux.org.uk>
> Tested-by: Grygorii Strashko <grygorii.strashko@ti.com>
> Tested-by: Nishanth Menon <nmenon@kernel.org>
> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
> ---
> ChangeLog v1->v2:
> - Better assembly, pass the #4 addition along in the str instruction
>    str r8, [r5, #4]
> - Collect Tested-by tags from successful boot tests on Keystone 2.
> - I will put this into Russell's patch tracker immediately.
> ---
>   arch/arm/include/asm/memory.h |  7 ++++---
>   arch/arm/kernel/head.S        | 17 ++++++++++++++---
>   arch/arm/mm/mmu.c             |  9 ++++++++-
>   arch/arm/mm/pv-fixup-asm.S    |  2 +-
>   4 files changed, 27 insertions(+), 8 deletions(-)
> 
> diff --git a/arch/arm/include/asm/memory.h b/arch/arm/include/asm/memory.h
> index cfc9dfd70aad..f673e13e0f94 100644
> --- a/arch/arm/include/asm/memory.h
> +++ b/arch/arm/include/asm/memory.h
> @@ -160,10 +160,11 @@ extern unsigned long vectors_base;
>   
>   /*
>    * Physical start and end address of the kernel sections. These addresses are
> - * 2MB-aligned to match the section mappings placed over the kernel.
> + * 2MB-aligned to match the section mappings placed over the kernel. We use
> + * u64 so that LPAE mappings beyond the 32bit limit will work out as well.
>    */
> -extern u32 kernel_sec_start;
> -extern u32 kernel_sec_end;
> +extern u64 kernel_sec_start;
> +extern u64 kernel_sec_end;
>   
>   /*
>    * Physical vs virtual RAM address space conversion.  These are
> diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S
> index 9eb0b4dbcc12..b6fc92d4cf23 100644
> --- a/arch/arm/kernel/head.S
> +++ b/arch/arm/kernel/head.S
> @@ -49,7 +49,8 @@
>   
>   	/*
>   	 * This needs to be assigned at runtime when the linker symbols are
> -	 * resolved.
> +	 * resolved. These are unsigned 64bit really, but in this assembly code
> +	 * We store them as 32bit.
>   	 */
>   	.pushsection .data
>   	.align	2
> @@ -57,7 +58,9 @@
>   	.globl	kernel_sec_end
>   kernel_sec_start:
>   	.long	0
> +	.long	0
>   kernel_sec_end:
> +	.long	0
>   	.long	0
>   	.popsection
>   
> @@ -250,7 +253,11 @@ __create_page_tables:
>   	add	r0, r4, #KERNEL_OFFSET >> (SECTION_SHIFT - PMD_ORDER)
>   	ldr	r6, =(_end - 1)
>   	adr_l	r5, kernel_sec_start		@ _pa(kernel_sec_start)
> -	str	r8, [r5]			@ Save physical start of kernel
> +#ifdef CONFIG_CPU_ENDIAN_BE8
> +	str	r8, [r5, #4]			@ Save physical start of kernel (BE)
> +#else
> +	str	r8, [r5]			@ Save physical start of kernel (LE)
> +#endif
>   	orr	r3, r8, r7			@ Add the MMU flags
>   	add	r6, r4, r6, lsr #(SECTION_SHIFT - PMD_ORDER)
>   1:	str	r3, [r0], #1 << PMD_ORDER
> @@ -259,7 +266,11 @@ __create_page_tables:
>   	bls	1b
>   	eor	r3, r3, r7			@ Remove the MMU flags
>   	adr_l	r5, kernel_sec_end		@ _pa(kernel_sec_end)
> -	str	r3, [r5]			@ Save physical end of kernel
> +#ifdef CONFIG_CPU_ENDIAN_BE8
> +	str	r8, [r5, #4]			@ Save physical end of kernel (BE)

For the benefit of the one person who tries running a BE kernel about 3 
years from now, that probably wants to be r3 ;)

Robin.

> +#else
> +	str	r3, [r5]			@ Save physical end of kernel (LE)
> +#endif
>   
>   #ifdef CONFIG_XIP_KERNEL
>   	/*
> diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c
> index 7583bda5ea7d..a4e006005107 100644
> --- a/arch/arm/mm/mmu.c
> +++ b/arch/arm/mm/mmu.c
> @@ -1608,6 +1608,13 @@ static void __init early_paging_init(const struct machine_desc *mdesc)
>   	if (offset == 0)
>   		return;
>   
> +	/*
> +	 * Offset the kernel section physical offsets so that the kernel
> +	 * mapping will work out later on.
> +	 */
> +	kernel_sec_start += offset;
> +	kernel_sec_end += offset;
> +
>   	/*
>   	 * Get the address of the remap function in the 1:1 identity
>   	 * mapping setup by the early page table assembly code.  We
> @@ -1716,7 +1723,7 @@ void __init paging_init(const struct machine_desc *mdesc)
>   {
>   	void *zero_page;
>   
> -	pr_debug("physical kernel sections: 0x%08x-0x%08x\n",
> +	pr_debug("physical kernel sections: 0x%08llx-0x%08llx\n",
>   		 kernel_sec_start, kernel_sec_end);
>   
>   	prepare_page_table();
> diff --git a/arch/arm/mm/pv-fixup-asm.S b/arch/arm/mm/pv-fixup-asm.S
> index 5c5e1952000a..f8e11f7c7880 100644
> --- a/arch/arm/mm/pv-fixup-asm.S
> +++ b/arch/arm/mm/pv-fixup-asm.S
> @@ -29,7 +29,7 @@ ENTRY(lpae_pgtables_remap_asm)
>   	ldr	r6, =(_end - 1)
>   	add	r7, r2, #0x1000
>   	add	r6, r7, r6, lsr #SECTION_SHIFT - L2_ORDER
> -	add	r7, r7, #PAGE_OFFSET >> (SECTION_SHIFT - L2_ORDER)
> +	add	r7, r7, #KERNEL_OFFSET >> (SECTION_SHIFT - L2_ORDER)
>   1:	ldrd	r4, r5, [r7]
>   	adds	r4, r4, r0
>   	adc	r5, r5, r1
>
diff mbox series

Patch

diff --git a/arch/arm/include/asm/memory.h b/arch/arm/include/asm/memory.h
index cfc9dfd70aad..f673e13e0f94 100644
--- a/arch/arm/include/asm/memory.h
+++ b/arch/arm/include/asm/memory.h
@@ -160,10 +160,11 @@  extern unsigned long vectors_base;
 
 /*
  * Physical start and end address of the kernel sections. These addresses are
- * 2MB-aligned to match the section mappings placed over the kernel.
+ * 2MB-aligned to match the section mappings placed over the kernel. We use
+ * u64 so that LPAE mappings beyond the 32bit limit will work out as well.
  */
-extern u32 kernel_sec_start;
-extern u32 kernel_sec_end;
+extern u64 kernel_sec_start;
+extern u64 kernel_sec_end;
 
 /*
  * Physical vs virtual RAM address space conversion.  These are
diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S
index 9eb0b4dbcc12..b6fc92d4cf23 100644
--- a/arch/arm/kernel/head.S
+++ b/arch/arm/kernel/head.S
@@ -49,7 +49,8 @@ 
 
 	/*
 	 * This needs to be assigned at runtime when the linker symbols are
-	 * resolved.
+	 * resolved. These are unsigned 64bit really, but in this assembly code
+	 * We store them as 32bit.
 	 */
 	.pushsection .data
 	.align	2
@@ -57,7 +58,9 @@ 
 	.globl	kernel_sec_end
 kernel_sec_start:
 	.long	0
+	.long	0
 kernel_sec_end:
+	.long	0
 	.long	0
 	.popsection
 
@@ -250,7 +253,11 @@  __create_page_tables:
 	add	r0, r4, #KERNEL_OFFSET >> (SECTION_SHIFT - PMD_ORDER)
 	ldr	r6, =(_end - 1)
 	adr_l	r5, kernel_sec_start		@ _pa(kernel_sec_start)
-	str	r8, [r5]			@ Save physical start of kernel
+#ifdef CONFIG_CPU_ENDIAN_BE8
+	str	r8, [r5, #4]			@ Save physical start of kernel (BE)
+#else
+	str	r8, [r5]			@ Save physical start of kernel (LE)
+#endif
 	orr	r3, r8, r7			@ Add the MMU flags
 	add	r6, r4, r6, lsr #(SECTION_SHIFT - PMD_ORDER)
 1:	str	r3, [r0], #1 << PMD_ORDER
@@ -259,7 +266,11 @@  __create_page_tables:
 	bls	1b
 	eor	r3, r3, r7			@ Remove the MMU flags
 	adr_l	r5, kernel_sec_end		@ _pa(kernel_sec_end)
-	str	r3, [r5]			@ Save physical end of kernel
+#ifdef CONFIG_CPU_ENDIAN_BE8
+	str	r8, [r5, #4]			@ Save physical end of kernel (BE)
+#else
+	str	r3, [r5]			@ Save physical end of kernel (LE)
+#endif
 
 #ifdef CONFIG_XIP_KERNEL
 	/*
diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c
index 7583bda5ea7d..a4e006005107 100644
--- a/arch/arm/mm/mmu.c
+++ b/arch/arm/mm/mmu.c
@@ -1608,6 +1608,13 @@  static void __init early_paging_init(const struct machine_desc *mdesc)
 	if (offset == 0)
 		return;
 
+	/*
+	 * Offset the kernel section physical offsets so that the kernel
+	 * mapping will work out later on.
+	 */
+	kernel_sec_start += offset;
+	kernel_sec_end += offset;
+
 	/*
 	 * Get the address of the remap function in the 1:1 identity
 	 * mapping setup by the early page table assembly code.  We
@@ -1716,7 +1723,7 @@  void __init paging_init(const struct machine_desc *mdesc)
 {
 	void *zero_page;
 
-	pr_debug("physical kernel sections: 0x%08x-0x%08x\n",
+	pr_debug("physical kernel sections: 0x%08llx-0x%08llx\n",
 		 kernel_sec_start, kernel_sec_end);
 
 	prepare_page_table();
diff --git a/arch/arm/mm/pv-fixup-asm.S b/arch/arm/mm/pv-fixup-asm.S
index 5c5e1952000a..f8e11f7c7880 100644
--- a/arch/arm/mm/pv-fixup-asm.S
+++ b/arch/arm/mm/pv-fixup-asm.S
@@ -29,7 +29,7 @@  ENTRY(lpae_pgtables_remap_asm)
 	ldr	r6, =(_end - 1)
 	add	r7, r2, #0x1000
 	add	r6, r7, r6, lsr #SECTION_SHIFT - L2_ORDER
-	add	r7, r7, #PAGE_OFFSET >> (SECTION_SHIFT - L2_ORDER)
+	add	r7, r7, #KERNEL_OFFSET >> (SECTION_SHIFT - L2_ORDER)
 1:	ldrd	r4, r5, [r7]
 	adds	r4, r4, r0
 	adc	r5, r5, r1