aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mn10300
diff options
context:
space:
mode:
authorAkira Takeuchi <takeuchi.akr@jp.panasonic.com>2010-10-27 12:28:52 -0400
committerDavid Howells <dhowells@redhat.com>2010-10-27 12:28:52 -0400
commit278d91c4609d55202c1e63d5fc5f01466cc7bbab (patch)
tree8b0c863837508959430c1741e2e5a2d37d2890d4 /arch/mn10300
parent965ea4bbb9ae926358273368144ba838c561bc38 (diff)
MN10300: Make the FPU operate in non-lazy mode under SMP
Make the FPU operate in non-lazy mode under SMP so that when the process that is currently using the FPU migrates to a different CPU, we don't have to ping its previous CPU to flush the FPU context. Signed-off-by: Akira Takeuchi <takeuchi.akr@jp.panasonic.com> Signed-off-by: Kiyoshi Owada <owada.kiyoshi@jp.panasonic.com> Signed-off-by: David Howells <dhowells@redhat.com>
Diffstat (limited to 'arch/mn10300')
-rw-r--r--arch/mn10300/Kconfig15
-rw-r--r--arch/mn10300/include/asm/elf.h2
-rw-r--r--arch/mn10300/include/asm/exceptions.h1
-rw-r--r--arch/mn10300/include/asm/fpu.h157
-rw-r--r--arch/mn10300/include/asm/processor.h1
-rw-r--r--arch/mn10300/include/asm/system.h16
-rw-r--r--arch/mn10300/kernel/Makefile8
-rw-r--r--arch/mn10300/kernel/asm-offsets.c9
-rw-r--r--arch/mn10300/kernel/fpu-low.S265
-rw-r--r--arch/mn10300/kernel/fpu-nofpu-low.S39
-rw-r--r--arch/mn10300/kernel/fpu-nofpu.c30
-rw-r--r--arch/mn10300/kernel/fpu.c141
-rw-r--r--arch/mn10300/kernel/traps.c2
-rw-r--r--arch/mn10300/proc-mn103e010/proc-init.c2
14 files changed, 440 insertions, 248 deletions
diff --git a/arch/mn10300/Kconfig b/arch/mn10300/Kconfig
index 7bd920b1c06f..a1f334ce0a36 100644
--- a/arch/mn10300/Kconfig
+++ b/arch/mn10300/Kconfig
@@ -140,6 +140,21 @@ config FPU
140 default y 140 default y
141 depends on MN10300_PROC_MN103E010 141 depends on MN10300_PROC_MN103E010
142 142
143config LAZY_SAVE_FPU
144 bool "Save FPU state lazily"
145 default y
146 depends on FPU && !SMP
147 help
148 Enable this to be lazy in the saving of the FPU state to the owning
149 task's thread struct. This is useful if most tasks on the system
150 don't use the FPU as only those tasks that use it will pass it
151 between them, and the state needn't be saved for a task that isn't
152 using it.
153
154 This can't be so easily used on SMP as the process that owns the FPU
155 state on a CPU may be currently running on another CPU, so for the
156 moment, it is disabled.
157
143source "arch/mn10300/mm/Kconfig.cache" 158source "arch/mn10300/mm/Kconfig.cache"
144 159
145config MN10300_TLB_USE_PIDR 160config MN10300_TLB_USE_PIDR
diff --git a/arch/mn10300/include/asm/elf.h b/arch/mn10300/include/asm/elf.h
index e5fa97cd9a14..a30d220de5ca 100644
--- a/arch/mn10300/include/asm/elf.h
+++ b/arch/mn10300/include/asm/elf.h
@@ -47,8 +47,6 @@ typedef struct {
47 u_int32_t fpcr; 47 u_int32_t fpcr;
48} elf_fpregset_t; 48} elf_fpregset_t;
49 49
50extern int dump_fpu(struct pt_regs *, elf_fpregset_t *);
51
52/* 50/*
53 * This is used to ensure we don't load something for the wrong architecture 51 * This is used to ensure we don't load something for the wrong architecture
54 */ 52 */
diff --git a/arch/mn10300/include/asm/exceptions.h b/arch/mn10300/include/asm/exceptions.h
index 3f3826abc745..7d8080bc6590 100644
--- a/arch/mn10300/include/asm/exceptions.h
+++ b/arch/mn10300/include/asm/exceptions.h
@@ -101,7 +101,6 @@ extern asmlinkage void dtlb_aerror(void);
101extern asmlinkage void raw_bus_error(void); 101extern asmlinkage void raw_bus_error(void);
102extern asmlinkage void double_fault(void); 102extern asmlinkage void double_fault(void);
103extern asmlinkage int system_call(struct pt_regs *); 103extern asmlinkage int system_call(struct pt_regs *);
104extern asmlinkage void fpu_exception(struct pt_regs *, enum exception_code);
105extern asmlinkage void nmi(struct pt_regs *, enum exception_code); 104extern asmlinkage void nmi(struct pt_regs *, enum exception_code);
106extern asmlinkage void uninitialised_exception(struct pt_regs *, 105extern asmlinkage void uninitialised_exception(struct pt_regs *,
107 enum exception_code); 106 enum exception_code);
diff --git a/arch/mn10300/include/asm/fpu.h b/arch/mn10300/include/asm/fpu.h
index 64a2b83a7a6a..b7625de8eade 100644
--- a/arch/mn10300/include/asm/fpu.h
+++ b/arch/mn10300/include/asm/fpu.h
@@ -12,74 +12,125 @@
12#ifndef _ASM_FPU_H 12#ifndef _ASM_FPU_H
13#define _ASM_FPU_H 13#define _ASM_FPU_H
14 14
15#include <asm/processor.h> 15#ifndef __ASSEMBLY__
16
17#include <linux/sched.h>
18#include <asm/exceptions.h>
16#include <asm/sigcontext.h> 19#include <asm/sigcontext.h>
17#include <asm/user.h>
18 20
19#ifdef __KERNEL__ 21#ifdef __KERNEL__
20 22
21/* the task that owns the FPU state */ 23extern asmlinkage void fpu_disabled(void);
24
25#ifdef CONFIG_FPU
26
27#ifdef CONFIG_LAZY_SAVE_FPU
28/* the task that currently owns the FPU state */
22extern struct task_struct *fpu_state_owner; 29extern struct task_struct *fpu_state_owner;
30#endif
23 31
24#define set_using_fpu(tsk) \ 32#if (THREAD_USING_FPU & ~0xff)
25do { \ 33#error THREAD_USING_FPU must be smaller than 0x100.
26 (tsk)->thread.fpu_flags |= THREAD_USING_FPU; \ 34#endif
27} while (0)
28 35
29#define clear_using_fpu(tsk) \ 36static inline void set_using_fpu(struct task_struct *tsk)
30do { \ 37{
31 (tsk)->thread.fpu_flags &= ~THREAD_USING_FPU; \ 38 asm volatile(
32} while (0) 39 "bset %0,(0,%1)"
40 :
41 : "i"(THREAD_USING_FPU), "a"(&tsk->thread.fpu_flags)
42 : "memory", "cc");
43}
33 44
34#define is_using_fpu(tsk) ((tsk)->thread.fpu_flags & THREAD_USING_FPU) 45static inline void clear_using_fpu(struct task_struct *tsk)
46{
47 asm volatile(
48 "bclr %0,(0,%1)"
49 :
50 : "i"(THREAD_USING_FPU), "a"(&tsk->thread.fpu_flags)
51 : "memory", "cc");
52}
35 53
36#define unlazy_fpu(tsk) \ 54#define is_using_fpu(tsk) ((tsk)->thread.fpu_flags & THREAD_USING_FPU)
37do { \
38 preempt_disable(); \
39 if (fpu_state_owner == (tsk)) \
40 fpu_save(&tsk->thread.fpu_state); \
41 preempt_enable(); \
42} while (0)
43
44#define exit_fpu() \
45do { \
46 struct task_struct *__tsk = current; \
47 preempt_disable(); \
48 if (fpu_state_owner == __tsk) \
49 fpu_state_owner = NULL; \
50 preempt_enable(); \
51} while (0)
52
53#define flush_fpu() \
54do { \
55 struct task_struct *__tsk = current; \
56 preempt_disable(); \
57 if (fpu_state_owner == __tsk) { \
58 fpu_state_owner = NULL; \
59 __tsk->thread.uregs->epsw &= ~EPSW_FE; \
60 } \
61 preempt_enable(); \
62 clear_using_fpu(__tsk); \
63} while (0)
64 55
65extern asmlinkage void fpu_init_state(void);
66extern asmlinkage void fpu_kill_state(struct task_struct *); 56extern asmlinkage void fpu_kill_state(struct task_struct *);
67extern asmlinkage void fpu_disabled(struct pt_regs *, enum exception_code);
68extern asmlinkage void fpu_exception(struct pt_regs *, enum exception_code); 57extern asmlinkage void fpu_exception(struct pt_regs *, enum exception_code);
69 58extern asmlinkage void fpu_invalid_op(struct pt_regs *, enum exception_code);
70#ifdef CONFIG_FPU 59extern asmlinkage void fpu_init_state(void);
71extern asmlinkage void fpu_save(struct fpu_state_struct *); 60extern asmlinkage void fpu_save(struct fpu_state_struct *);
72extern asmlinkage void fpu_restore(struct fpu_state_struct *);
73#else
74#define fpu_save(a)
75#define fpu_restore(a)
76#endif /* CONFIG_FPU */
77
78/*
79 * signal frame handlers
80 */
81extern int fpu_setup_sigcontext(struct fpucontext *buf); 61extern int fpu_setup_sigcontext(struct fpucontext *buf);
82extern int fpu_restore_sigcontext(struct fpucontext *buf); 62extern int fpu_restore_sigcontext(struct fpucontext *buf);
83 63
64static inline void unlazy_fpu(struct task_struct *tsk)
65{
66 preempt_disable();
67#ifndef CONFIG_LAZY_SAVE_FPU
68 if (tsk->thread.fpu_flags & THREAD_HAS_FPU) {
69 fpu_save(&tsk->thread.fpu_state);
70 tsk->thread.fpu_flags &= ~THREAD_HAS_FPU;
71 tsk->thread.uregs->epsw &= ~EPSW_FE;
72 }
73#else
74 if (fpu_state_owner == tsk)
75 fpu_save(&tsk->thread.fpu_state);
76#endif
77 preempt_enable();
78}
79
80static inline void exit_fpu(void)
81{
82#ifdef CONFIG_LAZY_SAVE_FPU
83 struct task_struct *tsk = current;
84
85 preempt_disable();
86 if (fpu_state_owner == tsk)
87 fpu_state_owner = NULL;
88 preempt_enable();
89#endif
90}
91
92static inline void flush_fpu(void)
93{
94 struct task_struct *tsk = current;
95
96 preempt_disable();
97#ifndef CONFIG_LAZY_SAVE_FPU
98 if (tsk->thread.fpu_flags & THREAD_HAS_FPU) {
99 tsk->thread.fpu_flags &= ~THREAD_HAS_FPU;
100 tsk->thread.uregs->epsw &= ~EPSW_FE;
101 }
102#else
103 if (fpu_state_owner == tsk) {
104 fpu_state_owner = NULL;
105 tsk->thread.uregs->epsw &= ~EPSW_FE;
106 }
107#endif
108 preempt_enable();
109 clear_using_fpu(tsk);
110}
111
112#else /* CONFIG_FPU */
113
114extern asmlinkage
115void unexpected_fpu_exception(struct pt_regs *, enum exception_code);
116#define fpu_invalid_op unexpected_fpu_exception
117#define fpu_exception unexpected_fpu_exception
118
119struct task_struct;
120struct fpu_state_struct;
121static inline bool is_using_fpu(struct task_struct *tsk) { return false; }
122static inline void set_using_fpu(struct task_struct *tsk) {}
123static inline void clear_using_fpu(struct task_struct *tsk) {}
124static inline void fpu_init_state(void) {}
125static inline void fpu_save(struct fpu_state_struct *s) {}
126static inline void fpu_kill_state(struct task_struct *tsk) {}
127static inline void unlazy_fpu(struct task_struct *tsk) {}
128static inline void exit_fpu(void) {}
129static inline void flush_fpu(void) {}
130static inline int fpu_setup_sigcontext(struct fpucontext *buf) { return 0; }
131static inline int fpu_restore_sigcontext(struct fpucontext *buf) { return 0; }
132#endif /* CONFIG_FPU */
133
84#endif /* __KERNEL__ */ 134#endif /* __KERNEL__ */
135#endif /* !__ASSEMBLY__ */
85#endif /* _ASM_FPU_H */ 136#endif /* _ASM_FPU_H */
diff --git a/arch/mn10300/include/asm/processor.h b/arch/mn10300/include/asm/processor.h
index fd96c180e649..0032fc76c8ba 100644
--- a/arch/mn10300/include/asm/processor.h
+++ b/arch/mn10300/include/asm/processor.h
@@ -95,6 +95,7 @@ struct thread_struct {
95 struct pt_regs *__frame; 95 struct pt_regs *__frame;
96 unsigned long fpu_flags; 96 unsigned long fpu_flags;
97#define THREAD_USING_FPU 0x00000001 /* T if this task is using the FPU */ 97#define THREAD_USING_FPU 0x00000001 /* T if this task is using the FPU */
98#define THREAD_HAS_FPU 0x00000002 /* T if this task owns the FPU right now */
98 struct fpu_state_struct fpu_state; 99 struct fpu_state_struct fpu_state;
99}; 100};
100 101
diff --git a/arch/mn10300/include/asm/system.h b/arch/mn10300/include/asm/system.h
index 9f7c7e17c01e..3c272a1a1015 100644
--- a/arch/mn10300/include/asm/system.h
+++ b/arch/mn10300/include/asm/system.h
@@ -19,6 +19,21 @@
19#include <linux/kernel.h> 19#include <linux/kernel.h>
20#include <linux/irqflags.h> 20#include <linux/irqflags.h>
21 21
22#if !defined(CONFIG_LAZY_SAVE_FPU)
23struct fpu_state_struct;
24extern asmlinkage void fpu_save(struct fpu_state_struct *);
25#define switch_fpu(prev, next) \
26 do { \
27 if ((prev)->thread.fpu_flags & THREAD_HAS_FPU) { \
28 (prev)->thread.fpu_flags &= ~THREAD_HAS_FPU; \
29 (prev)->thread.uregs->epsw &= ~EPSW_FE; \
30 fpu_save(&(prev)->thread.fpu_state); \
31 } \
32 } while (0)
33#else
34#define switch_fpu(prev, next) do {} while (0)
35#endif
36
22struct task_struct; 37struct task_struct;
23struct thread_struct; 38struct thread_struct;
24 39
@@ -30,6 +45,7 @@ struct task_struct *__switch_to(struct thread_struct *prev,
30/* context switching is now performed out-of-line in switch_to.S */ 45/* context switching is now performed out-of-line in switch_to.S */
31#define switch_to(prev, next, last) \ 46#define switch_to(prev, next, last) \
32do { \ 47do { \
48 switch_fpu(prev, next); \
33 current->thread.wchan = (u_long) __builtin_return_address(0); \ 49 current->thread.wchan = (u_long) __builtin_return_address(0); \
34 (last) = __switch_to(&(prev)->thread, &(next)->thread, (prev)); \ 50 (last) = __switch_to(&(prev)->thread, &(next)->thread, (prev)); \
35 mb(); \ 51 mb(); \
diff --git a/arch/mn10300/kernel/Makefile b/arch/mn10300/kernel/Makefile
index c4289e388071..99022351717a 100644
--- a/arch/mn10300/kernel/Makefile
+++ b/arch/mn10300/kernel/Makefile
@@ -3,13 +3,15 @@
3# 3#
4extra-y := head.o init_task.o vmlinux.lds 4extra-y := head.o init_task.o vmlinux.lds
5 5
6obj-y := process.o signal.o entry.o fpu.o traps.o irq.o \ 6fpu-obj-y := fpu-nofpu.o fpu-nofpu-low.o
7fpu-obj-$(CONFIG_FPU) := fpu.o fpu-low.o
8
9obj-y := process.o signal.o entry.o traps.o irq.o \
7 ptrace.o setup.o time.o sys_mn10300.o io.o kthread.o \ 10 ptrace.o setup.o time.o sys_mn10300.o io.o kthread.o \
8 switch_to.o mn10300_ksyms.o kernel_execve.o 11 switch_to.o mn10300_ksyms.o kernel_execve.o $(fpu-obj-y)
9 12
10obj-$(CONFIG_MN10300_WD_TIMER) += mn10300-watchdog.o mn10300-watchdog-low.o 13obj-$(CONFIG_MN10300_WD_TIMER) += mn10300-watchdog.o mn10300-watchdog-low.o
11 14
12obj-$(CONFIG_FPU) += fpu-low.o
13 15
14obj-$(CONFIG_MN10300_TTYSM) += mn10300-serial.o mn10300-serial-low.o \ 16obj-$(CONFIG_MN10300_TTYSM) += mn10300-serial.o mn10300-serial-low.o \
15 mn10300-debug.o 17 mn10300-debug.o
diff --git a/arch/mn10300/kernel/asm-offsets.c b/arch/mn10300/kernel/asm-offsets.c
index 02dc7e461fef..78e290e342fc 100644
--- a/arch/mn10300/kernel/asm-offsets.c
+++ b/arch/mn10300/kernel/asm-offsets.c
@@ -67,6 +67,15 @@ void foo(void)
67 OFFSET(THREAD_A3, thread_struct, a3); 67 OFFSET(THREAD_A3, thread_struct, a3);
68 OFFSET(THREAD_USP, thread_struct, usp); 68 OFFSET(THREAD_USP, thread_struct, usp);
69 OFFSET(THREAD_FRAME, thread_struct, __frame); 69 OFFSET(THREAD_FRAME, thread_struct, __frame);
70#ifdef CONFIG_FPU
71 OFFSET(THREAD_FPU_FLAGS, thread_struct, fpu_flags);
72 OFFSET(THREAD_FPU_STATE, thread_struct, fpu_state);
73 DEFINE(__THREAD_USING_FPU, THREAD_USING_FPU);
74 DEFINE(__THREAD_HAS_FPU, THREAD_HAS_FPU);
75#endif /* CONFIG_FPU */
76 BLANK();
77
78 OFFSET(TASK_THREAD, task_struct, thread);
70 BLANK(); 79 BLANK();
71 80
72 DEFINE(CLONE_VM_asm, CLONE_VM); 81 DEFINE(CLONE_VM_asm, CLONE_VM);
diff --git a/arch/mn10300/kernel/fpu-low.S b/arch/mn10300/kernel/fpu-low.S
index 96cfd47e68d5..78df25cfae29 100644
--- a/arch/mn10300/kernel/fpu-low.S
+++ b/arch/mn10300/kernel/fpu-low.S
@@ -8,25 +8,14 @@
8 * as published by the Free Software Foundation; either version 8 * as published by the Free Software Foundation; either version
9 * 2 of the Licence, or (at your option) any later version. 9 * 2 of the Licence, or (at your option) any later version.
10 */ 10 */
11#include <linux/linkage.h>
11#include <asm/cpu-regs.h> 12#include <asm/cpu-regs.h>
13#include <asm/smp.h>
14#include <asm/thread_info.h>
15#include <asm/asm-offsets.h>
16#include <asm/frame.inc>
12 17
13############################################################################### 18.macro FPU_INIT_STATE_ALL
14#
15# void fpu_init_state(void)
16# - initialise the FPU
17#
18###############################################################################
19 .globl fpu_init_state
20 .type fpu_init_state,@function
21fpu_init_state:
22 mov epsw,d0
23 or EPSW_FE,epsw
24
25#ifdef CONFIG_MN10300_PROC_MN103E010
26 nop
27 nop
28 nop
29#endif
30 fmov 0,fs0 19 fmov 0,fs0
31 fmov fs0,fs1 20 fmov fs0,fs1
32 fmov fs0,fs2 21 fmov fs0,fs2
@@ -60,7 +49,100 @@ fpu_init_state:
60 fmov fs0,fs30 49 fmov fs0,fs30
61 fmov fs0,fs31 50 fmov fs0,fs31
62 fmov FPCR_INIT,fpcr 51 fmov FPCR_INIT,fpcr
52.endm
53
54.macro FPU_SAVE_ALL areg,dreg
55 fmov fs0,(\areg+)
56 fmov fs1,(\areg+)
57 fmov fs2,(\areg+)
58 fmov fs3,(\areg+)
59 fmov fs4,(\areg+)
60 fmov fs5,(\areg+)
61 fmov fs6,(\areg+)
62 fmov fs7,(\areg+)
63 fmov fs8,(\areg+)
64 fmov fs9,(\areg+)
65 fmov fs10,(\areg+)
66 fmov fs11,(\areg+)
67 fmov fs12,(\areg+)
68 fmov fs13,(\areg+)
69 fmov fs14,(\areg+)
70 fmov fs15,(\areg+)
71 fmov fs16,(\areg+)
72 fmov fs17,(\areg+)
73 fmov fs18,(\areg+)
74 fmov fs19,(\areg+)
75 fmov fs20,(\areg+)
76 fmov fs21,(\areg+)
77 fmov fs22,(\areg+)
78 fmov fs23,(\areg+)
79 fmov fs24,(\areg+)
80 fmov fs25,(\areg+)
81 fmov fs26,(\areg+)
82 fmov fs27,(\areg+)
83 fmov fs28,(\areg+)
84 fmov fs29,(\areg+)
85 fmov fs30,(\areg+)
86 fmov fs31,(\areg+)
87 fmov fpcr,\dreg
88 mov \dreg,(\areg)
89.endm
90
91.macro FPU_RESTORE_ALL areg,dreg
92 fmov (\areg+),fs0
93 fmov (\areg+),fs1
94 fmov (\areg+),fs2
95 fmov (\areg+),fs3
96 fmov (\areg+),fs4
97 fmov (\areg+),fs5
98 fmov (\areg+),fs6
99 fmov (\areg+),fs7
100 fmov (\areg+),fs8
101 fmov (\areg+),fs9
102 fmov (\areg+),fs10
103 fmov (\areg+),fs11
104 fmov (\areg+),fs12
105 fmov (\areg+),fs13
106 fmov (\areg+),fs14
107 fmov (\areg+),fs15
108 fmov (\areg+),fs16
109 fmov (\areg+),fs17
110 fmov (\areg+),fs18
111 fmov (\areg+),fs19
112 fmov (\areg+),fs20
113 fmov (\areg+),fs21
114 fmov (\areg+),fs22
115 fmov (\areg+),fs23
116 fmov (\areg+),fs24
117 fmov (\areg+),fs25
118 fmov (\areg+),fs26
119 fmov (\areg+),fs27
120 fmov (\areg+),fs28
121 fmov (\areg+),fs29
122 fmov (\areg+),fs30
123 fmov (\areg+),fs31
124 mov (\areg),\dreg
125 fmov \dreg,fpcr
126.endm
63 127
128###############################################################################
129#
130# void fpu_init_state(void)
131# - initialise the FPU
132#
133###############################################################################
134 .globl fpu_init_state
135 .type fpu_init_state,@function
136fpu_init_state:
137 mov epsw,d0
138 or EPSW_FE,epsw
139
140#ifdef CONFIG_MN10300_PROC_MN103E010
141 nop
142 nop
143 nop
144#endif
145 FPU_INIT_STATE_ALL
64#ifdef CONFIG_MN10300_PROC_MN103E010 146#ifdef CONFIG_MN10300_PROC_MN103E010
65 nop 147 nop
66 nop 148 nop
@@ -89,40 +171,7 @@ fpu_save:
89 nop 171 nop
90#endif 172#endif
91 mov d0,a0 173 mov d0,a0
92 fmov fs0,(a0+) 174 FPU_SAVE_ALL a0,d0
93 fmov fs1,(a0+)
94 fmov fs2,(a0+)
95 fmov fs3,(a0+)
96 fmov fs4,(a0+)
97 fmov fs5,(a0+)
98 fmov fs6,(a0+)
99 fmov fs7,(a0+)
100 fmov fs8,(a0+)
101 fmov fs9,(a0+)
102 fmov fs10,(a0+)
103 fmov fs11,(a0+)
104 fmov fs12,(a0+)
105 fmov fs13,(a0+)
106 fmov fs14,(a0+)
107 fmov fs15,(a0+)
108 fmov fs16,(a0+)
109 fmov fs17,(a0+)
110 fmov fs18,(a0+)
111 fmov fs19,(a0+)
112 fmov fs20,(a0+)
113 fmov fs21,(a0+)
114 fmov fs22,(a0+)
115 fmov fs23,(a0+)
116 fmov fs24,(a0+)
117 fmov fs25,(a0+)
118 fmov fs26,(a0+)
119 fmov fs27,(a0+)
120 fmov fs28,(a0+)
121 fmov fs29,(a0+)
122 fmov fs30,(a0+)
123 fmov fs31,(a0+)
124 fmov fpcr,d0
125 mov d0,(a0)
126#ifdef CONFIG_MN10300_PROC_MN103E010 175#ifdef CONFIG_MN10300_PROC_MN103E010
127 nop 176 nop
128 nop 177 nop
@@ -135,63 +184,75 @@ fpu_save:
135 184
136############################################################################### 185###############################################################################
137# 186#
138# void fpu_restore(struct fpu_state_struct *) 187# void fpu_disabled(void)
139# - restore the fpu state 188# - handle an exception due to the FPU being disabled
140# - note that an FPU Operational exception might occur during this process 189# when CONFIG_FPU is enabled
141# 190#
142############################################################################### 191###############################################################################
143 .globl fpu_restore 192 .type fpu_disabled,@function
144 .type fpu_restore,@function 193 .globl fpu_disabled
145fpu_restore: 194fpu_disabled:
146 mov epsw,d1 195 or EPSW_nAR|EPSW_FE,epsw
147 or EPSW_FE,epsw /* enable the FPU so we can access it */
148
149#ifdef CONFIG_MN10300_PROC_MN103E010
150 nop 196 nop
151 nop 197 nop
152#endif
153 mov d0,a0
154 fmov (a0+),fs0
155 fmov (a0+),fs1
156 fmov (a0+),fs2
157 fmov (a0+),fs3
158 fmov (a0+),fs4
159 fmov (a0+),fs5
160 fmov (a0+),fs6
161 fmov (a0+),fs7
162 fmov (a0+),fs8
163 fmov (a0+),fs9
164 fmov (a0+),fs10
165 fmov (a0+),fs11
166 fmov (a0+),fs12
167 fmov (a0+),fs13
168 fmov (a0+),fs14
169 fmov (a0+),fs15
170 fmov (a0+),fs16
171 fmov (a0+),fs17
172 fmov (a0+),fs18
173 fmov (a0+),fs19
174 fmov (a0+),fs20
175 fmov (a0+),fs21
176 fmov (a0+),fs22
177 fmov (a0+),fs23
178 fmov (a0+),fs24
179 fmov (a0+),fs25
180 fmov (a0+),fs26
181 fmov (a0+),fs27
182 fmov (a0+),fs28
183 fmov (a0+),fs29
184 fmov (a0+),fs30
185 fmov (a0+),fs31
186 mov (a0),d0
187 fmov d0,fpcr
188#ifdef CONFIG_MN10300_PROC_MN103E010
189 nop 198 nop
199
200 mov sp,a1
201 mov (a1),d1 /* get epsw of user context */
202 and ~(THREAD_SIZE-1),a1 /* a1: (thread_info *ti) */
203 mov (TI_task,a1),a2 /* a2: (task_struct *tsk) */
204 btst EPSW_nSL,d1
205 beq fpu_used_in_kernel
206
207 or EPSW_FE,d1
208 mov d1,(sp)
209 mov (TASK_THREAD+THREAD_FPU_FLAGS,a2),d1
210#ifndef CONFIG_LAZY_SAVE_FPU
211 or __THREAD_HAS_FPU,d1
212 mov d1,(TASK_THREAD+THREAD_FPU_FLAGS,a2)
213#else /* !CONFIG_LAZY_SAVE_FPU */
214 mov (fpu_state_owner),a0
215 cmp 0,a0
216 beq fpu_regs_save_end
217
218 mov (TASK_THREAD+THREAD_UREGS,a0),a1
219 add TASK_THREAD+THREAD_FPU_STATE,a0
220 FPU_SAVE_ALL a0,d0
221
222 mov (REG_EPSW,a1),d0
223 and ~EPSW_FE,d0
224 mov d0,(REG_EPSW,a1)
225
226fpu_regs_save_end:
227 mov a2,(fpu_state_owner)
228#endif /* !CONFIG_LAZY_SAVE_FPU */
229
230 btst __THREAD_USING_FPU,d1
231 beq fpu_regs_init
232 add TASK_THREAD+THREAD_FPU_STATE,a2
233 FPU_RESTORE_ALL a2,d0
234 rti
235
236fpu_regs_init:
237 FPU_INIT_STATE_ALL
238 add TASK_THREAD+THREAD_FPU_FLAGS,a2
239 bset __THREAD_USING_FPU,(0,a2)
240 rti
241
242fpu_used_in_kernel:
243 and ~(EPSW_nAR|EPSW_FE),epsw
190 nop 244 nop
191 nop 245 nop
192#endif
193 246
194 mov d1,epsw 247 add -4,sp
195 ret [],0 248 SAVE_ALL
249 mov -1,d0
250 mov d0,(REG_ORIG_D0,fp)
251
252 and ~EPSW_NMID,epsw
253
254 mov fp,d0
255 call fpu_disabled_in_kernel[],0
256 jmp ret_from_exception
196 257
197 .size fpu_restore,.-fpu_restore 258 .size fpu_disabled,.-fpu_disabled
diff --git a/arch/mn10300/kernel/fpu-nofpu-low.S b/arch/mn10300/kernel/fpu-nofpu-low.S
new file mode 100644
index 000000000000..7ea087a549f4
--- /dev/null
+++ b/arch/mn10300/kernel/fpu-nofpu-low.S
@@ -0,0 +1,39 @@
1/* MN10300 Low level FPU management operations
2 *
3 * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
4 * Written by David Howells (dhowells@redhat.com)
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public Licence
8 * as published by the Free Software Foundation; either version
9 * 2 of the Licence, or (at your option) any later version.
10 */
11#include <linux/linkage.h>
12#include <asm/cpu-regs.h>
13#include <asm/smp.h>
14#include <asm/thread_info.h>
15#include <asm/asm-offsets.h>
16#include <asm/frame.inc>
17
18###############################################################################
19#
20# void fpu_disabled(void)
21# - handle an exception due to the FPU being disabled
22# when CONFIG_FPU is disabled
23#
24###############################################################################
25 .type fpu_disabled,@function
26 .globl fpu_disabled
27fpu_disabled:
28 add -4,sp
29 SAVE_ALL
30 mov -1,d0
31 mov d0,(REG_ORIG_D0,fp)
32
33 and ~EPSW_NMID,epsw
34
35 mov fp,d0
36 call unexpected_fpu_exception[],0
37 jmp ret_from_exception
38
39 .size fpu_disabled,.-fpu_disabled
diff --git a/arch/mn10300/kernel/fpu-nofpu.c b/arch/mn10300/kernel/fpu-nofpu.c
new file mode 100644
index 000000000000..31c765b92c5d
--- /dev/null
+++ b/arch/mn10300/kernel/fpu-nofpu.c
@@ -0,0 +1,30 @@
1/* MN10300 FPU management
2 *
3 * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
4 * Written by David Howells (dhowells@redhat.com)
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public Licence
8 * as published by the Free Software Foundation; either version
9 * 2 of the Licence, or (at your option) any later version.
10 */
11#include <asm/fpu.h>
12
13/*
14 * handle an FPU operational exception
15 * - there's a possibility that if the FPU is asynchronous, the signal might
16 * be meant for a process other than the current one
17 */
18asmlinkage
19void unexpected_fpu_exception(struct pt_regs *regs, enum exception_code code)
20{
21 panic("An FPU exception was received, but there's no FPU enabled.");
22}
23
24/*
25 * fill in the FPU structure for a core dump
26 */
27int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpreg)
28{
29 return 0; /* not valid */
30}
diff --git a/arch/mn10300/kernel/fpu.c b/arch/mn10300/kernel/fpu.c
index e705f25ad5ff..5f9c3fa19a85 100644
--- a/arch/mn10300/kernel/fpu.c
+++ b/arch/mn10300/kernel/fpu.c
@@ -12,56 +12,19 @@
12#include <asm/fpu.h> 12#include <asm/fpu.h>
13#include <asm/elf.h> 13#include <asm/elf.h>
14#include <asm/exceptions.h> 14#include <asm/exceptions.h>
15#include <asm/system.h>
15 16
17#ifdef CONFIG_LAZY_SAVE_FPU
16struct task_struct *fpu_state_owner; 18struct task_struct *fpu_state_owner;
19#endif
17 20
18/* 21/*
19 * handle an exception due to the FPU being disabled 22 * error functions in FPU disabled exception
20 */ 23 */
21asmlinkage void fpu_disabled(struct pt_regs *regs, enum exception_code code) 24asmlinkage void fpu_disabled_in_kernel(struct pt_regs *regs)
22{ 25{
23 struct task_struct *tsk = current; 26 die_if_no_fixup("An FPU Disabled exception happened in kernel space\n",
24 27 regs, EXCEP_FPU_DISABLED);
25 if (!user_mode(regs))
26 die_if_no_fixup("An FPU Disabled exception happened in"
27 " kernel space\n",
28 regs, code);
29
30#ifdef CONFIG_FPU
31 preempt_disable();
32
33 /* transfer the last process's FPU state to memory */
34 if (fpu_state_owner) {
35 fpu_save(&fpu_state_owner->thread.fpu_state);
36 fpu_state_owner->thread.uregs->epsw &= ~EPSW_FE;
37 }
38
39 /* the current process now owns the FPU state */
40 fpu_state_owner = tsk;
41 regs->epsw |= EPSW_FE;
42
43 /* load the FPU with the current process's FPU state or invent a new
44 * clean one if the process doesn't have one */
45 if (is_using_fpu(tsk)) {
46 fpu_restore(&tsk->thread.fpu_state);
47 } else {
48 fpu_init_state();
49 set_using_fpu(tsk);
50 }
51
52 preempt_enable();
53#else
54 {
55 siginfo_t info;
56
57 info.si_signo = SIGFPE;
58 info.si_errno = 0;
59 info.si_addr = (void *) tsk->thread.uregs->pc;
60 info.si_code = FPE_FLTINV;
61
62 force_sig_info(SIGFPE, &info, tsk);
63 }
64#endif /* CONFIG_FPU */
65} 28}
66 29
67/* 30/*
@@ -71,15 +34,16 @@ asmlinkage void fpu_disabled(struct pt_regs *regs, enum exception_code code)
71 */ 34 */
72asmlinkage void fpu_exception(struct pt_regs *regs, enum exception_code code) 35asmlinkage void fpu_exception(struct pt_regs *regs, enum exception_code code)
73{ 36{
74 struct task_struct *tsk = fpu_state_owner; 37 struct task_struct *tsk = current;
75 siginfo_t info; 38 siginfo_t info;
39 u32 fpcr;
76 40
77 if (!user_mode(regs)) 41 if (!user_mode(regs))
78 die_if_no_fixup("An FPU Operation exception happened in" 42 die_if_no_fixup("An FPU Operation exception happened in"
79 " kernel space\n", 43 " kernel space\n",
80 regs, code); 44 regs, code);
81 45
82 if (!tsk) 46 if (!is_using_fpu(tsk))
83 die_if_no_fixup("An FPU Operation exception happened," 47 die_if_no_fixup("An FPU Operation exception happened,"
84 " but the FPU is not in use", 48 " but the FPU is not in use",
85 regs, code); 49 regs, code);
@@ -89,48 +53,45 @@ asmlinkage void fpu_exception(struct pt_regs *regs, enum exception_code code)
89 info.si_addr = (void *) tsk->thread.uregs->pc; 53 info.si_addr = (void *) tsk->thread.uregs->pc;
90 info.si_code = FPE_FLTINV; 54 info.si_code = FPE_FLTINV;
91 55
92#ifdef CONFIG_FPU 56 unlazy_fpu(tsk);
93 {
94 u32 fpcr;
95 57
96 /* get FPCR (we need to enable the FPU whilst we do this) */ 58 fpcr = tsk->thread.fpu_state.fpcr;
97 asm volatile(" or %1,epsw \n" 59
98#ifdef CONFIG_MN10300_PROC_MN103E010 60 if (fpcr & FPCR_EC_Z)
99 " nop \n" 61 info.si_code = FPE_FLTDIV;
100 " nop \n" 62 else if (fpcr & FPCR_EC_O)
101 " nop \n" 63 info.si_code = FPE_FLTOVF;
102#endif 64 else if (fpcr & FPCR_EC_U)
103 " fmov fpcr,%0 \n" 65 info.si_code = FPE_FLTUND;
104#ifdef CONFIG_MN10300_PROC_MN103E010 66 else if (fpcr & FPCR_EC_I)
105 " nop \n" 67 info.si_code = FPE_FLTRES;
106 " nop \n"
107 " nop \n"
108#endif
109 " and %2,epsw \n"
110 : "=&d"(fpcr)
111 : "i"(EPSW_FE), "i"(~EPSW_FE)
112 );
113
114 if (fpcr & FPCR_EC_Z)
115 info.si_code = FPE_FLTDIV;
116 else if (fpcr & FPCR_EC_O)
117 info.si_code = FPE_FLTOVF;
118 else if (fpcr & FPCR_EC_U)
119 info.si_code = FPE_FLTUND;
120 else if (fpcr & FPCR_EC_I)
121 info.si_code = FPE_FLTRES;
122 }
123#endif
124 68
125 force_sig_info(SIGFPE, &info, tsk); 69 force_sig_info(SIGFPE, &info, tsk);
126} 70}
127 71
128/* 72/*
73 * handle an FPU invalid_op exception
74 * - Derived from DO_EINFO() macro in arch/mn10300/kernel/traps.c
75 */
76asmlinkage void fpu_invalid_op(struct pt_regs *regs, enum exception_code code)
77{
78 siginfo_t info;
79
80 if (!user_mode(regs))
81 die_if_no_fixup("FPU invalid opcode", regs, code);
82
83 info.si_signo = SIGILL;
84 info.si_errno = 0;
85 info.si_code = ILL_COPROC;
86 info.si_addr = (void *) regs->pc;
87 force_sig_info(info.si_signo, &info, current);
88}
89
90/*
129 * save the FPU state to a signal context 91 * save the FPU state to a signal context
130 */ 92 */
131int fpu_setup_sigcontext(struct fpucontext *fpucontext) 93int fpu_setup_sigcontext(struct fpucontext *fpucontext)
132{ 94{
133#ifdef CONFIG_FPU
134 struct task_struct *tsk = current; 95 struct task_struct *tsk = current;
135 96
136 if (!is_using_fpu(tsk)) 97 if (!is_using_fpu(tsk))
@@ -142,11 +103,19 @@ int fpu_setup_sigcontext(struct fpucontext *fpucontext)
142 */ 103 */
143 preempt_disable(); 104 preempt_disable();
144 105
106#ifndef CONFIG_LAZY_SAVE_FPU
107 if (tsk->thread.fpu_flags & THREAD_HAS_FPU) {
108 fpu_save(&tsk->thread.fpu_state);
109 tsk->thread.uregs->epsw &= ~EPSW_FE;
110 tsk->thread.fpu_flags &= ~THREAD_HAS_FPU;
111 }
112#else /* !CONFIG_LAZY_SAVE_FPU */
145 if (fpu_state_owner == tsk) { 113 if (fpu_state_owner == tsk) {
146 fpu_save(&tsk->thread.fpu_state); 114 fpu_save(&tsk->thread.fpu_state);
147 fpu_state_owner->thread.uregs->epsw &= ~EPSW_FE; 115 fpu_state_owner->thread.uregs->epsw &= ~EPSW_FE;
148 fpu_state_owner = NULL; 116 fpu_state_owner = NULL;
149 } 117 }
118#endif /* !CONFIG_LAZY_SAVE_FPU */
150 119
151 preempt_enable(); 120 preempt_enable();
152 121
@@ -161,9 +130,6 @@ int fpu_setup_sigcontext(struct fpucontext *fpucontext)
161 return -1; 130 return -1;
162 131
163 return 1; 132 return 1;
164#else
165 return 0;
166#endif
167} 133}
168 134
169/* 135/*
@@ -171,17 +137,23 @@ int fpu_setup_sigcontext(struct fpucontext *fpucontext)
171 */ 137 */
172void fpu_kill_state(struct task_struct *tsk) 138void fpu_kill_state(struct task_struct *tsk)
173{ 139{
174#ifdef CONFIG_FPU
175 /* disown anything left in the FPU */ 140 /* disown anything left in the FPU */
176 preempt_disable(); 141 preempt_disable();
177 142
143#ifndef CONFIG_LAZY_SAVE_FPU
144 if (tsk->thread.fpu_flags & THREAD_HAS_FPU) {
145 tsk->thread.uregs->epsw &= ~EPSW_FE;
146 tsk->thread.fpu_flags &= ~THREAD_HAS_FPU;
147 }
148#else /* !CONFIG_LAZY_SAVE_FPU */
178 if (fpu_state_owner == tsk) { 149 if (fpu_state_owner == tsk) {
179 fpu_state_owner->thread.uregs->epsw &= ~EPSW_FE; 150 fpu_state_owner->thread.uregs->epsw &= ~EPSW_FE;
180 fpu_state_owner = NULL; 151 fpu_state_owner = NULL;
181 } 152 }
153#endif /* !CONFIG_LAZY_SAVE_FPU */
182 154
183 preempt_enable(); 155 preempt_enable();
184#endif 156
185 /* we no longer have a valid current FPU state */ 157 /* we no longer have a valid current FPU state */
186 clear_using_fpu(tsk); 158 clear_using_fpu(tsk);
187} 159}
@@ -195,8 +167,7 @@ int fpu_restore_sigcontext(struct fpucontext *fpucontext)
195 int ret; 167 int ret;
196 168
197 /* load up the old FPU state */ 169 /* load up the old FPU state */
198 ret = copy_from_user(&tsk->thread.fpu_state, 170 ret = copy_from_user(&tsk->thread.fpu_state, fpucontext,
199 fpucontext,
200 min(sizeof(struct fpu_state_struct), 171 min(sizeof(struct fpu_state_struct),
201 sizeof(struct fpucontext))); 172 sizeof(struct fpucontext)));
202 if (!ret) 173 if (!ret)
diff --git a/arch/mn10300/kernel/traps.c b/arch/mn10300/kernel/traps.c
index c7257a1304a9..716a221df2f9 100644
--- a/arch/mn10300/kernel/traps.c
+++ b/arch/mn10300/kernel/traps.c
@@ -101,7 +101,6 @@ DO_EINFO(SIGILL, {}, "invalid opcode", invalid_op, ILL_ILLOPC);
101DO_EINFO(SIGILL, {}, "invalid ex opcode", invalid_exop, ILL_ILLOPC); 101DO_EINFO(SIGILL, {}, "invalid ex opcode", invalid_exop, ILL_ILLOPC);
102DO_EINFO(SIGBUS, {}, "invalid address", mem_error, BUS_ADRERR); 102DO_EINFO(SIGBUS, {}, "invalid address", mem_error, BUS_ADRERR);
103DO_EINFO(SIGBUS, {}, "bus error", bus_error, BUS_ADRERR); 103DO_EINFO(SIGBUS, {}, "bus error", bus_error, BUS_ADRERR);
104DO_EINFO(SIGILL, {}, "FPU invalid opcode", fpu_invalid_op, ILL_COPROC);
105 104
106DO_ERROR(SIGTRAP, 105DO_ERROR(SIGTRAP,
107#ifndef CONFIG_MN10300_USING_JTAG 106#ifndef CONFIG_MN10300_USING_JTAG
@@ -561,7 +560,6 @@ void __init trap_init(void)
561 set_excp_vector(EXCEP_PRIVINSACC, insn_acc_error); 560 set_excp_vector(EXCEP_PRIVINSACC, insn_acc_error);
562 set_excp_vector(EXCEP_PRIVDATACC, data_acc_error); 561 set_excp_vector(EXCEP_PRIVDATACC, data_acc_error);
563 set_excp_vector(EXCEP_DATINSACC, insn_acc_error); 562 set_excp_vector(EXCEP_DATINSACC, insn_acc_error);
564 set_excp_vector(EXCEP_FPU_DISABLED, fpu_disabled);
565 set_excp_vector(EXCEP_FPU_UNIMPINS, fpu_invalid_op); 563 set_excp_vector(EXCEP_FPU_UNIMPINS, fpu_invalid_op);
566 set_excp_vector(EXCEP_FPU_OPERATION, fpu_exception); 564 set_excp_vector(EXCEP_FPU_OPERATION, fpu_exception);
567 565
diff --git a/arch/mn10300/proc-mn103e010/proc-init.c b/arch/mn10300/proc-mn103e010/proc-init.c
index 9a482efafa82..0cee7878bee9 100644
--- a/arch/mn10300/proc-mn103e010/proc-init.c
+++ b/arch/mn10300/proc-mn103e010/proc-init.c
@@ -9,6 +9,7 @@
9 * 2 of the Licence, or (at your option) any later version. 9 * 2 of the Licence, or (at your option) any later version.
10 */ 10 */
11#include <linux/kernel.h> 11#include <linux/kernel.h>
12#include <asm/fpu.h>
12#include <asm/rtc.h> 13#include <asm/rtc.h>
13 14
14/* 15/*
@@ -28,6 +29,7 @@ asmlinkage void __init processor_init(void)
28 __set_intr_stub(EXCEP_DAERROR, dtlb_aerror); 29 __set_intr_stub(EXCEP_DAERROR, dtlb_aerror);
29 __set_intr_stub(EXCEP_BUSERROR, raw_bus_error); 30 __set_intr_stub(EXCEP_BUSERROR, raw_bus_error);
30 __set_intr_stub(EXCEP_DOUBLE_FAULT, double_fault); 31 __set_intr_stub(EXCEP_DOUBLE_FAULT, double_fault);
32 __set_intr_stub(EXCEP_FPU_DISABLED, fpu_disabled);
31 __set_intr_stub(EXCEP_SYSCALL0, system_call); 33 __set_intr_stub(EXCEP_SYSCALL0, system_call);
32 34
33 __set_intr_stub(EXCEP_NMI, nmi_handler); 35 __set_intr_stub(EXCEP_NMI, nmi_handler);