diff options
author | Pali Rohár <pali@kernel.org> | 2024-02-28 00:32:08 +0100 |
---|---|---|
committer | Martin Mares <mj@ucw.cz> | 2024-04-05 13:14:01 +0200 |
commit | 1836a2d4c62a3adbad269e8177528c42daf40f42 (patch) | |
tree | 1f14d8c925e084b1d73fd4c8d5f04bd858ca49c5 | |
parent | a34006f8e9c1f80e1446d1007bfff3ffefef4d23 (diff) | |
download | pciutils-1836a2d4c62a3adbad269e8177528c42daf40f42.tar.gz |
libpci: ecam: Fix scanning of Extended BIOS Data Area for ACPI RSDP
At physical address 0x40E (part of BDA) is stored indirect 16-bit paragraph
offset to the EBDA, and not the EBDA itself. Fix it.
ACPI code in linux kernel checks if the EBDA offset in BDA is above
physical address 0x400. Do the same check here. It is for detection if EBDA
is present as it does not have to be on the old computers or in some
virtualised environments.
-rw-r--r-- | lib/ecam.c | 36 |
1 files changed, 27 insertions, 9 deletions
@@ -221,9 +221,11 @@ find_rsdp_address(struct pci_access *a, const char *efisystab, int use_bsd UNUSE #if defined(__amd64__) || defined(__i386__) struct ecam_access *eacc = a->backend_data; struct physmem *physmem = eacc->physmem; + long pagesize = eacc->pagesize; u64 rsdp_addr; u64 addr; void *map; + u64 ebda; #endif size_t len; FILE *f; @@ -305,23 +307,39 @@ find_rsdp_address(struct pci_access *a, const char *efisystab, int use_bsd UNUSE rsdp_addr = 0; /* Scan first kB of Extended BIOS Data Area */ - a->debug("scanning first kB of EBDA..."); - map = physmem_map(physmem, 0, 0x40E + 1024, 0); + a->debug("reading EBDA location from BDA..."); + map = physmem_map(physmem, 0, 0x40E + 2, 0); if (map != (void *)-1) { - for (addr = 0x40E; addr < 0x40E + 1024; addr += 16) + ebda = (u64)physmem_readw((unsigned char *)map + 0x40E) << 4; + if (physmem_unmap(physmem, map, 0x40E + 2) != 0) + a->debug("unmapping of BDA failed: %s...", strerror(errno)); + if (ebda >= 0x400) { - if (check_rsdp((struct acpi_rsdp *)((unsigned char *)map + addr))) + a->debug("scanning first kB of EBDA at 0x%" PCI_U64_FMT_X "...", ebda); + map = physmem_map(physmem, ebda & ~(pagesize-1), 1024 + (ebda & (pagesize-1)), 0); + if (map != (void *)-1) { - rsdp_addr = addr; - break; + for (addr = ebda & (pagesize-1); addr < (ebda & (pagesize-1)) + 1024; addr += 16) + { + if (check_rsdp((struct acpi_rsdp *)((unsigned char *)map + addr))) + { + rsdp_addr = (ebda & ~(pagesize-1)) + addr; + break; + } + } + if (physmem_unmap(physmem, map, 1024 + (ebda & (pagesize-1))) != 0) + a->debug("unmapping of EBDA failed: %s...", strerror(errno)); } + else + a->debug("mapping of EBDA failed: %s...", strerror(errno)); } - if (physmem_unmap(physmem, map, 0x40E + 1024) != 0) - a->debug("unmapping of EBDA failed: %s...", strerror(errno)); + else + a->debug("EBDA location 0x%" PCI_U64_FMT_X " is insane...", ebda); } else - a->debug("mapping of EBDA failed: %s...", strerror(errno)); + a->debug("mapping of BDA failed: %s...", strerror(errno)); + if (rsdp_addr) return rsdp_addr; |