1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (C) 2005-2017 Andes Technology Corporation
4 #include <linux/proc_fs.h>
5 #include <linux/uaccess.h>
6 #include <linux/sysctl.h>
7 #include <asm/unaligned.h>
9 #define DEBUG(enable, tagged, ...) \
13 pr_warn("[ %30s() ] ", __func__); \
14 pr_warn(__VA_ARGS__); \
18 #define RT(inst) (((inst) >> 20) & 0x1FUL)
19 #define RA(inst) (((inst) >> 15) & 0x1FUL)
20 #define RB(inst) (((inst) >> 10) & 0x1FUL)
21 #define SV(inst) (((inst) >> 8) & 0x3UL)
22 #define IMM(inst) (((inst) >> 0) & 0x3FFFUL)
24 #define RA3(inst) (((inst) >> 3) & 0x7UL)
25 #define RT3(inst) (((inst) >> 6) & 0x7UL)
26 #define IMM3U(inst) (((inst) >> 0) & 0x7UL)
28 #define RA5(inst) (((inst) >> 0) & 0x1FUL)
29 #define RT4(inst) (((inst) >> 5) & 0xFUL)
31 #define __get8_data(val,addr,err) \
33 "1: lbi.bi %1, [%2], #1\n" \
35 " .pushsection .text.fixup,\"ax\"\n" \
40 " .pushsection __ex_table,\"a\"\n" \
44 : "=r" (err), "=&r" (val), "=r" (addr) \
45 : "0" (err), "2" (addr))
47 #define get16_data(addr, val_ptr) \
49 unsigned int err = 0, v, a = addr; \
50 __get8_data(v,a,err); \
52 __get8_data(v,a,err); \
56 *val_ptr = le16_to_cpu(*val_ptr); \
59 #define get32_data(addr, val_ptr) \
61 unsigned int err = 0, v, a = addr; \
62 __get8_data(v,a,err); \
64 __get8_data(v,a,err); \
66 __get8_data(v,a,err); \
67 *val_ptr |= v << 16; \
68 __get8_data(v,a,err); \
69 *val_ptr |= v << 24; \
72 *val_ptr = le32_to_cpu(*val_ptr); \
75 #define get_data(addr, val_ptr, len) \
77 get16_data(addr, val_ptr); \
79 get32_data(addr, val_ptr);
81 #define set16_data(addr, val) \
83 unsigned int err = 0, *ptr = addr ; \
84 val = le32_to_cpu(val); \
86 "1: sbi.bi %2, [%1], #1\n" \
87 " srli %2, %2, #8\n" \
90 " .pushsection .text.fixup,\"ax\"\n" \
95 " .pushsection __ex_table,\"a\"\n" \
100 : "=r" (err), "+r" (ptr), "+r" (val) \
107 #define set32_data(addr, val) \
109 unsigned int err = 0, *ptr = addr ; \
110 val = le32_to_cpu(val); \
112 "1: sbi.bi %2, [%1], #1\n" \
113 " srli %2, %2, #8\n" \
114 "2: sbi.bi %2, [%1], #1\n" \
115 " srli %2, %2, #8\n" \
116 "3: sbi.bi %2, [%1], #1\n" \
117 " srli %2, %2, #8\n" \
118 "4: sbi %2, [%1]\n" \
120 " .pushsection .text.fixup,\"ax\"\n" \
125 " .pushsection __ex_table,\"a\"\n" \
132 : "=r" (err), "+r" (ptr), "+r" (val) \
138 #define set_data(addr, val, len) \
140 set16_data(addr, val); \
142 set32_data(addr, val);
143 #define NDS32_16BIT_INSTRUCTION 0x80000000
145 extern pte_t va_present(struct mm_struct *mm, unsigned long addr);
146 extern pte_t va_kernel_present(unsigned long addr);
147 extern int va_readable(struct pt_regs *regs, unsigned long addr);
148 extern int va_writable(struct pt_regs *regs, unsigned long addr);
150 int unalign_access_mode = 0, unalign_access_debug = 0;
152 static inline unsigned long *idx_to_addr(struct pt_regs *regs, int idx)
154 /* this should be consistent with ptrace.h */
155 if (idx >= 0 && idx <= 25) /* R0-R25 */
156 return ®s->uregs[0] + idx;
157 else if (idx >= 28 && idx <= 30) /* FP, GP, LP */
158 return ®s->fp + (idx - 28);
159 else if (idx == 31) /* SP */
162 return NULL; /* cause a segfault */
165 static inline unsigned long get_inst(unsigned long addr)
167 return be32_to_cpu(get_unaligned((u32 *) addr));
170 static inline unsigned long sign_extend(unsigned long val, int len)
172 unsigned long ret = 0;
173 unsigned char *s, *t;
176 val = cpu_to_le32(val);
184 if (((*(t - 1)) & 0x80) && (i < 4)) {
190 return le32_to_cpu(ret);
193 static inline int do_16(unsigned long inst, struct pt_regs *regs)
195 int imm, regular, load, len, addr_mode, idx_mode;
196 unsigned long unaligned_addr, target_val, source_idx, target_idx,
198 switch ((inst >> 9) & 0x3F) {
200 case 0x12: /* LHI333 */
208 case 0x10: /* LWI333 */
216 case 0x11: /* LWI333.bi */
224 case 0x1A: /* LWI450 */
232 case 0x16: /* SHI333 */
240 case 0x14: /* SWI333 */
248 case 0x15: /* SWI333.bi */
256 case 0x1B: /* SWI450 */
269 if (addr_mode == 3) {
270 unaligned_addr = *idx_to_addr(regs, RA3(inst));
271 source_idx = RA3(inst);
273 unaligned_addr = *idx_to_addr(regs, RA5(inst));
274 source_idx = RA5(inst);
278 target_idx = RT3(inst);
280 target_idx = RT4(inst);
283 shift = IMM3U(inst) * len;
286 unaligned_addr += shift;
289 if (!access_ok(VERIFY_READ, (void *)unaligned_addr, len))
292 get_data(unaligned_addr, &target_val, len);
293 *idx_to_addr(regs, target_idx) = target_val;
295 if (!access_ok(VERIFY_WRITE, (void *)unaligned_addr, len))
297 target_val = *idx_to_addr(regs, target_idx);
298 set_data((void *)unaligned_addr, target_val, len);
302 *idx_to_addr(regs, source_idx) = unaligned_addr + shift;
310 static inline int do_32(unsigned long inst, struct pt_regs *regs)
312 int imm, regular, load, len, sign_ext;
313 unsigned long unaligned_addr, target_val, shift;
315 unaligned_addr = *idx_to_addr(regs, RA(inst));
317 switch ((inst >> 25) << 1) {
326 case 0x0A: /* LHI.bi */
333 case 0x22: /* LHSI */
340 case 0x2A: /* LHSI.bi */
354 case 0x0C: /* LWI.bi */
368 case 0x1A: /* SHI.bi */
382 case 0x1C: /* SWI.bi */
391 switch (inst & 0xff) {
400 case 0x05: /* LH.bi */
414 case 0x15: /* LHS.bi */
428 case 0x06: /* LW.bi */
442 case 0x0D: /* SH.bi */
456 case 0x0E: /* SW.bi */
470 shift = IMM(inst) * len;
472 shift = *idx_to_addr(regs, RB(inst)) << SV(inst);
475 unaligned_addr += shift;
479 if (!access_ok(VERIFY_READ, (void *)unaligned_addr, len))
482 get_data(unaligned_addr, &target_val, len);
485 *idx_to_addr(regs, RT(inst)) =
486 sign_extend(target_val, len);
488 *idx_to_addr(regs, RT(inst)) = target_val;
491 if (!access_ok(VERIFY_WRITE, (void *)unaligned_addr, len))
494 target_val = *idx_to_addr(regs, RT(inst));
495 set_data((void *)unaligned_addr, target_val, len);
499 *idx_to_addr(regs, RA(inst)) = unaligned_addr + shift;
508 int do_unaligned_access(unsigned long addr, struct pt_regs *regs)
512 mm_segment_t seg = get_fs();
514 inst = get_inst(regs->ipc);
516 DEBUG((unalign_access_debug > 0), 1,
517 "Faulting addr: 0x%08lx, pc: 0x%08lx [inst: 0x%08lx ]\n", addr,
522 if (inst & NDS32_16BIT_INSTRUCTION)
523 ret = do_16((inst >> 16) & 0xffff, regs);
525 ret = do_32(inst, regs);
531 #ifdef CONFIG_PROC_FS
533 static struct ctl_table alignment_tbl[3] = {
535 .procname = "enable",
536 .data = &unalign_access_mode,
537 .maxlen = sizeof(unalign_access_mode),
539 .proc_handler = &proc_dointvec
543 .procname = "debug_info",
544 .data = &unalign_access_debug,
545 .maxlen = sizeof(unalign_access_debug),
547 .proc_handler = &proc_dointvec
553 static struct ctl_table nds32_sysctl_table[2] = {
555 .procname = "unaligned_acess",
557 .child = alignment_tbl},
561 static struct ctl_path nds32_path[2] = {
562 {.procname = "nds32"},
567 * Initialize nds32 alignment-correction interface
569 static int __init nds32_sysctl_init(void)
571 register_sysctl_paths(nds32_path, nds32_sysctl_table);
575 __initcall(nds32_sysctl_init);
576 #endif /* CONFIG_PROC_FS */