1*081860b9SGuo Ren // SPDX-License-Identifier: GPL-2.0 2*081860b9SGuo Ren // Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd. 3*081860b9SGuo Ren 4*081860b9SGuo Ren #include <linux/kernel.h> 5*081860b9SGuo Ren #include <linux/uaccess.h> 6*081860b9SGuo Ren #include <linux/ptrace.h> 7*081860b9SGuo Ren 8*081860b9SGuo Ren static int align_enable = 1; 9*081860b9SGuo Ren static int align_count; 10*081860b9SGuo Ren 11*081860b9SGuo Ren static inline uint32_t get_ptreg(struct pt_regs *regs, uint32_t rx) 12*081860b9SGuo Ren { 13*081860b9SGuo Ren return rx == 15 ? regs->lr : *((uint32_t *)&(regs->a0) - 2 + rx); 14*081860b9SGuo Ren } 15*081860b9SGuo Ren 16*081860b9SGuo Ren static inline void put_ptreg(struct pt_regs *regs, uint32_t rx, uint32_t val) 17*081860b9SGuo Ren { 18*081860b9SGuo Ren if (rx == 15) 19*081860b9SGuo Ren regs->lr = val; 20*081860b9SGuo Ren else 21*081860b9SGuo Ren *((uint32_t *)&(regs->a0) - 2 + rx) = val; 22*081860b9SGuo Ren } 23*081860b9SGuo Ren 24*081860b9SGuo Ren /* 25*081860b9SGuo Ren * Get byte-value from addr and set it to *valp. 26*081860b9SGuo Ren * 27*081860b9SGuo Ren * Success: return 0 28*081860b9SGuo Ren * Failure: return 1 29*081860b9SGuo Ren */ 30*081860b9SGuo Ren static int ldb_asm(uint32_t addr, uint32_t *valp) 31*081860b9SGuo Ren { 32*081860b9SGuo Ren uint32_t val; 33*081860b9SGuo Ren int err; 34*081860b9SGuo Ren 35*081860b9SGuo Ren if (!access_ok(VERIFY_READ, (void *)addr, 1)) 36*081860b9SGuo Ren return 1; 37*081860b9SGuo Ren 38*081860b9SGuo Ren asm volatile ( 39*081860b9SGuo Ren "movi %0, 0\n" 40*081860b9SGuo Ren "1:\n" 41*081860b9SGuo Ren "ldb %1, (%2)\n" 42*081860b9SGuo Ren "br 3f\n" 43*081860b9SGuo Ren "2:\n" 44*081860b9SGuo Ren "movi %0, 1\n" 45*081860b9SGuo Ren "br 3f\n" 46*081860b9SGuo Ren ".section __ex_table,\"a\"\n" 47*081860b9SGuo Ren ".align 2\n" 48*081860b9SGuo Ren ".long 1b, 2b\n" 49*081860b9SGuo Ren ".previous\n" 50*081860b9SGuo Ren "3:\n" 51*081860b9SGuo Ren : "=&r"(err), "=r"(val) 52*081860b9SGuo Ren : "r" (addr) 53*081860b9SGuo Ren ); 54*081860b9SGuo Ren 55*081860b9SGuo Ren *valp = val; 56*081860b9SGuo Ren 57*081860b9SGuo Ren return err; 58*081860b9SGuo Ren } 59*081860b9SGuo Ren 60*081860b9SGuo Ren /* 61*081860b9SGuo Ren * Put byte-value to addr. 62*081860b9SGuo Ren * 63*081860b9SGuo Ren * Success: return 0 64*081860b9SGuo Ren * Failure: return 1 65*081860b9SGuo Ren */ 66*081860b9SGuo Ren static int stb_asm(uint32_t addr, uint32_t val) 67*081860b9SGuo Ren { 68*081860b9SGuo Ren int err; 69*081860b9SGuo Ren 70*081860b9SGuo Ren if (!access_ok(VERIFY_WRITE, (void *)addr, 1)) 71*081860b9SGuo Ren return 1; 72*081860b9SGuo Ren 73*081860b9SGuo Ren asm volatile ( 74*081860b9SGuo Ren "movi %0, 0\n" 75*081860b9SGuo Ren "1:\n" 76*081860b9SGuo Ren "stb %1, (%2)\n" 77*081860b9SGuo Ren "br 3f\n" 78*081860b9SGuo Ren "2:\n" 79*081860b9SGuo Ren "movi %0, 1\n" 80*081860b9SGuo Ren "br 3f\n" 81*081860b9SGuo Ren ".section __ex_table,\"a\"\n" 82*081860b9SGuo Ren ".align 2\n" 83*081860b9SGuo Ren ".long 1b, 2b\n" 84*081860b9SGuo Ren ".previous\n" 85*081860b9SGuo Ren "3:\n" 86*081860b9SGuo Ren : "=&r"(err) 87*081860b9SGuo Ren : "r"(val), "r" (addr) 88*081860b9SGuo Ren ); 89*081860b9SGuo Ren 90*081860b9SGuo Ren return err; 91*081860b9SGuo Ren } 92*081860b9SGuo Ren 93*081860b9SGuo Ren /* 94*081860b9SGuo Ren * Get half-word from [rx + imm] 95*081860b9SGuo Ren * 96*081860b9SGuo Ren * Success: return 0 97*081860b9SGuo Ren * Failure: return 1 98*081860b9SGuo Ren */ 99*081860b9SGuo Ren static int ldh_c(struct pt_regs *regs, uint32_t rz, uint32_t addr) 100*081860b9SGuo Ren { 101*081860b9SGuo Ren uint32_t byte0, byte1; 102*081860b9SGuo Ren 103*081860b9SGuo Ren if (ldb_asm(addr, &byte0)) 104*081860b9SGuo Ren return 1; 105*081860b9SGuo Ren addr += 1; 106*081860b9SGuo Ren if (ldb_asm(addr, &byte1)) 107*081860b9SGuo Ren return 1; 108*081860b9SGuo Ren 109*081860b9SGuo Ren byte0 |= byte1 << 8; 110*081860b9SGuo Ren put_ptreg(regs, rz, byte0); 111*081860b9SGuo Ren 112*081860b9SGuo Ren return 0; 113*081860b9SGuo Ren } 114*081860b9SGuo Ren 115*081860b9SGuo Ren /* 116*081860b9SGuo Ren * Store half-word to [rx + imm] 117*081860b9SGuo Ren * 118*081860b9SGuo Ren * Success: return 0 119*081860b9SGuo Ren * Failure: return 1 120*081860b9SGuo Ren */ 121*081860b9SGuo Ren static int sth_c(struct pt_regs *regs, uint32_t rz, uint32_t addr) 122*081860b9SGuo Ren { 123*081860b9SGuo Ren uint32_t byte0, byte1; 124*081860b9SGuo Ren 125*081860b9SGuo Ren byte0 = byte1 = get_ptreg(regs, rz); 126*081860b9SGuo Ren 127*081860b9SGuo Ren byte0 &= 0xff; 128*081860b9SGuo Ren 129*081860b9SGuo Ren if (stb_asm(addr, byte0)) 130*081860b9SGuo Ren return 1; 131*081860b9SGuo Ren 132*081860b9SGuo Ren addr += 1; 133*081860b9SGuo Ren byte1 = (byte1 >> 8) & 0xff; 134*081860b9SGuo Ren if (stb_asm(addr, byte1)) 135*081860b9SGuo Ren return 1; 136*081860b9SGuo Ren 137*081860b9SGuo Ren return 0; 138*081860b9SGuo Ren } 139*081860b9SGuo Ren 140*081860b9SGuo Ren /* 141*081860b9SGuo Ren * Get word from [rx + imm] 142*081860b9SGuo Ren * 143*081860b9SGuo Ren * Success: return 0 144*081860b9SGuo Ren * Failure: return 1 145*081860b9SGuo Ren */ 146*081860b9SGuo Ren static int ldw_c(struct pt_regs *regs, uint32_t rz, uint32_t addr) 147*081860b9SGuo Ren { 148*081860b9SGuo Ren uint32_t byte0, byte1, byte2, byte3; 149*081860b9SGuo Ren 150*081860b9SGuo Ren if (ldb_asm(addr, &byte0)) 151*081860b9SGuo Ren return 1; 152*081860b9SGuo Ren 153*081860b9SGuo Ren addr += 1; 154*081860b9SGuo Ren if (ldb_asm(addr, &byte1)) 155*081860b9SGuo Ren return 1; 156*081860b9SGuo Ren 157*081860b9SGuo Ren addr += 1; 158*081860b9SGuo Ren if (ldb_asm(addr, &byte2)) 159*081860b9SGuo Ren return 1; 160*081860b9SGuo Ren 161*081860b9SGuo Ren addr += 1; 162*081860b9SGuo Ren if (ldb_asm(addr, &byte3)) 163*081860b9SGuo Ren return 1; 164*081860b9SGuo Ren 165*081860b9SGuo Ren byte0 |= byte1 << 8; 166*081860b9SGuo Ren byte0 |= byte2 << 16; 167*081860b9SGuo Ren byte0 |= byte3 << 24; 168*081860b9SGuo Ren 169*081860b9SGuo Ren put_ptreg(regs, rz, byte0); 170*081860b9SGuo Ren 171*081860b9SGuo Ren return 0; 172*081860b9SGuo Ren } 173*081860b9SGuo Ren 174*081860b9SGuo Ren /* 175*081860b9SGuo Ren * Store word to [rx + imm] 176*081860b9SGuo Ren * 177*081860b9SGuo Ren * Success: return 0 178*081860b9SGuo Ren * Failure: return 1 179*081860b9SGuo Ren */ 180*081860b9SGuo Ren static int stw_c(struct pt_regs *regs, uint32_t rz, uint32_t addr) 181*081860b9SGuo Ren { 182*081860b9SGuo Ren uint32_t byte0, byte1, byte2, byte3; 183*081860b9SGuo Ren 184*081860b9SGuo Ren byte0 = byte1 = byte2 = byte3 = get_ptreg(regs, rz); 185*081860b9SGuo Ren 186*081860b9SGuo Ren byte0 &= 0xff; 187*081860b9SGuo Ren 188*081860b9SGuo Ren if (stb_asm(addr, byte0)) 189*081860b9SGuo Ren return 1; 190*081860b9SGuo Ren 191*081860b9SGuo Ren addr += 1; 192*081860b9SGuo Ren byte1 = (byte1 >> 8) & 0xff; 193*081860b9SGuo Ren if (stb_asm(addr, byte1)) 194*081860b9SGuo Ren return 1; 195*081860b9SGuo Ren 196*081860b9SGuo Ren addr += 1; 197*081860b9SGuo Ren byte2 = (byte2 >> 16) & 0xff; 198*081860b9SGuo Ren if (stb_asm(addr, byte2)) 199*081860b9SGuo Ren return 1; 200*081860b9SGuo Ren 201*081860b9SGuo Ren addr += 1; 202*081860b9SGuo Ren byte3 = (byte3 >> 24) & 0xff; 203*081860b9SGuo Ren if (stb_asm(addr, byte3)) 204*081860b9SGuo Ren return 1; 205*081860b9SGuo Ren 206*081860b9SGuo Ren align_count++; 207*081860b9SGuo Ren 208*081860b9SGuo Ren return 0; 209*081860b9SGuo Ren } 210*081860b9SGuo Ren 211*081860b9SGuo Ren extern int fixup_exception(struct pt_regs *regs); 212*081860b9SGuo Ren 213*081860b9SGuo Ren #define OP_LDH 0xc000 214*081860b9SGuo Ren #define OP_STH 0xd000 215*081860b9SGuo Ren #define OP_LDW 0x8000 216*081860b9SGuo Ren #define OP_STW 0x9000 217*081860b9SGuo Ren 218*081860b9SGuo Ren void csky_alignment(struct pt_regs *regs) 219*081860b9SGuo Ren { 220*081860b9SGuo Ren int ret; 221*081860b9SGuo Ren uint16_t tmp; 222*081860b9SGuo Ren uint32_t opcode = 0; 223*081860b9SGuo Ren uint32_t rx = 0; 224*081860b9SGuo Ren uint32_t rz = 0; 225*081860b9SGuo Ren uint32_t imm = 0; 226*081860b9SGuo Ren uint32_t addr = 0; 227*081860b9SGuo Ren 228*081860b9SGuo Ren if (!user_mode(regs)) 229*081860b9SGuo Ren goto bad_area; 230*081860b9SGuo Ren 231*081860b9SGuo Ren ret = get_user(tmp, (uint16_t *)instruction_pointer(regs)); 232*081860b9SGuo Ren if (ret) { 233*081860b9SGuo Ren pr_err("%s get_user failed.\n", __func__); 234*081860b9SGuo Ren goto bad_area; 235*081860b9SGuo Ren } 236*081860b9SGuo Ren 237*081860b9SGuo Ren opcode = (uint32_t)tmp; 238*081860b9SGuo Ren 239*081860b9SGuo Ren rx = opcode & 0xf; 240*081860b9SGuo Ren imm = (opcode >> 4) & 0xf; 241*081860b9SGuo Ren rz = (opcode >> 8) & 0xf; 242*081860b9SGuo Ren opcode &= 0xf000; 243*081860b9SGuo Ren 244*081860b9SGuo Ren if (rx == 0 || rx == 1 || rz == 0 || rz == 1) 245*081860b9SGuo Ren goto bad_area; 246*081860b9SGuo Ren 247*081860b9SGuo Ren switch (opcode) { 248*081860b9SGuo Ren case OP_LDH: 249*081860b9SGuo Ren addr = get_ptreg(regs, rx) + (imm << 1); 250*081860b9SGuo Ren ret = ldh_c(regs, rz, addr); 251*081860b9SGuo Ren break; 252*081860b9SGuo Ren case OP_LDW: 253*081860b9SGuo Ren addr = get_ptreg(regs, rx) + (imm << 2); 254*081860b9SGuo Ren ret = ldw_c(regs, rz, addr); 255*081860b9SGuo Ren break; 256*081860b9SGuo Ren case OP_STH: 257*081860b9SGuo Ren addr = get_ptreg(regs, rx) + (imm << 1); 258*081860b9SGuo Ren ret = sth_c(regs, rz, addr); 259*081860b9SGuo Ren break; 260*081860b9SGuo Ren case OP_STW: 261*081860b9SGuo Ren addr = get_ptreg(regs, rx) + (imm << 2); 262*081860b9SGuo Ren ret = stw_c(regs, rz, addr); 263*081860b9SGuo Ren break; 264*081860b9SGuo Ren } 265*081860b9SGuo Ren 266*081860b9SGuo Ren if (ret) 267*081860b9SGuo Ren goto bad_area; 268*081860b9SGuo Ren 269*081860b9SGuo Ren regs->pc += 2; 270*081860b9SGuo Ren 271*081860b9SGuo Ren return; 272*081860b9SGuo Ren 273*081860b9SGuo Ren bad_area: 274*081860b9SGuo Ren if (!user_mode(regs)) { 275*081860b9SGuo Ren if (fixup_exception(regs)) 276*081860b9SGuo Ren return; 277*081860b9SGuo Ren 278*081860b9SGuo Ren bust_spinlocks(1); 279*081860b9SGuo Ren pr_alert("%s opcode: %x, rz: %d, rx: %d, imm: %d, addr: %x.\n", 280*081860b9SGuo Ren __func__, opcode, rz, rx, imm, addr); 281*081860b9SGuo Ren show_regs(regs); 282*081860b9SGuo Ren bust_spinlocks(0); 283*081860b9SGuo Ren do_exit(SIGKILL); 284*081860b9SGuo Ren } 285*081860b9SGuo Ren 286*081860b9SGuo Ren force_sig_fault(SIGBUS, BUS_ADRALN, (void __user *)addr, current); 287*081860b9SGuo Ren } 288*081860b9SGuo Ren 289*081860b9SGuo Ren static struct ctl_table alignment_tbl[4] = { 290*081860b9SGuo Ren { 291*081860b9SGuo Ren .procname = "enable", 292*081860b9SGuo Ren .data = &align_enable, 293*081860b9SGuo Ren .maxlen = sizeof(align_enable), 294*081860b9SGuo Ren .mode = 0666, 295*081860b9SGuo Ren .proc_handler = &proc_dointvec 296*081860b9SGuo Ren }, 297*081860b9SGuo Ren { 298*081860b9SGuo Ren .procname = "count", 299*081860b9SGuo Ren .data = &align_count, 300*081860b9SGuo Ren .maxlen = sizeof(align_count), 301*081860b9SGuo Ren .mode = 0666, 302*081860b9SGuo Ren .proc_handler = &proc_dointvec 303*081860b9SGuo Ren }, 304*081860b9SGuo Ren {} 305*081860b9SGuo Ren }; 306*081860b9SGuo Ren 307*081860b9SGuo Ren static struct ctl_table sysctl_table[2] = { 308*081860b9SGuo Ren { 309*081860b9SGuo Ren .procname = "csky_alignment", 310*081860b9SGuo Ren .mode = 0555, 311*081860b9SGuo Ren .child = alignment_tbl}, 312*081860b9SGuo Ren {} 313*081860b9SGuo Ren }; 314*081860b9SGuo Ren 315*081860b9SGuo Ren static struct ctl_path sysctl_path[2] = { 316*081860b9SGuo Ren {.procname = "csky"}, 317*081860b9SGuo Ren {} 318*081860b9SGuo Ren }; 319*081860b9SGuo Ren 320*081860b9SGuo Ren static int __init csky_alignment_init(void) 321*081860b9SGuo Ren { 322*081860b9SGuo Ren register_sysctl_paths(sysctl_path, sysctl_table); 323*081860b9SGuo Ren return 0; 324*081860b9SGuo Ren } 325*081860b9SGuo Ren 326*081860b9SGuo Ren arch_initcall(csky_alignment_init); 327