1 // SPDX-License-Identifier: GPL-2.0 2 #include "util/jitdump.h" 3 4 #include <errno.h> 5 #include <fcntl.h> 6 #include <stddef.h> 7 #include <stdint.h> 8 #include <stdio.h> 9 #include <string.h> 10 #include <time.h> 11 12 #include <sys/mman.h> 13 #include <sys/stat.h> 14 #include <sys/types.h> 15 #include <unistd.h> 16 17 #include "../tests.h" 18 19 #ifndef HAVE_GETTID 20 #include <sys/syscall.h> 21 static inline pid_t gettid(void) 22 { 23 return (pid_t)syscall(SYS_gettid); 24 } 25 #endif 26 27 #define CHK_BYTE 0x5a 28 29 static inline uint64_t get_timestamp(void) 30 { 31 #if defined(__x86_64__) || defined(__i386__) 32 unsigned int low, high; 33 34 asm volatile("rdtsc" : "=a"(low), "=d"(high)); 35 36 return low | ((uint64_t)high) << 32; 37 #else 38 struct timespec ts; 39 int ret; 40 41 ret = clock_gettime(CLOCK_MONOTONIC, &ts); 42 if (ret) 43 return 0; 44 45 return ((uint64_t)ts.tv_sec * 1000000000) + ts.tv_nsec; 46 #endif 47 } 48 49 static FILE *open_jitdump(void) 50 { 51 struct jitheader header = { 52 .magic = JITHEADER_MAGIC, 53 .version = JITHEADER_VERSION, 54 .total_size = sizeof(header), 55 .pid = getpid(), 56 .timestamp = get_timestamp(), 57 .flags = 58 #if defined(__x86_64__) || defined(__i386__) 59 JITDUMP_FLAGS_ARCH_TIMESTAMP, 60 #else 61 0, 62 #endif 63 }; 64 char filename[256]; 65 int fd; 66 FILE *f; 67 void *m; 68 69 snprintf(filename, sizeof(filename), "jit-%d.dump", getpid()); 70 /* Securely open using O_CREAT | O_EXCL to prevent symlink attacks. */ 71 fd = open(filename, O_CREAT | O_EXCL | O_RDWR, 0644); 72 if (fd < 0) { 73 pr_err("Failed to open jitdump '%s': %s\n", filename, strerror(errno)); 74 return NULL; 75 } 76 f = fdopen(fd, "w+"); 77 if (!f) { 78 pr_err("Failed to associate stream with fd for '%s'\n", filename); 79 close(fd); 80 unlink(filename); 81 return NULL; 82 } 83 /* Create an MMAP event for the jitdump file. That is how perf tool finds it. */ 84 m = mmap(0, getpagesize(), PROT_READ | PROT_EXEC, MAP_PRIVATE, fileno(f), 0); 85 if (m == MAP_FAILED) { 86 pr_err("mmap failed: %s\n", strerror(errno)); 87 fclose(f); 88 unlink(filename); 89 return NULL; 90 } 91 munmap(m, getpagesize()); 92 93 if (fwrite(&header, sizeof(header), 1, f) != 1) { 94 pr_err("Error writing jitdump header\n"); 95 fclose(f); 96 unlink(filename); 97 return NULL; 98 } 99 return f; 100 } 101 102 static int write_jitdump(FILE *f, void *addr, const void *dat, size_t sz, uint64_t *idx) 103 { 104 const char *sym = "jit_workload"; 105 size_t sym_len = strlen(sym) + 1; 106 struct jr_code_load rec = { 107 .p.id = JIT_CODE_LOAD, 108 .p.total_size = sizeof(rec) + sym_len + sz, 109 .p.timestamp = get_timestamp(), 110 .pid = getpid(), 111 .tid = gettid(), 112 .vma = (unsigned long)addr, 113 .code_addr = (unsigned long)addr, 114 .code_size = sz, 115 .code_index = ++*idx, 116 }; 117 118 if (fwrite(&rec, sizeof(rec), 1, f) != 1 || 119 fwrite(sym, sym_len, 1, f) != 1 || 120 fwrite(dat, sz, 1, f) != 1) 121 return -1; 122 return 0; 123 } 124 125 static void close_jitdump(FILE *f) 126 { 127 fclose(f); 128 } 129 130 static int jitdump(int argc __maybe_unused, const char **argv __maybe_unused) 131 { 132 #if defined(__x86_64__) || defined(__i386__) 133 /* Code to execute: mov CHK_BYTE, %eax ; ret */ 134 uint8_t dat[] = { 0xb8, CHK_BYTE, 0x00, 0x00, 0x00, 0xc3 }; 135 #elif defined(__aarch64__) 136 /* Code to execute: mov w0, #CHK_BYTE ; ret */ 137 uint8_t dat[] = { 138 (CHK_BYTE << 5) & 0xff, (CHK_BYTE >> 3) & 0xff, 0x80, 0x52, 139 0xc0, 0x03, 0x5f, 0xd6 140 }; 141 #elif defined(__riscv) 142 /* Code to execute: li a0, CHK_BYTE ; ret */ 143 uint8_t dat[] = { 144 0x13, 0x05, (CHK_BYTE << 4) & 0xff, (CHK_BYTE >> 4) & 0xff, 145 0x67, 0x80, 0x00, 0x00 146 }; 147 #elif defined(__powerpc__) 148 /* Code to execute: li r3, CHK_BYTE ; blr */ 149 uint32_t dat[] = { 0x38600000 | (CHK_BYTE & 0xffff), 0x4e800020 }; 150 #elif defined(__s390x__) 151 /* Code to execute: lhi %r2, CHK_BYTE ; br %r14 */ 152 uint8_t dat[] = { 0xa7, 0x28, (CHK_BYTE >> 8) & 0xff, CHK_BYTE & 0xff, 0x07, 0xfe }; 153 #elif defined(__arm__) 154 /* Code to execute: mov r0, #CHK_BYTE ; bx lr */ 155 uint8_t dat[] = { 156 CHK_BYTE & 0xff, 0x00, 0xa0, 0xe3, 157 0x1e, 0xff, 0x2f, 0xe1 158 }; 159 #elif defined(__mips__) 160 /* Code to execute: addiu $v0, $zero, CHK_BYTE ; jr $ra ; nop */ 161 uint32_t dat[] = { 0x24020000 | (CHK_BYTE & 0xffff), 0x03e00008, 0x00000000 }; 162 #elif defined(__loongarch__) 163 /* Code to execute: addi.w $a0, $zero, CHK_BYTE ; jirl $zero, $ra, 0 */ 164 uint32_t dat[] = { 0x02800004 | ((CHK_BYTE & 0xfff) << 10), 0x4c000020 }; 165 #else 166 uint32_t dat[0]; 167 #endif 168 void *addr; 169 FILE *f; 170 uint64_t idx = 0; 171 int ret = 1; 172 173 /* Reachable fallback check for unsupported architectures right at start. */ 174 if (sizeof(dat) == 0) { 175 pr_err("JITDUMP workload not supported on this architecture\n"); 176 return 1; 177 } 178 179 /* Get a memory page to store executable code. */ 180 addr = mmap(0, getpagesize(), PROT_READ | PROT_WRITE | PROT_EXEC, 181 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 182 if (addr == MAP_FAILED) { 183 pr_err("Failed to map 1 -rwx page\n"); 184 return 1; 185 } 186 187 f = open_jitdump(); 188 if (!f) { 189 pr_err("Failed to open JITDUMP\n"); 190 munmap(addr, getpagesize()); 191 return 1; 192 } 193 /* Copy executable code to executable memory page. */ 194 memcpy(addr, dat, sizeof(dat)); 195 /* Synchronize the Instruction and Data caches. */ 196 __builtin___clear_cache(addr, (char *)addr + sizeof(dat)); 197 198 /* Record it in the jitdump file */ 199 if (write_jitdump(f, addr, dat, sizeof(dat), &idx) == 0) { 200 int (*fn)(void) = addr; 201 202 /* Call the function. */ 203 ret = fn() - CHK_BYTE; 204 } 205 close_jitdump(f); 206 munmap(addr, getpagesize()); 207 return ret; 208 } 209 210 DEFINE_WORKLOAD(jitdump); 211