Merge tag 'sched_urgent_for_v6.2_rc6' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-2.6-microblaze.git] / arch / loongarch / kernel / inst.c
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 }