aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Kristiansson <stefan.kristiansson@saunalahti.fi>2014-11-03 07:28:14 -0500
committerStafford Horne <shorne@gmail.com>2017-02-06 07:50:43 -0500
commit63104c06a9eddf53f88f6d16196bb036c62967b2 (patch)
tree3f4bfcaa467d0546512602d5ad961e5f62f4e529
parent8c9b7db0de3d64c9a6fcd12622636d4aa6a8c30c (diff)
openrisc: add l.lwa/l.swa emulation
This adds an emulation layer for implementations that lack the l.lwa and l.swa instructions. It handles these instructions both in kernel space and user space. Signed-off-by: Stefan Kristiansson <stefan.kristiansson@saunalahti.fi> [shorne@gmail.com: Added delay slot pc adjust logic] Signed-off-by: Stafford Horne <shorne@gmail.com>
-rw-r--r--arch/openrisc/kernel/entry.S22
-rw-r--r--arch/openrisc/kernel/process.c3
-rw-r--r--arch/openrisc/kernel/traps.c183
3 files changed, 206 insertions, 2 deletions
diff --git a/arch/openrisc/kernel/entry.S b/arch/openrisc/kernel/entry.S
index aac0bde3330c..ba1a361c9a1e 100644
--- a/arch/openrisc/kernel/entry.S
+++ b/arch/openrisc/kernel/entry.S
@@ -173,6 +173,11 @@ handler: ;\
173 l.j _ret_from_exception ;\ 173 l.j _ret_from_exception ;\
174 l.nop 174 l.nop
175 175
176/* clobbers 'reg' */
177#define CLEAR_LWA_FLAG(reg) \
178 l.movhi reg,hi(lwa_flag) ;\
179 l.ori reg,reg,lo(lwa_flag) ;\
180 l.sw 0(reg),r0
176/* 181/*
177 * NOTE: one should never assume that SPR_EPC, SPR_ESR, SPR_EEAR 182 * NOTE: one should never assume that SPR_EPC, SPR_ESR, SPR_EEAR
178 * contain the same values as when exception we're handling 183 * contain the same values as when exception we're handling
@@ -193,6 +198,7 @@ EXCEPTION_ENTRY(_tng_kernel_start)
193/* ---[ 0x200: BUS exception ]------------------------------------------- */ 198/* ---[ 0x200: BUS exception ]------------------------------------------- */
194 199
195EXCEPTION_ENTRY(_bus_fault_handler) 200EXCEPTION_ENTRY(_bus_fault_handler)
201 CLEAR_LWA_FLAG(r3)
196 /* r4: EA of fault (set by EXCEPTION_HANDLE) */ 202 /* r4: EA of fault (set by EXCEPTION_HANDLE) */
197 l.jal do_bus_fault 203 l.jal do_bus_fault
198 l.addi r3,r1,0 /* pt_regs */ 204 l.addi r3,r1,0 /* pt_regs */
@@ -202,11 +208,13 @@ EXCEPTION_ENTRY(_bus_fault_handler)
202 208
203/* ---[ 0x300: Data Page Fault exception ]------------------------------- */ 209/* ---[ 0x300: Data Page Fault exception ]------------------------------- */
204EXCEPTION_ENTRY(_dtlb_miss_page_fault_handler) 210EXCEPTION_ENTRY(_dtlb_miss_page_fault_handler)
211 CLEAR_LWA_FLAG(r3)
205 l.and r5,r5,r0 212 l.and r5,r5,r0
206 l.j 1f 213 l.j 1f
207 l.nop 214 l.nop
208 215
209EXCEPTION_ENTRY(_data_page_fault_handler) 216EXCEPTION_ENTRY(_data_page_fault_handler)
217 CLEAR_LWA_FLAG(r3)
210 /* set up parameters for do_page_fault */ 218 /* set up parameters for do_page_fault */
211 l.ori r5,r0,0x300 // exception vector 219 l.ori r5,r0,0x300 // exception vector
2121: 2201:
@@ -282,11 +290,13 @@ EXCEPTION_ENTRY(_data_page_fault_handler)
282 290
283/* ---[ 0x400: Insn Page Fault exception ]------------------------------- */ 291/* ---[ 0x400: Insn Page Fault exception ]------------------------------- */
284EXCEPTION_ENTRY(_itlb_miss_page_fault_handler) 292EXCEPTION_ENTRY(_itlb_miss_page_fault_handler)
293 CLEAR_LWA_FLAG(r3)
285 l.and r5,r5,r0 294 l.and r5,r5,r0
286 l.j 1f 295 l.j 1f
287 l.nop 296 l.nop
288 297
289EXCEPTION_ENTRY(_insn_page_fault_handler) 298EXCEPTION_ENTRY(_insn_page_fault_handler)
299 CLEAR_LWA_FLAG(r3)
290 /* set up parameters for do_page_fault */ 300 /* set up parameters for do_page_fault */
291 l.ori r5,r0,0x400 // exception vector 301 l.ori r5,r0,0x400 // exception vector
2921: 3021:
@@ -304,6 +314,7 @@ EXCEPTION_ENTRY(_insn_page_fault_handler)
304/* ---[ 0x500: Timer exception ]----------------------------------------- */ 314/* ---[ 0x500: Timer exception ]----------------------------------------- */
305 315
306EXCEPTION_ENTRY(_timer_handler) 316EXCEPTION_ENTRY(_timer_handler)
317 CLEAR_LWA_FLAG(r3)
307 l.jal timer_interrupt 318 l.jal timer_interrupt
308 l.addi r3,r1,0 /* pt_regs */ 319 l.addi r3,r1,0 /* pt_regs */
309 320
@@ -313,6 +324,7 @@ EXCEPTION_ENTRY(_timer_handler)
313/* ---[ 0x600: Aligment exception ]-------------------------------------- */ 324/* ---[ 0x600: Aligment exception ]-------------------------------------- */
314 325
315EXCEPTION_ENTRY(_alignment_handler) 326EXCEPTION_ENTRY(_alignment_handler)
327 CLEAR_LWA_FLAG(r3)
316 /* r4: EA of fault (set by EXCEPTION_HANDLE) */ 328 /* r4: EA of fault (set by EXCEPTION_HANDLE) */
317 l.jal do_unaligned_access 329 l.jal do_unaligned_access
318 l.addi r3,r1,0 /* pt_regs */ 330 l.addi r3,r1,0 /* pt_regs */
@@ -509,6 +521,7 @@ EXCEPTION_ENTRY(_external_irq_handler)
509// l.sw PT_SR(r1),r4 521// l.sw PT_SR(r1),r4
5101: 5221:
511#endif 523#endif
524 CLEAR_LWA_FLAG(r3)
512 l.addi r3,r1,0 525 l.addi r3,r1,0
513 l.movhi r8,hi(do_IRQ) 526 l.movhi r8,hi(do_IRQ)
514 l.ori r8,r8,lo(do_IRQ) 527 l.ori r8,r8,lo(do_IRQ)
@@ -556,8 +569,12 @@ ENTRY(_sys_call_handler)
556 * they should be clobbered, otherwise 569 * they should be clobbered, otherwise
557 */ 570 */
558 l.sw PT_GPR3(r1),r3 571 l.sw PT_GPR3(r1),r3
559 /* r4 already saved */ 572 /*
560 /* r4 holds the EEAR address of the fault, load the original r4 */ 573 * r4 already saved
574 * r4 holds the EEAR address of the fault, use it as screatch reg and
575 * then load the original r4
576 */
577 CLEAR_LWA_FLAG(r4)
561 l.lwz r4,PT_GPR4(r1) 578 l.lwz r4,PT_GPR4(r1)
562 l.sw PT_GPR5(r1),r5 579 l.sw PT_GPR5(r1),r5
563 l.sw PT_GPR6(r1),r6 580 l.sw PT_GPR6(r1),r6
@@ -776,6 +793,7 @@ UNHANDLED_EXCEPTION(_vector_0xd00,0xd00)
776/* ---[ 0xe00: Trap exception ]------------------------------------------ */ 793/* ---[ 0xe00: Trap exception ]------------------------------------------ */
777 794
778EXCEPTION_ENTRY(_trap_handler) 795EXCEPTION_ENTRY(_trap_handler)
796 CLEAR_LWA_FLAG(r3)
779 /* r4: EA of fault (set by EXCEPTION_HANDLE) */ 797 /* r4: EA of fault (set by EXCEPTION_HANDLE) */
780 l.jal do_trap 798 l.jal do_trap
781 l.addi r3,r1,0 /* pt_regs */ 799 l.addi r3,r1,0 /* pt_regs */
diff --git a/arch/openrisc/kernel/process.c b/arch/openrisc/kernel/process.c
index d7990df9025a..c49350b200e1 100644
--- a/arch/openrisc/kernel/process.c
+++ b/arch/openrisc/kernel/process.c
@@ -226,6 +226,7 @@ int dump_fpu(struct pt_regs *regs, elf_fpregset_t * fpu)
226 226
227extern struct thread_info *_switch(struct thread_info *old_ti, 227extern struct thread_info *_switch(struct thread_info *old_ti,
228 struct thread_info *new_ti); 228 struct thread_info *new_ti);
229extern int lwa_flag;
229 230
230struct task_struct *__switch_to(struct task_struct *old, 231struct task_struct *__switch_to(struct task_struct *old,
231 struct task_struct *new) 232 struct task_struct *new)
@@ -243,6 +244,8 @@ struct task_struct *__switch_to(struct task_struct *old,
243 new_ti = new->stack; 244 new_ti = new->stack;
244 old_ti = old->stack; 245 old_ti = old->stack;
245 246
247 lwa_flag = 0;
248
246 current_thread_info_set[smp_processor_id()] = new_ti; 249 current_thread_info_set[smp_processor_id()] = new_ti;
247 last = (_switch(old_ti, new_ti))->task; 250 last = (_switch(old_ti, new_ti))->task;
248 251
diff --git a/arch/openrisc/kernel/traps.c b/arch/openrisc/kernel/traps.c
index a4574cb4b0fb..7907b6cf5d8a 100644
--- a/arch/openrisc/kernel/traps.c
+++ b/arch/openrisc/kernel/traps.c
@@ -40,6 +40,8 @@
40extern char _etext, _stext; 40extern char _etext, _stext;
41 41
42int kstack_depth_to_print = 0x180; 42int kstack_depth_to_print = 0x180;
43int lwa_flag;
44unsigned long __user *lwa_addr;
43 45
44static inline int valid_stack_ptr(struct thread_info *tinfo, void *p) 46static inline int valid_stack_ptr(struct thread_info *tinfo, void *p)
45{ 47{
@@ -334,10 +336,191 @@ asmlinkage void do_bus_fault(struct pt_regs *regs, unsigned long address)
334 } 336 }
335} 337}
336 338
339static inline int in_delay_slot(struct pt_regs *regs)
340{
341#ifdef CONFIG_OPENRISC_NO_SPR_SR_DSX
342 /* No delay slot flag, do the old way */
343 unsigned int op, insn;
344
345 insn = *((unsigned int *)regs->pc);
346 op = insn >> 26;
347 switch (op) {
348 case 0x00: /* l.j */
349 case 0x01: /* l.jal */
350 case 0x03: /* l.bnf */
351 case 0x04: /* l.bf */
352 case 0x11: /* l.jr */
353 case 0x12: /* l.jalr */
354 return 1;
355 default:
356 return 0;
357 }
358#else
359 return regs->sr & SPR_SR_DSX;
360#endif
361}
362
363static inline void adjust_pc(struct pt_regs *regs, unsigned long address)
364{
365 int displacement;
366 unsigned int rb, op, jmp;
367
368 if (unlikely(in_delay_slot(regs))) {
369 /* In delay slot, instruction at pc is a branch, simulate it */
370 jmp = *((unsigned int *)regs->pc);
371
372 displacement = sign_extend32(((jmp) & 0x3ffffff) << 2, 27);
373 rb = (jmp & 0x0000ffff) >> 11;
374 op = jmp >> 26;
375
376 switch (op) {
377 case 0x00: /* l.j */
378 regs->pc += displacement;
379 return;
380 case 0x01: /* l.jal */
381 regs->pc += displacement;
382 regs->gpr[9] = regs->pc + 8;
383 return;
384 case 0x03: /* l.bnf */
385 if (regs->sr & SPR_SR_F)
386 regs->pc += 8;
387 else
388 regs->pc += displacement;
389 return;
390 case 0x04: /* l.bf */
391 if (regs->sr & SPR_SR_F)
392 regs->pc += displacement;
393 else
394 regs->pc += 8;
395 return;
396 case 0x11: /* l.jr */
397 regs->pc = regs->gpr[rb];
398 return;
399 case 0x12: /* l.jalr */
400 regs->pc = regs->gpr[rb];
401 regs->gpr[9] = regs->pc + 8;
402 return;
403 default:
404 break;
405 }
406 } else {
407 regs->pc += 4;
408 }
409}
410
411static inline void simulate_lwa(struct pt_regs *regs, unsigned long address,
412 unsigned int insn)
413{
414 unsigned int ra, rd;
415 unsigned long value;
416 unsigned long orig_pc;
417 long imm;
418
419 const struct exception_table_entry *entry;
420
421 orig_pc = regs->pc;
422 adjust_pc(regs, address);
423
424 ra = (insn >> 16) & 0x1f;
425 rd = (insn >> 21) & 0x1f;
426 imm = (short)insn;
427 lwa_addr = (unsigned long __user *)(regs->gpr[ra] + imm);
428
429 if ((unsigned long)lwa_addr & 0x3) {
430 do_unaligned_access(regs, address);
431 return;
432 }
433
434 if (get_user(value, lwa_addr)) {
435 if (user_mode(regs)) {
436 force_sig(SIGSEGV, current);
437 return;
438 }
439
440 if ((entry = search_exception_tables(orig_pc))) {
441 regs->pc = entry->fixup;
442 return;
443 }
444
445 /* kernel access in kernel space, load it directly */
446 value = *((unsigned long *)lwa_addr);
447 }
448
449 lwa_flag = 1;
450 regs->gpr[rd] = value;
451}
452
453static inline void simulate_swa(struct pt_regs *regs, unsigned long address,
454 unsigned int insn)
455{
456 unsigned long __user *vaddr;
457 unsigned long orig_pc;
458 unsigned int ra, rb;
459 long imm;
460
461 const struct exception_table_entry *entry;
462
463 orig_pc = regs->pc;
464 adjust_pc(regs, address);
465
466 ra = (insn >> 16) & 0x1f;
467 rb = (insn >> 11) & 0x1f;
468 imm = (short)(((insn & 0x2200000) >> 10) | (insn & 0x7ff));
469 vaddr = (unsigned long __user *)(regs->gpr[ra] + imm);
470
471 if (!lwa_flag || vaddr != lwa_addr) {
472 regs->sr &= ~SPR_SR_F;
473 return;
474 }
475
476 if ((unsigned long)vaddr & 0x3) {
477 do_unaligned_access(regs, address);
478 return;
479 }
480
481 if (put_user(regs->gpr[rb], vaddr)) {
482 if (user_mode(regs)) {
483 force_sig(SIGSEGV, current);
484 return;
485 }
486
487 if ((entry = search_exception_tables(orig_pc))) {
488 regs->pc = entry->fixup;
489 return;
490 }
491
492 /* kernel access in kernel space, store it directly */
493 *((unsigned long *)vaddr) = regs->gpr[rb];
494 }
495
496 lwa_flag = 0;
497 regs->sr |= SPR_SR_F;
498}
499
500#define INSN_LWA 0x1b
501#define INSN_SWA 0x33
502
337asmlinkage void do_illegal_instruction(struct pt_regs *regs, 503asmlinkage void do_illegal_instruction(struct pt_regs *regs,
338 unsigned long address) 504 unsigned long address)
339{ 505{
340 siginfo_t info; 506 siginfo_t info;
507 unsigned int op;
508 unsigned int insn = *((unsigned int *)address);
509
510 op = insn >> 26;
511
512 switch (op) {
513 case INSN_LWA:
514 simulate_lwa(regs, address, insn);
515 return;
516
517 case INSN_SWA:
518 simulate_swa(regs, address, insn);
519 return;
520
521 default:
522 break;
523 }
341 524
342 if (user_mode(regs)) { 525 if (user_mode(regs)) {
343 /* Send a SIGILL */ 526 /* Send a SIGILL */