From a7faac3d2dc49a2130906fbeea3298f32bb9349f Mon Sep 17 00:00:00 2001 From: Ian Romanick Date: Mon, 18 Jun 2007 12:56:40 -0700 Subject: [PATCH] Initial support for reading expansion ROM via VGA BIOS address. --- src/Makefile.am | 2 +- src/linux_devmem.c | 155 ++++++++++++++++++++++++++++++++++++++++++++++++ src/linux_devmem.h | 34 +++++++++++ src/linux_sysfs.c | 7 ++- src/pciaccess_private.h | 5 ++ 5 files changed, 201 insertions(+), 2 deletions(-) create mode 100644 src/linux_devmem.c create mode 100644 src/linux_devmem.h diff --git a/src/Makefile.am b/src/Makefile.am index c6554ba..760711c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -26,7 +26,7 @@ AM_CFLAGS = @PCIACCESS_CFLAGS@ lib_LTLIBRARIES = libpciaccess.la if LINUX -OS_SUPPORT = linux_sysfs.c +OS_SUPPORT = linux_sysfs.c linux_devmem.c endif if FREEBSD diff --git a/src/linux_devmem.c b/src/linux_devmem.c new file mode 100644 index 0000000..8650efd --- /dev/null +++ b/src/linux_devmem.c @@ -0,0 +1,155 @@ +/* + * (C) Copyright IBM Corporation 2007 + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * on the rights to use, copy, modify, merge, publish, distribute, sub + * license, and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +/** + * \file linux_devmem.c + * Access PCI subsystem using Linux's the old /dev/mem interface. + * + * \note + * This is currently just a skeleton. It only includes the /dev/mem based + * function for reading the device ROM. + * + * \author Ian Romanick + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pciaccess.h" +#include "pciaccess_private.h" +#include "linux_devmem.h" + +/** + * Read a device's expansion ROM using /dev/mem. + * + * \note + * This function could probably be used, as-is, on other platforms that have + * a /dev/mem interface. + * + * \bugs + * Before using the VGA special case code, this function should check that + * VGA access are routed to the device. Right? + */ +int pci_device_linux_devmem_read_rom(struct pci_device *dev, void *buffer) +{ + struct pci_device_private *priv = (struct pci_device_private *) dev; + int fd; + int err = 0; + uint32_t rom_base_tmp; + pciaddr_t rom_base; + pciaddr_t rom_size; + int PCI_ROM; + + + /* Handle some special cases of legacy devices. + */ + if (priv->base.rom_size == 0) { + /* VGA ROMs are supposed to be at 0xC0000. + */ + if ((priv->base.device_class & 0x00ffff00) == 0x000030000) { + rom_base = 0x000C0000; + rom_size = 0x00010000; + PCI_ROM = 0; + } + else { + /* "Function not implemented." + */ + return ENOSYS; + } + } + else { + rom_base = priv->rom_base; + rom_size = priv->base.rom_size; + PCI_ROM = 1; + } + + + + /* Enable the device's ROM. + */ + if (PCI_ROM) { + err = pci_device_cfg_read_u32(& priv->base, & rom_base_tmp, 48); + if (err) { + return err; + } + + if ((rom_base_tmp & 0x000000001) == 0) { + err = pci_device_cfg_write_u32(& priv->base, + rom_base_tmp | 1, 48); + if (err) { + return err; + } + } + } + + + /* Read the portion of /dev/mem that corresponds to the device's ROM. + */ + fd = open("/dev/mem", O_RDONLY, 0); + if (fd < 0) { + err = errno; + } + else { + size_t bytes; + + for (bytes = 0; bytes < priv->base.rom_size; /* empty */) { + const ssize_t got = pread(fd, buffer, rom_size - bytes, + rom_base + bytes); + if (got == -1) { + err = errno; + break; + } + + bytes += got; + } + + close(fd); + } + + + /* Disable the device's ROM. + */ + if (PCI_ROM && ((rom_base_tmp & 0x000000001) == 0)) { + const int tmp_err = pci_device_cfg_write_u32(& priv->base, + rom_base_tmp, 48); + + /* Prefer to return the first error that occured. + */ + if (err == 0) { + err = tmp_err; + } + } + + return err; +} diff --git a/src/linux_devmem.h b/src/linux_devmem.h new file mode 100644 index 0000000..2337f30 --- /dev/null +++ b/src/linux_devmem.h @@ -0,0 +1,34 @@ +/* + * (C) Copyright IBM Corporation 2007 + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * on the rights to use, copy, modify, merge, publish, distribute, sub + * license, and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +/** + * \file linux_devmem.h + * Functions and datastructures that are private to the /dev/mem based + * back-end for pciaccess. + * + * \author Ian Romanick + */ + +extern int pci_device_linux_devmem_read_rom(struct pci_device *dev, + void *buffer); diff --git a/src/linux_sysfs.c b/src/linux_sysfs.c index 8a4bd0f..ba5dce2 100644 --- a/src/linux_sysfs.c +++ b/src/linux_sysfs.c @@ -46,6 +46,7 @@ #include "pciaccess.h" #include "pciaccess_private.h" +#include "linux_devmem.h" static int pci_device_linux_sysfs_read_rom( struct pci_device * dev, void * buffer ); @@ -278,6 +279,7 @@ pci_device_linux_sysfs_probe( struct pci_device * dev ) high_addr = strtoull( next, & next, 16 ); flags = strtoull( next, & next, 16 ); if ( low_addr != 0 ) { + priv->rom_base = low_addr; dev->rom_size = (high_addr - low_addr) + 1; } } @@ -306,7 +308,10 @@ pci_device_linux_sysfs_read_rom( struct pci_device * dev, void * buffer ) fd = open( name, O_RDWR ); if ( fd == -1 ) { - return errno; + /* If reading the ROM using sysfs fails, fall back to the old + * /dev/mem based interface. + */ + return pci_device_linux_devmem_read_rom(dev, buffer); } diff --git a/src/pciaccess_private.h b/src/pciaccess_private.h index b23e589..afbeb75 100644 --- a/src/pciaccess_private.h +++ b/src/pciaccess_private.h @@ -63,6 +63,11 @@ struct pci_device_private { /*@}*/ /** + * Base address of the device's expansion ROM. + */ + pciaddr_t rom_base; + + /** * \name Bridge information. */ /*@{*/ -- 2.11.0