arm64: dts: qcom: sm8550: add TRNG node
[linux-modified.git] / arch / mips / tools / loongson3-llsc-check.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 #include <byteswap.h>
3 #include <elf.h>
4 #include <endian.h>
5 #include <errno.h>
6 #include <fcntl.h>
7 #include <inttypes.h>
8 #include <stdbool.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <sys/mman.h>
13 #include <sys/types.h>
14 #include <sys/stat.h>
15 #include <unistd.h>
16
17 #ifdef be32toh
18 /* If libc provides le{16,32,64}toh() then we'll use them */
19 #elif BYTE_ORDER == LITTLE_ENDIAN
20 # define le16toh(x)     (x)
21 # define le32toh(x)     (x)
22 # define le64toh(x)     (x)
23 #elif BYTE_ORDER == BIG_ENDIAN
24 # define le16toh(x)     bswap_16(x)
25 # define le32toh(x)     bswap_32(x)
26 # define le64toh(x)     bswap_64(x)
27 #endif
28
29 /* MIPS opcodes, in bits 31:26 of an instruction */
30 #define OP_SPECIAL      0x00
31 #define OP_REGIMM       0x01
32 #define OP_BEQ          0x04
33 #define OP_BNE          0x05
34 #define OP_BLEZ         0x06
35 #define OP_BGTZ         0x07
36 #define OP_BEQL         0x14
37 #define OP_BNEL         0x15
38 #define OP_BLEZL        0x16
39 #define OP_BGTZL        0x17
40 #define OP_LL           0x30
41 #define OP_LLD          0x34
42 #define OP_SC           0x38
43 #define OP_SCD          0x3c
44
45 /* Bits 20:16 of OP_REGIMM instructions */
46 #define REGIMM_BLTZ     0x00
47 #define REGIMM_BGEZ     0x01
48 #define REGIMM_BLTZL    0x02
49 #define REGIMM_BGEZL    0x03
50 #define REGIMM_BLTZAL   0x10
51 #define REGIMM_BGEZAL   0x11
52 #define REGIMM_BLTZALL  0x12
53 #define REGIMM_BGEZALL  0x13
54
55 /* Bits 5:0 of OP_SPECIAL instructions */
56 #define SPECIAL_SYNC    0x0f
57
58 static void usage(FILE *f)
59 {
60         fprintf(f, "Usage: loongson3-llsc-check /path/to/vmlinux\n");
61 }
62
63 static int se16(uint16_t x)
64 {
65         return (int16_t)x;
66 }
67
68 static bool is_ll(uint32_t insn)
69 {
70         switch (insn >> 26) {
71         case OP_LL:
72         case OP_LLD:
73                 return true;
74
75         default:
76                 return false;
77         }
78 }
79
80 static bool is_sc(uint32_t insn)
81 {
82         switch (insn >> 26) {
83         case OP_SC:
84         case OP_SCD:
85                 return true;
86
87         default:
88                 return false;
89         }
90 }
91
92 static bool is_sync(uint32_t insn)
93 {
94         /* Bits 31:11 should all be zeroes */
95         if (insn >> 11)
96                 return false;
97
98         /* Bits 5:0 specify the SYNC special encoding */
99         if ((insn & 0x3f) != SPECIAL_SYNC)
100                 return false;
101
102         return true;
103 }
104
105 static bool is_branch(uint32_t insn, int *off)
106 {
107         switch (insn >> 26) {
108         case OP_BEQ:
109         case OP_BEQL:
110         case OP_BNE:
111         case OP_BNEL:
112         case OP_BGTZ:
113         case OP_BGTZL:
114         case OP_BLEZ:
115         case OP_BLEZL:
116                 *off = se16(insn) + 1;
117                 return true;
118
119         case OP_REGIMM:
120                 switch ((insn >> 16) & 0x1f) {
121                 case REGIMM_BGEZ:
122                 case REGIMM_BGEZL:
123                 case REGIMM_BGEZAL:
124                 case REGIMM_BGEZALL:
125                 case REGIMM_BLTZ:
126                 case REGIMM_BLTZL:
127                 case REGIMM_BLTZAL:
128                 case REGIMM_BLTZALL:
129                         *off = se16(insn) + 1;
130                         return true;
131
132                 default:
133                         return false;
134                 }
135
136         default:
137                 return false;
138         }
139 }
140
141 static int check_ll(uint64_t pc, uint32_t *code, size_t sz)
142 {
143         ssize_t i, max, sc_pos;
144         int off;
145
146         /*
147          * Every LL must be preceded by a sync instruction in order to ensure
148          * that instruction reordering doesn't allow a prior memory access to
149          * execute after the LL & cause erroneous results.
150          */
151         if (!is_sync(le32toh(code[-1]))) {
152                 fprintf(stderr, "%" PRIx64 ": LL not preceded by sync\n", pc);
153                 return -EINVAL;
154         }
155
156         /* Find the matching SC instruction */
157         max = sz / 4;
158         for (sc_pos = 0; sc_pos < max; sc_pos++) {
159                 if (is_sc(le32toh(code[sc_pos])))
160                         break;
161         }
162         if (sc_pos >= max) {
163                 fprintf(stderr, "%" PRIx64 ": LL has no matching SC\n", pc);
164                 return -EINVAL;
165         }
166
167         /*
168          * Check branches within the LL/SC loop target sync instructions,
169          * ensuring that speculative execution can't generate memory accesses
170          * due to instructions outside of the loop.
171          */
172         for (i = 0; i < sc_pos; i++) {
173                 if (!is_branch(le32toh(code[i]), &off))
174                         continue;
175
176                 /*
177                  * If the branch target is within the LL/SC loop then we don't
178                  * need to worry about it.
179                  */
180                 if ((off >= -i) && (off <= sc_pos))
181                         continue;
182
183                 /* If the branch targets a sync instruction we're all good... */
184                 if (is_sync(le32toh(code[i + off])))
185                         continue;
186
187                 /* ...but if not, we have a problem */
188                 fprintf(stderr, "%" PRIx64 ": Branch target not a sync\n",
189                         pc + (i * 4));
190                 return -EINVAL;
191         }
192
193         return 0;
194 }
195
196 static int check_code(uint64_t pc, uint32_t *code, size_t sz)
197 {
198         int err = 0;
199
200         if (sz % 4) {
201                 fprintf(stderr, "%" PRIx64 ": Section size not a multiple of 4\n",
202                         pc);
203                 err = -EINVAL;
204                 sz -= (sz % 4);
205         }
206
207         if (is_ll(le32toh(code[0]))) {
208                 fprintf(stderr, "%" PRIx64 ": First instruction in section is an LL\n",
209                         pc);
210                 err = -EINVAL;
211         }
212
213 #define advance() (     \
214         code++,         \
215         pc += 4,        \
216         sz -= 4         \
217 )
218
219         /*
220          * Skip the first instruction, allowing check_ll to look backwards
221          * unconditionally.
222          */
223         advance();
224
225         /* Now scan through the code looking for LL instructions */
226         for (; sz; advance()) {
227                 if (is_ll(le32toh(code[0])))
228                         err |= check_ll(pc, code, sz);
229         }
230
231         return err;
232 }
233
234 int main(int argc, char *argv[])
235 {
236         int vmlinux_fd, status, err, i;
237         const char *vmlinux_path;
238         struct stat st;
239         Elf64_Ehdr *eh;
240         Elf64_Shdr *sh;
241         void *vmlinux;
242
243         status = EXIT_FAILURE;
244
245         if (argc < 2) {
246                 usage(stderr);
247                 goto out_ret;
248         }
249
250         vmlinux_path = argv[1];
251         vmlinux_fd = open(vmlinux_path, O_RDONLY);
252         if (vmlinux_fd == -1) {
253                 perror("Unable to open vmlinux");
254                 goto out_ret;
255         }
256
257         err = fstat(vmlinux_fd, &st);
258         if (err) {
259                 perror("Unable to stat vmlinux");
260                 goto out_close;
261         }
262
263         vmlinux = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, vmlinux_fd, 0);
264         if (vmlinux == MAP_FAILED) {
265                 perror("Unable to mmap vmlinux");
266                 goto out_close;
267         }
268
269         eh = vmlinux;
270         if (memcmp(eh->e_ident, ELFMAG, SELFMAG)) {
271                 fprintf(stderr, "vmlinux is not an ELF?\n");
272                 goto out_munmap;
273         }
274
275         if (eh->e_ident[EI_CLASS] != ELFCLASS64) {
276                 fprintf(stderr, "vmlinux is not 64b?\n");
277                 goto out_munmap;
278         }
279
280         if (eh->e_ident[EI_DATA] != ELFDATA2LSB) {
281                 fprintf(stderr, "vmlinux is not little endian?\n");
282                 goto out_munmap;
283         }
284
285         for (i = 0; i < le16toh(eh->e_shnum); i++) {
286                 sh = vmlinux + le64toh(eh->e_shoff) + (i * le16toh(eh->e_shentsize));
287
288                 if (sh->sh_type != SHT_PROGBITS)
289                         continue;
290                 if (!(sh->sh_flags & SHF_EXECINSTR))
291                         continue;
292
293                 err = check_code(le64toh(sh->sh_addr),
294                                  vmlinux + le64toh(sh->sh_offset),
295                                  le64toh(sh->sh_size));
296                 if (err)
297                         goto out_munmap;
298         }
299
300         status = EXIT_SUCCESS;
301 out_munmap:
302         munmap(vmlinux, st.st_size);
303 out_close:
304         close(vmlinux_fd);
305 out_ret:
306         fprintf(stdout, "loongson3-llsc-check returns %s\n",
307                 status ? "failure" : "success");
308         return status;
309 }