1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Exercise /dev/mem mmap cases that have been troublesome in the past
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * (c) Copyright 2007 Hewlett-Packard Development Company, L.P.
6*4882a593Smuzhiyun * Bjorn Helgaas <bjorn.helgaas@hp.com>
7*4882a593Smuzhiyun */
8*4882a593Smuzhiyun
9*4882a593Smuzhiyun #include <stdlib.h>
10*4882a593Smuzhiyun #include <stdio.h>
11*4882a593Smuzhiyun #include <sys/types.h>
12*4882a593Smuzhiyun #include <dirent.h>
13*4882a593Smuzhiyun #include <fcntl.h>
14*4882a593Smuzhiyun #include <fnmatch.h>
15*4882a593Smuzhiyun #include <string.h>
16*4882a593Smuzhiyun #include <sys/ioctl.h>
17*4882a593Smuzhiyun #include <sys/mman.h>
18*4882a593Smuzhiyun #include <sys/stat.h>
19*4882a593Smuzhiyun #include <unistd.h>
20*4882a593Smuzhiyun #include <linux/pci.h>
21*4882a593Smuzhiyun
22*4882a593Smuzhiyun int sum;
23*4882a593Smuzhiyun
map_mem(char * path,off_t offset,size_t length,int touch)24*4882a593Smuzhiyun static int map_mem(char *path, off_t offset, size_t length, int touch)
25*4882a593Smuzhiyun {
26*4882a593Smuzhiyun int fd, rc;
27*4882a593Smuzhiyun void *addr;
28*4882a593Smuzhiyun int *c;
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun fd = open(path, O_RDWR);
31*4882a593Smuzhiyun if (fd == -1) {
32*4882a593Smuzhiyun perror(path);
33*4882a593Smuzhiyun return -1;
34*4882a593Smuzhiyun }
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun if (fnmatch("/proc/bus/pci/*", path, 0) == 0) {
37*4882a593Smuzhiyun rc = ioctl(fd, PCIIOC_MMAP_IS_MEM);
38*4882a593Smuzhiyun if (rc == -1)
39*4882a593Smuzhiyun perror("PCIIOC_MMAP_IS_MEM ioctl");
40*4882a593Smuzhiyun }
41*4882a593Smuzhiyun
42*4882a593Smuzhiyun addr = mmap(NULL, length, PROT_READ|PROT_WRITE, MAP_SHARED, fd, offset);
43*4882a593Smuzhiyun if (addr == MAP_FAILED)
44*4882a593Smuzhiyun return 1;
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun if (touch) {
47*4882a593Smuzhiyun c = (int *) addr;
48*4882a593Smuzhiyun while (c < (int *) (addr + length))
49*4882a593Smuzhiyun sum += *c++;
50*4882a593Smuzhiyun }
51*4882a593Smuzhiyun
52*4882a593Smuzhiyun rc = munmap(addr, length);
53*4882a593Smuzhiyun if (rc == -1) {
54*4882a593Smuzhiyun perror("munmap");
55*4882a593Smuzhiyun return -1;
56*4882a593Smuzhiyun }
57*4882a593Smuzhiyun
58*4882a593Smuzhiyun close(fd);
59*4882a593Smuzhiyun return 0;
60*4882a593Smuzhiyun }
61*4882a593Smuzhiyun
scan_tree(char * path,char * file,off_t offset,size_t length,int touch)62*4882a593Smuzhiyun static int scan_tree(char *path, char *file, off_t offset, size_t length, int touch)
63*4882a593Smuzhiyun {
64*4882a593Smuzhiyun struct dirent **namelist;
65*4882a593Smuzhiyun char *name, *path2;
66*4882a593Smuzhiyun int i, n, r, rc = 0, result = 0;
67*4882a593Smuzhiyun struct stat buf;
68*4882a593Smuzhiyun
69*4882a593Smuzhiyun n = scandir(path, &namelist, 0, alphasort);
70*4882a593Smuzhiyun if (n < 0) {
71*4882a593Smuzhiyun perror("scandir");
72*4882a593Smuzhiyun return -1;
73*4882a593Smuzhiyun }
74*4882a593Smuzhiyun
75*4882a593Smuzhiyun for (i = 0; i < n; i++) {
76*4882a593Smuzhiyun name = namelist[i]->d_name;
77*4882a593Smuzhiyun
78*4882a593Smuzhiyun if (fnmatch(".", name, 0) == 0)
79*4882a593Smuzhiyun goto skip;
80*4882a593Smuzhiyun if (fnmatch("..", name, 0) == 0)
81*4882a593Smuzhiyun goto skip;
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun path2 = malloc(strlen(path) + strlen(name) + 3);
84*4882a593Smuzhiyun strcpy(path2, path);
85*4882a593Smuzhiyun strcat(path2, "/");
86*4882a593Smuzhiyun strcat(path2, name);
87*4882a593Smuzhiyun
88*4882a593Smuzhiyun if (fnmatch(file, name, 0) == 0) {
89*4882a593Smuzhiyun rc = map_mem(path2, offset, length, touch);
90*4882a593Smuzhiyun if (rc == 0)
91*4882a593Smuzhiyun fprintf(stderr, "PASS: %s 0x%lx-0x%lx is %s\n", path2, offset, offset + length, touch ? "readable" : "mappable");
92*4882a593Smuzhiyun else if (rc > 0)
93*4882a593Smuzhiyun fprintf(stderr, "PASS: %s 0x%lx-0x%lx not mappable\n", path2, offset, offset + length);
94*4882a593Smuzhiyun else {
95*4882a593Smuzhiyun fprintf(stderr, "FAIL: %s 0x%lx-0x%lx not accessible\n", path2, offset, offset + length);
96*4882a593Smuzhiyun return rc;
97*4882a593Smuzhiyun }
98*4882a593Smuzhiyun } else {
99*4882a593Smuzhiyun r = lstat(path2, &buf);
100*4882a593Smuzhiyun if (r == 0 && S_ISDIR(buf.st_mode)) {
101*4882a593Smuzhiyun rc = scan_tree(path2, file, offset, length, touch);
102*4882a593Smuzhiyun if (rc < 0)
103*4882a593Smuzhiyun return rc;
104*4882a593Smuzhiyun }
105*4882a593Smuzhiyun }
106*4882a593Smuzhiyun
107*4882a593Smuzhiyun result |= rc;
108*4882a593Smuzhiyun free(path2);
109*4882a593Smuzhiyun
110*4882a593Smuzhiyun skip:
111*4882a593Smuzhiyun free(namelist[i]);
112*4882a593Smuzhiyun }
113*4882a593Smuzhiyun free(namelist);
114*4882a593Smuzhiyun return result;
115*4882a593Smuzhiyun }
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun char buf[1024];
118*4882a593Smuzhiyun
read_rom(char * path)119*4882a593Smuzhiyun static int read_rom(char *path)
120*4882a593Smuzhiyun {
121*4882a593Smuzhiyun int fd, rc;
122*4882a593Smuzhiyun size_t size = 0;
123*4882a593Smuzhiyun
124*4882a593Smuzhiyun fd = open(path, O_RDWR);
125*4882a593Smuzhiyun if (fd == -1) {
126*4882a593Smuzhiyun perror(path);
127*4882a593Smuzhiyun return -1;
128*4882a593Smuzhiyun }
129*4882a593Smuzhiyun
130*4882a593Smuzhiyun rc = write(fd, "1", 2);
131*4882a593Smuzhiyun if (rc <= 0) {
132*4882a593Smuzhiyun close(fd);
133*4882a593Smuzhiyun perror("write");
134*4882a593Smuzhiyun return -1;
135*4882a593Smuzhiyun }
136*4882a593Smuzhiyun
137*4882a593Smuzhiyun do {
138*4882a593Smuzhiyun rc = read(fd, buf, sizeof(buf));
139*4882a593Smuzhiyun if (rc > 0)
140*4882a593Smuzhiyun size += rc;
141*4882a593Smuzhiyun } while (rc > 0);
142*4882a593Smuzhiyun
143*4882a593Smuzhiyun close(fd);
144*4882a593Smuzhiyun return size;
145*4882a593Smuzhiyun }
146*4882a593Smuzhiyun
scan_rom(char * path,char * file)147*4882a593Smuzhiyun static int scan_rom(char *path, char *file)
148*4882a593Smuzhiyun {
149*4882a593Smuzhiyun struct dirent **namelist;
150*4882a593Smuzhiyun char *name, *path2;
151*4882a593Smuzhiyun int i, n, r, rc = 0, result = 0;
152*4882a593Smuzhiyun struct stat buf;
153*4882a593Smuzhiyun
154*4882a593Smuzhiyun n = scandir(path, &namelist, 0, alphasort);
155*4882a593Smuzhiyun if (n < 0) {
156*4882a593Smuzhiyun perror("scandir");
157*4882a593Smuzhiyun return -1;
158*4882a593Smuzhiyun }
159*4882a593Smuzhiyun
160*4882a593Smuzhiyun for (i = 0; i < n; i++) {
161*4882a593Smuzhiyun name = namelist[i]->d_name;
162*4882a593Smuzhiyun
163*4882a593Smuzhiyun if (fnmatch(".", name, 0) == 0)
164*4882a593Smuzhiyun goto skip;
165*4882a593Smuzhiyun if (fnmatch("..", name, 0) == 0)
166*4882a593Smuzhiyun goto skip;
167*4882a593Smuzhiyun
168*4882a593Smuzhiyun path2 = malloc(strlen(path) + strlen(name) + 3);
169*4882a593Smuzhiyun strcpy(path2, path);
170*4882a593Smuzhiyun strcat(path2, "/");
171*4882a593Smuzhiyun strcat(path2, name);
172*4882a593Smuzhiyun
173*4882a593Smuzhiyun if (fnmatch(file, name, 0) == 0) {
174*4882a593Smuzhiyun rc = read_rom(path2);
175*4882a593Smuzhiyun
176*4882a593Smuzhiyun /*
177*4882a593Smuzhiyun * It's OK if the ROM is unreadable. Maybe there
178*4882a593Smuzhiyun * is no ROM, or some other error occurred. The
179*4882a593Smuzhiyun * important thing is that no MCA happened.
180*4882a593Smuzhiyun */
181*4882a593Smuzhiyun if (rc > 0)
182*4882a593Smuzhiyun fprintf(stderr, "PASS: %s read %d bytes\n", path2, rc);
183*4882a593Smuzhiyun else {
184*4882a593Smuzhiyun fprintf(stderr, "PASS: %s not readable\n", path2);
185*4882a593Smuzhiyun return rc;
186*4882a593Smuzhiyun }
187*4882a593Smuzhiyun } else {
188*4882a593Smuzhiyun r = lstat(path2, &buf);
189*4882a593Smuzhiyun if (r == 0 && S_ISDIR(buf.st_mode)) {
190*4882a593Smuzhiyun rc = scan_rom(path2, file);
191*4882a593Smuzhiyun if (rc < 0)
192*4882a593Smuzhiyun return rc;
193*4882a593Smuzhiyun }
194*4882a593Smuzhiyun }
195*4882a593Smuzhiyun
196*4882a593Smuzhiyun result |= rc;
197*4882a593Smuzhiyun free(path2);
198*4882a593Smuzhiyun
199*4882a593Smuzhiyun skip:
200*4882a593Smuzhiyun free(namelist[i]);
201*4882a593Smuzhiyun }
202*4882a593Smuzhiyun free(namelist);
203*4882a593Smuzhiyun return result;
204*4882a593Smuzhiyun }
205*4882a593Smuzhiyun
main(void)206*4882a593Smuzhiyun int main(void)
207*4882a593Smuzhiyun {
208*4882a593Smuzhiyun int rc;
209*4882a593Smuzhiyun
210*4882a593Smuzhiyun if (map_mem("/dev/mem", 0, 0xA0000, 1) == 0)
211*4882a593Smuzhiyun fprintf(stderr, "PASS: /dev/mem 0x0-0xa0000 is readable\n");
212*4882a593Smuzhiyun else
213*4882a593Smuzhiyun fprintf(stderr, "FAIL: /dev/mem 0x0-0xa0000 not accessible\n");
214*4882a593Smuzhiyun
215*4882a593Smuzhiyun /*
216*4882a593Smuzhiyun * It's not safe to blindly read the VGA frame buffer. If you know
217*4882a593Smuzhiyun * how to poke the card the right way, it should respond, but it's
218*4882a593Smuzhiyun * not safe in general. Many machines, e.g., Intel chipsets, cover
219*4882a593Smuzhiyun * up a non-responding card by just returning -1, but others will
220*4882a593Smuzhiyun * report the failure as a machine check.
221*4882a593Smuzhiyun */
222*4882a593Smuzhiyun if (map_mem("/dev/mem", 0xA0000, 0x20000, 0) == 0)
223*4882a593Smuzhiyun fprintf(stderr, "PASS: /dev/mem 0xa0000-0xc0000 is mappable\n");
224*4882a593Smuzhiyun else
225*4882a593Smuzhiyun fprintf(stderr, "FAIL: /dev/mem 0xa0000-0xc0000 not accessible\n");
226*4882a593Smuzhiyun
227*4882a593Smuzhiyun if (map_mem("/dev/mem", 0xC0000, 0x40000, 1) == 0)
228*4882a593Smuzhiyun fprintf(stderr, "PASS: /dev/mem 0xc0000-0x100000 is readable\n");
229*4882a593Smuzhiyun else
230*4882a593Smuzhiyun fprintf(stderr, "FAIL: /dev/mem 0xc0000-0x100000 not accessible\n");
231*4882a593Smuzhiyun
232*4882a593Smuzhiyun /*
233*4882a593Smuzhiyun * Often you can map all the individual pieces above (0-0xA0000,
234*4882a593Smuzhiyun * 0xA0000-0xC0000, and 0xC0000-0x100000), but can't map the whole
235*4882a593Smuzhiyun * thing at once. This is because the individual pieces use different
236*4882a593Smuzhiyun * attributes, and there's no single attribute supported over the
237*4882a593Smuzhiyun * whole region.
238*4882a593Smuzhiyun */
239*4882a593Smuzhiyun rc = map_mem("/dev/mem", 0, 1024*1024, 0);
240*4882a593Smuzhiyun if (rc == 0)
241*4882a593Smuzhiyun fprintf(stderr, "PASS: /dev/mem 0x0-0x100000 is mappable\n");
242*4882a593Smuzhiyun else if (rc > 0)
243*4882a593Smuzhiyun fprintf(stderr, "PASS: /dev/mem 0x0-0x100000 not mappable\n");
244*4882a593Smuzhiyun else
245*4882a593Smuzhiyun fprintf(stderr, "FAIL: /dev/mem 0x0-0x100000 not accessible\n");
246*4882a593Smuzhiyun
247*4882a593Smuzhiyun scan_tree("/sys/class/pci_bus", "legacy_mem", 0, 0xA0000, 1);
248*4882a593Smuzhiyun scan_tree("/sys/class/pci_bus", "legacy_mem", 0xA0000, 0x20000, 0);
249*4882a593Smuzhiyun scan_tree("/sys/class/pci_bus", "legacy_mem", 0xC0000, 0x40000, 1);
250*4882a593Smuzhiyun scan_tree("/sys/class/pci_bus", "legacy_mem", 0, 1024*1024, 0);
251*4882a593Smuzhiyun
252*4882a593Smuzhiyun scan_rom("/sys/devices", "rom");
253*4882a593Smuzhiyun
254*4882a593Smuzhiyun scan_tree("/proc/bus/pci", "??.?", 0, 0xA0000, 1);
255*4882a593Smuzhiyun scan_tree("/proc/bus/pci", "??.?", 0xA0000, 0x20000, 0);
256*4882a593Smuzhiyun scan_tree("/proc/bus/pci", "??.?", 0xC0000, 0x40000, 1);
257*4882a593Smuzhiyun scan_tree("/proc/bus/pci", "??.?", 0, 1024*1024, 0);
258*4882a593Smuzhiyun
259*4882a593Smuzhiyun return rc;
260*4882a593Smuzhiyun }
261