xref: /openbmc/linux/arch/loongarch/kernel/inst.c (revision f412eef0)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
4  */
5 #include <linux/sizes.h>
6 #include <linux/uaccess.h>
7 
8 #include <asm/cacheflush.h>
9 #include <asm/inst.h>
10 
11 static DEFINE_RAW_SPINLOCK(patch_lock);
12 
13 int larch_insn_read(void *addr, u32 *insnp)
14 {
15 	int ret;
16 	u32 val;
17 
18 	ret = copy_from_kernel_nofault(&val, addr, LOONGARCH_INSN_SIZE);
19 	if (!ret)
20 		*insnp = val;
21 
22 	return ret;
23 }
24 
25 int larch_insn_write(void *addr, u32 insn)
26 {
27 	int ret;
28 	unsigned long flags = 0;
29 
30 	raw_spin_lock_irqsave(&patch_lock, flags);
31 	ret = copy_to_kernel_nofault(addr, &insn, LOONGARCH_INSN_SIZE);
32 	raw_spin_unlock_irqrestore(&patch_lock, flags);
33 
34 	return ret;
35 }
36 
37 int larch_insn_patch_text(void *addr, u32 insn)
38 {
39 	int ret;
40 	u32 *tp = addr;
41 
42 	if ((unsigned long)tp & 3)
43 		return -EINVAL;
44 
45 	ret = larch_insn_write(tp, insn);
46 	if (!ret)
47 		flush_icache_range((unsigned long)tp,
48 				   (unsigned long)tp + LOONGARCH_INSN_SIZE);
49 
50 	return ret;
51 }
52 
53 u32 larch_insn_gen_nop(void)
54 {
55 	return INSN_NOP;
56 }
57 
58 u32 larch_insn_gen_b(unsigned long pc, unsigned long dest)
59 {
60 	long offset = dest - pc;
61 	union loongarch_instruction insn;
62 
63 	if ((offset & 3) || offset < -SZ_128M || offset >= SZ_128M) {
64 		pr_warn("The generated b instruction is out of range.\n");
65 		return INSN_BREAK;
66 	}
67 
68 	emit_b(&insn, offset >> 2);
69 
70 	return insn.word;
71 }
72 
73 u32 larch_insn_gen_bl(unsigned long pc, unsigned long dest)
74 {
75 	long offset = dest - pc;
76 	union loongarch_instruction insn;
77 
78 	if ((offset & 3) || offset < -SZ_128M || offset >= SZ_128M) {
79 		pr_warn("The generated bl instruction is out of range.\n");
80 		return INSN_BREAK;
81 	}
82 
83 	emit_bl(&insn, offset >> 2);
84 
85 	return insn.word;
86 }
87 
88 u32 larch_insn_gen_or(enum loongarch_gpr rd, enum loongarch_gpr rj, enum loongarch_gpr rk)
89 {
90 	union loongarch_instruction insn;
91 
92 	emit_or(&insn, rd, rj, rk);
93 
94 	return insn.word;
95 }
96 
97 u32 larch_insn_gen_move(enum loongarch_gpr rd, enum loongarch_gpr rj)
98 {
99 	return larch_insn_gen_or(rd, rj, 0);
100 }
101 
102 u32 larch_insn_gen_lu12iw(enum loongarch_gpr rd, int imm)
103 {
104 	union loongarch_instruction insn;
105 
106 	emit_lu12iw(&insn, rd, imm);
107 
108 	return insn.word;
109 }
110 
111 u32 larch_insn_gen_lu32id(enum loongarch_gpr rd, int imm)
112 {
113 	union loongarch_instruction insn;
114 
115 	emit_lu32id(&insn, rd, imm);
116 
117 	return insn.word;
118 }
119 
120 u32 larch_insn_gen_lu52id(enum loongarch_gpr rd, enum loongarch_gpr rj, int imm)
121 {
122 	union loongarch_instruction insn;
123 
124 	emit_lu52id(&insn, rd, rj, imm);
125 
126 	return insn.word;
127 }
128 
129 u32 larch_insn_gen_jirl(enum loongarch_gpr rd, enum loongarch_gpr rj, unsigned long pc, unsigned long dest)
130 {
131 	union loongarch_instruction insn;
132 
133 	emit_jirl(&insn, rj, rd, (dest - pc) >> 2);
134 
135 	return insn.word;
136 }
137