loongson3-llsc-check.c 6.1 KB

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