aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm')
-rw-r--r--arch/arm/Kconfig20
-rw-r--r--arch/arm/kernel/entry-armv.S14
-rw-r--r--arch/arm/kernel/head.S3
-rw-r--r--arch/arm/kernel/process.c25
-rw-r--r--arch/arm/kernel/sys_arm.c2
-rw-r--r--arch/arm/kernel/traps.c14
-rw-r--r--arch/arm/kernel/vmlinux.lds.S3
-rw-r--r--arch/arm/mach-clps711x/Kconfig3
-rw-r--r--arch/arm/mach-footbridge/Kconfig12
-rw-r--r--arch/arm/mach-imx/Kconfig1
-rw-r--r--arch/arm/mach-s3c2410/clock.c2
-rw-r--r--arch/arm/mach-s3c2410/s3c2440.c6
-rw-r--r--arch/arm/mm/Kconfig23
-rw-r--r--arch/arm/mm/copypage-v4mc.S80
-rw-r--r--arch/arm/mm/copypage-v4mc.c111
-rw-r--r--arch/arm/mm/copypage-v6.c28
-rw-r--r--arch/arm/mm/flush.c37
-rw-r--r--arch/arm/mm/mm-armv.c27
18 files changed, 249 insertions, 162 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 8bfcb37460f..bf397a9f8ac 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -85,6 +85,7 @@ choice
config ARCH_CLPS7500
bool "Cirrus-CL-PS7500FE"
select TIMER_ACORN
+ select ISA
config ARCH_CLPS711X
bool "CLPS711x/EP721x-based"
@@ -96,6 +97,7 @@ config ARCH_CO285
config ARCH_EBSA110
bool "EBSA-110"
+ select ISA
help
This is an evaluation board for the StrongARM processor available
from Digital. It has limited hardware on-board, including an onboard
@@ -120,13 +122,16 @@ config ARCH_INTEGRATOR
config ARCH_IOP3XX
bool "IOP3xx-based"
+ select PCI
config ARCH_IXP4XX
bool "IXP4xx-based"
select DMABOUNCE
+ select PCI
config ARCH_IXP2000
bool "IXP2400/2800-based"
+ select PCI
config ARCH_L7200
bool "LinkUp-L7200"
@@ -155,6 +160,8 @@ config ARCH_RPC
config ARCH_SA1100
bool "SA1100-based"
+ select ISA
+ select DISCONTIGMEM
config ARCH_S3C2410
bool "Samsung S3C2410"
@@ -165,6 +172,9 @@ config ARCH_S3C2410
config ARCH_SHARK
bool "Shark"
+ select ISA
+ select ISA_DMA
+ select PCI
config ARCH_LH7A40X
bool "Sharp LH7A40X"
@@ -252,8 +262,6 @@ config ARM_AMBA
config ISA
bool
- depends on FOOTBRIDGE_HOST || ARCH_SHARK || ARCH_CLPS7500 || ARCH_EBSA110 || ARCH_CDB89712 || ARCH_EDB7211 || ARCH_SA1100 || ARCH_MX1ADS
- default y
help
Find out whether you have ISA slots on your motherboard. ISA is the
name of a bus system, i.e. the way the CPU talks to the other stuff
@@ -263,8 +271,6 @@ config ISA
config ISA_DMA
bool
- depends on FOOTBRIDGE_HOST || ARCH_SHARK
- default y
config ISA_DMA_API
bool
@@ -272,7 +278,6 @@ config ISA_DMA_API
config PCI
bool "PCI support" if ARCH_INTEGRATOR_AP
- default y if ARCH_SHARK || FOOTBRIDGE_HOST || ARCH_IOP3XX || ARCH_IXP4XX || ARCH_IXP2000
help
Find out whether you have a PCI motherboard. PCI is the name of a
bus system, i.e. the way the CPU talks to the other stuff inside
@@ -300,7 +305,7 @@ menu "Kernel Features"
config SMP
bool "Symmetric Multi-Processing (EXPERIMENTAL)"
- depends on EXPERIMENTAL && n
+ depends on EXPERIMENTAL #&& n
help
This enables support for systems with more than one CPU. If you have
a system with only one CPU, like most personal computers, say N. If
@@ -340,8 +345,7 @@ config PREEMPT
config DISCONTIGMEM
bool
- depends on ARCH_EDB7211 || ARCH_SA1100 || (ARCH_LH7A40X && !LH7A40X_CONTIGMEM)
- default y
+ default (ARCH_LH7A40X && !LH7A40X_CONTIGMEM)
help
Say Y to support efficient handling of discontiguous physical memory,
for architectures which are either NUMA (Non-Uniform Memory Access)
diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S
index 080df907f24..4eb36155dc9 100644
--- a/arch/arm/kernel/entry-armv.S
+++ b/arch/arm/kernel/entry-armv.S
@@ -17,8 +17,8 @@
#include <asm/glue.h>
#include <asm/vfpmacros.h>
-#include <asm/hardware.h> @ should be moved into entry-macro.S
-#include <asm/arch/irqs.h> @ should be moved into entry-macro.S
+#include <asm/hardware.h> /* should be moved into entry-macro.S */
+#include <asm/arch/irqs.h> /* should be moved into entry-macro.S */
#include <asm/arch/entry-macro.S>
#include "entry-header.S"
@@ -505,9 +505,9 @@ ENTRY(__switch_to)
mra r4, r5, acc0
stmia ip, {r4, r5}
#endif
-#ifdef CONFIG_HAS_TLS_REG
+#if defined(CONFIG_HAS_TLS_REG)
mcr p15, 0, r3, c13, c0, 3 @ set TLS register
-#else
+#elif !defined(CONFIG_TLS_REG_EMUL)
mov r4, #0xffff0fff
str r3, [r4, #-15] @ TLS val at 0xffff0ff0
#endif
@@ -690,11 +690,7 @@ __kuser_cmpxchg: @ 0xffff0fc0
__kuser_get_tls: @ 0xffff0fe0
-#ifndef CONFIG_HAS_TLS_REG
-
-#ifdef CONFIG_SMP /* sanity check */
-#error "CONFIG_SMP without CONFIG_HAS_TLS_REG is wrong"
-#endif
+#if !defined(CONFIG_HAS_TLS_REG) && !defined(CONFIG_TLS_REG_EMUL)
ldr r0, [pc, #(16 - 8)] @ TLS stored at 0xffff0ff0
mov pc, lr
diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S
index 171b3e811c7..4733877296d 100644
--- a/arch/arm/kernel/head.S
+++ b/arch/arm/kernel/head.S
@@ -19,6 +19,7 @@
#include <asm/procinfo.h>
#include <asm/ptrace.h>
#include <asm/constants.h>
+#include <asm/thread_info.h>
#include <asm/system.h>
#define PROCINFO_MMUFLAGS 8
@@ -131,7 +132,7 @@ __switch_data:
.long processor_id @ r4
.long __machine_arch_type @ r5
.long cr_alignment @ r6
- .long init_thread_union+8192 @ sp
+ .long init_thread_union + THREAD_START_SP @ sp
/*
* The following fragment of code is executed with the MMU on, and uses
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c
index 26eacd3e5de..8f146a4b475 100644
--- a/arch/arm/kernel/process.c
+++ b/arch/arm/kernel/process.c
@@ -256,8 +256,6 @@ static unsigned long *thread_info_head;
static unsigned int nr_thread_info;
#define EXTRA_TASK_STRUCT 4
-#define ll_alloc_task_struct() ((struct thread_info *) __get_free_pages(GFP_KERNEL,1))
-#define ll_free_task_struct(p) free_pages((unsigned long)(p),1)
struct thread_info *alloc_thread_info(struct task_struct *task)
{
@@ -274,17 +272,16 @@ struct thread_info *alloc_thread_info(struct task_struct *task)
}
if (!thread)
- thread = ll_alloc_task_struct();
+ thread = (struct thread_info *)
+ __get_free_pages(GFP_KERNEL, THREAD_SIZE_ORDER);
-#ifdef CONFIG_MAGIC_SYSRQ
+#ifdef CONFIG_DEBUG_STACK_USAGE
/*
* The stack must be cleared if you want SYSRQ-T to
* give sensible stack usage information
*/
- if (thread) {
- char *p = (char *)thread;
- memzero(p+KERNEL_STACK_SIZE, KERNEL_STACK_SIZE);
- }
+ if (thread)
+ memzero(thread, THREAD_SIZE);
#endif
return thread;
}
@@ -297,7 +294,7 @@ void free_thread_info(struct thread_info *thread)
thread_info_head = p;
nr_thread_info += 1;
} else
- ll_free_task_struct(thread);
+ free_pages((unsigned long)thread, THREAD_SIZE_ORDER);
}
/*
@@ -350,7 +347,7 @@ copy_thread(int nr, unsigned long clone_flags, unsigned long stack_start,
struct thread_info *thread = p->thread_info;
struct pt_regs *childregs;
- childregs = ((struct pt_regs *)((unsigned long)thread + THREAD_SIZE - 8)) - 1;
+ childregs = ((struct pt_regs *)((unsigned long)thread + THREAD_START_SP)) - 1;
*childregs = *regs;
childregs->ARM_r0 = 0;
childregs->ARM_sp = stack_start;
@@ -447,15 +444,17 @@ EXPORT_SYMBOL(kernel_thread);
unsigned long get_wchan(struct task_struct *p)
{
unsigned long fp, lr;
- unsigned long stack_page;
+ unsigned long stack_start, stack_end;
int count = 0;
if (!p || p == current || p->state == TASK_RUNNING)
return 0;
- stack_page = 4096 + (unsigned long)p->thread_info;
+ stack_start = (unsigned long)(p->thread_info + 1);
+ stack_end = ((unsigned long)p->thread_info) + THREAD_SIZE;
+
fp = thread_saved_fp(p);
do {
- if (fp < stack_page || fp > 4092+stack_page)
+ if (fp < stack_start || fp > stack_end)
return 0;
lr = pc_pointer (((unsigned long *)fp)[-1]);
if (!in_sched_functions(lr))
diff --git a/arch/arm/kernel/sys_arm.c b/arch/arm/kernel/sys_arm.c
index ef32577da30..f897ce2ccf0 100644
--- a/arch/arm/kernel/sys_arm.c
+++ b/arch/arm/kernel/sys_arm.c
@@ -302,7 +302,7 @@ long execve(const char *filename, char **argv, char **envp)
"b ret_to_user"
:
: "r" (current_thread_info()),
- "Ir" (THREAD_SIZE - 8 - sizeof(regs)),
+ "Ir" (THREAD_START_SP - sizeof(regs)),
"r" (&regs),
"Ir" (sizeof(regs))
: "r0", "r1", "r2", "r3", "ip", "memory");
diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c
index 3a001fe5540..14df16b983f 100644
--- a/arch/arm/kernel/traps.c
+++ b/arch/arm/kernel/traps.c
@@ -218,7 +218,8 @@ NORET_TYPE void die(const char *str, struct pt_regs *regs, int err)
tsk->comm, tsk->pid, tsk->thread_info + 1);
if (!user_mode(regs) || in_interrupt()) {
- dump_mem("Stack: ", regs->ARM_sp, 8192+(unsigned long)tsk->thread_info);
+ dump_mem("Stack: ", regs->ARM_sp,
+ THREAD_SIZE + (unsigned long)tsk->thread_info);
dump_backtrace(regs, tsk);
dump_instr(regs);
}
@@ -450,9 +451,9 @@ asmlinkage int arm_syscall(int no, struct pt_regs *regs)
case NR(set_tls):
thread->tp_value = regs->ARM_r0;
-#ifdef CONFIG_HAS_TLS_REG
+#if defined(CONFIG_HAS_TLS_REG)
asm ("mcr p15, 0, %0, c13, c0, 3" : : "r" (regs->ARM_r0) );
-#else
+#elif !defined(CONFIG_TLS_REG_EMUL)
/*
* User space must never try to access this directly.
* Expect your app to break eventually if you do so.
@@ -497,11 +498,14 @@ asmlinkage int arm_syscall(int no, struct pt_regs *regs)
return 0;
}
-#if defined(CONFIG_CPU_32v6) && !defined(CONFIG_HAS_TLS_REG)
+#ifdef CONFIG_TLS_REG_EMUL
/*
* We might be running on an ARMv6+ processor which should have the TLS
- * register, but for some reason we can't use it and have to emulate it.
+ * register but for some reason we can't use it, or maybe an SMP system
+ * using a pre-ARMv6 processor (there are apparently a few prototypes like
+ * that in existence) and therefore access to that register must be
+ * emulated.
*/
static int get_tp_trap(struct pt_regs *regs, unsigned int instr)
diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S
index a39c6a42d68..ad2d66c93a5 100644
--- a/arch/arm/kernel/vmlinux.lds.S
+++ b/arch/arm/kernel/vmlinux.lds.S
@@ -5,6 +5,7 @@
#include <asm-generic/vmlinux.lds.h>
#include <linux/config.h>
+#include <asm/thread_info.h>
OUTPUT_ARCH(arm)
ENTRY(stext)
@@ -103,7 +104,7 @@ SECTIONS
__data_loc = ALIGN(4); /* location in binary */
. = DATAADDR;
#else
- . = ALIGN(8192);
+ . = ALIGN(THREAD_SIZE);
__data_loc = .;
#endif
diff --git a/arch/arm/mach-clps711x/Kconfig b/arch/arm/mach-clps711x/Kconfig
index f6e676322ca..45c930ccd06 100644
--- a/arch/arm/mach-clps711x/Kconfig
+++ b/arch/arm/mach-clps711x/Kconfig
@@ -10,6 +10,7 @@ config ARCH_AUTCPU12
config ARCH_CDB89712
bool "CDB89712"
+ select ISA
help
This is an evaluation board from Cirrus for the CS89712 processor.
The board includes 2 serial ports, Ethernet, IRDA, and expansion
@@ -26,6 +27,8 @@ config ARCH_CLEP7312
config ARCH_EDB7211
bool "EDB7211"
+ select ISA
+ select DISCONTIGMEM
help
Say Y here if you intend to run this kernel on a Cirrus Logic EDB-7211
evaluation board.
diff --git a/arch/arm/mach-footbridge/Kconfig b/arch/arm/mach-footbridge/Kconfig
index 1090c680b6d..324d9edeec3 100644
--- a/arch/arm/mach-footbridge/Kconfig
+++ b/arch/arm/mach-footbridge/Kconfig
@@ -5,6 +5,9 @@ menu "Footbridge Implementations"
config ARCH_CATS
bool "CATS"
select FOOTBRIDGE_HOST
+ select ISA
+ select ISA_DMA
+ select PCI
help
Say Y here if you intend to run this kernel on the CATS.
@@ -13,6 +16,9 @@ config ARCH_CATS
config ARCH_PERSONAL_SERVER
bool "Compaq Personal Server"
select FOOTBRIDGE_HOST
+ select ISA
+ select ISA_DMA
+ select PCI
---help---
Say Y here if you intend to run this kernel on the Compaq
Personal Server.
@@ -42,6 +48,9 @@ config ARCH_EBSA285_HOST
bool "EBSA285 (host mode)"
select ARCH_EBSA285
select FOOTBRIDGE_HOST
+ select ISA
+ select ISA_DMA
+ select PCI
help
Say Y here if you intend to run this kernel on the EBSA285 card
in host ("central function") mode.
@@ -51,6 +60,9 @@ config ARCH_EBSA285_HOST
config ARCH_NETWINDER
bool "NetWinder"
select FOOTBRIDGE_HOST
+ select ISA
+ select ISA_DMA
+ select PCI
help
Say Y here if you intend to run this kernel on the Rebel.COM
NetWinder. Information about this machine can be found at:
diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig
index ec85813ee5d..cddd194ac6e 100644
--- a/arch/arm/mach-imx/Kconfig
+++ b/arch/arm/mach-imx/Kconfig
@@ -4,6 +4,7 @@ menu "IMX Implementations"
config ARCH_MX1ADS
bool "mx1ads"
depends on ARCH_IMX
+ select ISA
help
Say Y here if you are using the Motorola MX1ADS board
diff --git a/arch/arm/mach-s3c2410/clock.c b/arch/arm/mach-s3c2410/clock.c
index e23f534d4e1..8d986b8401c 100644
--- a/arch/arm/mach-s3c2410/clock.c
+++ b/arch/arm/mach-s3c2410/clock.c
@@ -478,7 +478,7 @@ static int s3c2440_clk_add(struct sys_device *sysdev)
{
unsigned long upllcon = __raw_readl(S3C2410_UPLLCON);
- s3c2440_clk_upll.rate = s3c2410_get_pll(upllcon, clk_xtal.rate) * 2;
+ s3c2440_clk_upll.rate = s3c2410_get_pll(upllcon, clk_xtal.rate);
printk("S3C2440: Clock Support, UPLL %ld.%03ld MHz\n",
print_mhz(s3c2440_clk_upll.rate));
diff --git a/arch/arm/mach-s3c2410/s3c2440.c b/arch/arm/mach-s3c2410/s3c2440.c
index 9a8cc5ae225..d4c8281b55f 100644
--- a/arch/arm/mach-s3c2410/s3c2440.c
+++ b/arch/arm/mach-s3c2410/s3c2440.c
@@ -192,9 +192,11 @@ void __init s3c2440_map_io(struct map_desc *mach_desc, int size)
iotable_init(s3c2440_iodesc, ARRAY_SIZE(s3c2440_iodesc));
iotable_init(mach_desc, size);
+
/* rename any peripherals used differing from the s3c2410 */
- s3c_device_i2c.name = "s3c2440-i2c";
+ s3c_device_i2c.name = "s3c2440-i2c";
+ s3c_device_nand.name = "s3c2440-nand";
/* change irq for watchdog */
@@ -225,7 +227,7 @@ void __init s3c2440_init_clocks(int xtal)
break;
case S3C2440_CLKDIVN_HDIVN_2:
- hdiv = 1;
+ hdiv = 2;
break;
case S3C2440_CLKDIVN_HDIVN_4_8:
diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig
index 27892e34b06..48bac7da8c7 100644
--- a/arch/arm/mm/Kconfig
+++ b/arch/arm/mm/Kconfig
@@ -410,17 +410,22 @@ config CPU_BPREDICT_DISABLE
help
Say Y here to disable branch prediction. If unsure, say N.
+config TLS_REG_EMUL
+ bool
+ default y if SMP && (CPU_32v5 || CPU_32v4 || CPU_32v3)
+ help
+ An SMP system using a pre-ARMv6 processor (there are apparently
+ a few prototypes like that in existence) and therefore access to
+ that required register must be emulated.
+
config HAS_TLS_REG
bool
- depends on CPU_32v6 && !CPU_32v5 && !CPU_32v4 && !CPU_32v3
- default y
+ depends on !TLS_REG_EMUL
+ default y if SMP || CPU_32v7
help
This selects support for the CP15 thread register.
- It is defined to be available on ARMv6 or later. However
- if the kernel is configured to support multiple CPUs including
- a pre-ARMv6 processors, or if a given ARMv6 processor doesn't
- implement the thread register for some reason, then access to
- this register from user space must be trapped and emulated.
- If user space is relying on the __kuser_get_tls code then
- there should not be any impact.
+ It is defined to be available on some ARMv6 processors (including
+ all SMP capable ARMv6's) or later processors. User space may
+ assume directly accessing that register and always obtain the
+ expected value only on ARMv7 and above.
diff --git a/arch/arm/mm/copypage-v4mc.S b/arch/arm/mm/copypage-v4mc.S
deleted file mode 100644
index 305af3dab3d..00000000000
--- a/arch/arm/mm/copypage-v4mc.S
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * linux/arch/arm/lib/copy_page-armv4mc.S
- *
- * Copyright (C) 1995-2001 Russell King
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * ASM optimised string functions
- */
-#include <linux/linkage.h>
-#include <linux/init.h>
-#include <asm/constants.h>
-
- .text
- .align 5
-/*
- * ARMv4 mini-dcache optimised copy_user_page
- *
- * We flush the destination cache lines just before we write the data into the
- * corresponding address. Since the Dcache is read-allocate, this removes the
- * Dcache aliasing issue. The writes will be forwarded to the write buffer,
- * and merged as appropriate.
- *
- * Note: We rely on all ARMv4 processors implementing the "invalidate D line"
- * instruction. If your processor does not supply this, you have to write your
- * own copy_user_page that does the right thing.
- */
-ENTRY(v4_mc_copy_user_page)
- stmfd sp!, {r4, lr} @ 2
- mov r4, r0
- mov r0, r1
- bl map_page_minicache
- mov r1, #PAGE_SZ/64 @ 1
- ldmia r0!, {r2, r3, ip, lr} @ 4
-1: mcr p15, 0, r4, c7, c6, 1 @ 1 invalidate D line
- stmia r4!, {r2, r3, ip, lr} @ 4
- ldmia r0!, {r2, r3, ip, lr} @ 4+1
- stmia r4!, {r2, r3, ip, lr} @ 4
- ldmia r0!, {r2, r3, ip, lr} @ 4
- mcr p15, 0, r4, c7, c6, 1 @ 1 invalidate D line
- stmia r4!, {r2, r3, ip, lr} @ 4
- ldmia r0!, {r2, r3, ip, lr} @ 4
- subs r1, r1, #1 @ 1
- stmia r4!, {r2, r3, ip, lr} @ 4
- ldmneia r0!, {r2, r3, ip, lr} @ 4
- bne 1b @ 1
- ldmfd sp!, {r4, pc} @ 3
-
- .align 5
-/*
- * ARMv4 optimised clear_user_page
- *
- * Same story as above.
- */
-ENTRY(v4_mc_clear_user_page)
- str lr, [sp, #-4]!
- mov r1, #PAGE_SZ/64 @ 1
- mov r2, #0 @ 1
- mov r3, #0 @ 1
- mov ip, #0 @ 1
- mov lr, #0 @ 1
-1: mcr p15, 0, r0, c7, c6, 1 @ 1 invalidate D line
- stmia r0!, {r2, r3, ip, lr} @ 4
- stmia r0!, {r2, r3, ip, lr} @ 4
- mcr p15, 0, r0, c7, c6, 1 @ 1 invalidate D line
- stmia r0!, {r2, r3, ip, lr} @ 4
- stmia r0!, {r2, r3, ip, lr} @ 4
- subs r1, r1, #1 @ 1
- bne 1b @ 1
- ldr pc, [sp], #4
-
- __INITDATA
-
- .type v4_mc_user_fns, #object
-ENTRY(v4_mc_user_fns)
- .long v4_mc_clear_user_page
- .long v4_mc_copy_user_page
- .size v4_mc_user_fns, . - v4_mc_user_fns
diff --git a/arch/arm/mm/copypage-v4mc.c b/arch/arm/mm/copypage-v4mc.c
new file mode 100644
index 00000000000..fc69dccdace
--- /dev/null
+++ b/arch/arm/mm/copypage-v4mc.c
@@ -0,0 +1,111 @@
+/*
+ * linux/arch/arm/lib/copypage-armv4mc.S
+ *
+ * Copyright (C) 1995-2005 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This handles the mini data cache, as found on SA11x0 and XScale
+ * processors. When we copy a user page page, we map it in such a way
+ * that accesses to this page will not touch the main data cache, but
+ * will be cached in the mini data cache. This prevents us thrashing
+ * the main data cache on page faults.
+ */
+#include <linux/init.h>
+#include <linux/mm.h>
+
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/tlbflush.h>
+
+/*
+ * 0xffff8000 to 0xffffffff is reserved for any ARM architecture
+ * specific hacks for copying pages efficiently.
+ */
+#define minicache_pgprot __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | \
+ L_PTE_CACHEABLE)
+
+#define TOP_PTE(x) pte_offset_kernel(top_pmd, x)
+
+static DEFINE_SPINLOCK(minicache_lock);
+
+/*
+ * ARMv4 mini-dcache optimised copy_user_page
+ *
+ * We flush the destination cache lines just before we write the data into the
+ * corresponding address. Since the Dcache is read-allocate, this removes the
+ * Dcache aliasing issue. The writes will be forwarded to the write buffer,
+ * and merged as appropriate.
+ *
+ * Note: We rely on all ARMv4 processors implementing the "invalidate D line"
+ * instruction. If your processor does not supply this, you have to write your
+ * own copy_user_page that does the right thing.
+ */
+static void __attribute__((naked))
+mc_copy_user_page(void *from, void *to)
+{
+ asm volatile(
+ "stmfd sp!, {r4, lr} @ 2\n\
+ mov r4, %2 @ 1\n\
+ ldmia %0!, {r2, r3, ip, lr} @ 4\n\
+1: mcr p15, 0, %1, c7, c6, 1 @ 1 invalidate D line\n\
+ stmia %1!, {r2, r3, ip, lr} @ 4\n\
+ ldmia %0!, {r2, r3, ip, lr} @ 4+1\n\
+ stmia %1!, {r2, r3, ip, lr} @ 4\n\
+ ldmia %0!, {r2, r3, ip, lr} @ 4\n\
+ mcr p15, 0, %1, c7, c6, 1 @ 1 invalidate D line\n\
+ stmia %1!, {r2, r3, ip, lr} @ 4\n\
+ ldmia %0!, {r2, r3, ip, lr} @ 4\n\
+ subs r4, r4, #1 @ 1\n\
+ stmia %1!, {r2, r3, ip, lr} @ 4\n\
+ ldmneia %0!, {r2, r3, ip, lr} @ 4\n\
+ bne 1b @ 1\n\
+ ldmfd sp!, {r4, pc} @ 3"
+ :
+ : "r" (from), "r" (to), "I" (PAGE_SIZE / 64));
+}
+
+void v4_mc_copy_user_page(void *kto, const void *kfrom, unsigned long vaddr)
+{
+ spin_lock(&minicache_lock);
+
+ set_pte(TOP_PTE(0xffff8000), pfn_pte(__pa(kfrom) >> PAGE_SHIFT, minicache_pgprot));
+ flush_tlb_kernel_page(0xffff8000);
+
+ mc_copy_user_page((void *)0xffff8000, kto);
+
+ spin_unlock(&minicache_lock);
+}
+
+/*
+ * ARMv4 optimised clear_user_page
+ */
+void __attribute__((naked))
+v4_mc_clear_user_page(void *kaddr, unsigned long vaddr)
+{
+ asm volatile(
+ "str lr, [sp, #-4]!\n\
+ mov r1, %0 @ 1\n\
+ mov r2, #0 @ 1\n\
+ mov r3, #0 @ 1\n\
+ mov ip, #0 @ 1\n\
+ mov lr, #0 @ 1\n\
+1: mcr p15, 0, r0, c7, c6, 1 @ 1 invalidate D line\n\
+ stmia r0!, {r2, r3, ip, lr} @ 4\n\
+ stmia r0!, {r2, r3, ip, lr} @ 4\n\
+ mcr p15, 0, r0, c7, c6, 1 @ 1 invalidate D line\n\
+ stmia r0!, {r2, r3, ip, lr} @ 4\n\
+ stmia r0!, {r2, r3, ip, lr} @ 4\n\
+ subs r1, r1, #1 @ 1\n\
+ bne 1b @ 1\n\
+ ldr pc, [sp], #4"
+ :
+ : "I" (PAGE_SIZE / 64));
+}
+
+struct cpu_user_fns v4_mc_user_fns __initdata = {
+ .cpu_clear_user_page = v4_mc_clear_user_page,
+ .cpu_copy_user_page = v4_mc_copy_user_page,
+};
diff --git a/arch/arm/mm/copypage-v6.c b/arch/arm/mm/copypage-v6.c
index 694ac820885..a8c00236bd3 100644
--- a/arch/arm/mm/copypage-v6.c
+++ b/arch/arm/mm/copypage-v6.c
@@ -26,8 +26,8 @@
#define to_address (0xffffc000)
#define to_pgprot PAGE_KERNEL
-static pte_t *from_pte;
-static pte_t *to_pte;
+#define TOP_PTE(x) pte_offset_kernel(top_pmd, x)
+
static DEFINE_SPINLOCK(v6_lock);
#define DCACHE_COLOUR(vaddr) ((vaddr & (SHMLBA - 1)) >> PAGE_SHIFT)
@@ -74,8 +74,8 @@ void v6_copy_user_page_aliasing(void *kto, const void *kfrom, unsigned long vadd
*/
spin_lock(&v6_lock);
- set_pte(from_pte + offset, pfn_pte(__pa(kfrom) >> PAGE_SHIFT, from_pgprot));
- set_pte(to_pte + offset, pfn_pte(__pa(kto) >> PAGE_SHIFT, to_pgprot));
+ set_pte(TOP_PTE(from_address) + offset, pfn_pte(__pa(kfrom) >> PAGE_SHIFT, from_pgprot));
+ set_pte(TOP_PTE(to_address) + offset, pfn_pte(__pa(kto) >> PAGE_SHIFT, to_pgprot));
from = from_address + (offset << PAGE_SHIFT);
to = to_address + (offset << PAGE_SHIFT);
@@ -114,7 +114,7 @@ void v6_clear_user_page_aliasing(void *kaddr, unsigned long vaddr)
*/
spin_lock(&v6_lock);
- set_pte(to_pte + offset, pfn_pte(__pa(kaddr) >> PAGE_SHIFT, to_pgprot));
+ set_pte(TOP_PTE(to_address) + offset, pfn_pte(__pa(kaddr) >> PAGE_SHIFT, to_pgprot));
flush_tlb_kernel_page(to);
clear_page((void *)to);
@@ -129,21 +129,6 @@ struct cpu_user_fns v6_user_fns __initdata = {
static int __init v6_userpage_init(void)
{
if (cache_is_vipt_aliasing()) {
- pgd_t *pgd;
- pmd_t *pmd;
-
- pgd = pgd_offset_k(from_address);
- pmd = pmd_alloc(&init_mm, pgd, from_address);
- if (!pmd)
- BUG();
- from_pte = pte_alloc_kernel(&init_mm, pmd, from_address);
- if (!from_pte)
- BUG();
-
- to_pte = pte_alloc_kernel(&init_mm, pmd, to_address);
- if (!to_pte)
- BUG();
-
cpu_user.cpu_clear_user_page = v6_clear_user_page_aliasing;
cpu_user.cpu_copy_user_page = v6_copy_user_page_aliasing;
}
@@ -151,5 +136,4 @@ static int __init v6_userpage_init(void)
return 0;
}
-__initcall(v6_userpage_init);
-
+core_initcall(v6_userpage_init);
diff --git a/arch/arm/mm/flush.c b/arch/arm/mm/flush.c
index c6de48d8950..4085ed983e4 100644
--- a/arch/arm/mm/flush.c
+++ b/arch/arm/mm/flush.c
@@ -13,6 +13,29 @@
#include <asm/cacheflush.h>
#include <asm/system.h>
+#include <asm/tlbflush.h>
+
+#ifdef CONFIG_CPU_CACHE_VIPT
+#define ALIAS_FLUSH_START 0xffff4000
+
+#define TOP_PTE(x) pte_offset_kernel(top_pmd, x)
+
+static void flush_pfn_alias(unsigned long pfn, unsigned long vaddr)
+{
+ unsigned long to = ALIAS_FLUSH_START + (CACHE_COLOUR(vaddr) << PAGE_SHIFT);
+
+ set_pte(TOP_PTE(to), pfn_pte(pfn, PAGE_KERNEL));
+ flush_tlb_kernel_page(to);
+
+ asm( "mcrr p15, 0, %1, %0, c14\n"
+ " mcrr p15, 0, %1, %0, c5\n"
+ :
+ : "r" (to), "r" (to + PAGE_SIZE - L1_CACHE_BYTES)
+ : "cc");
+}
+#else
+#define flush_pfn_alias(pfn,vaddr) do { } while (0)
+#endif
static void __flush_dcache_page(struct address_space *mapping, struct page *page)
{
@@ -37,6 +60,18 @@ static void __flush_dcache_page(struct address_space *mapping, struct page *page
return;
/*
+ * This is a page cache page. If we have a VIPT cache, we
+ * only need to do one flush - which would be at the relevant
+ * userspace colour, which is congruent with page->index.
+ */
+ if (cache_is_vipt()) {
+ if (cache_is_vipt_aliasing())
+ flush_pfn_alias(page_to_pfn(page),
+ page->index << PAGE_CACHE_SHIFT);
+ return;
+ }
+
+ /*
* There are possible user space mappings of this page:
* - VIVT cache: we need to also write back and invalidate all user
* data in the current VM view associated with this page.
@@ -57,8 +92,6 @@ static void __flush_dcache_page(struct address_space *mapping, struct page *page
continue;
offset = (pgoff - mpnt->vm_pgoff) << PAGE_SHIFT;
flush_cache_page(mpnt, mpnt->vm_start + offset, page_to_pfn(page));
- if (cache_is_vipt())
- break;
}
flush_dcache_mmap_unlock(mapping);
}
diff --git a/arch/arm/mm/mm-armv.c b/arch/arm/mm/mm-armv.c
index 585dfb8e20b..2c2b93d77d4 100644
--- a/arch/arm/mm/mm-armv.c
+++ b/arch/arm/mm/mm-armv.c
@@ -37,6 +37,8 @@ pgprot_t pgprot_kernel;
EXPORT_SYMBOL(pgprot_kernel);
+pmd_t *top_pmd;
+
struct cachepolicy {
const char policy[16];
unsigned int cr_mask;
@@ -142,6 +144,16 @@ __setup("noalign", noalign_setup);
#define FIRST_KERNEL_PGD_NR (FIRST_USER_PGD_NR + USER_PTRS_PER_PGD)
+static inline pmd_t *pmd_off(pgd_t *pgd, unsigned long virt)
+{
+ return pmd_offset(pgd, virt);
+}
+
+static inline pmd_t *pmd_off_k(unsigned long virt)
+{
+ return pmd_off(pgd_offset_k(virt), virt);
+}
+
/*
* need to get a 16k page for level 1
*/
@@ -220,7 +232,7 @@ void free_pgd_slow(pgd_t *pgd)
return;
/* pgd is always present and good */
- pmd = (pmd_t *)pgd;
+ pmd = pmd_off(pgd, 0);
if (pmd_none(*pmd))
goto free;
if (pmd_bad(*pmd)) {
@@ -246,9 +258,8 @@ free:
static inline void
alloc_init_section(unsigned long virt, unsigned long phys, int prot)
{
- pmd_t *pmdp;
+ pmd_t *pmdp = pmd_off_k(virt);
- pmdp = pmd_offset(pgd_offset_k(virt), virt);
if (virt & (1 << 20))
pmdp++;
@@ -283,11 +294,9 @@ alloc_init_supersection(unsigned long virt, unsigned long phys, int prot)
static inline void
alloc_init_page(unsigned long virt, unsigned long phys, unsigned int prot_l1, pgprot_t prot)
{
- pmd_t *pmdp;
+ pmd_t *pmdp = pmd_off_k(virt);
pte_t *ptep;
- pmdp = pmd_offset(pgd_offset_k(virt), virt);
-
if (pmd_none(*pmdp)) {
unsigned long pmdval;
ptep = alloc_bootmem_low_pages(2 * PTRS_PER_PTE *
@@ -310,7 +319,7 @@ alloc_init_page(unsigned long virt, unsigned long phys, unsigned int prot_l1, pg
*/
static inline void clear_mapping(unsigned long virt)
{
- pmd_clear(pmd_offset(pgd_offset_k(virt), virt));
+ pmd_clear(pmd_off_k(virt));
}
struct mem_types {
@@ -578,7 +587,7 @@ void setup_mm_for_reboot(char mode)
PMD_TYPE_SECT;
if (cpu_arch <= CPU_ARCH_ARMv5)
pmdval |= PMD_BIT4;
- pmd = pmd_offset(pgd + i, i << PGDIR_SHIFT);
+ pmd = pmd_off(pgd, i << PGDIR_SHIFT);
pmd[0] = __pmd(pmdval);
pmd[1] = __pmd(pmdval + (1 << (PGDIR_SHIFT - 1)));
flush_pmd_entry(pmd);
@@ -675,6 +684,8 @@ void __init memtable_init(struct meminfo *mi)
flush_cache_all();
flush_tlb_all();
+
+ top_pmd = pmd_off_k(0xffff0000);
}
/*