diff options
author | Leonid Yegoshin <Leonid.Yegoshin@imgtec.com> | 2013-03-25 14:08:40 -0400 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2013-05-09 11:55:18 -0400 |
commit | fb6883e5809c08e43de23581759af4570ca91b0f (patch) | |
tree | 2bf479748d969f85c79cb8880a1573b2ca7c61ee /arch/mips/kernel | |
parent | 2a0b24f56c2492b932f1aed617ae80fb23500d21 (diff) |
MIPS: microMIPS: Support handling of delay slots.
Add logic needed to properly calculate exceptions for delay slots
when in microMIPS mode.
Signed-off-by: Leonid Yegoshin <Leonid.Yegoshin@imgtec.com>
Signed-off-by: Steven J. Hill <Steven.Hill@imgtec.com>
Diffstat (limited to 'arch/mips/kernel')
-rw-r--r-- | arch/mips/kernel/branch.c | 85 |
1 files changed, 85 insertions, 0 deletions
diff --git a/arch/mips/kernel/branch.c b/arch/mips/kernel/branch.c index 83ffe950f710..a03836b5b683 100644 --- a/arch/mips/kernel/branch.c +++ b/arch/mips/kernel/branch.c | |||
@@ -14,10 +14,93 @@ | |||
14 | #include <asm/cpu.h> | 14 | #include <asm/cpu.h> |
15 | #include <asm/cpu-features.h> | 15 | #include <asm/cpu-features.h> |
16 | #include <asm/fpu.h> | 16 | #include <asm/fpu.h> |
17 | #include <asm/fpu_emulator.h> | ||
17 | #include <asm/inst.h> | 18 | #include <asm/inst.h> |
18 | #include <asm/ptrace.h> | 19 | #include <asm/ptrace.h> |
19 | #include <asm/uaccess.h> | 20 | #include <asm/uaccess.h> |
20 | 21 | ||
22 | /* | ||
23 | * Calculate and return exception PC in case of branch delay | ||
24 | * slot for microMIPS. It does not clear the ISA mode bit. | ||
25 | */ | ||
26 | int __isa_exception_epc(struct pt_regs *regs) | ||
27 | { | ||
28 | long epc = regs->cp0_epc; | ||
29 | unsigned short inst; | ||
30 | |||
31 | /* Calculate exception PC in branch delay slot. */ | ||
32 | if (__get_user(inst, (u16 __user *) msk_isa16_mode(epc))) { | ||
33 | /* This should never happen because delay slot was checked. */ | ||
34 | force_sig(SIGSEGV, current); | ||
35 | return epc; | ||
36 | } | ||
37 | |||
38 | if (mm_insn_16bit(inst)) | ||
39 | epc += 2; | ||
40 | else | ||
41 | epc += 4; | ||
42 | |||
43 | return epc; | ||
44 | } | ||
45 | |||
46 | /* | ||
47 | * Compute return address and emulate branch in microMIPS mode after an | ||
48 | * exception only. It does not handle compact branches/jumps and cannot | ||
49 | * be used in interrupt context. (Compact branches/jumps do not cause | ||
50 | * exceptions.) | ||
51 | */ | ||
52 | int __microMIPS_compute_return_epc(struct pt_regs *regs) | ||
53 | { | ||
54 | u16 __user *pc16; | ||
55 | u16 halfword; | ||
56 | unsigned int word; | ||
57 | unsigned long contpc; | ||
58 | struct mm_decoded_insn mminsn = { 0 }; | ||
59 | |||
60 | mminsn.micro_mips_mode = 1; | ||
61 | |||
62 | /* This load never faults. */ | ||
63 | pc16 = (unsigned short __user *)msk_isa16_mode(regs->cp0_epc); | ||
64 | __get_user(halfword, pc16); | ||
65 | pc16++; | ||
66 | contpc = regs->cp0_epc + 2; | ||
67 | word = ((unsigned int)halfword << 16); | ||
68 | mminsn.pc_inc = 2; | ||
69 | |||
70 | if (!mm_insn_16bit(halfword)) { | ||
71 | __get_user(halfword, pc16); | ||
72 | pc16++; | ||
73 | contpc = regs->cp0_epc + 4; | ||
74 | mminsn.pc_inc = 4; | ||
75 | word |= halfword; | ||
76 | } | ||
77 | mminsn.insn = word; | ||
78 | |||
79 | if (get_user(halfword, pc16)) | ||
80 | goto sigsegv; | ||
81 | mminsn.next_pc_inc = 2; | ||
82 | word = ((unsigned int)halfword << 16); | ||
83 | |||
84 | if (!mm_insn_16bit(halfword)) { | ||
85 | pc16++; | ||
86 | if (get_user(halfword, pc16)) | ||
87 | goto sigsegv; | ||
88 | mminsn.next_pc_inc = 4; | ||
89 | word |= halfword; | ||
90 | } | ||
91 | mminsn.next_insn = word; | ||
92 | |||
93 | mm_isBranchInstr(regs, mminsn, &contpc); | ||
94 | |||
95 | regs->cp0_epc = contpc; | ||
96 | |||
97 | return 0; | ||
98 | |||
99 | sigsegv: | ||
100 | force_sig(SIGSEGV, current); | ||
101 | return -EFAULT; | ||
102 | } | ||
103 | |||
21 | /** | 104 | /** |
22 | * __compute_return_epc_for_insn - Computes the return address and do emulate | 105 | * __compute_return_epc_for_insn - Computes the return address and do emulate |
23 | * branch simulation, if required. | 106 | * branch simulation, if required. |
@@ -129,6 +212,8 @@ int __compute_return_epc_for_insn(struct pt_regs *regs, | |||
129 | epc <<= 28; | 212 | epc <<= 28; |
130 | epc |= (insn.j_format.target << 2); | 213 | epc |= (insn.j_format.target << 2); |
131 | regs->cp0_epc = epc; | 214 | regs->cp0_epc = epc; |
215 | if (insn.i_format.opcode == jalx_op) | ||
216 | set_isa16_mode(regs->cp0_epc); | ||
132 | break; | 217 | break; |
133 | 218 | ||
134 | /* | 219 | /* |