diff options
author | Stefan Kristiansson <stefan.kristiansson@saunalahti.fi> | 2014-11-03 07:28:14 -0500 |
---|---|---|
committer | Stafford Horne <shorne@gmail.com> | 2017-02-06 07:50:43 -0500 |
commit | 63104c06a9eddf53f88f6d16196bb036c62967b2 (patch) | |
tree | 3f4bfcaa467d0546512602d5ad961e5f62f4e529 | |
parent | 8c9b7db0de3d64c9a6fcd12622636d4aa6a8c30c (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.S | 22 | ||||
-rw-r--r-- | arch/openrisc/kernel/process.c | 3 | ||||
-rw-r--r-- | arch/openrisc/kernel/traps.c | 183 |
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 | ||
195 | EXCEPTION_ENTRY(_bus_fault_handler) | 200 | EXCEPTION_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 ]------------------------------- */ |
204 | EXCEPTION_ENTRY(_dtlb_miss_page_fault_handler) | 210 | EXCEPTION_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 | ||
209 | EXCEPTION_ENTRY(_data_page_fault_handler) | 216 | EXCEPTION_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 |
212 | 1: | 220 | 1: |
@@ -282,11 +290,13 @@ EXCEPTION_ENTRY(_data_page_fault_handler) | |||
282 | 290 | ||
283 | /* ---[ 0x400: Insn Page Fault exception ]------------------------------- */ | 291 | /* ---[ 0x400: Insn Page Fault exception ]------------------------------- */ |
284 | EXCEPTION_ENTRY(_itlb_miss_page_fault_handler) | 292 | EXCEPTION_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 | ||
289 | EXCEPTION_ENTRY(_insn_page_fault_handler) | 298 | EXCEPTION_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 |
292 | 1: | 302 | 1: |
@@ -304,6 +314,7 @@ EXCEPTION_ENTRY(_insn_page_fault_handler) | |||
304 | /* ---[ 0x500: Timer exception ]----------------------------------------- */ | 314 | /* ---[ 0x500: Timer exception ]----------------------------------------- */ |
305 | 315 | ||
306 | EXCEPTION_ENTRY(_timer_handler) | 316 | EXCEPTION_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 | ||
315 | EXCEPTION_ENTRY(_alignment_handler) | 326 | EXCEPTION_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 |
510 | 1: | 522 | 1: |
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 | ||
778 | EXCEPTION_ENTRY(_trap_handler) | 795 | EXCEPTION_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 | ||
227 | extern struct thread_info *_switch(struct thread_info *old_ti, | 227 | extern struct thread_info *_switch(struct thread_info *old_ti, |
228 | struct thread_info *new_ti); | 228 | struct thread_info *new_ti); |
229 | extern int lwa_flag; | ||
229 | 230 | ||
230 | struct task_struct *__switch_to(struct task_struct *old, | 231 | struct 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 @@ | |||
40 | extern char _etext, _stext; | 40 | extern char _etext, _stext; |
41 | 41 | ||
42 | int kstack_depth_to_print = 0x180; | 42 | int kstack_depth_to_print = 0x180; |
43 | int lwa_flag; | ||
44 | unsigned long __user *lwa_addr; | ||
43 | 45 | ||
44 | static inline int valid_stack_ptr(struct thread_info *tinfo, void *p) | 46 | static 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 | ||
339 | static 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 | |||
363 | static 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 | |||
411 | static 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 | |||
453 | static 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 | |||
337 | asmlinkage void do_illegal_instruction(struct pt_regs *regs, | 503 | asmlinkage 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 */ |