libpciaccess: Changes to 'upstream-unstable'
COPYING | 31 --
Makefile.am | 2
configure.ac | 8
src/Makefile.am | 10
src/common_capability.c | 12
src/common_init.c | 2
src/common_interface.c | 4
src/common_vgaarb.c | 10
src/pci_tools.h | 44 +--
src/pciaccess_private.h | 1
src/solx_devfs.c | 29 --
src/x86_pci.c | 669 ++++++++++++++++++++++++++++++++++++++++++++++++
12 files changed, 723 insertions(+), 99 deletions(-)
New commits:
commit fa3f1c1ea8ce4b45976e11520856cfa164f8b92d
Author: Dave Airlie <airlied@redhat.com>
Date: Wed Jul 21 09:39:26 2010 +1000
libpciaccess 0.12.0 - bump version for release
diff --git a/configure.ac b/configure.ac
index e5d962f..bba0cff 100644
--- a/configure.ac
+++ b/configure.ac
@@ -24,7 +24,7 @@ dnl Process this file with autoconf to create configure.
AC_PREREQ([2.60])
-AC_INIT(libpciaccess, 0.11.0, [https://bugs.freedesktop.org/enter_bug.cgi?product=xorg&component=libpciaccess], libpciaccess)
+AC_INIT(libpciaccess, 0.12.0, [https://bugs.freedesktop.org/enter_bug.cgi?product=xorg&component=libpciaccess], libpciaccess)
AM_INIT_AUTOMAKE([foreign dist-bzip2])
AM_MAINTAINER_MODE
commit 2f98724d1e603268360d704b3c8823b93d5fecb4
Author: Thomas Hellstrom <thellstrom@vmware.com>
Date: Tue Jul 20 22:14:22 2010 +0200
Fix long standing MTRR bug.
Add an include that makes the file common_vgaarb.c see the same
struct pci_system as the file linux_sysfs.c.
Without this fix, on a MTRR system, the vgaarb_fd member
would overwrite the mtrr_fd member and cause the MTRR_IOC_ADD_ENTRY call
to be issued to the incorrect device causing the infamous
"Inappropriate ioctl for device (25)" error.
This error would cause MTRR setup to fail on all systems relying on it
and is severe enough to warrant a new release.
Signed-off-by: Thomas Hellstrom <thellstrom@vmware.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
diff --git a/src/common_vgaarb.c b/src/common_vgaarb.c
index c94b362..86eceb5 100644
--- a/src/common_vgaarb.c
+++ b/src/common_vgaarb.c
@@ -33,6 +33,7 @@
#include <stdlib.h>
#include <limits.h>
+#include "config.h"
#include "pciaccess.h"
#include "pciaccess_private.h"
commit e5159771bd0a013201dcef760e9320071c6df59e
Author: Dave Airlie <airlied@redhat.com>
Date: Mon Jul 12 13:09:33 2010 +1000
pciaccess: fix use after free.
Using the pointer passed to realloc earlier isn't a recipe for success.
Signed-off-by: Dave Airlie <airlied@redhat.com>
diff --git a/src/common_interface.c b/src/common_interface.c
index d46feab..4af772a 100644
--- a/src/common_interface.c
+++ b/src/common_interface.c
@@ -317,8 +317,8 @@ pci_device_map_range(struct pci_device *dev, pciaddr_t base,
*addr = mappings[devp->num_mappings].memory;
devp->num_mappings++;
} else {
- mappings = realloc(devp->mappings,
- (sizeof(devp->mappings[0]) * devp->num_mappings));
+ mappings = realloc(mappings,
+ (sizeof(mappings[0]) * devp->num_mappings));
}
devp->mappings = mappings;
commit 3f59728ddc6f7d2cb17d434e81ac76c9288d0aef
Author: Alan Coopersmith <alan.coopersmith@oracle.com>
Date: Sat Jun 19 10:36:24 2010 -0700
Let libtool handle library flags for scanpci on Solaris
Stop hardcoding -ldevinfo since configure/libtool already know to add it
for static linking, and ELF dependencies handle it for dynamic linking
Signed-off-by: Alan Coopersmith <alan.coopersmith@oracle.com>
diff --git a/src/Makefile.am b/src/Makefile.am
index 8afd53b..0ee8449 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -69,7 +69,7 @@ libpciaccess_la_SOURCES = common_bridge.c \
INCLUDES = -I$(top_srcdir)/include
-libpciaccess_la_LIBADD = @PCIACCESS_LIBS@
+libpciaccess_la_LIBADD = $(PCIACCESS_LIBS)
libpciaccess_la_LDFLAGS = -version-number 0:10:8 -no-undefined
@@ -80,8 +80,4 @@ libpciaccessinclude_HEADERS = \
noinst_PROGRAMS = scanpci
scanpci_SOURCES = scanpci.c
-if SOLARIS
-scanpci_LDADD = libpciaccess.la -ldevinfo
-else
scanpci_LDADD = libpciaccess.la
-endif
commit e0ae5251cd030b201c14f5888a539aa40f17abfb
Author: Tiago Vignatti <tiago.vignatti@nokia.com>
Date: Sat May 22 18:23:33 2010 +0300
vgaarb: read back vga count when setting new decoding
Decode kernel operation can also change vga refcount, so we need to read back
the information when calling it.
Signed-off-by: Tiago Vignatti <tiago.vignatti@nokia.com>
diff --git a/src/common_vgaarb.c b/src/common_vgaarb.c
index f09d513..c94b362 100644
--- a/src/common_vgaarb.c
+++ b/src/common_vgaarb.c
@@ -263,6 +263,13 @@ pci_device_vgaarb_decodes(int new_vgaarb_rsrc)
ret = vgaarb_write(pci_sys->vgaarb_fd, buf, len);
if (ret == 0)
dev->vgaarb_rsrc = new_vgaarb_rsrc;
+
+ ret = read(pci_sys->vgaarb_fd, buf, BUFSIZE);
+ if (ret <= 0)
+ return -1;
+
+ parse_string_to_decodes_rsrc(buf, &pci_sys->vga_count, NULL);
+
return ret;
}
commit 0f153de858032c95ea5844aa6e0f1c8dad7531da
Author: Tiago Vignatti <tiago.vignatti@nokia.com>
Date: Wed May 12 13:12:47 2010 +0300
vgaarb: decode should send new information to the kernel
This was introduced in e360c11c.
Signed-off-by: Tiago Vignatti <tiago.vignatti@nokia.com>
diff --git a/src/common_vgaarb.c b/src/common_vgaarb.c
index a96aedf..f09d513 100644
--- a/src/common_vgaarb.c
+++ b/src/common_vgaarb.c
@@ -259,7 +259,7 @@ pci_device_vgaarb_decodes(int new_vgaarb_rsrc)
if (dev->vgaarb_rsrc == new_vgaarb_rsrc)
return 0;
- len = snprintf(buf, BUFSIZE, "decodes %s", rsrc_to_str(dev->vgaarb_rsrc));
+ len = snprintf(buf, BUFSIZE, "decodes %s", rsrc_to_str(new_vgaarb_rsrc));
ret = vgaarb_write(pci_sys->vgaarb_fd, buf, len);
if (ret == 0)
dev->vgaarb_rsrc = new_vgaarb_rsrc;
commit fa7cca617583eb93a862c5ebbb5a56843210e5a8
Author: Alan Coopersmith <alan.coopersmith@oracle.com>
Date: Fri Jun 4 16:58:56 2010 -0700
Delay allocation of agp_info so we don't leak it on prior errors
Memory leak of pointer 'agp_info'
at line 119 of src/common_capability.c in function 'pci_fill_capabilities_generic'.
'agp_info' allocated at line 107 with calloc(1, 12).
'agp_info' leaks when err != 0 at line 118.
at line 124 of src/common_capability.c in function 'pci_fill_capabilities_generic'.
'agp_info' allocated at line 107 with calloc(1, 12).
'agp_info' leaks when err != 0 at line 123.
[ This bug was found by the Parfait bug checking tool.
For more information see http://research.sun.com/projects/parfait ]
Signed-off-by: Alan Coopersmith <alan.coopersmith@oracle.com>
diff --git a/src/common_capability.c b/src/common_capability.c
index 31d59eb..3963db1 100644
--- a/src/common_capability.c
+++ b/src/common_capability.c
@@ -104,16 +104,11 @@ pci_fill_capabilities_generic( struct pci_device * dev )
switch ( cap_id ) {
case 2: {
- struct pci_agp_info * agp_info = calloc( 1, sizeof( struct pci_agp_info ) );
+ struct pci_agp_info * agp_info;
uint32_t agp_status;
uint8_t agp_ver;
- if ( agp_info == NULL ) {
- return ENOMEM;
- }
-
-
err = pci_device_cfg_read_u8( dev, & agp_ver, cap_offset + 2 );
if ( err ) {
return err;
@@ -124,6 +119,11 @@ pci_fill_capabilities_generic( struct pci_device * dev )
return err;
}
+ agp_info = calloc( 1, sizeof( struct pci_agp_info ) );
+ if ( agp_info == NULL ) {
+ return ENOMEM;
+ }
+
agp_info->config_offset = cap_offset;
agp_info->major_version = (agp_ver & 0x0f0) >> 4;
commit b8295f4776912d4c3cef836eb0f158e07b432a25
Author: Gaetan Nadon <memsize@videotron.ca>
Date: Mon Mar 29 16:50:34 2010 -0400
config: update AC_PREREQ statement to 2.60
Unrelated to the previous patches, the new value simply reflects
the reality that the minimum level for autoconf to configure
all x.org modules is 2.60 dated June 2006.
ftp://ftp.gnu.org/gnu/autoconf/autoconf-2.60.tar.gz
Signed-off-by: Gaetan Nadon <memsize@videotron.ca>
diff --git a/configure.ac b/configure.ac
index 535d704..e5d962f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -22,7 +22,7 @@ dnl DEALINGS IN THE SOFTWARE.
dnl
dnl Process this file with autoconf to create configure.
-AC_PREREQ([2.57])
+AC_PREREQ([2.60])
AC_INIT(libpciaccess, 0.11.0, [https://bugs.freedesktop.org/enter_bug.cgi?product=xorg&component=libpciaccess], libpciaccess)
AM_INIT_AUTOMAKE([foreign dist-bzip2])
commit a624de7af1d4c0956ecd485e3ae193f93bd78c57
Author: Gaetan Nadon <memsize@videotron.ca>
Date: Mon Mar 29 14:53:49 2010 -0400
config: remove the pkgconfig pc.in file from EXTRA_DIST
Automake always includes it in the tarball.
Signed-off-by: Gaetan Nadon <memsize@videotron.ca>
diff --git a/Makefile.am b/Makefile.am
index ced72dc..ff8db2f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -30,7 +30,7 @@ pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = pciaccess.pc
MAINTAINERCLEANFILES = ChangeLog INSTALL
-EXTRA_DIST = pciaccess.pc.in src/scanpci.c src/scanpci.man
+EXTRA_DIST = src/scanpci.c src/scanpci.man
.PHONY: ChangeLog INSTALL
commit af2be74979aeab9a2fc4c933462e97ce70f816b6
Author: Samuel Thibault <samuel.thibault@ens-lyon.org>
Date: Tue Jan 19 18:37:43 2010 +0100
libpciaccess x86 backend
This adds support on x86 for OSes that do not have a PCI interface,
tinkering with I/O ports, and makes use of it on GNU/Hurd.
Signed-off-by: Julien Cristau <jcristau@debian.org>
diff --git a/configure.ac b/configure.ac
index ffc1c8d..535d704 100644
--- a/configure.ac
+++ b/configure.ac
@@ -90,6 +90,9 @@ case $host_os in
solaris=yes
PCIACCESS_LIBS="$PCIACCESS_LIBS -ldevinfo"
;;
+ gnu*)
+ gnu=yes
+ ;;
esac
AM_CONDITIONAL(LINUX, [test "x$linux" = xyes])
@@ -97,6 +100,7 @@ AM_CONDITIONAL(FREEBSD, [test "x$freebsd" = xyes])
AM_CONDITIONAL(NETBSD, [test "x$netbsd" = xyes])
AM_CONDITIONAL(OPENBSD, [test "x$openbsd" = xyes])
AM_CONDITIONAL(SOLARIS, [test "x$solaris" = xyes])
+AM_CONDITIONAL(GNU, [test "x$gnu" = xyes])
AC_SYS_LARGEFILE
diff --git a/src/Makefile.am b/src/Makefile.am
index 4c06c25..8afd53b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -51,6 +51,10 @@ else
VGA_ARBITER = common_vgaarb_stub.c
endif
+if GNU
+OS_SUPPORT = x86_pci.c
+endif
+
libpciaccess_la_SOURCES = common_bridge.c \
common_iterator.c \
common_init.c \
diff --git a/src/common_init.c b/src/common_init.c
index 6b9b936..89c56ad 100644
--- a/src/common_init.c
+++ b/src/common_init.c
@@ -62,6 +62,8 @@ pci_system_init( void )
err = pci_system_openbsd_create();
#elif defined(__sun)
err = pci_system_solx_devfs_create();
+#elif defined(__GNU__)
+ err = pci_system_x86_create();
#endif
return err;
diff --git a/src/pciaccess_private.h b/src/pciaccess_private.h
index 77eb57b..1111ef0 100644
--- a/src/pciaccess_private.h
+++ b/src/pciaccess_private.h
@@ -167,4 +167,5 @@ 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_x86_create( void );
extern void pci_io_cleanup( void );
diff --git a/src/x86_pci.c b/src/x86_pci.c
new file mode 100644
index 0000000..c42d3e0
--- /dev/null
+++ b/src/x86_pci.c
@@ -0,0 +1,669 @@
+/*
+ * Copyright (c) 2009 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 "pciaccess.h"
+#include "pciaccess_private.h"
+
+#if defined(__GNU__)
+
+#include <sys/io.h>
+
+static int
+x86_enable_io(void)
+{
+ if (!ioperm(0, 0xffff, 1))
+ return 0;
+ return errno;
+}
+
+static int
+x86_disable_io(void)
+{
+ if (!ioperm(0, 0xffff, 0))
+ return 0;
+ return errno;
+}
+
+#elif defined(__GLIBC__)
+
+#include <sys/io.h>
+
+static int
+x86_enable_io(void)
+{
+ if (!iopl(3))
+ return 0;
+ return errno;
+}
+
+static int
+x86_disable_io(void)
+{
+ if (!iopl(0))
+ return 0;
+ return errno;
+}
+
+#else
+
+#error How to enable IO ports on this system?
+
+#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)
+{
+ unsigned long sav;
+ int res = ENODEV;
+
+ outb(0x01, 0xCFB);
+ sav = inl(0xCF8);
+ outl(0x80000000, 0xCF8);
+ if (inl(0xCF8) == 0x80000000)
+ res = 0;
+ outl(sav, 0xCF8);
+
+ return res;
+}
+
+static int
+pci_system_x86_conf1_read(unsigned bus, unsigned dev, unsigned func, pciaddr_t reg, void *data, unsigned size)
+{
+ unsigned addr = 0xCFC + (reg & 3);
+ unsigned long sav;
+ int ret = 0;
+
+ if (bus >= 0x100 || dev >= 32 || func >= 8 || reg >= 0x100 || size > 4 || size == 3)
+ return EIO;
+
+ sav = inl(0xCF8);
+ outl(0x80000000 | (bus << 16) | (dev << 11) | (func << 8) | (reg & ~3), 0xCF8);
+ /* NOTE: x86 is already LE */
+ switch (size) {
+ case 1: {
+ uint8_t *val = data;
+ *val = inb(addr);
+ break;
+ }
+ case 2: {
+ uint16_t *val = data;
+ *val = inw(addr);
+ break;
+ }
+ case 4: {
+ uint32_t *val = data;
+ *val = inl(addr);
+ break;
+ }
+ }
+ outl(sav, 0xCF8);
+
+ return ret;
+}
+
+static int
+pci_system_x86_conf1_write(unsigned bus, unsigned dev, unsigned func, pciaddr_t reg, const void *data, unsigned size)
+{
+ unsigned addr = 0xCFC + (reg & 3);
+ unsigned long sav;
+ int ret = 0;
+
+ if (bus >= 0x100 || dev >= 32 || func >= 8 || reg >= 0x100 || size > 4 || size == 3)
+ return EIO;
+
+ sav = inl(0xCF8);
+ outl(0x80000000 | (bus << 16) | (dev << 11) | (func << 8) | (reg & ~3), 0xCF8);
+ /* NOTE: x86 is already LE */
+ switch (size) {
+ case 1: {
+ const uint8_t *val = data;
+ outb(*val, addr);
+ break;
+ }
+ case 2: {
+ const uint16_t *val = data;
+ outw(*val, addr);
+ break;
+ }
+ case 4: {
+ const uint32_t *val = data;
+ outl(*val, addr);
+ break;
+ }
+ }
+ outl(sav, 0xCF8);
+
+ return ret;
+}
+
+static int
+pci_system_x86_conf2_probe(void)
+{
+ outb(0, 0xCFB);
+ outb(0, 0xCF8);
+ outb(0, 0xCFA);
+ if (inb(0xCF8) == 0 && inb(0xCFA) == 0)
+ return 0;
+
+ return ENODEV;
+}
+
+static int
+pci_system_x86_conf2_read(unsigned bus, unsigned dev, unsigned func, pciaddr_t reg, void *data, unsigned size)
+{
+ unsigned addr = 0xC000 | dev << 8 | reg;
+ int ret = 0;
+
+ if (bus >= 0x100 || dev >= 16 || func >= 8 || reg >= 0x100)
+ return EIO;
+
+ outb((func << 1) | 0xF0, 0xCF8);
+ outb(bus, 0xCFA);
+ /* NOTE: x86 is already LE */
+ switch (size) {
+ case 1: {
+ uint8_t *val = data;
+ *val = inb(addr);
+ break;
+ }
+ case 2: {
+ uint16_t *val = data;
+ *val = inw(addr);
+ break;
+ }
+ case 4: {
+ uint32_t *val = data;
+ *val = inl(addr);
+ break;
+ }
+ default:
+ ret = EIO;
+ break;
+ }
+ outb(0, 0xCF8);
+
+ return ret;
+}
+
+static int
+pci_system_x86_conf2_write(unsigned bus, unsigned dev, unsigned func, pciaddr_t reg, const void *data, unsigned size)
+{
+ unsigned addr = 0xC000 | dev << 8 | reg;
+ int ret = 0;
+
+ if (bus >= 0x100 || dev >= 16 || func >= 8 || reg >= 0x100)
+ return EIO;
+
+ outb((func << 1) | 0xF0, 0xCF8);
+ outb(bus, 0xCFA);
+ /* NOTE: x86 is already LE */
+ switch (size) {
+ case 1: {
+ const uint8_t *val = data;
+ outb(*val, addr);
+ break;
+ }
+ case 2: {
+ const uint16_t *val = data;
+ outw(*val, addr);
+ break;
+ }
+ case 4: {
+ const uint32_t *val = data;
+ outl(*val, addr);
+ break;
+ }
+ default:
+ ret = EIO;
+ break;
+ }
+ outb(0, 0xCF8);
+
+ return ret;
+}
+
+/* Check that this really looks like a PCI configuration. */
+static int
+pci_system_x86_check(struct pci_system_x86 *pci_sys_x86)
+{
+ int dev;
+ uint16_t class, vendor;
+
+ /* Look on bus 0 for a device that is a host bridge, a VGA card,
+ * or an intel or compaq device. */
+
+ 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;
+ }
+
+ return ENODEV;
+}
+
+static int
+pci_nfuncs(struct pci_system_x86 *pci_sys_x86, int bus, int dev)
+{
+ uint8_t hdr;
+ int err;
+
+ err = pci_sys_x86->read(bus, dev, 0, PCI_HDRTYPE, &hdr, sizeof(hdr));
+
+ if (err)
+ return err;
+
+ return hdr & 0x80 ? 8 : 1;
+}
+
+/**
+ * Read a VGA rom using the 0xc0000 mapping.
+ */
+static int
+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;
+ }
+
+ memfd = open("/dev/mem", O_RDONLY);
+ if (memfd == -1)
+ return errno;
+
+ bios = mmap(NULL, dev->rom_size, PROT_READ, 0, memfd, 0xc0000);
+ if (bios == MAP_FAILED) {
+ close(memfd);
+ return errno;
+ }
+
+ memcpy(buffer, bios, dev->rom_size);
+
+ munmap(bios, dev->rom_size);
+ close(memfd);
+
+ return 0;
+}
+
+/** Returns the number of regions (base address registers) the device has */
+static int
+pci_device_x86_get_num_regions(uint8_t header_type)
+{
+ switch (header_type & 0x7f) {
+ case 0:
+ return 6;
+ case 1:
+ return 2;
+ case 2:
+ return 1;
+ default:
+ fprintf(stderr,"unknown header type %02x\n", header_type);
+ return 0;
+ }
+}
+
+/** Masks out the flag bigs of the base address register value */
+static uint32_t
+get_map_base( uint32_t val )
+{
+ if (val & 0x01)
+ return val & ~0x03;
+ else
+ return val & ~0x0f;
+}
+
+/** Returns the size of a region based on the all-ones test value */
+static unsigned
+get_test_val_size( uint32_t testval )
+{
+ unsigned size = 1;
+
+ if (testval == 0)
+ return 0;
+
+ /* Mask out the flag bits */
+ testval = get_map_base( testval );
+ if (!testval)
+ return 0;
+
+ while ((testval & 1) == 0) {
+ size <<= 1;
+ testval >>= 1;
+ }
+
+ return size;
+}
+
+static int
+pci_device_x86_probe(struct pci_device *dev)
+{
+ uint8_t irq, hdrtype;
+ int err, i, bar;
+
+ /* 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;
+
+ err = pci_device_cfg_read_u8(dev, &hdrtype, PCI_HDRTYPE);
+ 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 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)))
+ {
+ dev->rom_size = 64 * 1024;
+ }
+
+ return 0;
+}
+
+static int
+pci_device_x86_map_range(struct pci_device *dev,
+ struct pci_device_mapping *map)
+{
+ int memfd = open("/dev/mem", O_RDWR);
+ int prot = PROT_READ;
+
+ if (memfd == -1)
+ return errno;
+
+ if (map->flags & PCI_DEV_MAP_FLAG_WRITABLE)
+ prot |= PROT_WRITE;
+
+ map->memory = mmap(NULL, map->size, prot, MAP_SHARED, memfd, map->base);
+ close(memfd);
+ if (map->memory == MAP_FAILED)
+ return errno;
+
+ return 0;
+}
+
+static int
+pci_device_x86_read(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;
+ while (size > 0) {
+ int toread = 1 << (ffs(0x4 + (offset & 0x03)) - 1);
+ if (toread > size)
+ toread = size;
+
+ err = pci_sys_x86->read(dev->bus, dev->dev, dev->func, offset, data, toread);
+ if (err)
+ return err;
+
+ offset += toread;
+ data = (char*)data + toread;
+ size -= toread;
+ *bytes_read += toread;
+ }
+ return 0;
+}
+
+static int
+pci_device_x86_write(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;
+ while (size > 0) {
+ int towrite = 4;
+ if (towrite > size)
+ towrite = size;
+ if (towrite > 4 - (offset & 0x3))
+ towrite = 4 - (offset & 0x3);
+
+ err = pci_sys_x86->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 void
+pci_system_x86_destroy(void)
+{
+ x86_disable_io();
+}
+
+static const struct pci_system_methods x86_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_generic_unmap_range,
+ .read = pci_device_x86_read,
+ .write = pci_device_x86_write,
+ .fill_capabilities = pci_fill_capabilities_generic,
+};
+
+static int pci_probe(struct pci_system_x86 *pci_sys_x86)
+{
+ 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_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;
+ }
+
+ return ENODEV;
+}
+
+_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;
+
+ 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;
+ }
+
+ 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->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;
+ }
+
Reply to: