From faa7bc51c11d5bbe440ac04710fd7a3208782000 Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Sat, 30 May 2009 14:00:14 +0100 Subject: Check whether the TLB operations need broadcasting on SMP systems ARMv7 SMP hardware can handle the TLB maintenance operations broadcasting in hardware so that the software can avoid the costly IPIs. This patch adds the necessary checks (the MMFR3 CPUID register) to avoid the broadcasting if already supported by the hardware. (this patch is based on the work done by Tony Thompson @ ARM) Signed-off-by: Catalin Marinas --- arch/arm/kernel/smp.c | 69 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 26 deletions(-) (limited to 'arch/arm/kernel') diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index 6014dfd22af4..ece658e773a6 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -32,6 +32,7 @@ #include #include #include +#include /* * as from 2.5, kernels no longer have an init_tasks structure @@ -545,6 +546,12 @@ struct tlb_args { unsigned long ta_end; }; +/* all SMP configurations have the extended CPUID registers */ +static inline int tlb_ops_need_broadcast(void) +{ + return ((read_cpuid_ext(CPUID_EXT_MMFR3) >> 12) & 0xf) < 2; +} + static inline void ipi_flush_tlb_all(void *ignored) { local_flush_tlb_all(); @@ -587,51 +594,61 @@ static inline void ipi_flush_tlb_kernel_range(void *arg) void flush_tlb_all(void) { - on_each_cpu(ipi_flush_tlb_all, NULL, 1); + if (tlb_ops_need_broadcast()) + on_each_cpu(ipi_flush_tlb_all, NULL, 1); + else + local_flush_tlb_all(); } void flush_tlb_mm(struct mm_struct *mm) { - on_each_cpu_mask(ipi_flush_tlb_mm, mm, 1, &mm->cpu_vm_mask); + if (tlb_ops_need_broadcast()) + on_each_cpu_mask(ipi_flush_tlb_mm, mm, 1, &mm->cpu_vm_mask); + else + local_flush_tlb_mm(mm); } void flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr) { - struct tlb_args ta; - - ta.ta_vma = vma; - ta.ta_start = uaddr; - - on_each_cpu_mask(ipi_flush_tlb_page, &ta, 1, &vma->vm_mm->cpu_vm_mask); + if (tlb_ops_need_broadcast()) { + struct tlb_args ta; + ta.ta_vma = vma; + ta.ta_start = uaddr; + on_each_cpu_mask(ipi_flush_tlb_page, &ta, 1, &vma->vm_mm->cpu_vm_mask); + } else + local_flush_tlb_page(vma, uaddr); } void flush_tlb_kernel_page(unsigned long kaddr) { - struct tlb_args ta; - - ta.ta_start = kaddr; - - on_each_cpu(ipi_flush_tlb_kernel_page, &ta, 1); + if (tlb_ops_need_broadcast()) { + struct tlb_args ta; + ta.ta_start = kaddr; + on_each_cpu(ipi_flush_tlb_kernel_page, &ta, 1); + } else + local_flush_tlb_kernel_page(kaddr); } void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end) { - struct tlb_args ta; - - ta.ta_vma = vma; - ta.ta_start = start; - ta.ta_end = end; - - on_each_cpu_mask(ipi_flush_tlb_range, &ta, 1, &vma->vm_mm->cpu_vm_mask); + if (tlb_ops_need_broadcast()) { + struct tlb_args ta; + ta.ta_vma = vma; + ta.ta_start = start; + ta.ta_end = end; + on_each_cpu_mask(ipi_flush_tlb_range, &ta, 1, &vma->vm_mm->cpu_vm_mask); + } else + local_flush_tlb_range(vma, start, end); } void flush_tlb_kernel_range(unsigned long start, unsigned long end) { - struct tlb_args ta; - - ta.ta_start = start; - ta.ta_end = end; - - on_each_cpu(ipi_flush_tlb_kernel_range, &ta, 1); + if (tlb_ops_need_broadcast()) { + struct tlb_args ta; + ta.ta_start = start; + ta.ta_end = end; + on_each_cpu(ipi_flush_tlb_kernel_range, &ta, 1); + } else + local_flush_tlb_kernel_range(start, end); } -- cgit v1.2.3 From d71e1352e240dea32d481ad8d662e8de4406ac7e Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Sat, 30 May 2009 14:00:15 +0100 Subject: Clear the IT state when invoking a Thumb-2 signal handler If a process is interrupted during an If-Then block and a signal is invoked, the ITSTATE bits must be cleared otherwise the handler would not run correctly. Signed-off-by: Catalin Marinas Cc: Joseph S. Myers --- arch/arm/include/asm/ptrace.h | 7 +++++++ arch/arm/kernel/signal.c | 8 ++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) (limited to 'arch/arm/kernel') diff --git a/arch/arm/include/asm/ptrace.h b/arch/arm/include/asm/ptrace.h index 236a06b9b7ce..4a4290f7b4a2 100644 --- a/arch/arm/include/asm/ptrace.h +++ b/arch/arm/include/asm/ptrace.h @@ -65,6 +65,13 @@ #define PSR_x 0x0000ff00 /* Extension */ #define PSR_c 0x000000ff /* Control */ +/* + * ARMv7 groups of APSR bits + */ +#define PSR_ISET_MASK 0x01000010 /* ISA state (J, T) mask */ +#define PSR_IT_MASK 0x0600fc00 /* If-Then execution state mask */ +#define PSR_ENDIAN_MASK 0x00000200 /* Endianness state mask */ + #ifndef __ASSEMBLY__ /* diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index 80b8b5c7e07a..442b87476f97 100644 --- a/arch/arm/kernel/signal.c +++ b/arch/arm/kernel/signal.c @@ -426,9 +426,13 @@ setup_return(struct pt_regs *regs, struct k_sigaction *ka, */ thumb = handler & 1; - if (thumb) + if (thumb) { cpsr |= PSR_T_BIT; - else +#if __LINUX_ARM_ARCH__ >= 7 + /* clear the If-Then Thumb-2 execution state */ + cpsr &= ~PSR_IT_MASK; +#endif + } else cpsr &= ~PSR_T_BIT; } #endif -- cgit v1.2.3 From 8c7e65742fd05f27071282d260376adb98e0e9ac Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Sat, 30 May 2009 14:00:17 +0100 Subject: arm: Provide _sdata and __bss_stop in the vmlinux.lds.S file _sdata and __bss_stop are common symbols defined by many architectures and made available to the kernel via asm-generic/sections.h. Kmemleak uses these symbols when scanning the data sections. Signed-off-by: Catalin Marinas --- arch/arm/kernel/vmlinux.lds.S | 2 ++ 1 file changed, 2 insertions(+) (limited to 'arch/arm/kernel') diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S index c90f27250ead..6c0779792546 100644 --- a/arch/arm/kernel/vmlinux.lds.S +++ b/arch/arm/kernel/vmlinux.lds.S @@ -141,6 +141,7 @@ SECTIONS .data : AT(__data_loc) { _data = .; /* address in memory */ + _sdata = .; /* * first, the init task union, aligned @@ -192,6 +193,7 @@ SECTIONS __bss_start = .; /* BSS */ *(.bss) *(COMMON) + __bss_stop = .; _end = .; } /* Stabs debugging sections. */ -- cgit v1.2.3 From 26584853a44c58f3d6ac7360d697a2ddcd1a3efa Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Sat, 30 May 2009 14:00:18 +0100 Subject: Add core support for ARMv6/v7 big-endian Starting with ARMv6, the CPUs support the BE-8 variant of big-endian (byte-invariant). This patch adds the core support: - setting of the BE-8 mode via the CPSR.E register for both kernel and user threads - big-endian page table walking - REV used to rotate instructions read from memory during fault processing as they are still little-endian format - Kconfig and Makefile support for BE-8. The --be8 option must be passed to the final linking stage to convert the instructions to little-endian Signed-off-by: Catalin Marinas --- arch/arm/Makefile | 3 +++ arch/arm/boot/compressed/Makefile | 5 ++++- arch/arm/boot/compressed/head.S | 6 ++++++ arch/arm/include/asm/processor.h | 1 + arch/arm/include/asm/ptrace.h | 10 ++++++++++ arch/arm/kernel/entry-armv.S | 3 +++ arch/arm/kernel/entry-common.S | 3 +++ arch/arm/kernel/process.c | 2 +- arch/arm/mm/Kconfig | 14 ++++++++++++++ arch/arm/mm/abort-ev6.S | 3 +++ arch/arm/mm/proc-v6.S | 3 +++ arch/arm/mm/proc-v7.S | 3 +++ 12 files changed, 54 insertions(+), 2 deletions(-) (limited to 'arch/arm/kernel') diff --git a/arch/arm/Makefile b/arch/arm/Makefile index e84729bf13d4..1167a4e8c7a8 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -11,6 +11,9 @@ # Copyright (C) 1995-2001 by Russell King LDFLAGS_vmlinux :=-p --no-undefined -X +ifeq ($(CONFIG_CPU_ENDIAN_BE8),y) +LDFLAGS_vmlinux += --be8 +endif CPPFLAGS_vmlinux.lds = -DTEXT_OFFSET=$(TEXT_OFFSET) OBJCOPYFLAGS :=-O binary -R .note -R .note.gnu.build-id -R .comment -S GZFLAGS :=-9 diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile index fbe5eef1f6c9..ce39dc540085 100644 --- a/arch/arm/boot/compressed/Makefile +++ b/arch/arm/boot/compressed/Makefile @@ -40,7 +40,7 @@ ifeq ($(CONFIG_PXA_SHARPSL),y) OBJS += head-sharpsl.o endif -ifeq ($(CONFIG_CPU_BIG_ENDIAN),y) +ifeq ($(CONFIG_CPU_ENDIAN_BE32),y) ifeq ($(CONFIG_CPU_CP15),y) OBJS += big-endian.o else @@ -78,6 +78,9 @@ EXTRA_AFLAGS := -Wa,-march=all # linker symbols. We only define initrd_phys and params_phys if the # machine class defined the corresponding makefile variable. LDFLAGS_vmlinux := --defsym zreladdr=$(ZRELADDR) +ifeq ($(CONFIG_CPU_ENDIAN_BE8),y) +LDFLAGS_vmlinux += --be8 +endif ifneq ($(INITRD_PHYS),) LDFLAGS_vmlinux += --defsym initrd_phys=$(INITRD_PHYS) endif diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S index b371fba1b954..01d49be3b2ca 100644 --- a/arch/arm/boot/compressed/head.S +++ b/arch/arm/boot/compressed/head.S @@ -438,6 +438,9 @@ __armv4_mmu_cache_on: mrc p15, 0, r0, c1, c0, 0 @ read control reg orr r0, r0, #0x5000 @ I-cache enable, RR cache replacement orr r0, r0, #0x0030 +#ifdef CONFIG_CPU_ENDIAN_BE8 + orr r0, r0, #1 << 25 @ big-endian page tables +#endif bl __common_mmu_cache_on mov r0, #0 mcr p15, 0, r0, c8, c7, 0 @ flush I,D TLBs @@ -455,6 +458,9 @@ __armv7_mmu_cache_on: mrc p15, 0, r0, c1, c0, 0 @ read control reg orr r0, r0, #0x5000 @ I-cache enable, RR cache replacement orr r0, r0, #0x003c @ write buffer +#ifdef CONFIG_CPU_ENDIAN_BE8 + orr r0, r0, #1 << 25 @ big-endian page tables +#endif orrne r0, r0, #1 @ MMU enabled movne r1, #-1 mcrne p15, 0, r3, c2, c0, 0 @ load page table pointer diff --git a/arch/arm/include/asm/processor.h b/arch/arm/include/asm/processor.h index 1845892260e7..6a89567ffc5b 100644 --- a/arch/arm/include/asm/processor.h +++ b/arch/arm/include/asm/processor.h @@ -71,6 +71,7 @@ struct thread_struct { regs->ARM_cpsr = USR26_MODE; \ if (elf_hwcap & HWCAP_THUMB && pc & 1) \ regs->ARM_cpsr |= PSR_T_BIT; \ + regs->ARM_cpsr |= PSR_ENDSTATE; \ regs->ARM_pc = pc & ~1; /* pc */ \ regs->ARM_sp = sp; /* sp */ \ regs->ARM_r2 = stack[2]; /* r2 (envp) */ \ diff --git a/arch/arm/include/asm/ptrace.h b/arch/arm/include/asm/ptrace.h index 4a4290f7b4a2..67b833c9b6b9 100644 --- a/arch/arm/include/asm/ptrace.h +++ b/arch/arm/include/asm/ptrace.h @@ -50,6 +50,7 @@ #define PSR_F_BIT 0x00000040 #define PSR_I_BIT 0x00000080 #define PSR_A_BIT 0x00000100 +#define PSR_E_BIT 0x00000200 #define PSR_J_BIT 0x01000000 #define PSR_Q_BIT 0x08000000 #define PSR_V_BIT 0x10000000 @@ -72,6 +73,15 @@ #define PSR_IT_MASK 0x0600fc00 /* If-Then execution state mask */ #define PSR_ENDIAN_MASK 0x00000200 /* Endianness state mask */ +/* + * Default endianness state + */ +#ifdef CONFIG_CPU_ENDIAN_BE8 +#define PSR_ENDSTATE PSR_E_BIT +#else +#define PSR_ENDSTATE 0 +#endif + #ifndef __ASSEMBLY__ /* diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S index d662a2f1fd85..9d7ce4377c5e 100644 --- a/arch/arm/kernel/entry-armv.S +++ b/arch/arm/kernel/entry-armv.S @@ -482,6 +482,9 @@ __und_usr: subeq r4, r2, #4 @ ARM instr at LR - 4 subne r4, r2, #2 @ Thumb instr at LR - 2 1: ldreqt r0, [r4] +#ifdef CONFIG_CPU_ENDIAN_BE8 + reveq r0, r0 @ little endian instruction +#endif beq call_fpe @ Thumb instruction #if __LINUX_ARM_ARCH__ >= 7 diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S index b55cb0331809..366e5097a41a 100644 --- a/arch/arm/kernel/entry-common.S +++ b/arch/arm/kernel/entry-common.S @@ -210,6 +210,9 @@ ENTRY(vector_swi) A710( teq ip, #0x0f000000 ) A710( bne .Larm710bug ) #endif +#ifdef CONFIG_CPU_ENDIAN_BE8 + rev r10, r10 @ little endian instruction +#endif #elif defined(CONFIG_AEABI) diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index c3265a2e7cd4..1585423699ee 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -365,7 +365,7 @@ pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) regs.ARM_r2 = (unsigned long)fn; regs.ARM_r3 = (unsigned long)do_exit; regs.ARM_pc = (unsigned long)kernel_thread_helper; - regs.ARM_cpsr = SVC_MODE; + regs.ARM_cpsr = SVC_MODE | PSR_ENDSTATE; return do_fork(flags|CLONE_VM|CLONE_UNTRACED, 0, ®s, 0, NULL, NULL); } diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig index 76bbcde049b0..a25f34bd99da 100644 --- a/arch/arm/mm/Kconfig +++ b/arch/arm/mm/Kconfig @@ -639,6 +639,20 @@ config CPU_BIG_ENDIAN port must properly enable any big-endian related features of your chipset/board/processor. +config CPU_ENDIAN_BE8 + bool + depends on CPU_BIG_ENDIAN + default CPU_V6 || CPU_V7 + help + Support for the BE-8 (big-endian) mode on ARMv6 and ARMv7 processors. + +config CPU_ENDIAN_BE32 + bool + depends on CPU_BIG_ENDIAN + default !CPU_ENDIAN_BE8 + help + Support for the BE-32 (big-endian) mode on pre-ARMv6 processors. + config CPU_HIGH_VECTOR depends on !MMU && CPU_CP15 && !CPU_ARM740T bool "Select the High exception vector" diff --git a/arch/arm/mm/abort-ev6.S b/arch/arm/mm/abort-ev6.S index 6f7e70907e44..f332df7f0d37 100644 --- a/arch/arm/mm/abort-ev6.S +++ b/arch/arm/mm/abort-ev6.S @@ -37,6 +37,9 @@ ENTRY(v6_early_abort) movne pc, lr do_thumb_abort ldreq r3, [r2] @ read aborted ARM instruction +#ifdef CONFIG_CPU_ENDIAN_BE8 + reveq r3, r3 +#endif do_ldrd_abort tst r3, #1 << 20 @ L = 0 -> write orreq r1, r1, #1 << 11 @ yes. diff --git a/arch/arm/mm/proc-v6.S b/arch/arm/mm/proc-v6.S index 087e239704df..524ddae92595 100644 --- a/arch/arm/mm/proc-v6.S +++ b/arch/arm/mm/proc-v6.S @@ -170,6 +170,9 @@ __v6_setup: #endif /* CONFIG_MMU */ adr r5, v6_crval ldmia r5, {r5, r6} +#ifdef CONFIG_CPU_ENDIAN_BE8 + orr r6, r6, #1 << 25 @ big-endian page tables +#endif mrc p15, 0, r0, c1, c0, 0 @ read control register bic r0, r0, r5 @ clear bits them orr r0, r0, r6 @ set them diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S index 0a8ffd3c03fd..4f8486475a79 100644 --- a/arch/arm/mm/proc-v7.S +++ b/arch/arm/mm/proc-v7.S @@ -253,6 +253,9 @@ __v7_setup: mcr p15, 0, r6, c10, c2, 1 @ write NMRR adr r5, v7_crval ldmia r5, {r5, r6} +#ifdef CONFIG_CPU_ENDIAN_BE8 + orr r6, r6, #1 << 25 @ big-endian page tables +#endif mrc p15, 0, r0, c1, c0, 0 @ read control register bic r0, r0, r5 @ clear bits them orr r0, r0, r6 @ set them -- cgit v1.2.3