diff options
Diffstat (limited to 'arch/mips/kernel/signal.c')
-rw-r--r-- | arch/mips/kernel/signal.c | 170 |
1 files changed, 148 insertions, 22 deletions
diff --git a/arch/mips/kernel/signal.c b/arch/mips/kernel/signal.c index 5199563c4403..33133d3df3e5 100644 --- a/arch/mips/kernel/signal.c +++ b/arch/mips/kernel/signal.c | |||
@@ -6,6 +6,7 @@ | |||
6 | * Copyright (C) 1991, 1992 Linus Torvalds | 6 | * Copyright (C) 1991, 1992 Linus Torvalds |
7 | * Copyright (C) 1994 - 2000 Ralf Baechle | 7 | * Copyright (C) 1994 - 2000 Ralf Baechle |
8 | * Copyright (C) 1999, 2000 Silicon Graphics, Inc. | 8 | * Copyright (C) 1999, 2000 Silicon Graphics, Inc. |
9 | * Copyright (C) 2014, Imagination Technologies Ltd. | ||
9 | */ | 10 | */ |
10 | #include <linux/cache.h> | 11 | #include <linux/cache.h> |
11 | #include <linux/context_tracking.h> | 12 | #include <linux/context_tracking.h> |
@@ -30,6 +31,7 @@ | |||
30 | #include <linux/bitops.h> | 31 | #include <linux/bitops.h> |
31 | #include <asm/cacheflush.h> | 32 | #include <asm/cacheflush.h> |
32 | #include <asm/fpu.h> | 33 | #include <asm/fpu.h> |
34 | #include <asm/msa.h> | ||
33 | #include <asm/sim.h> | 35 | #include <asm/sim.h> |
34 | #include <asm/ucontext.h> | 36 | #include <asm/ucontext.h> |
35 | #include <asm/cpu-features.h> | 37 | #include <asm/cpu-features.h> |
@@ -46,8 +48,8 @@ static int (*restore_fp_context)(struct sigcontext __user *sc); | |||
46 | extern asmlinkage int _save_fp_context(struct sigcontext __user *sc); | 48 | extern asmlinkage int _save_fp_context(struct sigcontext __user *sc); |
47 | extern asmlinkage int _restore_fp_context(struct sigcontext __user *sc); | 49 | extern asmlinkage int _restore_fp_context(struct sigcontext __user *sc); |
48 | 50 | ||
49 | extern asmlinkage int fpu_emulator_save_context(struct sigcontext __user *sc); | 51 | extern asmlinkage int _save_msa_context(struct sigcontext __user *sc); |
50 | extern asmlinkage int fpu_emulator_restore_context(struct sigcontext __user *sc); | 52 | extern asmlinkage int _restore_msa_context(struct sigcontext __user *sc); |
51 | 53 | ||
52 | struct sigframe { | 54 | struct sigframe { |
53 | u32 sf_ass[4]; /* argument save space for o32 */ | 55 | u32 sf_ass[4]; /* argument save space for o32 */ |
@@ -64,17 +66,95 @@ struct rt_sigframe { | |||
64 | }; | 66 | }; |
65 | 67 | ||
66 | /* | 68 | /* |
69 | * Thread saved context copy to/from a signal context presumed to be on the | ||
70 | * user stack, and therefore accessed with appropriate macros from uaccess.h. | ||
71 | */ | ||
72 | static int copy_fp_to_sigcontext(struct sigcontext __user *sc) | ||
73 | { | ||
74 | int i; | ||
75 | int err = 0; | ||
76 | |||
77 | for (i = 0; i < NUM_FPU_REGS; i++) { | ||
78 | err |= | ||
79 | __put_user(get_fpr64(¤t->thread.fpu.fpr[i], 0), | ||
80 | &sc->sc_fpregs[i]); | ||
81 | } | ||
82 | err |= __put_user(current->thread.fpu.fcr31, &sc->sc_fpc_csr); | ||
83 | |||
84 | return err; | ||
85 | } | ||
86 | |||
87 | static int copy_fp_from_sigcontext(struct sigcontext __user *sc) | ||
88 | { | ||
89 | int i; | ||
90 | int err = 0; | ||
91 | u64 fpr_val; | ||
92 | |||
93 | for (i = 0; i < NUM_FPU_REGS; i++) { | ||
94 | err |= __get_user(fpr_val, &sc->sc_fpregs[i]); | ||
95 | set_fpr64(¤t->thread.fpu.fpr[i], 0, fpr_val); | ||
96 | } | ||
97 | err |= __get_user(current->thread.fpu.fcr31, &sc->sc_fpc_csr); | ||
98 | |||
99 | return err; | ||
100 | } | ||
101 | |||
102 | /* | ||
103 | * These functions will save only the upper 64 bits of the vector registers, | ||
104 | * since the lower 64 bits have already been saved as the scalar FP context. | ||
105 | */ | ||
106 | static int copy_msa_to_sigcontext(struct sigcontext __user *sc) | ||
107 | { | ||
108 | int i; | ||
109 | int err = 0; | ||
110 | |||
111 | for (i = 0; i < NUM_FPU_REGS; i++) { | ||
112 | err |= | ||
113 | __put_user(get_fpr64(¤t->thread.fpu.fpr[i], 1), | ||
114 | &sc->sc_msaregs[i]); | ||
115 | } | ||
116 | err |= __put_user(current->thread.fpu.msacsr, &sc->sc_msa_csr); | ||
117 | |||
118 | return err; | ||
119 | } | ||
120 | |||
121 | static int copy_msa_from_sigcontext(struct sigcontext __user *sc) | ||
122 | { | ||
123 | int i; | ||
124 | int err = 0; | ||
125 | u64 val; | ||
126 | |||
127 | for (i = 0; i < NUM_FPU_REGS; i++) { | ||
128 | err |= __get_user(val, &sc->sc_msaregs[i]); | ||
129 | set_fpr64(¤t->thread.fpu.fpr[i], 1, val); | ||
130 | } | ||
131 | err |= __get_user(current->thread.fpu.msacsr, &sc->sc_msa_csr); | ||
132 | |||
133 | return err; | ||
134 | } | ||
135 | |||
136 | /* | ||
67 | * Helper routines | 137 | * Helper routines |
68 | */ | 138 | */ |
69 | static int protected_save_fp_context(struct sigcontext __user *sc) | 139 | static int protected_save_fp_context(struct sigcontext __user *sc, |
140 | unsigned used_math) | ||
70 | { | 141 | { |
71 | int err; | 142 | int err; |
143 | bool save_msa = cpu_has_msa && (used_math & USEDMATH_MSA); | ||
144 | #ifndef CONFIG_EVA | ||
72 | while (1) { | 145 | while (1) { |
73 | lock_fpu_owner(); | 146 | lock_fpu_owner(); |
74 | err = own_fpu_inatomic(1); | 147 | if (is_fpu_owner()) { |
75 | if (!err) | 148 | err = save_fp_context(sc); |
76 | err = save_fp_context(sc); /* this might fail */ | 149 | if (save_msa && !err) |
77 | unlock_fpu_owner(); | 150 | err = _save_msa_context(sc); |
151 | unlock_fpu_owner(); | ||
152 | } else { | ||
153 | unlock_fpu_owner(); | ||
154 | err = copy_fp_to_sigcontext(sc); | ||
155 | if (save_msa && !err) | ||
156 | err = copy_msa_to_sigcontext(sc); | ||
157 | } | ||
78 | if (likely(!err)) | 158 | if (likely(!err)) |
79 | break; | 159 | break; |
80 | /* touch the sigcontext and try again */ | 160 | /* touch the sigcontext and try again */ |
@@ -84,18 +164,44 @@ static int protected_save_fp_context(struct sigcontext __user *sc) | |||
84 | if (err) | 164 | if (err) |
85 | break; /* really bad sigcontext */ | 165 | break; /* really bad sigcontext */ |
86 | } | 166 | } |
167 | #else | ||
168 | /* | ||
169 | * EVA does not have FPU EVA instructions so saving fpu context directly | ||
170 | * does not work. | ||
171 | */ | ||
172 | disable_msa(); | ||
173 | lose_fpu(1); | ||
174 | err = save_fp_context(sc); /* this might fail */ | ||
175 | if (save_msa && !err) | ||
176 | err = copy_msa_to_sigcontext(sc); | ||
177 | #endif | ||
87 | return err; | 178 | return err; |
88 | } | 179 | } |
89 | 180 | ||
90 | static int protected_restore_fp_context(struct sigcontext __user *sc) | 181 | static int protected_restore_fp_context(struct sigcontext __user *sc, |
182 | unsigned used_math) | ||
91 | { | 183 | { |
92 | int err, tmp __maybe_unused; | 184 | int err, tmp __maybe_unused; |
185 | bool restore_msa = cpu_has_msa && (used_math & USEDMATH_MSA); | ||
186 | #ifndef CONFIG_EVA | ||
93 | while (1) { | 187 | while (1) { |
94 | lock_fpu_owner(); | 188 | lock_fpu_owner(); |
95 | err = own_fpu_inatomic(0); | 189 | if (is_fpu_owner()) { |
96 | if (!err) | 190 | err = restore_fp_context(sc); |
97 | err = restore_fp_context(sc); /* this might fail */ | 191 | if (restore_msa && !err) { |
98 | unlock_fpu_owner(); | 192 | enable_msa(); |
193 | err = _restore_msa_context(sc); | ||
194 | } else { | ||
195 | /* signal handler may have used MSA */ | ||
196 | disable_msa(); | ||
197 | } | ||
198 | unlock_fpu_owner(); | ||
199 | } else { | ||
200 | unlock_fpu_owner(); | ||
201 | err = copy_fp_from_sigcontext(sc); | ||
202 | if (!err && (used_math & USEDMATH_MSA)) | ||
203 | err = copy_msa_from_sigcontext(sc); | ||
204 | } | ||
99 | if (likely(!err)) | 205 | if (likely(!err)) |
100 | break; | 206 | break; |
101 | /* touch the sigcontext and try again */ | 207 | /* touch the sigcontext and try again */ |
@@ -105,6 +211,17 @@ static int protected_restore_fp_context(struct sigcontext __user *sc) | |||
105 | if (err) | 211 | if (err) |
106 | break; /* really bad sigcontext */ | 212 | break; /* really bad sigcontext */ |
107 | } | 213 | } |
214 | #else | ||
215 | /* | ||
216 | * EVA does not have FPU EVA instructions so restoring fpu context | ||
217 | * directly does not work. | ||
218 | */ | ||
219 | enable_msa(); | ||
220 | lose_fpu(0); | ||
221 | err = restore_fp_context(sc); /* this might fail */ | ||
222 | if (restore_msa && !err) | ||
223 | err = copy_msa_from_sigcontext(sc); | ||
224 | #endif | ||
108 | return err; | 225 | return err; |
109 | } | 226 | } |
110 | 227 | ||
@@ -135,7 +252,8 @@ int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) | |||
135 | err |= __put_user(rddsp(DSP_MASK), &sc->sc_dsp); | 252 | err |= __put_user(rddsp(DSP_MASK), &sc->sc_dsp); |
136 | } | 253 | } |
137 | 254 | ||
138 | used_math = !!used_math(); | 255 | used_math = used_math() ? USEDMATH_FP : 0; |
256 | used_math |= thread_msa_context_live() ? USEDMATH_MSA : 0; | ||
139 | err |= __put_user(used_math, &sc->sc_used_math); | 257 | err |= __put_user(used_math, &sc->sc_used_math); |
140 | 258 | ||
141 | if (used_math) { | 259 | if (used_math) { |
@@ -143,7 +261,7 @@ int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) | |||
143 | * Save FPU state to signal context. Signal handler | 261 | * Save FPU state to signal context. Signal handler |
144 | * will "inherit" current FPU state. | 262 | * will "inherit" current FPU state. |
145 | */ | 263 | */ |
146 | err |= protected_save_fp_context(sc); | 264 | err |= protected_save_fp_context(sc, used_math); |
147 | } | 265 | } |
148 | return err; | 266 | return err; |
149 | } | 267 | } |
@@ -168,14 +286,14 @@ int fpcsr_pending(unsigned int __user *fpcsr) | |||
168 | } | 286 | } |
169 | 287 | ||
170 | static int | 288 | static int |
171 | check_and_restore_fp_context(struct sigcontext __user *sc) | 289 | check_and_restore_fp_context(struct sigcontext __user *sc, unsigned used_math) |
172 | { | 290 | { |
173 | int err, sig; | 291 | int err, sig; |
174 | 292 | ||
175 | err = sig = fpcsr_pending(&sc->sc_fpc_csr); | 293 | err = sig = fpcsr_pending(&sc->sc_fpc_csr); |
176 | if (err > 0) | 294 | if (err > 0) |
177 | err = 0; | 295 | err = 0; |
178 | err |= protected_restore_fp_context(sc); | 296 | err |= protected_restore_fp_context(sc, used_math); |
179 | return err ?: sig; | 297 | return err ?: sig; |
180 | } | 298 | } |
181 | 299 | ||
@@ -215,9 +333,10 @@ int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) | |||
215 | if (used_math) { | 333 | if (used_math) { |
216 | /* restore fpu context if we have used it before */ | 334 | /* restore fpu context if we have used it before */ |
217 | if (!err) | 335 | if (!err) |
218 | err = check_and_restore_fp_context(sc); | 336 | err = check_and_restore_fp_context(sc, used_math); |
219 | } else { | 337 | } else { |
220 | /* signal handler may have used FPU. Give it up. */ | 338 | /* signal handler may have used FPU or MSA. Disable them. */ |
339 | disable_msa(); | ||
221 | lose_fpu(0); | 340 | lose_fpu(0); |
222 | } | 341 | } |
223 | 342 | ||
@@ -591,23 +710,26 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, void *unused, | |||
591 | } | 710 | } |
592 | 711 | ||
593 | #ifdef CONFIG_SMP | 712 | #ifdef CONFIG_SMP |
713 | #ifndef CONFIG_EVA | ||
594 | static int smp_save_fp_context(struct sigcontext __user *sc) | 714 | static int smp_save_fp_context(struct sigcontext __user *sc) |
595 | { | 715 | { |
596 | return raw_cpu_has_fpu | 716 | return raw_cpu_has_fpu |
597 | ? _save_fp_context(sc) | 717 | ? _save_fp_context(sc) |
598 | : fpu_emulator_save_context(sc); | 718 | : copy_fp_to_sigcontext(sc); |
599 | } | 719 | } |
600 | 720 | ||
601 | static int smp_restore_fp_context(struct sigcontext __user *sc) | 721 | static int smp_restore_fp_context(struct sigcontext __user *sc) |
602 | { | 722 | { |
603 | return raw_cpu_has_fpu | 723 | return raw_cpu_has_fpu |
604 | ? _restore_fp_context(sc) | 724 | ? _restore_fp_context(sc) |
605 | : fpu_emulator_restore_context(sc); | 725 | : copy_fp_from_sigcontext(sc); |
606 | } | 726 | } |
727 | #endif /* CONFIG_EVA */ | ||
607 | #endif | 728 | #endif |
608 | 729 | ||
609 | static int signal_setup(void) | 730 | static int signal_setup(void) |
610 | { | 731 | { |
732 | #ifndef CONFIG_EVA | ||
611 | #ifdef CONFIG_SMP | 733 | #ifdef CONFIG_SMP |
612 | /* For now just do the cpu_has_fpu check when the functions are invoked */ | 734 | /* For now just do the cpu_has_fpu check when the functions are invoked */ |
613 | save_fp_context = smp_save_fp_context; | 735 | save_fp_context = smp_save_fp_context; |
@@ -617,9 +739,13 @@ static int signal_setup(void) | |||
617 | save_fp_context = _save_fp_context; | 739 | save_fp_context = _save_fp_context; |
618 | restore_fp_context = _restore_fp_context; | 740 | restore_fp_context = _restore_fp_context; |
619 | } else { | 741 | } else { |
620 | save_fp_context = fpu_emulator_save_context; | 742 | save_fp_context = copy_fp_from_sigcontext; |
621 | restore_fp_context = fpu_emulator_restore_context; | 743 | restore_fp_context = copy_fp_to_sigcontext; |
622 | } | 744 | } |
745 | #endif /* CONFIG_SMP */ | ||
746 | #else | ||
747 | save_fp_context = copy_fp_from_sigcontext;; | ||
748 | restore_fp_context = copy_fp_to_sigcontext; | ||
623 | #endif | 749 | #endif |
624 | 750 | ||
625 | return 0; | 751 | return 0; |