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