Skip to contentSkip to conversation

LPAE fixes and extensions for Keystone

PATCH RESEND In review Cyril Chemparathy 5 participants 69 messages Sep 21, 2012 – Sep 25, 2012 Arm (32-bit)

lore
Share & tools
lore
#0 lore raw

v3 cover LPAE fixes and extensions for Keystone

This series is a repost of the LPAE related changes in preparation for the introduction of the Keystone sub-architecture. Earlier postings of this series can be found at [1], [2], and [3].

I would very much appreciate feedback and/or next steps towards including these in linux-next.

These patches are also available in git:
git://git.kernel.org/pub/scm/linux/kernel/git/cchemparathy/linux-keystone.git upstream/keystone-lpae-v3

[1] http://comments.gmane.org/gmane.linux.kernel/1341497
[2] http://comments.gmane.org/gmane.linux.kernel/1332069
[3] http://comments.gmane.org/gmane.linux.kernel/1356716

Series changelog:

(01/22) ARM: add mechanism for late code patching
(v3) ability to patch multiple sequential instructions with the IMM8 patch
type
(v3) error handling at module patch time
(v3) reuse __patch_text() from kprobes code
(v2) pulled runtime patching code into separate source files
(v2) reordered arguments to patch macros for consistency with assembly
"Rd, Rt, imm" ordering
(v2) added support for mov immediate patching
(v2) cache flush patched instructions instead of entire kernel code
(v2) pack patch table to reduce table volume
(v2) add to module vermagic to reflect abi change
(v2) misc. cleanups in naming and structure

(02/22) ARM: add self test for runtime patch mechanism
(v3) added tests for both even and odd shifts of immediate values
(v2) added init-time tests to verify instruction encoding

(03/22) ARM: use late patch framework for phys-virt patching
(v3) fixed commit description for unconditional init of __pv_* symbols
(v2) move __pv_offset and __pv_phys_offset to C code
(v2) restore conditional init of __pv_offset and __pv_phys_offset

(04/22) ARM: LPAE: use phys_addr_t on virt <--> phys conversion
(v3) unchanged from v2
(v2) fix patched __phys_to_virt() to use 32-bit operand
(v2) convert non-patch __phys_to_virt and __virt_to_phys to inlines to retain
type checking

(05/22) ARM: LPAE: support 64-bit virt_to_phys patching
(v3) added explicit patch stub for 64-bit to clean up compiler generated
code, both 64-bit and 32-bit cases generate optimal code with this
(v2) use phys_addr_t instead of split high/low phys_offsets
(v2) use mov immediate instead of add to zero when patching in high order
physical address bits
(v2) fix __pv_phys_offset handling for big-endian
(v2) remove set_phys_offset()

(06/22) ARM: LPAE: use signed arithmetic for mask definitions (07/22) ARM: LPAE: use phys_addr_t in alloc_init_pud() (08/22) ARM: LPAE: use phys_addr_t in free_memmap()
(v3) unchanged from v2
(v2) unchanged from v1

(09/22) ARM: LPAE: use phys_addr_t for initrd location and size
(v3) unchanged from v2
(v2) revert to unsigned long for initrd size

(10/22) ARM: LPAE: use phys_addr_t in switch_mm()
(v3) remove unnecessary handling for !LPAE in proc-v7-3level
(v2) use phys_addr_t instead of u64 in switch_mm()
(v2) revert on changes to v6 and v7-2level
(v2) fix register mapping for big-endian in v7-3level

(11/22) ARM: LPAE: use 64-bit accessors for TTBR registers
(v3) remove unnecessary condition code clobber
(v2) restore comment in cpu_set_reserved_ttbr0()

(12/22) ARM: LPAE: define ARCH_LOW_ADDRESS_LIMIT for bootmem (13/22) ARM: LPAE: factor out T1SZ and TTBR1 computations
(v3) unchanged from v2
(v2) unchanged from v1

(14/22) ARM: LPAE: accomodate >32-bit addresses for page table base
(v3) unchanged from v2
(v2) apply arch_pgd_shift only on lpae
(v2) move arch_pgd_shift definition to asm/memory.h
(v2) revert on changes to non-lpae procs
(v2) add check to ensure that the pgd physical address is aligned at an
ARCH_PGD_SHIFT boundary

(15/22) ARM: mm: use physical addresses in highmem sanity checks (16/22) ARM: mm: cleanup checks for membank overlap with vmalloc area (17/22) ARM: mm: clean up membank size limit checks
(v3) unchanged from v2
(v2) unchanged from v1

Cyril Chemparathy (14):
ARM: add mechanism for late code patching
ARM: add self test for runtime patch mechanism
ARM: use late patch framework for phys-virt patching
ARM: LPAE: use phys_addr_t on virt <--> phys conversion
ARM: LPAE: support 64-bit virt_to_phys patching
ARM: LPAE: use signed arithmetic for mask definitions
ARM: LPAE: use phys_addr_t in switch_mm()
ARM: LPAE: use 64-bit accessors for TTBR registers
ARM: LPAE: define ARCH_LOW_ADDRESS_LIMIT for bootmem
ARM: LPAE: factor out T1SZ and TTBR1 computations
ARM: LPAE: accomodate >32-bit addresses for page table base
ARM: mm: use physical addresses in highmem sanity checks
ARM: mm: cleanup checks for membank overlap with vmalloc area
ARM: mm: clean up membank size limit checks

Vitaly Andrianov (3):
ARM: LPAE: use phys_addr_t in alloc_init_pud()
ARM: LPAE: use phys_addr_t in free_memmap()
ARM: LPAE: use phys_addr_t for initrd location and size

 arch/arm/Kconfig                            |   16 ++
 arch/arm/include/asm/memory.h               |  101 +++++++---
 arch/arm/include/asm/module.h               |    7 +
 arch/arm/include/asm/page.h                 |    2 +-
 arch/arm/include/asm/pgtable-3level-hwdef.h |   10 +
 arch/arm/include/asm/pgtable-3level.h       |    6 +-
 arch/arm/include/asm/proc-fns.h             |   28 ++-
 arch/arm/include/asm/runtime-patch.h        |  208 +++++++++++++++++++++
 arch/arm/kernel/Makefile                    |    1 +
 arch/arm/kernel/armksyms.c                  |    4 -
 arch/arm/kernel/head.S                      |  107 +++--------
 arch/arm/kernel/module.c                    |   14 +-
 arch/arm/kernel/runtime-patch.c             |  268 +++++++++++++++++++++++++++
 arch/arm/kernel/setup.c                     |   15 ++
 arch/arm/kernel/smp.c                       |   11 +-
 arch/arm/kernel/vmlinux.lds.S               |   13 +-
 arch/arm/mm/context.c                       |    9 +-
 arch/arm/mm/init.c                          |   19 +-
 arch/arm/mm/mmu.c                           |   49 ++---
 arch/arm/mm/proc-v7-3level.S                |   41 ++--
 20 files changed, 728 insertions(+), 201 deletions(-)
 create mode 100644 arch/arm/include/asm/runtime-patch.h
 create mode 100644 arch/arm/kernel/runtime-patch.c
Signature · 1 line
1.7.9.5
#1 lore raw

PATCH RESEND v3 1/17 ARM: add mechanism for late code patching

The original phys_to_virt/virt_to_phys patching implementation relied on early patching prior to MMU initialization. On PAE systems running out of >4G address space, this would have entailed an additional round of patching after switching over to the high address space.

The approach implemented here conceptually extends the original PHYS_OFFSET patching implementation with the introduction of "early" patch stubs. Early patch code is required to be functional out of the box, even before the patch is applied. This is implemented by inserting functional (but inefficient) load code into the .runtime.patch.code init section. Having functional code out of the box then allows us to defer the init time patch application until later in the init sequence.

In addition to fitting better with our need for physical address-space switch-over, this implementation should be somewhat more extensible by virtue of its more readable (and hackable) C implementation. This should prove useful for other similar init time specialization needs, especially in light of our multi-platform kernel initiative.

This code has been boot tested in both ARM and Thumb-2 modes on an ARMv7 (Cortex-A8) device.

Note: the obtuse use of stringified symbols in patch_stub() and early_patch_stub() is intentional. Theoretically this should have been accomplished with formal operands passed into the asm block, but this requires the use of the 'c' modifier for instantiating the long (e.g. .long %c0). However, the 'c' modifier has been found to ICE certain versions of GCC, and therefore we resort to stringified symbols here.

---
 arch/arm/Kconfig                     |    3 +
 arch/arm/include/asm/module.h        |    7 ++
 arch/arm/include/asm/runtime-patch.h |  208 ++++++++++++++++++++++++++++++++++
 arch/arm/kernel/Makefile             |    1 +
 arch/arm/kernel/module.c             |    9 +-
 arch/arm/kernel/runtime-patch.c      |  193 +++++++++++++++++++++++++++++++
 arch/arm/kernel/setup.c              |    3 +
 arch/arm/kernel/vmlinux.lds.S        |   10 ++
 8 files changed, 433 insertions(+), 1 deletion(-)
 create mode 100644 arch/arm/include/asm/runtime-patch.h
 create mode 100644 arch/arm/kernel/runtime-patch.c
arch/arm/Kconfig +3 -0
index 2f88d8d..98a3a1a 100644
@@ -60,6 +60,9 @@ config ARM
 config ARM_HAS_SG_CHAIN
 	bool
 
+config ARM_RUNTIME_PATCH
+	bool
+
 config NEED_SG_DMA_LENGTH
 	bool
 
arch/arm/include/asm/module.h +7 -0
index 6c6809f..2090486 100644
@@ -43,9 +43,16 @@ struct mod_arch_specific {
 #define MODULE_ARCH_VERMAGIC_ARMTHUMB ""
 #endif
 
+#ifdef CONFIG_ARM_RUNTIME_PATCH
+#define MODULE_ARCH_VERMAGIC_RT_PATCH "rt-patch "
+#else
+#define MODULE_ARCH_VERMAGIC_RT_PATCH ""
+#endif
+
 #define MODULE_ARCH_VERMAGIC \
 	MODULE_ARCH_VERMAGIC_ARMVSN \
 	MODULE_ARCH_VERMAGIC_ARMTHUMB \
+	MODULE_ARCH_VERMAGIC_RT_PATCH \
 	MODULE_ARCH_VERMAGIC_P2V
 
 #endif /* _ASM_ARM_MODULE_H */
arch/arm/include/asm/runtime-patch.h new file +208 -0

208 lines not on this page · raw message

arch/arm/kernel/Makefile +1 -0
index 7ad2d5c..0b16b94 100644
@@ -81,5 +81,6 @@ endif
 head-y			:= head$(MMUEXT).o
 obj-$(CONFIG_DEBUG_LL)	+= debug.o
 obj-$(CONFIG_EARLY_PRINTK)	+= early_printk.o
+obj-$(CONFIG_ARM_RUNTIME_PATCH)	+= runtime-patch.o patch.o
 
 extra-y := $(head-y) vmlinux.lds
arch/arm/kernel/module.c +8 -1
index 1e9be5d..10a2922 100644
@@ -24,6 +24,7 @@
 #include <asm/sections.h>
 #include <asm/smp_plat.h>
 #include <asm/unwind.h>
+#include <asm/runtime-patch.h>
 
 #ifdef CONFIG_XIP_KERNEL
 /*
@@ -276,7 +277,7 @@ int module_finalize(const Elf32_Ehdr *hdr, const Elf_Shdr *sechdrs,
 	const char *secstrs = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
 	const Elf_Shdr *sechdrs_end = sechdrs + hdr->e_shnum;
 	struct mod_unwind_map maps[ARM_SEC_MAX];
-	int i;
+	int i, err;
 
 	memset(maps, 0, sizeof(maps));
 
@@ -321,6 +322,12 @@ int module_finalize(const Elf32_Ehdr *hdr, const Elf_Shdr *sechdrs,
 	if (s)
 		fixup_pv_table((void *)s->sh_addr, s->sh_size);
 #endif
+	s = find_mod_section(hdr, sechdrs, ".runtime.patch.table");
+	if (s) {
+		err = runtime_patch((void *)s->sh_addr, s->sh_size);
+		if (err)
+			return err;
+	}
 	s = find_mod_section(hdr, sechdrs, ".alt.smp.init");
 	if (s && !is_smp())
 #ifdef CONFIG_SMP_ON_UP
arch/arm/kernel/runtime-patch.c new file +193 -0

193 lines not on this page · raw message

arch/arm/kernel/setup.c +3 -0
index a81dcec..669bbf0 100644
@@ -55,6 +55,7 @@
 #include <asm/traps.h>
 #include <asm/unwind.h>
 #include <asm/memblock.h>
+#include <asm/runtime-patch.h>
 
 #if defined(CONFIG_DEPRECATED_PARAM_STRUCT)
 #include "compat.h"
@@ -998,6 +999,8 @@ void __init setup_arch(char **cmdline_p)
 
 	if (mdesc->init_early)
 		mdesc->init_early();
+
+	runtime_patch_kernel();
 }
 
 
arch/arm/kernel/vmlinux.lds.S +10 -0
index 36ff15b..ea35ca0 100644
@@ -167,6 +167,16 @@ SECTIONS
 		*(.pv_table)
 		__pv_table_end = .;
 	}
+	.init.runtime_patch_table : {
+		__runtime_patch_table_begin = .;
+		*(.runtime.patch.table)
+		__runtime_patch_table_end = .;
+	}
+	.init.runtime_patch_code : {
+		__runtime_patch_code_begin = .;
+		*(.runtime.patch.code)
+		__runtime_patch_code_end = .;
+	}
 	.init.data : {
 #ifndef CONFIG_XIP_KERNEL
 		INIT_DATA
Signature · 1 line
1.7.9.5
#2 lore raw

Re: [PATCH v3 RESEND 01/17] ARM: add mechanism for late code patching

Quoted 29 lines · Cyril Chemparathy

The original phys_to_virt/virt_to_phys patching implementation relied on early patching prior to MMU initialization. On PAE systems running out of >4G address space, this would have entailed an additional round of patching after switching over to the high address space.

The approach implemented here conceptually extends the original PHYS_OFFSET patching implementation with the introduction of "early" patch stubs. Early patch code is required to be functional out of the box, even before the patch is applied. This is implemented by inserting functional (but inefficient) load code into the .runtime.patch.code init section. Having functional code out of the box then allows us to defer the init time patch application until later in the init sequence.

In addition to fitting better with our need for physical address-space switch-over, this implementation should be somewhat more extensible by virtue of its more readable (and hackable) C implementation. This should prove useful for other similar init time specialization needs, especially in light of our multi-platform kernel initiative.

This code has been boot tested in both ARM and Thumb-2 modes on an ARMv7 (Cortex-A8) device.

Note: the obtuse use of stringified symbols in patch_stub() and early_patch_stub() is intentional. Theoretically this should have been accomplished with formal operands passed into the asm block, but this requires the use of the 'c' modifier for instantiating the long (e.g. .long %c0). However, the 'c' modifier has been found to ICE certain versions of GCC, and therefore we resort to stringified symbols here.

Quoted 2 lines

There is another problem with this.

[...]

Quoted 30 lines
arch/arm/include/asm/runtime-patch.h new file +24 -0
new file mode 100644
index 0000000..366444d
@@ -0,0 +1,208 @@
+/*
+ * arch/arm/include/asm/runtime-patch.h
+ * Note: this file should not be included by non-asm/.h files
+ *
+ * Copyright 2012 Texas Instruments, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_ARM_RUNTIME_PATCH_H
+#define __ASM_ARM_RUNTIME_PATCH_H
+
+#include <linux/stringify.h>
+
+#ifndef __ASSEMBLY__

arch/arm/include/asm/runtime-patch.h line 34

Quoted 10 lines
arch/arm/include/asm/runtime-patch.h new file +10 -0
+
+#ifdef CONFIG_ARM_RUNTIME_PATCH
+
+struct patch_info {
+	void		*insn;
+	u16		 type;
+	u8		 insn_size;
+	u8		 data_size;
+	u32		 data[0];
+};

This causes the following compilation error:

CC sound/core/pcm.o
In file included from sound/core/pcm.c:293:0:
include/linux/soundcard.h:223:8: error: redefinition of 'struct patch_info' arch/arm/include/asm/runtime-patch.h:28:8: note: originally defined here make[2]: *** [sound/core/pcm.o] Error 1

The problem is that asm/runtime-patch.h gets included by asm/memory.h and asm/memory.h is included by almost the entire kernel. Something like "struct patch_info" is a bit too generic a name to be exported to the world as the likelihood of a name collision with some private definition in a driver or the like is rather high.

In that context it might be worth moving everything that is not required for the patch stub definitions out of asm/runtime-patch.h. For example, the definition of struct patch_info, struct patch_info_imm8, patch_next() and patch_data() could be moved to runtime-patch.c directly instead. And then patch_stub() should be renamed to runtime_patch_stub(), early_patch_stub() to early_runtime_patch_stub(), patch_imm8() to runtime_patch_imm8(), etc. Even the __IMM8 symbol name is rather weak for kernel wide scope.

Nicolas

#3 lore raw

Re: [PATCH v3 RESEND 01/17] ARM: add mechanism for late code patching

Quoted 92 lines · Nicolas Pitre
Quoted 29 lines · Cyril Chemparathy

The original phys_to_virt/virt_to_phys patching implementation relied on early patching prior to MMU initialization. On PAE systems running out of >4G address space, this would have entailed an additional round of patching after switching over to the high address space.

The approach implemented here conceptually extends the original PHYS_OFFSET patching implementation with the introduction of "early" patch stubs. Early patch code is required to be functional out of the box, even before the patch is applied. This is implemented by inserting functional (but inefficient) load code into the .runtime.patch.code init section. Having functional code out of the box then allows us to defer the init time patch application until later in the init sequence.

In addition to fitting better with our need for physical address-space switch-over, this implementation should be somewhat more extensible by virtue of its more readable (and hackable) C implementation. This should prove useful for other similar init time specialization needs, especially in light of our multi-platform kernel initiative.

This code has been boot tested in both ARM and Thumb-2 modes on an ARMv7 (Cortex-A8) device.

Note: the obtuse use of stringified symbols in patch_stub() and early_patch_stub() is intentional. Theoretically this should have been accomplished with formal operands passed into the asm block, but this requires the use of the 'c' modifier for instantiating the long (e.g. .long %c0). However, the 'c' modifier has been found to ICE certain versions of GCC, and therefore we resort to stringified symbols here.

Quoted 2 lines

There is another problem with this.

[...]

Quoted 30 lines
arch/arm/include/asm/runtime-patch.h new file +24 -0
new file mode 100644
index 0000000..366444d
@@ -0,0 +1,208 @@
+/*
+ * arch/arm/include/asm/runtime-patch.h
+ * Note: this file should not be included by non-asm/.h files
+ *
+ * Copyright 2012 Texas Instruments, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_ARM_RUNTIME_PATCH_H
+#define __ASM_ARM_RUNTIME_PATCH_H
+
+#include <linux/stringify.h>
+
+#ifndef __ASSEMBLY__

arch/arm/include/asm/runtime-patch.h line 34

Quoted 10 lines
arch/arm/include/asm/runtime-patch.h new file +10 -0
+
+#ifdef CONFIG_ARM_RUNTIME_PATCH
+
+struct patch_info {
+	void		*insn;
+	u16		 type;
+	u8		 insn_size;
+	u8		 data_size;
+	u32		 data[0];
+};

This causes the following compilation error:

CC sound/core/pcm.o
In file included from sound/core/pcm.c:293:0:
include/linux/soundcard.h:223:8: error: redefinition of 'struct patch_info' arch/arm/include/asm/runtime-patch.h:28:8: note: originally defined here make[2]: *** [sound/core/pcm.o] Error 1

The problem is that asm/runtime-patch.h gets included by asm/memory.h and asm/memory.h is included by almost the entire kernel. Something like "struct patch_info" is a bit too generic a name to be exported to the world as the likelihood of a name collision with some private definition in a driver or the like is rather high.

Quoted 9 lines

In that context it might be worth moving everything that is not required for the patch stub definitions out of asm/runtime-patch.h. For example, the definition of struct patch_info, struct patch_info_imm8, patch_next() and patch_data() could be moved to runtime-patch.c directly instead. And then patch_stub() should be renamed to runtime_patch_stub(), early_patch_stub() to early_runtime_patch_stub(), patch_imm8() to runtime_patch_imm8(), etc. Even the __IMM8 symbol name is rather weak for kernel wide scope.

Thanks for trying this out. I'll take this up in the next rev.

-- Cyril.

#4 lore raw

Re: [PATCH v3 RESEND 01/17] ARM: add mechanism for late code patching

In reply to Cyril Chemparathy
Quoted 5 lines · Cyril Chemparathy

The original phys_to_virt/virt_to_phys patching implementation relied on early patching prior to MMU initialization. On PAE systems running out of >4G address space, this would have entailed an additional round of patching after switching over to the high address space.

Quoted 7 lines

The approach implemented here conceptually extends the original PHYS_OFFSET patching implementation with the introduction of "early" patch stubs. Early patch code is required to be functional out of the box, even before the patch is applied. This is implemented by inserting functional (but inefficient) load code into the .runtime.patch.code init section. Having functional code out of the box then allows us to defer the init time patch application until later in the init sequence.

There are currently a few different patching mechanisms in the kernel, and it would be good if we could collect more of them under some common framework.

For example, it might be possible to do the SMP-on-UP fixups using the same framework you propose. Best not to attempt that yet, though.

Overall, this looks well thought out and useful, though it looks like it has a few issues that need attention.

Comments below.

Cheers
---Dave

Quoted 9 lines

In addition to fitting better with our need for physical address-space switch-over, this implementation should be somewhat more extensible by virtue of its more readable (and hackable) C implementation. This should prove useful for other similar init time specialization needs, especially in light of our multi-platform kernel initiative.

This code has been boot tested in both ARM and Thumb-2 modes on an ARMv7 (Cortex-A8) device.

Quoted 6 lines

Note: the obtuse use of stringified symbols in patch_stub() and early_patch_stub() is intentional. Theoretically this should have been accomplished with formal operands passed into the asm block, but this requires the use of the 'c' modifier for instantiating the long (e.g. .long %c0). However, the 'c' modifier has been found to ICE certain versions of GCC, and therefore we resort to stringified symbols here.

You might find that the "n" constraint works. The explanation in the GCC docs is pretty incomprehensible, but at least it exists.

__stringify hacks are not uncommon anyway though, so it's not a problem either way.

Quoted 113 lines
---
 arch/arm/Kconfig                     |    3 +
 arch/arm/include/asm/module.h        |    7 ++
 arch/arm/include/asm/runtime-patch.h |  208 ++++++++++++++++++++++++++++++++++
 arch/arm/kernel/Makefile             |    1 +
 arch/arm/kernel/module.c             |    9 +-
 arch/arm/kernel/runtime-patch.c      |  193 +++++++++++++++++++++++++++++++
 arch/arm/kernel/setup.c              |    3 +
 arch/arm/kernel/vmlinux.lds.S        |   10 ++
 8 files changed, 433 insertions(+), 1 deletion(-)
 create mode 100644 arch/arm/include/asm/runtime-patch.h
 create mode 100644 arch/arm/kernel/runtime-patch.c
arch/arm/Kconfig +3 -0
index 2f88d8d..98a3a1a 100644
@@ -60,6 +60,9 @@ config ARM
 config ARM_HAS_SG_CHAIN
 	bool
 
+config ARM_RUNTIME_PATCH
+	bool
+
 config NEED_SG_DMA_LENGTH
 	bool
 
arch/arm/include/asm/module.h +7 -0
index 6c6809f..2090486 100644
@@ -43,9 +43,16 @@ struct mod_arch_specific {
 #define MODULE_ARCH_VERMAGIC_ARMTHUMB ""
 #endif
 
+#ifdef CONFIG_ARM_RUNTIME_PATCH
+#define MODULE_ARCH_VERMAGIC_RT_PATCH "rt-patch "
+#else
+#define MODULE_ARCH_VERMAGIC_RT_PATCH ""
+#endif
+
 #define MODULE_ARCH_VERMAGIC \
 	MODULE_ARCH_VERMAGIC_ARMVSN \
 	MODULE_ARCH_VERMAGIC_ARMTHUMB \
+	MODULE_ARCH_VERMAGIC_RT_PATCH \
 	MODULE_ARCH_VERMAGIC_P2V
 
 #endif /* _ASM_ARM_MODULE_H */
arch/arm/include/asm/runtime-patch.h new file +57 -0
new file mode 100644
index 0000000..366444d
@@ -0,0 +1,208 @@
+/*
+ * arch/arm/include/asm/runtime-patch.h
+ * Note: this file should not be included by non-asm/.h files
+ *
+ * Copyright 2012 Texas Instruments, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_ARM_RUNTIME_PATCH_H
+#define __ASM_ARM_RUNTIME_PATCH_H
+
+#include <linux/stringify.h>
+
+#ifndef __ASSEMBLY__
+
+#ifdef CONFIG_ARM_RUNTIME_PATCH
+
+struct patch_info {
+	void		*insn;
+	u16		 type;
+	u8		 insn_size;
+	u8		 data_size;
+	u32		 data[0];
+};
+
+#define PATCH_IMM8		0x0001
+struct patch_info_imm8 {
+	u32		*imm;
+	u32		 insn;
+};
+
+#define patch_next(p)	((void *)(p) + sizeof(*(p)) + (p)->data_size)
+#define patch_data(p)	((void *)&(p)->data[0])
+
+#define patch_stub(type, code, patch_data, ...)				\
+	__asm__("@ patch stub\n"					\
+		"1:\n"							\
+		code							\
+		"2:\n"							\
+		"	.pushsection .runtime.patch.table, \"a\"\n"	\
+		"3:\n"							\
+		"	.word 1b\n"					\
+		"	.hword (" __stringify(type) ")\n"		\
+		"	.byte (2b-1b)\n"				\
+		"	.byte (5f-4f)\n"				\
+		"4:\n"							\
+		patch_data						\

arch/arm/include/asm/runtime-patch.h line 67

Quoted 10 lines
arch/arm/include/asm/runtime-patch.h new file +10 -0
+		"	.align\n"					\
+		"5:\n"							\
+		"	.popsection\n"					\
+		__VA_ARGS__)
+
+#define early_patch_stub(type, code, pad, patch_data, ...)		\
+	__asm__("@ patch stub\n"					\
+		"1:\n"							\
+		"	b	6f\n"					\
+		"	.fill	" __stringify(pad) ", 1, 0\n"		\

What is the pad argument for? It never seems to be set to anything other than 0 in your series.

The compiler uses a pretty dumb heuristic to guess the size of asms:
4 * (number of ; or \n in the string)

Directives that the compiler can't predict the size of are not safe if they output into any segment that the compiler uses. .fill/.skip are obvious candidates, but macro expansions, .rept, .irp etc. can cause these problems too.

For example:

	void g(int);
	void f(void)
	{
		g(0xd00dfeed);
		asm(".skip 0x1000");
	}

If you try building this with gcc -marm -Os for example:

/tmp/ccXYm1uP.s: Assembler messages:
/tmp/ccXYm1uP.s:21: Error: bad immediate value for offset (4100)

...because the assembler assumes that it can dump a literal at the end of the function and reference it from the g() callsite.

It may be that you have some intended future use for pad (such as pasting one instruction sequence in place of another possibly differently-sized sequence at fixup time), in which case this might require a bit more thought.

arch/arm/include/asm/runtime-patch.h line 80

Quoted 13 lines
arch/arm/include/asm/runtime-patch.h new file +13 -0
+		"2:\n"							\
+		"	.pushsection .runtime.patch.table, \"a\"\n"	\
+		"3:\n"							\
+		"	.word 1b\n"					\
+		"	.hword (" __stringify(type) ")\n"		\
+		"	.byte (2b-1b)\n"				\
+		"	.byte (5f-4f)\n"				\
+		"4:\n"							\
+		patch_data						\
+		"	.align\n"					\
+		"5:\n"							\
+		"	.popsection\n"					\
+		"	.pushsection .runtime.patch.code, \"ax\"\n"	\

I have a vague feeling that this should have .text in the name somewhere, since it is code that gets executed in place.

arch/arm/include/asm/runtime-patch.h line 83

Quoted 3 lines
arch/arm/include/asm/runtime-patch.h new file +3 -0
+		"6:\n"							\
+		code							\
+		"	b 2b\n"						\

.ltorg

(See [1] below.)

arch/arm/include/asm/runtime-patch.h

Quoted 10 lines
arch/arm/include/asm/runtime-patch.h new file +10 -0
+		"	.popsection\n"					\
+		__VA_ARGS__)
+
+/* constant used to force encoding */
+#define __IMM8		(0x81 << 24)
+
+/*
+ * patch_imm8() - init-time specialized binary operation (imm8 operand)
+ *		  This effectively does: to = from "insn" sym,
+ *		  where the value of sym is fixed at init-time, and is patched

If I've understood correctly, then this description is a bit misleading. sym is not an absolute operand, but rather *(sym + ofs) is a variable containing the fixup operand.

arch/arm/include/asm/runtime-patch.h line 102

Quoted 9 lines
arch/arm/include/asm/runtime-patch.h new file +9 -0
+ *		  in as an immediate operand.  This value must be
+ *		  representible as an 8-bit quantity with an optional
+ *		  rotation.
+ *
+ *		  The stub code produced by this variant is non-functional
+ *		  prior to patching.  Use early_patch_imm8() if you need the
+ *		  code to be functional early on in the init sequence.
+ */
+#define patch_imm8(_insn, _to, _from, _sym, _ofs)			\

Why are the _sym and _ofs parameters separate? Nothing in your series seems to requre _sym to be a symbol or _ofs to be a number; and nothing passes _ofs != 0, but anyway I don't see why the caller can't add those two values together in the macro argument.

arch/arm/include/asm/runtime-patch.h line 109

Quoted 7 lines
arch/arm/include/asm/runtime-patch.h new file +7 -0
+	patch_stub(							\
+		/* type */						\
+			PATCH_IMM8,					\
+		/* code */						\
+			_insn "	%[to], %[from], %[imm]\n",		\
+		/* patch_data */					\
+			".long " __stringify(_sym + _ofs) "\n"		\

If _sym or _ofs is a complex expression, the + may mis-bind. If the _ofs parameter is needed at all, it would probably be a good idea to have parentheses around _sym and _ofs.

Quoted 31 lines
arch/arm/include/asm/runtime-patch.h new file +31 -0
+			_insn "	%[to], %[from], %[imm]\n",		\
+		/* operands */						\
+			: [to]	 "=r" (_to)				\
+			: [from] "r"  (_from),				\
+			  [imm]	 "I"  (__IMM8),				\
+				 "i"  (&(_sym))				\
+			: "cc")
+
+/*
+ * patch_imm8_mov() - same as patch_imm8(), but for mov/mvn instructions
+ */
+#define patch_imm8_mov(_insn, _to, _sym, _ofs)				\
+	patch_stub(							\
+		/* type */						\
+			PATCH_IMM8,					\
+		/* code */						\
+			_insn "	%[to], %[imm]\n",			\
+		/* patch_data */					\
+			".long " __stringify(_sym + _ofs) "\n"		\
+			_insn "	%[to], %[imm]\n",			\
+		/* operands */						\
+			: [to]	"=r" (_to)				\
+			: [imm]	"I"  (__IMM8),				\
+				"i"  (&(_sym))				\
+			: "cc")
+
+/*
+ * early_patch_imm8() - early functional variant of patch_imm8() above.  The
+ *			same restrictions on the constant apply here.  This
+ *			version emits workable (albeit inefficient) code at
+ *			compile-time, and therefore functions even prior to

arch/arm/include/asm/runtime-patch.h line 150

Quoted 10 lines
arch/arm/include/asm/runtime-patch.h new file +10 -0
+ *			patch application.
+ */
+#define early_patch_imm8(_insn, _to, _from, _sym, _ofs)			\
+do {									\
+	unsigned long __tmp;						\
+	early_patch_stub(						\
+		/* type */						\
+			PATCH_IMM8,					\
+		/* code */						\
+			"ldr	%[tmp], =" __stringify(_sym + _ofs) "\n"\

This would not be OK if assembled into the .text section, for the reasons described above. (The compiler doesn't know about the extra data word injected by the ldr= pseudo-instruction.)

early_patch_stub puts <code> into a custom section, so that's OK.

However, there's still nothing to ensure that the literal word is in range of the load. That can be fixed with an .ltorg to dump out the literal(s) right after the branch following <code> in early_patch_stub.

Quoted 4 lines
arch/arm/include/asm/runtime-patch.h new file +4 -0
+			"ldr	%[tmp], [%[tmp]]\n"			\
+			_insn "	%[to], %[from], %[tmp]\n",		\
+		/* pad */						\
+			0,						\

arch/arm/include/asm/runtime-patch.h line 164

Quoted 10 lines
arch/arm/include/asm/runtime-patch.h new file +10 -0
+		/* patch_data */					\
+			".long " __stringify(_sym + _ofs) "\n"		\
+			_insn "	%[to], %[from], %[imm]\n",		\
+		/* operands */						\
+			: [to]	 "=r"  (_to),				\
+			  [tmp]	 "=&r" (__tmp)				\
+			: [from] "r"   (_from),				\
+			  [imm]	 "I"   (__IMM8),			\
+				 "i"   (&(_sym))			\
+			: "cc");					\

Should we have "cc" here?

Since this macro is only used with a single instruction _insn, there would be no way to make use of a condition set by that instruction.

Therefore, flag-setting instructions don't really make any sense here, and "cc" could be removed.

If so, it could make sense for the apply_patch_imm8() implementation to check for and reject flag-setting encodings.

Quoted 116 lines
arch/arm/include/asm/runtime-patch.h new file +44 -0
+} while (0)
+
+#define early_patch_imm8_mov(_insn, _to, _sym, _ofs)			\
+do {									\
+	unsigned long __tmp;						\
+	early_patch_stub(						\
+		/* type */						\
+			PATCH_IMM8					\
+		/* code */						\
+			"ldr	%[tmp], =" __stringify(_sym + _ofs) "\n"\
+			"ldr	%[tmp], [%[tmp]]\n"			\
+			_insn "	%[to], %[tmp]\n",			\
+		/* pad */						\
+			0,						\
+		/* patch_data */					\
+			".long " __stringify(_sym + _ofs) "\n"		\
+			_insn " %[to], %[imm]\n",			\
+		/* operands */						\
+			: [to]	"=r"  (_to),				\
+			  [tmp]	"=&r" (__tmp)				\
+			: [imm]	"I"   (__IMM8),				\
+				"i"   (&(_sym))				\
+			: "cc");					\
+} while (0)
+
+int runtime_patch(const void *table, unsigned size);
+void runtime_patch_kernel(void);
+
+#else
+
+static inline int runtime_patch(const void *table, unsigned size)
+{
+	return 0;
+}
+
+static inline void runtime_patch_kernel(void)
+{
+}
+
+#endif /* CONFIG_ARM_RUNTIME_PATCH */
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* __ASM_ARM_RUNTIME_PATCH_H */
arch/arm/kernel/Makefile +1 -0
index 7ad2d5c..0b16b94 100644
@@ -81,5 +81,6 @@ endif
 head-y			:= head$(MMUEXT).o
 obj-$(CONFIG_DEBUG_LL)	+= debug.o
 obj-$(CONFIG_EARLY_PRINTK)	+= early_printk.o
+obj-$(CONFIG_ARM_RUNTIME_PATCH)	+= runtime-patch.o patch.o
 
 extra-y := $(head-y) vmlinux.lds
arch/arm/kernel/module.c +8 -1
index 1e9be5d..10a2922 100644
@@ -24,6 +24,7 @@
 #include <asm/sections.h>
 #include <asm/smp_plat.h>
 #include <asm/unwind.h>
+#include <asm/runtime-patch.h>
 
 #ifdef CONFIG_XIP_KERNEL
 /*
@@ -276,7 +277,7 @@ int module_finalize(const Elf32_Ehdr *hdr, const Elf_Shdr *sechdrs,
 	const char *secstrs = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
 	const Elf_Shdr *sechdrs_end = sechdrs + hdr->e_shnum;
 	struct mod_unwind_map maps[ARM_SEC_MAX];
-	int i;
+	int i, err;
 
 	memset(maps, 0, sizeof(maps));
 
@@ -321,6 +322,12 @@ int module_finalize(const Elf32_Ehdr *hdr, const Elf_Shdr *sechdrs,
 	if (s)
 		fixup_pv_table((void *)s->sh_addr, s->sh_size);
 #endif
+	s = find_mod_section(hdr, sechdrs, ".runtime.patch.table");
+	if (s) {
+		err = runtime_patch((void *)s->sh_addr, s->sh_size);
+		if (err)
+			return err;
+	}
 	s = find_mod_section(hdr, sechdrs, ".alt.smp.init");
 	if (s && !is_smp())
 #ifdef CONFIG_SMP_ON_UP
arch/arm/kernel/runtime-patch.c new file +21 -0
new file mode 100644
index 0000000..28a6367
@@ -0,0 +1,193 @@
+/*
+ * arch/arm/kernel/runtime-patch.c
+ *
+ * Copyright 2012 Texas Instruments, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/kernel.h>
+#include <linux/sched.h>
+
+#include <asm/opcodes.h>

arch/arm/kernel/runtime-patch.c line 31

Quoted 10 lines
arch/arm/kernel/runtime-patch.c new file +10 -0
+#include <asm/cacheflush.h>
+#include <asm/runtime-patch.h>
+
+#include "patch.h"
+
+static inline void flush_icache_insn(void *insn_ptr, int bytes)
+{
+	unsigned long insn_addr = (unsigned long)insn_ptr;
+	flush_icache_range(insn_addr, insn_addr + bytes - 1);
+}

This function appears unused.

Do we actually do the cache flushing anywhere?

Quoted 51 lines
arch/arm/kernel/runtime-patch.c new file +51 -0
+
+#ifdef CONFIG_THUMB2_KERNEL
+
+static int do_patch_imm8(u32 insn, u32 imm, u32 *ninsn)
+{
+	u32 op, rot, val;
+	const u32 supported_ops = (BIT(0)  | /* and */
+				   BIT(1)  | /* bic */
+				   BIT(2)  | /* orr/mov */
+				   BIT(3)  | /* orn/mvn */
+				   BIT(4)  | /* eor */
+				   BIT(8)  | /* add */
+				   BIT(10) | /* adc */
+				   BIT(11) | /* sbc */
+				   BIT(12) | /* sub */
+				   BIT(13)); /* rsb */
+
+	insn = __mem_to_opcode_thumb32(insn);
+
+	if (!__opcode_is_thumb32(insn)) {
+		pr_err("patch: invalid thumb2 insn %08x\n", insn);
+		return -EINVAL;
+	}
+
+	/* allow only data processing (immediate)
+	 * 1111 0x0x xxx0 xxxx 0xxx xxxx xxxx xxxx */
+	if ((insn & 0xfa008000) != 0xf0000000) {
+		pr_err("patch: unknown insn %08x\n", insn);
+		return -EINVAL;
+	}
+
+	/* extract op code */
+	op = (insn >> 21) & 0xf;
+
+	/* disallow unsupported opcodes */
+	if ((supported_ops & BIT(op)) == 0) {
+		pr_err("patch: unsupported opcode %x\n", op);
+		return -EINVAL;
+	}
+
+	if (imm <= 0xff) {
+		rot = 0;
+		val = imm;
+	} else {
+		rot = 32 - fls(imm); /* clz */
+		if (imm & ~(0xff000000 >> rot)) {
+			pr_err("patch: constant overflow %08x\n", imm);
+			return -EINVAL;
+		}
+		val  = (imm >> (24 - rot)) & 0x7f;
+		rot += 8; /* encoded i:imm3:a */

arch/arm/kernel/runtime-patch.c line 92

Quoted 10 lines
arch/arm/kernel/runtime-patch.c new file +10 -0
+
+		/* pack least-sig rot bit into most-sig val bit */
+		val |= (rot & 1) << 7;
+		rot >>= 1;
+	}
+
+	*ninsn  = insn & ~(BIT(26) | 0x7 << 12 | 0xff);
+	*ninsn |= (rot >> 3) << 26;	/* field "i" */
+	*ninsn |= (rot & 0x7) << 12;	/* field "imm3" */
+	*ninsn |= val;

You need to convert this back to memory order. If fixups might be applied while the MMU is off, misaligned 32-bit accesses will fail. It's better to store the two halfwords separately:

	__opcode_to_mem_thumb16(__opcode_thumb32_first(foo))
	__opcode_to_mem_thumb16(__opcode_thumb32_second(foo))

If the MMU is on, you can use __opcode_to_mem_thumb32(foo) and do a possibly misaligned store, though.

This may be a good idea even if the MMU is on, because fixup is a once-only process and we don't expect a meaningful performance impact from that, especially when caching is enabled. But splitting the access may also make it easier to reuse the framework in situations where the cache and MMU are off.

Because of all this, I suggest you don't repeatedly modify *ninsn. Preparing the value and then writing it (or each half) once is probably cleaner.

Quoted 37 lines
arch/arm/kernel/runtime-patch.c new file +37 -0
+
+	return 0;
+}
+
+#else
+
+static int do_patch_imm8(u32 insn, u32 imm, u32 *ninsn)
+{
+	u32 rot, val, op;
+
+	insn = __mem_to_opcode_arm(insn);
+
+	/* disallow special unconditional instructions
+	 * 1111 xxxx xxxx xxxx xxxx xxxx xxxx xxxx */
+	if ((insn >> 24) == 0xf) {
+		pr_err("patch: unconditional insn %08x\n", insn);
+		return -EINVAL;
+	}
+
+	/* allow only data processing (immediate)
+	 * xxxx 001x xxxx xxxx xxxx xxxx xxxx xxxx */
+	if (((insn >> 25) & 0x3) != 1) {
+		pr_err("patch: unknown insn %08x\n", insn);
+		return -EINVAL;
+	}
+
+	/* extract op code */
+	op = (insn >> 20) & 0x1f;
+
+	/* disallow unsupported 10xxx op codes */
+	if (((op >> 3) & 0x3) == 2) {
+		pr_err("patch: unsupported opcode %08x\n", insn);
+		return -EINVAL;
+	}
+
+	rot = imm ? __ffs(imm) / 2 : 0;
+	val = imm >> (rot * 2);

arch/arm/kernel/runtime-patch.c line 139

Quoted 10 lines
arch/arm/kernel/runtime-patch.c new file +10 -0
+	rot = (-rot) & 0xf;
+
+	/* does this fit in 8-bit? */
+	if (val > 0xff) {
+		pr_err("patch: constant overflow %08x\n", imm);
+		return -EINVAL;
+	}
+
+	/* patch in new immediate and rotation */
+	*ninsn = (insn & ~0xfff) | (rot << 8) | val;

You need __opcode_to_mem_arm() to convert this back to memory order.

Quoted 22 lines
arch/arm/kernel/runtime-patch.c new file +22 -0
+
+	return 0;
+}
+
+#endif	/* CONFIG_THUMB2_KERNEL */
+
+static int apply_patch_imm8(const struct patch_info *p)
+{
+	u32 *insn_ptr = p->insn, ninsn;
+	int count = p->insn_size / sizeof(u32);
+	const struct patch_info_imm8 *info;
+	int err;
+
+
+	if (count <= 0 || p->data_size != count * sizeof(*info)) {
+		pr_err("patch: bad patch, insn size %d, data size %d\n",
+		       p->insn_size, p->data_size);
+		return -EINVAL;
+	}
+
+	for (info = patch_data(p); count; count--, info++, insn_ptr++) {
+		err = do_patch_imm8(info->insn, *info->imm, &ninsn);

arch/arm/kernel/runtime-patch.c line 171

Quoted 10 lines
arch/arm/kernel/runtime-patch.c new file +10 -0
+		if (err)
+			return err;
+		__patch_text(insn_ptr, ninsn);
+	}
+
+
+	return 0;
+}
+
+int runtime_patch(const void *table, unsigned size)

Minor nits: the type-unsafety could be dealt with outside this function; table could be struct patch_info const *.

Also, why do we not just pass end to this function instead of subtracting the table base and then adding it back again?

Quoted 72 lines
arch/arm/kernel/runtime-patch.c new file +22 -0
+{
+	const struct patch_info *p = table, *end = (table + size);
+
+	for (p = table; p < end; p = patch_next(p)) {
+		int err = -EINVAL;
+
+		if (p->type == PATCH_IMM8)
+			err = apply_patch_imm8(p);
+		if (err)
+			return err;
+	}
+	return 0;
+}
+
+void __init runtime_patch_kernel(void)
+{
+	extern unsigned __runtime_patch_table_begin, __runtime_patch_table_end;
+	const void *start = &__runtime_patch_table_begin;
+	const void *end   = &__runtime_patch_table_end;
+
+	BUG_ON(runtime_patch(start, end - start));
+}
arch/arm/kernel/setup.c +3 -0
index a81dcec..669bbf0 100644
@@ -55,6 +55,7 @@
 #include <asm/traps.h>
 #include <asm/unwind.h>
 #include <asm/memblock.h>
+#include <asm/runtime-patch.h>
 
 #if defined(CONFIG_DEPRECATED_PARAM_STRUCT)
 #include "compat.h"
@@ -998,6 +999,8 @@ void __init setup_arch(char **cmdline_p)
 
 	if (mdesc->init_early)
 		mdesc->init_early();
+
+	runtime_patch_kernel();
 }
 
 
arch/arm/kernel/vmlinux.lds.S +10 -0
index 36ff15b..ea35ca0 100644
@@ -167,6 +167,16 @@ SECTIONS
 		*(.pv_table)
 		__pv_table_end = .;
 	}
+	.init.runtime_patch_table : {
+		__runtime_patch_table_begin = .;
+		*(.runtime.patch.table)
+		__runtime_patch_table_end = .;
+	}
+	.init.runtime_patch_code : {
+		__runtime_patch_code_begin = .;
+		*(.runtime.patch.code)
+		__runtime_patch_code_end = .;
+	}
 	.init.data : {
 #ifndef CONFIG_XIP_KERNEL
 		INIT_DATA
Signature · 7 lines
1.7.9.5


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel at lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
#5 lore raw

Re: [PATCH v3 RESEND 01/17] ARM: add mechanism for late code patching

Hi Dave,

Thanks for the detailed review...

Quoted 14 lines · Dave Martin
Quoted 5 lines · Cyril Chemparathy

The original phys_to_virt/virt_to_phys patching implementation relied on early patching prior to MMU initialization. On PAE systems running out of >4G address space, this would have entailed an additional round of patching after switching over to the high address space.

Quoted 7 lines

The approach implemented here conceptually extends the original PHYS_OFFSET patching implementation with the introduction of "early" patch stubs. Early patch code is required to be functional out of the box, even before the patch is applied. This is implemented by inserting functional (but inefficient) load code into the .runtime.patch.code init section. Having functional code out of the box then allows us to defer the init time patch application until later in the init sequence.

Quoted 4 lines

There are currently a few different patching mechanisms in the kernel, and it would be good if we could collect more of them under some common framework.

That would be great! I could use some pointers here. I've looked at the kprobes code, and there doesn't appear to be much of an intersection there...

Quoted 3 lines

For example, it might be possible to do the SMP-on-UP fixups using the same framework you propose. Best not to attempt that yet, though.

... and I've looked at the ALT_SMP/ALT_UP stuff as well. The problem here appears to be that ALT_SMP is needed way early in boot - up in head.S assembly-land.

The third place is probably the jump label mechanism. That may be a fit with some work, but I'm not sure yet.

Quoted 8 lines

Overall, this looks well thought out and useful, though it looks like it has a few issues that need attention.

Comments below.

Cheers
---Dave

[...]

Quoted 7 lines
Quoted 6 lines

Note: the obtuse use of stringified symbols in patch_stub() and early_patch_stub() is intentional. Theoretically this should have been accomplished with formal operands passed into the asm block, but this requires the use of the 'c' modifier for instantiating the long (e.g. .long %c0). However, the 'c' modifier has been found to ICE certain versions of GCC, and therefore we resort to stringified symbols here.

Quoted 6 lines

You might find that the "n" constraint works. The explanation in the GCC docs is pretty incomprehensible, but at least it exists.

__stringify hacks are not uncommon anyway though, so it's not a problem either way.

The stringify hack is nasty, and I'd like to get rid of it if I could...

I looked up constraints.md, and couldn't find "n" defined as a constraint. For that matter, I couldn't find "n" being handled as a modifier in arm_print_operand() either. BTW, I'm looking at GCC 4.6 for these.

Could you please point me to the doc in question that lists this constraint/modifier? Is this specific to newer (or older) versions of GCC.

[...]

Quoted 9 lines

arch/arm/include/asm/runtime-patch.h line 67

Quoted 5 lines
arch/arm/include/asm/runtime-patch.h new file +5 -0
+#define early_patch_stub(type, code, pad, patch_data, ...)		\
+	__asm__("@ patch stub\n"					\
+		"1:\n"							\
+		"	b	6f\n"					\
+		"	.fill	" __stringify(pad) ", 1, 0\n"		\

What is the pad argument for? It never seems to be set to anything other than 0 in your series.

The pad argument is used when we need more than one 32-bit instruction slot in the straight line (post patch) code path. Please look at patch 05/17 of the series (ARM: LPAE: support 64-bit virt_to_phys patching). When we patch 64-bit virt_to_phys(), we need that extra slot to fill up the upper 32-bits of the result.

Quoted 22 lines

The compiler uses a pretty dumb heuristic to guess the size of asms:
4 * (number of ; or \n in the string)

Directives that the compiler can't predict the size of are not safe if they output into any segment that the compiler uses. .fill/.skip are obvious candidates, but macro expansions, .rept, .irp etc. can cause these problems too.

For example:

	void g(int);
	void f(void)
	{
		g(0xd00dfeed);
		asm(".skip 0x1000");
	}

If you try building this with gcc -marm -Os for example:

/tmp/ccXYm1uP.s: Assembler messages:
/tmp/ccXYm1uP.s:21: Error: bad immediate value for offset (4100)

Quoted 9 lines

...because the assembler assumes that it can dump a literal at the end of the function and reference it from the g() callsite.

It may be that you have some intended future use for pad (such as pasting one instruction sequence in place of another possibly differently-sized sequence at fixup time), in which case this might require a bit more thought.

Good point. Thanks.

I'm not sure if this helps, but I don't think pad should be used to insert more than a couple of instruction words into the code.

Quoted 8 lines
Quoted 8 lines
arch/arm/include/asm/runtime-patch.h new file +8 -0
+		"2:\n"							\
+		"	.pushsection .runtime.patch.table, \"a\"\n"	\
+		"3:\n"							\
+		"	.word 1b\n"					\
+		"	.hword (" __stringify(type) ")\n"		\
+		"	.byte (2b-1b)\n"				\
+		"	.byte (5f-4f)\n"				\
+		"4:\n"							\
Quoted 9 lines

arch/arm/include/asm/runtime-patch.h line 80

Quoted 5 lines
arch/arm/include/asm/runtime-patch.h new file +5 -0
+		patch_data						\
+		"	.align\n"					\
+		"5:\n"							\
+		"	.popsection\n"					\
+		"	.pushsection .runtime.patch.code, \"ax\"\n"	\

I have a vague feeling that this should have .text in the name somewhere, since it is code that gets executed in place.

Fair enough. Will change to .runtime.patch.text instead.

Quoted 8 lines

arch/arm/include/asm/runtime-patch.h line 83

Quoted 3 lines
arch/arm/include/asm/runtime-patch.h new file +3 -0
+		"6:\n"							\
+		code							\
+		"	b 2b\n"						\

.ltorg

(See [1] below.)

Quoted 15 lines

arch/arm/include/asm/runtime-patch.h

Quoted 10 lines
arch/arm/include/asm/runtime-patch.h new file +10 -0
+		"	.popsection\n"					\
+		__VA_ARGS__)
+
+/* constant used to force encoding */
+#define __IMM8		(0x81 << 24)
+
+/*
+ * patch_imm8() - init-time specialized binary operation (imm8 operand)
+ *		  This effectively does: to = from "insn" sym,
+ *		  where the value of sym is fixed at init-time, and is patched

If I've understood correctly, then this description is a bit misleading. sym is not an absolute operand, but rather *(sym + ofs) is a variable containing the fixup operand.

Correct. I'll clarify in the text.

Quoted 10 lines
Quoted 9 lines
+ *		  in as an immediate operand.  This value must be
+ *		  representible as an 8-bit quantity with an optional
+ *		  rotation.
+ *
+ *		  The stub code produced by this variant is non-functional
+ *		  prior to patching.  Use early_patch_imm8() if you need the
+ *		  code to be functional early on in the init sequence.

+ */
+#define patch_imm8(_insn, _to, _from, _sym, _ofs) \

Quoted 5 lines

Why are the _sym and _ofs parameters separate? Nothing in your series seems to requre _sym to be a symbol or _ofs to be a number; and nothing passes _ofs != 0, but anyway I don't see why the caller can't add those two values together in the macro argument.

I could (should) really get rid of ofs. This is a hold over from earlier versions of the patch, where we were using the ofs to point to the upper 32-bits of the phys_offset ( = 4 for LE, 0 for BE).

I will clean this up in the next rev. Thanks.

Quoted 12 lines

arch/arm/include/asm/runtime-patch.h line 109

Quoted 7 lines
arch/arm/include/asm/runtime-patch.h new file +7 -0
+	patch_stub(							\
+		/* type */						\
+			PATCH_IMM8,					\
+		/* code */						\
+			_insn "	%[to], %[from], %[imm]\n",		\
+		/* patch_data */					\
+			".long " __stringify(_sym + _ofs) "\n"		\

If _sym or _ofs is a complex expression, the + may mis-bind. If the _ofs parameter is needed at all, it would probably be a good idea to have parentheses around _sym and _ofs.

I'm guessing this doesn't apply once we get rid of ofs?

Quoted 69 lines
Quoted 31 lines
arch/arm/include/asm/runtime-patch.h new file +31 -0
+			_insn "	%[to], %[from], %[imm]\n",		\
+		/* operands */						\
+			: [to]	 "=r" (_to)				\
+			: [from] "r"  (_from),				\
+			  [imm]	 "I"  (__IMM8),				\
+				 "i"  (&(_sym))				\
+			: "cc")
+
+/*
+ * patch_imm8_mov() - same as patch_imm8(), but for mov/mvn instructions
+ */
+#define patch_imm8_mov(_insn, _to, _sym, _ofs)				\
+	patch_stub(							\
+		/* type */						\
+			PATCH_IMM8,					\
+		/* code */						\
+			_insn "	%[to], %[imm]\n",			\
+		/* patch_data */					\
+			".long " __stringify(_sym + _ofs) "\n"		\
+			_insn "	%[to], %[imm]\n",			\
+		/* operands */						\
+			: [to]	"=r" (_to)				\
+			: [imm]	"I"  (__IMM8),				\
+				"i"  (&(_sym))				\
+			: "cc")
+
+/*
+ * early_patch_imm8() - early functional variant of patch_imm8() above.  The
+ *			same restrictions on the constant apply here.  This
+ *			version emits workable (albeit inefficient) code at
+ *			compile-time, and therefore functions even prior to

arch/arm/include/asm/runtime-patch.h line 150

Quoted 10 lines
arch/arm/include/asm/runtime-patch.h new file +10 -0
+ *			patch application.
+ */
+#define early_patch_imm8(_insn, _to, _from, _sym, _ofs)			\
+do {									\
+	unsigned long __tmp;						\
+	early_patch_stub(						\
+		/* type */						\
+			PATCH_IMM8,					\
+		/* code */						\
+			"ldr	%[tmp], =" __stringify(_sym + _ofs) "\n"\

This would not be OK if assembled into the .text section, for the reasons described above. (The compiler doesn't know about the extra data word injected by the ldr= pseudo-instruction.)

early_patch_stub puts <code> into a custom section, so that's OK.

However, there's still nothing to ensure that the literal word is in range of the load. That can be fixed with an .ltorg to dump out the literal(s) right after the branch following <code> in early_patch_stub.

Quoted 4 lines
arch/arm/include/asm/runtime-patch.h new file +4 -0
+			"ldr	%[tmp], [%[tmp]]\n"			\
+			_insn "	%[to], %[from], %[tmp]\n",		\
+		/* pad */						\
+			0,						\

arch/arm/include/asm/runtime-patch.h line 164

Quoted 10 lines
arch/arm/include/asm/runtime-patch.h new file +10 -0
+		/* patch_data */					\
+			".long " __stringify(_sym + _ofs) "\n"		\
+			_insn "	%[to], %[from], %[imm]\n",		\
+		/* operands */						\
+			: [to]	 "=r"  (_to),				\
+			  [tmp]	 "=&r" (__tmp)				\
+			: [from] "r"   (_from),				\
+			  [imm]	 "I"   (__IMM8),			\
+				 "i"   (&(_sym))			\
+			: "cc");					\

Should we have "cc" here?

Quoted 9 lines

Since this macro is only used with a single instruction _insn, there would be no way to make use of a condition set by that instruction.

Therefore, flag-setting instructions don't really make any sense here, and "cc" could be removed.

If so, it could make sense for the apply_patch_imm8() implementation to check for and reject flag-setting encodings.

That makes sense. I've modified the do_patch_imm8() functions to explicitly check for attempts to set condition codes, and removed the "cc".

[...]

Quoted 5 lines
Quoted 5 lines
arch/arm/kernel/runtime-patch.c new file +0 -0
new file mode 100644
index 0000000..28a6367

[...]

Quoted 8 lines

arch/arm/kernel/runtime-patch.c line 31

Quoted 5 lines
arch/arm/kernel/runtime-patch.c new file +5 -0
+static inline void flush_icache_insn(void *insn_ptr, int bytes)
+{
+	unsigned long insn_addr = (unsigned long)insn_ptr;
+	flush_icache_range(insn_addr, insn_addr + bytes - 1);
+}

This function appears unused.

Indeed. This function should have been removed. Thanks.

Quoted 2 lines

Do we actually do the cache flushing anywhere?

Yes, in __patch_text().

[...]

Quoted 2 lines
Quoted 2 lines
arch/arm/kernel/runtime-patch.c new file +2 -0
+static int do_patch_imm8(u32 insn, u32 imm, u32 *ninsn)
+{

[...]

Quoted 25 lines

arch/arm/kernel/runtime-patch.c line 92

Quoted 4 lines
arch/arm/kernel/runtime-patch.c new file +4 -0
+	*ninsn  = insn & ~(BIT(26) | 0x7 << 12 | 0xff);
+	*ninsn |= (rot >> 3) << 26;	/* field "i" */
+	*ninsn |= (rot & 0x7) << 12;	/* field "imm3" */
+	*ninsn |= val;

You need to convert this back to memory order. If fixups might be applied while the MMU is off, misaligned 32-bit accesses will fail. It's better to store the two halfwords separately:

	__opcode_to_mem_thumb16(__opcode_thumb32_first(foo))
	__opcode_to_mem_thumb16(__opcode_thumb32_second(foo))

If the MMU is on, you can use __opcode_to_mem_thumb32(foo) and do a possibly misaligned store, though.

This may be a good idea even if the MMU is on, because fixup is a once-only process and we don't expect a meaningful performance impact from that, especially when caching is enabled. But splitting the access may also make it easier to reuse the framework in situations where the cache and MMU are off.

Because of all this, I suggest you don't repeatedly modify *ninsn. Preparing the value and then writing it (or each half) once is probably cleaner.

[...]

Quoted 1 line
Quoted 1 line

+static int do_patch_imm8(u32 insn, u32 imm, u32 *ninsn)

[...]

Quoted 5 lines

arch/arm/kernel/runtime-patch.c line 139

Quoted 2 lines
arch/arm/kernel/runtime-patch.c new file +2 -0
+	/* patch in new immediate and rotation */
+	*ninsn = (insn & ~0xfff) | (rot << 8) | val;

You need __opcode_to_mem_arm() to convert this back to memory order.

The do_patch_imm8() functions do not write to the instruction. The ninsn pointer here is not a pointer to the instruction that is being patched, it is simply a pointer used to return a value to the caller (apply_patch_imm8()).

The instruction is written in apply_patch_imm8():

	u32 ninsn;
	...
	err = do_patch_imm8(info->insn, *info->imm, &ninsn);
	if (err)
		return err;
	__patch_text(insn_ptr, ninsn);

Here the __patch_text() call converts the instruction and performs the actual write. If I read this correctly, the __patch_text() code takes care of the splitting up thumb2 instructions as you've indicated.

[...]

Quoted 8 lines
Quoted 1 line

+int runtime_patch(const void *table, unsigned size)

Minor nits: the type-unsafety could be dealt with outside this function; table could be struct patch_info const *.

Also, why do we not just pass end to this function instead of subtracting the table base and then adding it back again?

In addition to the internal usage within runtime-patch.c, this function is called from module.c. In the module load case, the base + size form is more convenient.

Second, based on Nico's comments about keeping the namespace clean, I've now moved the structure definitions to runtime-patch.c. These types are no longer exposed, and so we have to keep them opaque in this interface.

[...]

Signature · 2 lines
Thanks
- Cyril
#6 lore raw

Re: [PATCH v3 RESEND 01/17] ARM: add mechanism for late code patching

Quoted 19 lines · Cyril Chemparathy

Hi Dave,

Thanks for the detailed review...

Quoted 14 lines · Dave Martin
Quoted 12 lines · Cyril Chemparathy

The original phys_to_virt/virt_to_phys patching implementation relied on early patching prior to MMU initialization. On PAE systems running out of >4G address space, this would have entailed an additional round of patching after switching over to the high address space.

The approach implemented here conceptually extends the original PHYS_OFFSET patching implementation with the introduction of "early" patch stubs. Early patch code is required to be functional out of the box, even before the patch is applied. This is implemented by inserting functional (but inefficient) load code into the .runtime.patch.code init section. Having functional code out of the box then allows us to defer the init time patch application until later in the init sequence.

Quoted 8 lines
Quoted 4 lines

There are currently a few different patching mechanisms in the kernel, and it would be good if we could collect more of them under some common framework.

That would be great! I could use some pointers here. I've looked at the kprobes code, and there doesn't appear to be much of an intersection there...

Well, I think we shouldn't try it for now. I think kprobes is doing something different enough that it doesn't necessarily make sense to unify that.

If your patches are useful as-is, we shouldn't worry about adding features until later.

Quoted 7 lines
Quoted 3 lines

For example, it might be possible to do the SMP-on-UP fixups using the same framework you propose. Best not to attempt that yet, though.

... and I've looked at the ALT_SMP/ALT_UP stuff as well. The problem here appears to be that ALT_SMP is needed way early in boot
- up in head.S assembly-land.

Indeed, this would need some thought. Again, this is just a "maybe". No need to try it right now.

Quoted 29 lines

The third place is probably the jump label mechanism. That may be a fit with some work, but I'm not sure yet.

Quoted 8 lines

Overall, this looks well thought out and useful, though it looks like it has a few issues that need attention.

Comments below.

Cheers
---Dave

[...]

Quoted 7 lines
Quoted 6 lines

Note: the obtuse use of stringified symbols in patch_stub() and early_patch_stub() is intentional. Theoretically this should have been accomplished with formal operands passed into the asm block, but this requires the use of the 'c' modifier for instantiating the long (e.g. .long %c0). However, the 'c' modifier has been found to ICE certain versions of GCC, and therefore we resort to stringified symbols here.

Quoted 6 lines

You might find that the "n" constraint works. The explanation in the GCC docs is pretty incomprehensible, but at least it exists.

__stringify hacks are not uncommon anyway though, so it's not a problem either way.

The stringify hack is nasty, and I'd like to get rid of it if I could...

Quoted 8 lines

I looked up constraints.md, and couldn't find "n" defined as a constraint. For that matter, I couldn't find "n" being handled as a modifier in arm_print_operand() either. BTW, I'm looking at GCC 4.6 for these.

Could you please point me to the doc in question that lists this constraint/modifier? Is this specific to newer (or older) versions of GCC.

See the "Simple Constraints" section in the GCC docs.

However, it looks like "n" only works for operands which really do evaluate to constant integers. It doesn't work for expressions based on symbols:

	void g(int);
        asm(".long %0" :: "n" (g));

tst.c:6:2: warning: asm operand 0 probably doesn't match constraints tst.c:6:2: error: impossible constraint in 'asm'

So scratch that suggestion... the __stringify() method looks better.

Quoted 12 lines

[...]

Quoted 9 lines

arch/arm/include/asm/runtime-patch.h line 67

Quoted 5 lines
arch/arm/include/asm/runtime-patch.h new file +5 -0
+#define early_patch_stub(type, code, pad, patch_data, ...)		\
+	__asm__("@ patch stub\n"					\
+		"1:\n"							\
+		"	b	6f\n"					\
+		"	.fill	" __stringify(pad) ", 1, 0\n"		\

What is the pad argument for? It never seems to be set to anything other than 0 in your series.

Quoted 5 lines

The pad argument is used when we need more than one 32-bit instruction slot in the straight line (post patch) code path. Please look at patch 05/17 of the series (ARM: LPAE: support 64-bit virt_to_phys patching). When we patch 64-bit virt_to_phys(), we need that extra slot to fill up the upper 32-bits of the result.

I see -- it looks like I missed that.

I guess this is probably OK, given that for every instruction-sized bit of padding, there should be a corresponding patch data definition in the same asm block which increases GCC's guess about the size of the asm at least enough to avoid fixup problems.

Used like this, there should probably be no problem.

Quoted 32 lines
Quoted 22 lines

The compiler uses a pretty dumb heuristic to guess the size of asms:
4 * (number of ; or \n in the string)

Directives that the compiler can't predict the size of are not safe if they output into any segment that the compiler uses. .fill/.skip are obvious candidates, but macro expansions, .rept, .irp etc. can cause these problems too.

For example:

	void g(int);
	void f(void)
	{
		g(0xd00dfeed);
		asm(".skip 0x1000");
	}

If you try building this with gcc -marm -Os for example:

/tmp/ccXYm1uP.s: Assembler messages:
/tmp/ccXYm1uP.s:21: Error: bad immediate value for offset (4100)

Quoted 9 lines

...because the assembler assumes that it can dump a literal at the end of the function and reference it from the g() callsite.

It may be that you have some intended future use for pad (such as pasting one instruction sequence in place of another possibly differently-sized sequence at fixup time), in which case this might require a bit more thought.

Quoted 4 lines

Good point. Thanks.

I'm not sure if this helps, but I don't think pad should be used to insert more than a couple of instruction words into the code.

A directive which inserts <= 4 bytes is safe. Anything else is not safe (unless GCC overestimates the size of the asm for other reasons).

As I explain above, I now think we are actually OK in this case.

Quoted 62 lines
Quoted 8 lines
Quoted 8 lines
arch/arm/include/asm/runtime-patch.h new file +8 -0
+		"2:\n"							\
+		"	.pushsection .runtime.patch.table, \"a\"\n"	\
+		"3:\n"							\
+		"	.word 1b\n"					\
+		"	.hword (" __stringify(type) ")\n"		\
+		"	.byte (2b-1b)\n"				\
+		"	.byte (5f-4f)\n"				\
+		"4:\n"							\
Quoted 9 lines

arch/arm/include/asm/runtime-patch.h line 80

Quoted 5 lines
arch/arm/include/asm/runtime-patch.h new file +5 -0
+		patch_data						\
+		"	.align\n"					\
+		"5:\n"							\
+		"	.popsection\n"					\
+		"	.pushsection .runtime.patch.code, \"ax\"\n"	\

I have a vague feeling that this should have .text in the name somewhere, since it is code that gets executed in place.

Fair enough. Will change to .runtime.patch.text instead.

Quoted 8 lines

arch/arm/include/asm/runtime-patch.h line 83

Quoted 3 lines
arch/arm/include/asm/runtime-patch.h new file +3 -0
+		"6:\n"							\
+		code							\
+		"	b 2b\n"						\

.ltorg

(See [1] below.)

Quoted 15 lines

arch/arm/include/asm/runtime-patch.h

Quoted 10 lines
arch/arm/include/asm/runtime-patch.h new file +10 -0
+		"	.popsection\n"					\
+		__VA_ARGS__)
+
+/* constant used to force encoding */
+#define __IMM8		(0x81 << 24)
+
+/*
+ * patch_imm8() - init-time specialized binary operation (imm8 operand)
+ *		  This effectively does: to = from "insn" sym,
+ *		  where the value of sym is fixed at init-time, and is patched

If I've understood correctly, then this description is a bit misleading. sym is not an absolute operand, but rather *(sym + ofs) is a variable containing the fixup operand.

Correct. I'll clarify in the text.

Quoted 10 lines
Quoted 9 lines
+ *		  in as an immediate operand.  This value must be
+ *		  representible as an 8-bit quantity with an optional
+ *		  rotation.
+ *
+ *		  The stub code produced by this variant is non-functional
+ *		  prior to patching.  Use early_patch_imm8() if you need the
+ *		  code to be functional early on in the init sequence.

+ */
+#define patch_imm8(_insn, _to, _from, _sym, _ofs) \

Quoted 5 lines

Why are the _sym and _ofs parameters separate? Nothing in your series seems to requre _sym to be a symbol or _ofs to be a number; and nothing passes _ofs != 0, but anyway I don't see why the caller can't add those two values together in the macro argument.

Quoted 5 lines

I could (should) really get rid of ofs. This is a hold over from earlier versions of the patch, where we were using the ofs to point to the upper 32-bits of the phys_offset ( = 4 for LE, 0 for BE).

I will clean this up in the next rev. Thanks.

Ah, OK. Removing that extra argument should make things clearer.

Quoted 14 lines
Quoted 13 lines

arch/arm/include/asm/runtime-patch.h line 109

Quoted 8 lines
arch/arm/include/asm/runtime-patch.h new file +7 -0
+	patch_stub(							\
+		/* type */						\
+			PATCH_IMM8,					\
+		/* code */						\
+			_insn "	%[to], %[from], %[imm]\n",		\
+		/* patch_data */					\
+			".long " __stringify(_sym + _ofs) "\n"		\

If _sym or _ofs is a complex expression, the + may mis-bind. If the _ofs parameter is needed at all, it would probably be a good idea to have parentheses around _sym and _ofs.

Quoted 1 line

I'm guessing this doesn't apply once we get rid of ofs?

Sure, no problem.

Quoted 75 lines
Quoted 75 lines
Quoted 31 lines
arch/arm/include/asm/runtime-patch.h new file +31 -0
+			_insn "	%[to], %[from], %[imm]\n",		\
+		/* operands */						\
+			: [to]	 "=r" (_to)				\
+			: [from] "r"  (_from),				\
+			  [imm]	 "I"  (__IMM8),				\
+				 "i"  (&(_sym))				\
+			: "cc")
+
+/*
+ * patch_imm8_mov() - same as patch_imm8(), but for mov/mvn instructions
+ */
+#define patch_imm8_mov(_insn, _to, _sym, _ofs)				\
+	patch_stub(							\
+		/* type */						\
+			PATCH_IMM8,					\
+		/* code */						\
+			_insn "	%[to], %[imm]\n",			\
+		/* patch_data */					\
+			".long " __stringify(_sym + _ofs) "\n"		\
+			_insn "	%[to], %[imm]\n",			\
+		/* operands */						\
+			: [to]	"=r" (_to)				\
+			: [imm]	"I"  (__IMM8),				\
+				"i"  (&(_sym))				\
+			: "cc")
+
+/*
+ * early_patch_imm8() - early functional variant of patch_imm8() above.  The
+ *			same restrictions on the constant apply here.  This
+ *			version emits workable (albeit inefficient) code at
+ *			compile-time, and therefore functions even prior to

arch/arm/include/asm/runtime-patch.h line 150

Quoted 10 lines
arch/arm/include/asm/runtime-patch.h new file +10 -0
+ *			patch application.
+ */
+#define early_patch_imm8(_insn, _to, _from, _sym, _ofs)			\
+do {									\
+	unsigned long __tmp;						\
+	early_patch_stub(						\
+		/* type */						\
+			PATCH_IMM8,					\
+		/* code */						\
+			"ldr	%[tmp], =" __stringify(_sym + _ofs) "\n"\

This would not be OK if assembled into the .text section, for the reasons described above. (The compiler doesn't know about the extra data word injected by the ldr= pseudo-instruction.)

early_patch_stub puts <code> into a custom section, so that's OK.

However, there's still nothing to ensure that the literal word is in range of the load. That can be fixed with an .ltorg to dump out the literal(s) right after the branch following <code> in early_patch_stub.

Quoted 4 lines
arch/arm/include/asm/runtime-patch.h new file +4 -0
+			"ldr	%[tmp], [%[tmp]]\n"			\
+			_insn "	%[to], %[from], %[tmp]\n",		\
+		/* pad */						\
+			0,						\

arch/arm/include/asm/runtime-patch.h line 164

Quoted 10 lines
arch/arm/include/asm/runtime-patch.h new file +10 -0
+		/* patch_data */					\
+			".long " __stringify(_sym + _ofs) "\n"		\
+			_insn "	%[to], %[from], %[imm]\n",		\
+		/* operands */						\
+			: [to]	 "=r"  (_to),				\
+			  [tmp]	 "=&r" (__tmp)				\
+			: [from] "r"   (_from),				\
+			  [imm]	 "I"   (__IMM8),			\
+				 "i"   (&(_sym))			\
+			: "cc");					\

Should we have "cc" here?

Since this macro is only used with a single instruction _insn, there would be no way to make use of a condition set by that instruction.

Therefore, flag-setting instructions don't really make any sense here, and "cc" could be removed.

Quoted 7 lines
Quoted 3 lines

If so, it could make sense for the apply_patch_imm8() implementation to check for and reject flag-setting encodings.

That makes sense. I've modified the do_patch_imm8() functions to explicitly check for attempts to set condition codes, and removed the "cc".

OK, cool.

Quoted 17 lines

[...]

Quoted 5 lines
Quoted 5 lines
arch/arm/kernel/runtime-patch.c new file +0 -0
new file mode 100644
index 0000000..28a6367

[...]

Quoted 8 lines

arch/arm/kernel/runtime-patch.c line 31

Quoted 5 lines
arch/arm/kernel/runtime-patch.c new file +5 -0
+static inline void flush_icache_insn(void *insn_ptr, int bytes)
+{
+	unsigned long insn_addr = (unsigned long)insn_ptr;
+	flush_icache_range(insn_addr, insn_addr + bytes - 1);
+}

This function appears unused.

Quoted 6 lines

Indeed. This function should have been removed. Thanks.

Quoted 2 lines

Do we actually do the cache flushing anywhere?

Yes, in __patch_text().

OK, fine.

Quoted 53 lines

[...]

Quoted 2 lines
Quoted 2 lines
arch/arm/kernel/runtime-patch.c new file +2 -0
+static int do_patch_imm8(u32 insn, u32 imm, u32 *ninsn)
+{

[...]

Quoted 25 lines

arch/arm/kernel/runtime-patch.c line 92

Quoted 4 lines
arch/arm/kernel/runtime-patch.c new file +4 -0
+	*ninsn  = insn & ~(BIT(26) | 0x7 << 12 | 0xff);
+	*ninsn |= (rot >> 3) << 26;	/* field "i" */
+	*ninsn |= (rot & 0x7) << 12;	/* field "imm3" */
+	*ninsn |= val;

You need to convert this back to memory order. If fixups might be applied while the MMU is off, misaligned 32-bit accesses will fail. It's better to store the two halfwords separately:

	__opcode_to_mem_thumb16(__opcode_thumb32_first(foo))
	__opcode_to_mem_thumb16(__opcode_thumb32_second(foo))

If the MMU is on, you can use __opcode_to_mem_thumb32(foo) and do a possibly misaligned store, though.

This may be a good idea even if the MMU is on, because fixup is a once-only process and we don't expect a meaningful performance impact from that, especially when caching is enabled. But splitting the access may also make it easier to reuse the framework in situations where the cache and MMU are off.

Because of all this, I suggest you don't repeatedly modify *ninsn. Preparing the value and then writing it (or each half) once is probably cleaner.

[...]

Quoted 1 line
Quoted 1 line

+static int do_patch_imm8(u32 insn, u32 imm, u32 *ninsn)

[...]

Quoted 5 lines

arch/arm/kernel/runtime-patch.c line 139

Quoted 2 lines
arch/arm/kernel/runtime-patch.c new file +2 -0
+	/* patch in new immediate and rotation */
+	*ninsn = (insn & ~0xfff) | (rot << 8) | val;

You need __opcode_to_mem_arm() to convert this back to memory order.

The do_patch_imm8() functions do not write to the instruction. The ninsn pointer here is not a pointer to the instruction that is being patched, it is simply a pointer used to return a value to the caller (apply_patch_imm8()).

The instruction is written in apply_patch_imm8():

	u32 ninsn;
	...
	err = do_patch_imm8(info->insn, *info->imm, &ninsn);
	if (err)
		return err;
	__patch_text(insn_ptr, ninsn);
Quoted 4 lines

Here the __patch_text() call converts the instruction and performs the actual write. If I read this correctly, the __patch_text() code takes care of the splitting up thumb2 instructions as you've indicated.

OK, I think you're right. I had vague memories about __patch_text(), but I missed its use here (I was too busy searching for "flush"...)

This makes the do_patch_imm8() functions a bit weird though, because they accept input in memory order but generate output in canonical order.

That could be avoided by having an #ifdef in apply_patch_imm8() to choose the correct conversion for the input, but that would be a bit ugly. Maybe just add a comment somewhere to clarify the different in byte order, and the reason for it: this would make the code easier to understand.

Quoted 11 lines

[...]

Quoted 8 lines
Quoted 1 line

+int runtime_patch(const void *table, unsigned size)

Minor nits: the type-unsafety could be dealt with outside this function; table could be struct patch_info const *.

Also, why do we not just pass end to this function instead of subtracting the table base and then adding it back again?

Quoted 8 lines

In addition to the internal usage within runtime-patch.c, this function is called from module.c. In the module load case, the base
+ size form is more convenient.

Second, based on Nico's comments about keeping the namespace clean, I've now moved the structure definitions to runtime-patch.c. These types are no longer exposed, and so we have to keep them opaque in this interface.

OK, fair enough. Those were very minor nits anyway.

[...]

Cheers
---Dave

#7 lore raw

PATCH RESEND v3 2/17 ARM: add self test for runtime patch mechanism

In reply to Cyril Chemparathy

This patch adds basic sanity tests to ensure that the instruction patching results in valid instruction encodings. This is done by verifying the output of the patch process against a vector of assembler generated instructions at init time.

---
 arch/arm/Kconfig                |   12 +++++++
 arch/arm/kernel/runtime-patch.c |   75 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 87 insertions(+)
arch/arm/Kconfig +12 -0
index 98a3a1a..5bfaa20 100644
@@ -205,6 +205,18 @@ config ARM_PATCH_PHYS_VIRT
 	  this feature (eg, building a kernel for a single machine) and
 	  you need to shrink the kernel to the minimal size.
 
+config ARM_RUNTIME_PATCH_TEST
+	bool "Self test runtime patching mechanism" if ARM_RUNTIME_PATCH
+	default y
+	help
+	  Select this to enable init time self checking for the runtime kernel
+	  patching mechanism.  This enables an ISA specific set of tests that
+	  ensure that the instructions generated by the patch process are
+	  consistent with those generated by the assembler at compile time.
+
+	  Only disable this option if you need to shrink the kernel to the
+	  minimal size.
+
 config NEED_MACH_IO_H
 	bool
 	help
arch/arm/kernel/runtime-patch.c +75 -0

86 lines not on this page · raw message

Signature · 1 line
1.7.9.5
#8 lore raw

PATCH RESEND v3 3/17 ARM: use late patch framework for phys-virt patching

In reply to Cyril Chemparathy

This patch replaces the original physical offset patching implementation with one that uses the newly added patching framework.

---
 arch/arm/Kconfig              |    1 +
 arch/arm/include/asm/memory.h |   26 +++--------
 arch/arm/kernel/armksyms.c    |    4 --
 arch/arm/kernel/head.S        |   95 +++++++----------------------------------
 arch/arm/kernel/module.c      |    5 ---
 arch/arm/kernel/setup.c       |   12 ++++++
 arch/arm/kernel/vmlinux.lds.S |    5 ---
 7 files changed, 36 insertions(+), 112 deletions(-)
arch/arm/Kconfig +1 -0
index 5bfaa20..0a92735 100644
@@ -193,6 +193,7 @@ config ARM_PATCH_PHYS_VIRT
 	default y
 	depends on !XIP_KERNEL && MMU
 	depends on !ARCH_REALVIEW || !SPARSEMEM
+	select ARM_RUNTIME_PATCH
 	help
 	  Patch phys-to-virt and virt-to-phys translation functions at
 	  boot and module load time according to the position of the
arch/arm/include/asm/memory.h +7 -19
index 5f6ddcc..c88d98c 100644
@@ -18,6 +18,8 @@
 #include <linux/types.h>
 #include <linux/sizes.h>
 
+#include <asm/runtime-patch.h>
+
 #ifdef CONFIG_NEED_MACH_MEMORY_H
 #include <mach/memory.h>
 #endif
@@ -151,35 +153,21 @@
 #ifndef __virt_to_phys
 #ifdef CONFIG_ARM_PATCH_PHYS_VIRT
 
-/*
- * Constants used to force the right instruction encodings and shifts
- * so that all we need to do is modify the 8-bit constant field.
- */
-#define __PV_BITS_31_24	0x81000000
-
-extern unsigned long __pv_phys_offset;
-#define PHYS_OFFSET __pv_phys_offset
-
-#define __pv_stub(from,to,instr,type)			\
-	__asm__("@ __pv_stub\n"				\
-	"1:	" instr "	%0, %1, %2\n"		\
-	"	.pushsection .pv_table,\"a\"\n"		\
-	"	.long	1b\n"				\
-	"	.popsection\n"				\
-	: "=r" (to)					\
-	: "r" (from), "I" (type))
+extern unsigned long	__pv_offset;
+extern unsigned long	__pv_phys_offset;
+#define PHYS_OFFSET	__virt_to_phys(PAGE_OFFSET)
 
 static inline unsigned long __virt_to_phys(unsigned long x)
 {
 	unsigned long t;
-	__pv_stub(x, t, "add", __PV_BITS_31_24);
+	early_patch_imm8("add", t, x, __pv_offset, 0);
 	return t;
 }
 
 static inline unsigned long __phys_to_virt(unsigned long x)
 {
 	unsigned long t;
-	__pv_stub(x, t, "sub", __PV_BITS_31_24);
+	early_patch_imm8("sub", t, x, __pv_offset, 0);
 	return t;
 }
 #else
arch/arm/kernel/armksyms.c +0 -4
index 60d3b73..6b388f8 100644
@@ -152,7 +152,3 @@ EXPORT_SYMBOL(mcount);
 #endif
 EXPORT_SYMBOL(__gnu_mcount_nc);
 #endif
-
-#ifdef CONFIG_ARM_PATCH_PHYS_VIRT
-EXPORT_SYMBOL(__pv_phys_offset);
-#endif
arch/arm/kernel/head.S +16 -79

115 lines not on this page · raw message

arch/arm/kernel/module.c +0 -5
index 10a2922..021a940 100644
@@ -317,11 +317,6 @@ int module_finalize(const Elf32_Ehdr *hdr, const Elf_Shdr *sechdrs,
 					         maps[i].txt_sec->sh_addr,
 					         maps[i].txt_sec->sh_size);
 #endif
-#ifdef CONFIG_ARM_PATCH_PHYS_VIRT
-	s = find_mod_section(hdr, sechdrs, ".pv_table");
-	if (s)
-		fixup_pv_table((void *)s->sh_addr, s->sh_size);
-#endif
 	s = find_mod_section(hdr, sechdrs, ".runtime.patch.table");
 	if (s) {
 		err = runtime_patch((void *)s->sh_addr, s->sh_size);
arch/arm/kernel/setup.c +12 -0
index 669bbf0..59e0f57 100644
@@ -152,6 +152,18 @@ static union { char c[4]; unsigned long l; } endian_test __initdata = { { 'l', '
 
 DEFINE_PER_CPU(struct cpuinfo_arm, cpu_data);
 
+#ifdef CONFIG_ARM_PATCH_PHYS_VIRT
+
+/*
+ * These are initialized in head.S code prior to BSS getting cleared out.
+ * The initializers here prevent these from landing in the BSS section.
+ */
+unsigned long __pv_offset = 0xdeadbeef;
+unsigned long __pv_phys_offset = 0xdeadbeef;
+EXPORT_SYMBOL(__pv_phys_offset);
+
+#endif
+
 /*
  * Standard memory resources
  */
arch/arm/kernel/vmlinux.lds.S +0 -5
index ea35ca0..2080111 100644
@@ -162,11 +162,6 @@ SECTIONS
 		__smpalt_end = .;
 	}
 #endif
-	.init.pv_table : {
-		__pv_table_begin = .;
-		*(.pv_table)
-		__pv_table_end = .;
-	}
 	.init.runtime_patch_table : {
 		__runtime_patch_table_begin = .;
 		*(.runtime.patch.table)
Signature · 1 line
1.7.9.5
#9 lore raw

PATCH RESEND v3 4/17 ARM: LPAE: use phys_addr_t on virt <--> phys conversion

In reply to Cyril Chemparathy

This patch fixes up the types used when converting back and forth between physical and virtual addresses.

---
 arch/arm/include/asm/memory.h |   26 ++++++++++++++++++--------
 1 file changed, 18 insertions(+), 8 deletions(-)
arch/arm/include/asm/memory.h +18 -8
index c88d98c..88ca206 100644
@@ -157,22 +157,32 @@ extern unsigned long	__pv_offset;
 extern unsigned long	__pv_phys_offset;
 #define PHYS_OFFSET	__virt_to_phys(PAGE_OFFSET)
 
-static inline unsigned long __virt_to_phys(unsigned long x)
+static inline phys_addr_t __virt_to_phys(unsigned long x)
 {
 	unsigned long t;
 	early_patch_imm8("add", t, x, __pv_offset, 0);
 	return t;
 }
 
-static inline unsigned long __phys_to_virt(unsigned long x)
+static inline unsigned long __phys_to_virt(phys_addr_t x)
 {
-	unsigned long t;
-	early_patch_imm8("sub", t, x, __pv_offset, 0);
+	unsigned long t, xlo = x;
+	early_patch_imm8("sub", t, xlo, __pv_offset, 0);
 	return t;
 }
+
 #else
-#define __virt_to_phys(x)	((x) - PAGE_OFFSET + PHYS_OFFSET)
-#define __phys_to_virt(x)	((x) - PHYS_OFFSET + PAGE_OFFSET)
+
+static inline phys_addr_t __virt_to_phys(unsigned long x)
+{
+	return (phys_addr_t)x - PAGE_OFFSET + PHYS_OFFSET;
+}
+
+static inline unsigned long __phys_to_virt(phys_addr_t x)
+{
+	return x - PHYS_OFFSET + PAGE_OFFSET;
+}
+
 #endif
 #endif
 #endif /* __ASSEMBLY__ */
@@ -210,14 +220,14 @@ static inline phys_addr_t virt_to_phys(const volatile void *x)
 
 static inline void *phys_to_virt(phys_addr_t x)
 {
-	return (void *)(__phys_to_virt((unsigned long)(x)));
+	return (void *)__phys_to_virt(x);
 }
 
 /*
  * Drivers should NOT use these either.
  */
 #define __pa(x)			__virt_to_phys((unsigned long)(x))
-#define __va(x)			((void *)__phys_to_virt((unsigned long)(x)))
+#define __va(x)			((void *)__phys_to_virt((phys_addr_t)(x)))
 #define pfn_to_kaddr(pfn)	__va((pfn) << PAGE_SHIFT)
 
 /*
Signature · 1 line
1.7.9.5
#11 lore raw

PATCH RESEND v3 4/17 ARM: LPAE: use phys_addr_t on virt <--> phys conversion

Quoted 10 lines · Catalin Marinas
Quoted 6 lines · Cyril Chemparathy

This patch fixes up the types used when converting back and forth between physical and virtual addresses.

Thanks, Catalin.

-- Cyril.

#12 lore raw

PATCH RESEND v3 5/17 ARM: LPAE: support 64-bit virt_to_phys patching

In reply to Cyril Chemparathy

This patch adds support for 64-bit physical addresses in virt_to_phys() patching. This does not do real 64-bit add/sub, but instead patches in the upper 32-bits of the phys_offset directly into the output of virt_to_phys.

There is no corresponding change on the phys_to_virt() side, because computations on the upper 32-bits would be discarded anyway.

---
 arch/arm/include/asm/memory.h |   38 ++++++++++++++++++++++++++++++++++++--
 arch/arm/kernel/head.S        |    4 ++++
 arch/arm/kernel/setup.c       |    2 +-
 3 files changed, 41 insertions(+), 3 deletions(-)
arch/arm/include/asm/memory.h +36 -2
index 88ca206..f3e8f88 100644
@@ -154,13 +154,47 @@
 #ifdef CONFIG_ARM_PATCH_PHYS_VIRT
 
 extern unsigned long	__pv_offset;
-extern unsigned long	__pv_phys_offset;
+extern phys_addr_t	__pv_phys_offset;
 #define PHYS_OFFSET	__virt_to_phys(PAGE_OFFSET)
 
 static inline phys_addr_t __virt_to_phys(unsigned long x)
 {
-	unsigned long t;
+	phys_addr_t t;
+
+#ifndef CONFIG_ARM_LPAE
 	early_patch_imm8("add", t, x, __pv_offset, 0);
+#else
+	unsigned long __tmp;
+
+#ifndef __ARMEB__
+#define PV_PHYS_HIGH	"(__pv_phys_offset + 4)"
+#else
+#define PV_PHYS_HIGH	"__pv_phys_offset"
+#endif
+
+	early_patch_stub(
+	/* type */		PATCH_IMM8,
+	/* code */
+		"ldr		%[tmp], =__pv_offset\n"
+		"ldr		%[tmp], [%[tmp]]\n"
+		"add		%Q[to], %[from], %[tmp]\n"
+		"ldr		%[tmp], =" PV_PHYS_HIGH "\n"
+		"ldr		%[tmp], [%[tmp]]\n"
+		"mov		%R[to], %[tmp]\n",
+	/* pad */		4,
+	/* patch_data */
+		".long		__pv_offset\n"
+		"add		%Q[to], %[from], %[imm]\n"
+		".long	"	PV_PHYS_HIGH "\n"
+		"mov		%R[to], %[imm]\n",
+	/* operands */
+		: [to]	 "=r"	(t),
+		  [tmp]	 "=&r"	(__tmp)
+		: [from] "r"	(x),
+		  [imm]	 "I"	(__IMM8),
+			 "i"	(&__pv_offset),
+			 "i"	(&__pv_phys_offset));
+#endif
 	return t;
 }
 
arch/arm/kernel/head.S +4 -0
index 69a3c09..61fb8df 100644
@@ -530,7 +530,11 @@ ENDPROC(__fixup_pv_offsets)
 
 	.align
 1:	.long	.
+#if defined(CONFIG_ARM_LPAE) && defined(__ARMEB__)
+	.long	__pv_phys_offset + 4
+#else
 	.long	__pv_phys_offset
+#endif
 	.long	__pv_offset
 	.long	PAGE_OFFSET
 #endif
arch/arm/kernel/setup.c +1 -1
index 59e0f57..edb4f42 100644
@@ -159,7 +159,7 @@ DEFINE_PER_CPU(struct cpuinfo_arm, cpu_data);
  * The initializers here prevent these from landing in the BSS section.
  */
 unsigned long __pv_offset = 0xdeadbeef;
-unsigned long __pv_phys_offset = 0xdeadbeef;
+phys_addr_t   __pv_phys_offset = 0xdeadbeef;
 EXPORT_SYMBOL(__pv_phys_offset);
 
 #endif
Signature · 1 line
1.7.9.5
Reviewed-by on v3 5/17
#13 lore raw

Re: [PATCH v3 RESEND 05/17] ARM: LPAE: support 64-bit virt_to_phys patching

Quoted 8 lines · Cyril Chemparathy

This patch adds support for 64-bit physical addresses in virt_to_phys() patching. This does not do real 64-bit add/sub, but instead patches in the upper 32-bits of the phys_offset directly into the output of virt_to_phys.

There is no corresponding change on the phys_to_virt() side, because computations on the upper 32-bits would be discarded anyway.

Quoted 92 lines
---
 arch/arm/include/asm/memory.h |   38 ++++++++++++++++++++++++++++++++++++--
 arch/arm/kernel/head.S        |    4 ++++
 arch/arm/kernel/setup.c       |    2 +-
 3 files changed, 41 insertions(+), 3 deletions(-)
arch/arm/include/asm/memory.h +36 -2
index 88ca206..f3e8f88 100644
@@ -154,13 +154,47 @@
 #ifdef CONFIG_ARM_PATCH_PHYS_VIRT
 
 extern unsigned long	__pv_offset;
-extern unsigned long	__pv_phys_offset;
+extern phys_addr_t	__pv_phys_offset;
 #define PHYS_OFFSET	__virt_to_phys(PAGE_OFFSET)
 
 static inline phys_addr_t __virt_to_phys(unsigned long x)
 {
-	unsigned long t;
+	phys_addr_t t;
+
+#ifndef CONFIG_ARM_LPAE
 	early_patch_imm8("add", t, x, __pv_offset, 0);
+#else
+	unsigned long __tmp;
+
+#ifndef __ARMEB__
+#define PV_PHYS_HIGH	"(__pv_phys_offset + 4)"
+#else
+#define PV_PHYS_HIGH	"__pv_phys_offset"
+#endif
+
+	early_patch_stub(
+	/* type */		PATCH_IMM8,
+	/* code */
+		"ldr		%[tmp], =__pv_offset\n"
+		"ldr		%[tmp], [%[tmp]]\n"
+		"add		%Q[to], %[from], %[tmp]\n"
+		"ldr		%[tmp], =" PV_PHYS_HIGH "\n"
+		"ldr		%[tmp], [%[tmp]]\n"
+		"mov		%R[to], %[tmp]\n",
+	/* pad */		4,
+	/* patch_data */
+		".long		__pv_offset\n"
+		"add		%Q[to], %[from], %[imm]\n"
+		".long	"	PV_PHYS_HIGH "\n"
+		"mov		%R[to], %[imm]\n",
+	/* operands */
+		: [to]	 "=r"	(t),
+		  [tmp]	 "=&r"	(__tmp)
+		: [from] "r"	(x),
+		  [imm]	 "I"	(__IMM8),
+			 "i"	(&__pv_offset),
+			 "i"	(&__pv_phys_offset));
+#endif
 	return t;
 }
 
arch/arm/kernel/head.S +4 -0
index 69a3c09..61fb8df 100644
@@ -530,7 +530,11 @@ ENDPROC(__fixup_pv_offsets)
 
 	.align
 1:	.long	.
+#if defined(CONFIG_ARM_LPAE) && defined(__ARMEB__)
+	.long	__pv_phys_offset + 4
+#else
 	.long	__pv_phys_offset
+#endif
 	.long	__pv_offset
 	.long	PAGE_OFFSET
 #endif
arch/arm/kernel/setup.c +1 -1
index 59e0f57..edb4f42 100644
@@ -159,7 +159,7 @@ DEFINE_PER_CPU(struct cpuinfo_arm, cpu_data);
  * The initializers here prevent these from landing in the BSS section.
  */
 unsigned long __pv_offset = 0xdeadbeef;
-unsigned long __pv_phys_offset = 0xdeadbeef;
+phys_addr_t   __pv_phys_offset = 0xdeadbeef;
 EXPORT_SYMBOL(__pv_phys_offset);
 
 #endif
Signature · 1 line
1.7.9.5
#14 lore raw

PATCH RESEND v3 5/17 ARM: LPAE: support 64-bit virt_to_phys patching

In reply to Cyril Chemparathy
Quoted 3 lines · Cyril Chemparathy

This patch adds support for 64-bit physical addresses in virt_to_phys() patching. This does not do real 64-bit add/sub, but instead patches in the upper 32-bits of the phys_offset directly into the output of virt_to_phys.

So this assumes that for the kernel linear mapping, all the physical addresses have the same upper 32-bit. That's a good optimisation but I haven't seen this check when calculating lowmem in sanity_check_meminfo. Someone may build platform with memory starting at 3GB and going across the 4GB limit.

Signature · 1 line
Catalin
#15 lore raw

PATCH RESEND v3 5/17 ARM: LPAE: support 64-bit virt_to_phys patching

Quoted 10 lines · Catalin Marinas
Quoted 3 lines · Cyril Chemparathy

This patch adds support for 64-bit physical addresses in virt_to_phys() patching. This does not do real 64-bit add/sub, but instead patches in the upper 32-bits of the phys_offset directly into the output of virt_to_phys.

So this assumes that for the kernel linear mapping, all the physical addresses have the same upper 32-bit. That's a good optimisation but I haven't seen this check when calculating lowmem in sanity_check_meminfo. Someone may build platform with memory starting at 3GB and going across the 4GB limit.

Good point. We better get an early warning if that happens.

Nicolas

#16 lore raw

PATCH RESEND v3 5/17 ARM: LPAE: support 64-bit virt_to_phys patching

Quoted 7 lines · Nicolas Pitre
Quoted 5 lines · Catalin Marinas
Quoted 3 lines · Cyril Chemparathy

This patch adds support for 64-bit physical addresses in virt_to_phys() patching. This does not do real 64-bit add/sub, but instead patches in the upper 32-bits of the phys_offset directly into the output of virt_to_phys.

Quoted 8 lines
Quoted 5 lines

So this assumes that for the kernel linear mapping, all the physical addresses have the same upper 32-bit. That's a good optimisation but I haven't seen this check when calculating lowmem in sanity_check_meminfo. Someone may build platform with memory starting at 3GB and going across the 4GB limit.

Good point. We better get an early warning if that happens.

Thanks.

I'm thinking of splitting the bank at the 32-bit boundary in such an event, assuming that the remaining memory should be usable as highmem.

Signature · 2 lines
Thanks
- Cyril
#17 lore raw

PATCH RESEND v3 5/17 ARM: LPAE: support 64-bit virt_to_phys patching

Quoted 16 lines · Cyril Chemparathy
Quoted 15 lines · Nicolas Pitre
Quoted 12 lines · Catalin Marinas
Quoted 5 lines · Cyril Chemparathy

This patch adds support for 64-bit physical addresses in virt_to_phys() patching. This does not do real 64-bit add/sub, but instead patches in the
upper 32-bits of the phys_offset directly into the output of virt_to_phys.

So this assumes that for the kernel linear mapping, all the physical addresses have the same upper 32-bit. That's a good optimisation but I haven't seen this check when calculating lowmem in sanity_check_meminfo. Someone may build platform with memory starting at 3GB and going across the 4GB limit.

Quoted 7 lines
Quoted 2 lines

Good point. We better get an early warning if that happens.

Thanks.

I'm thinking of splitting the bank at the 32-bit boundary in such an event, assuming that the remaining memory should be usable as highmem.

No. That's not the point here.

Let's suppose a system with 1GB of physical RAM starting at 0xe0000000.

In this case there is no need for highmem. However the v2p patching code in the LPAE case assumes the high bits of a physical address are always the same which wouldn't be the case in this hypothetical example.

We want to make sure the kernel won't boot if a system with physical RAM crossing the 4GB address mark is encountered.

Nicolas

#18 lore raw

PATCH RESEND v3 5/17 ARM: LPAE: support 64-bit virt_to_phys patching

Quoted 28 lines · Nicolas Pitre
Quoted 16 lines · Cyril Chemparathy
Quoted 15 lines · Nicolas Pitre
Quoted 12 lines · Catalin Marinas
Quoted 5 lines · Cyril Chemparathy

This patch adds support for 64-bit physical addresses in virt_to_phys() patching. This does not do real 64-bit add/sub, but instead patches in the
upper 32-bits of the phys_offset directly into the output of virt_to_phys.

So this assumes that for the kernel linear mapping, all the physical addresses have the same upper 32-bit. That's a good optimisation but I haven't seen this check when calculating lowmem in sanity_check_meminfo. Someone may build platform with memory starting at 3GB and going across the 4GB limit.

Quoted 7 lines
Quoted 2 lines

Good point. We better get an early warning if that happens.

Thanks.

I'm thinking of splitting the bank at the 32-bit boundary in such an event, assuming that the remaining memory should be usable as highmem.

No. That's not the point here.

Quoted 8 lines

Let's suppose a system with 1GB of physical RAM starting at 0xe0000000.

In this case there is no need for highmem. However the v2p patching code in the LPAE case assumes the high bits of a physical address are always the same which wouldn't be the case in this hypothetical example.

We want to make sure the kernel won't boot if a system with physical RAM crossing the 4GB address mark is encountered.

I think that's too restrictive. The case with 1 or 2GB below 4GB limit and the rest of the RAM above is a valid one. For such configurations we just limit the lowmem to the memory within 4GB and leave the rest as highmem. Another example is with RAM between 6GB and 10GB.

So we can consider lowmem all the addresses with the same upper 32-bit as the start of the first memory block.

Of course, if the lowmem turns out to be very small because of a weird address, the kernel will probably fail shortly anyway but I don't think we'll have such configurations.

Signature · 1 line
Catalin
#19 lore raw

PATCH RESEND v3 5/17 ARM: LPAE: support 64-bit virt_to_phys patching

Quoted 35 lines · Catalin Marinas
Quoted 34 lines · Nicolas Pitre
Quoted 16 lines · Cyril Chemparathy
Quoted 15 lines · Nicolas Pitre
Quoted 12 lines · Catalin Marinas
Quoted 5 lines · Cyril Chemparathy

This patch adds support for 64-bit physical addresses in virt_to_phys() patching. This does not do real 64-bit add/sub, but instead patches in the
upper 32-bits of the phys_offset directly into the output of virt_to_phys.

So this assumes that for the kernel linear mapping, all the physical addresses have the same upper 32-bit. That's a good optimisation but I haven't seen this check when calculating lowmem in sanity_check_meminfo. Someone may build platform with memory starting at 3GB and going across the 4GB limit.

Quoted 7 lines
Quoted 2 lines

Good point. We better get an early warning if that happens.

Thanks.

I'm thinking of splitting the bank at the 32-bit boundary in such an event, assuming that the remaining memory should be usable as highmem.

No. That's not the point here.

Let's suppose a system with 1GB of physical RAM starting at 0xe0000000.

In this case there is no need for highmem. However the v2p patching code in the LPAE case assumes the high bits of a physical address are always the same which wouldn't be the case in this hypothetical example.

Quoted 5 lines
Quoted 2 lines

We want to make sure the kernel won't boot if a system with physical RAM crossing the 4GB address mark is encountered.

I think that's too restrictive. The case with 1 or 2GB below 4GB limit and the rest of the RAM above is a valid one.

Valid: sure. Likely: probably not. That would complicate address decoding in hardware for little gain.

Quoted 3 lines

For such configurations
we just limit the lowmem to the memory within 4GB and leave the rest as highmem.

We don't want to limit lowmem. The lowmem is very precious memory and we want to maximize its size. In that case it is probably best to implement a real patchable 64-bit addition rather than artificially restricting the lowmem size.

Nicolas

#20 lore raw

PATCH RESEND v3 5/17 ARM: LPAE: support 64-bit virt_to_phys patching

Quoted 4 lines · Nicolas Pitre

We don't want to limit lowmem. The lowmem is very precious memory and we want to maximize its size. In that case it is probably best to implement a real patchable 64-bit addition rather than artificially restricting the lowmem size.

You don't need to. You can solve the V->P translation like this:

        movw    %hi, #0xVWXY            @ fixup
        adds    %lo, %lo, #offset       @ fixup
        adc     %hi, %hi, #0

which is probably the simplest way to do the fixup - keep the existing fixup code, and add support for that movw instruction. And that will work across any 4GB boundary just fine (we won't have more than 4GB of virtual address space on a 32-bit CPU anyway, so we only have to worry about one carry.)

And the P->V translation is truncating anyway, so that is just:

	sub	%lo, %lo, #offset

and nothing more.

#21 lore raw

PATCH RESEND v3 5/17 ARM: LPAE: support 64-bit virt_to_phys patching

Quoted 18 lines · Russell King - ARM Linux
Quoted 4 lines · Nicolas Pitre

We don't want to limit lowmem. The lowmem is very precious memory and we want to maximize its size. In that case it is probably best to implement a real patchable 64-bit addition rather than artificially restricting the lowmem size.

You don't need to. You can solve the V->P translation like this:

         movw    %hi, #0xVWXY            @ fixup
         adds    %lo, %lo, #offset       @ fixup
         adc     %hi, %hi, #0

which is probably the simplest way to do the fixup - keep the existing fixup code, and add support for that movw instruction. And that will work across any 4GB boundary just fine (we won't have more than 4GB of virtual address space on a 32-bit CPU anyway, so we only have to worry about one carry.)

Quoted 6 lines

And the P->V translation is truncating anyway, so that is just:

	sub	%lo, %lo, #offset

and nothing more.

The existing fixup code runs too early - before we get a chance to switch over from the boot-time sub-4G alias to the real physical address space. Readability aside, part of the reasoning behind the C re-implementation was to defer the patch application until a later point in the boot process.

That said, we could patch once at boot, and then re-patch at switch over, I guess. Is that what you're recommending?

Signature · 2 lines
Thanks
- Cyril
#22 lore raw

PATCH RESEND v3 5/17 ARM: LPAE: support 64-bit virt_to_phys patching

Quoted 26 lines · Cyril Chemparathy
Quoted 18 lines · Russell King - ARM Linux
Quoted 4 lines · Nicolas Pitre

We don't want to limit lowmem. The lowmem is very precious memory and we want to maximize its size. In that case it is probably best to implement a real patchable 64-bit addition rather than artificially restricting the lowmem size.

You don't need to. You can solve the V->P translation like this:

         movw    %hi, #0xVWXY            @ fixup
         adds    %lo, %lo, #offset       @ fixup
         adc     %hi, %hi, #0

which is probably the simplest way to do the fixup - keep the existing fixup code, and add support for that movw instruction. And that will work across any 4GB boundary just fine (we won't have more than 4GB of virtual address space on a 32-bit CPU anyway, so we only have to worry about one carry.)

Quoted 6 lines

And the P->V translation is truncating anyway, so that is just:

	sub	%lo, %lo, #offset

and nothing more.

Quoted 7 lines

The existing fixup code runs too early - before we get a chance to switch over from the boot-time sub-4G alias to the real physical address space. Readability aside, part of the reasoning behind the C re-implementation was to defer the patch application until a later point in the boot process.

That said, we could patch once at boot, and then re-patch at switch over, I guess. Is that what you're recommending?

I don't think that would be wise. Let's keep only one patching pass with only one implementation.

It is true that the C version is more complex, mainly due to the fact that the instruction fixup code is more comprehensive. However, it is certainly easier to maintain in the long run, and much easier to debug.

The approach where a suboptimal stub is used until the actual patching takes place is pretty nice. That helps keeping the boot code small and simple.

Nicolas

#23 lore raw

PATCH RESEND v3 5/17 ARM: LPAE: support 64-bit virt_to_phys patching

In reply to Cyril Chemparathy
Quoted 8 lines · Cyril Chemparathy

The existing fixup code runs too early - before we get a chance to switch over from the boot-time sub-4G alias to the real physical address space. Readability aside, part of the reasoning behind the C re-implementation was to defer the patch application until a later point in the boot process.

That said, we could patch once at boot, and then re-patch at switch over, I guess. Is that what you're recommending?

That would solve the problem, and probably in the least disruptive manner too, since the existing solution will continue to work, and we'd only need to fixup the movw instructions if we're building in support for the rare 'high-lowmem' situation.

We don't even need any different module marking - the 'high-lowmem' modules don't need their movw instructions fixed.

#24 lore raw

PATCH RESEND v3 5/17 ARM: LPAE: support 64-bit virt_to_phys patching

In reply to Russell King - ARM Linux
Quoted 11 lines · Russell King - ARM Linux
Quoted 4 lines · Nicolas Pitre

We don't want to limit lowmem. The lowmem is very precious memory and we want to maximize its size. In that case it is probably best to implement a real patchable 64-bit addition rather than artificially restricting the lowmem size.

You don't need to. You can solve the V->P translation like this:

        movw    %hi, #0xVWXY            @ fixup
        adds    %lo, %lo, #offset       @ fixup
        adc     %hi, %hi, #0

That's exactly what I mean when I say a real 64-bit addition. Despite one of the arguments being a 32 bit value, the overflow needs to be carried to the high part.

Quoted 5 lines

which is probably the simplest way to do the fixup - keep the existing fixup code, and add support for that movw instruction. And that will work across any 4GB boundary just fine (we won't have more than 4GB of virtual address space on a 32-bit CPU anyway, so we only have to worry about one carry.)

Exactly.

Quoted 5 lines

And the P->V translation is truncating anyway, so that is just:

	sub	%lo, %lo, #offset

and nothing more.

Yes, that's what the posted code does already.

Nicolas

#25 lore raw

PATCH RESEND v3 5/17 ARM: LPAE: support 64-bit virt_to_phys patching

Quoted 26 lines · Nicolas Pitre
Quoted 11 lines · Russell King - ARM Linux
Quoted 4 lines · Nicolas Pitre

We don't want to limit lowmem. The lowmem is very precious memory and we want to maximize its size. In that case it is probably best to implement a real patchable 64-bit addition rather than artificially restricting the lowmem size.

You don't need to. You can solve the V->P translation like this:

        movw    %hi, #0xVWXY            @ fixup
        adds    %lo, %lo, #offset       @ fixup
        adc     %hi, %hi, #0

That's exactly what I mean when I say a real 64-bit addition. Despite one of the arguments being a 32 bit value, the overflow needs to be carried to the high part.

Quoted 5 lines

which is probably the simplest way to do the fixup - keep the existing fixup code, and add support for that movw instruction. And that will work across any 4GB boundary just fine (we won't have more than 4GB of virtual address space on a 32-bit CPU anyway, so we only have to worry about one carry.)

Exactly.

Quoted 7 lines
Quoted 5 lines

And the P->V translation is truncating anyway, so that is just:

	sub	%lo, %lo, #offset

and nothing more.

Yes, that's what the posted code does already.

Taking a step back, this is all feeling very complex. Do we actually need to solve the P2V patching problem for this platform?

For 32-bit platforms, especially pre-v6, small TLBs and caches, lack of branch prediction etc. may make the performance boost from P2V patching more significant, but I have some doubts about whether it is really worth it for more heavyweight v7+LPAE platforms.

Would it be worth trying a simple macro implementation that reads a global variable for this case, and investigating the performance impact?

For realistic workloads running on newer CPUs, I suspect that any costs from this may disappear into the noise...

Cheers
---Dave

#26 lore raw

PATCH RESEND v3 5/17 ARM: LPAE: support 64-bit virt_to_phys patching

In reply to Nicolas Pitre
Quoted 37 lines · Nicolas Pitre
Quoted 35 lines · Catalin Marinas
Quoted 34 lines · Nicolas Pitre
Quoted 16 lines · Cyril Chemparathy
Quoted 15 lines · Nicolas Pitre
Quoted 12 lines · Catalin Marinas
Quoted 5 lines · Cyril Chemparathy

This patch adds support for 64-bit physical addresses in virt_to_phys() patching. This does not do real 64-bit add/sub, but instead patches in the
upper 32-bits of the phys_offset directly into the output of virt_to_phys.

So this assumes that for the kernel linear mapping, all the physical addresses have the same upper 32-bit. That's a good optimisation but I haven't seen this check when calculating lowmem in sanity_check_meminfo. Someone may build platform with memory starting at 3GB and going across the 4GB limit.

Quoted 7 lines
Quoted 2 lines

Good point. We better get an early warning if that happens.

Thanks.

I'm thinking of splitting the bank at the 32-bit boundary in such an event, assuming that the remaining memory should be usable as highmem.

No. That's not the point here.

Let's suppose a system with 1GB of physical RAM starting at 0xe0000000.

In this case there is no need for highmem. However the v2p patching code in the LPAE case assumes the high bits of a physical address are always the same which wouldn't be the case in this hypothetical example.

Quoted 8 lines
Quoted 5 lines
Quoted 2 lines

We want to make sure the kernel won't boot if a system with physical RAM crossing the 4GB address mark is encountered.

I think that's too restrictive. The case with 1 or 2GB below 4GB limit and the rest of the RAM above is a valid one.

Valid: sure. Likely: probably not. That would complicate address decoding in hardware for little gain.

You could have a block of 4GB RAM at 0x100000000 but the top 2GB aliased at 0x80000000. It is also possible that the top 2GB are not visible to software directly but only via the 0x80000000 alias.

But with Russell's proposal for full 64-bit address patching, no limitations are needed.

Signature · 1 line
Catalin
#27 lore raw

PATCH RESEND v3 5/17 ARM: LPAE: support 64-bit virt_to_phys patching

In reply to Nicolas Pitre
Quoted 28 lines · Nicolas Pitre
Quoted 16 lines · Cyril Chemparathy
Quoted 15 lines · Nicolas Pitre
Quoted 12 lines · Catalin Marinas
Quoted 5 lines · Cyril Chemparathy

This patch adds support for 64-bit physical addresses in virt_to_phys() patching. This does not do real 64-bit add/sub, but instead patches in the
upper 32-bits of the phys_offset directly into the output of virt_to_phys.

So this assumes that for the kernel linear mapping, all the physical addresses have the same upper 32-bit. That's a good optimisation but I haven't seen this check when calculating lowmem in sanity_check_meminfo. Someone may build platform with memory starting at 3GB and going across the 4GB limit.

Quoted 7 lines
Quoted 2 lines

Good point. We better get an early warning if that happens.

Thanks.

I'm thinking of splitting the bank at the 32-bit boundary in such an event, assuming that the remaining memory should be usable as highmem.

No. That's not the point here.

Quoted 9 lines

Let's suppose a system with 1GB of physical RAM starting at 0xe0000000.

In this case there is no need for highmem. However the v2p patching code in the LPAE case assumes the high bits of a physical address are always the same which wouldn't be the case in this hypothetical example.

We want to make sure the kernel won't boot if a system with physical RAM crossing the 4GB address mark is encountered.

Why brick the box if memory crosses over the 4G mark?

Since this constraint (constant upper 32-bits of PA) applies only to lowmem, why couldn't this hypothetical system run with 256M of lowmem, and the remaining 768M being used as highmem (if enabled).

IOW, why can't we treat this constraint just like the vmalloc limit constraint in sanity_check_meminfo(), by splitting the bank at the limit?

Signature · 2 lines
Thanks
- Cyril
#28 lore raw

PATCH RESEND v3 5/17 ARM: LPAE: support 64-bit virt_to_phys patching

In reply to Cyril Chemparathy
Quoted 2 lines · Cyril Chemparathy

I'm thinking of splitting the bank at the 32-bit boundary in such an event, assuming that the remaining memory should be usable as highmem.

I'd say, don't make it any more complicated than it has to be. Don't overdesign, especially in this area, and from what I read in this thread, that's the danger here.

For the time being, we have two classes of systems: those where they have a decent amount of RAM located below the 4GB mark, and yours which have all their RAM above the 4GB mark - and again a decent size contained within any naturally aligned 4GB chunk of the address space.

So, at the moment making the restriction that the top 32-bits of the address space is constant _is_ the right thing to do.

And given that, I'd suggest that we reduce the size of these changes. We don't need to go and rewrite the P2V patching stuff - we can merely extend it.

The p->v translation can work just fine by ignoring the top 32-bits and adding the offset, as is done today.

The v->p translation can also work in the reverse way, except that we need to set the high order bits. That can be done with a mov or movw instruction, which the v:p patching code _can_ recognise and do the appropriate thing.

And let's not forget that the range over which the v:p translation has to work is just the lowmem only, which on most normal configurations is no more than 1GB. Though that can be resolved if the high-order bits need to change by doing this in the v->p translation:

	movw	%hi, #0xVWXY		@ fixup
	adds	%lo, %lo, #offset	@ fixup
	adc	%hi, %hi, #0

_if_ we need to.

#29 lore raw

Re: [PATCH v3 RESEND 05/17] ARM: LPAE: support 64-bit virt_to_phys patching

In reply to Cyril Chemparathy
Quoted 54 lines · Cyril Chemparathy

This patch adds support for 64-bit physical addresses in virt_to_phys() patching. This does not do real 64-bit add/sub, but instead patches in the upper 32-bits of the phys_offset directly into the output of virt_to_phys.

There is no corresponding change on the phys_to_virt() side, because computations on the upper 32-bits would be discarded anyway.

---
 arch/arm/include/asm/memory.h |   38 ++++++++++++++++++++++++++++++++++++--
 arch/arm/kernel/head.S        |    4 ++++
 arch/arm/kernel/setup.c       |    2 +-
 3 files changed, 41 insertions(+), 3 deletions(-)
arch/arm/include/asm/memory.h +25 -2
index 88ca206..f3e8f88 100644
@@ -154,13 +154,47 @@
 #ifdef CONFIG_ARM_PATCH_PHYS_VIRT
 
 extern unsigned long	__pv_offset;
-extern unsigned long	__pv_phys_offset;
+extern phys_addr_t	__pv_phys_offset;
 #define PHYS_OFFSET	__virt_to_phys(PAGE_OFFSET)
 
 static inline phys_addr_t __virt_to_phys(unsigned long x)
 {
-	unsigned long t;
+	phys_addr_t t;
+
+#ifndef CONFIG_ARM_LPAE
 	early_patch_imm8("add", t, x, __pv_offset, 0);
+#else
+	unsigned long __tmp;
+
+#ifndef __ARMEB__
+#define PV_PHYS_HIGH	"(__pv_phys_offset + 4)"
+#else
+#define PV_PHYS_HIGH	"__pv_phys_offset"
+#endif
+
+	early_patch_stub(
+	/* type */		PATCH_IMM8,
+	/* code */
+		"ldr		%[tmp], =__pv_offset\n"
+		"ldr		%[tmp], [%[tmp]]\n"
+		"add		%Q[to], %[from], %[tmp]\n"
+		"ldr		%[tmp], =" PV_PHYS_HIGH "\n"
+		"ldr		%[tmp], [%[tmp]]\n"
+		"mov		%R[to], %[tmp]\n",
+	/* pad */		4,
+	/* patch_data */
+		".long		__pv_offset\n"

arch/arm/include/asm/memory.h line 196

Quoted 10 lines
arch/arm/include/asm/memory.h +10 -0
+		"add		%Q[to], %[from], %[imm]\n"
+		".long	"	PV_PHYS_HIGH "\n"
+		"mov		%R[to], %[imm]\n",
+	/* operands */
+		: [to]	 "=r"	(t),
+		  [tmp]	 "=&r"	(__tmp)
+		: [from] "r"	(x),
+		  [imm]	 "I"	(__IMM8),
+			 "i"	(&__pv_offset),
+			 "i"	(&__pv_phys_offset));

So, the actual offset we can apply is:

__pv_phys_offset + __pv_offset

where:

* the high 32 bits of the address being fixed up are assumed to be 0 (true, because the kernel is initially always fixed up to an address range <4GB)

* the low 32 bits of __pv_phys_offset are assumed to be 0 (?)

* the full offset is of the form

        ([..0..]XX[..0..] << 32) | [..0..]YY[..0..] 

Is this intentional? It seems like a rather weird constraint... but it may be sensible. PAGE_OFFSET is probably 0xc0000000 or 0x80000000, (so YY can handle that) and the actual RAM above 4GB will likely be huge and aligned on some enormous boundary in such situations (so that XX can handle that).

So long as the low RAM alias is not misaligned relative to the high alias on a finer granularity than 16MB (so that YY = (PAGE_OFFSET +/- the misalignment) is still a legal immediate), I guess there should not be a problem.

[...]

Cheers
---Dave

#30 lore raw

Re: [PATCH v3 RESEND 05/17] ARM: LPAE: support 64-bit virt_to_phys patching

Quoted 68 lines · Dave Martin
Quoted 54 lines · Cyril Chemparathy

This patch adds support for 64-bit physical addresses in virt_to_phys() patching. This does not do real 64-bit add/sub, but instead patches in the upper 32-bits of the phys_offset directly into the output of virt_to_phys.

There is no corresponding change on the phys_to_virt() side, because computations on the upper 32-bits would be discarded anyway.

---
 arch/arm/include/asm/memory.h |   38 ++++++++++++++++++++++++++++++++++++--
 arch/arm/kernel/head.S        |    4 ++++
 arch/arm/kernel/setup.c       |    2 +-
 3 files changed, 41 insertions(+), 3 deletions(-)
arch/arm/include/asm/memory.h +25 -2
index 88ca206..f3e8f88 100644
@@ -154,13 +154,47 @@
 #ifdef CONFIG_ARM_PATCH_PHYS_VIRT
 
 extern unsigned long	__pv_offset;
-extern unsigned long	__pv_phys_offset;
+extern phys_addr_t	__pv_phys_offset;
 #define PHYS_OFFSET	__virt_to_phys(PAGE_OFFSET)
 
 static inline phys_addr_t __virt_to_phys(unsigned long x)
 {
-	unsigned long t;
+	phys_addr_t t;
+
+#ifndef CONFIG_ARM_LPAE
 	early_patch_imm8("add", t, x, __pv_offset, 0);
+#else
+	unsigned long __tmp;
+
+#ifndef __ARMEB__
+#define PV_PHYS_HIGH	"(__pv_phys_offset + 4)"
+#else
+#define PV_PHYS_HIGH	"__pv_phys_offset"
+#endif
+
+	early_patch_stub(
+	/* type */		PATCH_IMM8,
+	/* code */
+		"ldr		%[tmp], =__pv_offset\n"
+		"ldr		%[tmp], [%[tmp]]\n"
+		"add		%Q[to], %[from], %[tmp]\n"
+		"ldr		%[tmp], =" PV_PHYS_HIGH "\n"
+		"ldr		%[tmp], [%[tmp]]\n"
+		"mov		%R[to], %[tmp]\n",
+	/* pad */		4,
+	/* patch_data */
+		".long		__pv_offset\n"

arch/arm/include/asm/memory.h line 196

Quoted 10 lines
arch/arm/include/asm/memory.h +10 -0
+		"add		%Q[to], %[from], %[imm]\n"
+		".long	"	PV_PHYS_HIGH "\n"
+		"mov		%R[to], %[imm]\n",
+	/* operands */
+		: [to]	 "=r"	(t),
+		  [tmp]	 "=&r"	(__tmp)
+		: [from] "r"	(x),
+		  [imm]	 "I"	(__IMM8),
+			 "i"	(&__pv_offset),
+			 "i"	(&__pv_phys_offset));

So, the actual offset we can apply is:

Quoted 7 lines

__pv_phys_offset + __pv_offset

where:

* the high 32 bits of the address being fixed up are assumed to be 0 (true, because the kernel is initially always fixed up to an address range <4GB)

The fixed up address is a virtual address. So yes, by definition it must be <4GB on ARM32.

Quoted 1 line

* the low 32 bits of __pv_phys_offset are assumed to be 0 (?)

It is typically representable with a shifted 8 bit immediate but not necessarily 0, just like on platforms without LPAE.

Quoted 10 lines

* the full offset is of the form

        ([..0..]XX[..0..] << 32) | [..0..]YY[..0..] 

Is this intentional? It seems like a rather weird constraint... but it may be sensible. PAGE_OFFSET is probably 0xc0000000 or 0x80000000, (so YY can handle that) and the actual RAM above 4GB will likely be huge and aligned on some enormous boundary in such situations (so that XX can handle that).

Quoted 4 lines

So long as the low RAM alias is not misaligned relative to the high alias on a finer granularity than 16MB (so that YY = (PAGE_OFFSET +/- the misalignment) is still a legal immediate), I guess there should not be a problem.

There are already similar constraints for the current ARM_PATCH_PHYS_VIRT code. So nothing really new here.

Nicolas

#31 lore raw

PATCH RESEND v3 6/17 ARM: LPAE: use signed arithmetic for mask definitions

In reply to Cyril Chemparathy

This patch applies to PAGE_MASK, PMD_MASK, and PGDIR_MASK, where forcing unsigned long math truncates the mask at the 32-bits. This clearly does bad things on PAE systems.

This patch fixes this problem by defining these masks as signed quantities. We then rely on sign extension to do the right thing.

---
 arch/arm/include/asm/page.h           |    2 +-
 arch/arm/include/asm/pgtable-3level.h |    6 +++---
 2 files changed, 4 insertions(+), 4 deletions(-)
arch/arm/include/asm/page.h +1 -1
index ecf9019..1e0fe08 100644
@@ -13,7 +13,7 @@
 /* PAGE_SHIFT determines the page size */
 #define PAGE_SHIFT		12
 #define PAGE_SIZE		(_AC(1,UL) << PAGE_SHIFT)
-#define PAGE_MASK		(~(PAGE_SIZE-1))
+#define PAGE_MASK		(~((1 << PAGE_SHIFT) - 1))
 
 #ifndef __ASSEMBLY__
 
arch/arm/include/asm/pgtable-3level.h +3 -3
index b249035..ae39d11 100644
@@ -48,16 +48,16 @@
 #define PMD_SHIFT		21
 
 #define PMD_SIZE		(1UL << PMD_SHIFT)
-#define PMD_MASK		(~(PMD_SIZE-1))
+#define PMD_MASK		(~((1 << PMD_SHIFT) - 1))
 #define PGDIR_SIZE		(1UL << PGDIR_SHIFT)
-#define PGDIR_MASK		(~(PGDIR_SIZE-1))
+#define PGDIR_MASK		(~((1 << PGDIR_SHIFT) - 1))
 
 /*
  * section address mask and size definitions.
  */
 #define SECTION_SHIFT		21
 #define SECTION_SIZE		(1UL << SECTION_SHIFT)
-#define SECTION_MASK		(~(SECTION_SIZE-1))
+#define SECTION_MASK		(~((1 << SECTION_SHIFT) - 1))
 
 #define USER_PTRS_PER_PGD	(PAGE_OFFSET / PGDIR_SIZE)
 
Signature · 1 line
1.7.9.5
#32 lore raw

PATCH RESEND v3 6/17 ARM: LPAE: use signed arithmetic for mask definitions

Quoted 10 lines · Cyril Chemparathy

This patch applies to PAGE_MASK, PMD_MASK, and PGDIR_MASK, where forcing unsigned long math truncates the mask at the 32-bits. This clearly does bad things on PAE systems.

This patch fixes this problem by defining these masks as signed quantities. We then rely on sign extension to do the right thing.

#33 lore raw

Re: [PATCH v3 RESEND 06/17] ARM: LPAE: use signed arithmetic for mask definitions

In reply to Cyril Chemparathy
Quoted 20 lines · Cyril Chemparathy

This patch applies to PAGE_MASK, PMD_MASK, and PGDIR_MASK, where forcing unsigned long math truncates the mask at the 32-bits. This clearly does bad things on PAE systems.

This patch fixes this problem by defining these masks as signed quantities. We then rely on sign extension to do the right thing.

---
 arch/arm/include/asm/page.h           |    2 +-
 arch/arm/include/asm/pgtable-3level.h |    6 +++---
 2 files changed, 4 insertions(+), 4 deletions(-)
arch/arm/include/asm/page.h +0 -0
index ecf9019..1e0fe08 100644

arch/arm/include/asm/page.h line 16

Quoted 5 lines
arch/arm/include/asm/page.h +1 -1
@@ -13,7 +13,7 @@
 /* PAGE_SHIFT determines the page size */
 #define PAGE_SHIFT		12
 #define PAGE_SIZE		(_AC(1,UL) << PAGE_SHIFT)
-#define PAGE_MASK		(~(PAGE_SIZE-1))
+#define PAGE_MASK		(~((1 << PAGE_SHIFT) - 1))

It's strange. Every other platform in the kernel, including those with PAE, manage to get away with defining PAGE_MASK as the original above (see asm-generic/pgtable.h) Why is ARM any different?

Note that PAGE_MASK gets used@the moment on ARM for both virtual and physical addresses. x86 has PHYSICAL_PAGE_MASK for masking physical addresses. Maybe we should adopt the same approach?

Whatever, I feel that we should not deviate from the established definitions across every other architecture.

Quoted 14 lines
arch/arm/include/asm/page.h +0 -0
 #ifndef __ASSEMBLY__
 
arch/arm/include/asm/pgtable-3level.h +1 -1
index b249035..ae39d11 100644
@@ -48,16 +48,16 @@
 #define PMD_SHIFT		21
 
 #define PMD_SIZE		(1UL << PMD_SHIFT)
-#define PMD_MASK		(~(PMD_SIZE-1))
+#define PMD_MASK		(~((1 << PMD_SHIFT) - 1))
 #define PGDIR_SIZE		(1UL << PGDIR_SHIFT)

arch/arm/include/asm/pgtable-3level.h line 60

Quoted 10 lines
arch/arm/include/asm/pgtable-3level.h +2 -2
-#define PGDIR_MASK		(~(PGDIR_SIZE-1))
+#define PGDIR_MASK		(~((1 << PGDIR_SHIFT) - 1))
 
 /*
  * section address mask and size definitions.
  */
 #define SECTION_SHIFT		21
 #define SECTION_SIZE		(1UL << SECTION_SHIFT)
-#define SECTION_MASK		(~(SECTION_SIZE-1))
+#define SECTION_MASK		(~((1 << SECTION_SHIFT) - 1))

These masks are applied to a _virtual_ kernel address, not the physical addresses, Even with LPAE, the virtual address space is still 32-bit.

So this is definitely wrong.

#34 lore raw

PATCH RESEND v3 7/17 ARM: LPAE: use phys_addr_t in alloc_init_pud()

In reply to Cyril Chemparathy

From: Vitaly Andrianov <[email protected]>

This patch fixes the alloc_init_pud() function to use phys_addr_t instead of unsigned long when passing in the phys argument.

This is an extension to commit 97092e0c56830457af0639f6bd904537a150ea4a (ARM:
pgtable: use phys_addr_t for physical addresses), which applied similar changes elsewhere in the ARM memory management code.

---
 arch/arm/mm/mmu.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
arch/arm/mm/mmu.c +2 -1
index c2fa21d..8db7c74 100644
@@ -622,7 +622,8 @@ static void __init alloc_init_section(pud_t *pud, unsigned long addr,
 }
 
 static void __init alloc_init_pud(pgd_t *pgd, unsigned long addr,
-	unsigned long end, unsigned long phys, const struct mem_type *type)
+				  unsigned long end, phys_addr_t phys,
+				  const struct mem_type *type)
 {
 	pud_t *pud = pud_offset(pgd, addr);
 	unsigned long next;
Signature · 1 line
1.7.9.5
#35 lore raw

PATCH RESEND v3 7/17 ARM: LPAE: use phys_addr_t in alloc_init_pud()

Quoted 5 lines · Cyril Chemparathy

From: Vitaly Andrianov <[email protected]>

This patch fixes the alloc_init_pud() function to use phys_addr_t instead of unsigned long when passing in the phys argument.

Quoted 7 lines

This is an extension to commit 97092e0c56830457af0639f6bd904537a150ea4a (ARM:
pgtable: use phys_addr_t for physical addresses), which applied similar changes elsewhere in the ARM memory management code.

#36 lore raw

PATCH RESEND v3 8/17 ARM: LPAE: use phys_addr_t in free_memmap()

In reply to Cyril Chemparathy

From: Vitaly Andrianov <[email protected]>

The free_memmap() was mistakenly using unsigned long type to represent physical addresses. This breaks on PAE systems where memory could be placed above the 32-bit addressible limit.

This patch fixes this function to properly use phys_addr_t instead.

---
 arch/arm/mm/init.c |    6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
arch/arm/mm/init.c +3 -3
index 9aec41f..19ba70b 100644
@@ -457,7 +457,7 @@ static inline void
 free_memmap(unsigned long start_pfn, unsigned long end_pfn)
 {
 	struct page *start_pg, *end_pg;
-	unsigned long pg, pgend;
+	phys_addr_t pg, pgend;
 
 	/*
 	 * Convert start_pfn/end_pfn to a struct page pointer.
@@ -469,8 +469,8 @@ free_memmap(unsigned long start_pfn, unsigned long end_pfn)
 	 * Convert to physical addresses, and
 	 * round start upwards and end downwards.
 	 */
-	pg = (unsigned long)PAGE_ALIGN(__pa(start_pg));
-	pgend = (unsigned long)__pa(end_pg) & PAGE_MASK;
+	pg = PAGE_ALIGN(__pa(start_pg));
+	pgend = __pa(end_pg) & PAGE_MASK;
 
 	/*
 	 * If there are free pages between these,
Signature · 1 line
1.7.9.5
#37 lore raw

PATCH RESEND v3 8/17 ARM: LPAE: use phys_addr_t in free_memmap()

Quoted 29 lines · Cyril Chemparathy

From: Vitaly Andrianov <[email protected]>

The free_memmap() was mistakenly using unsigned long type to represent physical addresses. This breaks on PAE systems where memory could be placed above the 32-bit addressible limit.

This patch fixes this function to properly use phys_addr_t instead.

---
 arch/arm/mm/init.c |    6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
arch/arm/mm/init.c +1 -1
index 9aec41f..19ba70b 100644
@@ -457,7 +457,7 @@ static inline void
 free_memmap(unsigned long start_pfn, unsigned long end_pfn)
 {
 	struct page *start_pg, *end_pg;
-	unsigned long pg, pgend;
+	phys_addr_t pg, pgend;
 
 	/*
 	 * Convert start_pfn/end_pfn to a struct page pointer.

arch/arm/mm/init.c free_memmap(unsigned long start_pfn, unsigned long end_pfn) · line 476

Quoted 10 lines
arch/arm/mm/init.c +2 -2
@@ -469,8 +469,8 @@ free_memmap(unsigned long start_pfn, unsigned long end_pfn)
 	 * Convert to physical addresses, and
 	 * round start upwards and end downwards.
 	 */
-	pg = (unsigned long)PAGE_ALIGN(__pa(start_pg));
-	pgend = (unsigned long)__pa(end_pg) & PAGE_MASK;
+	pg = PAGE_ALIGN(__pa(start_pg));
+	pgend = __pa(end_pg) & PAGE_MASK;
 
 	/*
 	 * If there are free pages between these,

This function also calls free_bootmem() which takes unsigned long. Are patches sent separately for this or we just ignore holes in memmap? There are other calls to free_bootmem() or reserve_bootmem(), do they just work with the high phys addresses?

Signature · 1 line
Catalin
#38 lore raw

PATCH RESEND v3 8/17 ARM: LPAE: use phys_addr_t in free_memmap()

Quoted 4 lines · Catalin Marinas

This function also calls free_bootmem() which takes unsigned long. Are patches sent separately for this or we just ignore holes in memmap? There are other calls to free_bootmem() or reserve_bootmem(), do they just work with the high phys addresses?

Bootmem only deals with physical addresses which fit within the size of an 'unsigned long'. Unfortunately, the bootmem API is a mess of 'unsigned long' physical addresses and PFNs.

Years ago there was a patch to make it use only PFNs but other changes resulted in that patch being thrown away.

#39 lore raw

PATCH RESEND v3 8/17 ARM: LPAE: use phys_addr_t in free_memmap()

Quoted 6 lines · Russell King - ARM Linux
Quoted 4 lines · Catalin Marinas

This function also calls free_bootmem() which takes unsigned long. Are patches sent separately for this or we just ignore holes in memmap? There are other calls to free_bootmem() or reserve_bootmem(), do they just work with the high phys addresses?

Quoted 7 lines

Bootmem only deals with physical addresses which fit within the size of an 'unsigned long'. Unfortunately, the bootmem API is a mess of 'unsigned long' physical addresses and PFNs.

Years ago there was a patch to make it use only PFNs but other changes resulted in that patch being thrown away.

A separate patch has been posted for bootmem (see [1]).

Tejun suggested that we'd be better off moving entirely to memblock instead (see [2]).

I'd be happy to take this up, but I'm not very familiar with the reasoning behind the mixed bootmem + memblock usage that we have today.
Some background and pointers on this topic would greatly help.

[1] https://lkml.org/lkml/2012/9/12/435
[2] https://lkml.org/lkml/2012/9/13/511

Signature · 2 lines
Thanks
- Cyril
#40 lore raw

PATCH RESEND v3 8/17 ARM: LPAE: use phys_addr_t in free_memmap()

Quoted 11 lines · Cyril Chemparathy
Quoted 10 lines · Russell King - ARM Linux
Quoted 4 lines · Catalin Marinas

This function also calls free_bootmem() which takes unsigned long. Are patches sent separately for this or we just ignore holes in memmap? There are other calls to free_bootmem() or reserve_bootmem(), do they just work with the high phys addresses?

Bootmem only deals with physical addresses which fit within the size of an 'unsigned long'. Unfortunately, the bootmem API is a mess of 'unsigned long' physical addresses and PFNs.

Quoted 8 lines
Quoted 3 lines

Years ago there was a patch to make it use only PFNs but other changes resulted in that patch being thrown away.

A separate patch has been posted for bootmem (see [1]).

Tejun suggested that we'd be better off moving entirely to memblock instead (see [2]).

Yes we should, but that's not as easy as typing a few words in an email. When I tried it when I integrated memblock into our boot sequence, I found that sparsemem wouldn't work without bootmem being in place.

It may be that things have now moved on, and we can just eliminate bootmem once and for all, but that's something I've not looked at for quite some time.

#41 lore raw

PATCH RESEND v3 8/17 ARM: LPAE: use phys_addr_t in free_memmap()

Quoted 21 lines · Russell King - ARM Linux
Quoted 11 lines · Cyril Chemparathy
Quoted 10 lines · Russell King - ARM Linux
Quoted 4 lines · Catalin Marinas

This function also calls free_bootmem() which takes unsigned long. Are patches sent separately for this or we just ignore holes in memmap? There are other calls to free_bootmem() or reserve_bootmem(), do they just work with the high phys addresses?

Bootmem only deals with physical addresses which fit within the size of an 'unsigned long'. Unfortunately, the bootmem API is a mess of 'unsigned long' physical addresses and PFNs.

Quoted 8 lines
Quoted 3 lines

Years ago there was a patch to make it use only PFNs but other changes resulted in that patch being thrown away.

A separate patch has been posted for bootmem (see [1]).

Tejun suggested that we'd be better off moving entirely to memblock instead (see [2]).

Quoted 8 lines

Yes we should, but that's not as easy as typing a few words in an email. When I tried it when I integrated memblock into our boot sequence, I found that sparsemem wouldn't work without bootmem being in place.

It may be that things have now moved on, and we can just eliminate bootmem once and for all, but that's something I've not looked at for quite some time.

It appears to be not that hard actually... Or maybe I'm totally missing your point. Could it be that you last looked at this prior to the nobootmem compatibility stuff being added in?

The following patch appears to work just fine on a faked sparsemem system:

arch/arm/Kconfig +0 -0
index 92f598a..e94fa4c 100644
@@ -49,6 +49,7 @@ config ARM
  	select GENERIC_STRNCPY_FROM_USER
  	select GENERIC_STRNLEN_USER
  	select DCACHE_WORD_ACCESS if (CPU_V6 || CPU_V6K || CPU_V7) && 

!CPU_BIG_ENDIAN
+ select NO_BOOTMEM
help
The ARM series is a line of low-power-consumption RISC chip designs licensed by ARM Ltd and targeted at embedded applications and

arch/arm/mm/init.c +0 -0
index 29560d8..0ca6c00 100644
@@ -150,58 +150,6 @@ static void __init find_limits(unsigned long *min, 

unsigned long *max_low,
*max_high = bank_pfn_end(&mi->bank[mi->nr_banks - 1]);
}

-static void __init arm_bootmem_init(unsigned long start_pfn,
- unsigned long end_pfn)
-{
- struct memblock_region *reg;
- unsigned int boot_pages;
- phys_addr_t bitmap;
- pg_data_t *pgdat;
-
- /*
- * Allocate the bootmem bitmap page. This must be in a region
- * of memory which has already been mapped.
- */
- boot_pages = bootmem_bootmap_pages(end_pfn - start_pfn);
- bitmap = memblock_alloc_base(boot_pages << PAGE_SHIFT, L1_CACHE_BYTES,
- __pfn_to_phys(end_pfn));
-
- /*
- * Initialise the bootmem allocator, handing the
- * memory banks over to bootmem.
- */
- node_set_online(0);
- pgdat = NODE_DATA(0);
- init_bootmem_node(pgdat, __phys_to_pfn(bitmap), start_pfn, end_pfn);
-
- /* Free the lowmem regions from memblock into bootmem. */
- for_each_memblock(memory, reg) {
- unsigned long start = memblock_region_memory_base_pfn(reg);
- unsigned long end = memblock_region_memory_end_pfn(reg);
-
- if (end >= end_pfn)
- end = end_pfn;
- if (start >= end)
- break;
-
- free_bootmem(__pfn_to_phys(start), (end - start) << PAGE_SHIFT);
- }
-
- /* Reserve the lowmem memblock reserved regions in bootmem. */
- for_each_memblock(reserved, reg) {
- unsigned long start = memblock_region_reserved_base_pfn(reg);
- unsigned long end = memblock_region_reserved_end_pfn(reg);
-
- if (end >= end_pfn)
- end = end_pfn;
- if (start >= end)
- break;
-
- reserve_bootmem(__pfn_to_phys(start),
- (end - start) << PAGE_SHIFT, BOOTMEM_DEFAULT);
- }
-}
-
#ifdef CONFIG_ZONE_DMA

unsigned long arm_dma_zone_size __read_mostly;
@@ -393,8 +341,6 @@ void __init bootmem_init(void)

  	find_limits(&min, &max_low, &max_high);

- arm_bootmem_init(min, max_low);
-
/*
* Sparsemem tries to allocate bootmem in memory_present(),
* so must be done after the fixed reservations
@@ -443,6 +389,54 @@ static inline int free_area(unsigned long pfn, unsigned long end, char *s)
return pages;
}

+static unsigned long free_all_lowmem(void)
+{
+ unsigned long max_low = max_low_pfn + PHYS_PFN_OFFSET;
+ struct memblock_region *mem, *res;
+ unsigned long total_pages = 0;
+
+ /* set highmem page free */
+ for_each_memblock(memory, mem) {
+ unsigned long start = memblock_region_memory_base_pfn(mem);
+ unsigned long end = memblock_region_memory_end_pfn(mem);
+
+ /* Ignore complete lowmem entries */
+ if (start > max_low)
+ continue;
+
+ /* Truncate partial highmem entries */
+ if (end > max_low)
+ end = max_low;
+
+ /* Find and exclude any reserved regions */
+ for_each_memblock(reserved, res) {
+ unsigned long res_start, res_end;
+
+ res_start = memblock_region_reserved_base_pfn(res);
+ res_end = memblock_region_reserved_end_pfn(res);
+
+ if (res_end < start)
+ continue;
+ if (res_start < start)
+ res_start = start;
+ if (res_start > end)
+ res_start = end;
+ if (res_end > end)
+ res_end = end;
+ if (res_start != start)
+ total_pages += free_area(start, res_start, NULL);
+ start = res_end;
+ if (start == end)
+ break;
+ }
+
+ /* And now free anything which remains */
+ if (start < end)
+ total_pages += free_area(start, end, NULL);
+ }
+ return total_pages;
+}
+
/*
* Poison init memory with an undefined instruction (ARM) or a branch
to an
* undefined instruction (Thumb).
@@ -606,7 +600,7 @@ void __init mem_init(void)
/* this will put all unused low memory onto the freelists */ free_unused_memmap(&meminfo);

- totalram_pages += free_all_bootmem();
+ totalram_pages += free_all_lowmem();

#ifdef CONFIG_SA1111
/* now that our DMA memory is actually so designated, we can free it */

Signature · 2 lines
Thanks
- Cyril
#42 lore raw

PATCH RESEND v3 8/17 ARM: LPAE: use phys_addr_t in free_memmap()

Quoted 26 lines · Cyril Chemparathy
Quoted 25 lines · Russell King - ARM Linux
Quoted 11 lines · Cyril Chemparathy
Quoted 10 lines · Russell King - ARM Linux
Quoted 4 lines · Catalin Marinas

This function also calls free_bootmem() which takes unsigned long. Are patches sent separately for this or we just ignore holes in memmap? There are other calls to free_bootmem() or reserve_bootmem(), do they just work with the high phys addresses?

Bootmem only deals with physical addresses which fit within the size of an 'unsigned long'. Unfortunately, the bootmem API is a mess of 'unsigned long' physical addresses and PFNs.

Quoted 8 lines
Quoted 3 lines

Years ago there was a patch to make it use only PFNs but other changes resulted in that patch being thrown away.

A separate patch has been posted for bootmem (see [1]).

Tejun suggested that we'd be better off moving entirely to memblock instead (see [2]).

Yes we should, but that's not as easy as typing a few words in an email. When I tried it when I integrated memblock into our boot sequence, I found that sparsemem wouldn't work without bootmem being in place.

Quoted 8 lines
Quoted 4 lines

It may be that things have now moved on, and we can just eliminate bootmem once and for all, but that's something I've not looked at for quite some time.

It appears to be not that hard actually... Or maybe I'm totally missing your point. Could it be that you last looked at this prior to the nobootmem compatibility stuff being added in?

I was just trying the same :)

Quoted 1 line

+static unsigned long free_all_lowmem(void)

I don't think that's needed. free_all_bootmem() in mm/nobootmem.c takes care of freeing lowmem but it has a different notion of max_low_pfn. So this hunk did the trick:

@@ -420,8 +366,8 @@ void __init bootmem_init(void)
* Note: max_low_pfn and max_pfn reflect the number of _pages_ in
* the system, not the maximum PFN.
*/
- max_low_pfn = max_low - PHYS_PFN_OFFSET;
- max_pfn = max_high - PHYS_PFN_OFFSET;
+ max_low_pfn = max_low;
+ max_pfn = max_high;
}

static inline int free_area(unsigned long pfn, unsigned long end, char *s)

Signature · 1 line
Catalin
#43 lore raw

PATCH RESEND v3 8/17 ARM: LPAE: use phys_addr_t in free_memmap()

Quoted 36 lines · Catalin Marinas
Quoted 26 lines · Cyril Chemparathy
Quoted 25 lines · Russell King - ARM Linux
Quoted 11 lines · Cyril Chemparathy
Quoted 10 lines · Russell King - ARM Linux
Quoted 4 lines · Catalin Marinas

This function also calls free_bootmem() which takes unsigned long. Are patches sent separately for this or we just ignore holes in memmap? There are other calls to free_bootmem() or reserve_bootmem(), do they just work with the high phys addresses?

Bootmem only deals with physical addresses which fit within the size of an 'unsigned long'. Unfortunately, the bootmem API is a mess of 'unsigned long' physical addresses and PFNs.

Quoted 8 lines
Quoted 3 lines

Years ago there was a patch to make it use only PFNs but other changes resulted in that patch being thrown away.

A separate patch has been posted for bootmem (see [1]).

Tejun suggested that we'd be better off moving entirely to memblock instead (see [2]).

Yes we should, but that's not as easy as typing a few words in an email. When I tried it when I integrated memblock into our boot sequence, I found that sparsemem wouldn't work without bootmem being in place.

Quoted 8 lines
Quoted 4 lines

It may be that things have now moved on, and we can just eliminate bootmem once and for all, but that's something I've not looked at for quite some time.

It appears to be not that hard actually... Or maybe I'm totally missing your point. Could it be that you last looked at this prior to the nobootmem compatibility stuff being added in?

Quoted 8 lines

I was just trying the same :)

Quoted 1 line

+static unsigned long free_all_lowmem(void)

I don't think that's needed. free_all_bootmem() in mm/nobootmem.c takes care of freeing lowmem but it has a different notion of max_low_pfn. So this hunk did the trick:

Indeed.

My intent behind cloning (and hacking) free_highpages(), was to unify these into a single step free-all-memory operation that operates directly on memblock.

Quoted 12 lines
(quoted diff) +2 -2
@@ -420,8 +366,8 @@ void __init bootmem_init(void)
  	 * Note: max_low_pfn and max_pfn reflect the number of _pages_ in
  	 * the system, not the maximum PFN.
  	 */
-	max_low_pfn = max_low - PHYS_PFN_OFFSET;
-	max_pfn = max_high - PHYS_PFN_OFFSET;
+	max_low_pfn = max_low;
+	max_pfn = max_high;
  }
 
  static inline int free_area(unsigned long pfn, unsigned long end, char *s)
Signature · 2 lines
Thanks
- Cyril
#44 lore raw

PATCH RESEND v3 8/17 ARM: LPAE: use phys_addr_t in free_memmap()

In reply to Catalin Marinas

Review void __init bootmem_init(void) · line 371

Quoted 13 lines · Catalin Marinas

I don't think that's needed. free_all_bootmem() in mm/nobootmem.c takes care of freeing lowmem but it has a different notion of max_low_pfn. So this hunk did the trick:

(quoted diff) +2 -2
@@ -420,8 +366,8 @@ void __init bootmem_init(void)
 	 * Note: max_low_pfn and max_pfn reflect the number of _pages_ in
 	 * the system, not the maximum PFN.
 	 */
-	max_low_pfn = max_low - PHYS_PFN_OFFSET;
-	max_pfn = max_high - PHYS_PFN_OFFSET;
+	max_low_pfn = max_low;
+	max_pfn = max_high;
 }

Did you actually look to see where that's used before you made the change. I don't think you did.

The reason we have that there is that much of the kernel assumes memory always starts at physical zero, so the max*pfn variables can be used to generate bitmasks to cover the range of system memory addresses - iow, (1 << max_low_pfn) - 1.

Eg, in the block code:

        blk_max_low_pfn = max_low_pfn - 1;
        blk_max_pfn = max_pfn - 1;
...
        unsigned long b_pfn = dma_mask >> PAGE_SHIFT;

        if (b_pfn < blk_max_low_pfn)
                dma = 1;

Having a DMA mask for a peripheral which only has 24 bits wired (so 0x00ffffff) with a system memory offset of 0xc0000000 results in apparantly _all_ system memory being DMA-able according to this test unless max_low_pfn is defined as the _number_ of bits in the RAM address.

In dma_get_required_mask():

        u32 low_totalram = ((max_pfn - 1) << PAGE_SHIFT);

                low_totalram = (1 << (fls(low_totalram) - 1));
                low_totalram += low_totalram - 1;

which results in (for a phys offset of 0xc0000000) low_totalram being 0xffffffff unconditionally no matter how much RAM you actually have.

fs/proc/kcore.c can be ignored because that's not supported on ARM.

So, for DMA masks to work correctly, max_pfn and max_low_pfn must be defined to cover the number of signiciant address bits in the RAM region, and not covering the number of significant bits of the last RAM address.

#45 lore raw

PATCH RESEND v3 8/17 ARM: LPAE: use phys_addr_t in free_memmap()

Quoted 41 lines · Russell King - ARM Linux

Review void __init bootmem_init(void) · line 371

Quoted 13 lines · Catalin Marinas

I don't think that's needed. free_all_bootmem() in mm/nobootmem.c takes care of freeing lowmem but it has a different notion of max_low_pfn. So this hunk did the trick:

(quoted diff) +2 -2
@@ -420,8 +366,8 @@ void __init bootmem_init(void)
 	 * Note: max_low_pfn and max_pfn reflect the number of _pages_ in
 	 * the system, not the maximum PFN.
 	 */
-	max_low_pfn = max_low - PHYS_PFN_OFFSET;
-	max_pfn = max_high - PHYS_PFN_OFFSET;
+	max_low_pfn = max_low;
+	max_pfn = max_high;
 }

Did you actually look to see where that's used before you made the change. I don't think you did.

The reason we have that there is that much of the kernel assumes memory always starts at physical zero, so the max*pfn variables can be used to generate bitmasks to cover the range of system memory addresses - iow, (1 << max_low_pfn) - 1.

Eg, in the block code:

        blk_max_low_pfn = max_low_pfn - 1;
        blk_max_pfn = max_pfn - 1;
...
        unsigned long b_pfn = dma_mask >> PAGE_SHIFT;

        if (b_pfn < blk_max_low_pfn)
                dma = 1;

Having a DMA mask for a peripheral which only has 24 bits wired (so 0x00ffffff) with a system memory offset of 0xc0000000 results in apparantly _all_ system memory being DMA-able according to this test unless max_low_pfn is defined as the _number_ of bits in the RAM address.

In dma_get_required_mask():

Quoted 7 lines
        u32 low_totalram = ((max_pfn - 1) << PAGE_SHIFT);

                low_totalram = (1 << (fls(low_totalram) - 1));
                low_totalram += low_totalram - 1;

which results in (for a phys offset of 0xc0000000) low_totalram being 0xffffffff unconditionally no matter how much RAM you actually have.

And for those platforms the phys and bus (dma) addresses are different. So it's not about whether the physical RAM starts at 0 but whether the device has a different view of the RAM address space. The reverse could also be problematic if phys == bus and max_low_pfn corresponds to an address that's not actually accessible for the device (though in practice I don't expect this).

There is no consistency in the kernel on whether max_low_pfn is absolute or relative (nobootmem.c considers it absolute). There is also a min_low_pfn which could be used to update the above functions or introduce a new min_dma_pfn.

Signature · 1 line
Catalin
#46 lore raw

PATCH RESEND v3 8/17 ARM: LPAE: use phys_addr_t in free_memmap()

Quoted 44 lines · Catalin Marinas
Quoted 43 lines · Russell King - ARM Linux

Review void __init bootmem_init(void) · line 371

Quoted 13 lines · Catalin Marinas

I don't think that's needed. free_all_bootmem() in mm/nobootmem.c takes care of freeing lowmem but it has a different notion of max_low_pfn. So this hunk did the trick:

(quoted diff) +2 -2
@@ -420,8 +366,8 @@ void __init bootmem_init(void)
 	 * Note: max_low_pfn and max_pfn reflect the number of _pages_ in
 	 * the system, not the maximum PFN.
 	 */
-	max_low_pfn = max_low - PHYS_PFN_OFFSET;
-	max_pfn = max_high - PHYS_PFN_OFFSET;
+	max_low_pfn = max_low;
+	max_pfn = max_high;
 }

Did you actually look to see where that's used before you made the change. I don't think you did.

The reason we have that there is that much of the kernel assumes memory always starts at physical zero, so the max*pfn variables can be used to generate bitmasks to cover the range of system memory addresses - iow, (1 << max_low_pfn) - 1.

Eg, in the block code:

        blk_max_low_pfn = max_low_pfn - 1;
        blk_max_pfn = max_pfn - 1;
...
        unsigned long b_pfn = dma_mask >> PAGE_SHIFT;

        if (b_pfn < blk_max_low_pfn)
                dma = 1;

Having a DMA mask for a peripheral which only has 24 bits wired (so 0x00ffffff) with a system memory offset of 0xc0000000 results in apparantly _all_ system memory being DMA-able according to this test unless max_low_pfn is defined as the _number_ of bits in the RAM address.

In dma_get_required_mask():

        u32 low_totalram = ((max_pfn - 1) << PAGE_SHIFT);
Quoted 7 lines
Quoted 5 lines
                low_totalram = (1 << (fls(low_totalram) - 1));
                low_totalram += low_totalram - 1;

which results in (for a phys offset of 0xc0000000) low_totalram being 0xffffffff unconditionally no matter how much RAM you actually have.

And for those platforms the phys and bus (dma) addresses are different.

That doesn't have anything to do with it actually. If you look at the above analysis, where the DMA addresses are has nothing to do with it.

Quoted 2 lines

So it's not about whether the physical RAM starts at 0 but whether the device has a different view of the RAM address space.

Yes it is. Much of the kernel assumes PFN0 equals physical address 0. That's a pretty sane assumption.

max_pfn/max_low_pfn are PFNs, so the theoretical correct value for these should be max_physical_address_of_ram >> PAGE_SHIFT. But, as I say, that breaks _anything_ that has an offset of RAM _and_ a device which can't address all RAM. The reason for that is that DMA _masks_ are _masks_ and are not the upper limit of addressible RAM.

Taking my example above, the DMA mask for a device with only 24-bits of addressing is 0x00ffffff. That may be connected to a system bridge which maps the low order bus space to system RAM, and system RAM may start at 0xc0000000, and end at 0xcfffffff.

In that situation:

	blk_max_low_pfn = max_low_pfn - 1;

	unsigned long b_pfn = dma_mask >> PAGE_SHIFT;
	if (b_pfn < blk_max_low_pfn)
		dma = 1;

_breaks_ if we define max_low_pfn to be 0xcfffffff >> PAGE_SHIFT, because now the above calculation says the whole of system memory is DMA-able.

This isn't a question of setting a different DMA mask for the device. The DMA mask for the device is correct. What's wrong is that there's a mis-interpretation between different parts of the kernel caused by the assumption that system RAM always starts at PFN0, physical address zero.

Quoted 4 lines

The reverse could
also be problematic if phys == bus and max_low_pfn corresponds to an address that's not actually accessible for the device (though in practice I don't expect this).

"In practice I don't expect this" - history has shown, for at least the last 12 years, that this isn't a problem because this has been how things have been done on ARM.

What I have shown above is that there _are_ problems, particularly in the block layer, if we _don't_ do this.

#47 lore raw

PATCH RESEND v3 8/17 ARM: LPAE: use phys_addr_t in free_memmap()

In reply to Cyril Chemparathy
Quoted 3 lines · Cyril Chemparathy

It appears to be not that hard actually... Or maybe I'm totally missing your point. Could it be that you last looked at this prior to the nobootmem compatibility stuff being added in?

Quite possible - we converted over to memblock here:

commit 2778f62056ada442414392d7ccd41188bb631619

Author: Russell King <[email protected]>
Date:   Fri Jul 9 16:27:52 2010 +0100

It looks like nobootmem.c was created here:

commit 0932587328d9bd5b500a640fbaff3290c8d4cabf

Author: Yinghai Lu <[email protected]>
Date:   Thu Feb 24 14:43:05 2011 +0100

So, when I was porting ARM to memblock, we had:

commit 08677214e318297f228237be0042aac754f48f1d

Author: Yinghai Lu <[email protected]>
Date:   Wed Feb 10 01:20:20 2010 -0800

which causes a load of bootmem functions to panic() when bootmem is disabled.

Quoted 1 line

The following patch appears to work just fine on a faked sparsemem system:

Great. Don't forget to also check flatmem too, just to be sure.

Quoted 156 lines
arch/arm/Kconfig +0 -0
index 92f598a..e94fa4c 100644
@@ -49,6 +49,7 @@ config ARM
 	select GENERIC_STRNCPY_FROM_USER
 	select GENERIC_STRNLEN_USER
 	select DCACHE_WORD_ACCESS if (CPU_V6 || CPU_V6K || CPU_V7) &&  

!CPU_BIG_ENDIAN

arch/arm/Kconfig +1 -0
+	select NO_BOOTMEM
 	help
 	  The ARM series is a line of low-power-consumption RISC chip designs
 	  licensed by ARM Ltd and targeted at embedded applications and
arch/arm/mm/init.c +0 -52
index 29560d8..0ca6c00 100644
@@ -150,58 +150,6 @@ static void __init find_limits(unsigned long *min,  
 unsigned long *max_low,
 	*max_high = bank_pfn_end(&mi->bank[mi->nr_banks - 1]);
 }
 
-static void __init arm_bootmem_init(unsigned long start_pfn,
-	unsigned long end_pfn)
-{
-	struct memblock_region *reg;
-	unsigned int boot_pages;
-	phys_addr_t bitmap;
-	pg_data_t *pgdat;
-
-	/*
-	 * Allocate the bootmem bitmap page.  This must be in a region
-	 * of memory which has already been mapped.
-	 */
-	boot_pages = bootmem_bootmap_pages(end_pfn - start_pfn);
-	bitmap = memblock_alloc_base(boot_pages << PAGE_SHIFT, L1_CACHE_BYTES,
-				__pfn_to_phys(end_pfn));
-
-	/*
-	 * Initialise the bootmem allocator, handing the
-	 * memory banks over to bootmem.
-	 */
-	node_set_online(0);
-	pgdat = NODE_DATA(0);
-	init_bootmem_node(pgdat, __phys_to_pfn(bitmap), start_pfn, end_pfn);
-
-	/* Free the lowmem regions from memblock into bootmem. */
-	for_each_memblock(memory, reg) {
-		unsigned long start = memblock_region_memory_base_pfn(reg);
-		unsigned long end = memblock_region_memory_end_pfn(reg);
-
-		if (end >= end_pfn)
-			end = end_pfn;
-		if (start >= end)
-			break;
-
-		free_bootmem(__pfn_to_phys(start), (end - start) << PAGE_SHIFT);
-	}
-
-	/* Reserve the lowmem memblock reserved regions in bootmem. */
-	for_each_memblock(reserved, reg) {
-		unsigned long start = memblock_region_reserved_base_pfn(reg);
-		unsigned long end = memblock_region_reserved_end_pfn(reg);
-
-		if (end >= end_pfn)
-			end = end_pfn;
-		if (start >= end)
-			break;
-
-		reserve_bootmem(__pfn_to_phys(start),
-			        (end - start) << PAGE_SHIFT, BOOTMEM_DEFAULT);
-	}
-}
-
 #ifdef CONFIG_ZONE_DMA
 

unsigned long arm_dma_zone_size __read_mostly;

arch/arm/mm/init.c +48 -2
@@ -393,8 +341,6 @@ void __init bootmem_init(void)
 
 	find_limits(&min, &max_low, &max_high);
 
-	arm_bootmem_init(min, max_low);
-
 	/*
 	 * Sparsemem tries to allocate bootmem in memory_present(),
 	 * so must be done after the fixed reservations
@@ -443,6 +389,54 @@ static inline int free_area(unsigned long pfn,  
 unsigned long end, char *s)
 	return pages;
 }
 
+static unsigned long free_all_lowmem(void)
+{
+	unsigned long max_low = max_low_pfn + PHYS_PFN_OFFSET;
+	struct memblock_region *mem, *res;
+	unsigned long total_pages = 0;
+
+	/* set highmem page free */
+	for_each_memblock(memory, mem) {
+		unsigned long start = memblock_region_memory_base_pfn(mem);
+		unsigned long end = memblock_region_memory_end_pfn(mem);
+
+		/* Ignore complete lowmem entries */
+		if (start > max_low)
+			continue;
+
+		/* Truncate partial highmem entries */
+		if (end > max_low)
+			end = max_low;
+
+		/* Find and exclude any reserved regions */
+		for_each_memblock(reserved, res) {
+			unsigned long res_start, res_end;
+
+			res_start = memblock_region_reserved_base_pfn(res);
+			res_end = memblock_region_reserved_end_pfn(res);
+
+			if (res_end < start)
+				continue;
+			if (res_start < start)
+				res_start = start;
+			if (res_start > end)
+				res_start = end;
+			if (res_end > end)
+				res_end = end;
+			if (res_start != start)
+				total_pages += free_area(start, res_start, NULL);
+			start = res_end;
+			if (start == end)
+				break;
+		}
+
+		/* And now free anything which remains */
+		if (start < end)
+			total_pages += free_area(start, end, NULL);
+	}
+	return total_pages;
+}
+
 /*
  * Poison init memory with an undefined instruction (ARM) or a branch  

to an
* undefined instruction (Thumb).

arch/arm/mm/init.c +1 -1
@@ -606,7 +600,7 @@ void __init mem_init(void)
 	/* this will put all unused low memory onto the freelists */
 	free_unused_memmap(&meminfo);
 
-	totalram_pages += free_all_bootmem();
+	totalram_pages += free_all_lowmem();
 
 #ifdef CONFIG_SA1111
 	/* now that our DMA memory is actually so designated, we can free it */
Signature · 3 lines
-- 
Thanks
- Cyril
#48 lore raw

PATCH RESEND v3 8/17 ARM: LPAE: use phys_addr_t in free_memmap()

Quoted 23 lines · Russell King - ARM Linux
Quoted 3 lines · Cyril Chemparathy

It appears to be not that hard actually... Or maybe I'm totally missing your point. Could it be that you last looked at this prior to the nobootmem compatibility stuff being added in?

Quite possible - we converted over to memblock here:

commit 2778f62056ada442414392d7ccd41188bb631619

Author: Russell King <[email protected]>
Date:   Fri Jul 9 16:27:52 2010 +0100

It looks like nobootmem.c was created here:

commit 0932587328d9bd5b500a640fbaff3290c8d4cabf

Author: Yinghai Lu <[email protected]>
Date:   Thu Feb 24 14:43:05 2011 +0100

So, when I was porting ARM to memblock, we had:

commit 08677214e318297f228237be0042aac754f48f1d

Author: Yinghai Lu <[email protected]>
Date:   Wed Feb 10 01:20:20 2010 -0800
Quoted 6 lines

which causes a load of bootmem functions to panic() when bootmem is disabled.

Quoted 1 line

The following patch appears to work just fine on a faked sparsemem system:

Great. Don't forget to also check flatmem too, just to be sure.

There is still a problem since nobootmem.c doesn't handle 64-bit physical addresses, so calling free_bootmem() still truncates the values. Simply replacing bootmem.c with nobootmem.c is not enough, we need to also replace bootmem calls with the corresponding memblock ones.

Signature · 1 line
Catalin
#49 lore raw

PATCH RESEND v3 9/17 ARM: LPAE: use phys_addr_t for initrd location and size

In reply to Cyril Chemparathy

From: Vitaly Andrianov <[email protected]>

This patch fixes the initrd setup code to use phys_addr_t instead of assuming 32-bit addressing. Without this we cannot boot on systems where initrd is located above the 4G physical address limit.

---
 arch/arm/mm/init.c |   13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)
arch/arm/mm/init.c +7 -6
index 19ba70b..bae9d05 100644
@@ -36,12 +36,13 @@
 
 #include "mm.h"
 
-static unsigned long phys_initrd_start __initdata = 0;
+static phys_addr_t phys_initrd_start __initdata = 0;
 static unsigned long phys_initrd_size __initdata = 0;
 
 static int __init early_initrd(char *p)
 {
-	unsigned long start, size;
+	phys_addr_t start;
+	unsigned long size;
 	char *endp;
 
 	start = memparse(p, &endp);
@@ -347,14 +348,14 @@ void __init arm_memblock_init(struct meminfo *mi, struct machine_desc *mdesc)
 #ifdef CONFIG_BLK_DEV_INITRD
 	if (phys_initrd_size &&
 	    !memblock_is_region_memory(phys_initrd_start, phys_initrd_size)) {
-		pr_err("INITRD: 0x%08lx+0x%08lx is not a memory region - disabling initrd\n",
-		       phys_initrd_start, phys_initrd_size);
+		pr_err("INITRD: 0x%08llx+0x%08lx is not a memory region - disabling initrd\n",
+		       (u64)phys_initrd_start, phys_initrd_size);
 		phys_initrd_start = phys_initrd_size = 0;
 	}
 	if (phys_initrd_size &&
 	    memblock_is_region_reserved(phys_initrd_start, phys_initrd_size)) {
-		pr_err("INITRD: 0x%08lx+0x%08lx overlaps in-use memory region - disabling initrd\n",
-		       phys_initrd_start, phys_initrd_size);
+		pr_err("INITRD: 0x%08llx+0x%08lx overlaps in-use memory region - disabling initrd\n",
+		       (u64)phys_initrd_start, phys_initrd_size);
 		phys_initrd_start = phys_initrd_size = 0;
 	}
 	if (phys_initrd_size) {
Signature · 1 line
1.7.9.5
#50 lore raw

PATCH RESEND v3 9/17 ARM: LPAE: use phys_addr_t for initrd location and size

Quoted 9 lines · Cyril Chemparathy

From: Vitaly Andrianov <[email protected]>

This patch fixes the initrd setup code to use phys_addr_t instead of assuming 32-bit addressing. Without this we cannot boot on systems where initrd is located above the 4G physical address limit.

#51 lore raw

Re: [PATCH v3 RESEND 09/17] ARM: LPAE: use phys_addr_t for initrd location and size

In reply to Cyril Chemparathy
Quoted 5 lines · Cyril Chemparathy

From: Vitaly Andrianov <[email protected]>

This patch fixes the initrd setup code to use phys_addr_t instead of assuming 32-bit addressing. Without this we cannot boot on systems where initrd is located above the 4G physical address limit.

The description needs updating. You're no longer using phys_addr_t for the initrd size.

#52 lore raw

Re: [PATCH v3 RESEND 09/17] ARM: LPAE: use phys_addr_t for initrd location and size

Quoted 10 lines · Russell King - ARM Linux
Quoted 5 lines · Cyril Chemparathy

From: Vitaly Andrianov <[email protected]>

This patch fixes the initrd setup code to use phys_addr_t instead of assuming 32-bit addressing. Without this we cannot boot on systems where initrd is located above the 4G physical address limit.

The description needs updating. You're no longer using phys_addr_t for the initrd size.

Thanks. Nico had pointed this out earlier. I've fixed for v4.

Thanks
-- Cyril.

#53 lore raw

PATCH RESEND v3 10/17 ARM: LPAE: use phys_addr_t in switch_mm()

In reply to Cyril Chemparathy

This patch modifies the switch_mm() processor functions to use phys_addr_t. On LPAE systems, we now honor the upper 32-bits of the physical address that is being passed in, and program these into TTBR as expected.

---
 arch/arm/include/asm/proc-fns.h |    4 ++--
 arch/arm/mm/proc-v7-3level.S    |   17 +++++++++++++----
 2 files changed, 15 insertions(+), 6 deletions(-)
arch/arm/include/asm/proc-fns.h +2 -2
index f3628fb..75b5f14 100644
@@ -60,7 +60,7 @@ extern struct processor {
 	/*
 	 * Set the page table
 	 */
-	void (*switch_mm)(unsigned long pgd_phys, struct mm_struct *mm);
+	void (*switch_mm)(phys_addr_t pgd_phys, struct mm_struct *mm);
 	/*
 	 * Set a possibly extended PTE.  Non-extended PTEs should
 	 * ignore 'ext'.
@@ -82,7 +82,7 @@ extern void cpu_proc_init(void);
 extern void cpu_proc_fin(void);
 extern int cpu_do_idle(void);
 extern void cpu_dcache_clean_area(void *, int);
-extern void cpu_do_switch_mm(unsigned long pgd_phys, struct mm_struct *mm);
+extern void cpu_do_switch_mm(phys_addr_t pgd_phys, struct mm_struct *mm);
 #ifdef CONFIG_ARM_LPAE
 extern void cpu_set_pte_ext(pte_t *ptep, pte_t pte);
 #else
arch/arm/mm/proc-v7-3level.S +13 -4
index 8de0f1d..c4f4251 100644
@@ -39,6 +39,14 @@
 #define TTB_FLAGS_SMP	(TTB_IRGN_WBWA|TTB_S|TTB_RGN_OC_WBWA)
 #define PMD_FLAGS_SMP	(PMD_SECT_WBWA|PMD_SECT_S)
 
+#ifndef __ARMEB__
+#  define rpgdl	r0
+#  define rpgdh	r1
+#else
+#  define rpgdl	r1
+#  define rpgdh	r0
+#endif
+
 /*
  * cpu_v7_switch_mm(pgd_phys, tsk)
  *
@@ -47,10 +55,11 @@
  */
 ENTRY(cpu_v7_switch_mm)
 #ifdef CONFIG_MMU
-	ldr	r1, [r1, #MM_CONTEXT_ID]	@ get mm->context.id
-	and	r3, r1, #0xff
-	mov	r3, r3, lsl #(48 - 32)		@ ASID
-	mcrr	p15, 0, r0, r3, c2		@ set TTB 0
+	ldr	r2, [r2, #MM_CONTEXT_ID]	@ get mm->context.id
+	and	r2, r2, #0xff
+	mov	r2, r2, lsl #(48 - 32)		@ ASID
+	orr	rpgdh, rpgdh, r2		@ upper 32-bits of pgd phys
+	mcrr	p15, 0, rpgdl, rpgdh, c2	@ set TTB 0
 	isb
 #endif
 	mov	pc, lr
Signature · 1 line
1.7.9.5
#54 lore raw

PATCH RESEND v3 10/17 ARM: LPAE: use phys_addr_t in switch_mm()

Quoted 54 lines · Cyril Chemparathy

This patch modifies the switch_mm() processor functions to use phys_addr_t. On LPAE systems, we now honor the upper 32-bits of the physical address that is being passed in, and program these into TTBR as expected.

---
 arch/arm/include/asm/proc-fns.h |    4 ++--
 arch/arm/mm/proc-v7-3level.S    |   17 +++++++++++++----
 2 files changed, 15 insertions(+), 6 deletions(-)
arch/arm/include/asm/proc-fns.h +2 -2
index f3628fb..75b5f14 100644
@@ -60,7 +60,7 @@ extern struct processor {
 	/*
 	 * Set the page table
 	 */
-	void (*switch_mm)(unsigned long pgd_phys, struct mm_struct *mm);
+	void (*switch_mm)(phys_addr_t pgd_phys, struct mm_struct *mm);
 	/*
 	 * Set a possibly extended PTE.  Non-extended PTEs should
 	 * ignore 'ext'.
@@ -82,7 +82,7 @@ extern void cpu_proc_init(void);
 extern void cpu_proc_fin(void);
 extern int cpu_do_idle(void);
 extern void cpu_dcache_clean_area(void *, int);
-extern void cpu_do_switch_mm(unsigned long pgd_phys, struct mm_struct *mm);
+extern void cpu_do_switch_mm(phys_addr_t pgd_phys, struct mm_struct *mm);
 #ifdef CONFIG_ARM_LPAE
 extern void cpu_set_pte_ext(pte_t *ptep, pte_t pte);
 #else
arch/arm/mm/proc-v7-3level.S +8 -0
index 8de0f1d..c4f4251 100644
@@ -39,6 +39,14 @@
 #define TTB_FLAGS_SMP	(TTB_IRGN_WBWA|TTB_S|TTB_RGN_OC_WBWA)
 #define PMD_FLAGS_SMP	(PMD_SECT_WBWA|PMD_SECT_S)
 
+#ifndef __ARMEB__
+#  define rpgdl	r0
+#  define rpgdh	r1
+#else
+#  define rpgdl	r1
+#  define rpgdh	r0
+#endif
+
 /*
  * cpu_v7_switch_mm(pgd_phys, tsk)
  *
@@ -47,10 +55,11 @@
  */

arch/arm/mm/proc-v7-3level.S line 61

Quoted 10 lines
arch/arm/mm/proc-v7-3level.S +4 -4
 ENTRY(cpu_v7_switch_mm)
 #ifdef CONFIG_MMU
-	ldr	r1, [r1, #MM_CONTEXT_ID]	@ get mm->context.id
-	and	r3, r1, #0xff
-	mov	r3, r3, lsl #(48 - 32)		@ ASID
-	mcrr	p15, 0, r0, r3, c2		@ set TTB 0
+	ldr	r2, [r2, #MM_CONTEXT_ID]	@ get mm->context.id
+	and	r2, r2, #0xff
+	mov	r2, r2, lsl #(48 - 32)		@ ASID
+	orr	rpgdh, rpgdh, r2		@ upper 32-bits of pgd phys

Can you do:

	orr	rpgdh, rpgdh, r2, lsl #(48 - 32)
Signature · 1 line
Catalin
#55 lore raw

PATCH RESEND v3 10/17 ARM: LPAE: use phys_addr_t in switch_mm()

Quoted 8 lines · Catalin Marinas
Quoted 7 lines · Cyril Chemparathy

This patch modifies the switch_mm() processor functions to use phys_addr_t. On LPAE systems, we now honor the upper 32-bits of the physical address that is being passed in, and program these into TTBR as expected.

---

[...]

Quoted 4 lines
Quoted 4 lines
arch/arm/mm/proc-v7-3level.S +0 -0
index 8de0f1d..c4f4251 100644

[...]

Quoted 13 lines

arch/arm/mm/proc-v7-3level.S line 61

Quoted 10 lines
  ENTRY(cpu_v7_switch_mm)
  #ifdef CONFIG_MMU
arch/arm/mm/proc-v7-3level.S +4 -4
-	ldr	r1, [r1, #MM_CONTEXT_ID]	@ get mm->context.id
-	and	r3, r1, #0xff
-	mov	r3, r3, lsl #(48 - 32)		@ ASID
-	mcrr	p15, 0, r0, r3, c2		@ set TTB 0
+	ldr	r2, [r2, #MM_CONTEXT_ID]	@ get mm->context.id
+	and	r2, r2, #0xff
+	mov	r2, r2, lsl #(48 - 32)		@ ASID
+	orr	rpgdh, rpgdh, r2		@ upper 32-bits of pgd phys

Can you do:

Quoted 2 lines
	orr	rpgdh, rpgdh, r2, lsl #(48 - 32)

Sure. Thanks.

-- Cyril.

#56 lore raw

PATCH RESEND v3 11/17 ARM: LPAE: use 64-bit accessors for TTBR registers

In reply to Cyril Chemparathy

This patch adds TTBR accessor macros, and modifies cpu_get_pgd() and the LPAE version of cpu_set_reserved_ttbr0() to use these instead.

In the process, we also fix these functions to correctly handle cases where the physical address lies beyond the 4G limit of 32-bit addressing.

---
 arch/arm/include/asm/proc-fns.h |   24 +++++++++++++++++++-----
 arch/arm/mm/context.c           |    9 ++-------
 2 files changed, 21 insertions(+), 12 deletions(-)
arch/arm/include/asm/proc-fns.h +19 -5
index 75b5f14..2d270b8 100644
@@ -116,13 +116,27 @@ extern void cpu_resume(void);
 #define cpu_switch_mm(pgd,mm) cpu_do_switch_mm(virt_to_phys(pgd),mm)
 
 #ifdef CONFIG_ARM_LPAE
+
+#define cpu_get_ttbr(nr)					\
+	({							\
+		u64 ttbr;					\
+		__asm__("mrrc	p15, " #nr ", %Q0, %R0, c2"	\
+			: "=r" (ttbr)				\
+			: : );					\
+		ttbr;						\
+	})
+
+#define cpu_set_ttbr(nr, val)					\
+	do {							\
+		u64 ttbr = val;					\
+		__asm__("mcrr	p15, " #nr ", %Q0, %R0, c2"	\
+			: : "r" (ttbr)				\
+			: "cc");				\
+	} while (0)
+
 #define cpu_get_pgd()	\
 	({						\
-		unsigned long pg, pg2;			\
-		__asm__("mrrc	p15, 0, %0, %1, c2"	\
-			: "=r" (pg), "=r" (pg2)		\
-			:				\
-			: "cc");			\
+		u64 pg = cpu_get_ttbr(0);		\
 		pg &= ~(PTRS_PER_PGD*sizeof(pgd_t)-1);	\
 		(pgd_t *)phys_to_virt(pg);		\
 	})
arch/arm/mm/context.c +2 -7
index 4e07eec..f03437e 100644
@@ -16,6 +16,7 @@
 #include <asm/mmu_context.h>
 #include <asm/thread_notify.h>
 #include <asm/tlbflush.h>
+#include <asm/proc-fns.h>
 
 static DEFINE_RAW_SPINLOCK(cpu_asid_lock);
 unsigned int cpu_last_asid = ASID_FIRST_VERSION;
@@ -23,17 +24,11 @@ unsigned int cpu_last_asid = ASID_FIRST_VERSION;
 #ifdef CONFIG_ARM_LPAE
 void cpu_set_reserved_ttbr0(void)
 {
-	unsigned long ttbl = __pa(swapper_pg_dir);
-	unsigned long ttbh = 0;
-
 	/*
 	 * Set TTBR0 to swapper_pg_dir which contains only global entries. The
 	 * ASID is set to 0.
 	 */
-	asm volatile(
-	"	mcrr	p15, 0, %0, %1, c2		@ set TTBR0\n"
-	:
-	: "r" (ttbl), "r" (ttbh));
+	cpu_set_ttbr(0, __pa(swapper_pg_dir));
 	isb();
 }
 #else
Signature · 1 line
1.7.9.5
#57 lore raw

PATCH RESEND v3 11/17 ARM: LPAE: use 64-bit accessors for TTBR registers

Quoted 19 lines · Cyril Chemparathy

This patch adds TTBR accessor macros, and modifies cpu_get_pgd() and the LPAE version of cpu_set_reserved_ttbr0() to use these instead.

In the process, we also fix these functions to correctly handle cases where the physical address lies beyond the 4G limit of 32-bit addressing.

---
 arch/arm/include/asm/proc-fns.h |   24 +++++++++++++++++++-----
 arch/arm/mm/context.c           |    9 ++-------
 2 files changed, 21 insertions(+), 12 deletions(-)
arch/arm/include/asm/proc-fns.h +0 -0
index 75b5f14..2d270b8 100644

arch/arm/include/asm/proc-fns.h extern void cpu_resume(void); · line 125

Quoted 10 lines
arch/arm/include/asm/proc-fns.h +7 -0
@@ -116,13 +116,27 @@ extern void cpu_resume(void);
 #define cpu_switch_mm(pgd,mm) cpu_do_switch_mm(virt_to_phys(pgd),mm)
 
 #ifdef CONFIG_ARM_LPAE
+
+#define cpu_get_ttbr(nr)					\
+	({							\
+		u64 ttbr;					\
+		__asm__("mrrc	p15, " #nr ", %Q0, %R0, c2"	\
+			: "=r" (ttbr)				\
+			: : );					\

Minor nit, we don't need the extra : : (it makes the macro shorter by one line).

arch/arm/include/asm/proc-fns.h extern void cpu_resume(void); · line 134

Quoted 9 lines
arch/arm/include/asm/proc-fns.h +9 -0
+		ttbr;						\
+	})
+
+#define cpu_set_ttbr(nr, val)					\
+	do {							\
+		u64 ttbr = val;					\
+		__asm__("mcrr	p15, " #nr ", %Q0, %R0, c2"	\
+			: : "r" (ttbr)				\
+			: "cc");				\

Do we need the "cc" here? I know it was there before but the instruction does not affect the condition flags.

It looks fine otherwise.

#58 lore raw

PATCH RESEND v3 12/17 ARM: LPAE: define ARCH_LOW_ADDRESS_LIMIT for bootmem

In reply to Cyril Chemparathy

This patch adds an architecture defined override for ARCH_LOW_ADDRESS_LIMIT. On PAE systems, the absence of this override causes bootmem to incorrectly limit itself to 32-bit addressable physical memory.

---
 arch/arm/include/asm/memory.h |    2 ++
 1 file changed, 2 insertions(+)
arch/arm/include/asm/memory.h +2 -0
index f3e8f88..19221c2 100644
@@ -315,6 +315,8 @@ static inline __deprecated void *bus_to_virt(unsigned long x)
 #define arch_is_coherent()		0
 #endif
 
+#define ARCH_LOW_ADDRESS_LIMIT		PHYS_MASK
+
 #endif
 
 #include <asm-generic/memory_model.h>
Signature · 1 line
1.7.9.5
#59 lore raw

PATCH RESEND v3 12/17 ARM: LPAE: define ARCH_LOW_ADDRESS_LIMIT for bootmem

Quoted 15 lines · Cyril Chemparathy

This patch adds an architecture defined override for ARCH_LOW_ADDRESS_LIMIT. On PAE systems, the absence of this override causes bootmem to incorrectly limit itself to 32-bit addressable physical memory.

---
 arch/arm/include/asm/memory.h |    2 ++
 1 file changed, 2 insertions(+)
arch/arm/include/asm/memory.h +0 -0
index f3e8f88..19221c2 100644

arch/arm/include/asm/memory.h static inline __deprecated void *bus_to_virt(unsigned long x) · line 318

Quoted 4 lines
arch/arm/include/asm/memory.h +1 -0
@@ -315,6 +315,8 @@ static inline __deprecated void *bus_to_virt(unsigned long x)
 #define arch_is_coherent()		0
 #endif
 
+#define ARCH_LOW_ADDRESS_LIMIT		PHYS_MASK

It looks like mm/bootmem.c casts this to unsigned long anyway, so the patch doesn't have any effect on its own.

Signature · 1 line
Catalin
#60 lore raw

PATCH RESEND v3 13/17 ARM: LPAE: factor out T1SZ and TTBR1 computations

In reply to Cyril Chemparathy

This patch moves the TTBR1 offset calculation and the T1SZ calculation out of the TTB setup assembly code. This should not affect functionality in any way, but improves code readability as well as readability of subsequent patches in this series.

---
 arch/arm/include/asm/pgtable-3level-hwdef.h |   10 ++++++++++
 arch/arm/mm/proc-v7-3level.S                |   16 ++++------------
 2 files changed, 14 insertions(+), 12 deletions(-)
arch/arm/include/asm/pgtable-3level-hwdef.h +10 -0
index d795282..b501650 100644
@@ -74,4 +74,14 @@
 #define PHYS_MASK_SHIFT		(40)
 #define PHYS_MASK		((1ULL << PHYS_MASK_SHIFT) - 1)
 
+#if defined CONFIG_VMSPLIT_2G
+#define TTBR1_OFFSET	(1 << 4)		/* skip two L1 entries */
+#elif defined CONFIG_VMSPLIT_3G
+#define TTBR1_OFFSET	(4096 * (1 + 3))	/* only L2, skip pgd + 3*pmd */
+#else
+#define TTBR1_OFFSET	0
+#endif
+
+#define TTBR1_SIZE	(((PAGE_OFFSET >> 30) - 1) << 16)
+
 #endif
arch/arm/mm/proc-v7-3level.S +4 -12
index c4f4251..5d93f00 100644
@@ -128,18 +128,10 @@ ENDPROC(cpu_v7_set_pte_ext)
 	 * booting secondary CPUs would end up using TTBR1 for the identity
 	 * mapping set up in TTBR0.
 	 */
-	bhi	9001f				@ PHYS_OFFSET > PAGE_OFFSET?
-	orr	\tmp, \tmp, #(((PAGE_OFFSET >> 30) - 1) << 16) @ TTBCR.T1SZ
-#if defined CONFIG_VMSPLIT_2G
-	/* PAGE_OFFSET == 0x80000000, T1SZ == 1 */
-	add	\ttbr1, \ttbr1, #1 << 4		@ skip two L1 entries
-#elif defined CONFIG_VMSPLIT_3G
-	/* PAGE_OFFSET == 0xc0000000, T1SZ == 2 */
-	add	\ttbr1, \ttbr1, #4096 * (1 + 3)	@ only L2 used, skip pgd+3*pmd
-#endif
-	/* CONFIG_VMSPLIT_1G does not need TTBR1 adjustment */
-9001:	mcr	p15, 0, \tmp, c2, c0, 2		@ TTB control register
-	mcrr	p15, 1, \ttbr1, \zero, c2	@ load TTBR1
+	orrls	\tmp, \tmp, #TTBR1_SIZE				@ TTBCR.T1SZ
+	mcr	p15, 0, \tmp, c2, c0, 2				@ TTBCR
+	addls	\ttbr1, \ttbr1, #TTBR1_OFFSET
+	mcrr	p15, 1, \ttbr1, \zero, c2			@ load TTBR1
 	.endm
 
 	__CPUINIT
Signature · 1 line
1.7.9.5
#61 lore raw

PATCH RESEND v3 13/17 ARM: LPAE: factor out T1SZ and TTBR1 computations

Quoted 18 lines · Cyril Chemparathy

This patch moves the TTBR1 offset calculation and the T1SZ calculation out of the TTB setup assembly code. This should not affect functionality in any way, but improves code readability as well as readability of subsequent patches in this series.

---
 arch/arm/include/asm/pgtable-3level-hwdef.h |   10 ++++++++++
 arch/arm/mm/proc-v7-3level.S                |   16 ++++------------
 2 files changed, 14 insertions(+), 12 deletions(-)
arch/arm/include/asm/pgtable-3level-hwdef.h +0 -0
index d795282..b501650 100644

arch/arm/include/asm/pgtable-3level-hwdef.h line 78

Quoted 5 lines
arch/arm/include/asm/pgtable-3level-hwdef.h +2 -0
@@ -74,4 +74,14 @@
 #define PHYS_MASK_SHIFT		(40)
 #define PHYS_MASK		((1ULL << PHYS_MASK_SHIFT) - 1)
 
+#if defined CONFIG_VMSPLIT_2G
+#define TTBR1_OFFSET	(1 << 4)		/* skip two L1 entries */

I know that was my code but I'm wondering why the (1<<4) rather than just a plain 16.

arch/arm/include/asm/pgtable-3level-hwdef.h line 85

Quoted 7 lines
arch/arm/include/asm/pgtable-3level-hwdef.h +7 -0
+#elif defined CONFIG_VMSPLIT_3G
+#define TTBR1_OFFSET	(4096 * (1 + 3))	/* only L2, skip pgd + 3*pmd */
+#else
+#define TTBR1_OFFSET	0
+#endif
+
+#define TTBR1_SIZE	(((PAGE_OFFSET >> 30) - 1) << 16)

You could also move the comment about TxSZ in proc-v7-3level.S, it makes it easier to figure out why TTBR1_SIZE is defined this way.

arch/arm/mm/proc-v7-3level.S ENDPROC(cpu_v7_set_pte_ext) · line 130

Quoted 6 lines
arch/arm/mm/proc-v7-3level.S +0 -0
@@ -128,18 +128,10 @@ ENDPROC(cpu_v7_set_pte_ext)
 	 * booting secondary CPUs would end up using TTBR1 for the identity
 	 * mapping set up in TTBR0.
 	 */

Higher up in this file there is a cmp with a "branch below" comment. You should remove that as well since the 9001 branch was removed.

Signature · 1 line
Catalin
#62 lore raw

PATCH RESEND v3 13/17 ARM: LPAE: factor out T1SZ and TTBR1 computations

Quoted 22 lines · Catalin Marinas
Quoted 21 lines · Cyril Chemparathy

This patch moves the TTBR1 offset calculation and the T1SZ calculation out of the TTB setup assembly code. This should not affect functionality in any way, but improves code readability as well as readability of subsequent patches in this series.

---
  arch/arm/include/asm/pgtable-3level-hwdef.h |   10 ++++++++++
  arch/arm/mm/proc-v7-3level.S                |   16 ++++------------
  2 files changed, 14 insertions(+), 12 deletions(-)
arch/arm/include/asm/pgtable-3level-hwdef.h +0 -0
index d795282..b501650 100644
@@ -74,4 +74,14 @@
  #define PHYS_MASK_SHIFT		(40)
  #define PHYS_MASK		((1ULL << PHYS_MASK_SHIFT) - 1)
 
Quoted 6 lines

arch/arm/include/asm/pgtable-3level-hwdef.h line 78

Quoted 2 lines
arch/arm/include/asm/pgtable-3level-hwdef.h +2 -0
+#if defined CONFIG_VMSPLIT_2G
+#define TTBR1_OFFSET	(1 << 4)		/* skip two L1 entries */

I know that was my code but I'm wondering why the (1<<4) rather than just a plain 16.

... to test my admittedly limited mental arithmetic skills, I'm guessing? :-)

Will modify.

Quoted 11 lines

arch/arm/include/asm/pgtable-3level-hwdef.h line 85

Quoted 7 lines
arch/arm/include/asm/pgtable-3level-hwdef.h +7 -0
+#elif defined CONFIG_VMSPLIT_3G
+#define TTBR1_OFFSET	(4096 * (1 + 3))	/* only L2, skip pgd + 3*pmd */
+#else
+#define TTBR1_OFFSET	0
+#endif
+
+#define TTBR1_SIZE	(((PAGE_OFFSET >> 30) - 1) << 16)

You could also move the comment about TxSZ in proc-v7-3level.S, it makes it easier to figure out why TTBR1_SIZE is defined this way.

Sure. Fixed for v4 of this series.

Quoted 10 lines

arch/arm/mm/proc-v7-3level.S ENDPROC(cpu_v7_set_pte_ext) · line 130

Quoted 6 lines
arch/arm/mm/proc-v7-3level.S +0 -0
@@ -128,18 +128,10 @@ ENDPROC(cpu_v7_set_pte_ext)
  	 * booting secondary CPUs would end up using TTBR1 for the identity
  	 * mapping set up in TTBR0.
  	 */

Higher up in this file there is a cmp with a "branch below" comment. You should remove that as well since the 9001 branch was removed.

Fixed for v4 of this series.

Signature · 2 lines
Thanks
- Cyril
#63 lore raw

PATCH RESEND v3 14/17 ARM: LPAE: accomodate >32-bit addresses for page table base

In reply to Cyril Chemparathy

This patch redefines the early boot time use of the R4 register to steal a few low order bits (ARCH_PGD_SHIFT bits) on LPAE systems. This allows for up to 38-bit physical addresses.

---
 arch/arm/include/asm/memory.h |   15 +++++++++++++++
 arch/arm/kernel/head.S        |   10 ++++------
 arch/arm/kernel/smp.c         |   11 +++++++++--
 arch/arm/mm/proc-v7-3level.S  |    8 ++++++++
 4 files changed, 36 insertions(+), 8 deletions(-)
arch/arm/include/asm/memory.h +15 -0
index 19221c2..8255b8f 100644
@@ -18,6 +18,7 @@
 #include <linux/types.h>
 #include <linux/sizes.h>
 
+#include <asm/cache.h>
 #include <asm/runtime-patch.h>
 
 #ifdef CONFIG_NEED_MACH_MEMORY_H
@@ -143,6 +144,20 @@
 #define page_to_phys(page)	(__pfn_to_phys(page_to_pfn(page)))
 #define phys_to_page(phys)	(pfn_to_page(__phys_to_pfn(phys)))
 
+/*
+ * Minimum guaranted alignment in pgd_alloc().  The page table pointers passed
+ * around in head.S and proc-*.S are shifted by this amount, in order to
+ * leave spare high bits for systems with physical address extension.  This
+ * does not fully accomodate the 40-bit addressing capability of ARM LPAE, but
+ * gives us about 38-bits or so.
+ */
+#ifdef CONFIG_ARM_LPAE
+#define ARCH_PGD_SHIFT		L1_CACHE_SHIFT
+#else
+#define ARCH_PGD_SHIFT		0
+#endif
+#define ARCH_PGD_MASK		((1 << ARCH_PGD_SHIFT) - 1)
+
 #ifndef __ASSEMBLY__
 
 /*
arch/arm/kernel/head.S +4 -6
index 61fb8df..9664db0 100644
@@ -152,7 +152,7 @@ ENDPROC(stext)
  *
  * Returns:
  *  r0, r3, r5-r7 corrupted
- *  r4 = physical page table address
+ *  r4 = page table (see ARCH_PGD_SHIFT in asm/memory.h)
  */
 __create_page_tables:
 	pgtbl	r4, r8				@ page table address
@@ -306,6 +306,7 @@ __create_page_tables:
 #endif
 #ifdef CONFIG_ARM_LPAE
 	sub	r4, r4, #0x1000		@ point to the PGD table
+	mov	r4, r4, lsr #ARCH_PGD_SHIFT
 #endif
 	mov	pc, lr
 ENDPROC(__create_page_tables)
@@ -379,7 +380,7 @@ __secondary_data:
  *  r0  = cp#15 control register
  *  r1  = machine ID
  *  r2  = atags or dtb pointer
- *  r4  = page table pointer
+ *  r4  = page table (see ARCH_PGD_SHIFT in asm/memory.h)
  *  r9  = processor ID
  *  r13 = *virtual* address to jump to upon completion
  */
@@ -398,10 +399,7 @@ __enable_mmu:
 #ifdef CONFIG_CPU_ICACHE_DISABLE
 	bic	r0, r0, #CR_I
 #endif
-#ifdef CONFIG_ARM_LPAE
-	mov	r5, #0
-	mcrr	p15, 0, r4, r5, c2		@ load TTBR0
-#else
+#ifndef CONFIG_ARM_LPAE
 	mov	r5, #(domain_val(DOMAIN_USER, DOMAIN_MANAGER) | \
 		      domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \
 		      domain_val(DOMAIN_TABLE, DOMAIN_MANAGER) | \
arch/arm/kernel/smp.c +9 -2
index ebd8ad2..9831716 100644
@@ -60,6 +60,13 @@ enum ipi_msg_type {
 
 static DECLARE_COMPLETION(cpu_running);
 
+static unsigned long get_arch_pgd(pgd_t *pgd)
+{
+	phys_addr_t pgdir = virt_to_phys(pgd);
+	BUG_ON(pgdir & ARCH_PGD_MASK);
+	return pgdir >> ARCH_PGD_SHIFT;
+}
+
 int __cpuinit __cpu_up(unsigned int cpu, struct task_struct *idle)
 {
 	int ret;
@@ -69,8 +76,8 @@ int __cpuinit __cpu_up(unsigned int cpu, struct task_struct *idle)
 	 * its stack and the page tables.
 	 */
 	secondary_data.stack = task_stack_page(idle) + THREAD_START_SP;
-	secondary_data.pgdir = virt_to_phys(idmap_pgd);
-	secondary_data.swapper_pg_dir = virt_to_phys(swapper_pg_dir);
+	secondary_data.pgdir = get_arch_pgd(idmap_pgd);
+	secondary_data.swapper_pg_dir = get_arch_pgd(swapper_pg_dir);
 	__cpuc_flush_dcache_area(&secondary_data, sizeof(secondary_data));
 	outer_clean_range(__pa(&secondary_data), __pa(&secondary_data + 1));
 
arch/arm/mm/proc-v7-3level.S +8 -0
index 5d93f00..e920763 100644
@@ -111,6 +111,7 @@ ENDPROC(cpu_v7_set_pte_ext)
 	 */
 	.macro	v7_ttb_setup, zero, ttbr0, ttbr1, tmp
 	ldr	\tmp, =swapper_pg_dir		@ swapper_pg_dir virtual address
+	mov	\tmp, \tmp, lsr #ARCH_PGD_SHIFT
 	cmp	\ttbr1, \tmp			@ PHYS_OFFSET > PAGE_OFFSET? (branch below)
 	mrc	p15, 0, \tmp, c2, c0, 2		@ TTB control register
 	orr	\tmp, \tmp, #TTB_EAE
@@ -130,8 +131,15 @@ ENDPROC(cpu_v7_set_pte_ext)
 	 */
 	orrls	\tmp, \tmp, #TTBR1_SIZE				@ TTBCR.T1SZ
 	mcr	p15, 0, \tmp, c2, c0, 2				@ TTBCR
+	mov	\tmp, \ttbr1, lsr #(32 - ARCH_PGD_SHIFT)	@ upper bits
+	mov	\ttbr1, \ttbr1, lsl #ARCH_PGD_SHIFT		@ lower bits
 	addls	\ttbr1, \ttbr1, #TTBR1_OFFSET
 	mcrr	p15, 1, \ttbr1, \zero, c2			@ load TTBR1
+	mov	\tmp, \ttbr0, lsr #(32 - ARCH_PGD_SHIFT)	@ upper bits
+	mov	\ttbr0, \ttbr0, lsl #ARCH_PGD_SHIFT		@ lower bits
+	mcrr	p15, 0, \ttbr0, \zero, c2			@ load TTBR0
+	mcrr	p15, 1, \ttbr1, \zero, c2			@ load TTBR1
+	mcrr	p15, 0, \ttbr0, \zero, c2			@ load TTBR0
 	.endm
 
 	__CPUINIT
Signature · 1 line
1.7.9.5
#64 lore raw

PATCH RESEND v3 14/17 ARM: LPAE: accomodate >32-bit addresses for page table base

Quoted 3 lines · Cyril Chemparathy

This patch redefines the early boot time use of the R4 register to steal a few low order bits (ARCH_PGD_SHIFT bits) on LPAE systems. This allows for up to 38-bit physical addresses.

That's fine. We figured out that on the vexpress with A15 and sparsemem enabled could only use 36-bit because of section number stored in the top bits of page->flags.

An alternative to this patch could be to always patch in the upper 32-bit of phys_offset like you do in virt_to_phys(). We know that pgd is always lowmem.

Signature · 1 line
Catalin
#65 lore raw

PATCH RESEND v3 15/17 ARM: mm: use physical addresses in highmem sanity checks

In reply to Cyril Chemparathy

This patch modifies the highmem sanity checking code to use physical addresses instead. This change eliminates the wrap-around problems associated with the original virtual address based checks, and this simplifies the code a bit.

The one constraint imposed here is that low physical memory must be mapped in a monotonically increasing fashion if there are multiple banks of memory, i.e., x < y must => pa(x) < pa(y).

---
 arch/arm/mm/mmu.c |   22 ++++++++++------------
 1 file changed, 10 insertions(+), 12 deletions(-)
arch/arm/mm/mmu.c +10 -12
index 8db7c74..fcdc185 100644
@@ -895,6 +895,7 @@ phys_addr_t arm_lowmem_limit __initdata = 0;
 void __init sanity_check_meminfo(void)
 {
 	int i, j, highmem = 0;
+	phys_addr_t vmalloc_limit = __pa(vmalloc_min - 1) + 1;
 
 	for (i = 0, j = 0; i < meminfo.nr_banks; i++) {
 		struct membank *bank = &meminfo.bank[j];
@@ -904,8 +905,7 @@ void __init sanity_check_meminfo(void)
 			highmem = 1;
 
 #ifdef CONFIG_HIGHMEM
-		if (__va(bank->start) >= vmalloc_min ||
-		    __va(bank->start) < (void *)PAGE_OFFSET)
+		if (bank->start >= vmalloc_limit)
 			highmem = 1;
 
 		bank->highmem = highmem;
@@ -914,8 +914,8 @@ void __init sanity_check_meminfo(void)
 		 * Split those memory banks which are partially overlapping
 		 * the vmalloc area greatly simplifying things later.
 		 */
-		if (!highmem && __va(bank->start) < vmalloc_min &&
-		    bank->size > vmalloc_min - __va(bank->start)) {
+		if (!highmem && bank->start < vmalloc_limit &&
+		    bank->size > vmalloc_limit - bank->start) {
 			if (meminfo.nr_banks >= NR_BANKS) {
 				printk(KERN_CRIT "NR_BANKS too low, "
 						 "ignoring high memory\n");
@@ -924,12 +924,12 @@ void __init sanity_check_meminfo(void)
 					(meminfo.nr_banks - i) * sizeof(*bank));
 				meminfo.nr_banks++;
 				i++;
-				bank[1].size -= vmalloc_min - __va(bank->start);
-				bank[1].start = __pa(vmalloc_min - 1) + 1;
+				bank[1].size -= vmalloc_limit - bank->start;
+				bank[1].start = vmalloc_limit;
 				bank[1].highmem = highmem = 1;
 				j++;
 			}
-			bank->size = vmalloc_min - __va(bank->start);
+			bank->size = vmalloc_limit - bank->start;
 		}
 #else
 		bank->highmem = highmem;
@@ -949,8 +949,7 @@ void __init sanity_check_meminfo(void)
 		 * Check whether this memory bank would entirely overlap
 		 * the vmalloc area.
 		 */
-		if (__va(bank->start) >= vmalloc_min ||
-		    __va(bank->start) < (void *)PAGE_OFFSET) {
+		if (bank->start >= vmalloc_limit) {
 			printk(KERN_NOTICE "Ignoring RAM at %.8llx-%.8llx "
 			       "(vmalloc region overlap).\n",
 			       (unsigned long long)bank->start,
@@ -962,9 +961,8 @@ void __init sanity_check_meminfo(void)
 		 * Check whether this memory bank would partially overlap
 		 * the vmalloc area.
 		 */
-		if (__va(bank->start + bank->size - 1) >= vmalloc_min ||
-		    __va(bank->start + bank->size - 1) <= __va(bank->start)) {
-			unsigned long newsize = vmalloc_min - __va(bank->start);
+		if (bank->start + bank->size > vmalloc_limit)
+			unsigned long newsize = vmalloc_limit - bank->start;
 			printk(KERN_NOTICE "Truncating RAM at %.8llx-%.8llx "
 			       "to -%.8llx (vmalloc region overlap).\n",
 			       (unsigned long long)bank->start,
Signature · 1 line
1.7.9.5
#66 lore raw

PATCH RESEND v3 15/17 ARM: mm: use physical addresses in highmem sanity checks

Quoted 3 lines · Cyril Chemparathy

This patch modifies the highmem sanity checking code to use physical addresses instead. This change eliminates the wrap-around problems associated with the original virtual address based checks, and this simplifies the code a bit.

We need some check here that the upper 32-bit of lowmem addresses are constant.

Signature · 1 line
Catalin
#67 lore raw

PATCH RESEND v3 16/17 ARM: mm: cleanup checks for membank overlap with vmalloc area

In reply to Cyril Chemparathy

On Keystone platforms, physical memory is entirely outside the 32-bit addressible range. Therefore, the (bank->start > ULONG_MAX) check below marks the entire system memory as highmem, and this causes unpleasentness all over.

This patch eliminates the extra bank start check (against ULONG_MAX) by checking bank->start against the physical address corresponding to vmalloc_min instead.

In the process, this patch also cleans up parts of the highmem sanity check code by removing what has now become a redundant check for banks that entirely overlap with the vmalloc range.

---
 arch/arm/mm/mmu.c |   19 +------------------
 1 file changed, 1 insertion(+), 18 deletions(-)
arch/arm/mm/mmu.c +1 -18
index fcdc185..e631f73 100644
@@ -901,15 +901,12 @@ void __init sanity_check_meminfo(void)
 		struct membank *bank = &meminfo.bank[j];
 		*bank = meminfo.bank[i];
 
-		if (bank->start > ULONG_MAX)
-			highmem = 1;
-
-#ifdef CONFIG_HIGHMEM
 		if (bank->start >= vmalloc_limit)
 			highmem = 1;
 
 		bank->highmem = highmem;
 
+#ifdef CONFIG_HIGHMEM
 		/*
 		 * Split those memory banks which are partially overlapping
 		 * the vmalloc area greatly simplifying things later.
@@ -932,8 +929,6 @@ void __init sanity_check_meminfo(void)
 			bank->size = vmalloc_limit - bank->start;
 		}
 #else
-		bank->highmem = highmem;
-
 		/*
 		 * Highmem banks not allowed with !CONFIG_HIGHMEM.
 		 */
@@ -946,18 +941,6 @@ void __init sanity_check_meminfo(void)
 		}
 
 		/*
-		 * Check whether this memory bank would entirely overlap
-		 * the vmalloc area.
-		 */
-		if (bank->start >= vmalloc_limit) {
-			printk(KERN_NOTICE "Ignoring RAM at %.8llx-%.8llx "
-			       "(vmalloc region overlap).\n",
-			       (unsigned long long)bank->start,
-			       (unsigned long long)bank->start + bank->size - 1);
-			continue;
-		}
-
-		/*
 		 * Check whether this memory bank would partially overlap
 		 * the vmalloc area.
 		 */
Signature · 1 line
1.7.9.5
#68 lore raw

PATCH RESEND v3 17/17 ARM: mm: clean up membank size limit checks

In reply to Cyril Chemparathy

This patch cleans up the highmem sanity check code by simplifying the range checks with a pre-calculated size_limit. This patch should otherwise have no functional impact on behavior.

This patch also removes a redundant (bank->start < vmalloc_limit) check, since this is already covered by the !highmem condition.

---
 arch/arm/mm/mmu.c |   19 +++++++++++--------
 1 file changed, 11 insertions(+), 8 deletions(-)
arch/arm/mm/mmu.c +11 -8
index e631f73..794457a 100644
@@ -899,10 +899,15 @@ void __init sanity_check_meminfo(void)
 
 	for (i = 0, j = 0; i < meminfo.nr_banks; i++) {
 		struct membank *bank = &meminfo.bank[j];
+		phys_addr_t size_limit;
+
 		*bank = meminfo.bank[i];
+		size_limit = bank->size;
 
 		if (bank->start >= vmalloc_limit)
 			highmem = 1;
+		else
+			size_limit = vmalloc_limit - bank->start;
 
 		bank->highmem = highmem;
 
@@ -911,8 +916,7 @@ void __init sanity_check_meminfo(void)
 		 * Split those memory banks which are partially overlapping
 		 * the vmalloc area greatly simplifying things later.
 		 */
-		if (!highmem && bank->start < vmalloc_limit &&
-		    bank->size > vmalloc_limit - bank->start) {
+		if (!highmem && bank->size > size_limit) {
 			if (meminfo.nr_banks >= NR_BANKS) {
 				printk(KERN_CRIT "NR_BANKS too low, "
 						 "ignoring high memory\n");
@@ -921,12 +925,12 @@ void __init sanity_check_meminfo(void)
 					(meminfo.nr_banks - i) * sizeof(*bank));
 				meminfo.nr_banks++;
 				i++;
-				bank[1].size -= vmalloc_limit - bank->start;
+				bank[1].size -= size_limit;
 				bank[1].start = vmalloc_limit;
 				bank[1].highmem = highmem = 1;
 				j++;
 			}
-			bank->size = vmalloc_limit - bank->start;
+			bank->size = size_limit;
 		}
 #else
 		/*
@@ -944,14 +948,13 @@ void __init sanity_check_meminfo(void)
 		 * Check whether this memory bank would partially overlap
 		 * the vmalloc area.
 		 */
-		if (bank->start + bank->size > vmalloc_limit)
-			unsigned long newsize = vmalloc_limit - bank->start;
+		if (bank->size > size_limit) {
 			printk(KERN_NOTICE "Truncating RAM at %.8llx-%.8llx "
 			       "to -%.8llx (vmalloc region overlap).\n",
 			       (unsigned long long)bank->start,
 			       (unsigned long long)bank->start + bank->size - 1,
-			       (unsigned long long)bank->start + newsize - 1);
-			bank->size = newsize;
+			       (unsigned long long)bank->start + size_limit - 1);
+			bank->size = size_limit;
 		}
 #endif
 		if (!bank->highmem && bank->start + bank->size > arm_lowmem_limit)
Signature · 1 line
1.7.9.5