Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

MIPS: MIPS16e: Support handling of delay slots.

Add logic needed to properly calculate exceptions for delay slots
when in MIPS16e mode.

Signed-off-by: Steven J. Hill <Steven.Hill@imgtec.com>

authored by

Steven J. Hill and committed by
Ralf Baechle
8508488f cd574704

+119 -5
+18
arch/mips/include/asm/branch.h
··· 16 16 extern int __compute_return_epc_for_insn(struct pt_regs *regs, 17 17 union mips_instruction insn); 18 18 extern int __microMIPS_compute_return_epc(struct pt_regs *regs); 19 + extern int __MIPS16e_compute_return_epc(struct pt_regs *regs); 19 20 20 21 21 22 static inline int delay_slot(struct pt_regs *regs) ··· 42 41 if (get_isa16_mode(regs->cp0_epc)) { 43 42 if (cpu_has_mmips) 44 43 return __microMIPS_compute_return_epc(regs); 44 + if (cpu_has_mips16) 45 + return __MIPS16e_compute_return_epc(regs); 45 46 return regs->cp0_epc; 46 47 } 47 48 ··· 53 50 } 54 51 55 52 return __compute_return_epc(regs); 53 + } 54 + 55 + static inline int MIPS16e_compute_return_epc(struct pt_regs *regs, 56 + union mips16e_instruction *inst) 57 + { 58 + if (likely(!delay_slot(regs))) { 59 + if (inst->ri.opcode == MIPS16e_extend_op) { 60 + regs->cp0_epc += 4; 61 + return 0; 62 + } 63 + regs->cp0_epc += 2; 64 + return 0; 65 + } 66 + 67 + return __MIPS16e_compute_return_epc(regs); 56 68 } 57 69 58 70 #endif /* _ASM_BRANCH_H */
+3
arch/mips/include/asm/inst.h
··· 82 82 int micro_mips_mode; 83 83 }; 84 84 85 + /* Recode table from 16-bit register notation to 32-bit GPR. Do NOT export!!! */ 86 + extern const int reg16to32[]; 87 + 85 88 #endif /* _ASM_INST_H */
+98 -5
arch/mips/kernel/branch.c
··· 20 20 #include <asm/uaccess.h> 21 21 22 22 /* 23 - * Calculate and return exception PC in case of branch delay 24 - * slot for microMIPS. It does not clear the ISA mode bit. 23 + * Calculate and return exception PC in case of branch delay slot 24 + * for microMIPS and MIPS16e. It does not clear the ISA mode bit. 25 25 */ 26 26 int __isa_exception_epc(struct pt_regs *regs) 27 27 { 28 - long epc = regs->cp0_epc; 29 28 unsigned short inst; 29 + long epc = regs->cp0_epc; 30 30 31 31 /* Calculate exception PC in branch delay slot. */ 32 32 if (__get_user(inst, (u16 __user *) msk_isa16_mode(epc))) { ··· 34 34 force_sig(SIGSEGV, current); 35 35 return epc; 36 36 } 37 - 38 - if (mm_insn_16bit(inst)) 37 + if (cpu_has_mips16) { 38 + if (((union mips16e_instruction)inst).ri.opcode 39 + == MIPS16e_jal_op) 40 + epc += 4; 41 + else 42 + epc += 2; 43 + } else if (mm_insn_16bit(inst)) 39 44 epc += 2; 40 45 else 41 46 epc += 4; ··· 104 99 sigsegv: 105 100 force_sig(SIGSEGV, current); 106 101 return -EFAULT; 102 + } 103 + 104 + /* 105 + * Compute return address and emulate branch in MIPS16e mode after an 106 + * exception only. It does not handle compact branches/jumps and cannot 107 + * be used in interrupt context. (Compact branches/jumps do not cause 108 + * exceptions.) 109 + */ 110 + int __MIPS16e_compute_return_epc(struct pt_regs *regs) 111 + { 112 + u16 __user *addr; 113 + union mips16e_instruction inst; 114 + u16 inst2; 115 + u32 fullinst; 116 + long epc; 117 + 118 + epc = regs->cp0_epc; 119 + 120 + /* Read the instruction. */ 121 + addr = (u16 __user *)msk_isa16_mode(epc); 122 + if (__get_user(inst.full, addr)) { 123 + force_sig(SIGSEGV, current); 124 + return -EFAULT; 125 + } 126 + 127 + switch (inst.ri.opcode) { 128 + case MIPS16e_extend_op: 129 + regs->cp0_epc += 4; 130 + return 0; 131 + 132 + /* 133 + * JAL and JALX in MIPS16e mode 134 + */ 135 + case MIPS16e_jal_op: 136 + addr += 1; 137 + if (__get_user(inst2, addr)) { 138 + force_sig(SIGSEGV, current); 139 + return -EFAULT; 140 + } 141 + fullinst = ((unsigned)inst.full << 16) | inst2; 142 + regs->regs[31] = epc + 6; 143 + epc += 4; 144 + epc >>= 28; 145 + epc <<= 28; 146 + /* 147 + * JAL:5 X:1 TARGET[20-16]:5 TARGET[25:21]:5 TARGET[15:0]:16 148 + * 149 + * ......TARGET[15:0].................TARGET[20:16]........... 150 + * ......TARGET[25:21] 151 + */ 152 + epc |= 153 + ((fullinst & 0xffff) << 2) | ((fullinst & 0x3e00000) >> 3) | 154 + ((fullinst & 0x1f0000) << 7); 155 + if (!inst.jal.x) 156 + set_isa16_mode(epc); /* Set ISA mode bit. */ 157 + regs->cp0_epc = epc; 158 + return 0; 159 + 160 + /* 161 + * J(AL)R(C) 162 + */ 163 + case MIPS16e_rr_op: 164 + if (inst.rr.func == MIPS16e_jr_func) { 165 + 166 + if (inst.rr.ra) 167 + regs->cp0_epc = regs->regs[31]; 168 + else 169 + regs->cp0_epc = 170 + regs->regs[reg16to32[inst.rr.rx]]; 171 + 172 + if (inst.rr.l) { 173 + if (inst.rr.nd) 174 + regs->regs[31] = epc + 2; 175 + else 176 + regs->regs[31] = epc + 4; 177 + } 178 + return 0; 179 + } 180 + break; 181 + } 182 + 183 + /* 184 + * All other cases have no branch delay slot and are 16-bits. 185 + * Branches do not cause an exception. 186 + */ 187 + regs->cp0_epc += 2; 188 + 189 + return 0; 107 190 } 108 191 109 192 /**