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. --- src/Makefile.am | 4 +- src/common_init.c | 4 +- src/hurd_pci.c | 299 ++++++++++++++++++++++++++++++++++++++++++++++++ src/pciaccess_private.h | 6 + src/x86_pci.c | 58 +++------- src/x86_pci.h | 77 +++++++++++++ 6 files changed, 406 insertions(+), 42 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 b1c0c3e..21572ee 100644 --- a/src/common_init.c +++ b/src/common_init.c @@ -62,7 +62,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..cf0b1d6 --- /dev/null +++ b/src/hurd_pci.c @@ -0,0 +1,299 @@ +/* + * 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 <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <string.h> +#include <strings.h> +#include <hurd.h> +#include <hurd/pci_conf.h> +#include <hurd/paths.h> + +#include "x86_pci.h" +#include "pciaccess.h" +#include "pciaccess_private.h" + +struct pci_system_hurd { + struct pci_system system; +}; + +/* Server port */ +mach_port_t pci_server_port = MACH_PORT_NULL; + +/* Get the server port */ +static int +init_client(void) +{ + int ret = 0; + pci_server_port = file_name_lookup(_SERVERS_PCI_CONF, 0, 0); + + if (pci_server_port == MACH_PORT_NULL) + ret = errno; + + return ret; +} + +/* + * Read 'size' bytes from B/D/F + reg and store them in 'data'. + * + * It's assumed that 'size' bytes are allocated in 'data' + */ +static int +pciclient_cfg_read(int bus, int dev, int func, int reg, char *buf, + size_t * nbytes) +{ + error_t err; + size_t nread; + char *data; + + data = buf; + nread = *nbytes; + err = pci_conf_read(pci_server_port, bus, dev, func, 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 'size' bytes from 'data' to B/D/F + reg */ +static int +pciclient_cfg_write(int bus, int dev, int func, int reg, char *buf, + size_t * nbytes) +{ + error_t err; + size_t nwrote; + + err = pci_conf_write(pci_server_port, bus, dev, func, reg, buf, *nbytes, + &nwrote); + + if (!err) + *nbytes = nwrote; + + return err; +} + +static int +pci_nfuncs(struct pci_system_hurd *pci_sys_hurd, int bus, int dev) +{ + uint8_t hdr; + size_t size; + int err; + + size = sizeof(hdr); + err = pciclient_cfg_read(bus, dev, 0, PCI_HDRTYPE, (char*)&hdr, &size); + + if (err) + return err; + + if(size != sizeof(hdr)) + return EIO; + + return hdr & 0x80 ? 8 : 1; +} + +static int +pci_device_hurd_read(struct pci_device *dev, void *data, + pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_read) +{ + int err; + + *bytes_read = 0; + while (size > 0) { + size_t toread = 1 << (ffs(0x4 + (offset & 0x03)) - 1); + if (toread > size) + toread = size; + + err = pciclient_cfg_read(dev->bus, dev->dev, dev->func, offset, + (char*)data, &toread); + if (err) + return err; + + offset += toread; + data = (char*)data + toread; + size -= toread; + *bytes_read += toread; + } + return 0; +} + +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; + + *bytes_written = 0; + 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(dev->bus, dev->dev, dev->func, offset, + (char*)data, &towrite); + if (err) + return err; + + offset += towrite; + data = (const char*)data + towrite; + size -= towrite; + *bytes_written += towrite; + } + return 0; +} + +static const struct pci_system_methods hurd_pci_methods = { + .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_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 ret, bus, dev, ndevs, func, nfuncs; + struct pci_system_hurd *pci_sys_hurd; + uint32_t reg; + size_t toread; + + ret = x86_enable_io(); + if (ret) + return ret; + + ret = init_client(); + if (ret) + return ret; + + 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; + + ndevs = 0; + for (bus = 0; bus < 256; bus++) { + for (dev = 0; dev < 32; dev++) { + nfuncs = pci_nfuncs(pci_sys_hurd, bus, dev); + for (func = 0; func < nfuncs; func++) { + toread = sizeof(reg); + if (pciclient_cfg_read(bus, dev, func, PCI_VENDOR_ID, + (char*)®, &toread) != 0) + continue; + if (PCI_VENDOR(reg) == PCI_VENDOR_INVALID || + PCI_VENDOR(reg) == 0 || + toread != sizeof(reg)) + continue; + ndevs++; + } + } + } + + 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; + for (bus = 0; bus < 256; bus++) { + for (dev = 0; dev < 32; dev++) { + nfuncs = pci_nfuncs(pci_sys_hurd, bus, dev); + for (func = 0; func < nfuncs; func++) { + toread = sizeof(reg); + if (pciclient_cfg_read(bus, dev, func, PCI_VENDOR_ID, + (char*)®, &toread) != 0) + continue; + if (PCI_VENDOR(reg) == PCI_VENDOR_INVALID || + PCI_VENDOR(reg) == 0 || + toread != sizeof(reg)) + continue; + device->base.domain = 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); + + toread = sizeof(reg); + if (pciclient_cfg_read(bus, dev, func, PCI_CLASS, + (char*)®,&toread) != 0) + continue; + device->base.device_class = reg >> 8; + device->base.revision = reg & 0xFF; + + toread = sizeof(reg); + if (pciclient_cfg_read(bus, dev, func, PCI_SUB_VENDOR_ID, + (char*)®, &toread) != 0) + continue; + if (toread != sizeof(reg)) + continue; + device->base.subvendor_id = PCI_VENDOR(reg); + device->base.subdevice_id = PCI_DEVICE(reg); + + device++; + } + } + } + + return 0; +} diff --git a/src/pciaccess_private.h b/src/pciaccess_private.h index 2f05b29..329e2fd 100644 --- a/src/pciaccess_private.h +++ b/src/pciaccess_private.h @@ -29,6 +29,9 @@ * \author Ian Romanick <i...@us.ibm.com> */ +#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) @@ -189,5 +192,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 49c1cab..9fccc5d 100644 --- a/src/x86_pci.c +++ b/src/x86_pci.c @@ -20,6 +20,8 @@ */ #define _GNU_SOURCE +#include "x86_pci.h" + #include <unistd.h> #include <stdio.h> #include <stdlib.h> @@ -36,7 +38,7 @@ #include <sys/io.h> -static int +int x86_enable_io(void) { if (!ioperm(0, 0xffff, 1)) @@ -44,7 +46,7 @@ x86_enable_io(void) return errno; } -static int +int x86_disable_io(void) { if (!ioperm(0, 0xffff, 0)) @@ -205,28 +207,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); @@ -446,7 +426,7 @@ pci_nfuncs(struct pci_system_x86 *pci_sys_x86, int bus, int dev) /** * Read a VGA rom using the 0xc0000 mapping. */ -static int +int pci_device_x86_read_rom(struct pci_device *dev, void *buffer) { void *bios; @@ -524,7 +504,7 @@ get_test_val_size( uint32_t testval ) return size; } -static int +int pci_device_x86_probe(struct pci_device *dev) { uint8_t irq, hdrtype; @@ -633,7 +613,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) { @@ -654,7 +634,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) { @@ -715,13 +695,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) { @@ -734,7 +714,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 @@ -742,46 +722,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) { @@ -797,7 +777,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) { diff --git a/src/x86_pci.h b/src/x86_pci.h new file mode 100644 index 0000000..2f796f4 --- /dev/null +++ b/src/x86_pci.h @@ -0,0 +1,77 @@ +/* + * 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. + */ + +#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_IRQ 0x3C + +int x86_enable_io(void); +int x86_disable_io(void); +void pci_system_x86_destroy(void); +int pci_device_x86_read_rom(struct pci_device *dev, void *buffer); +int pci_device_x86_probe(struct pci_device *dev); +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 */ -- 2.14.0