projects/linux·blob master · refs

210 lines · 5.2 KiB

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>
21static inline pid_t gettid(void)
22{
23 return (pid_t)syscall(SYS_gettid);
24}
25#endif
26
27#define CHK_BYTE 0x5a
28
29static 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
49static 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
102static 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
125static void close_jitdump(FILE *f)
126{
127 fclose(f);
128}
129
130static 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
210DEFINE_WORKLOAD(jitdump);