aboutsummaryrefslogtreecommitdiff
path: root/pci-arbiter/x86_pci.c
diff options
context:
space:
mode:
Diffstat (limited to 'pci-arbiter/x86_pci.c')
-rw-r--r--pci-arbiter/x86_pci.c843
1 files changed, 843 insertions, 0 deletions
diff --git a/pci-arbiter/x86_pci.c b/pci-arbiter/x86_pci.c
new file mode 100644
index 00000000..9cf1f54a
--- /dev/null
+++ b/pci-arbiter/x86_pci.c
@@ -0,0 +1,843 @@
+/*
+ * Copyright (c) 2017 Joan Lledó
+ * Copyright (c) 2009, 2012, 2018 Samuel Thibault
+ * Heavily inspired from the freebsd, netbsd, and openbsd backends
+ * (C) Copyright Eric Anholt 2006
+ * (C) Copyright IBM Corporation 2006
+ * Copyright (c) 2008 Juan Romero Pardines
+ * Copyright (c) 2008 Mark Kettenis
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * PCI backend for x86 (32 and 64 bit) architectures.
+ *
+ * Following code is borrowed from libpciaccess:
+ * https://cgit.freedesktop.org/xorg/lib/libpciaccess/
+ */
+
+#include "x86_pci.h"
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/io.h>
+#include <string.h>
+
+#include "pci_access.h"
+
+#define PCI_VENDOR(reg) ((reg) & 0xFFFF)
+#define PCI_VENDOR_INVALID 0xFFFF
+
+#define PCI_VENDOR_ID 0x00
+#define PCI_VENDOR_ID_COMPAQ 0x0e11
+#define PCI_VENDOR_ID_INTEL 0x8086
+
+#define PCI_CLASS 0x08
+#define PCI_CLASS_DEVICE 0x0a
+#define PCI_CLASS_DISPLAY_VGA 0x0300
+#define PCI_CLASS_BRIDGE_HOST 0x0600
+
+#define PCI_BAR_ADDR_0 0x10
+#define PCI_XROMBAR_ADDR_00 0x30
+#define PCI_XROMBAR_ADDR_01 0x38
+
+#define PCI_HDRTYPE 0x0E
+#define PCI_HDRTYPE_DEVICE 0x00
+#define PCI_HDRTYPE_BRIDGE 0x01
+#define PCI_HDRTYPE_CARDBUS 0x02
+
+#define PCI_COMMAND 0x04
+#define PCI_SECONDARY_BUS 0x19
+
+#define PCI_CONFIG_SIZE 256
+
+static error_t
+x86_enable_io (void)
+{
+ if (!ioperm (0, 0xffff, 1))
+ return 0;
+ return errno;
+}
+
+static error_t
+x86_disable_io (void)
+{
+ if (!ioperm (0, 0xffff, 0))
+ return 0;
+ return errno;
+}
+
+static error_t
+pci_system_x86_conf1_probe (void)
+{
+ unsigned long sav;
+ int res = ENODEV;
+
+ outb (0x01, 0xCFB);
+ sav = inl (0xCF8);
+ outl (0x80000000, 0xCF8);
+ if (inl (0xCF8) == 0x80000000)
+ res = 0;
+ outl (sav, 0xCF8);
+
+ return res;
+}
+
+static error_t
+pci_system_x86_conf1_read (unsigned bus, unsigned dev, unsigned func,
+ pciaddr_t reg, void *data, unsigned size)
+{
+ unsigned addr = 0xCFC + (reg & 3);
+ unsigned long sav;
+ error_t ret = 0;
+
+ if (bus >= 0x100 || dev >= 32 || func >= 8 || reg >= 0x100 || size > 4
+ || size == 3)
+ return EIO;
+
+ sav = inl (0xCF8);
+ outl (0x80000000 | (bus << 16) | (dev << 11) | (func << 8) | (reg & ~3),
+ 0xCF8);
+ /* NOTE: x86 is already LE */
+ switch (size)
+ {
+ case 1:
+ {
+ uint8_t *val = data;
+ *val = inb (addr);
+ break;
+ }
+ case 2:
+ {
+ uint16_t *val = data;
+ *val = inw (addr);
+ break;
+ }
+ case 4:
+ {
+ uint32_t *val = data;
+ *val = inl (addr);
+ break;
+ }
+ }
+ outl (sav, 0xCF8);
+
+ return ret;
+}
+
+static error_t
+pci_system_x86_conf1_write (unsigned bus, unsigned dev, unsigned func,
+ pciaddr_t reg, void *data, unsigned size)
+{
+ unsigned addr = 0xCFC + (reg & 3);
+ unsigned long sav;
+ error_t ret = 0;
+
+ if (bus >= 0x100 || dev >= 32 || func >= 8 || reg >= 0x100 || size > 4
+ || size == 3)
+ return EIO;
+
+ sav = inl (0xCF8);
+ outl (0x80000000 | (bus << 16) | (dev << 11) | (func << 8) | (reg & ~3),
+ 0xCF8);
+ /* NOTE: x86 is already LE */
+ switch (size)
+ {
+ case 1:
+ {
+ const uint8_t *val = data;
+ outb (*val, addr);
+ break;
+ }
+ case 2:
+ {
+ const uint16_t *val = data;
+ outw (*val, addr);
+ break;
+ }
+ case 4:
+ {
+ const uint32_t *val = data;
+ outl (*val, addr);
+ break;
+ }
+ }
+ outl (sav, 0xCF8);
+
+ return ret;
+}
+
+static error_t
+pci_system_x86_conf2_probe (void)
+{
+ outb (0, 0xCFB);
+ outb (0, 0xCF8);
+ outb (0, 0xCFA);
+ if (inb (0xCF8) == 0 && inb (0xCFA) == 0)
+ return 0;
+
+ return ENODEV;
+}
+
+static error_t
+pci_system_x86_conf2_read (unsigned bus, unsigned dev, unsigned func,
+ pciaddr_t reg, void *data, unsigned size)
+{
+ unsigned addr = 0xC000 | dev << 8 | reg;
+ error_t ret = 0;
+
+ if (bus >= 0x100 || dev >= 16 || func >= 8 || reg >= 0x100)
+ return EIO;
+
+ outb ((func << 1) | 0xF0, 0xCF8);
+ outb (bus, 0xCFA);
+ /* NOTE: x86 is already LE */
+ switch (size)
+ {
+ case 1:
+ {
+ uint8_t *val = data;
+ *val = inb (addr);
+ break;
+ }
+ case 2:
+ {
+ uint16_t *val = data;
+ *val = inw (addr);
+ break;
+ }
+ case 4:
+ {
+ uint32_t *val = data;
+ *val = inl (addr);
+ break;
+ }
+ default:
+ ret = EIO;
+ break;
+ }
+ outb (0, 0xCF8);
+
+ return ret;
+}
+
+static error_t
+pci_system_x86_conf2_write (unsigned bus, unsigned dev, unsigned func,
+ pciaddr_t reg, void *data, unsigned size)
+{
+ unsigned addr = 0xC000 | dev << 8 | reg;
+ error_t ret = 0;
+
+ if (bus >= 0x100 || dev >= 16 || func >= 8 || reg >= 0x100)
+ return EIO;
+
+ outb ((func << 1) | 0xF0, 0xCF8);
+ outb (bus, 0xCFA);
+ /* NOTE: x86 is already LE */
+ switch (size)
+ {
+ case 1:
+ {
+ const uint8_t *val = data;
+ outb (*val, addr);
+ break;
+ }
+ case 2:
+ {
+ const uint16_t *val = data;
+ outw (*val, addr);
+ break;
+ }
+ case 4:
+ {
+ const uint32_t *val = data;
+ outl (*val, addr);
+ break;
+ }
+ default:
+ ret = EIO;
+ break;
+ }
+ outb (0, 0xCF8);
+
+ return ret;
+}
+
+/* Returns the number of regions (base address registers) the device has */
+static int
+pci_device_x86_get_num_regions (uint8_t header_type)
+{
+ switch (header_type & 0x7f)
+ {
+ case 0:
+ return 6;
+ case 1:
+ return 2;
+ case 2:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+/* Masks out the flag bigs of the base address register value */
+static uint32_t
+get_map_base (uint32_t val)
+{
+ if (val & 0x01)
+ return val & ~0x03;
+ else
+ return val & ~0x0f;
+}
+
+/* Returns the size of a region based on the all-ones test value */
+static unsigned
+get_test_val_size (uint32_t testval)
+{
+ unsigned size = 1;
+
+ if (testval == 0)
+ return 0;
+
+ /* Mask out the flag bits */
+ testval = get_map_base (testval);
+ if (!testval)
+ return 0;
+
+ while ((testval & 1) == 0)
+ {
+ size <<= 1;
+ testval >>= 1;
+ }
+
+ return size;
+}
+
+/* Read BAR `reg_num' in `dev' and map the data if any */
+static error_t
+pci_device_x86_region_probe (struct pci_device *dev, int reg_num)
+{
+ error_t err;
+ uint8_t offset;
+ uint32_t reg, addr, testval;
+ int memfd;
+
+ offset = PCI_BAR_ADDR_0 + 0x4 * reg_num;
+
+ /* Get the base address */
+ err =
+ pci_sys->read (dev->bus, dev->dev, dev->func, offset, &addr,
+ sizeof (addr));
+ if (err)
+ return err;
+
+ /* Test write all ones to the register, then restore it. */
+ reg = 0xffffffff;
+ err = pci_sys->write (dev->bus, dev->dev, dev->func, offset, &reg,
+ sizeof (reg));
+ if (err)
+ return err;
+ err = pci_sys->read (dev->bus, dev->dev, dev->func, offset, &testval,
+ sizeof (testval));
+ if (err)
+ return err;
+ err = pci_sys->write (dev->bus, dev->dev, dev->func, offset, &addr,
+ sizeof (addr));
+ if (err)
+ return err;
+
+ if (addr & 0x01)
+ dev->regions[reg_num].is_IO = 1;
+ if (addr & 0x04)
+ dev->regions[reg_num].is_64 = 1;
+ if (addr & 0x08)
+ dev->regions[reg_num].is_prefetchable = 1;
+
+ /* Set the size */
+ dev->regions[reg_num].size = get_test_val_size (testval);
+
+ /* Set the base address value */
+ dev->regions[reg_num].base_addr = get_map_base (addr);
+
+ if (dev->regions[reg_num].is_64)
+ {
+ err =
+ pci_sys->read (dev->bus, dev->dev, dev->func, offset + 4, &addr,
+ sizeof (addr));
+ if (err)
+ return err;
+
+ dev->regions[reg_num].base_addr |= ((uint64_t) addr << 32);
+ }
+
+ if (dev->regions[reg_num].is_IO)
+ {
+ /* Enable the I/O Space bit */
+ err =
+ pci_sys->read (dev->bus, dev->dev, dev->func, PCI_COMMAND, &reg,
+ sizeof (reg));
+ if (err)
+ return err;
+
+ if (!(reg & 0x1))
+ {
+ reg |= 0x1;
+
+ err =
+ pci_sys->write (dev->bus, dev->dev, dev->func, PCI_COMMAND,
+ &reg, sizeof (reg));
+ if (err)
+ return err;
+ }
+
+ /* Clear the map pointer */
+ dev->regions[reg_num].memory = 0;
+ }
+ else if (dev->regions[reg_num].size > 0)
+ {
+ /* Enable the Memory Space bit */
+ err =
+ pci_sys->read (dev->bus, dev->dev, dev->func, PCI_COMMAND, &reg,
+ sizeof (reg));
+ if (err)
+ return err;
+
+ if (!(reg & 0x2))
+ {
+ reg |= 0x2;
+
+ err =
+ pci_sys->write (dev->bus, dev->dev, dev->func, PCI_COMMAND,
+ &reg, sizeof (reg));
+ if (err)
+ return err;
+ }
+
+ /* Map the region in our space */
+ memfd = open ("/dev/mem", O_RDONLY | O_CLOEXEC);
+ if (memfd == -1)
+ return errno;
+
+ dev->regions[reg_num].memory =
+ mmap (NULL, dev->regions[reg_num].size, PROT_READ | PROT_WRITE, 0,
+ memfd, dev->regions[reg_num].base_addr);
+ if (dev->regions[reg_num].memory == MAP_FAILED)
+ {
+ dev->regions[reg_num].memory = 0;
+ close (memfd);
+ return errno;
+ }
+
+ close (memfd);
+ }
+
+ return 0;
+}
+
+/* Read the XROMBAR in `dev' and map the data if any */
+static error_t
+pci_device_x86_rom_probe (struct pci_device *dev)
+{
+ error_t err;
+ uint8_t reg_8, xrombar_addr;
+ uint32_t reg, reg_back;
+ pciaddr_t rom_size;
+ pciaddr_t rom_base;
+ void *rom_mapped;
+ int memfd;
+
+ /* First we need to know which type of header is this */
+ err = pci_sys->read (dev->bus, dev->dev, dev->func, PCI_HDRTYPE, &reg_8,
+ sizeof (reg_8));
+ if (err)
+ return err;
+
+ /* Get the XROMBAR register address */
+ switch (reg_8 & 0x3)
+ {
+ case PCI_HDRTYPE_DEVICE:
+ xrombar_addr = PCI_XROMBAR_ADDR_00;
+ break;
+ case PCI_HDRTYPE_BRIDGE:
+ xrombar_addr = PCI_XROMBAR_ADDR_01;
+ break;
+ default:
+ return -1;
+ }
+
+ /* Get size and physical address */
+ err = pci_sys->read (dev->bus, dev->dev, dev->func, xrombar_addr, &reg,
+ sizeof (reg));
+ if (err)
+ return err;
+
+ reg_back = reg;
+ reg = 0xFFFFF800; /* Base address: first 21 bytes */
+ err = pci_sys->write (dev->bus, dev->dev, dev->func, xrombar_addr, &reg,
+ sizeof (reg));
+ if (err)
+ return err;
+ err = pci_sys->read (dev->bus, dev->dev, dev->func, xrombar_addr, &reg,
+ sizeof (reg));
+ if (err)
+ return err;
+
+ rom_size = (~reg + 1);
+ rom_base = reg_back & reg;
+
+ if (rom_size == 0)
+ return 0;
+
+ /* Enable the address decoder and write the physical address back */
+ reg_back |= 0x1;
+ err = pci_sys->write
+ (dev->bus, dev->dev, dev->func, xrombar_addr, &reg_back,
+ sizeof (reg_back));
+ if (err)
+ return err;
+
+ /* Enable the Memory Space bit */
+ err = pci_sys->read (dev->bus, dev->dev, dev->func, PCI_COMMAND, &reg,
+ sizeof (reg));
+ if (err)
+ return err;
+
+ if (!(reg & 0x2))
+ {
+ reg |= 0x2;
+
+ err =
+ pci_sys->write (dev->bus, dev->dev, dev->func, PCI_COMMAND, &reg,
+ sizeof (reg));
+ if (err)
+ return err;
+ }
+
+ /* Map the ROM in our space */
+ memfd = open ("/dev/mem", O_RDONLY | O_CLOEXEC);
+ if (memfd == -1)
+ return errno;
+
+ rom_mapped = mmap (NULL, rom_size, PROT_READ, 0, memfd, rom_base);
+ if (rom_mapped == MAP_FAILED)
+ {
+ close (memfd);
+ return errno;
+ }
+
+ close (memfd);
+
+ dev->rom_size = rom_size;
+ dev->rom_base = rom_base;
+ dev->rom_memory = rom_mapped;
+
+ return 0;
+}
+
+/* Configure BARs and ROM */
+static error_t
+pci_device_x86_probe (struct pci_device *dev)
+{
+ error_t err;
+ uint8_t hdrtype;
+ int i;
+
+ /* Probe BARs */
+ err = pci_sys->read (dev->bus, dev->dev, dev->func, PCI_HDRTYPE, &hdrtype,
+ sizeof (hdrtype));
+ if (err)
+ return err;
+
+ for (i = 0; i < pci_device_x86_get_num_regions (hdrtype); i++)
+ {
+ err = pci_device_x86_region_probe (dev, i);
+ if (err)
+ return err;
+
+ if (dev->regions[i].is_64)
+ /* Move the pointer one BAR ahead */
+ i++;
+ }
+
+ /* Probe ROM */
+ err = pci_device_x86_rom_probe (dev);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+/*
+ * Refresh the device. Check for updates in region `reg_num'
+ * or in ROM if `rom' = true. `reg_num' < 0 means no region check.
+ */
+static error_t
+pci_device_x86_refresh (struct pci_device *dev, int reg_num, int rom)
+{
+ error_t err;
+ uint8_t offset, hdrtype;
+ uint32_t addr;
+
+ if (reg_num >= 0 && dev->regions[reg_num].size > 0)
+ {
+ /* Read the BAR */
+ offset = PCI_BAR_ADDR_0 + 0x4 * reg_num;
+ err =
+ pci_sys->read (dev->bus, dev->dev, dev->func, offset, &addr,
+ sizeof (addr));
+ if (err)
+ return err;
+
+ /* Check whether the region is outdated, if so, the refresh it */
+ if (dev->regions[reg_num].base_addr != get_map_base (addr))
+ {
+ err = pci_device_x86_region_probe (dev, reg_num);
+ if (err)
+ return err;
+ }
+ }
+
+ if (rom && dev->rom_size > 0)
+ {
+ /* Read the BAR */
+ err =
+ pci_sys->read (dev->bus, dev->dev, dev->func, PCI_HDRTYPE, &hdrtype,
+ sizeof (hdrtype));
+ if (err)
+ return err;
+
+ switch (hdrtype & 0x3)
+ {
+ case PCI_HDRTYPE_DEVICE:
+ offset = PCI_XROMBAR_ADDR_00;
+ break;
+ case PCI_HDRTYPE_BRIDGE:
+ offset = PCI_XROMBAR_ADDR_01;
+ break;
+ default:
+ return -1;
+ }
+
+ err = pci_sys->read (dev->bus, dev->dev, dev->func, offset, &addr,
+ sizeof (addr));
+ if (err)
+ return err;
+
+ /* Check whether the ROM is outdated, if so, the refresh it */
+ if (dev->rom_base != (addr & 0xFFFFF800))
+ {
+ err = pci_device_x86_rom_probe (dev);
+ if (err)
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+/* Check that this really looks like a PCI configuration. */
+static error_t
+pci_system_x86_check (struct pci_system *pci_sys)
+{
+ int dev;
+ uint16_t class, vendor;
+
+ /* Look on bus 0 for a device that is a host bridge, a VGA card,
+ * or an intel or compaq device. */
+
+ for (dev = 0; dev < 32; dev++)
+ {
+ if (pci_sys->read (0, dev, 0, PCI_CLASS_DEVICE, &class, sizeof (class)))
+ continue;
+ if (class == PCI_CLASS_BRIDGE_HOST || class == PCI_CLASS_DISPLAY_VGA)
+ return 0;
+ if (pci_sys->read (0, dev, 0, PCI_VENDOR_ID, &vendor, sizeof (vendor)))
+ continue;
+ if (vendor == PCI_VENDOR_ID_INTEL || class == PCI_VENDOR_ID_COMPAQ)
+ return 0;
+ }
+
+ return ENODEV;
+}
+
+/* Find out which conf access method use */
+static error_t
+pci_probe (struct pci_system *pci_sys)
+{
+ if (pci_system_x86_conf1_probe () == 0)
+ {
+ pci_sys->read = pci_system_x86_conf1_read;
+ pci_sys->write = pci_system_x86_conf1_write;
+ if (pci_system_x86_check (pci_sys) == 0)
+ return 0;
+ }
+
+ if (pci_system_x86_conf2_probe () == 0)
+ {
+ pci_sys->read = pci_system_x86_conf2_read;
+ pci_sys->write = pci_system_x86_conf2_write;
+ if (pci_system_x86_check (pci_sys) == 0)
+ return 0;
+ }
+
+ return ENODEV;
+}
+
+static error_t
+pci_nfuncs (struct pci_system *pci_sys, int bus, int dev, uint8_t * nfuncs)
+{
+ uint8_t hdrtype;
+ error_t err;
+
+ err = pci_sys->read (bus, dev, 0, PCI_HDRTYPE, &hdrtype, sizeof (hdrtype));
+ if (err)
+ return err;
+
+ *nfuncs = hdrtype & 0x80 ? 8 : 1;
+
+ return 0;
+}
+
+/* Recursively scan bus number `bus' */
+static error_t
+pci_system_x86_scan_bus (struct pci_system *pci_sys, uint8_t bus)
+{
+ error_t err;
+ uint8_t dev, func, nfuncs, hdrtype, secbus;
+ uint32_t reg;
+ struct pci_device *d, *devices;
+
+ for (dev = 0; dev < 32; dev++)
+ {
+ err = pci_nfuncs (pci_sys, bus, dev, &nfuncs);
+ if (err)
+ return err;
+
+ for (func = 0; func < nfuncs; func++)
+ {
+ err =
+ pci_sys->read (bus, dev, func, PCI_VENDOR_ID, &reg, sizeof (reg));
+ if (err)
+ return err;
+
+ if (PCI_VENDOR (reg) == PCI_VENDOR_INVALID || PCI_VENDOR (reg) == 0)
+ continue;
+
+ err = pci_sys->read (bus, dev, func, PCI_CLASS, &reg, sizeof (reg));
+ if (err)
+ return err;
+
+ err =
+ pci_sys->read (bus, dev, func, PCI_HDRTYPE, &hdrtype,
+ sizeof (hdrtype));
+ if (err)
+ return err;
+
+ devices =
+ realloc (pci_sys->devices,
+ (pci_sys->num_devices + 1) * sizeof (struct pci_device));
+ if (!devices)
+ return ENOMEM;
+
+ d = devices + pci_sys->num_devices;
+ memset (d, 0, sizeof (struct pci_device));
+
+ /* Fixed values as PCI express is still not supported */
+ d->domain = 0;
+ d->config_size = PCI_CONFIG_SIZE;
+
+ d->bus = bus;
+ d->dev = dev;
+ d->func = func;
+
+ d->device_class = reg >> 8;
+
+ err = pci_device_x86_probe (d);
+ if (err)
+ return err;
+
+ pci_sys->devices = devices;
+ pci_sys->num_devices++;
+
+ switch (hdrtype & 0x3)
+ {
+ case PCI_HDRTYPE_DEVICE:
+ break;
+ case PCI_HDRTYPE_BRIDGE:
+ case PCI_HDRTYPE_CARDBUS:
+ {
+ err =
+ pci_sys->read (bus, dev, func, PCI_SECONDARY_BUS, &secbus,
+ sizeof (secbus));
+ if (err)
+ return err;
+
+ err = pci_system_x86_scan_bus (pci_sys, secbus);
+ if (err)
+ return err;
+
+ break;
+ }
+ default:
+ /* Unknown header, do nothing */
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* Initialize the x86 module */
+error_t
+pci_system_x86_create (void)
+{
+ error_t err;
+
+ err = x86_enable_io ();
+ if (err)
+ return err;
+
+ pci_sys = calloc (1, sizeof (struct pci_system));
+ if (pci_sys == NULL)
+ {
+ x86_disable_io ();
+ return ENOMEM;
+ }
+
+ err = pci_probe (pci_sys);
+ if (err)
+ {
+ x86_disable_io ();
+ free (pci_sys);
+ return err;
+ }
+ pci_sys->device_refresh = pci_device_x86_refresh;
+
+ /* Recursive scan */
+ pci_sys->num_devices = 0;
+ err = pci_system_x86_scan_bus (pci_sys, 0);
+ if (err)
+ {
+ x86_disable_io ();
+ free (pci_sys);
+ pci_sys = NULL;
+ return err;
+ }
+
+ return 0;
+}