Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/mips/tools/loongson3-llsc-check.c
26424 views
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
}
310
311