18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Exercise /dev/mem mmap cases that have been troublesome in the past 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * (c) Copyright 2007 Hewlett-Packard Development Company, L.P. 68c2ecf20Sopenharmony_ci * Bjorn Helgaas <bjorn.helgaas@hp.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <stdlib.h> 108c2ecf20Sopenharmony_ci#include <stdio.h> 118c2ecf20Sopenharmony_ci#include <sys/types.h> 128c2ecf20Sopenharmony_ci#include <dirent.h> 138c2ecf20Sopenharmony_ci#include <fcntl.h> 148c2ecf20Sopenharmony_ci#include <fnmatch.h> 158c2ecf20Sopenharmony_ci#include <string.h> 168c2ecf20Sopenharmony_ci#include <sys/ioctl.h> 178c2ecf20Sopenharmony_ci#include <sys/mman.h> 188c2ecf20Sopenharmony_ci#include <sys/stat.h> 198c2ecf20Sopenharmony_ci#include <unistd.h> 208c2ecf20Sopenharmony_ci#include <linux/pci.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ciint sum; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic int map_mem(char *path, off_t offset, size_t length, int touch) 258c2ecf20Sopenharmony_ci{ 268c2ecf20Sopenharmony_ci int fd, rc; 278c2ecf20Sopenharmony_ci void *addr; 288c2ecf20Sopenharmony_ci int *c; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci fd = open(path, O_RDWR); 318c2ecf20Sopenharmony_ci if (fd == -1) { 328c2ecf20Sopenharmony_ci perror(path); 338c2ecf20Sopenharmony_ci return -1; 348c2ecf20Sopenharmony_ci } 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci if (fnmatch("/proc/bus/pci/*", path, 0) == 0) { 378c2ecf20Sopenharmony_ci rc = ioctl(fd, PCIIOC_MMAP_IS_MEM); 388c2ecf20Sopenharmony_ci if (rc == -1) 398c2ecf20Sopenharmony_ci perror("PCIIOC_MMAP_IS_MEM ioctl"); 408c2ecf20Sopenharmony_ci } 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci addr = mmap(NULL, length, PROT_READ|PROT_WRITE, MAP_SHARED, fd, offset); 438c2ecf20Sopenharmony_ci if (addr == MAP_FAILED) 448c2ecf20Sopenharmony_ci return 1; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci if (touch) { 478c2ecf20Sopenharmony_ci c = (int *) addr; 488c2ecf20Sopenharmony_ci while (c < (int *) (addr + length)) 498c2ecf20Sopenharmony_ci sum += *c++; 508c2ecf20Sopenharmony_ci } 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci rc = munmap(addr, length); 538c2ecf20Sopenharmony_ci if (rc == -1) { 548c2ecf20Sopenharmony_ci perror("munmap"); 558c2ecf20Sopenharmony_ci return -1; 568c2ecf20Sopenharmony_ci } 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci close(fd); 598c2ecf20Sopenharmony_ci return 0; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic int scan_tree(char *path, char *file, off_t offset, size_t length, int touch) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci struct dirent **namelist; 658c2ecf20Sopenharmony_ci char *name, *path2; 668c2ecf20Sopenharmony_ci int i, n, r, rc = 0, result = 0; 678c2ecf20Sopenharmony_ci struct stat buf; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci n = scandir(path, &namelist, 0, alphasort); 708c2ecf20Sopenharmony_ci if (n < 0) { 718c2ecf20Sopenharmony_ci perror("scandir"); 728c2ecf20Sopenharmony_ci return -1; 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci for (i = 0; i < n; i++) { 768c2ecf20Sopenharmony_ci name = namelist[i]->d_name; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci if (fnmatch(".", name, 0) == 0) 798c2ecf20Sopenharmony_ci goto skip; 808c2ecf20Sopenharmony_ci if (fnmatch("..", name, 0) == 0) 818c2ecf20Sopenharmony_ci goto skip; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci path2 = malloc(strlen(path) + strlen(name) + 3); 848c2ecf20Sopenharmony_ci strcpy(path2, path); 858c2ecf20Sopenharmony_ci strcat(path2, "/"); 868c2ecf20Sopenharmony_ci strcat(path2, name); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci if (fnmatch(file, name, 0) == 0) { 898c2ecf20Sopenharmony_ci rc = map_mem(path2, offset, length, touch); 908c2ecf20Sopenharmony_ci if (rc == 0) 918c2ecf20Sopenharmony_ci fprintf(stderr, "PASS: %s 0x%lx-0x%lx is %s\n", path2, offset, offset + length, touch ? "readable" : "mappable"); 928c2ecf20Sopenharmony_ci else if (rc > 0) 938c2ecf20Sopenharmony_ci fprintf(stderr, "PASS: %s 0x%lx-0x%lx not mappable\n", path2, offset, offset + length); 948c2ecf20Sopenharmony_ci else { 958c2ecf20Sopenharmony_ci fprintf(stderr, "FAIL: %s 0x%lx-0x%lx not accessible\n", path2, offset, offset + length); 968c2ecf20Sopenharmony_ci return rc; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci } else { 998c2ecf20Sopenharmony_ci r = lstat(path2, &buf); 1008c2ecf20Sopenharmony_ci if (r == 0 && S_ISDIR(buf.st_mode)) { 1018c2ecf20Sopenharmony_ci rc = scan_tree(path2, file, offset, length, touch); 1028c2ecf20Sopenharmony_ci if (rc < 0) 1038c2ecf20Sopenharmony_ci return rc; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci result |= rc; 1088c2ecf20Sopenharmony_ci free(path2); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ciskip: 1118c2ecf20Sopenharmony_ci free(namelist[i]); 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci free(namelist); 1148c2ecf20Sopenharmony_ci return result; 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cichar buf[1024]; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic int read_rom(char *path) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci int fd, rc; 1228c2ecf20Sopenharmony_ci size_t size = 0; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci fd = open(path, O_RDWR); 1258c2ecf20Sopenharmony_ci if (fd == -1) { 1268c2ecf20Sopenharmony_ci perror(path); 1278c2ecf20Sopenharmony_ci return -1; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci rc = write(fd, "1", 2); 1318c2ecf20Sopenharmony_ci if (rc <= 0) { 1328c2ecf20Sopenharmony_ci close(fd); 1338c2ecf20Sopenharmony_ci perror("write"); 1348c2ecf20Sopenharmony_ci return -1; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci do { 1388c2ecf20Sopenharmony_ci rc = read(fd, buf, sizeof(buf)); 1398c2ecf20Sopenharmony_ci if (rc > 0) 1408c2ecf20Sopenharmony_ci size += rc; 1418c2ecf20Sopenharmony_ci } while (rc > 0); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci close(fd); 1448c2ecf20Sopenharmony_ci return size; 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic int scan_rom(char *path, char *file) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci struct dirent **namelist; 1508c2ecf20Sopenharmony_ci char *name, *path2; 1518c2ecf20Sopenharmony_ci int i, n, r, rc = 0, result = 0; 1528c2ecf20Sopenharmony_ci struct stat buf; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci n = scandir(path, &namelist, 0, alphasort); 1558c2ecf20Sopenharmony_ci if (n < 0) { 1568c2ecf20Sopenharmony_ci perror("scandir"); 1578c2ecf20Sopenharmony_ci return -1; 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci for (i = 0; i < n; i++) { 1618c2ecf20Sopenharmony_ci name = namelist[i]->d_name; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci if (fnmatch(".", name, 0) == 0) 1648c2ecf20Sopenharmony_ci goto skip; 1658c2ecf20Sopenharmony_ci if (fnmatch("..", name, 0) == 0) 1668c2ecf20Sopenharmony_ci goto skip; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci path2 = malloc(strlen(path) + strlen(name) + 3); 1698c2ecf20Sopenharmony_ci strcpy(path2, path); 1708c2ecf20Sopenharmony_ci strcat(path2, "/"); 1718c2ecf20Sopenharmony_ci strcat(path2, name); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci if (fnmatch(file, name, 0) == 0) { 1748c2ecf20Sopenharmony_ci rc = read_rom(path2); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci /* 1778c2ecf20Sopenharmony_ci * It's OK if the ROM is unreadable. Maybe there 1788c2ecf20Sopenharmony_ci * is no ROM, or some other error occurred. The 1798c2ecf20Sopenharmony_ci * important thing is that no MCA happened. 1808c2ecf20Sopenharmony_ci */ 1818c2ecf20Sopenharmony_ci if (rc > 0) 1828c2ecf20Sopenharmony_ci fprintf(stderr, "PASS: %s read %d bytes\n", path2, rc); 1838c2ecf20Sopenharmony_ci else { 1848c2ecf20Sopenharmony_ci fprintf(stderr, "PASS: %s not readable\n", path2); 1858c2ecf20Sopenharmony_ci return rc; 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci } else { 1888c2ecf20Sopenharmony_ci r = lstat(path2, &buf); 1898c2ecf20Sopenharmony_ci if (r == 0 && S_ISDIR(buf.st_mode)) { 1908c2ecf20Sopenharmony_ci rc = scan_rom(path2, file); 1918c2ecf20Sopenharmony_ci if (rc < 0) 1928c2ecf20Sopenharmony_ci return rc; 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci result |= rc; 1978c2ecf20Sopenharmony_ci free(path2); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ciskip: 2008c2ecf20Sopenharmony_ci free(namelist[i]); 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci free(namelist); 2038c2ecf20Sopenharmony_ci return result; 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ciint main(void) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci int rc; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci if (map_mem("/dev/mem", 0, 0xA0000, 1) == 0) 2118c2ecf20Sopenharmony_ci fprintf(stderr, "PASS: /dev/mem 0x0-0xa0000 is readable\n"); 2128c2ecf20Sopenharmony_ci else 2138c2ecf20Sopenharmony_ci fprintf(stderr, "FAIL: /dev/mem 0x0-0xa0000 not accessible\n"); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci /* 2168c2ecf20Sopenharmony_ci * It's not safe to blindly read the VGA frame buffer. If you know 2178c2ecf20Sopenharmony_ci * how to poke the card the right way, it should respond, but it's 2188c2ecf20Sopenharmony_ci * not safe in general. Many machines, e.g., Intel chipsets, cover 2198c2ecf20Sopenharmony_ci * up a non-responding card by just returning -1, but others will 2208c2ecf20Sopenharmony_ci * report the failure as a machine check. 2218c2ecf20Sopenharmony_ci */ 2228c2ecf20Sopenharmony_ci if (map_mem("/dev/mem", 0xA0000, 0x20000, 0) == 0) 2238c2ecf20Sopenharmony_ci fprintf(stderr, "PASS: /dev/mem 0xa0000-0xc0000 is mappable\n"); 2248c2ecf20Sopenharmony_ci else 2258c2ecf20Sopenharmony_ci fprintf(stderr, "FAIL: /dev/mem 0xa0000-0xc0000 not accessible\n"); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if (map_mem("/dev/mem", 0xC0000, 0x40000, 1) == 0) 2288c2ecf20Sopenharmony_ci fprintf(stderr, "PASS: /dev/mem 0xc0000-0x100000 is readable\n"); 2298c2ecf20Sopenharmony_ci else 2308c2ecf20Sopenharmony_ci fprintf(stderr, "FAIL: /dev/mem 0xc0000-0x100000 not accessible\n"); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci /* 2338c2ecf20Sopenharmony_ci * Often you can map all the individual pieces above (0-0xA0000, 2348c2ecf20Sopenharmony_ci * 0xA0000-0xC0000, and 0xC0000-0x100000), but can't map the whole 2358c2ecf20Sopenharmony_ci * thing at once. This is because the individual pieces use different 2368c2ecf20Sopenharmony_ci * attributes, and there's no single attribute supported over the 2378c2ecf20Sopenharmony_ci * whole region. 2388c2ecf20Sopenharmony_ci */ 2398c2ecf20Sopenharmony_ci rc = map_mem("/dev/mem", 0, 1024*1024, 0); 2408c2ecf20Sopenharmony_ci if (rc == 0) 2418c2ecf20Sopenharmony_ci fprintf(stderr, "PASS: /dev/mem 0x0-0x100000 is mappable\n"); 2428c2ecf20Sopenharmony_ci else if (rc > 0) 2438c2ecf20Sopenharmony_ci fprintf(stderr, "PASS: /dev/mem 0x0-0x100000 not mappable\n"); 2448c2ecf20Sopenharmony_ci else 2458c2ecf20Sopenharmony_ci fprintf(stderr, "FAIL: /dev/mem 0x0-0x100000 not accessible\n"); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci scan_tree("/sys/class/pci_bus", "legacy_mem", 0, 0xA0000, 1); 2488c2ecf20Sopenharmony_ci scan_tree("/sys/class/pci_bus", "legacy_mem", 0xA0000, 0x20000, 0); 2498c2ecf20Sopenharmony_ci scan_tree("/sys/class/pci_bus", "legacy_mem", 0xC0000, 0x40000, 1); 2508c2ecf20Sopenharmony_ci scan_tree("/sys/class/pci_bus", "legacy_mem", 0, 1024*1024, 0); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci scan_rom("/sys/devices", "rom"); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci scan_tree("/proc/bus/pci", "??.?", 0, 0xA0000, 1); 2558c2ecf20Sopenharmony_ci scan_tree("/proc/bus/pci", "??.?", 0xA0000, 0x20000, 0); 2568c2ecf20Sopenharmony_ci scan_tree("/proc/bus/pci", "??.?", 0xC0000, 0x40000, 1); 2578c2ecf20Sopenharmony_ci scan_tree("/proc/bus/pci", "??.?", 0, 1024*1024, 0); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci return rc; 2608c2ecf20Sopenharmony_ci} 261