From 048fe0613847e3a9b39bbc3ea3e6db0f54517191 Mon Sep 17 00:00:00 2001 From: Damien Zammit Date: Sat, 10 Nov 2018 06:11:19 -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 Signed-off-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 | 364 ++++++++++++++++++++--------------- src/x86_pci.h | 88 +++++++++ 6 files changed, 813 insertions(+), 152 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 18f717d..1940cff 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 a45b093..d3b68d4 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 }; @@ -190,5 +196,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..1be5f17 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; @@ -635,7 +617,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 +638,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 +648,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 +683,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 +696,9 @@ 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) { - struct pci_system_x86 *pci_sys_x86 = (struct pci_system_x86 *) pci_sys; int err; *bytes_written = 0; @@ -705,7 +709,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_conf1_write(dev->bus, dev->dev, dev->func, offset, data, towrite); if (err) return err; @@ -717,13 +721,39 @@ pci_device_x86_write(struct pci_device *dev, const void *data, return 0; } -static void +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(); } -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 +766,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 +774,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 +829,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 +842,14 @@ 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, - .write = pci_device_x86_write, + .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, @@ -833,108 +863,144 @@ 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 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) { - 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) { + uint8_t nfuncs = 0; + uint8_t bus, dev, func; + uint32_t reg = 0, ndevs; + struct pci_device tmpdev = { 0 }; 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; - - 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; - } + error_t err; + int confx; - pci_sys->methods = &x86_pci_methods; + err = x86_enable_io (); + if (err) + return err; 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++; - } - } + tmpdev.bus = bus; + for (dev = 0; dev < 32; dev++) { + tmpdev.dev = dev; + pci_nfuncs(&tmpdev, &nfuncs); + for (func = 0; func < nfuncs; func++) { + if (pci_device_cfg_read_u32(&tmpdev, ®, PCI_VENDOR_ID)) + 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; + x86_disable_io(); + free(pci_sys); + 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++; - } - } + tmpdev.bus = bus; + for (dev = 0; dev < 32; dev++) { + tmpdev.dev = dev; + err = pci_nfuncs(&tmpdev, &nfuncs); + if (err) { + x86_disable_io (); + free (pci_sys); + pci_sys = NULL; + return ENODEV; + } + for (func = 0; func < nfuncs; func++) { + tmpdev.func = func; + if (pci_device_cfg_read_u32(&tmpdev, ®, PCI_VENDOR_ID)) + 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_device_cfg_read_u32(&tmpdev, ®, PCI_CLASS)) + continue; + device->base.device_class = (reg >> 8) & 0xFF; + device->base.revision = reg & 0xFF; + + if (pci_device_cfg_read_u32(&tmpdev, ®, PCI_SUB_VENDOR_ID)) + continue; + device->base.subvendor_id = PCI_VENDOR(reg); + device->base.subdevice_id = PCI_DEVICE(reg); + + device++; + } + } } 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.1