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 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 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 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 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 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 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 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 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 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 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