]> git.ipfire.org Git - thirdparty/pciutils.git/commitdiff
libpci: ecam: Fix scanning of Extended BIOS Data Area for ACPI RSDP
authorPali Rohár <pali@kernel.org>
Tue, 27 Feb 2024 23:32:08 +0000 (00:32 +0100)
committerMartin Mares <mj@ucw.cz>
Fri, 5 Apr 2024 11:14:01 +0000 (13:14 +0200)
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.

lib/ecam.c

index 7f73d51e98b9d12534827a5cc5cb91a9e3b398dd..fdeec0786f6978891564f8d6db0e303894e2c8ed 100644 (file)
@@ -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;