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