2012-08-11 01:25:55

by Cyril Chemparathy

[permalink] [raw]
Subject: [PATCH v2 00/22] Introducing the TI Keystone platform

This series is a follow on to the series posted earlier (archived at [1]).

Patches 01/22 .. 09/22 of this series have been pretty intensively reviewed;
thanks to all who helped. We've modified per feedback, and these should be in
reasonable shape.

Patches 10/22 .. 19/22 of this series have not been very widely reviewed.
We'd very much appreciate eyeballs here.

Patches 20/22 .. 22/22 of this series are specific to the TI Keystone platform.
These are not ready to be merged in. These are being provided here for the sake
of completeness, and to better illustrate the other patches in this series.
These are dependent on the smpops patches (see [2]).

These patches are also available on the following git repository:
git://arago-project.org/git/projects/linux-keystone.git keystone-v2


[1] - http://thread.gmane.org/gmane.linux.kernel/1336081
[2] - http://permalink.gmane.org/gmane.linux.ports.arm.kernel/171540


Series changelog:

(01/22) ARM: add mechanism for late code patching
(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
(v2) added init-time tests to verify instruction encoding

(03/22) ARM: use late patch framework for phys-virt patching
(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
(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
(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()
(v2) unchanged from v1

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

(10/22) ARM: LPAE: use phys_addr_t in switch_mm()
(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
(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
(v2) unchanged from v1

(14/22) ARM: LPAE: accomodate >32-bit addresses for page table base
(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
(18/22) ARM: add virt_to_idmap for interconnect aliasing
(v2) unchanged from v1

(19/22) ARM: recreate kernel mappings in early_paging_init()
(v2) disable on !lpae at compile time


arch/arm/Kconfig | 36 ++++
arch/arm/Makefile | 1 +
arch/arm/boot/dts/keystone-sim.dts | 77 +++++++
arch/arm/configs/keystone_defconfig | 23 +++
arch/arm/include/asm/mach/arch.h | 1 +
arch/arm/include/asm/memory.h | 94 ++++++---
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 | 175 ++++++++++++++++
arch/arm/kernel/Makefile | 1 +
arch/arm/kernel/armksyms.c | 4 -
arch/arm/kernel/head.S | 107 +++-------
arch/arm/kernel/module.c | 7 +-
arch/arm/kernel/runtime-patch.c | 230 +++++++++++++++++++++
arch/arm/kernel/setup.c | 18 ++
arch/arm/kernel/smp.c | 11 +-
arch/arm/kernel/vmlinux.lds.S | 13 +-
arch/arm/mach-keystone/Makefile | 2 +
arch/arm/mach-keystone/Makefile.boot | 1 +
arch/arm/mach-keystone/include/mach/debug-macro.S | 44 ++++
arch/arm/mach-keystone/include/mach/memory.h | 47 +++++
arch/arm/mach-keystone/include/mach/timex.h | 21 ++
arch/arm/mach-keystone/include/mach/uncompress.h | 24 +++
arch/arm/mach-keystone/keystone.c | 124 +++++++++++
arch/arm/mach-keystone/keystone.h | 23 +++
arch/arm/mach-keystone/platsmp.c | 84 ++++++++
arch/arm/mm/context.c | 9 +-
arch/arm/mm/idmap.c | 4 +-
arch/arm/mm/init.c | 19 +-
arch/arm/mm/mmu.c | 114 +++++++---
arch/arm/mm/proc-v7-3level.S | 50 +++--
34 files changed, 1214 insertions(+), 203 deletions(-)
create mode 100644 arch/arm/boot/dts/keystone-sim.dts
create mode 100644 arch/arm/configs/keystone_defconfig
create mode 100644 arch/arm/include/asm/runtime-patch.h
create mode 100644 arch/arm/kernel/runtime-patch.c
create mode 100644 arch/arm/mach-keystone/Makefile
create mode 100644 arch/arm/mach-keystone/Makefile.boot
create mode 100644 arch/arm/mach-keystone/include/mach/debug-macro.S
create mode 100644 arch/arm/mach-keystone/include/mach/memory.h
create mode 100644 arch/arm/mach-keystone/include/mach/timex.h
create mode 100644 arch/arm/mach-keystone/include/mach/uncompress.h
create mode 100644 arch/arm/mach-keystone/keystone.c
create mode 100644 arch/arm/mach-keystone/keystone.h
create mode 100644 arch/arm/mach-keystone/platsmp.c

--
1.7.9.5


2012-08-11 01:25:38

by Cyril Chemparathy

[permalink] [raw]
Subject: [PATCH v2 14/22] ARM: LPAE: accomodate >32-bit addresses for page table base

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.

Signed-off-by: Cyril Chemparathy <[email protected]>
Signed-off-by: Vitaly Andrianov <[email protected]>
---
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(-)

diff --git a/arch/arm/include/asm/memory.h b/arch/arm/include/asm/memory.h
index 64db5a4..e5d0cc8 100644
--- a/arch/arm/include/asm/memory.h
+++ b/arch/arm/include/asm/memory.h
@@ -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__

/*
diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S
index 61fb8df..9664db0 100644
--- a/arch/arm/kernel/head.S
+++ b/arch/arm/kernel/head.S
@@ -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) | \
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c
index ebd8ad2..9831716 100644
--- a/arch/arm/kernel/smp.c
+++ b/arch/arm/kernel/smp.c
@@ -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));

diff --git a/arch/arm/mm/proc-v7-3level.S b/arch/arm/mm/proc-v7-3level.S
index e28383f..6fa0444 100644
--- a/arch/arm/mm/proc-v7-3level.S
+++ b/arch/arm/mm/proc-v7-3level.S
@@ -120,6 +120,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
@@ -139,8 +140,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
--
1.7.9.5

2012-08-11 01:25:42

by Cyril Chemparathy

[permalink] [raw]
Subject: [PATCH v2 17/22] ARM: mm: clean up membank size limit checks

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.

Signed-off-by: Cyril Chemparathy <[email protected]>
Signed-off-by: Vitaly Andrianov <[email protected]>
---
arch/arm/mm/mmu.c | 19 +++++++++++--------
1 file changed, 11 insertions(+), 8 deletions(-)

diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c
index 3d685c6..662684b 100644
--- a/arch/arm/mm/mmu.c
+++ b/arch/arm/mm/mmu.c
@@ -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)
--
1.7.9.5

2012-08-11 01:26:12

by Cyril Chemparathy

[permalink] [raw]
Subject: [PATCH v2 15/22] ARM: mm: use physical addresses in highmem sanity checks

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).

Signed-off-by: Cyril Chemparathy <[email protected]>
Signed-off-by: Vitaly Andrianov <[email protected]>
---
arch/arm/mm/mmu.c | 22 ++++++++++------------
1 file changed, 10 insertions(+), 12 deletions(-)

diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c
index 53eeeb8..f764c03 100644
--- a/arch/arm/mm/mmu.c
+++ b/arch/arm/mm/mmu.c
@@ -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) > vmalloc_min ||
- __va(bank->start + bank->size) < __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,
--
1.7.9.5

2012-08-11 01:25:59

by Cyril Chemparathy

[permalink] [raw]
Subject: [PATCH v2 19/22] ARM: recreate kernel mappings in early_paging_init()

This patch adds a step in the init sequence, in order to recreate the kernel
code/data page table mappings prior to full paging initialization. This is
necessary on LPAE systems that run out of a physical address space outside the
4G limit. On these systems, this implementation provides a machine descriptor
hook that allows the PHYS_OFFSET to be overridden in a machine specific
fashion.

Signed-off-by: Cyril Chemparathy <[email protected]>
Signed-off-by: Vitaly Andrianov <[email protected]>
---
arch/arm/include/asm/mach/arch.h | 1 +
arch/arm/kernel/setup.c | 3 ++
arch/arm/mm/mmu.c | 65 ++++++++++++++++++++++++++++++++++++++
3 files changed, 69 insertions(+)

diff --git a/arch/arm/include/asm/mach/arch.h b/arch/arm/include/asm/mach/arch.h
index 0b1c94b..2b9ecc5 100644
--- a/arch/arm/include/asm/mach/arch.h
+++ b/arch/arm/include/asm/mach/arch.h
@@ -37,6 +37,7 @@ struct machine_desc {
char restart_mode; /* default restart mode */
void (*fixup)(struct tag *, char **,
struct meminfo *);
+ void (*init_meminfo)(void);
void (*reserve)(void);/* reserve mem blocks */
void (*map_io)(void);/* IO mapping function */
void (*init_early)(void);
diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
index edb4f42..e37cbaf 100644
--- a/arch/arm/kernel/setup.c
+++ b/arch/arm/kernel/setup.c
@@ -79,6 +79,7 @@ static int __init fpe_setup(char *line)
__setup("fpe=", fpe_setup);
#endif

+extern void early_paging_init(struct machine_desc *, struct proc_info_list *);
extern void paging_init(struct machine_desc *desc);
extern void sanity_check_meminfo(void);
extern void reboot_setup(char *str);
@@ -978,6 +979,8 @@ void __init setup_arch(char **cmdline_p)
parse_early_param();

sort(&meminfo.bank, meminfo.nr_banks, sizeof(meminfo.bank[0]), meminfo_cmp, NULL);
+
+ early_paging_init(mdesc, lookup_processor_type(read_cpuid_id()));
sanity_check_meminfo();
arm_memblock_init(&meminfo, mdesc);

diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c
index 662684b..5d240da 100644
--- a/arch/arm/mm/mmu.c
+++ b/arch/arm/mm/mmu.c
@@ -28,6 +28,7 @@
#include <asm/highmem.h>
#include <asm/system_info.h>
#include <asm/traps.h>
+#include <asm/procinfo.h>

#include <asm/mach/arch.h>
#include <asm/mach/map.h>
@@ -1168,6 +1169,70 @@ static void __init map_lowmem(void)
}
}

+#ifdef CONFIG_ARM_LPAE
+/*
+ * early_paging_init() recreates boot time page table setup, allowing machines
+ * to switch over to a high (>4G) address space on LPAE systems
+ */
+void __init early_paging_init(struct machine_desc *mdesc,
+ struct proc_info_list *procinfo)
+{
+ pmdval_t pmdprot = procinfo->__cpu_mm_mmu_flags;
+ unsigned long map_start, map_end;
+ pgd_t *pgd0, *pgdk;
+ pud_t *pud0, *pudk;
+ pmd_t *pmd0, *pmdk;
+ phys_addr_t phys;
+ int i;
+
+ /* remap kernel code and data */
+ map_start = init_mm.start_code;
+ map_end = init_mm.brk;
+
+ /* get a handle on things... */
+ pgd0 = pgd_offset_k(0);
+ pud0 = pud_offset(pgd0, 0);
+ pmd0 = pmd_offset(pud0, 0);
+
+ pgdk = pgd_offset_k(map_start);
+ pudk = pud_offset(pgdk, map_start);
+ pmdk = pmd_offset(pudk, map_start);
+
+ phys = PHYS_OFFSET;
+
+ if (mdesc->init_meminfo)
+ mdesc->init_meminfo();
+
+ /* remap level 1 table */
+ for (i = 0; i < PTRS_PER_PGD; i++) {
+ *pud0++ = __pud(__pa(pmd0) | PMD_TYPE_TABLE | L_PGD_SWAPPER);
+ pmd0 += PTRS_PER_PMD;
+ }
+
+ /* remap pmds for kernel mapping */
+ phys = __pa(map_start) & PMD_MASK;
+ do {
+ *pmdk++ = __pmd(phys | pmdprot);
+ phys += PMD_SIZE;
+ } while (phys < map_end);
+
+ flush_cache_all();
+ cpu_set_ttbr(0, __pa(pgd0));
+ cpu_set_ttbr(1, __pa(pgd0) + TTBR1_OFFSET);
+ local_flush_tlb_all();
+}
+
+#else
+
+void __init early_paging_init(struct machine_desc *mdesc,
+ struct proc_info_list *procinfo)
+{
+ if (mdesc->init_meminfo)
+ mdesc->init_meminfo();
+}
+
+#endif
+
/*
* paging_init() sets up the page tables, initialises the zone memory
* maps, and sets up the zero page, bad page and bad page tables.
--
1.7.9.5

2012-08-11 01:26:16

by Cyril Chemparathy

[permalink] [raw]
Subject: [PATCH v2 10/22] ARM: LPAE: use phys_addr_t in switch_mm()

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.

Signed-off-by: Cyril Chemparathy <[email protected]>
Signed-off-by: Vitaly Andrianov <[email protected]>
---
arch/arm/include/asm/proc-fns.h | 4 ++--
arch/arm/mm/proc-v7-3level.S | 26 ++++++++++++++++++++++----
2 files changed, 24 insertions(+), 6 deletions(-)

diff --git a/arch/arm/include/asm/proc-fns.h b/arch/arm/include/asm/proc-fns.h
index f3628fb..75b5f14 100644
--- a/arch/arm/include/asm/proc-fns.h
+++ b/arch/arm/include/asm/proc-fns.h
@@ -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
diff --git a/arch/arm/mm/proc-v7-3level.S b/arch/arm/mm/proc-v7-3level.S
index 8de0f1d..78bd88c 100644
--- a/arch/arm/mm/proc-v7-3level.S
+++ b/arch/arm/mm/proc-v7-3level.S
@@ -39,6 +39,22 @@
#define TTB_FLAGS_SMP (TTB_IRGN_WBWA|TTB_S|TTB_RGN_OC_WBWA)
#define PMD_FLAGS_SMP (PMD_SECT_WBWA|PMD_SECT_S)

+#define rzero r3
+#ifndef CONFIG_ARM_LPAE
+# define rpgdl r0
+# define rpgdh rzero
+# define rmm r1
+#else
+# define rmm r2
+#ifndef __ARMEB__
+# define rpgdl r0
+# define rpgdh r1
+#else
+# define rpgdl r1
+# define rpgdh r0
+#endif
+#endif
+
/*
* cpu_v7_switch_mm(pgd_phys, tsk)
*
@@ -47,10 +63,12 @@
*/
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
+ mov rzero, #0
+ ldr rmm, [rmm, #MM_CONTEXT_ID] @ get mm->context.id
+ and rmm, rmm, #0xff
+ mov rmm, rmm, lsl #(48 - 32) @ ASID
+ orr rpgdh, rpgdh, rmm @ upper 32-bits of pgd phys
+ mcrr p15, 0, rpgdl, rpgdh, c2 @ set TTB 0
isb
#endif
mov pc, lr
--
1.7.9.5

2012-08-11 01:26:20

by Cyril Chemparathy

[permalink] [raw]
Subject: [RFC v2 20/22] ARM: keystone: introducing TI Keystone platform

Texas Instruments Keystone family of multicore devices now includes an
upcoming slew of Cortex A15 based devices. This patch adds basic definitions
for a new Keystone sub-architecture in ARM.

Subsequent patches in this series will extend support to include SMP and take
advantage of the large physical memory addressing capabilities via LPAE.

Signed-off-by: Vitaly Andrianov <[email protected]>
Signed-off-by: Cyril Chemparathy <[email protected]>
Reviewed-by: Arnd Bergmann <[email protected]>
---
arch/arm/Kconfig | 18 +++++
arch/arm/Makefile | 1 +
arch/arm/boot/dts/keystone-sim.dts | 77 +++++++++++++++++++
arch/arm/configs/keystone_defconfig | 20 +++++
arch/arm/mach-keystone/Makefile | 1 +
arch/arm/mach-keystone/Makefile.boot | 1 +
arch/arm/mach-keystone/include/mach/debug-macro.S | 44 +++++++++++
arch/arm/mach-keystone/include/mach/memory.h | 22 ++++++
arch/arm/mach-keystone/include/mach/timex.h | 21 ++++++
arch/arm/mach-keystone/include/mach/uncompress.h | 24 ++++++
arch/arm/mach-keystone/keystone.c | 82 +++++++++++++++++++++
11 files changed, 311 insertions(+)
create mode 100644 arch/arm/boot/dts/keystone-sim.dts
create mode 100644 arch/arm/configs/keystone_defconfig
create mode 100644 arch/arm/mach-keystone/Makefile
create mode 100644 arch/arm/mach-keystone/Makefile.boot
create mode 100644 arch/arm/mach-keystone/include/mach/debug-macro.S
create mode 100644 arch/arm/mach-keystone/include/mach/memory.h
create mode 100644 arch/arm/mach-keystone/include/mach/timex.h
create mode 100644 arch/arm/mach-keystone/include/mach/uncompress.h
create mode 100644 arch/arm/mach-keystone/keystone.c

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 9ac86ea..f1b8aa0 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -402,6 +402,24 @@ config ARCH_HIGHBANK
help
Support for the Calxeda Highbank SoC based boards.

+config ARCH_KEYSTONE
+ bool "Texas Instruments Keystone Devices"
+ select ARCH_WANT_OPTIONAL_GPIOLIB
+ select ARM_GIC
+ select MULTI_IRQ_HANDLER
+ select CLKDEV_LOOKUP
+ select COMMON_CLK
+ select CLKSRC_MMIO
+ select CPU_V7
+ select GENERIC_CLOCKEVENTS
+ select USE_OF
+ select SPARSE_IRQ
+ select NEED_MACH_MEMORY_H
+ select HAVE_SCHED_CLOCK
+ help
+ Support for boards based on the Texas Instruments Keystone family of
+ SoCs.
+
config ARCH_CLPS711X
bool "Cirrus Logic CLPS711x/EP721x/EP731x-based"
select CPU_ARM720T
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index 30eae87..75b5b79 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -146,6 +146,7 @@ machine-$(CONFIG_ARCH_EP93XX) := ep93xx
machine-$(CONFIG_ARCH_GEMINI) := gemini
machine-$(CONFIG_ARCH_H720X) := h720x
machine-$(CONFIG_ARCH_HIGHBANK) := highbank
+machine-$(CONFIG_ARCH_KEYSTONE) := keystone
machine-$(CONFIG_ARCH_INTEGRATOR) := integrator
machine-$(CONFIG_ARCH_IOP13XX) := iop13xx
machine-$(CONFIG_ARCH_IOP32X) := iop32x
diff --git a/arch/arm/boot/dts/keystone-sim.dts b/arch/arm/boot/dts/keystone-sim.dts
new file mode 100644
index 0000000..acec30f8
--- /dev/null
+++ b/arch/arm/boot/dts/keystone-sim.dts
@@ -0,0 +1,77 @@
+/dts-v1/;
+/include/ "skeleton.dtsi"
+
+/ {
+ model = "Texas Instruments Keystone 2 SoC";
+ compatible = "ti,keystone-evm";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ interrupt-parent = <&gic>;
+
+ aliases {
+ serial0 = &uart0;
+ };
+
+ chosen {
+ bootargs = "console=ttyS0,115200n8 debug earlyprintk lpj=50000 rdinit=/bin/ash rw root=/dev/ram0 initrd=0x85000000,2M";
+ };
+
+ memory {
+ reg = <0x80000000 0x8000000>;
+ };
+
+ cpus {
+ interrupt-parent = <&gic>;
+
+ cpu@0 {
+ compatible = "arm,cortex-a15";
+ };
+
+ cpu@1 {
+ compatible = "arm,cortex-a15";
+ };
+
+ cpu@2 {
+ compatible = "arm,cortex-a15";
+ };
+
+ cpu@3 {
+ compatible = "arm,cortex-a15";
+ };
+
+ };
+
+ soc {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+ compatible = "ti,keystone","simple-bus";
+ interrupt-parent = <&gic>;
+
+ gic: interrupt-controller@02560000 {
+ compatible = "arm,cortex-a15-gic";
+ #interrupt-cells = <3>;
+ #size-cells = <0>;
+ #address-cells = <1>;
+ interrupt-controller;
+ reg = <0x02561000 0x1000>,
+ <0x02562000 0x2000>;
+ };
+
+ timer {
+ compatible = "arm,armv7-timer";
+ interrupts = <1 13 0xf08 1 14 0xf08>;
+ clock-frequency = <10000000>; /* Freq in Hz - optional */
+ };
+
+ uart0: serial@02530c00 {
+ compatible = "ns16550a";
+ current-speed = <115200>;
+ reg-shift = <2>;
+ reg-io-width = <4>;
+ reg = <0x02530c00 0x100>;
+ clock-frequency = <48000000>;
+ interrupts = <0 277 0xf01>;
+ };
+ };
+};
diff --git a/arch/arm/configs/keystone_defconfig b/arch/arm/configs/keystone_defconfig
new file mode 100644
index 0000000..7f2a04b
--- /dev/null
+++ b/arch/arm/configs/keystone_defconfig
@@ -0,0 +1,20 @@
+CONFIG_EXPERIMENTAL=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_ARCH_KEYSTONE=y
+CONFIG_ARM_ARCH_TIMER=y
+CONFIG_AEABI=y
+CONFIG_HIGHMEM=y
+CONFIG_VFP=y
+CONFIG_NEON=y
+# CONFIG_SUSPEND is not set
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_BLK_DEV_RAM=y
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_OF_PLATFORM=y
+CONFIG_PRINTK_TIME=y
+CONFIG_DEBUG_KERNEL=y
+CONFIG_DEBUG_INFO=y
+CONFIG_DEBUG_USER=y
+CONFIG_DEBUG_LL=y
+CONFIG_EARLY_PRINTK=y
diff --git a/arch/arm/mach-keystone/Makefile b/arch/arm/mach-keystone/Makefile
new file mode 100644
index 0000000..d4671d5
--- /dev/null
+++ b/arch/arm/mach-keystone/Makefile
@@ -0,0 +1 @@
+obj-y := keystone.o
diff --git a/arch/arm/mach-keystone/Makefile.boot b/arch/arm/mach-keystone/Makefile.boot
new file mode 100644
index 0000000..dae9661
--- /dev/null
+++ b/arch/arm/mach-keystone/Makefile.boot
@@ -0,0 +1 @@
+zreladdr-y := 0x00008000
diff --git a/arch/arm/mach-keystone/include/mach/debug-macro.S b/arch/arm/mach-keystone/include/mach/debug-macro.S
new file mode 100644
index 0000000..1108210
--- /dev/null
+++ b/arch/arm/mach-keystone/include/mach/debug-macro.S
@@ -0,0 +1,44 @@
+/*
+ * Debugging macro include header
+ *
+ * Copyright 2010-2012 Texas Instruments, Inc.
+ * Copyright (C) 1994-1999 Russell King
+ * Moved from linux/arch/arm/kernel/debug.S by Ben Dooks
+ *
+ * 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/serial_reg.h>
+
+#define UART_SHIFT 2
+
+ .macro addruart,rp,rv,tmp
+ movw \rv, #0x0c00
+ movt \rv, #0xfed3
+ movw \rp, #0x0c00
+ movt \rp, #0x0253
+ .endm
+
+
+ .macro senduart,rd,rx
+ str \rd, [\rx, #UART_TX << UART_SHIFT]
+ .endm
+
+ .macro busyuart,rd,rx
+1002: ldr \rd, [\rx, #UART_LSR << UART_SHIFT]
+ and \rd, \rd, #UART_LSR_TEMT | UART_LSR_THRE
+ teq \rd, #UART_LSR_TEMT | UART_LSR_THRE
+ bne 1002b
+ .endm
+
+ .macro waituart,rd,rx
+ .endm
diff --git a/arch/arm/mach-keystone/include/mach/memory.h b/arch/arm/mach-keystone/include/mach/memory.h
new file mode 100644
index 0000000..7c78b1e
--- /dev/null
+++ b/arch/arm/mach-keystone/include/mach/memory.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2010-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_MACH_MEMORY_H
+#define __ASM_MACH_MEMORY_H
+
+#define MAX_PHYSMEM_BITS 36
+#define SECTION_SIZE_BITS 34
+
+#endif /* __ASM_MACH_MEMORY_H */
diff --git a/arch/arm/mach-keystone/include/mach/timex.h b/arch/arm/mach-keystone/include/mach/timex.h
new file mode 100644
index 0000000..f355ecb
--- /dev/null
+++ b/arch/arm/mach-keystone/include/mach/timex.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2010-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 __MACH_TIMEX_H
+#define __MACH_TIMEX_H
+
+#define CLOCK_TICK_RATE 1000000
+
+#endif
diff --git a/arch/arm/mach-keystone/include/mach/uncompress.h b/arch/arm/mach-keystone/include/mach/uncompress.h
new file mode 100644
index 0000000..1071761
--- /dev/null
+++ b/arch/arm/mach-keystone/include/mach/uncompress.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2010-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 __MACH_UNCOMPRESS_H
+#define __MACH_UNCOMPRESS_H
+
+#define putc(c)
+#define flush()
+#define arch_decomp_setup()
+#define arch_decomp_wdog()
+
+#endif
diff --git a/arch/arm/mach-keystone/keystone.c b/arch/arm/mach-keystone/keystone.c
new file mode 100644
index 0000000..702c184
--- /dev/null
+++ b/arch/arm/mach-keystone/keystone.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2010-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/io.h>
+#include <linux/of.h>
+#include <linux/init.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+
+#include <asm/setup.h>
+#include <asm/mach/map.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/time.h>
+#include <asm/arch_timer.h>
+#include <asm/hardware/gic.h>
+
+static struct map_desc io_desc[] = {
+ {
+ .virtual = 0xfe800000UL,
+ .pfn = __phys_to_pfn(0x02000000UL),
+ .length = 0x800000UL,
+ .type = MT_DEVICE
+ },
+};
+
+static void __init keystone_map_io(void)
+{
+ iotable_init(io_desc, ARRAY_SIZE(io_desc));
+}
+
+static const struct of_device_id irq_match[] = {
+ { .compatible = "arm,cortex-a15-gic", .data = gic_of_init, },
+ {}
+};
+
+static void __init keystone_init_irq(void)
+{
+ of_irq_init(irq_match);
+}
+
+
+static void __init keystone_timer_init(void)
+{
+ arch_timer_of_register();
+ arch_timer_sched_clock_init();
+}
+
+static struct sys_timer keystone_timer = {
+ .init = keystone_timer_init,
+};
+
+
+static void __init keystone_init(void)
+{
+ of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
+}
+
+static const char *keystone_match[] __initconst = {
+ "ti,keystone-evm",
+ NULL,
+};
+
+DT_MACHINE_START(KEYSTONE, "Keystone")
+ .map_io = keystone_map_io,
+ .init_irq = keystone_init_irq,
+ .timer = &keystone_timer,
+ .handle_irq = gic_handle_irq,
+ .init_machine = keystone_init,
+ .dt_compat = keystone_match,
+MACHINE_END
--
1.7.9.5

2012-08-11 01:26:25

by Cyril Chemparathy

[permalink] [raw]
Subject: [PATCH v2 05/22] ARM: LPAE: support 64-bit virt_to_phys patching

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.

Signed-off-by: Cyril Chemparathy <[email protected]>
---
arch/arm/include/asm/memory.h | 22 ++++++++++++++++++----
arch/arm/kernel/head.S | 4 ++++
arch/arm/kernel/setup.c | 2 +-
3 files changed, 23 insertions(+), 5 deletions(-)

diff --git a/arch/arm/include/asm/memory.h b/arch/arm/include/asm/memory.h
index 81e1714..dc5fbf3 100644
--- a/arch/arm/include/asm/memory.h
+++ b/arch/arm/include/asm/memory.h
@@ -154,14 +154,28 @@
#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;
- early_patch_imm8("add", t, x, __pv_offset, 0);
- return t;
+ unsigned long tlo, thi;
+
+ early_patch_imm8("add", tlo, x, __pv_offset, 0);
+
+#ifdef CONFIG_ARM_LPAE
+ /*
+ * On LPAE, we do not _need_ to do 64-bit arithmetic because the high
+ * order 32 bits are never changed by the phys-virt offset. We simply
+ * patch in the high order physical address bits instead.
+ */
+#ifdef __ARMEB__
+ early_patch_imm8_mov("mov", thi, __pv_phys_offset, 0);
+#else
+ early_patch_imm8_mov("mov", thi, __pv_phys_offset, 4);
+#endif
+#endif
+ return (u64)tlo | (u64)thi << 32;
}

static inline unsigned long __phys_to_virt(phys_addr_t x)
diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S
index 69a3c09..61fb8df 100644
--- a/arch/arm/kernel/head.S
+++ b/arch/arm/kernel/head.S
@@ -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
diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
index 59e0f57..edb4f42 100644
--- a/arch/arm/kernel/setup.c
+++ b/arch/arm/kernel/setup.c
@@ -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
--
1.7.9.5

2012-08-11 01:26:02

by Cyril Chemparathy

[permalink] [raw]
Subject: [PATCH v2 12/22] ARM: LPAE: define ARCH_LOW_ADDRESS_LIMIT for bootmem

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.

Signed-off-by: Cyril Chemparathy <[email protected]>
Signed-off-by: Vitaly Andrianov <[email protected]>
---
arch/arm/include/asm/memory.h | 2 ++
1 file changed, 2 insertions(+)

diff --git a/arch/arm/include/asm/memory.h b/arch/arm/include/asm/memory.h
index dc5fbf3..64db5a4 100644
--- a/arch/arm/include/asm/memory.h
+++ b/arch/arm/include/asm/memory.h
@@ -292,6 +292,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>
--
1.7.9.5

2012-08-11 01:27:08

by Cyril Chemparathy

[permalink] [raw]
Subject: [PATCH v2 01/22] 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.

Signed-off-by: Cyril Chemparathy <[email protected]>
---
arch/arm/Kconfig | 3 +
arch/arm/include/asm/module.h | 7 ++
arch/arm/include/asm/runtime-patch.h | 175 +++++++++++++++++++++++++++++++
arch/arm/kernel/Makefile | 1 +
arch/arm/kernel/module.c | 4 +
arch/arm/kernel/runtime-patch.c | 189 ++++++++++++++++++++++++++++++++++
arch/arm/kernel/setup.c | 3 +
arch/arm/kernel/vmlinux.lds.S | 10 ++
8 files changed, 392 insertions(+)
create mode 100644 arch/arm/include/asm/runtime-patch.h
create mode 100644 arch/arm/kernel/runtime-patch.c

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index e91c7cd..d0a04ad 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -61,6 +61,9 @@ config ARM
config ARM_HAS_SG_CHAIN
bool

+config ARM_RUNTIME_PATCH
+ bool
+
config NEED_SG_DMA_LENGTH
bool

diff --git a/arch/arm/include/asm/module.h b/arch/arm/include/asm/module.h
index 6c6809f..2090486 100644
--- a/arch/arm/include/asm/module.h
+++ b/arch/arm/include/asm/module.h
@@ -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 */
diff --git a/arch/arm/include/asm/runtime-patch.h b/arch/arm/include/asm/runtime-patch.h
new file mode 100644
index 0000000..6c6e8a2
--- /dev/null
+++ b/arch/arm/include/asm/runtime-patch.h
@@ -0,0 +1,175 @@
+/*
+ * 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_next(p) ((void *)(p) + sizeof(*(p)) + (p)->data_size)
+
+#define PATCH_TYPE_MASK 0x00ff
+#define PATCH_IMM8 0x0001
+
+#define PATCH_EARLY 0x8000
+
+#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 \
+ " .align\n" \
+ "5:\n" \
+ " .popsection\n" \
+ __VA_ARGS__)
+
+#define early_patch_stub(type, code, patch_data, ...) \
+ __asm__("@ patch stub\n" \
+ "1:\n" \
+ " b 6f\n" \
+ "2:\n" \
+ " .pushsection .runtime.patch.table, \"a\"\n" \
+ "3:\n" \
+ " .word 1b\n" \
+ " .hword (" __stringify(type | PATCH_EARLY) ")\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" \
+ "6:\n" \
+ code \
+ " b 2b\n" \
+ " .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
+ * 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, offset) \
+ patch_stub(PATCH_IMM8, \
+ /* code */ \
+ insn " %0, %1, %2\n", \
+ /* patch_data */ \
+ ".long " __stringify(sym + offset) "\n" \
+ insn " %0, %1, %2\n", \
+ : "=r" (to) \
+ : "r" (from), "I" (__IMM8), "m" (sym) \
+ : "cc")
+
+/*
+ * patch_imm8_mov() - same as patch_imm8(), but for mov/mvn instructions
+ */
+#define patch_imm8_mov(insn, to, sym, offset) \
+ patch_stub(PATCH_IMM8, \
+ /* code */ \
+ insn " %0, %1\n", \
+ /* patch_data */ \
+ ".long " __stringify(sym + offset) "\n" \
+ insn " %0, %1\n", \
+ : "=r" (to) \
+ : "I" (__IMM8), "m" (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
+ * patch application.
+ */
+#define early_patch_imm8(insn, to, from, sym, offset) \
+ early_patch_stub(PATCH_IMM8, \
+ /* code */ \
+ "ldr %0, =" __stringify(sym + offset) "\n" \
+ "ldr %0, [%0]\n" \
+ insn " %0, %1, %0\n", \
+ /* patch_data */ \
+ ".long " __stringify(sym + offset) "\n" \
+ insn " %0, %1, %2\n", \
+ : "=&r" (to) \
+ : "r" (from), "I" (__IMM8), "m" (sym) \
+ : "cc")
+
+#define early_patch_imm8_mov(insn, to, sym, offset) \
+ early_patch_stub(PATCH_IMM8, \
+ /* code */ \
+ "ldr %0, =" __stringify(sym + offset) "\n" \
+ "ldr %0, [%0]\n" \
+ insn " %0, %0\n", \
+ /* patch_data */ \
+ ".long " __stringify(sym + offset) "\n" \
+ insn " %0, %1\n", \
+ : "=&r" (to) \
+ : "I" (__IMM8), "m" (sym) \
+ : "cc")
+
+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 */
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
index 7ad2d5c..12cc1e7 100644
--- a/arch/arm/kernel/Makefile
+++ b/arch/arm/kernel/Makefile
@@ -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

extra-y := $(head-y) vmlinux.lds
diff --git a/arch/arm/kernel/module.c b/arch/arm/kernel/module.c
index 1e9be5d..dcebf80 100644
--- a/arch/arm/kernel/module.c
+++ b/arch/arm/kernel/module.c
@@ -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
/*
@@ -321,6 +322,9 @@ 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)
+ runtime_patch((void *)s->sh_addr, s->sh_size);
s = find_mod_section(hdr, sechdrs, ".alt.smp.init");
if (s && !is_smp())
#ifdef CONFIG_SMP_ON_UP
diff --git a/arch/arm/kernel/runtime-patch.c b/arch/arm/kernel/runtime-patch.c
new file mode 100644
index 0000000..fd37a2b
--- /dev/null
+++ b/arch/arm/kernel/runtime-patch.c
@@ -0,0 +1,189 @@
+/*
+ * 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>
+#include <asm/cacheflush.h>
+#include <asm/runtime-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);
+}
+
+#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 */
+
+ /* 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;
+ *ninsn = __opcode_to_mem_thumb32(*ninsn);
+
+ 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);
+ 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;
+ *ninsn = __opcode_to_mem_arm(*ninsn);
+
+ return 0;
+}
+
+#endif /* CONFIG_THUMB2_KERNEL */
+
+static int apply_patch_imm8(const struct patch_info *p)
+{
+ u32 *insn_ptr = p->insn;
+ int ret;
+
+ if (p->insn_size != sizeof(u32) || p->data_size != 2 * sizeof(u32)) {
+ pr_err("patch: bad patch, insn size %d, data size %d\n",
+ p->insn_size, p->data_size);
+ return -EINVAL;
+ }
+
+ ret = do_patch_imm8(p->data[1], *(u32 *)p->data[0], insn_ptr);
+ if (ret < 0)
+ return ret;
+
+ flush_icache_insn(insn_ptr, sizeof(u32));
+
+ return 0;
+}
+
+int runtime_patch(const void *table, unsigned size)
+{
+ const struct patch_info *p = table, *end = (table + size);
+
+ for (p = table; p < end; p = patch_next(p)) {
+ int type = p->type & PATCH_TYPE_MASK;
+ int ret = -EINVAL;
+
+ if (type == PATCH_IMM8)
+ ret = apply_patch_imm8(p);
+ if (ret < 0)
+ return ret;
+ }
+ 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));
+}
diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
index a81dcec..669bbf0 100644
--- a/arch/arm/kernel/setup.c
+++ b/arch/arm/kernel/setup.c
@@ -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();
}


diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S
index 36ff15b..ea35ca0 100644
--- a/arch/arm/kernel/vmlinux.lds.S
+++ b/arch/arm/kernel/vmlinux.lds.S
@@ -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
--
1.7.9.5

2012-08-11 01:27:17

by Cyril Chemparathy

[permalink] [raw]
Subject: [PATCH v2 04/22] ARM: LPAE: use phys_addr_t on virt <--> phys conversion

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

Signed-off-by: Vitaly Andrianov <[email protected]>
Signed-off-by: Cyril Chemparathy <[email protected]>
---
arch/arm/include/asm/memory.h | 26 ++++++++++++++++++--------
1 file changed, 18 insertions(+), 8 deletions(-)

diff --git a/arch/arm/include/asm/memory.h b/arch/arm/include/asm/memory.h
index 3d93779..81e1714 100644
--- a/arch/arm/include/asm/memory.h
+++ b/arch/arm/include/asm/memory.h
@@ -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

@@ -207,14 +217,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)

/*
--
1.7.9.5

2012-08-11 01:27:42

by Cyril Chemparathy

[permalink] [raw]
Subject: [PATCH v2 13/22] ARM: LPAE: factor out T1SZ and TTBR1 computations

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.

Signed-off-by: Cyril Chemparathy <[email protected]>
Signed-off-by: Vitaly Andrianov <[email protected]>
---
arch/arm/include/asm/pgtable-3level-hwdef.h | 10 ++++++++++
arch/arm/mm/proc-v7-3level.S | 16 ++++------------
2 files changed, 14 insertions(+), 12 deletions(-)

diff --git a/arch/arm/include/asm/pgtable-3level-hwdef.h b/arch/arm/include/asm/pgtable-3level-hwdef.h
index d795282..b501650 100644
--- a/arch/arm/include/asm/pgtable-3level-hwdef.h
+++ b/arch/arm/include/asm/pgtable-3level-hwdef.h
@@ -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
diff --git a/arch/arm/mm/proc-v7-3level.S b/arch/arm/mm/proc-v7-3level.S
index 78bd88c..e28383f 100644
--- a/arch/arm/mm/proc-v7-3level.S
+++ b/arch/arm/mm/proc-v7-3level.S
@@ -137,18 +137,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
--
1.7.9.5

2012-08-11 01:28:06

by Cyril Chemparathy

[permalink] [raw]
Subject: [PATCH v2 07/22] ARM: LPAE: use phys_addr_t in alloc_init_pud()

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.

Signed-off-by: Vitaly Andrianov <[email protected]>
Signed-off-by: Cyril Chemparathy <[email protected]>
Acked-by: Nicolas Pitre <[email protected]>
---
arch/arm/mm/mmu.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c
index 4c2d045..53eeeb8 100644
--- a/arch/arm/mm/mmu.c
+++ b/arch/arm/mm/mmu.c
@@ -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;
--
1.7.9.5

2012-08-11 01:28:23

by Cyril Chemparathy

[permalink] [raw]
Subject: [RFC v2 22/22] ARM: keystone: add switch over to high physical address range

Keystone platforms have their physical memory mapped at an address outside the
32-bit physical range. A Keystone machine with 16G of RAM would find its
memory at 0x0800000000 - 0x0bffffffff.

For boot purposes, the interconnect supports a limited alias of some of this
memory within the 32-bit addressable space (0x80000000 - 0xffffffff). This
aliasing is implemented in hardware, and is not intended to be used much
beyond boot. For instance, DMA coherence does not work when running out of
this aliased address space.

Therefore, we've taken the approach of booting out of the low physical address
range, and subsequently we switch over to the high range once we're safely
inside machine specific territory. This patch implements this switch over
mechanism, which involves rewiring the TTBRs and page tables to point to the
new physical address space.

Signed-off-by: Vitaly Andrianov <[email protected]>
Signed-off-by: Cyril Chemparathy <[email protected]>
---
arch/arm/Kconfig | 1 +
arch/arm/boot/dts/keystone-sim.dts | 8 +++---
arch/arm/configs/keystone_defconfig | 1 +
arch/arm/mach-keystone/include/mach/memory.h | 25 +++++++++++++++++
arch/arm/mach-keystone/keystone.c | 39 ++++++++++++++++++++++++++
arch/arm/mach-keystone/platsmp.c | 16 +++++++++--
6 files changed, 83 insertions(+), 7 deletions(-)

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 37b4e9c..18ffff7 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -417,6 +417,7 @@ config ARCH_KEYSTONE
select NEED_MACH_MEMORY_H
select HAVE_SCHED_CLOCK
select HAVE_SMP
+ select ZONE_DMA if ARM_LPAE
help
Support for boards based on the Texas Instruments Keystone family of
SoCs.
diff --git a/arch/arm/boot/dts/keystone-sim.dts b/arch/arm/boot/dts/keystone-sim.dts
index acec30f8..17ee473 100644
--- a/arch/arm/boot/dts/keystone-sim.dts
+++ b/arch/arm/boot/dts/keystone-sim.dts
@@ -4,8 +4,8 @@
/ {
model = "Texas Instruments Keystone 2 SoC";
compatible = "ti,keystone-evm";
- #address-cells = <1>;
- #size-cells = <1>;
+ #address-cells = <2>;
+ #size-cells = <2>;
interrupt-parent = <&gic>;

aliases {
@@ -13,11 +13,11 @@
};

chosen {
- bootargs = "console=ttyS0,115200n8 debug earlyprintk lpj=50000 rdinit=/bin/ash rw root=/dev/ram0 initrd=0x85000000,2M";
+ bootargs = "console=ttyS0,115200n8 debug earlyprintk lpj=50000 rdinit=/bin/ash rw root=/dev/ram0 initrd=0x805000000,2M";
};

memory {
- reg = <0x80000000 0x8000000>;
+ reg = <0x00000008 0x00000000 0x00000000 0x8000000>;
};

cpus {
diff --git a/arch/arm/configs/keystone_defconfig b/arch/arm/configs/keystone_defconfig
index 5f71e66..8ea3b96 100644
--- a/arch/arm/configs/keystone_defconfig
+++ b/arch/arm/configs/keystone_defconfig
@@ -1,6 +1,7 @@
CONFIG_EXPERIMENTAL=y
CONFIG_BLK_DEV_INITRD=y
CONFIG_ARCH_KEYSTONE=y
+CONFIG_ARM_LPAE=y
CONFIG_SMP=y
CONFIG_ARM_ARCH_TIMER=y
CONFIG_NR_CPUS=4
diff --git a/arch/arm/mach-keystone/include/mach/memory.h b/arch/arm/mach-keystone/include/mach/memory.h
index 7c78b1e..a5f7a1a 100644
--- a/arch/arm/mach-keystone/include/mach/memory.h
+++ b/arch/arm/mach-keystone/include/mach/memory.h
@@ -19,4 +19,29 @@
#define MAX_PHYSMEM_BITS 36
#define SECTION_SIZE_BITS 34

+#define KEYSTONE_LOW_PHYS_START 0x80000000ULL
+#define KEYSTONE_LOW_PHYS_SIZE 0x80000000ULL /* 2G */
+#define KEYSTONE_LOW_PHYS_END (KEYSTONE_LOW_PHYS_START + \
+ KEYSTONE_LOW_PHYS_SIZE - 1)
+
+#define KEYSTONE_HIGH_PHYS_START 0x800000000ULL
+#define KEYSTONE_HIGH_PHYS_SIZE 0x400000000ULL /* 16G */
+#define KEYSTONE_HIGH_PHYS_END (KEYSTONE_HIGH_PHYS_START + \
+ KEYSTONE_HIGH_PHYS_SIZE - 1)
+#ifdef CONFIG_ARM_LPAE
+
+#ifndef __ASSEMBLY__
+
+static inline phys_addr_t __virt_to_idmap(unsigned long x)
+{
+ return (phys_addr_t)(x) - CONFIG_PAGE_OFFSET +
+ KEYSTONE_LOW_PHYS_START;
+}
+
+#define virt_to_idmap(x) __virt_to_idmap((unsigned long)(x))
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* CONFIG_ARM_LPAE */
+
#endif /* __ASM_MACH_MEMORY_H */
diff --git a/arch/arm/mach-keystone/keystone.c b/arch/arm/mach-keystone/keystone.c
index 6a8ece9..c4be7a7 100644
--- a/arch/arm/mach-keystone/keystone.c
+++ b/arch/arm/mach-keystone/keystone.c
@@ -74,6 +74,41 @@ static const char *keystone_match[] __initconst = {
NULL,
};

+static void __init keystone_init_meminfo(void)
+{
+ bool lpae = IS_ENABLED(CONFIG_ARM_LPAE);
+ bool pvpatch = IS_ENABLED(CONFIG_ARM_PATCH_PHYS_VIRT);
+ phys_addr_t offset = PHYS_OFFSET - KEYSTONE_LOW_PHYS_START;
+ phys_addr_t mem_start, mem_end;
+
+ BUG_ON(meminfo.nr_banks < 1);
+ mem_start = meminfo.bank[0].start;
+ mem_end = mem_start + meminfo.bank[0].size - 1;
+
+ /* nothing to do if we are running out of the <32-bit space */
+ if (mem_start >= KEYSTONE_LOW_PHYS_START &&
+ mem_end <= KEYSTONE_LOW_PHYS_END)
+ return;
+
+ if (!lpae || !pvpatch) {
+ panic("Enable %s%s%s to run outside 32-bit space\n",
+ !lpae ? __stringify(CONFIG_ARM_LPAE) : "",
+ (!lpae && !pvpatch) ? " and " : "",
+ !pvpatch ? __stringify(CONFIG_ARM_PATCH_PHYS_VIRT) : "");
+ }
+
+ if (mem_start < KEYSTONE_HIGH_PHYS_START ||
+ mem_end > KEYSTONE_HIGH_PHYS_END) {
+ panic("Invalid address space for memory (%08llx-%08llx)\n",
+ mem_start, mem_end);
+ }
+
+ offset += KEYSTONE_HIGH_PHYS_START;
+ pr_info("switching to high address space at 0x%llx\n", offset);
+ __pv_phys_offset = offset;
+ __pv_offset = offset - PAGE_OFFSET;
+}
+
DT_MACHINE_START(KEYSTONE, "Keystone")
smp_ops(keystone_smp_ops)
.map_io = keystone_map_io,
@@ -82,4 +117,8 @@ DT_MACHINE_START(KEYSTONE, "Keystone")
.handle_irq = gic_handle_irq,
.init_machine = keystone_init,
.dt_compat = keystone_match,
+ .init_meminfo = keystone_init_meminfo,
+#ifdef CONFIG_ZONE_DMA
+ .dma_zone_size = SZ_2G,
+#endif
MACHINE_END
diff --git a/arch/arm/mach-keystone/platsmp.c b/arch/arm/mach-keystone/platsmp.c
index dbe7601..0151906 100644
--- a/arch/arm/mach-keystone/platsmp.c
+++ b/arch/arm/mach-keystone/platsmp.c
@@ -24,6 +24,7 @@
#include <asm/smp_ops.h>
#include <asm/hardware/gic.h>
#include <asm/cacheflush.h>
+#include <asm/tlbflush.h>
#include <asm/memory.h>

#include "keystone.h"
@@ -51,17 +52,26 @@ static void __init keystone_smp_prepare_cpus(unsigned int max_cpus)
/* nothing for now */
}

+static void __cpuinit keystone_secondary_initmem(void)
+{
+#ifdef CONFIG_ARM_LPAE
+ pgd_t *pgd0 = pgd_offset_k(0);
+ cpu_set_ttbr(1, __pa(pgd0) + TTBR1_OFFSET);
+ local_flush_tlb_all();
+#endif
+}
+
static void __cpuinit keystone_secondary_init(unsigned int cpu)
{
gic_secondary_init(0);
+ keystone_secondary_initmem();
}

static int __cpuinit
keystone_boot_secondary(unsigned int cpu, struct task_struct *idle)
{
- unsigned long *ptr;
-
- ptr = phys_to_virt(0x800001f0);
+ unsigned long *ptr = (unsigned long *)(PAGE_OFFSET + 0x1f0);
+
ptr[cpu] = virt_to_idmap(&secondary_startup);
__cpuc_flush_dcache_area(ptr, sizeof(ptr) * 4);

--
1.7.9.5

2012-08-11 01:28:40

by Cyril Chemparathy

[permalink] [raw]
Subject: [PATCH v2 16/22] ARM: mm: cleanup checks for membank overlap with vmalloc area

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.

Signed-off-by: Cyril Chemparathy <[email protected]>
Signed-off-by: Vitaly Andrianov <[email protected]>
---
arch/arm/mm/mmu.c | 19 +------------------
1 file changed, 1 insertion(+), 18 deletions(-)

diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c
index f764c03..3d685c6 100644
--- a/arch/arm/mm/mmu.c
+++ b/arch/arm/mm/mmu.c
@@ -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.
*/
--
1.7.9.5

2012-08-11 01:29:01

by Cyril Chemparathy

[permalink] [raw]
Subject: [PATCH v2 02/22] ARM: add self test for runtime patch mechanism

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.

Signed-off-by: Cyril Chemparathy <[email protected]>
---
arch/arm/Kconfig | 12 ++++++++++++
arch/arm/kernel/runtime-patch.c | 41 +++++++++++++++++++++++++++++++++++++++
2 files changed, 53 insertions(+)

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index d0a04ad..7e552dc 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -211,6 +211,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
diff --git a/arch/arm/kernel/runtime-patch.c b/arch/arm/kernel/runtime-patch.c
index fd37a2b..c471d8c 100644
--- a/arch/arm/kernel/runtime-patch.c
+++ b/arch/arm/kernel/runtime-patch.c
@@ -163,6 +163,44 @@ static int apply_patch_imm8(const struct patch_info *p)
return 0;
}

+#ifdef CONFIG_ARM_RUNTIME_PATCH_TEST
+static void __init __used __naked __patch_test_code_imm8(void)
+{
+ __asm__ __volatile__ (
+ " .irp shift1, 0, 6, 12, 18\n"
+ " .irp shift2, 0, 1, 2, 3, 4, 5\n"
+ " add r1, r2, #(0x41 << (\\shift1 + \\shift2))\n"
+ " .endr\n"
+ " .endr\n"
+ " .word 0\n"
+ : : :
+ );
+}
+
+static void __init test_patch_imm8(void)
+{
+ u32 test_code_addr = (u32)(&__patch_test_code_imm8);
+ u32 *test_code = (u32 *)(test_code_addr & ~0x3);
+ int i, ret;
+ u32 ninsn, insn;
+
+ insn = test_code[0];
+ for (i = 0; test_code[i]; i++) {
+ ret = do_patch_imm8(insn, 0x41 << i, &ninsn);
+ if (ret < 0)
+ pr_err("runtime patch (imm8): failed at shift %d\n", i);
+ else if (ninsn != test_code[i])
+ pr_err("runtime patch (imm8): failed, need %x got %x\n",
+ test_code[i], ninsn);
+ }
+}
+
+static void __init runtime_patch_test(void)
+{
+ test_patch_imm8();
+}
+#endif
+
int runtime_patch(const void *table, unsigned size)
{
const struct patch_info *p = table, *end = (table + size);
@@ -185,5 +223,8 @@ void __init runtime_patch_kernel(void)
const void *start = &__runtime_patch_table_begin;
const void *end = &__runtime_patch_table_end;

+#ifdef CONFIG_ARM_RUNTIME_PATCH_TEST
+ runtime_patch_test();
+#endif
BUG_ON(runtime_patch(start, end - start));
}
--
1.7.9.5

2012-08-11 01:28:58

by Cyril Chemparathy

[permalink] [raw]
Subject: [PATCH v2 11/22] ARM: LPAE: use 64-bit accessors for TTBR registers

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.

Signed-off-by: Cyril Chemparathy <[email protected]>
Signed-off-by: Vitaly Andrianov <[email protected]>
---
arch/arm/include/asm/proc-fns.h | 24 +++++++++++++++++++-----
arch/arm/mm/context.c | 9 ++-------
2 files changed, 21 insertions(+), 12 deletions(-)

diff --git a/arch/arm/include/asm/proc-fns.h b/arch/arm/include/asm/proc-fns.h
index 75b5f14..24224df 100644
--- a/arch/arm/include/asm/proc-fns.h
+++ b/arch/arm/include/asm/proc-fns.h
@@ -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) \
+ : : "cc"); \
+ 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); \
})
diff --git a/arch/arm/mm/context.c b/arch/arm/mm/context.c
index 119bc52..cd27f35 100644
--- a/arch/arm/mm/context.c
+++ b/arch/arm/mm/context.c
@@ -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
--
1.7.9.5

2012-08-11 01:25:51

by Cyril Chemparathy

[permalink] [raw]
Subject: [PATCH v2 03/22] ARM: use late patch framework for phys-virt patching

This patch replaces the original physical offset patching implementation
with one that uses the newly added patching framework. In the process, we now
unconditionally initialize the __pv_phys_offset and __pv_offset globals in the
head.S code.

Signed-off-by: Cyril Chemparathy <[email protected]>
---
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(-)

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 7e552dc..9ac86ea 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -199,6 +199,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
diff --git a/arch/arm/include/asm/memory.h b/arch/arm/include/asm/memory.h
index e965f1b..3d93779 100644
--- a/arch/arm/include/asm/memory.h
+++ b/arch/arm/include/asm/memory.h
@@ -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
diff --git a/arch/arm/kernel/armksyms.c b/arch/arm/kernel/armksyms.c
index 60d3b73..6b388f8 100644
--- a/arch/arm/kernel/armksyms.c
+++ b/arch/arm/kernel/armksyms.c
@@ -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
diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S
index 3db960e..69a3c09 100644
--- a/arch/arm/kernel/head.S
+++ b/arch/arm/kernel/head.S
@@ -117,7 +117,7 @@ ENTRY(stext)
bl __fixup_smp
#endif
#ifdef CONFIG_ARM_PATCH_PHYS_VIRT
- bl __fixup_pv_table
+ bl __fixup_pv_offsets
#endif
bl __create_page_tables

@@ -511,92 +511,29 @@ ENDPROC(fixup_smp)

#ifdef CONFIG_ARM_PATCH_PHYS_VIRT

-/* __fixup_pv_table - patch the stub instructions with the delta between
- * PHYS_OFFSET and PAGE_OFFSET, which is assumed to be 16MiB aligned and
- * can be expressed by an immediate shifter operand. The stub instruction
- * has a form of '(add|sub) rd, rn, #imm'.
+/*
+ * __fixup_pv_offsets - update __pv_offset and __pv_phys_offset based on the
+ * runtime location of the kernel.
*/
__HEAD
-__fixup_pv_table:
+__fixup_pv_offsets:
adr r0, 1f
- ldmia r0, {r3-r5, r7}
+ ldmia r0, {r3-r6}
sub r3, r0, r3 @ PHYS_OFFSET - PAGE_OFFSET
- add r4, r4, r3 @ adjust table start address
- add r5, r5, r3 @ adjust table end address
- add r7, r7, r3 @ adjust __pv_phys_offset address
- str r8, [r7] @ save computed PHYS_OFFSET to __pv_phys_offset
- mov r6, r3, lsr #24 @ constant for add/sub instructions
- teq r3, r6, lsl #24 @ must be 16MiB aligned
-THUMB( it ne @ cross section branch )
- bne __error
- str r6, [r7, #4] @ save to __pv_offset
- b __fixup_a_pv_table
-ENDPROC(__fixup_pv_table)
+ add r4, r4, r3 @ virt_to_phys(__pv_phys_offset)
+ add r5, r5, r3 @ virt_to_phys(__pv_offset)
+ add r6, r6, r3 @ virt_to_phys(PAGE_OFFSET) = PHYS_OFFSET
+ str r6, [r4] @ save __pv_phys_offset
+ str r3, [r5] @ save __pv_offset
+ mov pc, lr
+ENDPROC(__fixup_pv_offsets)

.align
1: .long .
- .long __pv_table_begin
- .long __pv_table_end
-2: .long __pv_phys_offset
-
- .text
-__fixup_a_pv_table:
-#ifdef CONFIG_THUMB2_KERNEL
- lsls r6, #24
- beq 2f
- clz r7, r6
- lsr r6, #24
- lsl r6, r7
- bic r6, #0x0080
- lsrs r7, #1
- orrcs r6, #0x0080
- orr r6, r6, r7, lsl #12
- orr r6, #0x4000
- b 2f
-1: add r7, r3
- ldrh ip, [r7, #2]
- and ip, 0x8f00
- orr ip, r6 @ mask in offset bits 31-24
- strh ip, [r7, #2]
-2: cmp r4, r5
- ldrcc r7, [r4], #4 @ use branch for delay slot
- bcc 1b
- bx lr
-#else
- b 2f
-1: ldr ip, [r7, r3]
- bic ip, ip, #0x000000ff
- orr ip, ip, r6 @ mask in offset bits 31-24
- str ip, [r7, r3]
-2: cmp r4, r5
- ldrcc r7, [r4], #4 @ use branch for delay slot
- bcc 1b
- mov pc, lr
+ .long __pv_phys_offset
+ .long __pv_offset
+ .long PAGE_OFFSET
#endif
-ENDPROC(__fixup_a_pv_table)
-
-ENTRY(fixup_pv_table)
- stmfd sp!, {r4 - r7, lr}
- ldr r2, 2f @ get address of __pv_phys_offset
- mov r3, #0 @ no offset
- mov r4, r0 @ r0 = table start
- add r5, r0, r1 @ r1 = table size
- ldr r6, [r2, #4] @ get __pv_offset
- bl __fixup_a_pv_table
- ldmfd sp!, {r4 - r7, pc}
-ENDPROC(fixup_pv_table)

- .align
-2: .long __pv_phys_offset
-
- .data
- .globl __pv_phys_offset
- .type __pv_phys_offset, %object
-__pv_phys_offset:
- .long 0
- .size __pv_phys_offset, . - __pv_phys_offset
-__pv_offset:
- .long 0
-#endif

#include "head-common.S"
diff --git a/arch/arm/kernel/module.c b/arch/arm/kernel/module.c
index dcebf80..ac13dd5 100644
--- a/arch/arm/kernel/module.c
+++ b/arch/arm/kernel/module.c
@@ -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)
runtime_patch((void *)s->sh_addr, s->sh_size);
diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
index 669bbf0..59e0f57 100644
--- a/arch/arm/kernel/setup.c
+++ b/arch/arm/kernel/setup.c
@@ -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
*/
diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S
index ea35ca0..2080111 100644
--- a/arch/arm/kernel/vmlinux.lds.S
+++ b/arch/arm/kernel/vmlinux.lds.S
@@ -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)
--
1.7.9.5

2012-08-11 01:29:40

by Cyril Chemparathy

[permalink] [raw]
Subject: [PATCH v2 08/22] ARM: LPAE: use phys_addr_t in free_memmap()

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.

Signed-off-by: Vitaly Andrianov <[email protected]>
Signed-off-by: Cyril Chemparathy <[email protected]>
Acked-by: Nicolas Pitre <[email protected]>
---
arch/arm/mm/init.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c
index 9aec41f..19ba70b 100644
--- a/arch/arm/mm/init.c
+++ b/arch/arm/mm/init.c
@@ -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,
--
1.7.9.5

2012-08-11 01:29:41

by Cyril Chemparathy

[permalink] [raw]
Subject: [PATCH v2 18/22] ARM: add virt_to_idmap for interconnect aliasing

From: Vitaly Andrianov <[email protected]>

On some PAE systems (e.g. TI Keystone), memory is above the 32-bit addressible
limit, and the interconnect provides an aliased view of parts of physical
memory in the 32-bit addressible space. This alias is strictly for boot time
usage, and is not otherwise usable because of coherency limitations.

On such systems, the idmap mechanism needs to take this aliased mapping into
account. This patch introduces a virt_to_idmap() macro, which can be used on
such sub-architectures to represent the interconnect supported boot time
alias. Most other systems would leave this macro untouched, i.e., do a simply
virt_to_phys() and nothing more.

Signed-off-by: Vitaly Andrianov <[email protected]>
Signed-off-by: Cyril Chemparathy <[email protected]>
---
arch/arm/include/asm/memory.h | 9 +++++++++
arch/arm/kernel/smp.c | 2 +-
arch/arm/mm/idmap.c | 4 ++--
3 files changed, 12 insertions(+), 3 deletions(-)

diff --git a/arch/arm/include/asm/memory.h b/arch/arm/include/asm/memory.h
index e5d0cc8..59f101c 100644
--- a/arch/arm/include/asm/memory.h
+++ b/arch/arm/include/asm/memory.h
@@ -257,6 +257,15 @@ static inline void *phys_to_virt(phys_addr_t x)
#define pfn_to_kaddr(pfn) __va((pfn) << PAGE_SHIFT)

/*
+ * These are for systems that have a hardware interconnect supported alias of
+ * physical memory for idmap purposes. Most cases should leave these
+ * untouched.
+ */
+#ifndef virt_to_idmap
+#define virt_to_idmap(x) virt_to_phys(x)
+#endif
+
+/*
* Virtual <-> DMA view memory address translations
* Again, these are *only* valid on the kernel direct mapped RAM
* memory. Use of these is *deprecated* (and that doesn't mean
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c
index 9831716..628f895 100644
--- a/arch/arm/kernel/smp.c
+++ b/arch/arm/kernel/smp.c
@@ -62,7 +62,7 @@ static DECLARE_COMPLETION(cpu_running);

static unsigned long get_arch_pgd(pgd_t *pgd)
{
- phys_addr_t pgdir = virt_to_phys(pgd);
+ phys_addr_t pgdir = virt_to_idmap(pgd);
BUG_ON(pgdir & ARCH_PGD_MASK);
return pgdir >> ARCH_PGD_SHIFT;
}
diff --git a/arch/arm/mm/idmap.c b/arch/arm/mm/idmap.c
index ab88ed4..919cb6e 100644
--- a/arch/arm/mm/idmap.c
+++ b/arch/arm/mm/idmap.c
@@ -85,8 +85,8 @@ static int __init init_static_idmap(void)
return -ENOMEM;

/* Add an identity mapping for the physical address of the section. */
- idmap_start = virt_to_phys((void *)__idmap_text_start);
- idmap_end = virt_to_phys((void *)__idmap_text_end);
+ idmap_start = virt_to_idmap((void *)__idmap_text_start);
+ idmap_end = virt_to_idmap((void *)__idmap_text_end);

pr_info("Setting up static identity map for 0x%llx - 0x%llx\n",
(long long)idmap_start, (long long)idmap_end);
--
1.7.9.5

2012-08-11 01:30:14

by Cyril Chemparathy

[permalink] [raw]
Subject: [RFC v2 21/22] ARM: keystone: enable SMP on Keystone machines

This patch adds basic SMP support for Keystone machines. Nothing very fancy
here, just enough to get 4 CPUs booted up. This does not include support for
hotplug, etc.

Signed-off-by: Vitaly Andrianov <[email protected]>
Signed-off-by: Cyril Chemparathy <[email protected]>
---
arch/arm/Kconfig | 1 +
arch/arm/configs/keystone_defconfig | 2 +
arch/arm/mach-keystone/Makefile | 1 +
arch/arm/mach-keystone/keystone.c | 3 ++
arch/arm/mach-keystone/keystone.h | 23 +++++++++++
arch/arm/mach-keystone/platsmp.c | 74 +++++++++++++++++++++++++++++++++++
6 files changed, 104 insertions(+)
create mode 100644 arch/arm/mach-keystone/keystone.h
create mode 100644 arch/arm/mach-keystone/platsmp.c

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index f1b8aa0..37b4e9c 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -416,6 +416,7 @@ config ARCH_KEYSTONE
select SPARSE_IRQ
select NEED_MACH_MEMORY_H
select HAVE_SCHED_CLOCK
+ select HAVE_SMP
help
Support for boards based on the Texas Instruments Keystone family of
SoCs.
diff --git a/arch/arm/configs/keystone_defconfig b/arch/arm/configs/keystone_defconfig
index 7f2a04b..5f71e66 100644
--- a/arch/arm/configs/keystone_defconfig
+++ b/arch/arm/configs/keystone_defconfig
@@ -1,7 +1,9 @@
CONFIG_EXPERIMENTAL=y
CONFIG_BLK_DEV_INITRD=y
CONFIG_ARCH_KEYSTONE=y
+CONFIG_SMP=y
CONFIG_ARM_ARCH_TIMER=y
+CONFIG_NR_CPUS=4
CONFIG_AEABI=y
CONFIG_HIGHMEM=y
CONFIG_VFP=y
diff --git a/arch/arm/mach-keystone/Makefile b/arch/arm/mach-keystone/Makefile
index d4671d5..3f6b8ab 100644
--- a/arch/arm/mach-keystone/Makefile
+++ b/arch/arm/mach-keystone/Makefile
@@ -1 +1,2 @@
obj-y := keystone.o
+obj-$(CONFIG_SMP) += platsmp.o
diff --git a/arch/arm/mach-keystone/keystone.c b/arch/arm/mach-keystone/keystone.c
index 702c184..6a8ece9 100644
--- a/arch/arm/mach-keystone/keystone.c
+++ b/arch/arm/mach-keystone/keystone.c
@@ -26,6 +26,8 @@
#include <asm/arch_timer.h>
#include <asm/hardware/gic.h>

+#include "keystone.h"
+
static struct map_desc io_desc[] = {
{
.virtual = 0xfe800000UL,
@@ -73,6 +75,7 @@ static const char *keystone_match[] __initconst = {
};

DT_MACHINE_START(KEYSTONE, "Keystone")
+ smp_ops(keystone_smp_ops)
.map_io = keystone_map_io,
.init_irq = keystone_init_irq,
.timer = &keystone_timer,
diff --git a/arch/arm/mach-keystone/keystone.h b/arch/arm/mach-keystone/keystone.h
new file mode 100644
index 0000000..71bd0f4
--- /dev/null
+++ b/arch/arm/mach-keystone/keystone.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2010-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 __KEYSTONE_H__
+#define __KEYSTONE_H__
+
+extern struct smp_ops keystone_smp_ops;
+extern void secondary_startup(void);
+
+#endif /* __KEYSTONE_H__ */
diff --git a/arch/arm/mach-keystone/platsmp.c b/arch/arm/mach-keystone/platsmp.c
new file mode 100644
index 0000000..dbe7601
--- /dev/null
+++ b/arch/arm/mach-keystone/platsmp.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2012 Texas Instruments, Inc.
+ *
+ * Based on platsmp.c, Copyright 2010-2011 Calxeda, Inc.
+ * Based on platsmp.c, Copyright (C) 2002 ARM Ltd.
+ *
+ * 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/init.h>
+#include <linux/smp.h>
+#include <linux/io.h>
+
+#include <asm/smp_plat.h>
+#include <asm/smp_ops.h>
+#include <asm/hardware/gic.h>
+#include <asm/cacheflush.h>
+#include <asm/memory.h>
+
+#include "keystone.h"
+
+static void __init keystone_smp_init_cpus(void)
+{
+ unsigned int i, ncores;
+
+ ncores = 4;
+
+ /* sanity check */
+ if (ncores > NR_CPUS) {
+ pr_warn("restricted to %d cpus\n", NR_CPUS);
+ ncores = NR_CPUS;
+ }
+
+ for (i = 0; i < ncores; i++)
+ set_cpu_possible(i, true);
+
+ set_smp_cross_call(gic_raise_softirq);
+}
+
+static void __init keystone_smp_prepare_cpus(unsigned int max_cpus)
+{
+ /* nothing for now */
+}
+
+static void __cpuinit keystone_secondary_init(unsigned int cpu)
+{
+ gic_secondary_init(0);
+}
+
+static int __cpuinit
+keystone_boot_secondary(unsigned int cpu, struct task_struct *idle)
+{
+ unsigned long *ptr;
+
+ ptr = phys_to_virt(0x800001f0);
+ ptr[cpu] = virt_to_idmap(&secondary_startup);
+ __cpuc_flush_dcache_area(ptr, sizeof(ptr) * 4);
+
+ return 0;
+}
+
+struct smp_ops keystone_smp_ops __initdata = {
+ smp_init_ops(keystone)
+ smp_secondary_ops(keystone)
+};
--
1.7.9.5

2012-08-11 01:30:21

by Cyril Chemparathy

[permalink] [raw]
Subject: [PATCH v2 06/22] ARM: LPAE: use signed arithmetic for mask definitions

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.

Signed-off-by: Cyril Chemparathy <[email protected]>
Signed-off-by: Vitaly Andrianov <[email protected]>
---
arch/arm/include/asm/page.h | 2 +-
arch/arm/include/asm/pgtable-3level.h | 6 +++---
2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/arch/arm/include/asm/page.h b/arch/arm/include/asm/page.h
index ecf9019..1e0fe08 100644
--- a/arch/arm/include/asm/page.h
+++ b/arch/arm/include/asm/page.h
@@ -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__

diff --git a/arch/arm/include/asm/pgtable-3level.h b/arch/arm/include/asm/pgtable-3level.h
index b249035..ae39d11 100644
--- a/arch/arm/include/asm/pgtable-3level.h
+++ b/arch/arm/include/asm/pgtable-3level.h
@@ -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)

--
1.7.9.5

2012-08-11 01:30:18

by Cyril Chemparathy

[permalink] [raw]
Subject: [PATCH v2 09/22] ARM: LPAE: use phys_addr_t for initrd location and size

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.

Signed-off-by: Vitaly Andrianov <[email protected]>
Signed-off-by: Cyril Chemparathy <[email protected]>
---
arch/arm/mm/init.c | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c
index 19ba70b..bae9d05 100644
--- a/arch/arm/mm/init.c
+++ b/arch/arm/mm/init.c
@@ -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) {
--
1.7.9.5

2012-08-12 02:22:16

by Nicolas Pitre

[permalink] [raw]
Subject: Re: [PATCH v2 01/22] ARM: add mechanism for late code patching

On Fri, 10 Aug 2012, Cyril Chemparathy wrote:

> 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.
>
> Signed-off-by: Cyril Chemparathy <[email protected]>

Reviewed-by: Nicolas Pitre <[email protected]>


> ---
> arch/arm/Kconfig | 3 +
> arch/arm/include/asm/module.h | 7 ++
> arch/arm/include/asm/runtime-patch.h | 175 +++++++++++++++++++++++++++++++
> arch/arm/kernel/Makefile | 1 +
> arch/arm/kernel/module.c | 4 +
> arch/arm/kernel/runtime-patch.c | 189 ++++++++++++++++++++++++++++++++++
> arch/arm/kernel/setup.c | 3 +
> arch/arm/kernel/vmlinux.lds.S | 10 ++
> 8 files changed, 392 insertions(+)
> create mode 100644 arch/arm/include/asm/runtime-patch.h
> create mode 100644 arch/arm/kernel/runtime-patch.c
>
> diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> index e91c7cd..d0a04ad 100644
> --- a/arch/arm/Kconfig
> +++ b/arch/arm/Kconfig
> @@ -61,6 +61,9 @@ config ARM
> config ARM_HAS_SG_CHAIN
> bool
>
> +config ARM_RUNTIME_PATCH
> + bool
> +
> config NEED_SG_DMA_LENGTH
> bool
>
> diff --git a/arch/arm/include/asm/module.h b/arch/arm/include/asm/module.h
> index 6c6809f..2090486 100644
> --- a/arch/arm/include/asm/module.h
> +++ b/arch/arm/include/asm/module.h
> @@ -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 */
> diff --git a/arch/arm/include/asm/runtime-patch.h b/arch/arm/include/asm/runtime-patch.h
> new file mode 100644
> index 0000000..6c6e8a2
> --- /dev/null
> +++ b/arch/arm/include/asm/runtime-patch.h
> @@ -0,0 +1,175 @@
> +/*
> + * 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_next(p) ((void *)(p) + sizeof(*(p)) + (p)->data_size)
> +
> +#define PATCH_TYPE_MASK 0x00ff
> +#define PATCH_IMM8 0x0001
> +
> +#define PATCH_EARLY 0x8000
> +
> +#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 \
> + " .align\n" \
> + "5:\n" \
> + " .popsection\n" \
> + __VA_ARGS__)
> +
> +#define early_patch_stub(type, code, patch_data, ...) \
> + __asm__("@ patch stub\n" \
> + "1:\n" \
> + " b 6f\n" \
> + "2:\n" \
> + " .pushsection .runtime.patch.table, \"a\"\n" \
> + "3:\n" \
> + " .word 1b\n" \
> + " .hword (" __stringify(type | PATCH_EARLY) ")\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" \
> + "6:\n" \
> + code \
> + " b 2b\n" \
> + " .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
> + * 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, offset) \
> + patch_stub(PATCH_IMM8, \
> + /* code */ \
> + insn " %0, %1, %2\n", \
> + /* patch_data */ \
> + ".long " __stringify(sym + offset) "\n" \
> + insn " %0, %1, %2\n", \
> + : "=r" (to) \
> + : "r" (from), "I" (__IMM8), "m" (sym) \
> + : "cc")
> +
> +/*
> + * patch_imm8_mov() - same as patch_imm8(), but for mov/mvn instructions
> + */
> +#define patch_imm8_mov(insn, to, sym, offset) \
> + patch_stub(PATCH_IMM8, \
> + /* code */ \
> + insn " %0, %1\n", \
> + /* patch_data */ \
> + ".long " __stringify(sym + offset) "\n" \
> + insn " %0, %1\n", \
> + : "=r" (to) \
> + : "I" (__IMM8), "m" (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
> + * patch application.
> + */
> +#define early_patch_imm8(insn, to, from, sym, offset) \
> + early_patch_stub(PATCH_IMM8, \
> + /* code */ \
> + "ldr %0, =" __stringify(sym + offset) "\n" \
> + "ldr %0, [%0]\n" \
> + insn " %0, %1, %0\n", \
> + /* patch_data */ \
> + ".long " __stringify(sym + offset) "\n" \
> + insn " %0, %1, %2\n", \
> + : "=&r" (to) \
> + : "r" (from), "I" (__IMM8), "m" (sym) \
> + : "cc")
> +
> +#define early_patch_imm8_mov(insn, to, sym, offset) \
> + early_patch_stub(PATCH_IMM8, \
> + /* code */ \
> + "ldr %0, =" __stringify(sym + offset) "\n" \
> + "ldr %0, [%0]\n" \
> + insn " %0, %0\n", \
> + /* patch_data */ \
> + ".long " __stringify(sym + offset) "\n" \
> + insn " %0, %1\n", \
> + : "=&r" (to) \
> + : "I" (__IMM8), "m" (sym) \
> + : "cc")
> +
> +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 */
> diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
> index 7ad2d5c..12cc1e7 100644
> --- a/arch/arm/kernel/Makefile
> +++ b/arch/arm/kernel/Makefile
> @@ -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
>
> extra-y := $(head-y) vmlinux.lds
> diff --git a/arch/arm/kernel/module.c b/arch/arm/kernel/module.c
> index 1e9be5d..dcebf80 100644
> --- a/arch/arm/kernel/module.c
> +++ b/arch/arm/kernel/module.c
> @@ -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
> /*
> @@ -321,6 +322,9 @@ 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)
> + runtime_patch((void *)s->sh_addr, s->sh_size);
> s = find_mod_section(hdr, sechdrs, ".alt.smp.init");
> if (s && !is_smp())
> #ifdef CONFIG_SMP_ON_UP
> diff --git a/arch/arm/kernel/runtime-patch.c b/arch/arm/kernel/runtime-patch.c
> new file mode 100644
> index 0000000..fd37a2b
> --- /dev/null
> +++ b/arch/arm/kernel/runtime-patch.c
> @@ -0,0 +1,189 @@
> +/*
> + * 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>
> +#include <asm/cacheflush.h>
> +#include <asm/runtime-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);
> +}
> +
> +#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 */
> +
> + /* 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;
> + *ninsn = __opcode_to_mem_thumb32(*ninsn);
> +
> + 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);
> + 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;
> + *ninsn = __opcode_to_mem_arm(*ninsn);
> +
> + return 0;
> +}
> +
> +#endif /* CONFIG_THUMB2_KERNEL */
> +
> +static int apply_patch_imm8(const struct patch_info *p)
> +{
> + u32 *insn_ptr = p->insn;
> + int ret;
> +
> + if (p->insn_size != sizeof(u32) || p->data_size != 2 * sizeof(u32)) {
> + pr_err("patch: bad patch, insn size %d, data size %d\n",
> + p->insn_size, p->data_size);
> + return -EINVAL;
> + }
> +
> + ret = do_patch_imm8(p->data[1], *(u32 *)p->data[0], insn_ptr);
> + if (ret < 0)
> + return ret;
> +
> + flush_icache_insn(insn_ptr, sizeof(u32));
> +
> + return 0;
> +}
> +
> +int runtime_patch(const void *table, unsigned size)
> +{
> + const struct patch_info *p = table, *end = (table + size);
> +
> + for (p = table; p < end; p = patch_next(p)) {
> + int type = p->type & PATCH_TYPE_MASK;
> + int ret = -EINVAL;
> +
> + if (type == PATCH_IMM8)
> + ret = apply_patch_imm8(p);
> + if (ret < 0)
> + return ret;
> + }
> + 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));
> +}
> diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
> index a81dcec..669bbf0 100644
> --- a/arch/arm/kernel/setup.c
> +++ b/arch/arm/kernel/setup.c
> @@ -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();
> }
>
>
> diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S
> index 36ff15b..ea35ca0 100644
> --- a/arch/arm/kernel/vmlinux.lds.S
> +++ b/arch/arm/kernel/vmlinux.lds.S
> @@ -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
> --
> 1.7.9.5
>

2012-08-12 02:36:00

by Nicolas Pitre

[permalink] [raw]
Subject: Re: [PATCH v2 02/22] ARM: add self test for runtime patch mechanism

On Fri, 10 Aug 2012, Cyril Chemparathy wrote:

> 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.
>
> Signed-off-by: Cyril Chemparathy <[email protected]>
> ---
> arch/arm/Kconfig | 12 ++++++++++++
> arch/arm/kernel/runtime-patch.c | 41 +++++++++++++++++++++++++++++++++++++++
> 2 files changed, 53 insertions(+)
>
> diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> index d0a04ad..7e552dc 100644
> --- a/arch/arm/Kconfig
> +++ b/arch/arm/Kconfig
> @@ -211,6 +211,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
> diff --git a/arch/arm/kernel/runtime-patch.c b/arch/arm/kernel/runtime-patch.c
> index fd37a2b..c471d8c 100644
> --- a/arch/arm/kernel/runtime-patch.c
> +++ b/arch/arm/kernel/runtime-patch.c
> @@ -163,6 +163,44 @@ static int apply_patch_imm8(const struct patch_info *p)
> return 0;
> }
>
> +#ifdef CONFIG_ARM_RUNTIME_PATCH_TEST
> +static void __init __used __naked __patch_test_code_imm8(void)
> +{
> + __asm__ __volatile__ (
> + " .irp shift1, 0, 6, 12, 18\n"
> + " .irp shift2, 0, 1, 2, 3, 4, 5\n"
> + " add r1, r2, #(0x41 << (\\shift1 + \\shift2))\n"
> + " .endr\n"
> + " .endr\n"


Maybe adding a "add r1, r2 #0x81 << 24" here might be a good thing since
this is the most used case but missing from the above.

> + " .word 0\n"
> + : : :
> + );
> +}
> +
> +static void __init test_patch_imm8(void)
> +{
> + u32 test_code_addr = (u32)(&__patch_test_code_imm8);
> + u32 *test_code = (u32 *)(test_code_addr & ~0x3);

Why this masking? With Thumb2 you may find functions starting at
halfword aligned addresses. Only the LSB (indicating thumb mode) should
be masked out.

> + int i, ret;
> + u32 ninsn, insn;
> +
> + insn = test_code[0];
> + for (i = 0; test_code[i]; i++) {
> + ret = do_patch_imm8(insn, 0x41 << i, &ninsn);
> + if (ret < 0)
> + pr_err("runtime patch (imm8): failed at shift %d\n", i);
> + else if (ninsn != test_code[i])
> + pr_err("runtime patch (imm8): failed, need %x got %x\n",
> + test_code[i], ninsn);
> + }
> +}
> +
> +static void __init runtime_patch_test(void)
> +{
> + test_patch_imm8();
> +}
> +#endif
> +
> int runtime_patch(const void *table, unsigned size)
> {
> const struct patch_info *p = table, *end = (table + size);
> @@ -185,5 +223,8 @@ void __init runtime_patch_kernel(void)
> const void *start = &__runtime_patch_table_begin;
> const void *end = &__runtime_patch_table_end;
>
> +#ifdef CONFIG_ARM_RUNTIME_PATCH_TEST
> + runtime_patch_test();
> +#endif
> BUG_ON(runtime_patch(start, end - start));
> }
> --
> 1.7.9.5
>

2012-08-12 03:03:13

by Nicolas Pitre

[permalink] [raw]
Subject: Re: [PATCH v2 03/22] ARM: use late patch framework for phys-virt patching

On Fri, 10 Aug 2012, Cyril Chemparathy wrote:

> This patch replaces the original physical offset patching implementation
> with one that uses the newly added patching framework. In the process, we now
> unconditionally initialize the __pv_phys_offset and __pv_offset globals in the
> head.S code.

This last sentence is now wrong.

[...]
> diff --git a/arch/arm/include/asm/memory.h b/arch/arm/include/asm/memory.h
> index e965f1b..3d93779 100644
> --- a/arch/arm/include/asm/memory.h
> +++ b/arch/arm/include/asm/memory.h
> @@ -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)

What was wrong with the former PHYS_OFFSET = __pv_phys_offset ?

If you really want to have it optimized at run time, you could simply
use your new stub to patch a mov instruction instead of going through
__virt_to_phys which uses and add on top of a constant.

> 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
> diff --git a/arch/arm/kernel/armksyms.c b/arch/arm/kernel/armksyms.c
> index 60d3b73..6b388f8 100644
> --- a/arch/arm/kernel/armksyms.c
> +++ b/arch/arm/kernel/armksyms.c
> @@ -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
> diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S
> index 3db960e..69a3c09 100644
> --- a/arch/arm/kernel/head.S
> +++ b/arch/arm/kernel/head.S
> @@ -117,7 +117,7 @@ ENTRY(stext)
> bl __fixup_smp
> #endif
> #ifdef CONFIG_ARM_PATCH_PHYS_VIRT
> - bl __fixup_pv_table
> + bl __fixup_pv_offsets
> #endif
> bl __create_page_tables
>
> @@ -511,92 +511,29 @@ ENDPROC(fixup_smp)
>
> #ifdef CONFIG_ARM_PATCH_PHYS_VIRT
>
> -/* __fixup_pv_table - patch the stub instructions with the delta between
> - * PHYS_OFFSET and PAGE_OFFSET, which is assumed to be 16MiB aligned and
> - * can be expressed by an immediate shifter operand. The stub instruction
> - * has a form of '(add|sub) rd, rn, #imm'.
> +/*
> + * __fixup_pv_offsets - update __pv_offset and __pv_phys_offset based on the
> + * runtime location of the kernel.
> */
> __HEAD
> -__fixup_pv_table:
> +__fixup_pv_offsets:
> adr r0, 1f
> - ldmia r0, {r3-r5, r7}
> + ldmia r0, {r3-r6}
> sub r3, r0, r3 @ PHYS_OFFSET - PAGE_OFFSET
> - add r4, r4, r3 @ adjust table start address
> - add r5, r5, r3 @ adjust table end address
> - add r7, r7, r3 @ adjust __pv_phys_offset address
> - str r8, [r7] @ save computed PHYS_OFFSET to __pv_phys_offset
> - mov r6, r3, lsr #24 @ constant for add/sub instructions
> - teq r3, r6, lsl #24 @ must be 16MiB aligned
> -THUMB( it ne @ cross section branch )
> - bne __error
> - str r6, [r7, #4] @ save to __pv_offset
> - b __fixup_a_pv_table
> -ENDPROC(__fixup_pv_table)
> + add r4, r4, r3 @ virt_to_phys(__pv_phys_offset)
> + add r5, r5, r3 @ virt_to_phys(__pv_offset)
> + add r6, r6, r3 @ virt_to_phys(PAGE_OFFSET) = PHYS_OFFSET
> + str r6, [r4] @ save __pv_phys_offset
> + str r3, [r5] @ save __pv_offset
> + mov pc, lr
> +ENDPROC(__fixup_pv_offsets)
>
> .align
> 1: .long .
> - .long __pv_table_begin
> - .long __pv_table_end
> -2: .long __pv_phys_offset
> -
> - .text
> -__fixup_a_pv_table:
> -#ifdef CONFIG_THUMB2_KERNEL
> - lsls r6, #24
> - beq 2f
> - clz r7, r6
> - lsr r6, #24
> - lsl r6, r7
> - bic r6, #0x0080
> - lsrs r7, #1
> - orrcs r6, #0x0080
> - orr r6, r6, r7, lsl #12
> - orr r6, #0x4000
> - b 2f
> -1: add r7, r3
> - ldrh ip, [r7, #2]
> - and ip, 0x8f00
> - orr ip, r6 @ mask in offset bits 31-24
> - strh ip, [r7, #2]
> -2: cmp r4, r5
> - ldrcc r7, [r4], #4 @ use branch for delay slot
> - bcc 1b
> - bx lr
> -#else
> - b 2f
> -1: ldr ip, [r7, r3]
> - bic ip, ip, #0x000000ff
> - orr ip, ip, r6 @ mask in offset bits 31-24
> - str ip, [r7, r3]
> -2: cmp r4, r5
> - ldrcc r7, [r4], #4 @ use branch for delay slot
> - bcc 1b
> - mov pc, lr
> + .long __pv_phys_offset
> + .long __pv_offset
> + .long PAGE_OFFSET
> #endif
> -ENDPROC(__fixup_a_pv_table)
> -
> -ENTRY(fixup_pv_table)
> - stmfd sp!, {r4 - r7, lr}
> - ldr r2, 2f @ get address of __pv_phys_offset
> - mov r3, #0 @ no offset
> - mov r4, r0 @ r0 = table start
> - add r5, r0, r1 @ r1 = table size
> - ldr r6, [r2, #4] @ get __pv_offset
> - bl __fixup_a_pv_table
> - ldmfd sp!, {r4 - r7, pc}
> -ENDPROC(fixup_pv_table)
>
> - .align
> -2: .long __pv_phys_offset
> -
> - .data
> - .globl __pv_phys_offset
> - .type __pv_phys_offset, %object
> -__pv_phys_offset:
> - .long 0
> - .size __pv_phys_offset, . - __pv_phys_offset
> -__pv_offset:
> - .long 0
> -#endif
>
> #include "head-common.S"
> diff --git a/arch/arm/kernel/module.c b/arch/arm/kernel/module.c
> index dcebf80..ac13dd5 100644
> --- a/arch/arm/kernel/module.c
> +++ b/arch/arm/kernel/module.c
> @@ -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)
> runtime_patch((void *)s->sh_addr, s->sh_size);

I missed this in the previous patch, but could you fail the module
loading by returning an error if runtime_patch() fails? That would take
care of not accepting modules that might have been compiled with future
runtime patching extensions that are not yet supported in an earlier
kernel.

You also should remove the MODULE_ARCH_VERMAGIC_P2V definitions now that
the corresponding code is no longer there.


Nicolas

2012-08-12 03:04:55

by Nicolas Pitre

[permalink] [raw]
Subject: Re: [PATCH v2 04/22] ARM: LPAE: use phys_addr_t on virt <--> phys conversion

On Fri, 10 Aug 2012, Cyril Chemparathy wrote:

> This patch fixes up the types used when converting back and forth between
> physical and virtual addresses.
>
> Signed-off-by: Vitaly Andrianov <[email protected]>
> Signed-off-by: Cyril Chemparathy <[email protected]>

Reviewed-by: Nicolas Pitre <[email protected]>

> ---
> arch/arm/include/asm/memory.h | 26 ++++++++++++++++++--------
> 1 file changed, 18 insertions(+), 8 deletions(-)
>
> diff --git a/arch/arm/include/asm/memory.h b/arch/arm/include/asm/memory.h
> index 3d93779..81e1714 100644
> --- a/arch/arm/include/asm/memory.h
> +++ b/arch/arm/include/asm/memory.h
> @@ -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
>
> @@ -207,14 +217,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)
>
> /*
> --
> 1.7.9.5
>

2012-08-12 03:39:12

by Nicolas Pitre

[permalink] [raw]
Subject: Re: [PATCH v2 05/22] ARM: LPAE: support 64-bit virt_to_phys patching

On Fri, 10 Aug 2012, Cyril Chemparathy wrote:

> 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.
>
> Signed-off-by: Cyril Chemparathy <[email protected]>
> ---
> arch/arm/include/asm/memory.h | 22 ++++++++++++++++++----
> arch/arm/kernel/head.S | 4 ++++
> arch/arm/kernel/setup.c | 2 +-
> 3 files changed, 23 insertions(+), 5 deletions(-)
>
> diff --git a/arch/arm/include/asm/memory.h b/arch/arm/include/asm/memory.h
> index 81e1714..dc5fbf3 100644
> --- a/arch/arm/include/asm/memory.h
> +++ b/arch/arm/include/asm/memory.h
> @@ -154,14 +154,28 @@
> #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;
> - early_patch_imm8("add", t, x, __pv_offset, 0);
> - return t;
> + unsigned long tlo, thi;
> +
> + early_patch_imm8("add", tlo, x, __pv_offset, 0);
> +
> +#ifdef CONFIG_ARM_LPAE
> + /*
> + * On LPAE, we do not _need_ to do 64-bit arithmetic because the high
> + * order 32 bits are never changed by the phys-virt offset. We simply
> + * patch in the high order physical address bits instead.
> + */
> +#ifdef __ARMEB__
> + early_patch_imm8_mov("mov", thi, __pv_phys_offset, 0);
> +#else
> + early_patch_imm8_mov("mov", thi, __pv_phys_offset, 4);
> +#endif
> +#endif
> + return (u64)tlo | (u64)thi << 32;
> }

Hmmm... I'm afraid this is going to be suboptimal when LPAE is not
selected.

First of all, you do not need to cast tlo to a u64 in the return value.

Then, I'm not sure if the compiler is smart enough to see that the
returned value is a phys_addr_t which can be a u32, and in this case the
(u64)thi << 32 is going to be truncated right away, and therefore there
is no point in emiting the corresponding instructions.

Furthermore, if LPAE is not defined, then thi never gets initialized and
should produce a warning. Did you test compilation of the code with LPAE
turned off?

I'd prefer something like this where more stuff is validated by the
compiler:

static inline phys_addr_t __virt_to_phys(unsigned long x)
{
unsigned long tlo, thi;
phys_addr_t ret;

early_patch_imm8("add", tlo, x, __pv_offset, 0);
ret = tlo;

if (sizeof(phys_addr_t) > 4) {
#ifdef __ARMEB__
early_patch_imm8_mov("mov", thi, __pv_phys_offset, 0);
#else
early_patch_imm8_mov("mov", thi, __pv_phys_offset, 4);
#endif
ret |= ((u64)thi) << 32;
}

return ret);
}

This should let the compiler optimize things whether LPAE is enabledor
not while validating both cases.


Nicolas

2012-08-12 03:58:00

by Nicolas Pitre

[permalink] [raw]
Subject: Re: [PATCH v2 06/22] ARM: LPAE: use signed arithmetic for mask definitions

On Fri, 10 Aug 2012, Cyril Chemparathy wrote:

> 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.
>
> Signed-off-by: Cyril Chemparathy <[email protected]>
> Signed-off-by: Vitaly Andrianov <[email protected]>

Reviewed-by: Nicolas Pitre <[email protected]>

> ---
> arch/arm/include/asm/page.h | 2 +-
> arch/arm/include/asm/pgtable-3level.h | 6 +++---
> 2 files changed, 4 insertions(+), 4 deletions(-)
>
> diff --git a/arch/arm/include/asm/page.h b/arch/arm/include/asm/page.h
> index ecf9019..1e0fe08 100644
> --- a/arch/arm/include/asm/page.h
> +++ b/arch/arm/include/asm/page.h
> @@ -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__
>
> diff --git a/arch/arm/include/asm/pgtable-3level.h b/arch/arm/include/asm/pgtable-3level.h
> index b249035..ae39d11 100644
> --- a/arch/arm/include/asm/pgtable-3level.h
> +++ b/arch/arm/include/asm/pgtable-3level.h
> @@ -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)
>
> --
> 1.7.9.5
>

2012-08-12 03:58:46

by Nicolas Pitre

[permalink] [raw]
Subject: Re: [PATCH v2 09/22] ARM: LPAE: use phys_addr_t for initrd location and size

On Fri, 10 Aug 2012, Cyril Chemparathy wrote:

> 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.
>
> Signed-off-by: Vitaly Andrianov <[email protected]>
> Signed-off-by: Cyril Chemparathy <[email protected]>

Acked-by: Nicolas Pitre <[email protected]>

> ---
> arch/arm/mm/init.c | 13 +++++++------
> 1 file changed, 7 insertions(+), 6 deletions(-)
>
> diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c
> index 19ba70b..bae9d05 100644
> --- a/arch/arm/mm/init.c
> +++ b/arch/arm/mm/init.c
> @@ -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) {
> --
> 1.7.9.5
>

2012-08-12 04:05:06

by Nicolas Pitre

[permalink] [raw]
Subject: Re: [PATCH v2 10/22] ARM: LPAE: use phys_addr_t in switch_mm()

On Fri, 10 Aug 2012, Cyril Chemparathy wrote:

> 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.
>
> Signed-off-by: Cyril Chemparathy <[email protected]>
> Signed-off-by: Vitaly Andrianov <[email protected]>
> ---
> arch/arm/include/asm/proc-fns.h | 4 ++--
> arch/arm/mm/proc-v7-3level.S | 26 ++++++++++++++++++++++----
> 2 files changed, 24 insertions(+), 6 deletions(-)
>
> diff --git a/arch/arm/include/asm/proc-fns.h b/arch/arm/include/asm/proc-fns.h
> index f3628fb..75b5f14 100644
> --- a/arch/arm/include/asm/proc-fns.h
> +++ b/arch/arm/include/asm/proc-fns.h
> @@ -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
> diff --git a/arch/arm/mm/proc-v7-3level.S b/arch/arm/mm/proc-v7-3level.S
> index 8de0f1d..78bd88c 100644
> --- a/arch/arm/mm/proc-v7-3level.S
> +++ b/arch/arm/mm/proc-v7-3level.S
> @@ -39,6 +39,22 @@
> #define TTB_FLAGS_SMP (TTB_IRGN_WBWA|TTB_S|TTB_RGN_OC_WBWA)
> #define PMD_FLAGS_SMP (PMD_SECT_WBWA|PMD_SECT_S)
>
> +#define rzero r3
> +#ifndef CONFIG_ARM_LPAE
> +# define rpgdl r0
> +# define rpgdh rzero
> +# define rmm r1
> +#else
> +# define rmm r2
> +#ifndef __ARMEB__
> +# define rpgdl r0
> +# define rpgdh r1
> +#else
> +# define rpgdl r1
> +# define rpgdh r0
> +#endif
> +#endif

Given proc-v7-3level.S is used only when CONFIG_ARM_LPAE is defined, you
shouldn't need all the above.

> /*
> * cpu_v7_switch_mm(pgd_phys, tsk)
> *
> @@ -47,10 +63,12 @@
> */
> 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
> + mov rzero, #0
> + ldr rmm, [rmm, #MM_CONTEXT_ID] @ get mm->context.id
> + and rmm, rmm, #0xff
> + mov rmm, rmm, lsl #(48 - 32) @ ASID
> + orr rpgdh, rpgdh, rmm @ upper 32-bits of pgd phys
> + mcrr p15, 0, rpgdl, rpgdh, c2 @ set TTB 0
> isb
> #endif
> mov pc, lr
> --
> 1.7.9.5
>

2012-08-12 04:11:38

by Nicolas Pitre

[permalink] [raw]
Subject: Re: [PATCH v2 11/22] ARM: LPAE: use 64-bit accessors for TTBR registers

On Fri, 10 Aug 2012, Cyril Chemparathy wrote:

> 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.
>
> Signed-off-by: Cyril Chemparathy <[email protected]>
> Signed-off-by: Vitaly Andrianov <[email protected]>
> ---
> arch/arm/include/asm/proc-fns.h | 24 +++++++++++++++++++-----
> arch/arm/mm/context.c | 9 ++-------
> 2 files changed, 21 insertions(+), 12 deletions(-)
>
> diff --git a/arch/arm/include/asm/proc-fns.h b/arch/arm/include/asm/proc-fns.h
> index 75b5f14..24224df 100644
> --- a/arch/arm/include/asm/proc-fns.h
> +++ b/arch/arm/include/asm/proc-fns.h
> @@ -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) \
> + : : "cc"); \
> + ttbr; \
> + })

I don't think you have to clobber the condition code (cc) here. It has
been a long while since the ability to pass a condition code inside an
inline assembly statement was removed from gcc.

Other that that...

Acked-by: Nicolas Pitre <[email protected]>


Nicolas

2012-08-12 04:19:18

by Nicolas Pitre

[permalink] [raw]
Subject: Re: [PATCH v2 13/22] ARM: LPAE: factor out T1SZ and TTBR1 computations

On Fri, 10 Aug 2012, Cyril Chemparathy wrote:

> 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.
>
> Signed-off-by: Cyril Chemparathy <[email protected]>
> Signed-off-by: Vitaly Andrianov <[email protected]>

Acked-by: Nicolas Pitre <[email protected]>


> ---
> arch/arm/include/asm/pgtable-3level-hwdef.h | 10 ++++++++++
> arch/arm/mm/proc-v7-3level.S | 16 ++++------------
> 2 files changed, 14 insertions(+), 12 deletions(-)
>
> diff --git a/arch/arm/include/asm/pgtable-3level-hwdef.h b/arch/arm/include/asm/pgtable-3level-hwdef.h
> index d795282..b501650 100644
> --- a/arch/arm/include/asm/pgtable-3level-hwdef.h
> +++ b/arch/arm/include/asm/pgtable-3level-hwdef.h
> @@ -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
> diff --git a/arch/arm/mm/proc-v7-3level.S b/arch/arm/mm/proc-v7-3level.S
> index 78bd88c..e28383f 100644
> --- a/arch/arm/mm/proc-v7-3level.S
> +++ b/arch/arm/mm/proc-v7-3level.S
> @@ -137,18 +137,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
> --
> 1.7.9.5
>

2012-08-12 04:29:58

by Nicolas Pitre

[permalink] [raw]
Subject: Re: [PATCH v2 15/22] ARM: mm: use physical addresses in highmem sanity checks

On Fri, 10 Aug 2012, Cyril Chemparathy wrote:

> 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).
>
> Signed-off-by: Cyril Chemparathy <[email protected]>
> Signed-off-by: Vitaly Andrianov <[email protected]>

Acked-by: Nicolas Pitre <[email protected]>


> ---
> arch/arm/mm/mmu.c | 22 ++++++++++------------
> 1 file changed, 10 insertions(+), 12 deletions(-)
>
> diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c
> index 53eeeb8..f764c03 100644
> --- a/arch/arm/mm/mmu.c
> +++ b/arch/arm/mm/mmu.c
> @@ -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) > vmalloc_min ||
> - __va(bank->start + bank->size) < __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,
> --
> 1.7.9.5
>

2012-08-12 04:36:25

by Nicolas Pitre

[permalink] [raw]
Subject: Re: [PATCH v2 16/22] ARM: mm: cleanup checks for membank overlap with vmalloc area

On Fri, 10 Aug 2012, Cyril Chemparathy wrote:

> 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.

Are you sure of this? The code that you removed not only checks for
banks that fall into the vmalloc area, but it also skipp them. This is
now lost.

> Signed-off-by: Cyril Chemparathy <[email protected]>
> Signed-off-by: Vitaly Andrianov <[email protected]>
> ---
> arch/arm/mm/mmu.c | 19 +------------------
> 1 file changed, 1 insertion(+), 18 deletions(-)
>
> diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c
> index f764c03..3d685c6 100644
> --- a/arch/arm/mm/mmu.c
> +++ b/arch/arm/mm/mmu.c
> @@ -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.
> */
> --
> 1.7.9.5
>

2012-08-12 16:33:09

by Cyril Chemparathy

[permalink] [raw]
Subject: Re: [PATCH v2 02/22] ARM: add self test for runtime patch mechanism

On 08/11/12 22:35, Nicolas Pitre wrote:
> On Fri, 10 Aug 2012, Cyril Chemparathy wrote:
>
>> 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.
>>
>> Signed-off-by: Cyril Chemparathy <[email protected]>
>> ---
[...]
>> + __asm__ __volatile__ (
>> + " .irp shift1, 0, 6, 12, 18\n"
>> + " .irp shift2, 0, 1, 2, 3, 4, 5\n"
>> + " add r1, r2, #(0x41 << (\\shift1 + \\shift2))\n"
>> + " .endr\n"
>> + " .endr\n"
>
>
> Maybe adding a "add r1, r2 #0x81 << 24" here might be a good thing since
> this is the most used case but missing from the above.
>

Indeed. I've now replaced this with something a bit more extensive.
Does the following look better?

+struct patch_test_imm8 {
+ u16 imm;
+ u16 shift;
+ u32 insn;
+};
+
+static void __init __used __naked __patch_test_code_imm8(void)
+{
+ __asm__ __volatile__ (
+
+ /* a single test case */
+ " .macro test_one, imm, sft\n"
+ " .hword \\imm\n"
+ " .hword \\sft\n"
+ " add r1, r2, #(\\imm << \\sft)\n"
+ " .endm\n"
+
+ /* a sequence of tests at 'inc' increments of shift */
+ " .macro test_seq, imm, sft, max, inc\n"
+ " test_one \\imm, \\sft\n"
+ " .if \\sft < \\max\n"
+ " test_seq \\imm, (\\sft + \\inc), \\max,
\\inc\n"
+ " .endif\n"
+ " .endm\n"
+
+ /* an empty record to mark the end */
+ " .macro test_end\n"
+ " .hword 0, 0\n"
+ " .word 0\n"
+ " .endm\n"
+
+ /* finally generate the test sequences */
+ " test_seq 0x41, 0, 24, 1\n"
+ " test_seq 0x81, 0, 24, 2\n"
+ " test_end\n"
+ : : :
+ );
+}
+

[...]
>> + u32 test_code_addr = (u32)(&__patch_test_code_imm8);
>> + u32 *test_code = (u32 *)(test_code_addr & ~0x3);
>
> Why this masking? With Thumb2 you may find functions starting at
> halfword aligned addresses. Only the LSB (indicating thumb mode) should
> be masked out.
>

Fixed. Thanks.

Thanks
-- Cyril.

2012-08-12 17:34:25

by Cyril Chemparathy

[permalink] [raw]
Subject: Re: [PATCH v2 03/22] ARM: use late patch framework for phys-virt patching

On 08/11/12 23:03, Nicolas Pitre wrote:
> On Fri, 10 Aug 2012, Cyril Chemparathy wrote:
>
>> This patch replaces the original physical offset patching implementation
>> with one that uses the newly added patching framework. In the process, we now
>> unconditionally initialize the __pv_phys_offset and __pv_offset globals in the
>> head.S code.
>
> This last sentence is now wrong.
>

Removed.

[...]
>> -extern unsigned long __pv_phys_offset;
>> -#define PHYS_OFFSET __pv_phys_offset
[...]
>> +#define PHYS_OFFSET __virt_to_phys(PAGE_OFFSET)
>
> What was wrong with the former PHYS_OFFSET = __pv_phys_offset ?
>
> If you really want to have it optimized at run time, you could simply
> use your new stub to patch a mov instruction instead of going through
> __virt_to_phys which uses and add on top of a constant.
>

The intent was to optimize out the load(s) on references to PHYS_OFFSET,
but is it worth it? If so, we could go with a patched mov (or two) with
the necessary endian fixups. If not, we could revert to
__pv_phys_offset loads as before.

[...]
>> s = find_mod_section(hdr, sechdrs, ".runtime.patch.table");
>> if (s)
>> runtime_patch((void *)s->sh_addr, s->sh_size);
>
> I missed this in the previous patch, but could you fail the module
> loading by returning an error if runtime_patch() fails? That would take
> care of not accepting modules that might have been compiled with future
> runtime patching extensions that are not yet supported in an earlier
> kernel.
>

Sure. Thanks.

> You also should remove the MODULE_ARCH_VERMAGIC_P2V definitions now that
> the corresponding code is no longer there.
>

Hmm...

"rt-patch" needs to be in vermagic to prevent modules built against the
new code from being loaded on older kernels that used the traditional
patch code.

"p2v" needs to be in there as well, because it should be possible to
build without PATCH_PHYS_VIRT, but with RUNTIME_PATCH as and when there
are other users for this.

Thanks
-- Cyril.

2012-08-12 18:14:15

by Cyril Chemparathy

[permalink] [raw]
Subject: Re: [PATCH v2 01/22] ARM: add mechanism for late code patching

On 08/11/12 22:22, Nicolas Pitre wrote:
> On Fri, 10 Aug 2012, Cyril Chemparathy wrote:
>
>> 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.
>>
>> Signed-off-by: Cyril Chemparathy <[email protected]>
>
> Reviewed-by: Nicolas Pitre <[email protected]>
>

Thanks.

I've been looking at the compiler emitted code, and had to make a couple
of changes to keep things streamlined...

[...]
>> +#define early_patch_imm8(insn, to, from, sym, offset) \
>> + early_patch_stub(PATCH_IMM8, \
>> + /* code */ \
>> + "ldr %0, =" __stringify(sym + offset) "\n" \
>> + "ldr %0, [%0]\n" \
>> + insn " %0, %1, %0\n", \
>> + /* patch_data */ \
>> + ".long " __stringify(sym + offset) "\n" \
>> + insn " %0, %1, %2\n", \
>> + : "=&r" (to) \
>> + : "r" (from), "I" (__IMM8), "m" (sym) \
>> + : "cc")

First, the "m" operand modifier for "sym" forces GCC to emit code to
load the address of the symbol into a register. I've replaced this with
"i" (&(sym) to make that go away. With this, the emitted code doesn't
contain any such unexpected nonsense.

Second, marking the "to" operand as early clobber makes the compiler
generate horrid register moves around the assembly block, even when it
has registers to spare. Simply adding a temporary variable does a much
much better job, especially since this temporary register is used only
in the patched-out "early" code.

Thanks
-- Cyril.

2012-08-12 23:28:11

by Cyril Chemparathy

[permalink] [raw]
Subject: Re: [PATCH v2 05/22] ARM: LPAE: support 64-bit virt_to_phys patching

On 08/11/12 23:39, Nicolas Pitre wrote:
> On Fri, 10 Aug 2012, Cyril Chemparathy wrote:
>
>> 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.
>>
>> Signed-off-by: Cyril Chemparathy <[email protected]>
>> ---
>> arch/arm/include/asm/memory.h | 22 ++++++++++++++++++----
>> arch/arm/kernel/head.S | 4 ++++
>> arch/arm/kernel/setup.c | 2 +-
>> 3 files changed, 23 insertions(+), 5 deletions(-)
>>
>> diff --git a/arch/arm/include/asm/memory.h b/arch/arm/include/asm/memory.h
>> index 81e1714..dc5fbf3 100644
>> --- a/arch/arm/include/asm/memory.h
>> +++ b/arch/arm/include/asm/memory.h
>> @@ -154,14 +154,28 @@
>> #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;
>> - early_patch_imm8("add", t, x, __pv_offset, 0);
>> - return t;
>> + unsigned long tlo, thi;
>> +
>> + early_patch_imm8("add", tlo, x, __pv_offset, 0);
>> +
>> +#ifdef CONFIG_ARM_LPAE
>> + /*
>> + * On LPAE, we do not _need_ to do 64-bit arithmetic because the high
>> + * order 32 bits are never changed by the phys-virt offset. We simply
>> + * patch in the high order physical address bits instead.
>> + */
>> +#ifdef __ARMEB__
>> + early_patch_imm8_mov("mov", thi, __pv_phys_offset, 0);
>> +#else
>> + early_patch_imm8_mov("mov", thi, __pv_phys_offset, 4);
>> +#endif
>> +#endif
>> + return (u64)tlo | (u64)thi << 32;
>> }
>
> Hmmm... I'm afraid this is going to be suboptimal when LPAE is not
> selected.
>

I understand your concern, but I don't see the sub-optimality. I tested
the following function with GCC versions 4.3.3 and 4.7. This is after
the other changes that I mentioned in my previous email, but with the
__virt_to_phys() code itself unchanged:

phys_addr_t ____test_virt_to_phys(unsigned long addr)
{
return __virt_to_phys(addr);
}

The resulting code in both cases looks like:

<____test_virt_to_phys>:
b c04f0528
bx lr

[the branch of course gets patched to an add]

> First of all, you do not need to cast tlo to a u64 in the return value.
>

True enough.

> Then, I'm not sure if the compiler is smart enough to see that the
> returned value is a phys_addr_t which can be a u32, and in this case the
> (u64)thi << 32 is going to be truncated right away, and therefore there
> is no point in emiting the corresponding instructions.
>

In this case, it appears to be smart enough. However, I agree that
relying on compiler smarts is probably not the best thing for us to do.

> Furthermore, if LPAE is not defined, then thi never gets initialized and
> should produce a warning. Did you test compilation of the code with LPAE
> turned off?
>

Sure. One of our test platforms is non-LPAE. The compiler does not
produce warnings on this, and this is consistent across both compiler
versions.

> I'd prefer something like this where more stuff is validated by the
> compiler:
>
> static inline phys_addr_t __virt_to_phys(unsigned long x)
> {
> unsigned long tlo, thi;
> phys_addr_t ret;
>
> early_patch_imm8("add", tlo, x, __pv_offset, 0);
> ret = tlo;
>
> if (sizeof(phys_addr_t) > 4) {
> #ifdef __ARMEB__
> early_patch_imm8_mov("mov", thi, __pv_phys_offset, 0);
> #else
> early_patch_imm8_mov("mov", thi, __pv_phys_offset, 4);
> #endif
> ret |= ((u64)thi) << 32;
> }
>
> return ret);
> }
>
> This should let the compiler optimize things whether LPAE is enabledor
> not while validating both cases.
>

Agreed on the principal, but more below...


I've meanwhile been chasing down another problem - the code generated
for the LPAE case. The original code resulted in the following:

<____test_virt_to_phys>:
mov r2, #0
b c01bc800 # patch: add r1, r0, __pv_offset
b c01bc810 # patch: mov r0, __phys_offset_high
orr r2, r2, r1
mov r3, r0
mov r1, r3
mov r0, r2
bx lr

Yikes! This code does a bunch of futile register shuffling and a
pointless or, all in the name of generating the result in a 64-bit
register-pair from the 32-bit halves.

In order to get past this, I tried adding operand qualifiers (R = upper
32-bits, Q = lower 32-bits) in the patch macros, in the hope that
treating these as native 64-bit register pairs would eliminate the need
to shuffle them around after the inline assembly blocks. This then
allows us to implement __virt_to_phys() as follows:

static inline phys_addr_t __virt_to_phys(unsigned long x)
{
phys_addr_t t;

if (sizeof(t) == 4) {
t = x;
early_patch_imm8("add", t, "", t, __pv_offset, 0);
return t;
}

/*
* On LPAE, we do not _need_ to do 64-bit arithmetic because
* the high order 32 bits are never changed by the phys-virt
* offset. We simply patch in the high order physical address
* bits instead.
*
* Note: the mov _must_ be first here. From the compiler's
* perspective, this is the initializer for the variable. The
* mov itself initializes only the upper half. The subsequent
* add treats t as a read/write operand and initializes the
* lower half.
*/
#ifdef __ARMEB__
early_patch_imm8_mov("mov", t, "R", __pv_phys_offset, 0);
#else
early_patch_imm8_mov("mov", t, "R", __pv_phys_offset, 4);
#endif
early_patch_imm8("add", t, "Q", x, __pv_offset, 0);

return t;
}

With this, we get significantly better looking generated code:

<____test_virt_to_phys>:
b c01d519c # patch: mov r3, __phys_offset_high
b c01d51ac # patch: add r2, r0, __phys_offset_high
mov r0, r2
mov r1, r3
bx lr

This is about as far along as I've been able to proceed. I still
haven't figured out a way to get it to patch in place without an extra
register pair.

Overall, this is still a bit too kludgy for my liking. In particular,
the read/write operand forces add/sub/... users to initialize the result
variable. I am currently leaning towards adding native support for
64-bit operations in the runtime patch code, instead of having to hack
around it with 32-bit primitives. Better ideas, any one?

Thanks
-- Cyril.

2012-08-13 03:19:52

by Nicolas Pitre

[permalink] [raw]
Subject: Re: [PATCH v2 02/22] ARM: add self test for runtime patch mechanism

On Sun, 12 Aug 2012, Cyril Chemparathy wrote:

> On 08/11/12 22:35, Nicolas Pitre wrote:
> > On Fri, 10 Aug 2012, Cyril Chemparathy wrote:
> >
> > > 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.
> > >
> > > Signed-off-by: Cyril Chemparathy <[email protected]>
> > > ---
> [...]
> > > + __asm__ __volatile__ (
> > > + " .irp shift1, 0, 6, 12, 18\n"
> > > + " .irp shift2, 0, 1, 2, 3, 4, 5\n"
> > > + " add r1, r2, #(0x41 << (\\shift1 + \\shift2))\n"
> > > + " .endr\n"
> > > + " .endr\n"
> >
> >
> > Maybe adding a "add r1, r2 #0x81 << 24" here might be a good thing since
> > this is the most used case but missing from the above.
> >
>
> Indeed. I've now replaced this with something a bit more extensive. Does the
> following look better?
>
> +struct patch_test_imm8 {
> + u16 imm;
> + u16 shift;
> + u32 insn;
> +};
> +
> +static void __init __used __naked __patch_test_code_imm8(void)
> +{
> + __asm__ __volatile__ (
> +
> + /* a single test case */
> + " .macro test_one, imm, sft\n"
> + " .hword \\imm\n"
> + " .hword \\sft\n"
> + " add r1, r2, #(\\imm << \\sft)\n"
> + " .endm\n"
> +
> + /* a sequence of tests at 'inc' increments of shift */
> + " .macro test_seq, imm, sft, max, inc\n"
> + " test_one \\imm, \\sft\n"
> + " .if \\sft < \\max\n"
> + " test_seq \\imm, (\\sft + \\inc), \\max,
> \\inc\n"
> + " .endif\n"
> + " .endm\n"
> +
> + /* an empty record to mark the end */
> + " .macro test_end\n"
> + " .hword 0, 0\n"
> + " .word 0\n"
> + " .endm\n"
> +
> + /* finally generate the test sequences */
> + " test_seq 0x41, 0, 24, 1\n"
> + " test_seq 0x81, 0, 24, 2\n"
> + " test_end\n"
> + : : :
> + );
> +}

Yes, this is certainly quite extensive. :-)


Nicolas

2012-08-13 03:32:48

by Nicolas Pitre

[permalink] [raw]
Subject: Re: [PATCH v2 03/22] ARM: use late patch framework for phys-virt patching

On Sun, 12 Aug 2012, Cyril Chemparathy wrote:

> On 08/11/12 23:03, Nicolas Pitre wrote:
> > On Fri, 10 Aug 2012, Cyril Chemparathy wrote:
> >
> > > -extern unsigned long __pv_phys_offset;
> > > -#define PHYS_OFFSET __pv_phys_offset
> [...]
> > > +#define PHYS_OFFSET __virt_to_phys(PAGE_OFFSET)
> >
> > What was wrong with the former PHYS_OFFSET = __pv_phys_offset ?
> >
> > If you really want to have it optimized at run time, you could simply
> > use your new stub to patch a mov instruction instead of going through
> > __virt_to_phys which uses and add on top of a constant.
> >
>
> The intent was to optimize out the load(s) on references to PHYS_OFFSET, but
> is it worth it? If so, we could go with a patched mov (or two) with the
> necessary endian fixups. If not, we could revert to __pv_phys_offset loads as
> before.

If you want to do better than the load, then you'd better go all the way
with the patched move.

> > You also should remove the MODULE_ARCH_VERMAGIC_P2V definitions now that
> > the corresponding code is no longer there.
> >
>
> Hmm...
>
> "rt-patch" needs to be in vermagic to prevent modules built against the new
> code from being loaded on older kernels that used the traditional patch code.

Right.

> "p2v" needs to be in there as well, because it should be possible to build
> without PATCH_PHYS_VIRT, but with RUNTIME_PATCH as and when there are other
> users for this.

That doesn't matter if there are other users. As soon as there is a
.init.runtime_patch_table that needs to be processed then "rt-patch"
flags it. Whether this is used for PATCH_PHYS_VIRT or other purposes is
irrelevant.

However there isn't any pv_table anymore, so "p2v" should go as there is
no more code to process those tables if they're ever encountered.


Nicolas

2012-08-13 04:03:51

by Nicolas Pitre

[permalink] [raw]
Subject: Re: [PATCH v2 05/22] ARM: LPAE: support 64-bit virt_to_phys patching

On Sun, 12 Aug 2012, Cyril Chemparathy wrote:

> On 08/11/12 23:39, Nicolas Pitre wrote:
> > On Fri, 10 Aug 2012, Cyril Chemparathy wrote:
> >
> > > 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.
> > >
> > > Signed-off-by: Cyril Chemparathy <[email protected]>
> > > ---
> > > arch/arm/include/asm/memory.h | 22 ++++++++++++++++++----
> > > arch/arm/kernel/head.S | 4 ++++
> > > arch/arm/kernel/setup.c | 2 +-
> > > 3 files changed, 23 insertions(+), 5 deletions(-)
> > >
> > > diff --git a/arch/arm/include/asm/memory.h b/arch/arm/include/asm/memory.h
> > > index 81e1714..dc5fbf3 100644
> > > --- a/arch/arm/include/asm/memory.h
> > > +++ b/arch/arm/include/asm/memory.h
> > > @@ -154,14 +154,28 @@
> > > #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;
> > > - early_patch_imm8("add", t, x, __pv_offset, 0);
> > > - return t;
> > > + unsigned long tlo, thi;
> > > +
> > > + early_patch_imm8("add", tlo, x, __pv_offset, 0);
> > > +
> > > +#ifdef CONFIG_ARM_LPAE
> > > + /*
> > > + * On LPAE, we do not _need_ to do 64-bit arithmetic because the high
> > > + * order 32 bits are never changed by the phys-virt offset. We simply
> > > + * patch in the high order physical address bits instead.
> > > + */
> > > +#ifdef __ARMEB__
> > > + early_patch_imm8_mov("mov", thi, __pv_phys_offset, 0);
> > > +#else
> > > + early_patch_imm8_mov("mov", thi, __pv_phys_offset, 4);
> > > +#endif
> > > +#endif
> > > + return (u64)tlo | (u64)thi << 32;
> > > }
> >
> > Hmmm... I'm afraid this is going to be suboptimal when LPAE is not
> > selected.
> >
>
> I understand your concern, but I don't see the sub-optimality. I tested the
> following function with GCC versions 4.3.3 and 4.7. This is after the other
> changes that I mentioned in my previous email, but with the __virt_to_phys()
> code itself unchanged:
>
> phys_addr_t ____test_virt_to_phys(unsigned long addr)
> {
> return __virt_to_phys(addr);
> }
>
> The resulting code in both cases looks like:
>
> <____test_virt_to_phys>:
> b c04f0528
> bx lr
>
> [the branch of course gets patched to an add]

OK. Please add such information and results in your commit log.

> > First of all, you do not need to cast tlo to a u64 in the return value.
> >
>
> True enough.
>
> > Then, I'm not sure if the compiler is smart enough to see that the
> > returned value is a phys_addr_t which can be a u32, and in this case the
> > (u64)thi << 32 is going to be truncated right away, and therefore there
> > is no point in emiting the corresponding instructions.
> >
>
> In this case, it appears to be smart enough. However, I agree that relying on
> compiler smarts is probably not the best thing for us to do.

Well, we do rely on a lot of compiler smarts in the kernel. As long as
sufficiently old compiler versions do get it right then it should be
fine.

> > Furthermore, if LPAE is not defined, then thi never gets initialized and
> > should produce a warning. Did you test compilation of the code with LPAE
> > turned off?
> >
>
> Sure. One of our test platforms is non-LPAE. The compiler does not produce
> warnings on this, and this is consistent across both compiler versions.

That is odd. I still prefer my version nevertheless so to avoid this
potential issue in the future.

> > I'd prefer something like this where more stuff is validated by the
> > compiler:
> >
> > static inline phys_addr_t __virt_to_phys(unsigned long x)
> > {
> > unsigned long tlo, thi;
> > phys_addr_t ret;
> >
> > early_patch_imm8("add", tlo, x, __pv_offset, 0);
> > ret = tlo;
> >
> > if (sizeof(phys_addr_t) > 4) {
> > #ifdef __ARMEB__
> > early_patch_imm8_mov("mov", thi, __pv_phys_offset, 0);
> > #else
> > early_patch_imm8_mov("mov", thi, __pv_phys_offset, 4);
> > #endif
> > ret |= ((u64)thi) << 32;
> > }
> >
> > return ret);
> > }
> >
> > This should let the compiler optimize things whether LPAE is enabledor
> > not while validating both cases.
> >
>
> Agreed on the principal, but more below...
>
>
> I've meanwhile been chasing down another problem - the code generated for the
> LPAE case. The original code resulted in the following:
>
> <____test_virt_to_phys>:
> mov r2, #0
> b c01bc800 # patch: add r1, r0, __pv_offset
> b c01bc810 # patch: mov r0, __phys_offset_high
> orr r2, r2, r1
> mov r3, r0
> mov r1, r3
> mov r0, r2
> bx lr
>
> Yikes! This code does a bunch of futile register shuffling and a pointless or,
> all in the name of generating the result in a 64-bit register-pair from the
> 32-bit halves.
>
> In order to get past this, I tried adding operand qualifiers (R = upper
> 32-bits, Q = lower 32-bits) in the patch macros, in the hope that treating
> these as native 64-bit register pairs would eliminate the need to shuffle them
> around after the inline assembly blocks. This then allows us to implement
> __virt_to_phys() as follows:
>
> static inline phys_addr_t __virt_to_phys(unsigned long x)
> {
> phys_addr_t t;
>
> if (sizeof(t) == 4) {
> t = x;
> early_patch_imm8("add", t, "", t, __pv_offset, 0);
> return t;
> }
>
> /*
> * On LPAE, we do not _need_ to do 64-bit arithmetic because
> * the high order 32 bits are never changed by the phys-virt
> * offset. We simply patch in the high order physical address
> * bits instead.
> *
> * Note: the mov _must_ be first here. From the compiler's
> * perspective, this is the initializer for the variable. The
> * mov itself initializes only the upper half. The subsequent
> * add treats t as a read/write operand and initializes the
> * lower half.
> */
> #ifdef __ARMEB__
> early_patch_imm8_mov("mov", t, "R", __pv_phys_offset, 0);
> #else
> early_patch_imm8_mov("mov", t, "R", __pv_phys_offset, 4);
> #endif
> early_patch_imm8("add", t, "Q", x, __pv_offset, 0);
>
> return t;
> }
>
> With this, we get significantly better looking generated code:
>
> <____test_virt_to_phys>:
> b c01d519c # patch: mov r3, __phys_offset_high
> b c01d51ac # patch: add r2, r0, __phys_offset_high
> mov r0, r2
> mov r1, r3
> bx lr
>
> This is about as far along as I've been able to proceed. I still haven't
> figured out a way to get it to patch in place without an extra register pair.
>
> Overall, this is still a bit too kludgy for my liking. In particular, the
> read/write operand forces add/sub/... users to initialize the result variable.
> I am currently leaning towards adding native support for 64-bit operations in
> the runtime patch code, instead of having to hack around it with 32-bit
> primitives. Better ideas, any one?

The best I've come up with is this:

//typedef unsigned long phys_addr_t;
typedef unsigned long long phys_addr_t;

phys_addr_t __virt_to_phys(unsigned long v)
{
phys_addr_t p;

if (sizeof(phys_addr_t) > 4) {
asm ("add %Q0, %1, #PV_OFFSET" : "=r" (p) : "r" (v));
asm ("mov %R0, #PHYS_HIGHBITS" : "+r" (p));
} else {
asm ("add %0, %1, #PV_OFFSET" : "=r" (p) : "r" (v));
}

return p;
}

The add is done first as this reduce the life span of the v variable.
Because p is listed as an early clobber, then in theory the register
holding v could be used to store the low part of p right away. If you
put the mov first then you can't do that especially on big endian.

It seems that there is no clean way to combine the LPAE and non LPAE
cases.


Nicolas