aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/arm/kernel/entry-armv.S24
-rw-r--r--arch/arm/kernel/iwmmxt.S2
-rw-r--r--arch/arm/kernel/process.c24
-rw-r--r--arch/arm/nwfpe/fpmodule.c25
-rw-r--r--arch/arm/vfp/vfpmodule.c71
-rw-r--r--include/asm-arm/thread_notify.h48
6 files changed, 135 insertions, 59 deletions
diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S
index ab8e600c18c8..86c92523a346 100644
--- a/arch/arm/kernel/entry-armv.S
+++ b/arch/arm/kernel/entry-armv.S
@@ -20,6 +20,7 @@
20#include <asm/glue.h> 20#include <asm/glue.h>
21#include <asm/vfpmacros.h> 21#include <asm/vfpmacros.h>
22#include <asm/arch/entry-macro.S> 22#include <asm/arch/entry-macro.S>
23#include <asm/thread_notify.h>
23 24
24#include "entry-header.S" 25#include "entry-header.S"
25 26
@@ -560,10 +561,8 @@ ENTRY(__switch_to)
560 add ip, r1, #TI_CPU_SAVE 561 add ip, r1, #TI_CPU_SAVE
561 ldr r3, [r2, #TI_TP_VALUE] 562 ldr r3, [r2, #TI_TP_VALUE]
562 stmia ip!, {r4 - sl, fp, sp, lr} @ Store most regs on stack 563 stmia ip!, {r4 - sl, fp, sp, lr} @ Store most regs on stack
563#ifndef CONFIG_MMU 564#ifdef CONFIG_MMU
564 add r2, r2, #TI_CPU_DOMAIN 565 ldr r6, [r2, #TI_CPU_DOMAIN]
565#else
566 ldr r6, [r2, #TI_CPU_DOMAIN]!
567#endif 566#endif
568#if __LINUX_ARM_ARCH__ >= 6 567#if __LINUX_ARM_ARCH__ >= 6
569#ifdef CONFIG_CPU_32v6K 568#ifdef CONFIG_CPU_32v6K
@@ -585,21 +584,20 @@ ENTRY(__switch_to)
585#ifdef CONFIG_MMU 584#ifdef CONFIG_MMU
586 mcr p15, 0, r6, c3, c0, 0 @ Set domain register 585 mcr p15, 0, r6, c3, c0, 0 @ Set domain register
587#endif 586#endif
588#ifdef CONFIG_VFP
589 @ Always disable VFP so we can lazily save/restore the old
590 @ state. This occurs in the context of the previous thread.
591 VFPFMRX r4, FPEXC
592 bic r4, r4, #FPEXC_ENABLE
593 VFPFMXR FPEXC, r4
594#endif
595#if defined(CONFIG_IWMMXT) 587#if defined(CONFIG_IWMMXT)
596 bl iwmmxt_task_switch 588 bl iwmmxt_task_switch
597#elif defined(CONFIG_CPU_XSCALE) 589#elif defined(CONFIG_CPU_XSCALE)
598 add r4, r2, #40 @ cpu_context_save->extra 590 add r4, r2, #TI_CPU_DOMAIN + 40 @ cpu_context_save->extra
599 ldmib r4, {r4, r5} 591 ldmib r4, {r4, r5}
600 mar acc0, r4, r5 592 mar acc0, r4, r5
601#endif 593#endif
602 ldmib r2, {r4 - sl, fp, sp, pc} @ Load all regs saved previously 594 mov r5, r0
595 add r4, r2, #TI_CPU_SAVE
596 ldr r0, =thread_notify_head
597 mov r1, #THREAD_NOTIFY_SWITCH
598 bl atomic_notifier_call_chain
599 mov r0, r5
600 ldmia r4, {r4 - sl, fp, sp, pc} @ Load all regs saved previously
603 601
604 __INIT 602 __INIT
605 603
diff --git a/arch/arm/kernel/iwmmxt.S b/arch/arm/kernel/iwmmxt.S
index 24c7b0477a09..af9e0ae952d5 100644
--- a/arch/arm/kernel/iwmmxt.S
+++ b/arch/arm/kernel/iwmmxt.S
@@ -285,7 +285,7 @@ ENTRY(iwmmxt_task_switch)
285 bne 1f @ yes: block them for next task 285 bne 1f @ yes: block them for next task
286 286
287 ldr r5, =concan_owner 287 ldr r5, =concan_owner
288 add r6, r2, #(TI_IWMMXT_STATE - TI_CPU_DOMAIN) @ get next task Concan save area 288 add r6, r2, #TI_IWMMXT_STATE @ get next task Concan save area
289 ldr r5, [r5] @ get current Concan owner 289 ldr r5, [r5] @ get current Concan owner
290 teq r5, r6 @ next task owns it? 290 teq r5, r6 @ next task owns it?
291 movne pc, lr @ no: leave Concan disabled 291 movne pc, lr @ no: leave Concan disabled
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c
index 17c38dbf2f3c..e1c77ee885a7 100644
--- a/arch/arm/kernel/process.c
+++ b/arch/arm/kernel/process.c
@@ -33,6 +33,7 @@
33#include <asm/leds.h> 33#include <asm/leds.h>
34#include <asm/processor.h> 34#include <asm/processor.h>
35#include <asm/system.h> 35#include <asm/system.h>
36#include <asm/thread_notify.h>
36#include <asm/uaccess.h> 37#include <asm/uaccess.h>
37#include <asm/mach/time.h> 38#include <asm/mach/time.h>
38 39
@@ -338,13 +339,9 @@ void exit_thread(void)
338{ 339{
339} 340}
340 341
341static void default_fp_init(union fp_state *fp) 342ATOMIC_NOTIFIER_HEAD(thread_notify_head);
342{
343 memset(fp, 0, sizeof(union fp_state));
344}
345 343
346void (*fp_init)(union fp_state *) = default_fp_init; 344EXPORT_SYMBOL_GPL(thread_notify_head);
347EXPORT_SYMBOL(fp_init);
348 345
349void flush_thread(void) 346void flush_thread(void)
350{ 347{
@@ -353,22 +350,21 @@ void flush_thread(void)
353 350
354 memset(thread->used_cp, 0, sizeof(thread->used_cp)); 351 memset(thread->used_cp, 0, sizeof(thread->used_cp));
355 memset(&tsk->thread.debug, 0, sizeof(struct debug_info)); 352 memset(&tsk->thread.debug, 0, sizeof(struct debug_info));
353 memset(&thread->fpstate, 0, sizeof(union fp_state));
354
355 thread_notify(THREAD_NOTIFY_FLUSH, thread);
356#if defined(CONFIG_IWMMXT) 356#if defined(CONFIG_IWMMXT)
357 iwmmxt_task_release(thread); 357 iwmmxt_task_release(thread);
358#endif 358#endif
359 fp_init(&thread->fpstate);
360#if defined(CONFIG_VFP)
361 vfp_flush_thread(&thread->vfpstate);
362#endif
363} 359}
364 360
365void release_thread(struct task_struct *dead_task) 361void release_thread(struct task_struct *dead_task)
366{ 362{
367#if defined(CONFIG_VFP) 363 struct thread_info *thread = task_thread_info(dead_task);
368 vfp_release_thread(&task_thread_info(dead_task)->vfpstate); 364
369#endif 365 thread_notify(THREAD_NOTIFY_RELEASE, thread);
370#if defined(CONFIG_IWMMXT) 366#if defined(CONFIG_IWMMXT)
371 iwmmxt_task_release(task_thread_info(dead_task)); 367 iwmmxt_task_release(thread);
372#endif 368#endif
373} 369}
374 370
diff --git a/arch/arm/nwfpe/fpmodule.c b/arch/arm/nwfpe/fpmodule.c
index 2dfe1ac42ee8..7d977d23f026 100644
--- a/arch/arm/nwfpe/fpmodule.c
+++ b/arch/arm/nwfpe/fpmodule.c
@@ -33,7 +33,8 @@
33#include <linux/signal.h> 33#include <linux/signal.h>
34#include <linux/sched.h> 34#include <linux/sched.h>
35#include <linux/init.h> 35#include <linux/init.h>
36/* XXX */ 36
37#include <asm/thread_notify.h>
37 38
38#include "softfloat.h" 39#include "softfloat.h"
39#include "fpopcode.h" 40#include "fpopcode.h"
@@ -56,16 +57,28 @@ void fp_send_sig(unsigned long sig, struct task_struct *p, int priv);
56extern char fpe_type[]; 57extern char fpe_type[];
57#endif 58#endif
58 59
60static int nwfpe_notify(struct notifier_block *self, unsigned long cmd, void *v)
61{
62 struct thread_info *thread = v;
63
64 if (cmd == THREAD_NOTIFY_FLUSH)
65 nwfpe_init_fpa(&thread->fpstate);
66
67 return NOTIFY_DONE;
68}
69
70static struct notifier_block nwfpe_notifier_block = {
71 .notifier_call = nwfpe_notify,
72};
73
59/* kernel function prototypes required */ 74/* kernel function prototypes required */
60void fp_setup(void); 75void fp_setup(void);
61 76
62/* external declarations for saved kernel symbols */ 77/* external declarations for saved kernel symbols */
63extern void (*kern_fp_enter)(void); 78extern void (*kern_fp_enter)(void);
64extern void (*fp_init)(union fp_state *);
65 79
66/* Original value of fp_enter from kernel before patched by fpe_init. */ 80/* Original value of fp_enter from kernel before patched by fpe_init. */
67static void (*orig_fp_enter)(void); 81static void (*orig_fp_enter)(void);
68static void (*orig_fp_init)(union fp_state *);
69 82
70/* forward declarations */ 83/* forward declarations */
71extern void nwfpe_enter(void); 84extern void nwfpe_enter(void);
@@ -88,20 +101,20 @@ static int __init fpe_init(void)
88 printk(KERN_WARNING "NetWinder Floating Point Emulator V0.97 (" 101 printk(KERN_WARNING "NetWinder Floating Point Emulator V0.97 ("
89 NWFPE_BITS " precision)\n"); 102 NWFPE_BITS " precision)\n");
90 103
104 thread_register_notifier(&nwfpe_notifier_block);
105
91 /* Save pointer to the old FP handler and then patch ourselves in */ 106 /* Save pointer to the old FP handler and then patch ourselves in */
92 orig_fp_enter = kern_fp_enter; 107 orig_fp_enter = kern_fp_enter;
93 orig_fp_init = fp_init;
94 kern_fp_enter = nwfpe_enter; 108 kern_fp_enter = nwfpe_enter;
95 fp_init = nwfpe_init_fpa;
96 109
97 return 0; 110 return 0;
98} 111}
99 112
100static void __exit fpe_exit(void) 113static void __exit fpe_exit(void)
101{ 114{
115 thread_unregister_notifier(&nwfpe_notifier_block);
102 /* Restore the values we saved earlier. */ 116 /* Restore the values we saved earlier. */
103 kern_fp_enter = orig_fp_enter; 117 kern_fp_enter = orig_fp_enter;
104 fp_init = orig_fp_init;
105} 118}
106 119
107/* 120/*
diff --git a/arch/arm/vfp/vfpmodule.c b/arch/arm/vfp/vfpmodule.c
index 03486be04193..2476f4c2e760 100644
--- a/arch/arm/vfp/vfpmodule.c
+++ b/arch/arm/vfp/vfpmodule.c
@@ -15,6 +15,8 @@
15#include <linux/signal.h> 15#include <linux/signal.h>
16#include <linux/sched.h> 16#include <linux/sched.h>
17#include <linux/init.h> 17#include <linux/init.h>
18
19#include <asm/thread_notify.h>
18#include <asm/vfp.h> 20#include <asm/vfp.h>
19 21
20#include "vfpinstr.h" 22#include "vfpinstr.h"
@@ -36,38 +38,55 @@ union vfp_state *last_VFP_context;
36 */ 38 */
37unsigned int VFP_arch; 39unsigned int VFP_arch;
38 40
39/* 41static int vfp_notifier(struct notifier_block *self, unsigned long cmd, void *v)
40 * Per-thread VFP initialisation.
41 */
42void vfp_flush_thread(union vfp_state *vfp)
43{ 42{
44 memset(vfp, 0, sizeof(union vfp_state)); 43 struct thread_info *thread = v;
44 union vfp_state *vfp = &thread->vfpstate;
45 45
46 vfp->hard.fpexc = FPEXC_ENABLE; 46 switch (cmd) {
47 vfp->hard.fpscr = FPSCR_ROUND_NEAREST; 47 case THREAD_NOTIFY_FLUSH:
48 /*
49 * Per-thread VFP initialisation.
50 */
51 memset(vfp, 0, sizeof(union vfp_state));
48 52
49 /* 53 vfp->hard.fpexc = FPEXC_ENABLE;
50 * Disable VFP to ensure we initialise it first. 54 vfp->hard.fpscr = FPSCR_ROUND_NEAREST;
51 */
52 fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_ENABLE);
53 55
54 /* 56 /*
55 * Ensure we don't try to overwrite our newly initialised 57 * Disable VFP to ensure we initialise it first.
56 * state information on the first fault. 58 */
57 */ 59 fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_ENABLE);
58 if (last_VFP_context == vfp)
59 last_VFP_context = NULL;
60}
61 60
62/* 61 /*
63 * Per-thread VFP cleanup. 62 * FALLTHROUGH: Ensure we don't try to overwrite our newly
64 */ 63 * initialised state information on the first fault.
65void vfp_release_thread(union vfp_state *vfp) 64 */
66{ 65
67 if (last_VFP_context == vfp) 66 case THREAD_NOTIFY_RELEASE:
68 last_VFP_context = NULL; 67 /*
68 * Per-thread VFP cleanup.
69 */
70 if (last_VFP_context == vfp)
71 last_VFP_context = NULL;
72 break;
73
74 case THREAD_NOTIFY_SWITCH:
75 /*
76 * Always disable VFP so we can lazily save/restore the
77 * old state.
78 */
79 fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_ENABLE);
80 break;
81 }
82
83 return NOTIFY_DONE;
69} 84}
70 85
86static struct notifier_block vfp_notifier_block = {
87 .notifier_call = vfp_notifier,
88};
89
71/* 90/*
72 * Raise a SIGFPE for the current process. 91 * Raise a SIGFPE for the current process.
73 * sicode describes the signal being raised. 92 * sicode describes the signal being raised.
@@ -281,6 +300,8 @@ static int __init vfp_init(void)
281 (vfpsid & FPSID_VARIANT_MASK) >> FPSID_VARIANT_BIT, 300 (vfpsid & FPSID_VARIANT_MASK) >> FPSID_VARIANT_BIT,
282 (vfpsid & FPSID_REV_MASK) >> FPSID_REV_BIT); 301 (vfpsid & FPSID_REV_MASK) >> FPSID_REV_BIT);
283 vfp_vector = vfp_support_entry; 302 vfp_vector = vfp_support_entry;
303
304 thread_register_notifier(&vfp_notifier_block);
284 } 305 }
285 return 0; 306 return 0;
286} 307}
diff --git a/include/asm-arm/thread_notify.h b/include/asm-arm/thread_notify.h
new file mode 100644
index 000000000000..8866e5216840
--- /dev/null
+++ b/include/asm-arm/thread_notify.h
@@ -0,0 +1,48 @@
1/*
2 * linux/include/asm-arm/thread_notify.h
3 *
4 * Copyright (C) 2006 Russell King.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 */
10#ifndef ASMARM_THREAD_NOTIFY_H
11#define ASMARM_THREAD_NOTIFY_H
12
13#ifdef __KERNEL__
14
15#ifndef __ASSEMBLY__
16
17#include <linux/notifier.h>
18#include <asm/thread_info.h>
19
20static inline int thread_register_notifier(struct notifier_block *n)
21{
22 extern struct atomic_notifier_head thread_notify_head;
23 return atomic_notifier_chain_register(&thread_notify_head, n);
24}
25
26static inline void thread_unregister_notifier(struct notifier_block *n)
27{
28 extern struct atomic_notifier_head thread_notify_head;
29 atomic_notifier_chain_unregister(&thread_notify_head, n);
30}
31
32static inline void thread_notify(unsigned long rc, struct thread_info *thread)
33{
34 extern struct atomic_notifier_head thread_notify_head;
35 atomic_notifier_call_chain(&thread_notify_head, rc, thread);
36}
37
38#endif
39
40/*
41 * These are the reason codes for the thread notifier.
42 */
43#define THREAD_NOTIFY_FLUSH 0
44#define THREAD_NOTIFY_RELEASE 1
45#define THREAD_NOTIFY_SWITCH 2
46
47#endif
48#endif