1 // SPDX-License-Identifier: GPL-2.0-or-later
2
3 /*
4 * Copyright (C) 2018 Intel Corporation
5 * Author: Ammy Yi (ammy.yi@intel.com)
6 */
7
8 /*
9 * This test will check if Intel PT(Intel Processer Trace) is working.
10 *
11 * Intel CPU of 5th-generation Core (Broadwell) or newer is required for the test.
12 *
13 * kconfig requirement: CONFIG_PERF_EVENTS
14 */
15
16
17 #include <sched.h>
18 #include <stdlib.h>
19 #include <stdio.h>
20 #include <string.h>
21 #include "tst_test.h"
22 #include "lapi/syscalls.h"
23 #include "config.h"
24
25 #ifdef HAVE_STRUCT_PERF_EVENT_MMAP_PAGE_AUX_HEAD
26 # include <linux/perf_event.h>
27
28 #define PAGESIZE 4096
29 #define INTEL_PT_MEMSIZE (17*PAGESIZE)
30
31 #define BIT(nr) (1UL << (nr))
32
33 #define INTEL_PT_PATH "/sys/devices/intel_pt"
34 #define INTEL_PT_PMU_TYPE "/sys/devices/intel_pt/type"
35 #define INTEL_PT_FORMAT_TSC "/sys/devices/intel_pt/format/tsc"
36 #define INTEL_PT_FORMAT_NRT "/sys/devices/intel_pt/format/noretcomp"
37
38 //Intel PT event handle
39 int fde = -1;
40 //map head and size
41 uint64_t **bufm;
42 long buhsz;
43 static char *str_mode;
44 static char *str_exclude_info;
45 static char *str_branch_flag;
46 int mode = 1;
47
create_map(int fde,long bufsize,int flag)48 static uint64_t **create_map(int fde, long bufsize, int flag)
49 {
50 uint64_t **buf_ev;
51 int pro_flag;
52 struct perf_event_mmap_page *pc;
53
54 buf_ev = SAFE_MALLOC(2*sizeof(uint64_t *));
55 buf_ev[0] = NULL;
56 buf_ev[1] = NULL;
57 if (flag == 1) {
58 tst_res(TINFO, "Memory will be r/w for full trace mode");
59 pro_flag = PROT_READ | PROT_WRITE;
60 } else {
61 tst_res(TINFO, "Memory will be r only for snapshot mode");
62 pro_flag = PROT_READ;
63 }
64 buf_ev[0] = SAFE_MMAP(NULL, INTEL_PT_MEMSIZE, PROT_READ | PROT_WRITE,
65 MAP_SHARED, fde, 0);
66
67 tst_res(TINFO, "Open Intel PT event failed");
68 pc = (struct perf_event_mmap_page *)buf_ev[0];
69 pc->aux_offset = INTEL_PT_MEMSIZE;
70 pc->aux_size = bufsize;
71 buf_ev[1] = SAFE_MMAP(NULL, bufsize, pro_flag,
72 MAP_SHARED, fde, INTEL_PT_MEMSIZE);
73 return buf_ev;
74 }
75
76
intel_pt_pmu_value(char * dir)77 int intel_pt_pmu_value(char *dir)
78 {
79 char *value;
80 int val = 0;
81 char delims[] = ":";
82
83 SAFE_FILE_SCANF(dir, "%m[^\n]", &value);
84 if (strstr(value, delims) == NULL) {
85 val = atoi(value);
86 } else {
87 strsep(&value, delims);
88 val = atoi(value);
89 }
90 return val;
91 }
92
del_map(uint64_t ** buf_ev,long bufsize)93 static void del_map(uint64_t **buf_ev, long bufsize)
94 {
95 if (buf_ev) {
96 if (buf_ev[0])
97 munmap(buf_ev[0], INTEL_PT_MEMSIZE);
98 if (buf_ev[1])
99 munmap(buf_ev[1], bufsize);
100 }
101
102 free(buf_ev);
103 }
104
intel_pt_trace_check(void)105 static void intel_pt_trace_check(void)
106 {
107 uint64_t aux_head = 0;
108 struct perf_event_mmap_page *pmp;
109 /* enable tracing */
110 SAFE_IOCTL(fde, PERF_EVENT_IOC_RESET);
111 SAFE_IOCTL(fde, PERF_EVENT_IOC_ENABLE);
112
113 /* stop tracing */
114 SAFE_IOCTL(fde, PERF_EVENT_IOC_DISABLE);
115
116 /* check if there is some trace generated */
117 pmp = (struct perf_event_mmap_page *)bufm[0];
118 aux_head = *(volatile uint64_t *)&pmp->aux_head;
119 if (aux_head == 0) {
120 tst_res(TFAIL, "There is no trace");
121 return;
122 }
123
124 tst_res(TPASS, "perf trace test passed");
125 }
126
is_affected_by_erratum_BDM106(void)127 static int is_affected_by_erratum_BDM106(void)
128 {
129 int family = -1, model = -1;
130
131 if (FILE_LINES_SCANF("/proc/cpuinfo", "cpu family%*s%d", &family)
132 || family != 6)
133 return 0;
134
135 if (!FILE_LINES_SCANF("/proc/cpuinfo", "model%*s%d", &model)) {
136 tst_res(TINFO, "Intel FAM6 model %d", model);
137
138 switch (model) {
139 case 0x3D: /* INTEL_FAM6_BROADWELL */
140 /* fallthrough */
141 case 0x47: /* INTEL_FAM6_BROADWELL_G */
142 /* fallthrough */
143 case 0x4F: /* INTEL_FAM6_BROADWELL_X */
144 /* fallthrough */
145 case 0x56: /* INTEL_FAM6_BROADWELL_D */
146 return 1;
147 }
148 }
149
150 return 0;
151 }
152
setup(void)153 static void setup(void)
154 {
155 struct perf_event_attr attr = {};
156
157 buhsz = 2 * PAGESIZE;
158
159 if (access(INTEL_PT_PATH, F_OK)) {
160 tst_brk(TCONF,
161 "Requires Intel Core 5th+ generation (Broadwell and newer) and CONFIG_PERF_EVENTS enabled");
162 }
163
164 /* set attr for Intel PT trace */
165 attr.type = intel_pt_pmu_value(INTEL_PT_PMU_TYPE);
166 attr.read_format = PERF_FORMAT_ID | PERF_FORMAT_TOTAL_TIME_RUNNING |
167 PERF_FORMAT_TOTAL_TIME_ENABLED;
168 attr.disabled = 1;
169 attr.config = BIT(intel_pt_pmu_value(INTEL_PT_FORMAT_TSC)) |
170 BIT(intel_pt_pmu_value(INTEL_PT_FORMAT_NRT));
171 attr.size = sizeof(struct perf_event_attr);
172 attr.mmap = 1;
173 if (str_branch_flag) {
174 if (is_affected_by_erratum_BDM106()) {
175 tst_brk(TCONF, "erratum BDM106 disallows not "
176 "setting BRANCH_EN on this CPU");
177 }
178
179 tst_res(TINFO, "Intel PT will disable branch trace");
180 attr.config |= 1;
181 }
182
183 attr.exclude_kernel = 0;
184 attr.exclude_user = 0;
185 if (str_exclude_info) {
186 if (!strcmp(str_exclude_info, "user")) {
187 tst_res(TINFO, "Intel PT will exclude user trace");
188 attr.exclude_user = 1;
189 } else if (!strcmp(str_exclude_info, "kernel")) {
190 tst_res(TINFO, "Intel PT will exclude kernel trace");
191 attr.exclude_kernel = 1;
192 } else {
193 tst_brk(TBROK, "Invalid -e '%s'", str_exclude_info);
194 }
195 }
196
197 /* only get trace for own pid */
198 fde = tst_syscall(__NR_perf_event_open, &attr, 0, -1, -1, 0);
199 if (fde < 0) {
200 tst_res(TINFO, "Open Intel PT event failed");
201 tst_res(TFAIL, "perf trace full mode failed");
202 return;
203 }
204 bufm = NULL;
205 if (str_mode)
206 mode = 0;
207
208 bufm = create_map(fde, buhsz, mode);
209 }
210
cleanup(void)211 static void cleanup(void)
212 {
213 if (fde != -1)
214 close(fde);
215
216 del_map(bufm, buhsz);
217 }
218
219 static struct tst_test test = {
220 .test_all = intel_pt_trace_check,
221 .options = (struct tst_option[]) {
222 {"m", &str_mode, "Different mode, default is full mode"},
223 {"e:", &str_exclude_info, "Exclude info, user or kernel"},
224 {"b", &str_branch_flag, "Disable branch trace"},
225 {}
226 },
227 .min_kver = "4.1",
228 .setup = setup,
229 .cleanup = cleanup,
230 .needs_root = 1,
231 };
232
233 #else
234 TST_TEST_TCONF("Missing aux_* fields in struct perf_event_mmap_page");
235 #endif /* HAVE_STRUCT_PERF_EVENT_MMAP_PAGE_AUX_HEAD */
236