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