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 | |
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')
-rw-r--r-- | arch/mips/include/asm/branch.h | 22 | ||||
-rw-r--r-- | arch/mips/kernel/branch.c | 85 |
2 files changed, 102 insertions, 5 deletions
diff --git a/arch/mips/include/asm/branch.h b/arch/mips/include/asm/branch.h index 888766ae1f85..40bb9ebcc7aa 100644 --- a/arch/mips/include/asm/branch.h +++ b/arch/mips/include/asm/branch.h | |||
@@ -11,6 +11,13 @@ | |||
11 | #include <asm/ptrace.h> | 11 | #include <asm/ptrace.h> |
12 | #include <asm/inst.h> | 12 | #include <asm/inst.h> |
13 | 13 | ||
14 | extern int __isa_exception_epc(struct pt_regs *regs); | ||
15 | extern int __compute_return_epc(struct pt_regs *regs); | ||
16 | extern int __compute_return_epc_for_insn(struct pt_regs *regs, | ||
17 | union mips_instruction insn); | ||
18 | extern int __microMIPS_compute_return_epc(struct pt_regs *regs); | ||
19 | |||
20 | |||
14 | static inline int delay_slot(struct pt_regs *regs) | 21 | static inline int delay_slot(struct pt_regs *regs) |
15 | { | 22 | { |
16 | return regs->cp0_cause & CAUSEF_BD; | 23 | return regs->cp0_cause & CAUSEF_BD; |
@@ -18,20 +25,25 @@ static inline int delay_slot(struct pt_regs *regs) | |||
18 | 25 | ||
19 | static inline unsigned long exception_epc(struct pt_regs *regs) | 26 | static inline unsigned long exception_epc(struct pt_regs *regs) |
20 | { | 27 | { |
21 | if (!delay_slot(regs)) | 28 | if (likely(!delay_slot(regs))) |
22 | return regs->cp0_epc; | 29 | return regs->cp0_epc; |
23 | 30 | ||
31 | if (get_isa16_mode(regs->cp0_epc)) | ||
32 | return __isa_exception_epc(regs); | ||
33 | |||
24 | return regs->cp0_epc + 4; | 34 | return regs->cp0_epc + 4; |
25 | } | 35 | } |
26 | 36 | ||
27 | #define BRANCH_LIKELY_TAKEN 0x0001 | 37 | #define BRANCH_LIKELY_TAKEN 0x0001 |
28 | 38 | ||
29 | extern int __compute_return_epc(struct pt_regs *regs); | ||
30 | extern int __compute_return_epc_for_insn(struct pt_regs *regs, | ||
31 | union mips_instruction insn); | ||
32 | |||
33 | static inline int compute_return_epc(struct pt_regs *regs) | 39 | static inline int compute_return_epc(struct pt_regs *regs) |
34 | { | 40 | { |
41 | if (get_isa16_mode(regs->cp0_epc)) { | ||
42 | if (cpu_has_mmips) | ||
43 | return __microMIPS_compute_return_epc(regs); | ||
44 | return regs->cp0_epc; | ||
45 | } | ||
46 | |||
35 | if (!delay_slot(regs)) { | 47 | if (!delay_slot(regs)) { |
36 | regs->cp0_epc += 4; | 48 | regs->cp0_epc += 4; |
37 | return 0; | 49 | return 0; |
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 | /* |