/* * Copyright (c) 2018 Damien Zammit * Copyright (c) 2017 Joan Lledó * Copyright (c) 2009, 2012, 2020 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "x86_pci.h" #include #include #include #include #include #include #include #include #include "pciaccess.h" #include "pciaccess_private.h" #if defined(__GNU__) #include int x86_enable_io(void) { if (!ioperm(0, 0xffff, 1)) return 0; return errno; } int x86_disable_io(void) { if (!ioperm(0, 0xffff, 0)) return 0; return errno; } #elif defined(__GLIBC__) #include static int x86_enable_io(void) { if (!iopl(3)) return 0; return errno; } static int x86_disable_io(void) { if (!iopl(0)) return 0; return errno; } #elif defined(__CYGWIN__) #include /* WinIo declarations */ typedef BYTE bool; typedef struct tagPhysStruct { DWORD64 dwPhysMemSizeInBytes; DWORD64 pvPhysAddress; DWORD64 PhysicalMemoryHandle; DWORD64 pvPhysMemLin; DWORD64 pvPhysSection; } tagPhysStruct; typedef bool (_stdcall* INITIALIZEWINIO)(void); typedef void (_stdcall* SHUTDOWNWINIO)(void); typedef bool (_stdcall* GETPORTVAL)(WORD,PDWORD,BYTE); typedef bool (_stdcall* SETPORTVAL)(WORD,DWORD,BYTE); typedef PBYTE (_stdcall* MAPPHYSTOLIN)(tagPhysStruct*); typedef bool (_stdcall* UNMAPPHYSMEM)(tagPhysStruct*); SHUTDOWNWINIO ShutdownWinIo; GETPORTVAL GetPortVal; SETPORTVAL SetPortVal; INITIALIZEWINIO InitializeWinIo; MAPPHYSTOLIN MapPhysToLin; UNMAPPHYSMEM UnmapPhysicalMemory; static int x86_enable_io(void) { HMODULE lib = NULL; if ((GetVersion() & 0x80000000) == 0) { /* running on NT, try WinIo version 3 (32 or 64 bits) */ #ifdef WIN64 lib = LoadLibrary("WinIo64.dll"); #else lib = LoadLibrary("WinIo32.dll"); #endif } if (!lib) { fprintf(stderr, "Failed to load WinIo library.\n"); return 1; } #define GETPROC(n, d) \ n = (d) GetProcAddress(lib, #n); \ if (!n) { \ fprintf(stderr, "Failed to load " #n " function.\n"); \ return 1; \ } GETPROC(InitializeWinIo, INITIALIZEWINIO); GETPROC(ShutdownWinIo, SHUTDOWNWINIO); GETPROC(GetPortVal, GETPORTVAL); GETPROC(SetPortVal, SETPORTVAL); GETPROC(MapPhysToLin, MAPPHYSTOLIN); GETPROC(UnmapPhysicalMemory, UNMAPPHYSMEM); #undef GETPROC if (!InitializeWinIo()) { fprintf(stderr, "Failed to initialize WinIo.\n" "NOTE: WinIo.dll and WinIo.sys must be in the same directory as the executable!\n"); return 0; } return 0; } static int x86_disable_io(void) { ShutdownWinIo(); return 1; } static inline uint8_t inb(uint16_t port) { DWORD pv; if (GetPortVal(port, &pv, 1)) return (uint8_t)pv; return 0; } static inline uint16_t inw(uint16_t port) { DWORD pv; if (GetPortVal(port, &pv, 2)) return (uint16_t)pv; return 0; } static inline uint32_t inl(uint16_t port) { DWORD pv; if (GetPortVal(port, &pv, 4)) return (uint32_t)pv; return 0; } static inline void outb(uint8_t value, uint16_t port) { SetPortVal(port, value, 1); } static inline void outw(uint16_t value, uint16_t port) { SetPortVal(port, value, 2); } static inline void outl(uint32_t value, uint16_t port) { SetPortVal(port, value, 4); } #else #error How to enable IO ports on this system? #endif static int cmp_devices(const void *dev1, const void *dev2) { const struct pci_device *d1 = dev1; const struct pci_device *d2 = dev2; if (d1->bus != d2->bus) { return (d1->bus > d2->bus) ? 1 : -1; } if (d1->dev != d2->dev) { return (d1->dev > d2->dev) ? 1 : -1; } return (d1->func > d2->func) ? 1 : -1; } static void sort_devices(void) { qsort(pci_sys->devices, pci_sys->num_devices, sizeof (pci_sys->devices[0]), &cmp_devices); } #if defined(__GNU__) #include #include #include #endif static int map_dev_mem(void **dest, size_t mem_offset, size_t mem_size, int write) { #if defined(__GNU__) int err; mach_port_t master_device; mach_port_t devmem; mach_port_t pager; dev_mode_t mode = D_READ; vm_prot_t prot = VM_PROT_READ; int pagesize; if (get_privileged_ports (NULL, &master_device)) { *dest = 0; return EPERM; } if (write) { mode |= D_WRITE; prot |= VM_PROT_WRITE; } err = device_open (master_device, mode, "mem", &devmem); if (err) return err; pagesize = getpagesize(); if (mem_size % pagesize) mem_size += pagesize - (mem_size % pagesize); /* XXX: Mach should be fixed into supporting non-zero offset */ err = device_map (devmem, prot, 0x0, mem_offset + mem_size, &pager, 0); if (err) return err; err = vm_map (mach_task_self (), (vm_address_t *)dest, mem_size, (vm_address_t) 0, /* mask */ 1, /* anywhere? */ pager, mem_offset, 0, /* copy */ prot, VM_PROT_ALL, VM_INHERIT_SHARE); return err; #else int prot = PROT_READ; int flags = O_RDONLY; int memfd; if (write) { prot |= PROT_WRITE; flags = O_RDWR; } memfd = open("/dev/mem", flags | O_CLOEXEC); if (memfd == -1) return errno; *dest = mmap(NULL, mem_size, prot, MAP_SHARED, memfd, mem_offset); if (*dest == MAP_FAILED) { close(memfd); *dest = NULL; return errno; } close(memfd); return 0; #endif } static int 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 int 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; int 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 int pci_system_x86_conf1_write(unsigned bus, unsigned dev, unsigned func, pciaddr_t reg, const void *data, unsigned size) { unsigned addr = 0xCFC + (reg & 3); unsigned long sav; int 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 int 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 int pci_system_x86_conf2_read(unsigned bus, unsigned dev, unsigned func, pciaddr_t reg, void *data, unsigned size) { unsigned addr = 0xC000 | dev << 8 | reg; int 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 int pci_system_x86_conf2_write(unsigned bus, unsigned dev, unsigned func, pciaddr_t reg, const void *data, unsigned size) { unsigned addr = 0xC000 | dev << 8 | reg; int 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; } /* Check that this really looks like a PCI configuration. */ static error_t pci_system_x86_check (void) { int dev; uint16_t class, vendor; struct pci_device tmpdev = { 0 }; /* Look on bus 0 for a device that is a host bridge, a VGA card, * or an intel or compaq device. */ tmpdev.bus = 0; tmpdev.func = 0; class = 0; vendor = 0; for (dev = 0; dev < 32; dev++) { tmpdev.dev = dev; if (pci_device_cfg_read_u16 (&tmpdev, &class, PCI_CLASS_DEVICE)) continue; if (class == PCI_CLASS_BRIDGE_HOST || class == PCI_CLASS_DISPLAY_VGA) return 0; if (pci_device_cfg_read_u16 (&tmpdev, &vendor, PCI_VENDOR_ID)) continue; if (vendor == PCI_VENDOR_ID_INTEL || class == PCI_VENDOR_ID_COMPAQ) return 0; } return ENODEV; } static int pci_nfuncs(struct pci_device *dev, uint8_t *nfuncs) { uint8_t hdr; int err; struct pci_device tmpdev = *dev; tmpdev.func = 0; err = pci_device_cfg_read_u8 (&tmpdev, &hdr, PCI_HDRTYPE); if (err) return err; *nfuncs = hdr & 0x80 ? 8 : 1; return err; } /** * Read a PCI rom. */ static error_t pci_device_x86_read_rom(struct pci_device *dev, void *buffer) { void *bios = NULL; struct pci_device_private *d = (struct pci_device_private *)dev; int err; if ( (err = map_dev_mem(&bios, d->rom_base, dev->rom_size, 0)) ) return err; memcpy(buffer, bios, dev->rom_size); munmap(bios, dev->rom_size); return 0; } /** 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: fprintf(stderr,"unknown header type %02x\n", header_type); 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; offset = PCI_BAR_ADDR_0 + 0x4 * reg_num; /* Get the base address */ err = pci_device_cfg_read_u32 (dev, &addr, offset); if (err) return err; /* Test write all ones to the register, then restore it. */ reg = 0xffffffff; err = pci_device_cfg_write_u32 (dev, reg, offset); if (err) return err; err = pci_device_cfg_read_u32 (dev, &testval, offset); if (err) return err; err = pci_device_cfg_write_u32 (dev, addr, offset); 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_device_cfg_read_u32 (dev, &addr, offset + 4); 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_device_cfg_read_u32 (dev, ®, PCI_COMMAND); if (err) return err; if (!(reg & 0x1)) { reg |= 0x1; err = pci_device_cfg_write_u32 (dev, reg, PCI_COMMAND); if (err) return err; } } else if (dev->regions[reg_num].size > 0) { /* Enable the Memory Space bit */ err = pci_device_cfg_read_u32 (dev, ®, PCI_COMMAND); if (err) return err; if (!(reg & 0x2)) { reg |= 0x2; err = pci_device_cfg_write_u32 (dev, reg, PCI_COMMAND); if (err) return err; } } /* Clear the map pointer */ dev->regions[reg_num].memory = 0; return 0; } /* Read the XROMBAR in `dev' and save the rom size and rom base */ static error_t pci_device_x86_probe_rom (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; struct pci_device_private *d = (struct pci_device_private *)dev; /* First we need to know which type of header is this */ err = pci_device_cfg_read_u8 (dev, ®_8, PCI_HDRTYPE); 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_device_cfg_read_u32 (dev, ®, xrombar_addr); if (err) return err; reg_back = reg; reg = 0xFFFFF800; /* Base address: first 21 bytes */ err = pci_device_cfg_write_u32 (dev, reg, xrombar_addr); if (err) return err; err = pci_device_cfg_read_u32 (dev, ®, xrombar_addr); 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_device_cfg_write_u32 (dev, reg_back, xrombar_addr); if (err) return err; /* Enable the Memory Space bit */ err = pci_device_cfg_read_u32 (dev, ®, PCI_COMMAND); if (err) return err; if (!(reg & 0x2)) { reg |= 0x2; err = pci_device_cfg_write_u32 (dev, reg, PCI_COMMAND); if (err) return err; } dev->rom_size = rom_size; d->rom_base = rom_base; 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_device_cfg_read_u8 (dev, &hdrtype, PCI_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 */ pci_device_x86_probe_rom(dev); return 0; } /* Recursively scan bus number `bus' */ static error_t pci_system_x86_scan_bus (uint8_t bus) { error_t err; uint8_t dev, func, nfuncs, hdrtype, secbus; uint32_t reg; struct pci_device_private *d, *devices; struct pci_device scratchdev; scratchdev.bus = bus; for (dev = 0; dev < 32; dev++) { scratchdev.dev = dev; scratchdev.func = 0; err = pci_nfuncs (&scratchdev, &nfuncs); if (err) return err; for (func = 0; func < nfuncs; func++) { scratchdev.func = func; err = pci_device_cfg_read_u32 (&scratchdev, ®, PCI_VENDOR_ID); if (err) return err; if (PCI_VENDOR (reg) == PCI_VENDOR_INVALID || PCI_VENDOR (reg) == 0) continue; err = pci_device_cfg_read_u32 (&scratchdev, ®, PCI_CLASS); if (err) return err; err = pci_device_cfg_read_u8 (&scratchdev, &hdrtype, PCI_HDRTYPE); if (err) return err; devices = realloc (pci_sys->devices, (pci_sys->num_devices + 1) * sizeof (struct pci_device_private)); if (!devices) return ENOMEM; d = devices + pci_sys->num_devices; memset (d, 0, sizeof (struct pci_device_private)); /* Fixed values as PCI express is still not supported */ d->base.domain = 0; d->base.bus = bus; d->base.dev = dev; d->base.func = func; d->base.device_class = reg >> 8; 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_device_cfg_read_u8 (&scratchdev, &secbus, PCI_SECONDARY_BUS); if (err) return err; err = pci_system_x86_scan_bus (secbus); if (err) return err; break; } default: /* Unknown header, do nothing */ break; } } } return 0; } #if defined(__CYGWIN__) static int pci_device_x86_map_range(struct pci_device *dev, struct pci_device_mapping *map) { tagPhysStruct phys; phys.pvPhysAddress = (DWORD64)(DWORD32)map->base; phys.dwPhysMemSizeInBytes = map->size; map->memory = (PDWORD)MapPhysToLin(&phys); if (map->memory == NULL) return EFAULT; return 0; } static int pci_device_x86_unmap_range(struct pci_device *dev, struct pci_device_mapping *map) { tagPhysStruct phys; phys.pvPhysAddress = (DWORD64)(DWORD32)map->base; phys.dwPhysMemSizeInBytes = map->size; if (!UnmapPhysicalMemory(&phys)) return EFAULT; return 0; } #else int pci_device_x86_map_range(struct pci_device *dev, struct pci_device_mapping *map) { int err; if ( (err = map_dev_mem(&map->memory, map->base, map->size, map->flags & PCI_DEV_MAP_FLAG_WRITABLE)) ) return err; return 0; } int pci_device_x86_unmap_range(struct pci_device *dev, struct pci_device_mapping *map) { int err; err = pci_device_generic_unmap_range(dev, map); map->memory = NULL; return err; } #endif static int pci_device_x86_read_conf1(struct pci_device *dev, void *data, pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_read) { int err; *bytes_read = 0; while (size > 0) { int toread = 1 << (ffs(0x4 + (offset & 0x03)) - 1); if (toread > size) toread = size; err = pci_system_x86_conf1_read(dev->bus, dev->dev, dev->func, offset, data, toread); if (err) return err; offset += toread; data = (char*)data + toread; size -= toread; *bytes_read += toread; } return 0; } static int pci_device_x86_read_conf2(struct pci_device *dev, void *data, pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_read) { int err; *bytes_read = 0; while (size > 0) { int toread = 1 << (ffs(0x4 + (offset & 0x03)) - 1); if (toread > size) toread = size; err = pci_system_x86_conf2_read(dev->bus, dev->dev, dev->func, offset, data, toread); if (err) return err; offset += toread; data = (char*)data + toread; size -= toread; *bytes_read += toread; } return 0; } static int pci_device_x86_write_conf1(struct pci_device *dev, const void *data, pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_written) { int err; *bytes_written = 0; while (size > 0) { int towrite = 4; if (towrite > size) towrite = size; if (towrite > 4 - (offset & 0x3)) towrite = 4 - (offset & 0x3); err = pci_system_x86_conf1_write(dev->bus, dev->dev, dev->func, offset, data, towrite); if (err) return err; offset += towrite; data = (const char*)data + towrite; size -= towrite; *bytes_written += towrite; } return 0; } static int pci_device_x86_write_conf2(struct pci_device *dev, const void *data, pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_written) { int err; *bytes_written = 0; while (size > 0) { int towrite = 4; if (towrite > size) towrite = size; if (towrite > 4 - (offset & 0x3)) towrite = 4 - (offset & 0x3); err = pci_system_x86_conf2_write(dev->bus, dev->dev, dev->func, offset, data, towrite); if (err) return err; offset += towrite; data = (const char*)data + towrite; size -= towrite; *bytes_written += towrite; } return 0; } void pci_system_x86_destroy(void) { x86_disable_io(); } struct pci_io_handle * pci_device_x86_open_legacy_io(struct pci_io_handle *ret, struct pci_device *dev, pciaddr_t base, pciaddr_t size) { x86_enable_io(); ret->base = base; ret->size = size; ret->is_legacy = 1; return ret; } void pci_device_x86_close_io(struct pci_device *dev, struct pci_io_handle *handle) { /* Like in the Linux case, do not disable I/O, as it may be opened several * times, and closed fewer times. */ /* x86_disable_io(); */ } uint32_t pci_device_x86_read32(struct pci_io_handle *handle, uint32_t reg) { return inl(reg + handle->base); } uint16_t pci_device_x86_read16(struct pci_io_handle *handle, uint32_t reg) { return inw(reg + handle->base); } uint8_t pci_device_x86_read8(struct pci_io_handle *handle, uint32_t reg) { return inb(reg + handle->base); } void pci_device_x86_write32(struct pci_io_handle *handle, uint32_t reg, uint32_t data) { outl(data, reg + handle->base); } void pci_device_x86_write16(struct pci_io_handle *handle, uint32_t reg, uint16_t data) { outw(data, reg + handle->base); } void pci_device_x86_write8(struct pci_io_handle *handle, uint32_t reg, uint8_t data) { outb(data, reg + handle->base); } int pci_device_x86_map_legacy(struct pci_device *dev, pciaddr_t base, pciaddr_t size, unsigned map_flags, void **addr) { struct pci_device_mapping map; int err; map.base = base; map.size = size; map.flags = map_flags; err = pci_device_x86_map_range(dev, &map); *addr = map.memory; return err; } int pci_device_x86_unmap_legacy(struct pci_device *dev, void *addr, pciaddr_t size) { struct pci_device_mapping map; map.size = size; map.flags = 0; map.memory = addr; return pci_device_x86_unmap_range(dev, &map); } static const struct pci_system_methods x86_pci_method_conf1 = { .destroy = pci_system_x86_destroy, .read_rom = pci_device_x86_read_rom, .probe = pci_device_x86_probe, .map_range = pci_device_x86_map_range, .unmap_range = pci_device_x86_unmap_range, .read = pci_device_x86_read_conf1, .write = pci_device_x86_write_conf1, .fill_capabilities = pci_fill_capabilities_generic, .open_legacy_io = pci_device_x86_open_legacy_io, .close_io = pci_device_x86_close_io, .read32 = pci_device_x86_read32, .read16 = pci_device_x86_read16, .read8 = pci_device_x86_read8, .write32 = pci_device_x86_write32, .write16 = pci_device_x86_write16, .write8 = pci_device_x86_write8, .map_legacy = pci_device_x86_map_legacy, .unmap_legacy = pci_device_x86_unmap_legacy, }; static const struct pci_system_methods x86_pci_method_conf2 = { .destroy = pci_system_x86_destroy, .read_rom = pci_device_x86_read_rom, .probe = pci_device_x86_probe, .map_range = pci_device_x86_map_range, .unmap_range = pci_device_x86_unmap_range, .read = pci_device_x86_read_conf2, .write = pci_device_x86_write_conf2, .fill_capabilities = pci_fill_capabilities_generic, .open_legacy_io = pci_device_x86_open_legacy_io, .close_io = pci_device_x86_close_io, .read32 = pci_device_x86_read32, .read16 = pci_device_x86_read16, .read8 = pci_device_x86_read8, .write32 = pci_device_x86_write32, .write16 = pci_device_x86_write16, .write8 = pci_device_x86_write8, .map_legacy = pci_device_x86_map_legacy, .unmap_legacy = pci_device_x86_unmap_legacy, }; static int pci_probe(void) { pci_sys->methods = &x86_pci_method_conf1; if (pci_system_x86_conf1_probe() == 0) { if (pci_system_x86_check() == 0) return 1; } pci_sys->methods = &x86_pci_method_conf2; if (pci_system_x86_conf2_probe() == 0) { if (pci_system_x86_check() == 0) return 2; } pci_sys->methods = NULL; return 0; } _pci_hidden int pci_system_x86_create(void) { error_t err; int confx; 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; } confx = pci_probe (); if (!confx) { x86_disable_io (); free (pci_sys); pci_sys = NULL; return ENODEV; } else if (confx == 1) pci_sys->methods = &x86_pci_method_conf1; else pci_sys->methods = &x86_pci_method_conf2; /* Recursive scan */ pci_sys->num_devices = 0; err = pci_system_x86_scan_bus (0); if (err) { x86_disable_io (); if (pci_sys->num_devices) { free (pci_sys->devices); } free (pci_sys); pci_sys = NULL; return err; } sort_devices (); return 0; }