summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDamien Zammit <damien@zamaudio.com>2018-11-10 06:11:19 -0500
committerAdam Jackson <ajax@nwnk.net>2019-07-11 15:49:07 +0000
commit048fe0613847e3a9b39bbc3ea3e6db0f54517191 (patch)
tree5c7e035405d53e22a88222770ad60258a0b4b534
parent6e3837284372d70a9427918f622bf1dc7e2a3db6 (diff)
downloadxorg-lib-libpciaccess-048fe0613847e3a9b39bbc3ea3e6db0f54517191.tar.gz
New module for the Hurd
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ó <joanlluislledo@gmail.com> Also-by: Damien Zammit <damien@zamaudio.com> Signed-off-by: Damien Zammit <damien@zamaudio.com>
-rw-r--r--src/Makefile.am4
-rw-r--r--src/common_init.c4
-rw-r--r--src/hurd_pci.c496
-rw-r--r--src/pciaccess_private.h9
-rw-r--r--src/x86_pci.c364
-rw-r--r--src/x86_pci.h88
6 files changed, 813 insertions, 152 deletions
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 <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <sys/mman.h>
+#include <string.h>
+#include <strings.h>
+#include <hurd.h>
+#include <hurd/pci.h>
+#include <hurd/paths.h>
+
+#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 *)&regions;
+ 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*)&regions != buf)
+ {
+ /* Sanity check for bogus server. */
+ if(size > sizeof(regions))
+ {
+ vm_deallocate(mach_task_self(), (vm_address_t) buf, size);
+ return EGRATUITOUS;
+ }
+
+ memcpy(&regions, 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*)&reg,
+ &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*)&reg,
+ &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*)&reg, &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 <idr@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)
@@ -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 <unistd.h>
#include <stdio.h>
#include <stdlib.h>
@@ -38,7 +42,7 @@
#include <sys/io.h>
-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, &reg, 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, &reg, 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, &reg, 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, &reg, 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, &reg, 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, &reg, 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, &reg, PCI_CLASS))
+ continue;
+ device->base.device_class = (reg >> 8) & 0xFF;
+ device->base.revision = reg & 0xFF;
+
+ if (pci_device_cfg_read_u32(&tmpdev, &reg, 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 */