/* * fwts_low_mmap_walkdown() * try to allocate a free space under the 2GB limit by * walking down memory in CHUNK_SIZE steps for an unmapped region */ static void *fwts_low_mmap_walkdown(const size_t requested_size) { void *addr; size_t page_size = fwts_page_size(); size_t sz = (requested_size + page_size) & ~(page_size - 1); size_t pages = sz / page_size; unsigned char vec[pages]; static void *last_addr = (void *)LIMIT_2GB; if (requested_size == 0) /* Illegal */ return MAP_FAILED; for (addr = last_addr - sz; addr > (void *)LIMIT_START; addr -= CHUNK_SIZE) { void *mapping; /* Already mapped? */ if (mincore(addr, pages, vec) == 0) continue; /* Not mapped but mincore returned something unexpected? */ if (errno != ENOMEM) continue; mapping = mmap(addr, requested_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS, -1, 0); if (mapping != MAP_FAILED) { last_addr = mapping; return mapping; } } /* We've scanned all of memory, give up on subsequent calls */ last_addr = (void *)LIMIT_START; return MAP_FAILED; }
/* * fwts_munmap() * Unmap memory mapped wuth fwts_mmap. Needs the mmap'd address and size. */ int fwts_munmap(void *mem, const size_t size) { int page_size; off_t offset; page_size = fwts_page_size(); offset = ((off_t)(mem)) & (page_size - 1); if (munmap((void *)((uint8_t *)mem - offset), size + offset) < 0) return FWTS_ERROR; return FWTS_OK; }
/* * fwts_mmap() * Try and map physical memory from offset address 'start' and length * 'size'. Return either the address or FWTS_MAP_FAILED if failed to mmap. */ void *fwts_mmap(const off_t start, const size_t size) { int fd; int page_size; off_t offset; size_t length; void *mem; void *ret = FWTS_MAP_FAILED; page_size = fwts_page_size(); offset = ((size_t)start) & (page_size - 1); length = (size_t)size + offset; if ((fd = open("/dev/mem", O_RDONLY)) < 0) return ret; if ((mem = mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, start - offset)) != MAP_FAILED) ret = (void *)((uint8_t *)mem + offset); close(fd); return ret; }
static int compare_config_space( fwts_framework *fw, const fwts_acpi_mcfg_configuration *config) { uint8_t *mapped_config_space; uint8_t config_space[16]; size_t page_size, n; bool match; char path[PATH_MAX]; FILE *fp; int i; page_size = fwts_page_size(); /* * Sanity check on first config, this is enough to * see if MMIO base is OK or not */ snprintf(path, sizeof(path), "/sys/bus/pci/devices/%4.4" PRIx16 ":00:00.0/config", config->pci_segment_group_number); if ((fp = fopen(path, "r")) == NULL) { fwts_log_warning(fw, "Could not open %s.", path); return FWTS_ERROR; } n = fread(config_space, 1, sizeof(config_space), fp); (void)fclose(fp); if (n != sizeof(config_space)) { fwts_log_warning(fw, "Could only read %zd bytes from %s, expecting %zd.", n, path, sizeof(config_space)); return FWTS_ERROR; } if ((mapped_config_space = fwts_mmap(config->base_address, page_size)) == FWTS_MAP_FAILED) { fwts_log_error(fw, "Cannot mmap PCI config space at 0x%" PRIx64 ".", config->base_address); return FWTS_ERROR; } /* We only need to check if just the config space is readable */ if (fwts_safe_memread(mapped_config_space, sizeof(config_space)) != FWTS_OK) { fwts_log_error(fw, "Cannot read PCI config space at 0x%" PRIx64 ".", config->base_address); (void)fwts_munmap(mapped_config_space, page_size); return FWTS_ERROR; } /* * Need to explicitly do byte comparisons on region * memcmp() fails as this can do 64 bit reads */ for (match = true, i = 0; i < 16; i++) { if (config_space[i] != mapped_config_space[i]) { match = false; break; } } (void)fwts_munmap(mapped_config_space, page_size); if (match) fwts_passed(fw, "PCI config space verified."); else fwts_failed(fw, LOG_LEVEL_MEDIUM, "PCIConfigSpaceBad", "PCI config space appears to not work."); return FWTS_OK; }