aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/mm
diff options
context:
space:
mode:
authorJimi Xenidis <jimix@pobox.com>2011-09-29 06:55:14 -0400
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2011-11-24 22:11:28 -0500
commitc3dcf53a3fcb01f1d98f6b0cf440bb781dbcbc34 (patch)
tree8b0271a59dc103db39b3e8259419b9b549c89eae /arch/powerpc/mm
parentfac26ad4f9cb794c9d1032f55f40a31cb55be09a (diff)
powerpc/icswx: Simple ACOP fault handler
This patch adds a fault handler that responds to illegal Coprocessor types. Currently all CTs are treated and illegal. There are two ways to report the fault back to the application. If the application used the record form ("icswx.") then the architected "reject" is emulated. If the application did not used the record form ("icswx") then it is selectable by config whether the failure is silent (as architected) or a SIGILL is generated. In all cases pr_warn() is used to log the bad CT. Signed-off-by: Jimi Xenidis <jimix@pobox.com> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch/powerpc/mm')
-rw-r--r--arch/powerpc/mm/fault.c17
-rw-r--r--arch/powerpc/mm/icswx.c113
-rw-r--r--arch/powerpc/mm/icswx.h23
3 files changed, 153 insertions, 0 deletions
diff --git a/arch/powerpc/mm/fault.c b/arch/powerpc/mm/fault.c
index 5efe8c96d37f..2f0d1b032a89 100644
--- a/arch/powerpc/mm/fault.c
+++ b/arch/powerpc/mm/fault.c
@@ -44,6 +44,8 @@
44#include <asm/siginfo.h> 44#include <asm/siginfo.h>
45#include <mm/mmu_decl.h> 45#include <mm/mmu_decl.h>
46 46
47#include "icswx.h"
48
47#ifdef CONFIG_KPROBES 49#ifdef CONFIG_KPROBES
48static inline int notify_page_fault(struct pt_regs *regs) 50static inline int notify_page_fault(struct pt_regs *regs)
49{ 51{
@@ -143,6 +145,21 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address,
143 is_write = error_code & ESR_DST; 145 is_write = error_code & ESR_DST;
144#endif /* CONFIG_4xx || CONFIG_BOOKE */ 146#endif /* CONFIG_4xx || CONFIG_BOOKE */
145 147
148#ifdef CONFIG_PPC_ICSWX
149 /*
150 * we need to do this early because this "data storage
151 * interrupt" does not update the DAR/DEAR so we don't want to
152 * look at it
153 */
154 if (error_code & ICSWX_DSI_UCT) {
155 int ret;
156
157 ret = acop_handle_fault(regs, address, error_code);
158 if (ret)
159 return ret;
160 }
161#endif
162
146 if (notify_page_fault(regs)) 163 if (notify_page_fault(regs))
147 return 0; 164 return 0;
148 165
diff --git a/arch/powerpc/mm/icswx.c b/arch/powerpc/mm/icswx.c
index a98850fd7777..5d9a59eaad93 100644
--- a/arch/powerpc/mm/icswx.c
+++ b/arch/powerpc/mm/icswx.c
@@ -17,6 +17,8 @@
17#include <linux/mm.h> 17#include <linux/mm.h>
18#include <linux/spinlock.h> 18#include <linux/spinlock.h>
19#include <linux/module.h> 19#include <linux/module.h>
20#include <linux/uaccess.h>
21
20#include "icswx.h" 22#include "icswx.h"
21 23
22/* 24/*
@@ -158,3 +160,114 @@ void drop_cop(unsigned long acop, struct mm_struct *mm)
158 spin_unlock(&mm->page_table_lock); 160 spin_unlock(&mm->page_table_lock);
159} 161}
160EXPORT_SYMBOL_GPL(drop_cop); 162EXPORT_SYMBOL_GPL(drop_cop);
163
164static int acop_use_cop(int ct)
165{
166 /* todo */
167 return -1;
168}
169
170/*
171 * Get the instruction word at the NIP
172 */
173static u32 acop_get_inst(struct pt_regs *regs)
174{
175 u32 inst;
176 u32 __user *p;
177
178 p = (u32 __user *)regs->nip;
179 if (!access_ok(VERIFY_READ, p, sizeof(*p)))
180 return 0;
181
182 if (__get_user(inst, p))
183 return 0;
184
185 return inst;
186}
187
188/**
189 * @regs: regsiters at time of interrupt
190 * @address: storage address
191 * @error_code: Fault code, usually the DSISR or ESR depending on
192 * processor type
193 *
194 * Return 0 if we are able to resolve the data storage fault that
195 * results from a CT miss in the ACOP register.
196 */
197int acop_handle_fault(struct pt_regs *regs, unsigned long address,
198 unsigned long error_code)
199{
200 int ct;
201 u32 inst = 0;
202
203 if (!cpu_has_feature(CPU_FTR_ICSWX)) {
204 pr_info("No coprocessors available");
205 _exception(SIGILL, regs, ILL_ILLOPN, address);
206 }
207
208 if (!user_mode(regs)) {
209 /* this could happen if the HV denies the
210 * kernel access, for now we just die */
211 die("ICSWX from kernel failed", regs, SIGSEGV);
212 }
213
214 /* Some implementations leave us a hint for the CT */
215 ct = ICSWX_GET_CT_HINT(error_code);
216 if (ct < 0) {
217 /* we have to peek at the instruction word to figure out CT */
218 u32 ccw;
219 u32 rs;
220
221 inst = acop_get_inst(regs);
222 if (inst == 0)
223 return -1;
224
225 rs = (inst >> (31 - 10)) & 0x1f;
226 ccw = regs->gpr[rs];
227 ct = (ccw >> 16) & 0x3f;
228 }
229
230 if (!acop_use_cop(ct))
231 return 0;
232
233 /* at this point the CT is unknown to the system */
234 pr_warn("%s[%d]: Coprocessor %d is unavailable",
235 current->comm, current->pid, ct);
236
237 /* get inst if we don't already have it */
238 if (inst == 0) {
239 inst = acop_get_inst(regs);
240 if (inst == 0)
241 return -1;
242 }
243
244 /* Check if the instruction is the "record form" */
245 if (inst & 1) {
246 /*
247 * the instruction is "record" form so we can reject
248 * using CR0
249 */
250 regs->ccr &= ~(0xful << 28);
251 regs->ccr |= ICSWX_RC_NOT_FOUND << 28;
252
253 /* Move on to the next instruction */
254 regs->nip += 4;
255 } else {
256 /*
257 * There is no architected mechanism to report a bad
258 * CT so we could either SIGILL or report nothing.
259 * Since the non-record version should only bu used
260 * for "hints" or "don't care" we should probably do
261 * nothing. However, I could see how some people
262 * might want an SIGILL so it here if you want it.
263 */
264#ifdef CONFIG_PPC_ICSWX_USE_SIGILL
265 _exception(SIGILL, regs, ILL_ILLOPN, address);
266#else
267 regs->nip += 4;
268#endif
269 }
270
271 return 0;
272}
273EXPORT_SYMBOL_GPL(acop_handle_fault);
diff --git a/arch/powerpc/mm/icswx.h b/arch/powerpc/mm/icswx.h
index 07514e49498e..42176bd0884c 100644
--- a/arch/powerpc/mm/icswx.h
+++ b/arch/powerpc/mm/icswx.h
@@ -36,4 +36,27 @@ extern void free_cop_pid(int free_pid);
36#define free_cop_pid(p) 36#define free_cop_pid(p)
37#endif 37#endif
38 38
39/*
40 * These are implementation bits for architected registers. If this
41 * ever becomes architecture the should be moved to reg.h et. al.
42 */
43/* UCT is the same bit for Server and Embedded */
44#define ICSWX_DSI_UCT 0x00004000 /* Unavailable Coprocessor Type */
45
46#ifdef CONFIG_PPC_BOOK3E
47/* Embedded implementation gives us no hints as to what the CT is */
48#define ICSWX_GET_CT_HINT(x) (-1)
49#else
50/* Server implementation contains the CT value in the DSISR */
51#define ICSWX_DSISR_CTMASK 0x00003f00
52#define ICSWX_GET_CT_HINT(x) (((x) & ICSWX_DSISR_CTMASK) >> 8)
53#endif
54
55#define ICSWX_RC_STARTED 0x8 /* The request has been started */
56#define ICSWX_RC_NOT_IDLE 0x4 /* No coprocessor found idle */
57#define ICSWX_RC_NOT_FOUND 0x2 /* No coprocessor found */
58#define ICSWX_RC_UNDEFINED 0x1 /* Reserved */
59
60extern int acop_handle_fault(struct pt_regs *regs, unsigned long address,
61 unsigned long error_code);
39#endif /* !_ARCH_POWERPC_MM_ICSWX_H_ */ 62#endif /* !_ARCH_POWERPC_MM_ICSWX_H_ */