1*084d2ac4SLorenzo Stoakes // SPDX-License-Identifier: GPL-2.0-only
2*084d2ac4SLorenzo Stoakes #define _GNU_SOURCE
3*084d2ac4SLorenzo Stoakes
4*084d2ac4SLorenzo Stoakes #include <dirent.h>
5*084d2ac4SLorenzo Stoakes #include <sched.h>
6*084d2ac4SLorenzo Stoakes #include <stdbool.h>
7*084d2ac4SLorenzo Stoakes #include <stdio.h>
8*084d2ac4SLorenzo Stoakes #include <unistd.h>
9*084d2ac4SLorenzo Stoakes
10*084d2ac4SLorenzo Stoakes #include <sys/ioctl.h>
11*084d2ac4SLorenzo Stoakes #include <sys/mman.h>
12*084d2ac4SLorenzo Stoakes #include <sys/syscall.h>
13*084d2ac4SLorenzo Stoakes #include <sys/types.h>
14*084d2ac4SLorenzo Stoakes
15*084d2ac4SLorenzo Stoakes #include <linux/perf_event.h>
16*084d2ac4SLorenzo Stoakes
17*084d2ac4SLorenzo Stoakes #include "../kselftest_harness.h"
18*084d2ac4SLorenzo Stoakes
19*084d2ac4SLorenzo Stoakes #define RB_SIZE 0x3000
20*084d2ac4SLorenzo Stoakes #define AUX_SIZE 0x10000
21*084d2ac4SLorenzo Stoakes #define AUX_OFFS 0x4000
22*084d2ac4SLorenzo Stoakes
23*084d2ac4SLorenzo Stoakes #define HOLE_SIZE 0x1000
24*084d2ac4SLorenzo Stoakes
25*084d2ac4SLorenzo Stoakes /* Reserve space for rb, aux with space for shrink-beyond-vma testing. */
26*084d2ac4SLorenzo Stoakes #define REGION_SIZE (2 * RB_SIZE + 2 * AUX_SIZE)
27*084d2ac4SLorenzo Stoakes #define REGION_AUX_OFFS (2 * RB_SIZE)
28*084d2ac4SLorenzo Stoakes
29*084d2ac4SLorenzo Stoakes #define MAP_BASE 1
30*084d2ac4SLorenzo Stoakes #define MAP_AUX 2
31*084d2ac4SLorenzo Stoakes
32*084d2ac4SLorenzo Stoakes #define EVENT_SRC_DIR "/sys/bus/event_source/devices"
33*084d2ac4SLorenzo Stoakes
FIXTURE(perf_mmap)34*084d2ac4SLorenzo Stoakes FIXTURE(perf_mmap)
35*084d2ac4SLorenzo Stoakes {
36*084d2ac4SLorenzo Stoakes int fd;
37*084d2ac4SLorenzo Stoakes void *ptr;
38*084d2ac4SLorenzo Stoakes void *region;
39*084d2ac4SLorenzo Stoakes };
40*084d2ac4SLorenzo Stoakes
FIXTURE_VARIANT(perf_mmap)41*084d2ac4SLorenzo Stoakes FIXTURE_VARIANT(perf_mmap)
42*084d2ac4SLorenzo Stoakes {
43*084d2ac4SLorenzo Stoakes bool aux;
44*084d2ac4SLorenzo Stoakes unsigned long ptr_size;
45*084d2ac4SLorenzo Stoakes };
46*084d2ac4SLorenzo Stoakes
FIXTURE_VARIANT_ADD(perf_mmap,rb)47*084d2ac4SLorenzo Stoakes FIXTURE_VARIANT_ADD(perf_mmap, rb)
48*084d2ac4SLorenzo Stoakes {
49*084d2ac4SLorenzo Stoakes .aux = false,
50*084d2ac4SLorenzo Stoakes .ptr_size = RB_SIZE,
51*084d2ac4SLorenzo Stoakes };
52*084d2ac4SLorenzo Stoakes
FIXTURE_VARIANT_ADD(perf_mmap,aux)53*084d2ac4SLorenzo Stoakes FIXTURE_VARIANT_ADD(perf_mmap, aux)
54*084d2ac4SLorenzo Stoakes {
55*084d2ac4SLorenzo Stoakes .aux = true,
56*084d2ac4SLorenzo Stoakes .ptr_size = AUX_SIZE,
57*084d2ac4SLorenzo Stoakes };
58*084d2ac4SLorenzo Stoakes
read_event_type(struct dirent * dent,__u32 * type)59*084d2ac4SLorenzo Stoakes static bool read_event_type(struct dirent *dent, __u32 *type)
60*084d2ac4SLorenzo Stoakes {
61*084d2ac4SLorenzo Stoakes char typefn[512];
62*084d2ac4SLorenzo Stoakes FILE *fp;
63*084d2ac4SLorenzo Stoakes int res;
64*084d2ac4SLorenzo Stoakes
65*084d2ac4SLorenzo Stoakes snprintf(typefn, sizeof(typefn), "%s/%s/type", EVENT_SRC_DIR, dent->d_name);
66*084d2ac4SLorenzo Stoakes fp = fopen(typefn, "r");
67*084d2ac4SLorenzo Stoakes if (!fp)
68*084d2ac4SLorenzo Stoakes return false;
69*084d2ac4SLorenzo Stoakes
70*084d2ac4SLorenzo Stoakes res = fscanf(fp, "%u", type);
71*084d2ac4SLorenzo Stoakes fclose(fp);
72*084d2ac4SLorenzo Stoakes return res > 0;
73*084d2ac4SLorenzo Stoakes }
74*084d2ac4SLorenzo Stoakes
FIXTURE_SETUP(perf_mmap)75*084d2ac4SLorenzo Stoakes FIXTURE_SETUP(perf_mmap)
76*084d2ac4SLorenzo Stoakes {
77*084d2ac4SLorenzo Stoakes struct perf_event_attr attr = {
78*084d2ac4SLorenzo Stoakes .size = sizeof(attr),
79*084d2ac4SLorenzo Stoakes .disabled = 1,
80*084d2ac4SLorenzo Stoakes .exclude_kernel = 1,
81*084d2ac4SLorenzo Stoakes .exclude_hv = 1,
82*084d2ac4SLorenzo Stoakes };
83*084d2ac4SLorenzo Stoakes struct perf_event_attr attr_ok = {};
84*084d2ac4SLorenzo Stoakes unsigned int eacces = 0, map = 0;
85*084d2ac4SLorenzo Stoakes struct perf_event_mmap_page *rb;
86*084d2ac4SLorenzo Stoakes struct dirent *dent;
87*084d2ac4SLorenzo Stoakes void *aux, *region;
88*084d2ac4SLorenzo Stoakes DIR *dir;
89*084d2ac4SLorenzo Stoakes
90*084d2ac4SLorenzo Stoakes self->ptr = NULL;
91*084d2ac4SLorenzo Stoakes
92*084d2ac4SLorenzo Stoakes dir = opendir(EVENT_SRC_DIR);
93*084d2ac4SLorenzo Stoakes if (!dir)
94*084d2ac4SLorenzo Stoakes SKIP(return, "perf not available.");
95*084d2ac4SLorenzo Stoakes
96*084d2ac4SLorenzo Stoakes region = mmap(NULL, REGION_SIZE, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0);
97*084d2ac4SLorenzo Stoakes ASSERT_NE(region, MAP_FAILED);
98*084d2ac4SLorenzo Stoakes self->region = region;
99*084d2ac4SLorenzo Stoakes
100*084d2ac4SLorenzo Stoakes // Try to find a suitable event on this system
101*084d2ac4SLorenzo Stoakes while ((dent = readdir(dir))) {
102*084d2ac4SLorenzo Stoakes int fd;
103*084d2ac4SLorenzo Stoakes
104*084d2ac4SLorenzo Stoakes if (!read_event_type(dent, &attr.type))
105*084d2ac4SLorenzo Stoakes continue;
106*084d2ac4SLorenzo Stoakes
107*084d2ac4SLorenzo Stoakes fd = syscall(SYS_perf_event_open, &attr, 0, -1, -1, 0);
108*084d2ac4SLorenzo Stoakes if (fd < 0) {
109*084d2ac4SLorenzo Stoakes if (errno == EACCES)
110*084d2ac4SLorenzo Stoakes eacces++;
111*084d2ac4SLorenzo Stoakes continue;
112*084d2ac4SLorenzo Stoakes }
113*084d2ac4SLorenzo Stoakes
114*084d2ac4SLorenzo Stoakes // Check whether the event supports mmap()
115*084d2ac4SLorenzo Stoakes rb = mmap(region, RB_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, 0);
116*084d2ac4SLorenzo Stoakes if (rb == MAP_FAILED) {
117*084d2ac4SLorenzo Stoakes close(fd);
118*084d2ac4SLorenzo Stoakes continue;
119*084d2ac4SLorenzo Stoakes }
120*084d2ac4SLorenzo Stoakes
121*084d2ac4SLorenzo Stoakes if (!map) {
122*084d2ac4SLorenzo Stoakes // Save the event in case that no AUX capable event is found
123*084d2ac4SLorenzo Stoakes attr_ok = attr;
124*084d2ac4SLorenzo Stoakes map = MAP_BASE;
125*084d2ac4SLorenzo Stoakes }
126*084d2ac4SLorenzo Stoakes
127*084d2ac4SLorenzo Stoakes if (!variant->aux)
128*084d2ac4SLorenzo Stoakes continue;
129*084d2ac4SLorenzo Stoakes
130*084d2ac4SLorenzo Stoakes rb->aux_offset = AUX_OFFS;
131*084d2ac4SLorenzo Stoakes rb->aux_size = AUX_SIZE;
132*084d2ac4SLorenzo Stoakes
133*084d2ac4SLorenzo Stoakes // Check whether it supports a AUX buffer
134*084d2ac4SLorenzo Stoakes aux = mmap(region + REGION_AUX_OFFS, AUX_SIZE, PROT_READ | PROT_WRITE,
135*084d2ac4SLorenzo Stoakes MAP_SHARED | MAP_FIXED, fd, AUX_OFFS);
136*084d2ac4SLorenzo Stoakes if (aux == MAP_FAILED) {
137*084d2ac4SLorenzo Stoakes munmap(rb, RB_SIZE);
138*084d2ac4SLorenzo Stoakes close(fd);
139*084d2ac4SLorenzo Stoakes continue;
140*084d2ac4SLorenzo Stoakes }
141*084d2ac4SLorenzo Stoakes
142*084d2ac4SLorenzo Stoakes attr_ok = attr;
143*084d2ac4SLorenzo Stoakes map = MAP_AUX;
144*084d2ac4SLorenzo Stoakes munmap(aux, AUX_SIZE);
145*084d2ac4SLorenzo Stoakes munmap(rb, RB_SIZE);
146*084d2ac4SLorenzo Stoakes close(fd);
147*084d2ac4SLorenzo Stoakes break;
148*084d2ac4SLorenzo Stoakes }
149*084d2ac4SLorenzo Stoakes closedir(dir);
150*084d2ac4SLorenzo Stoakes
151*084d2ac4SLorenzo Stoakes if (!map) {
152*084d2ac4SLorenzo Stoakes if (!eacces)
153*084d2ac4SLorenzo Stoakes SKIP(return, "No mappable perf event found.");
154*084d2ac4SLorenzo Stoakes else
155*084d2ac4SLorenzo Stoakes SKIP(return, "No permissions for perf_event_open()");
156*084d2ac4SLorenzo Stoakes }
157*084d2ac4SLorenzo Stoakes
158*084d2ac4SLorenzo Stoakes self->fd = syscall(SYS_perf_event_open, &attr_ok, 0, -1, -1, 0);
159*084d2ac4SLorenzo Stoakes ASSERT_NE(self->fd, -1);
160*084d2ac4SLorenzo Stoakes
161*084d2ac4SLorenzo Stoakes rb = mmap(region, RB_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, self->fd, 0);
162*084d2ac4SLorenzo Stoakes ASSERT_NE(rb, MAP_FAILED);
163*084d2ac4SLorenzo Stoakes
164*084d2ac4SLorenzo Stoakes if (!variant->aux) {
165*084d2ac4SLorenzo Stoakes self->ptr = rb;
166*084d2ac4SLorenzo Stoakes return;
167*084d2ac4SLorenzo Stoakes }
168*084d2ac4SLorenzo Stoakes
169*084d2ac4SLorenzo Stoakes if (map != MAP_AUX)
170*084d2ac4SLorenzo Stoakes SKIP(return, "No AUX event found.");
171*084d2ac4SLorenzo Stoakes
172*084d2ac4SLorenzo Stoakes rb->aux_offset = AUX_OFFS;
173*084d2ac4SLorenzo Stoakes rb->aux_size = AUX_SIZE;
174*084d2ac4SLorenzo Stoakes aux = mmap(region + REGION_AUX_OFFS, AUX_SIZE, PROT_READ | PROT_WRITE,
175*084d2ac4SLorenzo Stoakes MAP_SHARED | MAP_FIXED, self->fd, AUX_OFFS);
176*084d2ac4SLorenzo Stoakes ASSERT_NE(aux, MAP_FAILED);
177*084d2ac4SLorenzo Stoakes self->ptr = aux;
178*084d2ac4SLorenzo Stoakes }
179*084d2ac4SLorenzo Stoakes
FIXTURE_TEARDOWN(perf_mmap)180*084d2ac4SLorenzo Stoakes FIXTURE_TEARDOWN(perf_mmap)
181*084d2ac4SLorenzo Stoakes {
182*084d2ac4SLorenzo Stoakes ASSERT_EQ(munmap(self->region, REGION_SIZE), 0);
183*084d2ac4SLorenzo Stoakes if (self->fd != -1)
184*084d2ac4SLorenzo Stoakes ASSERT_EQ(close(self->fd), 0);
185*084d2ac4SLorenzo Stoakes }
186*084d2ac4SLorenzo Stoakes
TEST_F(perf_mmap,remap)187*084d2ac4SLorenzo Stoakes TEST_F(perf_mmap, remap)
188*084d2ac4SLorenzo Stoakes {
189*084d2ac4SLorenzo Stoakes void *tmp, *ptr = self->ptr;
190*084d2ac4SLorenzo Stoakes unsigned long size = variant->ptr_size;
191*084d2ac4SLorenzo Stoakes
192*084d2ac4SLorenzo Stoakes // Test the invalid remaps
193*084d2ac4SLorenzo Stoakes ASSERT_EQ(mremap(ptr, size, HOLE_SIZE, MREMAP_MAYMOVE), MAP_FAILED);
194*084d2ac4SLorenzo Stoakes ASSERT_EQ(mremap(ptr + HOLE_SIZE, size, HOLE_SIZE, MREMAP_MAYMOVE), MAP_FAILED);
195*084d2ac4SLorenzo Stoakes ASSERT_EQ(mremap(ptr + size - HOLE_SIZE, HOLE_SIZE, size, MREMAP_MAYMOVE), MAP_FAILED);
196*084d2ac4SLorenzo Stoakes // Shrink the end of the mapping such that we only unmap past end of the VMA,
197*084d2ac4SLorenzo Stoakes // which should succeed and poke a hole into the PROT_NONE region
198*084d2ac4SLorenzo Stoakes ASSERT_NE(mremap(ptr + size - HOLE_SIZE, size, HOLE_SIZE, MREMAP_MAYMOVE), MAP_FAILED);
199*084d2ac4SLorenzo Stoakes
200*084d2ac4SLorenzo Stoakes // Remap the whole buffer to a new address
201*084d2ac4SLorenzo Stoakes tmp = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
202*084d2ac4SLorenzo Stoakes ASSERT_NE(tmp, MAP_FAILED);
203*084d2ac4SLorenzo Stoakes
204*084d2ac4SLorenzo Stoakes // Try splitting offset 1 hole size into VMA, this should fail
205*084d2ac4SLorenzo Stoakes ASSERT_EQ(mremap(ptr + HOLE_SIZE, size - HOLE_SIZE, size - HOLE_SIZE,
206*084d2ac4SLorenzo Stoakes MREMAP_MAYMOVE | MREMAP_FIXED, tmp), MAP_FAILED);
207*084d2ac4SLorenzo Stoakes // Remapping the whole thing should succeed fine
208*084d2ac4SLorenzo Stoakes ptr = mremap(ptr, size, size, MREMAP_MAYMOVE | MREMAP_FIXED, tmp);
209*084d2ac4SLorenzo Stoakes ASSERT_EQ(ptr, tmp);
210*084d2ac4SLorenzo Stoakes ASSERT_EQ(munmap(tmp, size), 0);
211*084d2ac4SLorenzo Stoakes }
212*084d2ac4SLorenzo Stoakes
TEST_F(perf_mmap,unmap)213*084d2ac4SLorenzo Stoakes TEST_F(perf_mmap, unmap)
214*084d2ac4SLorenzo Stoakes {
215*084d2ac4SLorenzo Stoakes unsigned long size = variant->ptr_size;
216*084d2ac4SLorenzo Stoakes
217*084d2ac4SLorenzo Stoakes // Try to poke holes into the mappings
218*084d2ac4SLorenzo Stoakes ASSERT_NE(munmap(self->ptr, HOLE_SIZE), 0);
219*084d2ac4SLorenzo Stoakes ASSERT_NE(munmap(self->ptr + HOLE_SIZE, HOLE_SIZE), 0);
220*084d2ac4SLorenzo Stoakes ASSERT_NE(munmap(self->ptr + size - HOLE_SIZE, HOLE_SIZE), 0);
221*084d2ac4SLorenzo Stoakes }
222*084d2ac4SLorenzo Stoakes
TEST_F(perf_mmap,map)223*084d2ac4SLorenzo Stoakes TEST_F(perf_mmap, map)
224*084d2ac4SLorenzo Stoakes {
225*084d2ac4SLorenzo Stoakes unsigned long size = variant->ptr_size;
226*084d2ac4SLorenzo Stoakes
227*084d2ac4SLorenzo Stoakes // Try to poke holes into the mappings by mapping anonymous memory over it
228*084d2ac4SLorenzo Stoakes ASSERT_EQ(mmap(self->ptr, HOLE_SIZE, PROT_READ | PROT_WRITE,
229*084d2ac4SLorenzo Stoakes MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0), MAP_FAILED);
230*084d2ac4SLorenzo Stoakes ASSERT_EQ(mmap(self->ptr + HOLE_SIZE, HOLE_SIZE, PROT_READ | PROT_WRITE,
231*084d2ac4SLorenzo Stoakes MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0), MAP_FAILED);
232*084d2ac4SLorenzo Stoakes ASSERT_EQ(mmap(self->ptr + size - HOLE_SIZE, HOLE_SIZE, PROT_READ | PROT_WRITE,
233*084d2ac4SLorenzo Stoakes MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0), MAP_FAILED);
234*084d2ac4SLorenzo Stoakes }
235*084d2ac4SLorenzo Stoakes
236*084d2ac4SLorenzo Stoakes TEST_HARNESS_MAIN
237