1 // SPDX-License-Identifier: GPL-2.0-only
13 #include <sys/types.h>
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)
29 /* MIPS opcodes, in bits 31:26 of an instruction */
30 #define OP_SPECIAL 0x00
31 #define OP_REGIMM 0x01
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
55 /* Bits 5:0 of OP_SPECIAL instructions */
56 #define SPECIAL_SYNC 0x0f
58 static void usage(FILE *f
)
60 fprintf(f
, "Usage: loongson3-llsc-check /path/to/vmlinux\n");
63 static int se16(uint16_t x
)
68 static bool is_ll(uint32_t insn
)
80 static bool is_sc(uint32_t insn
)
92 static bool is_sync(uint32_t insn
)
94 /* Bits 31:11 should all be zeroes */
98 /* Bits 5:0 specify the SYNC special encoding */
99 if ((insn
& 0x3f) != SPECIAL_SYNC
)
105 static bool is_branch(uint32_t insn
, int *off
)
107 switch (insn
>> 26) {
116 *off
= se16(insn
) + 1;
120 switch ((insn
>> 16) & 0x1f) {
129 *off
= se16(insn
) + 1;
141 static int check_ll(uint64_t pc
, uint32_t *code
, size_t sz
)
143 ssize_t i
, max
, sc_pos
;
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.
151 if (!is_sync(le32toh(code
[-1]))) {
152 fprintf(stderr
, "%" PRIx64
": LL not preceded by sync\n", pc
);
156 /* Find the matching SC instruction */
158 for (sc_pos
= 0; sc_pos
< max
; sc_pos
++) {
159 if (is_sc(le32toh(code
[sc_pos
])))
163 fprintf(stderr
, "%" PRIx64
": LL has no matching SC\n", pc
);
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.
172 for (i
= 0; i
< sc_pos
; i
++) {
173 if (!is_branch(le32toh(code
[i
]), &off
))
177 * If the branch target is within the LL/SC loop then we don't
178 * need to worry about it.
180 if ((off
>= -i
) && (off
<= sc_pos
))
183 /* If the branch targets a sync instruction we're all good... */
184 if (is_sync(le32toh(code
[i
+ off
])))
187 /* ...but if not, we have a problem */
188 fprintf(stderr
, "%" PRIx64
": Branch target not a sync\n",
196 static int check_code(uint64_t pc
, uint32_t *code
, size_t sz
)
201 fprintf(stderr
, "%" PRIx64
": Section size not a multiple of 4\n",
207 if (is_ll(le32toh(code
[0]))) {
208 fprintf(stderr
, "%" PRIx64
": First instruction in section is an LL\n",
213 #define advance() ( \
220 * Skip the first instructionm allowing check_ll to look backwards
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
);
234 int main(int argc
, char *argv
[])
236 int vmlinux_fd
, status
, err
, i
;
237 const char *vmlinux_path
;
243 status
= EXIT_FAILURE
;
250 vmlinux_path
= argv
[1];
251 vmlinux_fd
= open(vmlinux_path
, O_RDONLY
);
252 if (vmlinux_fd
== -1) {
253 perror("Unable to open vmlinux");
257 err
= fstat(vmlinux_fd
, &st
);
259 perror("Unable to stat vmlinux");
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");
270 if (memcmp(eh
->e_ident
, ELFMAG
, SELFMAG
)) {
271 fprintf(stderr
, "vmlinux is not an ELF?\n");
275 if (eh
->e_ident
[EI_CLASS
] != ELFCLASS64
) {
276 fprintf(stderr
, "vmlinux is not 64b?\n");
280 if (eh
->e_ident
[EI_DATA
] != ELFDATA2LSB
) {
281 fprintf(stderr
, "vmlinux is not little endian?\n");
285 for (i
= 0; i
< le16toh(eh
->e_shnum
); i
++) {
286 sh
= vmlinux
+ le64toh(eh
->e_shoff
) + (i
* le16toh(eh
->e_shentsize
));
288 if (sh
->sh_type
!= SHT_PROGBITS
)
290 if (!(sh
->sh_flags
& SHF_EXECINSTR
))
293 err
= check_code(le64toh(sh
->sh_addr
),
294 vmlinux
+ le64toh(sh
->sh_offset
),
295 le64toh(sh
->sh_size
));
300 status
= EXIT_SUCCESS
;
302 munmap(vmlinux
, st
.st_size
);