From b7ac3a88cf72aca317c14447c9305d60e7e07460 Mon Sep 17 00:00:00 2001 From: Damien Zammit Date: Sat, 10 Nov 2018 05:06:03 -0500 Subject: New module for the Hurd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This new module uses Hurd's RPCs for accessing the PCI configuration space. Direct access as in {read_write}_{8,16,32} functions is done by the old x86 module. Some x86 function prototypes are now declared in a new header for the Hurd module to use them, in order to duplicate as little code as possible. Author: Joan Lledó Also-by: Damien Zammit --- src/Makefile.am | 4 +- src/common_init.c | 4 +- src/hurd_pci.c | 496 ++++++++++++++++++++++++++++++++++++ src/pciaccess_private.h | 9 + src/x86_pci.c | 664 ++++++++++++++++++++++++++++++++---------------- src/x86_pci.h | 88 +++++++ 6 files changed, 1046 insertions(+), 219 deletions(-) create mode 100644 src/hurd_pci.c create mode 100644 src/x86_pci.h diff --git a/src/Makefile.am b/src/Makefile.am index 3a46a85..f222aa5 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -52,12 +52,12 @@ VGA_ARBITER = common_vgaarb_stub.c endif if GNU -OS_SUPPORT = x86_pci.c +OS_SUPPORT = hurd_pci.c x86_pci.c x86_pci.h VGA_ARBITER = common_vgaarb_stub.c endif if CYGWIN -OS_SUPPORT = x86_pci.c +OS_SUPPORT = x86_pci.c x86_pci.h VGA_ARBITER = common_vgaarb_stub.c endif diff --git a/src/common_init.c b/src/common_init.c index f7b59bd..d83eb33 100644 --- a/src/common_init.c +++ b/src/common_init.c @@ -65,7 +65,9 @@ pci_system_init( void ) err = pci_system_openbsd_create(); #elif defined(__sun) err = pci_system_solx_devfs_create(); -#elif defined(__GNU__) || defined(__CYGWIN__) +#elif defined(__GNU__) + err = pci_system_hurd_create(); +#elif defined(__CYGWIN__) err = pci_system_x86_create(); #else # error "Unsupported OS" diff --git a/src/hurd_pci.c b/src/hurd_pci.c new file mode 100644 index 0000000..28bef16 --- /dev/null +++ b/src/hurd_pci.c @@ -0,0 +1,496 @@ +/* + * Copyright (c) 2018, Damien Zammit + * Copyright (c) 2017, Joan Lledó + * Copyright (c) 2009, 2012 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. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "x86_pci.h" +#include "pciaccess.h" +#include "pciaccess_private.h" + +/* + * Hurd PCI access using RPCs. + * + * Some functions are shared with the x86 module to avoid repeating code. + */ + +/* Server path */ +#define _SERVERS_BUS_PCI _SERVERS_BUS "/pci" + +/* File names */ +#define FILE_CONFIG_NAME "config" +#define FILE_ROM_NAME "rom" + +/* Level in the fs tree */ +typedef enum { + LEVEL_NONE, + LEVEL_DOMAIN, + LEVEL_BUS, + LEVEL_DEV, + LEVEL_FUNC +} tree_level; + +struct pci_system_hurd { + struct pci_system system; +}; + +static int +pci_device_hurd_probe(struct pci_device *dev) +{ + uint8_t irq; + int err, i; + struct pci_bar regions[6]; + struct pci_xrom_bar rom; + struct pci_device_private *d; + size_t size; + char *buf; + + /* Many of the fields were filled in during initial device enumeration. + * At this point, we need to fill in regions, rom_size, and irq. + */ + + err = pci_device_cfg_read_u8(dev, &irq, PCI_IRQ); + if (err) + return err; + dev->irq = irq; + + /* Get regions */ + buf = (char *)®ions; + size = sizeof(regions); + d = (struct pci_device_private *)dev; + err = pci_get_dev_regions(d->device_port, &buf, &size); + if(err) + return err; + + if((char*)®ions != buf) + { + /* Sanity check for bogus server. */ + if(size > sizeof(regions)) + { + vm_deallocate(mach_task_self(), (vm_address_t) buf, size); + return EGRATUITOUS; + } + + memcpy(®ions, buf, size); + vm_deallocate(mach_task_self(), (vm_address_t) buf, size); + } + + for(i=0; i<6; i++) + { + if(regions[i].size == 0) + continue; + + dev->regions[i].base_addr = regions[i].base_addr; + dev->regions[i].size = regions[i].size; + dev->regions[i].is_IO = regions[i].is_IO; + dev->regions[i].is_prefetchable = regions[i].is_prefetchable; + dev->regions[i].is_64 = regions[i].is_64; + } + + /* Get rom info */ + buf = (char *)&rom; + size = sizeof(rom); + err = pci_get_dev_rom(d->device_port, &buf, &size); + if(err) + return err; + + if((char*)&rom != buf) + { + /* Sanity check for bogus server. */ + if(size > sizeof(rom)) + { + vm_deallocate(mach_task_self(), (vm_address_t) buf, size); + return EGRATUITOUS; + } + + memcpy(&rom, buf, size); + vm_deallocate(mach_task_self(), (vm_address_t) buf, size); + } + + d->rom_base = rom.base_addr; + dev->rom_size = rom.size; + + return 0; +} + +/* + * Read `nbytes' bytes from `reg' in device's configuretion space + * and store them in `buf'. + * + * It's assumed that `nbytes' bytes are allocated in `buf' + */ +static int +pciclient_cfg_read(mach_port_t device_port, int reg, char *buf, + size_t * nbytes) +{ + int err; + size_t nread; + char *data; + + data = buf; + nread = *nbytes; + err = pci_conf_read(device_port, reg, &data, &nread, *nbytes); + if (err) + return err; + + if (data != buf) { + if (nread > *nbytes) /* Sanity check for bogus server. */ { + vm_deallocate(mach_task_self(), (vm_address_t) data, nread); + return EGRATUITOUS; + } + + memcpy(buf, data, nread); + vm_deallocate(mach_task_self(), (vm_address_t)data, nread); + } + + *nbytes = nread; + + return 0; +} + +/* Write `nbytes' bytes from `buf' to `reg' in device's configuration space */ +static int +pciclient_cfg_write(mach_port_t device_port, int reg, char *buf, + size_t * nbytes) +{ + int err; + size_t nwrote; + + err = pci_conf_write(device_port, reg, buf, *nbytes, &nwrote); + + if (!err) + *nbytes = nwrote; + + return err; +} + +/* + * Read up to `size' bytes from `dev' configuration space to `data' starting + * at `offset'. Write the amount on read bytes in `bytes_read'. + */ +static int +pci_device_hurd_read(struct pci_device *dev, void *data, + pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_read) +{ + int err; + struct pci_device_private *d; + + *bytes_read = 0; + d = (struct pci_device_private *)dev; + while (size > 0) { + size_t toread = 1 << (ffs(0x4 + (offset & 0x03)) - 1); + if (toread > size) + toread = size; + + err = pciclient_cfg_read(d->device_port, offset, (char*)data, + &toread); + if (err) + return err; + + offset += toread; + data = (char*)data + toread; + size -= toread; + *bytes_read += toread; + } + return 0; +} + +/* + * Write up to `size' bytes from `data' to `dev' configuration space starting + * at `offset'. Write the amount on written bytes in `bytes_written'. + */ +static int +pci_device_hurd_write(struct pci_device *dev, const void *data, + pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_written) +{ + int err; + struct pci_device_private *d; + + *bytes_written = 0; + d = (struct pci_device_private *)dev; + while (size > 0) { + size_t towrite = 4; + if (towrite > size) + towrite = size; + if (towrite > 4 - (offset & 0x3)) + towrite = 4 - (offset & 0x3); + + err = pciclient_cfg_write(d->device_port, offset, (char*)data, + &towrite); + if (err) + return err; + + offset += towrite; + data = (const char*)data + towrite; + size -= towrite; + *bytes_written += towrite; + } + return 0; +} + +/* + * Copy the device's firmware in `buffer' + */ +static int +pci_device_hurd_read_rom(struct pci_device * dev, void * buffer) +{ + ssize_t rd; + int romfd; + char server[NAME_MAX]; + + snprintf(server, NAME_MAX, "%s/%04x/%02x/%02x/%01u/%s", _SERVERS_BUS_PCI, + dev->domain, dev->bus, dev->dev, dev->func, FILE_ROM_NAME); + + romfd = open(server, O_RDONLY | O_CLOEXEC); + if (romfd == -1) + return errno; + + rd = read(romfd, buffer, dev->rom_size); + if (rd != dev->rom_size) { + close(romfd); + return errno; + } + + close(romfd); + + return 0; +} + +/* + * Each device has its own server where send RPC's to. + * + * Deallocate the port before destroying the device. + */ +static void +pci_device_hurd_destroy(struct pci_device *dev) +{ + struct pci_device_private *d = (struct pci_device_private*) dev; + + mach_port_deallocate (mach_task_self (), d->device_port); +} + +/* Walk through the FS tree to see what is allowed for us */ +static int +enum_devices(const char *parent, struct pci_device_private **device, + int domain, int bus, int dev, int func, tree_level lev) +{ + int err, ret; + DIR *dir; + struct dirent *entry; + char path[NAME_MAX]; + char server[NAME_MAX]; + uint32_t reg; + size_t toread; + mach_port_t device_port; + + dir = opendir(parent); + if (!dir) + return errno; + + while ((entry = readdir(dir)) != 0) { + snprintf(path, NAME_MAX, "%s/%s", parent, entry->d_name); + if (entry->d_type == DT_DIR) { + if (!strncmp(entry->d_name, ".", NAME_MAX) + || !strncmp(entry->d_name, "..", NAME_MAX)) + continue; + + errno = 0; + ret = strtol(entry->d_name, 0, 16); + if (errno) + return errno; + + /* + * We found a valid directory. + * Update the address and switch to the next level. + */ + switch (lev) { + case LEVEL_DOMAIN: + domain = ret; + break; + case LEVEL_BUS: + bus = ret; + break; + case LEVEL_DEV: + dev = ret; + break; + case LEVEL_FUNC: + func = ret; + break; + default: + return -1; + } + + err = enum_devices(path, device, domain, bus, dev, func, lev+1); + if (err == EPERM) + continue; + } + else { + if (strncmp(entry->d_name, FILE_CONFIG_NAME, NAME_MAX)) + /* We are looking for the config file */ + continue; + + /* We found an available virtual device, add it to our list */ + snprintf(server, NAME_MAX, "%s/%04x/%02x/%02x/%01u/%s", + _SERVERS_BUS_PCI, domain, bus, dev, func, + entry->d_name); + device_port = file_name_lookup(server, 0, 0); + if (device_port == MACH_PORT_NULL) + return errno; + + toread = sizeof(reg); + err = pciclient_cfg_read(device_port, PCI_VENDOR_ID, (char*)®, + &toread); + if (err) + return err; + if (toread != sizeof(reg)) + return -1; + + (*device)->base.domain = domain; + (*device)->base.bus = bus; + (*device)->base.dev = dev; + (*device)->base.func = func; + (*device)->base.vendor_id = PCI_VENDOR(reg); + (*device)->base.device_id = PCI_DEVICE(reg); + + toread = sizeof(reg); + err = pciclient_cfg_read(device_port, PCI_CLASS, (char*)®, + &toread); + if (err) + return err; + if (toread != sizeof(reg)) + return -1; + + (*device)->base.device_class = reg >> 8; + (*device)->base.revision = reg & 0xFF; + + toread = sizeof(reg); + err = pciclient_cfg_read(device_port, PCI_SUB_VENDOR_ID, + (char*)®, &toread); + if (err) + return err; + if (toread != sizeof(reg)) + return -1; + + (*device)->base.subvendor_id = PCI_VENDOR(reg); + (*device)->base.subdevice_id = PCI_DEVICE(reg); + + (*device)->device_port = device_port; + + (*device)++; + } + } + + return 0; +} + +static const struct pci_system_methods hurd_pci_methods = { + .destroy = pci_system_x86_destroy, + .destroy_device = pci_device_hurd_destroy, + .read_rom = pci_device_hurd_read_rom, + .probe = pci_device_hurd_probe, + .map_range = pci_device_x86_map_range, + .unmap_range = pci_device_x86_unmap_range, + .read = pci_device_hurd_read, + .write = pci_device_hurd_write, + .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, +}; + +_pci_hidden int +pci_system_hurd_create(void) +{ + struct pci_device_private *device; + int err; + struct pci_system_hurd *pci_sys_hurd; + size_t ndevs; + mach_port_t pci_server_port; + + /* If we can open pci cfg io ports on hurd, + * we are the arbiter, therefore try x86 method first */ + err = pci_system_x86_create(); + if (!err) + return 0; + + pci_sys_hurd = calloc(1, sizeof(struct pci_system_hurd)); + if (pci_sys_hurd == NULL) { + x86_disable_io(); + return ENOMEM; + } + pci_sys = &pci_sys_hurd->system; + + pci_sys->methods = &hurd_pci_methods; + + pci_server_port = file_name_lookup(_SERVERS_BUS_PCI, 0, 0); + if (!pci_server_port) { + /* Fall back to x86 access method */ + return pci_system_x86_create(); + } + + /* The server gives us the number of available devices for us */ + err = pci_get_ndevs (pci_server_port, &ndevs); + if (err) { + mach_port_deallocate (mach_task_self (), pci_server_port); + /* Fall back to x86 access method */ + return pci_system_x86_create(); + } + mach_port_deallocate (mach_task_self (), pci_server_port); + + pci_sys->num_devices = ndevs; + pci_sys->devices = calloc(ndevs, sizeof(struct pci_device_private)); + if (pci_sys->devices == NULL) { + x86_disable_io(); + free(pci_sys_hurd); + pci_sys = NULL; + return ENOMEM; + } + + device = pci_sys->devices; + err = enum_devices(_SERVERS_BUS_PCI, &device, -1, -1, -1, -1, + LEVEL_DOMAIN); + if (err) + return pci_system_x86_create(); + + return 0; +} diff --git a/src/pciaccess_private.h b/src/pciaccess_private.h index 2f05b29..10c9698 100644 --- a/src/pciaccess_private.h +++ b/src/pciaccess_private.h @@ -29,6 +29,9 @@ * \author Ian Romanick */ +#ifndef PCIACCESS_PRIVATE_H +#define PCIACCESS_PRIVATE_H + #if defined(__GNUC__) && (__GNUC__ >= 4) # define _pci_hidden __attribute__((visibility("hidden"))) #elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550) @@ -150,6 +153,9 @@ struct pci_device_private { #ifdef __sun int is_primary; #endif +#ifdef __GNU__ + unsigned long device_port; +#endif }; @@ -189,5 +195,8 @@ extern int pci_system_netbsd_create( void ); extern int pci_system_openbsd_create( void ); extern void pci_system_openbsd_init_dev_mem( int ); extern int pci_system_solx_devfs_create( void ); +extern int pci_system_hurd_create( void ); extern int pci_system_x86_create( void ); extern void pci_io_cleanup( void ); + +#endif /* PCIACCESS_PRIVATE_H */ diff --git a/src/x86_pci.c b/src/x86_pci.c index 6b6a026..786ee3c 100644 --- a/src/x86_pci.c +++ b/src/x86_pci.c @@ -1,4 +1,6 @@ /* + * Copyright (c) 2018 Damien Zammit + * Copyright (c) 2017 Joan Lledó * Copyright (c) 2009, 2012 Samuel Thibault * Heavily inspired from the freebsd, netbsd, and openbsd backends * (C) Copyright Eric Anholt 2006 @@ -22,6 +24,8 @@ #include "config.h" #endif +#include "x86_pci.h" + #include #include #include @@ -38,7 +42,7 @@ #include -static int +int x86_enable_io(void) { if (!ioperm(0, 0xffff, 1)) @@ -46,7 +50,7 @@ x86_enable_io(void) return errno; } -static int +int x86_disable_io(void) { if (!ioperm(0, 0xffff, 0)) @@ -207,34 +211,6 @@ outl(uint32_t value, uint16_t port) #endif -#define PCI_VENDOR(reg) ((reg) & 0xFFFF) -#define PCI_VENDOR_INVALID 0xFFFF - -#define PCI_VENDOR_ID 0x00 -#define PCI_SUB_VENDOR_ID 0x2c -#define PCI_VENDOR_ID_COMPAQ 0x0e11 -#define PCI_VENDOR_ID_INTEL 0x8086 - -#define PCI_DEVICE(reg) (((reg) >> 16) & 0xFFFF) -#define PCI_DEVICE_INVALID 0xFFFF - -#define PCI_CLASS 0x08 -#define PCI_CLASS_DEVICE 0x0a -#define PCI_CLASS_DISPLAY_VGA 0x0300 -#define PCI_CLASS_BRIDGE_HOST 0x0600 - -#define PCIC_DISPLAY 0x03 -#define PCIS_DISPLAY_VGA 0x00 - -#define PCI_HDRTYPE 0x0E -#define PCI_IRQ 0x3C - -struct pci_system_x86 { - struct pci_system system; - int (*read)(unsigned bus, unsigned dev, unsigned func, pciaddr_t reg, void *data, unsigned size); - int (*write)(unsigned bus, unsigned dev, unsigned func, pciaddr_t reg, const void *data, unsigned size); -}; - static int pci_system_x86_conf1_probe(void) { @@ -408,62 +384,68 @@ pci_system_x86_conf2_write(unsigned bus, unsigned dev, unsigned func, pciaddr_t } /* Check that this really looks like a PCI configuration. */ -static int -pci_system_x86_check(struct pci_system_x86 *pci_sys_x86) +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++) { - if (pci_sys_x86->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_x86->read(0, dev, 0, PCI_VENDOR_ID, &vendor, sizeof(vendor))) - continue; - if (vendor == PCI_VENDOR_ID_INTEL || class == PCI_VENDOR_ID_COMPAQ) - return 0; + 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_system_x86 *pci_sys_x86, int bus, int dev) +pci_nfuncs(struct pci_device *dev, uint8_t *nfuncs) { uint8_t hdr; int err; + struct pci_device tmpdev = *dev; - err = pci_sys_x86->read(bus, dev, 0, PCI_HDRTYPE, &hdr, sizeof(hdr)); + tmpdev.func = 0; + + err = pci_device_cfg_read_u8 (&tmpdev, &hdr, PCI_HDRTYPE); if (err) return err; - return hdr & 0x80 ? 8 : 1; + *nfuncs = hdr & 0x80 ? 8 : 1; + return err; } /** - * Read a VGA rom using the 0xc0000 mapping. + * Read a PCI rom. */ -static int +static error_t pci_device_x86_read_rom(struct pci_device *dev, void *buffer) { void *bios; int memfd; - - if ((dev->device_class & 0x00ffff00) != - ((PCIC_DISPLAY << 16) | ( PCIS_DISPLAY_VGA << 8))) { - return ENOSYS; - } + struct pci_device_private *d = (struct pci_device_private *)dev; memfd = open("/dev/mem", O_RDONLY | O_CLOEXEC); if (memfd == -1) return errno; - bios = mmap(NULL, dev->rom_size, PROT_READ, 0, memfd, 0xc0000); + bios = mmap(NULL, dev->rom_size, PROT_READ, 0, memfd, d->rom_base); if (bios == MAP_FAILED) { close(memfd); return errno; @@ -526,75 +508,301 @@ get_test_val_size( uint32_t testval ) return size; } -static int -pci_device_x86_probe(struct pci_device *dev) +/* 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) { - uint8_t irq, hdrtype; - int err, i, bar; + error_t err; + uint8_t offset; + uint32_t reg, addr, testval; + int memfd; - /* Many of the fields were filled in during initial device enumeration. - * At this point, we need to fill in regions, rom_size, and irq. - */ + offset = PCI_BAR_ADDR_0 + 0x4 * reg_num; - err = pci_device_cfg_read_u8(dev, &irq, PCI_IRQ); + /* Get the base address */ + err = pci_device_cfg_read_u32 (dev, &addr, offset); if (err) - return err; - dev->irq = irq; + return err; - err = pci_device_cfg_read_u8(dev, &hdrtype, PCI_HDRTYPE); + /* 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; + 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; - bar = 0x10; - for (i = 0; i < pci_device_x86_get_num_regions(hdrtype); i++, bar += 4) { - uint32_t addr, testval; - - /* Get the base address */ - err = pci_device_cfg_read_u32(dev, &addr, bar); - if (err != 0) - continue; - - /* Test write all ones to the register, then restore it. */ - err = pci_device_cfg_write_u32(dev, 0xffffffff, bar); - if (err != 0) - continue; - pci_device_cfg_read_u32(dev, &testval, bar); - err = pci_device_cfg_write_u32(dev, addr, bar); - - if (addr & 0x01) - dev->regions[i].is_IO = 1; - if (addr & 0x04) - dev->regions[i].is_64 = 1; - if (addr & 0x08) - dev->regions[i].is_prefetchable = 1; - - /* Set the size */ - dev->regions[i].size = get_test_val_size(testval); - - /* Set the base address value */ - if (dev->regions[i].is_64) { - uint32_t top; - - err = pci_device_cfg_read_u32(dev, &top, bar + 4); - if (err != 0) - continue; - - dev->regions[i].base_addr = ((uint64_t)top << 32) | - get_map_base(addr); - bar += 4; - i++; - } else { - dev->regions[i].base_addr = get_map_base(addr); - } + 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; + } + + /* 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_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; + } + + /* 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 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++; } - /* If it's a VGA device, set up the rom size for read_rom using the - * 0xc0000 mapping. - */ - if ((dev->device_class & 0x00ffff00) == - ((PCIC_DISPLAY << 16) | (PCIS_DISPLAY_VGA << 8))) + /* 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++) { - dev->rom_size = 64 * 1024; + 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; + + err = pci_device_x86_probe (&d->base); + 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_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; @@ -635,7 +843,7 @@ pci_device_x86_unmap_range(struct pci_device *dev, #else -static int +int pci_device_x86_map_range(struct pci_device *dev, struct pci_device_mapping *map) { @@ -656,7 +864,7 @@ pci_device_x86_map_range(struct pci_device *dev, return 0; } -static int +int pci_device_x86_unmap_range(struct pci_device *dev, struct pci_device_mapping *map) { @@ -666,10 +874,33 @@ pci_device_x86_unmap_range(struct pci_device *dev, #endif static int -pci_device_x86_read(struct pci_device *dev, void *data, +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) { - struct pci_system_x86 *pci_sys_x86 = (struct pci_system_x86 *) pci_sys; int err; *bytes_read = 0; @@ -678,7 +909,7 @@ pci_device_x86_read(struct pci_device *dev, void *data, if (toread > size) toread = size; - err = pci_sys_x86->read(dev->bus, dev->dev, dev->func, offset, data, toread); + err = pci_system_x86_conf2_read(dev->bus, dev->dev, dev->func, offset, data, toread); if (err) return err; @@ -691,10 +922,35 @@ pci_device_x86_read(struct pci_device *dev, void *data, } static int -pci_device_x86_write(struct pci_device *dev, const void *data, +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) { - struct pci_system_x86 *pci_sys_x86 = (struct pci_system_x86 *) pci_sys; int err; *bytes_written = 0; @@ -705,7 +961,7 @@ pci_device_x86_write(struct pci_device *dev, const void *data, if (towrite > 4 - (offset & 0x3)) towrite = 4 - (offset & 0x3); - err = pci_sys_x86->write(dev->bus, dev->dev, dev->func, offset, data, towrite); + err = pci_system_x86_conf2_write(dev->bus, dev->dev, dev->func, offset, data, towrite); if (err) return err; @@ -717,13 +973,13 @@ pci_device_x86_write(struct pci_device *dev, const void *data, return 0; } -static void +void pci_system_x86_destroy(void) { x86_disable_io(); } -static struct pci_io_handle * +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) { @@ -736,7 +992,7 @@ pci_device_x86_open_legacy_io(struct pci_io_handle *ret, return ret; } -static void +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 @@ -744,46 +1000,46 @@ pci_device_x86_close_io(struct pci_device *dev, struct pci_io_handle *handle) /* x86_disable_io(); */ } -static uint32_t +uint32_t pci_device_x86_read32(struct pci_io_handle *handle, uint32_t reg) { return inl(reg + handle->base); } -static uint16_t +uint16_t pci_device_x86_read16(struct pci_io_handle *handle, uint32_t reg) { return inw(reg + handle->base); } -static uint8_t +uint8_t pci_device_x86_read8(struct pci_io_handle *handle, uint32_t reg) { return inb(reg + handle->base); } -static void +void pci_device_x86_write32(struct pci_io_handle *handle, uint32_t reg, uint32_t data) { outl(data, reg + handle->base); } -static void +void pci_device_x86_write16(struct pci_io_handle *handle, uint32_t reg, uint16_t data) { outw(data, reg + handle->base); } -static void +void pci_device_x86_write8(struct pci_io_handle *handle, uint32_t reg, uint8_t data) { outb(data, reg + handle->base); } -static int +int pci_device_x86_map_legacy(struct pci_device *dev, pciaddr_t base, pciaddr_t size, unsigned map_flags, void **addr) { @@ -799,7 +1055,7 @@ pci_device_x86_map_legacy(struct pci_device *dev, pciaddr_t base, return err; } -static int +int pci_device_x86_unmap_legacy(struct pci_device *dev, void *addr, pciaddr_t size) { @@ -812,14 +1068,35 @@ pci_device_x86_unmap_legacy(struct pci_device *dev, void *addr, return pci_device_x86_unmap_range(dev, &map); } -static const struct pci_system_methods x86_pci_methods = { +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, - .write = pci_device_x86_write, + .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, @@ -833,108 +1110,63 @@ static const struct pci_system_methods x86_pci_methods = { .unmap_legacy = pci_device_x86_unmap_legacy, }; -static int pci_probe(struct pci_system_x86 *pci_sys_x86) +static int pci_probe(void) { + pci_sys->methods = &x86_pci_method_conf1; if (pci_system_x86_conf1_probe() == 0) { - pci_sys_x86->read = pci_system_x86_conf1_read; - pci_sys_x86->write = pci_system_x86_conf1_write; - if (pci_system_x86_check(pci_sys_x86) == 0) - return 0; + if (pci_system_x86_check() == 0) + return 1; } + pci_sys->methods = &x86_pci_method_conf2; if (pci_system_x86_conf2_probe() == 0) { - pci_sys_x86->read = pci_system_x86_conf2_read; - pci_sys_x86->write = pci_system_x86_conf2_write; - if (pci_system_x86_check(pci_sys_x86) == 0) - return 0; + if (pci_system_x86_check() == 0) + return 2; } - return ENODEV; + pci_sys->methods = NULL; + return 0; } _pci_hidden int pci_system_x86_create(void) { - struct pci_device_private *device; - int ret, bus, dev, ndevs, func, nfuncs; - struct pci_system_x86 *pci_sys_x86; - uint32_t reg; - - ret = x86_enable_io(); - if (ret) - return ret; + error_t err; + int confx; - pci_sys_x86 = calloc(1, sizeof(struct pci_system_x86)); - if (pci_sys_x86 == NULL) { - x86_disable_io(); - return ENOMEM; - } - pci_sys = &pci_sys_x86->system; - - ret = pci_probe(pci_sys_x86); - if (ret) { - x86_disable_io(); - free(pci_sys_x86); - pci_sys = NULL; - return ret; - } + err = x86_enable_io (); + if (err) + return err; - pci_sys->methods = &x86_pci_methods; - - ndevs = 0; - for (bus = 0; bus < 256; bus++) { - for (dev = 0; dev < 32; dev++) { - nfuncs = pci_nfuncs(pci_sys_x86, bus, dev); - for (func = 0; func < nfuncs; func++) { - if (pci_sys_x86->read(bus, dev, func, PCI_VENDOR_ID, ®, sizeof(reg)) != 0) - continue; - if (PCI_VENDOR(reg) == PCI_VENDOR_INVALID || - PCI_VENDOR(reg) == 0) - continue; - ndevs++; - } - } + pci_sys = calloc (1, sizeof (struct pci_system)); + if (pci_sys == NULL) + { + x86_disable_io (); + return ENOMEM; } - pci_sys->num_devices = ndevs; - pci_sys->devices = calloc(ndevs, sizeof(struct pci_device_private)); - if (pci_sys->devices == NULL) { - x86_disable_io(); - free(pci_sys_x86); - pci_sys = NULL; - 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; - device = pci_sys->devices; - for (bus = 0; bus < 256; bus++) { - for (dev = 0; dev < 32; dev++) { - nfuncs = pci_nfuncs(pci_sys_x86, bus, dev); - for (func = 0; func < nfuncs; func++) { - if (pci_sys_x86->read(bus, dev, func, PCI_VENDOR_ID, ®, sizeof(reg)) != 0) - continue; - if (PCI_VENDOR(reg) == PCI_VENDOR_INVALID || - PCI_VENDOR(reg) == 0) - continue; - device->base.domain = device->base.domain_16 = 0; - device->base.bus = bus; - device->base.dev = dev; - device->base.func = func; - device->base.vendor_id = PCI_VENDOR(reg); - device->base.device_id = PCI_DEVICE(reg); - - if (pci_sys_x86->read(bus, dev, func, PCI_CLASS, ®, sizeof(reg)) != 0) - continue; - device->base.device_class = reg >> 8; - device->base.revision = reg & 0xFF; - - if (pci_sys_x86->read(bus, dev, func, PCI_SUB_VENDOR_ID, ®, sizeof(reg)) != 0) - continue; - device->base.subvendor_id = PCI_VENDOR(reg); - device->base.subdevice_id = PCI_DEVICE(reg); - - device++; - } - } + /* Recursive scan */ + pci_sys->num_devices = 0; + err = pci_system_x86_scan_bus (0); + if (err) + { + x86_disable_io (); + free (pci_sys); + pci_sys = NULL; + return err; } return 0; diff --git a/src/x86_pci.h b/src/x86_pci.h new file mode 100644 index 0000000..22c9318 --- /dev/null +++ b/src/x86_pci.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2017 Joan Lledó + * Copyright (c) 2009, 2012 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. + */ + +/* Macros and declarations used by both x86 and Hurd modules. */ + +#ifndef X86_PCI_H +#define X86_PCI_H + +#include "pciaccess.h" +#include "pciaccess_private.h" + +#define PCI_VENDOR(reg) ((reg) & 0xFFFF) +#define PCI_VENDOR_INVALID 0xFFFF + +#define PCI_VENDOR_ID 0x00 +#define PCI_SUB_VENDOR_ID 0x2c +#define PCI_VENDOR_ID_COMPAQ 0x0e11 +#define PCI_VENDOR_ID_INTEL 0x8086 + +#define PCI_DEVICE(reg) (((reg) >> 16) & 0xFFFF) +#define PCI_DEVICE_INVALID 0xFFFF + +#define PCI_CLASS 0x08 +#define PCI_CLASS_DEVICE 0x0a +#define PCI_CLASS_DISPLAY_VGA 0x0300 +#define PCI_CLASS_BRIDGE_HOST 0x0600 + +#define PCIC_DISPLAY 0x03 +#define PCIS_DISPLAY_VGA 0x00 + +#define PCI_HDRTYPE 0x0E +#define PCI_HDRTYPE_DEVICE 0x00 +#define PCI_HDRTYPE_BRIDGE 0x01 +#define PCI_HDRTYPE_CARDBUS 0x02 +#define PCI_IRQ 0x3C + +#define PCI_BAR_ADDR_0 0x10 +#define PCI_XROMBAR_ADDR_00 0x30 +#define PCI_XROMBAR_ADDR_01 0x38 + +#define PCI_COMMAND 0x04 +#define PCI_SECONDARY_BUS 0x19 + +int x86_enable_io(void); +int x86_disable_io(void); +void pci_system_x86_destroy(void); +int pci_device_x86_map_range(struct pci_device *dev, + struct pci_device_mapping *map); +int pci_device_x86_unmap_range(struct pci_device *dev, + struct pci_device_mapping *map); +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); +void pci_device_x86_close_io(struct pci_device *dev, + struct pci_io_handle *handle); +uint32_t pci_device_x86_read32(struct pci_io_handle *handle, uint32_t reg); +uint16_t pci_device_x86_read16(struct pci_io_handle *handle, uint32_t reg); +uint8_t pci_device_x86_read8(struct pci_io_handle *handle, uint32_t reg); +void pci_device_x86_write32(struct pci_io_handle *handle, uint32_t reg, + uint32_t data); +void pci_device_x86_write16(struct pci_io_handle *handle, uint32_t reg, + uint16_t data); +void pci_device_x86_write8(struct pci_io_handle *handle, uint32_t reg, + uint8_t data); +int pci_device_x86_map_legacy(struct pci_device *dev, pciaddr_t base, + pciaddr_t size, unsigned map_flags, void **addr); +int pci_device_x86_unmap_legacy(struct pci_device *dev, void *addr, + pciaddr_t size); + +#endif /* X86_PCI_H */ -- cgit v1.2.3