1081860b9SGuo Ren // SPDX-License-Identifier: GPL-2.0 2081860b9SGuo Ren // Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd. 3081860b9SGuo Ren 4081860b9SGuo Ren #include <linux/kernel.h> 5081860b9SGuo Ren #include <linux/uaccess.h> 6081860b9SGuo Ren #include <linux/ptrace.h> 7081860b9SGuo Ren 8*c7e6f0e9SGuo Ren static int align_kern_enable = 1; 9*c7e6f0e9SGuo Ren static int align_usr_enable = 1; 10*c7e6f0e9SGuo Ren static int align_kern_count = 0; 11*c7e6f0e9SGuo Ren static int align_usr_count = 0; 12081860b9SGuo Ren 13081860b9SGuo Ren static inline uint32_t get_ptreg(struct pt_regs *regs, uint32_t rx) 14081860b9SGuo Ren { 15081860b9SGuo Ren return rx == 15 ? regs->lr : *((uint32_t *)&(regs->a0) - 2 + rx); 16081860b9SGuo Ren } 17081860b9SGuo Ren 18081860b9SGuo Ren static inline void put_ptreg(struct pt_regs *regs, uint32_t rx, uint32_t val) 19081860b9SGuo Ren { 20081860b9SGuo Ren if (rx == 15) 21081860b9SGuo Ren regs->lr = val; 22081860b9SGuo Ren else 23081860b9SGuo Ren *((uint32_t *)&(regs->a0) - 2 + rx) = val; 24081860b9SGuo Ren } 25081860b9SGuo Ren 26081860b9SGuo Ren /* 27081860b9SGuo Ren * Get byte-value from addr and set it to *valp. 28081860b9SGuo Ren * 29081860b9SGuo Ren * Success: return 0 30081860b9SGuo Ren * Failure: return 1 31081860b9SGuo Ren */ 32081860b9SGuo Ren static int ldb_asm(uint32_t addr, uint32_t *valp) 33081860b9SGuo Ren { 34081860b9SGuo Ren uint32_t val; 35081860b9SGuo Ren int err; 36081860b9SGuo Ren 37081860b9SGuo Ren asm volatile ( 38081860b9SGuo Ren "movi %0, 0\n" 39081860b9SGuo Ren "1:\n" 40081860b9SGuo Ren "ldb %1, (%2)\n" 41081860b9SGuo Ren "br 3f\n" 42081860b9SGuo Ren "2:\n" 43081860b9SGuo Ren "movi %0, 1\n" 44081860b9SGuo Ren "br 3f\n" 45081860b9SGuo Ren ".section __ex_table,\"a\"\n" 46081860b9SGuo Ren ".align 2\n" 47081860b9SGuo Ren ".long 1b, 2b\n" 48081860b9SGuo Ren ".previous\n" 49081860b9SGuo Ren "3:\n" 50081860b9SGuo Ren : "=&r"(err), "=r"(val) 51081860b9SGuo Ren : "r" (addr) 52081860b9SGuo Ren ); 53081860b9SGuo Ren 54081860b9SGuo Ren *valp = val; 55081860b9SGuo Ren 56081860b9SGuo Ren return err; 57081860b9SGuo Ren } 58081860b9SGuo Ren 59081860b9SGuo Ren /* 60081860b9SGuo Ren * Put byte-value to addr. 61081860b9SGuo Ren * 62081860b9SGuo Ren * Success: return 0 63081860b9SGuo Ren * Failure: return 1 64081860b9SGuo Ren */ 65081860b9SGuo Ren static int stb_asm(uint32_t addr, uint32_t val) 66081860b9SGuo Ren { 67081860b9SGuo Ren int err; 68081860b9SGuo Ren 69081860b9SGuo Ren asm volatile ( 70081860b9SGuo Ren "movi %0, 0\n" 71081860b9SGuo Ren "1:\n" 72081860b9SGuo Ren "stb %1, (%2)\n" 73081860b9SGuo Ren "br 3f\n" 74081860b9SGuo Ren "2:\n" 75081860b9SGuo Ren "movi %0, 1\n" 76081860b9SGuo Ren "br 3f\n" 77081860b9SGuo Ren ".section __ex_table,\"a\"\n" 78081860b9SGuo Ren ".align 2\n" 79081860b9SGuo Ren ".long 1b, 2b\n" 80081860b9SGuo Ren ".previous\n" 81081860b9SGuo Ren "3:\n" 82081860b9SGuo Ren : "=&r"(err) 83081860b9SGuo Ren : "r"(val), "r" (addr) 84081860b9SGuo Ren ); 85081860b9SGuo Ren 86081860b9SGuo Ren return err; 87081860b9SGuo Ren } 88081860b9SGuo Ren 89081860b9SGuo Ren /* 90081860b9SGuo Ren * Get half-word from [rx + imm] 91081860b9SGuo Ren * 92081860b9SGuo Ren * Success: return 0 93081860b9SGuo Ren * Failure: return 1 94081860b9SGuo Ren */ 95081860b9SGuo Ren static int ldh_c(struct pt_regs *regs, uint32_t rz, uint32_t addr) 96081860b9SGuo Ren { 97081860b9SGuo Ren uint32_t byte0, byte1; 98081860b9SGuo Ren 99081860b9SGuo Ren if (ldb_asm(addr, &byte0)) 100081860b9SGuo Ren return 1; 101081860b9SGuo Ren addr += 1; 102081860b9SGuo Ren if (ldb_asm(addr, &byte1)) 103081860b9SGuo Ren return 1; 104081860b9SGuo Ren 105081860b9SGuo Ren byte0 |= byte1 << 8; 106081860b9SGuo Ren put_ptreg(regs, rz, byte0); 107081860b9SGuo Ren 108081860b9SGuo Ren return 0; 109081860b9SGuo Ren } 110081860b9SGuo Ren 111081860b9SGuo Ren /* 112081860b9SGuo Ren * Store half-word to [rx + imm] 113081860b9SGuo Ren * 114081860b9SGuo Ren * Success: return 0 115081860b9SGuo Ren * Failure: return 1 116081860b9SGuo Ren */ 117081860b9SGuo Ren static int sth_c(struct pt_regs *regs, uint32_t rz, uint32_t addr) 118081860b9SGuo Ren { 119081860b9SGuo Ren uint32_t byte0, byte1; 120081860b9SGuo Ren 121081860b9SGuo Ren byte0 = byte1 = get_ptreg(regs, rz); 122081860b9SGuo Ren 123081860b9SGuo Ren byte0 &= 0xff; 124081860b9SGuo Ren 125081860b9SGuo Ren if (stb_asm(addr, byte0)) 126081860b9SGuo Ren return 1; 127081860b9SGuo Ren 128081860b9SGuo Ren addr += 1; 129081860b9SGuo Ren byte1 = (byte1 >> 8) & 0xff; 130081860b9SGuo Ren if (stb_asm(addr, byte1)) 131081860b9SGuo Ren return 1; 132081860b9SGuo Ren 133081860b9SGuo Ren return 0; 134081860b9SGuo Ren } 135081860b9SGuo Ren 136081860b9SGuo Ren /* 137081860b9SGuo Ren * Get word from [rx + imm] 138081860b9SGuo Ren * 139081860b9SGuo Ren * Success: return 0 140081860b9SGuo Ren * Failure: return 1 141081860b9SGuo Ren */ 142081860b9SGuo Ren static int ldw_c(struct pt_regs *regs, uint32_t rz, uint32_t addr) 143081860b9SGuo Ren { 144081860b9SGuo Ren uint32_t byte0, byte1, byte2, byte3; 145081860b9SGuo Ren 146081860b9SGuo Ren if (ldb_asm(addr, &byte0)) 147081860b9SGuo Ren return 1; 148081860b9SGuo Ren 149081860b9SGuo Ren addr += 1; 150081860b9SGuo Ren if (ldb_asm(addr, &byte1)) 151081860b9SGuo Ren return 1; 152081860b9SGuo Ren 153081860b9SGuo Ren addr += 1; 154081860b9SGuo Ren if (ldb_asm(addr, &byte2)) 155081860b9SGuo Ren return 1; 156081860b9SGuo Ren 157081860b9SGuo Ren addr += 1; 158081860b9SGuo Ren if (ldb_asm(addr, &byte3)) 159081860b9SGuo Ren return 1; 160081860b9SGuo Ren 161081860b9SGuo Ren byte0 |= byte1 << 8; 162081860b9SGuo Ren byte0 |= byte2 << 16; 163081860b9SGuo Ren byte0 |= byte3 << 24; 164081860b9SGuo Ren 165081860b9SGuo Ren put_ptreg(regs, rz, byte0); 166081860b9SGuo Ren 167081860b9SGuo Ren return 0; 168081860b9SGuo Ren } 169081860b9SGuo Ren 170081860b9SGuo Ren /* 171081860b9SGuo Ren * Store word to [rx + imm] 172081860b9SGuo Ren * 173081860b9SGuo Ren * Success: return 0 174081860b9SGuo Ren * Failure: return 1 175081860b9SGuo Ren */ 176081860b9SGuo Ren static int stw_c(struct pt_regs *regs, uint32_t rz, uint32_t addr) 177081860b9SGuo Ren { 178081860b9SGuo Ren uint32_t byte0, byte1, byte2, byte3; 179081860b9SGuo Ren 180081860b9SGuo Ren byte0 = byte1 = byte2 = byte3 = get_ptreg(regs, rz); 181081860b9SGuo Ren 182081860b9SGuo Ren byte0 &= 0xff; 183081860b9SGuo Ren 184081860b9SGuo Ren if (stb_asm(addr, byte0)) 185081860b9SGuo Ren return 1; 186081860b9SGuo Ren 187081860b9SGuo Ren addr += 1; 188081860b9SGuo Ren byte1 = (byte1 >> 8) & 0xff; 189081860b9SGuo Ren if (stb_asm(addr, byte1)) 190081860b9SGuo Ren return 1; 191081860b9SGuo Ren 192081860b9SGuo Ren addr += 1; 193081860b9SGuo Ren byte2 = (byte2 >> 16) & 0xff; 194081860b9SGuo Ren if (stb_asm(addr, byte2)) 195081860b9SGuo Ren return 1; 196081860b9SGuo Ren 197081860b9SGuo Ren addr += 1; 198081860b9SGuo Ren byte3 = (byte3 >> 24) & 0xff; 199081860b9SGuo Ren if (stb_asm(addr, byte3)) 200081860b9SGuo Ren return 1; 201081860b9SGuo Ren 202081860b9SGuo Ren return 0; 203081860b9SGuo Ren } 204081860b9SGuo Ren 205081860b9SGuo Ren extern int fixup_exception(struct pt_regs *regs); 206081860b9SGuo Ren 207081860b9SGuo Ren #define OP_LDH 0xc000 208081860b9SGuo Ren #define OP_STH 0xd000 209081860b9SGuo Ren #define OP_LDW 0x8000 210081860b9SGuo Ren #define OP_STW 0x9000 211081860b9SGuo Ren 212081860b9SGuo Ren void csky_alignment(struct pt_regs *regs) 213081860b9SGuo Ren { 214081860b9SGuo Ren int ret; 215081860b9SGuo Ren uint16_t tmp; 216081860b9SGuo Ren uint32_t opcode = 0; 217081860b9SGuo Ren uint32_t rx = 0; 218081860b9SGuo Ren uint32_t rz = 0; 219081860b9SGuo Ren uint32_t imm = 0; 220081860b9SGuo Ren uint32_t addr = 0; 221081860b9SGuo Ren 222081860b9SGuo Ren if (!user_mode(regs)) 223*c7e6f0e9SGuo Ren goto kernel_area; 224*c7e6f0e9SGuo Ren 225*c7e6f0e9SGuo Ren if (!align_usr_enable) { 226*c7e6f0e9SGuo Ren pr_err("%s user disabled.\n", __func__); 227081860b9SGuo Ren goto bad_area; 228*c7e6f0e9SGuo Ren } 229*c7e6f0e9SGuo Ren 230*c7e6f0e9SGuo Ren align_usr_count++; 231081860b9SGuo Ren 232081860b9SGuo Ren ret = get_user(tmp, (uint16_t *)instruction_pointer(regs)); 233081860b9SGuo Ren if (ret) { 234081860b9SGuo Ren pr_err("%s get_user failed.\n", __func__); 235081860b9SGuo Ren goto bad_area; 236081860b9SGuo Ren } 237081860b9SGuo Ren 238*c7e6f0e9SGuo Ren goto good_area; 239*c7e6f0e9SGuo Ren 240*c7e6f0e9SGuo Ren kernel_area: 241*c7e6f0e9SGuo Ren if (!align_kern_enable) { 242*c7e6f0e9SGuo Ren pr_err("%s kernel disabled.\n", __func__); 243*c7e6f0e9SGuo Ren goto bad_area; 244*c7e6f0e9SGuo Ren } 245*c7e6f0e9SGuo Ren 246*c7e6f0e9SGuo Ren align_kern_count++; 247*c7e6f0e9SGuo Ren 248*c7e6f0e9SGuo Ren tmp = *(uint16_t *)instruction_pointer(regs); 249*c7e6f0e9SGuo Ren 250*c7e6f0e9SGuo Ren good_area: 251081860b9SGuo Ren opcode = (uint32_t)tmp; 252081860b9SGuo Ren 253081860b9SGuo Ren rx = opcode & 0xf; 254081860b9SGuo Ren imm = (opcode >> 4) & 0xf; 255081860b9SGuo Ren rz = (opcode >> 8) & 0xf; 256081860b9SGuo Ren opcode &= 0xf000; 257081860b9SGuo Ren 258081860b9SGuo Ren if (rx == 0 || rx == 1 || rz == 0 || rz == 1) 259081860b9SGuo Ren goto bad_area; 260081860b9SGuo Ren 261081860b9SGuo Ren switch (opcode) { 262081860b9SGuo Ren case OP_LDH: 263081860b9SGuo Ren addr = get_ptreg(regs, rx) + (imm << 1); 264081860b9SGuo Ren ret = ldh_c(regs, rz, addr); 265081860b9SGuo Ren break; 266081860b9SGuo Ren case OP_LDW: 267081860b9SGuo Ren addr = get_ptreg(regs, rx) + (imm << 2); 268081860b9SGuo Ren ret = ldw_c(regs, rz, addr); 269081860b9SGuo Ren break; 270081860b9SGuo Ren case OP_STH: 271081860b9SGuo Ren addr = get_ptreg(regs, rx) + (imm << 1); 272081860b9SGuo Ren ret = sth_c(regs, rz, addr); 273081860b9SGuo Ren break; 274081860b9SGuo Ren case OP_STW: 275081860b9SGuo Ren addr = get_ptreg(regs, rx) + (imm << 2); 276081860b9SGuo Ren ret = stw_c(regs, rz, addr); 277081860b9SGuo Ren break; 278081860b9SGuo Ren } 279081860b9SGuo Ren 280081860b9SGuo Ren if (ret) 281081860b9SGuo Ren goto bad_area; 282081860b9SGuo Ren 283081860b9SGuo Ren regs->pc += 2; 284081860b9SGuo Ren 285081860b9SGuo Ren return; 286081860b9SGuo Ren 287081860b9SGuo Ren bad_area: 288081860b9SGuo Ren if (!user_mode(regs)) { 289081860b9SGuo Ren if (fixup_exception(regs)) 290081860b9SGuo Ren return; 291081860b9SGuo Ren 292081860b9SGuo Ren bust_spinlocks(1); 293081860b9SGuo Ren pr_alert("%s opcode: %x, rz: %d, rx: %d, imm: %d, addr: %x.\n", 294081860b9SGuo Ren __func__, opcode, rz, rx, imm, addr); 295081860b9SGuo Ren show_regs(regs); 296081860b9SGuo Ren bust_spinlocks(0); 297081860b9SGuo Ren do_exit(SIGKILL); 298081860b9SGuo Ren } 299081860b9SGuo Ren 3002e1661d2SEric W. Biederman force_sig_fault(SIGBUS, BUS_ADRALN, (void __user *)addr); 301081860b9SGuo Ren } 302081860b9SGuo Ren 303*c7e6f0e9SGuo Ren static struct ctl_table alignment_tbl[5] = { 304081860b9SGuo Ren { 305*c7e6f0e9SGuo Ren .procname = "kernel_enable", 306*c7e6f0e9SGuo Ren .data = &align_kern_enable, 307*c7e6f0e9SGuo Ren .maxlen = sizeof(align_kern_enable), 308081860b9SGuo Ren .mode = 0666, 309081860b9SGuo Ren .proc_handler = &proc_dointvec 310081860b9SGuo Ren }, 311081860b9SGuo Ren { 312*c7e6f0e9SGuo Ren .procname = "user_enable", 313*c7e6f0e9SGuo Ren .data = &align_usr_enable, 314*c7e6f0e9SGuo Ren .maxlen = sizeof(align_usr_enable), 315*c7e6f0e9SGuo Ren .mode = 0666, 316*c7e6f0e9SGuo Ren .proc_handler = &proc_dointvec 317*c7e6f0e9SGuo Ren }, 318*c7e6f0e9SGuo Ren { 319*c7e6f0e9SGuo Ren .procname = "kernel_count", 320*c7e6f0e9SGuo Ren .data = &align_kern_count, 321*c7e6f0e9SGuo Ren .maxlen = sizeof(align_kern_count), 322*c7e6f0e9SGuo Ren .mode = 0666, 323*c7e6f0e9SGuo Ren .proc_handler = &proc_dointvec 324*c7e6f0e9SGuo Ren }, 325*c7e6f0e9SGuo Ren { 326*c7e6f0e9SGuo Ren .procname = "user_count", 327*c7e6f0e9SGuo Ren .data = &align_usr_count, 328*c7e6f0e9SGuo Ren .maxlen = sizeof(align_usr_count), 329081860b9SGuo Ren .mode = 0666, 330081860b9SGuo Ren .proc_handler = &proc_dointvec 331081860b9SGuo Ren }, 332081860b9SGuo Ren {} 333081860b9SGuo Ren }; 334081860b9SGuo Ren 335081860b9SGuo Ren static struct ctl_table sysctl_table[2] = { 336081860b9SGuo Ren { 337081860b9SGuo Ren .procname = "csky_alignment", 338081860b9SGuo Ren .mode = 0555, 339081860b9SGuo Ren .child = alignment_tbl}, 340081860b9SGuo Ren {} 341081860b9SGuo Ren }; 342081860b9SGuo Ren 343081860b9SGuo Ren static struct ctl_path sysctl_path[2] = { 344081860b9SGuo Ren {.procname = "csky"}, 345081860b9SGuo Ren {} 346081860b9SGuo Ren }; 347081860b9SGuo Ren 348081860b9SGuo Ren static int __init csky_alignment_init(void) 349081860b9SGuo Ren { 350081860b9SGuo Ren register_sysctl_paths(sysctl_path, sysctl_table); 351081860b9SGuo Ren return 0; 352081860b9SGuo Ren } 353081860b9SGuo Ren 354081860b9SGuo Ren arch_initcall(csky_alignment_init); 355