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