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 8081860b9SGuo Ren static int align_enable = 1; 9081860b9SGuo Ren static int align_count; 10081860b9SGuo Ren 11081860b9SGuo Ren static inline uint32_t get_ptreg(struct pt_regs *regs, uint32_t rx) 12081860b9SGuo Ren { 13081860b9SGuo Ren return rx == 15 ? regs->lr : *((uint32_t *)&(regs->a0) - 2 + rx); 14081860b9SGuo Ren } 15081860b9SGuo Ren 16081860b9SGuo Ren static inline void put_ptreg(struct pt_regs *regs, uint32_t rx, uint32_t val) 17081860b9SGuo Ren { 18081860b9SGuo Ren if (rx == 15) 19081860b9SGuo Ren regs->lr = val; 20081860b9SGuo Ren else 21081860b9SGuo Ren *((uint32_t *)&(regs->a0) - 2 + rx) = val; 22081860b9SGuo Ren } 23081860b9SGuo Ren 24081860b9SGuo Ren /* 25081860b9SGuo Ren * Get byte-value from addr and set it to *valp. 26081860b9SGuo Ren * 27081860b9SGuo Ren * Success: return 0 28081860b9SGuo Ren * Failure: return 1 29081860b9SGuo Ren */ 30081860b9SGuo Ren static int ldb_asm(uint32_t addr, uint32_t *valp) 31081860b9SGuo Ren { 32081860b9SGuo Ren uint32_t val; 33081860b9SGuo Ren int err; 34081860b9SGuo Ren 3596d4f267SLinus Torvalds if (!access_ok((void *)addr, 1)) 36081860b9SGuo Ren return 1; 37081860b9SGuo Ren 38081860b9SGuo Ren asm volatile ( 39081860b9SGuo Ren "movi %0, 0\n" 40081860b9SGuo Ren "1:\n" 41081860b9SGuo Ren "ldb %1, (%2)\n" 42081860b9SGuo Ren "br 3f\n" 43081860b9SGuo Ren "2:\n" 44081860b9SGuo Ren "movi %0, 1\n" 45081860b9SGuo Ren "br 3f\n" 46081860b9SGuo Ren ".section __ex_table,\"a\"\n" 47081860b9SGuo Ren ".align 2\n" 48081860b9SGuo Ren ".long 1b, 2b\n" 49081860b9SGuo Ren ".previous\n" 50081860b9SGuo Ren "3:\n" 51081860b9SGuo Ren : "=&r"(err), "=r"(val) 52081860b9SGuo Ren : "r" (addr) 53081860b9SGuo Ren ); 54081860b9SGuo Ren 55081860b9SGuo Ren *valp = val; 56081860b9SGuo Ren 57081860b9SGuo Ren return err; 58081860b9SGuo Ren } 59081860b9SGuo Ren 60081860b9SGuo Ren /* 61081860b9SGuo Ren * Put byte-value to addr. 62081860b9SGuo Ren * 63081860b9SGuo Ren * Success: return 0 64081860b9SGuo Ren * Failure: return 1 65081860b9SGuo Ren */ 66081860b9SGuo Ren static int stb_asm(uint32_t addr, uint32_t val) 67081860b9SGuo Ren { 68081860b9SGuo Ren int err; 69081860b9SGuo Ren 7096d4f267SLinus Torvalds if (!access_ok((void *)addr, 1)) 71081860b9SGuo Ren return 1; 72081860b9SGuo Ren 73081860b9SGuo Ren asm volatile ( 74081860b9SGuo Ren "movi %0, 0\n" 75081860b9SGuo Ren "1:\n" 76081860b9SGuo Ren "stb %1, (%2)\n" 77081860b9SGuo Ren "br 3f\n" 78081860b9SGuo Ren "2:\n" 79081860b9SGuo Ren "movi %0, 1\n" 80081860b9SGuo Ren "br 3f\n" 81081860b9SGuo Ren ".section __ex_table,\"a\"\n" 82081860b9SGuo Ren ".align 2\n" 83081860b9SGuo Ren ".long 1b, 2b\n" 84081860b9SGuo Ren ".previous\n" 85081860b9SGuo Ren "3:\n" 86081860b9SGuo Ren : "=&r"(err) 87081860b9SGuo Ren : "r"(val), "r" (addr) 88081860b9SGuo Ren ); 89081860b9SGuo Ren 90081860b9SGuo Ren return err; 91081860b9SGuo Ren } 92081860b9SGuo Ren 93081860b9SGuo Ren /* 94081860b9SGuo Ren * Get half-word from [rx + imm] 95081860b9SGuo Ren * 96081860b9SGuo Ren * Success: return 0 97081860b9SGuo Ren * Failure: return 1 98081860b9SGuo Ren */ 99081860b9SGuo Ren static int ldh_c(struct pt_regs *regs, uint32_t rz, uint32_t addr) 100081860b9SGuo Ren { 101081860b9SGuo Ren uint32_t byte0, byte1; 102081860b9SGuo Ren 103081860b9SGuo Ren if (ldb_asm(addr, &byte0)) 104081860b9SGuo Ren return 1; 105081860b9SGuo Ren addr += 1; 106081860b9SGuo Ren if (ldb_asm(addr, &byte1)) 107081860b9SGuo Ren return 1; 108081860b9SGuo Ren 109081860b9SGuo Ren byte0 |= byte1 << 8; 110081860b9SGuo Ren put_ptreg(regs, rz, byte0); 111081860b9SGuo Ren 112081860b9SGuo Ren return 0; 113081860b9SGuo Ren } 114081860b9SGuo Ren 115081860b9SGuo Ren /* 116081860b9SGuo Ren * Store half-word to [rx + imm] 117081860b9SGuo Ren * 118081860b9SGuo Ren * Success: return 0 119081860b9SGuo Ren * Failure: return 1 120081860b9SGuo Ren */ 121081860b9SGuo Ren static int sth_c(struct pt_regs *regs, uint32_t rz, uint32_t addr) 122081860b9SGuo Ren { 123081860b9SGuo Ren uint32_t byte0, byte1; 124081860b9SGuo Ren 125081860b9SGuo Ren byte0 = byte1 = get_ptreg(regs, rz); 126081860b9SGuo Ren 127081860b9SGuo Ren byte0 &= 0xff; 128081860b9SGuo Ren 129081860b9SGuo Ren if (stb_asm(addr, byte0)) 130081860b9SGuo Ren return 1; 131081860b9SGuo Ren 132081860b9SGuo Ren addr += 1; 133081860b9SGuo Ren byte1 = (byte1 >> 8) & 0xff; 134081860b9SGuo Ren if (stb_asm(addr, byte1)) 135081860b9SGuo Ren return 1; 136081860b9SGuo Ren 137081860b9SGuo Ren return 0; 138081860b9SGuo Ren } 139081860b9SGuo Ren 140081860b9SGuo Ren /* 141081860b9SGuo Ren * Get word from [rx + imm] 142081860b9SGuo Ren * 143081860b9SGuo Ren * Success: return 0 144081860b9SGuo Ren * Failure: return 1 145081860b9SGuo Ren */ 146081860b9SGuo Ren static int ldw_c(struct pt_regs *regs, uint32_t rz, uint32_t addr) 147081860b9SGuo Ren { 148081860b9SGuo Ren uint32_t byte0, byte1, byte2, byte3; 149081860b9SGuo Ren 150081860b9SGuo Ren if (ldb_asm(addr, &byte0)) 151081860b9SGuo Ren return 1; 152081860b9SGuo Ren 153081860b9SGuo Ren addr += 1; 154081860b9SGuo Ren if (ldb_asm(addr, &byte1)) 155081860b9SGuo Ren return 1; 156081860b9SGuo Ren 157081860b9SGuo Ren addr += 1; 158081860b9SGuo Ren if (ldb_asm(addr, &byte2)) 159081860b9SGuo Ren return 1; 160081860b9SGuo Ren 161081860b9SGuo Ren addr += 1; 162081860b9SGuo Ren if (ldb_asm(addr, &byte3)) 163081860b9SGuo Ren return 1; 164081860b9SGuo Ren 165081860b9SGuo Ren byte0 |= byte1 << 8; 166081860b9SGuo Ren byte0 |= byte2 << 16; 167081860b9SGuo Ren byte0 |= byte3 << 24; 168081860b9SGuo Ren 169081860b9SGuo Ren put_ptreg(regs, rz, byte0); 170081860b9SGuo Ren 171081860b9SGuo Ren return 0; 172081860b9SGuo Ren } 173081860b9SGuo Ren 174081860b9SGuo Ren /* 175081860b9SGuo Ren * Store word to [rx + imm] 176081860b9SGuo Ren * 177081860b9SGuo Ren * Success: return 0 178081860b9SGuo Ren * Failure: return 1 179081860b9SGuo Ren */ 180081860b9SGuo Ren static int stw_c(struct pt_regs *regs, uint32_t rz, uint32_t addr) 181081860b9SGuo Ren { 182081860b9SGuo Ren uint32_t byte0, byte1, byte2, byte3; 183081860b9SGuo Ren 184081860b9SGuo Ren byte0 = byte1 = byte2 = byte3 = get_ptreg(regs, rz); 185081860b9SGuo Ren 186081860b9SGuo Ren byte0 &= 0xff; 187081860b9SGuo Ren 188081860b9SGuo Ren if (stb_asm(addr, byte0)) 189081860b9SGuo Ren return 1; 190081860b9SGuo Ren 191081860b9SGuo Ren addr += 1; 192081860b9SGuo Ren byte1 = (byte1 >> 8) & 0xff; 193081860b9SGuo Ren if (stb_asm(addr, byte1)) 194081860b9SGuo Ren return 1; 195081860b9SGuo Ren 196081860b9SGuo Ren addr += 1; 197081860b9SGuo Ren byte2 = (byte2 >> 16) & 0xff; 198081860b9SGuo Ren if (stb_asm(addr, byte2)) 199081860b9SGuo Ren return 1; 200081860b9SGuo Ren 201081860b9SGuo Ren addr += 1; 202081860b9SGuo Ren byte3 = (byte3 >> 24) & 0xff; 203081860b9SGuo Ren if (stb_asm(addr, byte3)) 204081860b9SGuo Ren return 1; 205081860b9SGuo Ren 206081860b9SGuo Ren align_count++; 207081860b9SGuo Ren 208081860b9SGuo Ren return 0; 209081860b9SGuo Ren } 210081860b9SGuo Ren 211081860b9SGuo Ren extern int fixup_exception(struct pt_regs *regs); 212081860b9SGuo Ren 213081860b9SGuo Ren #define OP_LDH 0xc000 214081860b9SGuo Ren #define OP_STH 0xd000 215081860b9SGuo Ren #define OP_LDW 0x8000 216081860b9SGuo Ren #define OP_STW 0x9000 217081860b9SGuo Ren 218081860b9SGuo Ren void csky_alignment(struct pt_regs *regs) 219081860b9SGuo Ren { 220081860b9SGuo Ren int ret; 221081860b9SGuo Ren uint16_t tmp; 222081860b9SGuo Ren uint32_t opcode = 0; 223081860b9SGuo Ren uint32_t rx = 0; 224081860b9SGuo Ren uint32_t rz = 0; 225081860b9SGuo Ren uint32_t imm = 0; 226081860b9SGuo Ren uint32_t addr = 0; 227081860b9SGuo Ren 228081860b9SGuo Ren if (!user_mode(regs)) 229081860b9SGuo Ren goto bad_area; 230081860b9SGuo Ren 231081860b9SGuo Ren ret = get_user(tmp, (uint16_t *)instruction_pointer(regs)); 232081860b9SGuo Ren if (ret) { 233081860b9SGuo Ren pr_err("%s get_user failed.\n", __func__); 234081860b9SGuo Ren goto bad_area; 235081860b9SGuo Ren } 236081860b9SGuo Ren 237081860b9SGuo Ren opcode = (uint32_t)tmp; 238081860b9SGuo Ren 239081860b9SGuo Ren rx = opcode & 0xf; 240081860b9SGuo Ren imm = (opcode >> 4) & 0xf; 241081860b9SGuo Ren rz = (opcode >> 8) & 0xf; 242081860b9SGuo Ren opcode &= 0xf000; 243081860b9SGuo Ren 244081860b9SGuo Ren if (rx == 0 || rx == 1 || rz == 0 || rz == 1) 245081860b9SGuo Ren goto bad_area; 246081860b9SGuo Ren 247081860b9SGuo Ren switch (opcode) { 248081860b9SGuo Ren case OP_LDH: 249081860b9SGuo Ren addr = get_ptreg(regs, rx) + (imm << 1); 250081860b9SGuo Ren ret = ldh_c(regs, rz, addr); 251081860b9SGuo Ren break; 252081860b9SGuo Ren case OP_LDW: 253081860b9SGuo Ren addr = get_ptreg(regs, rx) + (imm << 2); 254081860b9SGuo Ren ret = ldw_c(regs, rz, addr); 255081860b9SGuo Ren break; 256081860b9SGuo Ren case OP_STH: 257081860b9SGuo Ren addr = get_ptreg(regs, rx) + (imm << 1); 258081860b9SGuo Ren ret = sth_c(regs, rz, addr); 259081860b9SGuo Ren break; 260081860b9SGuo Ren case OP_STW: 261081860b9SGuo Ren addr = get_ptreg(regs, rx) + (imm << 2); 262081860b9SGuo Ren ret = stw_c(regs, rz, addr); 263081860b9SGuo Ren break; 264081860b9SGuo Ren } 265081860b9SGuo Ren 266081860b9SGuo Ren if (ret) 267081860b9SGuo Ren goto bad_area; 268081860b9SGuo Ren 269081860b9SGuo Ren regs->pc += 2; 270081860b9SGuo Ren 271081860b9SGuo Ren return; 272081860b9SGuo Ren 273081860b9SGuo Ren bad_area: 274081860b9SGuo Ren if (!user_mode(regs)) { 275081860b9SGuo Ren if (fixup_exception(regs)) 276081860b9SGuo Ren return; 277081860b9SGuo Ren 278081860b9SGuo Ren bust_spinlocks(1); 279081860b9SGuo Ren pr_alert("%s opcode: %x, rz: %d, rx: %d, imm: %d, addr: %x.\n", 280081860b9SGuo Ren __func__, opcode, rz, rx, imm, addr); 281081860b9SGuo Ren show_regs(regs); 282081860b9SGuo Ren bust_spinlocks(0); 283081860b9SGuo Ren do_exit(SIGKILL); 284081860b9SGuo Ren } 285081860b9SGuo Ren 286*2e1661d2SEric W. Biederman force_sig_fault(SIGBUS, BUS_ADRALN, (void __user *)addr); 287081860b9SGuo Ren } 288081860b9SGuo Ren 289081860b9SGuo Ren static struct ctl_table alignment_tbl[4] = { 290081860b9SGuo Ren { 291081860b9SGuo Ren .procname = "enable", 292081860b9SGuo Ren .data = &align_enable, 293081860b9SGuo Ren .maxlen = sizeof(align_enable), 294081860b9SGuo Ren .mode = 0666, 295081860b9SGuo Ren .proc_handler = &proc_dointvec 296081860b9SGuo Ren }, 297081860b9SGuo Ren { 298081860b9SGuo Ren .procname = "count", 299081860b9SGuo Ren .data = &align_count, 300081860b9SGuo Ren .maxlen = sizeof(align_count), 301081860b9SGuo Ren .mode = 0666, 302081860b9SGuo Ren .proc_handler = &proc_dointvec 303081860b9SGuo Ren }, 304081860b9SGuo Ren {} 305081860b9SGuo Ren }; 306081860b9SGuo Ren 307081860b9SGuo Ren static struct ctl_table sysctl_table[2] = { 308081860b9SGuo Ren { 309081860b9SGuo Ren .procname = "csky_alignment", 310081860b9SGuo Ren .mode = 0555, 311081860b9SGuo Ren .child = alignment_tbl}, 312081860b9SGuo Ren {} 313081860b9SGuo Ren }; 314081860b9SGuo Ren 315081860b9SGuo Ren static struct ctl_path sysctl_path[2] = { 316081860b9SGuo Ren {.procname = "csky"}, 317081860b9SGuo Ren {} 318081860b9SGuo Ren }; 319081860b9SGuo Ren 320081860b9SGuo Ren static int __init csky_alignment_init(void) 321081860b9SGuo Ren { 322081860b9SGuo Ren register_sysctl_paths(sysctl_path, sysctl_table); 323081860b9SGuo Ren return 0; 324081860b9SGuo Ren } 325081860b9SGuo Ren 326081860b9SGuo Ren arch_initcall(csky_alignment_init); 327