0001
0002 #include <byteswap.h>
0003 #include <elf.h>
0004 #include <endian.h>
0005 #include <errno.h>
0006 #include <fcntl.h>
0007 #include <inttypes.h>
0008 #include <stdbool.h>
0009 #include <stdio.h>
0010 #include <stdlib.h>
0011 #include <string.h>
0012 #include <sys/mman.h>
0013 #include <sys/types.h>
0014 #include <sys/stat.h>
0015 #include <unistd.h>
0016
0017 #ifdef be32toh
0018
0019 #elif BYTE_ORDER == LITTLE_ENDIAN
0020 # define le16toh(x) (x)
0021 # define le32toh(x) (x)
0022 # define le64toh(x) (x)
0023 #elif BYTE_ORDER == BIG_ENDIAN
0024 # define le16toh(x) bswap_16(x)
0025 # define le32toh(x) bswap_32(x)
0026 # define le64toh(x) bswap_64(x)
0027 #endif
0028
0029
0030 #define OP_SPECIAL 0x00
0031 #define OP_REGIMM 0x01
0032 #define OP_BEQ 0x04
0033 #define OP_BNE 0x05
0034 #define OP_BLEZ 0x06
0035 #define OP_BGTZ 0x07
0036 #define OP_BEQL 0x14
0037 #define OP_BNEL 0x15
0038 #define OP_BLEZL 0x16
0039 #define OP_BGTZL 0x17
0040 #define OP_LL 0x30
0041 #define OP_LLD 0x34
0042 #define OP_SC 0x38
0043 #define OP_SCD 0x3c
0044
0045
0046 #define REGIMM_BLTZ 0x00
0047 #define REGIMM_BGEZ 0x01
0048 #define REGIMM_BLTZL 0x02
0049 #define REGIMM_BGEZL 0x03
0050 #define REGIMM_BLTZAL 0x10
0051 #define REGIMM_BGEZAL 0x11
0052 #define REGIMM_BLTZALL 0x12
0053 #define REGIMM_BGEZALL 0x13
0054
0055
0056 #define SPECIAL_SYNC 0x0f
0057
0058 static void usage(FILE *f)
0059 {
0060 fprintf(f, "Usage: loongson3-llsc-check /path/to/vmlinux\n");
0061 }
0062
0063 static int se16(uint16_t x)
0064 {
0065 return (int16_t)x;
0066 }
0067
0068 static bool is_ll(uint32_t insn)
0069 {
0070 switch (insn >> 26) {
0071 case OP_LL:
0072 case OP_LLD:
0073 return true;
0074
0075 default:
0076 return false;
0077 }
0078 }
0079
0080 static bool is_sc(uint32_t insn)
0081 {
0082 switch (insn >> 26) {
0083 case OP_SC:
0084 case OP_SCD:
0085 return true;
0086
0087 default:
0088 return false;
0089 }
0090 }
0091
0092 static bool is_sync(uint32_t insn)
0093 {
0094
0095 if (insn >> 11)
0096 return false;
0097
0098
0099 if ((insn & 0x3f) != SPECIAL_SYNC)
0100 return false;
0101
0102 return true;
0103 }
0104
0105 static bool is_branch(uint32_t insn, int *off)
0106 {
0107 switch (insn >> 26) {
0108 case OP_BEQ:
0109 case OP_BEQL:
0110 case OP_BNE:
0111 case OP_BNEL:
0112 case OP_BGTZ:
0113 case OP_BGTZL:
0114 case OP_BLEZ:
0115 case OP_BLEZL:
0116 *off = se16(insn) + 1;
0117 return true;
0118
0119 case OP_REGIMM:
0120 switch ((insn >> 16) & 0x1f) {
0121 case REGIMM_BGEZ:
0122 case REGIMM_BGEZL:
0123 case REGIMM_BGEZAL:
0124 case REGIMM_BGEZALL:
0125 case REGIMM_BLTZ:
0126 case REGIMM_BLTZL:
0127 case REGIMM_BLTZAL:
0128 case REGIMM_BLTZALL:
0129 *off = se16(insn) + 1;
0130 return true;
0131
0132 default:
0133 return false;
0134 }
0135
0136 default:
0137 return false;
0138 }
0139 }
0140
0141 static int check_ll(uint64_t pc, uint32_t *code, size_t sz)
0142 {
0143 ssize_t i, max, sc_pos;
0144 int off;
0145
0146
0147
0148
0149
0150
0151 if (!is_sync(le32toh(code[-1]))) {
0152 fprintf(stderr, "%" PRIx64 ": LL not preceded by sync\n", pc);
0153 return -EINVAL;
0154 }
0155
0156
0157 max = sz / 4;
0158 for (sc_pos = 0; sc_pos < max; sc_pos++) {
0159 if (is_sc(le32toh(code[sc_pos])))
0160 break;
0161 }
0162 if (sc_pos >= max) {
0163 fprintf(stderr, "%" PRIx64 ": LL has no matching SC\n", pc);
0164 return -EINVAL;
0165 }
0166
0167
0168
0169
0170
0171
0172 for (i = 0; i < sc_pos; i++) {
0173 if (!is_branch(le32toh(code[i]), &off))
0174 continue;
0175
0176
0177
0178
0179
0180 if ((off >= -i) && (off <= sc_pos))
0181 continue;
0182
0183
0184 if (is_sync(le32toh(code[i + off])))
0185 continue;
0186
0187
0188 fprintf(stderr, "%" PRIx64 ": Branch target not a sync\n",
0189 pc + (i * 4));
0190 return -EINVAL;
0191 }
0192
0193 return 0;
0194 }
0195
0196 static int check_code(uint64_t pc, uint32_t *code, size_t sz)
0197 {
0198 int err = 0;
0199
0200 if (sz % 4) {
0201 fprintf(stderr, "%" PRIx64 ": Section size not a multiple of 4\n",
0202 pc);
0203 err = -EINVAL;
0204 sz -= (sz % 4);
0205 }
0206
0207 if (is_ll(le32toh(code[0]))) {
0208 fprintf(stderr, "%" PRIx64 ": First instruction in section is an LL\n",
0209 pc);
0210 err = -EINVAL;
0211 }
0212
0213 #define advance() ( \
0214 code++, \
0215 pc += 4, \
0216 sz -= 4 \
0217 )
0218
0219
0220
0221
0222
0223 advance();
0224
0225
0226 for (; sz; advance()) {
0227 if (is_ll(le32toh(code[0])))
0228 err |= check_ll(pc, code, sz);
0229 }
0230
0231 return err;
0232 }
0233
0234 int main(int argc, char *argv[])
0235 {
0236 int vmlinux_fd, status, err, i;
0237 const char *vmlinux_path;
0238 struct stat st;
0239 Elf64_Ehdr *eh;
0240 Elf64_Shdr *sh;
0241 void *vmlinux;
0242
0243 status = EXIT_FAILURE;
0244
0245 if (argc < 2) {
0246 usage(stderr);
0247 goto out_ret;
0248 }
0249
0250 vmlinux_path = argv[1];
0251 vmlinux_fd = open(vmlinux_path, O_RDONLY);
0252 if (vmlinux_fd == -1) {
0253 perror("Unable to open vmlinux");
0254 goto out_ret;
0255 }
0256
0257 err = fstat(vmlinux_fd, &st);
0258 if (err) {
0259 perror("Unable to stat vmlinux");
0260 goto out_close;
0261 }
0262
0263 vmlinux = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, vmlinux_fd, 0);
0264 if (vmlinux == MAP_FAILED) {
0265 perror("Unable to mmap vmlinux");
0266 goto out_close;
0267 }
0268
0269 eh = vmlinux;
0270 if (memcmp(eh->e_ident, ELFMAG, SELFMAG)) {
0271 fprintf(stderr, "vmlinux is not an ELF?\n");
0272 goto out_munmap;
0273 }
0274
0275 if (eh->e_ident[EI_CLASS] != ELFCLASS64) {
0276 fprintf(stderr, "vmlinux is not 64b?\n");
0277 goto out_munmap;
0278 }
0279
0280 if (eh->e_ident[EI_DATA] != ELFDATA2LSB) {
0281 fprintf(stderr, "vmlinux is not little endian?\n");
0282 goto out_munmap;
0283 }
0284
0285 for (i = 0; i < le16toh(eh->e_shnum); i++) {
0286 sh = vmlinux + le64toh(eh->e_shoff) + (i * le16toh(eh->e_shentsize));
0287
0288 if (sh->sh_type != SHT_PROGBITS)
0289 continue;
0290 if (!(sh->sh_flags & SHF_EXECINSTR))
0291 continue;
0292
0293 err = check_code(le64toh(sh->sh_addr),
0294 vmlinux + le64toh(sh->sh_offset),
0295 le64toh(sh->sh_size));
0296 if (err)
0297 goto out_munmap;
0298 }
0299
0300 status = EXIT_SUCCESS;
0301 out_munmap:
0302 munmap(vmlinux, st.st_size);
0303 out_close:
0304 close(vmlinux_fd);
0305 out_ret:
0306 fprintf(stdout, "loongson3-llsc-check returns %s\n",
0307 status ? "failure" : "success");
0308 return status;
0309 }