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 */