xref: /libpciaccess/src/linux_sysfs.c (revision 9b77a21e)
15a04522aSIan Romanick /*
25a04522aSIan Romanick  * (C) Copyright IBM Corporation 2006
35a04522aSIan Romanick  * All Rights Reserved.
45a04522aSIan Romanick  *
55a04522aSIan Romanick  * Permission is hereby granted, free of charge, to any person obtaining a
65a04522aSIan Romanick  * copy of this software and associated documentation files (the "Software"),
75a04522aSIan Romanick  * to deal in the Software without restriction, including without limitation
85a04522aSIan Romanick  * on the rights to use, copy, modify, merge, publish, distribute, sub
95a04522aSIan Romanick  * license, and/or sell copies of the Software, and to permit persons to whom
105a04522aSIan Romanick  * the Software is furnished to do so, subject to the following conditions:
115a04522aSIan Romanick  *
125a04522aSIan Romanick  * The above copyright notice and this permission notice (including the next
135a04522aSIan Romanick  * paragraph) shall be included in all copies or substantial portions of the
145a04522aSIan Romanick  * Software.
155a04522aSIan Romanick  *
165a04522aSIan Romanick  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
175a04522aSIan Romanick  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
185a04522aSIan Romanick  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
195a04522aSIan Romanick  * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
205a04522aSIan Romanick  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
215a04522aSIan Romanick  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
225a04522aSIan Romanick  * DEALINGS IN THE SOFTWARE.
235a04522aSIan Romanick  */
245a04522aSIan Romanick 
255a04522aSIan Romanick /**
265a04522aSIan Romanick  * \file linux_sysfs.c
275a04522aSIan Romanick  * Access PCI subsystem using Linux's sysfs interface.  This interface is
285a04522aSIan Romanick  * available starting somewhere in the late 2.5.x kernel phase, and is the
29d43d21c8SAlan Coopersmith  * preferred method on all 2.6.x kernels.
305a04522aSIan Romanick  *
315a04522aSIan Romanick  * \author Ian Romanick <[email protected]>
325a04522aSIan Romanick  */
335a04522aSIan Romanick 
345a04522aSIan Romanick #define _GNU_SOURCE
355a04522aSIan Romanick 
365a04522aSIan Romanick #include <stdlib.h>
375a04522aSIan Romanick #include <string.h>
385a04522aSIan Romanick #include <stdio.h>
395a04522aSIan Romanick #include <unistd.h>
405a04522aSIan Romanick #include <sys/types.h>
415a04522aSIan Romanick #include <sys/stat.h>
425a04522aSIan Romanick #include <fcntl.h>
435a04522aSIan Romanick #include <sys/mman.h>
445a04522aSIan Romanick #include <dirent.h>
455a04522aSIan Romanick #include <errno.h>
465a04522aSIan Romanick 
47*9b77a21eSChad Versace #ifndef ANDROID
4808ff9f7fSIan Romanick #include "config.h"
49*9b77a21eSChad Versace #endif
5008ff9f7fSIan Romanick 
5108ff9f7fSIan Romanick #ifdef HAVE_MTRR
5208ff9f7fSIan Romanick #include <asm/mtrr.h>
5308ff9f7fSIan Romanick #include <sys/ioctl.h>
5408ff9f7fSIan Romanick #endif
5508ff9f7fSIan Romanick 
565a04522aSIan Romanick #include "pciaccess.h"
575a04522aSIan Romanick #include "pciaccess_private.h"
58a7faac3dSIan Romanick #include "linux_devmem.h"
595a04522aSIan Romanick 
605e8d4c19SAdam Jackson static const struct pci_system_methods linux_sysfs_methods;
615a04522aSIan Romanick 
625a04522aSIan Romanick #define SYS_BUS_PCI "/sys/bus/pci/devices"
635a04522aSIan Romanick 
645e8d4c19SAdam Jackson static int
655e8d4c19SAdam Jackson pci_device_linux_sysfs_read( struct pci_device * dev, void * data,
665e8d4c19SAdam Jackson 			     pciaddr_t offset, pciaddr_t size,
675e8d4c19SAdam Jackson 			     pciaddr_t * bytes_read );
685a04522aSIan Romanick 
69061a011cSIan Romanick static int populate_entries(struct pci_system * pci_sys);
705a04522aSIan Romanick 
715a04522aSIan Romanick /**
725a04522aSIan Romanick  * Attempt to access PCI subsystem using Linux's sysfs interface.
735a04522aSIan Romanick  */
74adc46f65SJulien Cristau _pci_hidden int
pci_system_linux_sysfs_create(void)755a04522aSIan Romanick pci_system_linux_sysfs_create( void )
765a04522aSIan Romanick {
775a04522aSIan Romanick     int err = 0;
785a04522aSIan Romanick     struct stat st;
795a04522aSIan Romanick 
805a04522aSIan Romanick 
815a04522aSIan Romanick     /* If the directory "/sys/bus/pci/devices" exists, then the PCI subsystem
825a04522aSIan Romanick      * can be accessed using this interface.
835a04522aSIan Romanick      */
845a04522aSIan Romanick 
855a04522aSIan Romanick     if ( stat( SYS_BUS_PCI, & st ) == 0 ) {
865a04522aSIan Romanick 	pci_sys = calloc( 1, sizeof( struct pci_system ) );
875a04522aSIan Romanick 	if ( pci_sys != NULL ) {
885a04522aSIan Romanick 	    pci_sys->methods = & linux_sysfs_methods;
894586bb67SAdam Jackson #ifdef HAVE_MTRR
904586bb67SAdam Jackson 	    pci_sys->mtrr_fd = open("/proc/mtrr", O_WRONLY);
914586bb67SAdam Jackson #endif
92061a011cSIan Romanick 	    err = populate_entries(pci_sys);
935a04522aSIan Romanick 	}
945a04522aSIan Romanick 	else {
955a04522aSIan Romanick 	    err = ENOMEM;
965a04522aSIan Romanick 	}
975a04522aSIan Romanick     }
985a04522aSIan Romanick     else {
995a04522aSIan Romanick 	err = errno;
1005a04522aSIan Romanick     }
1015a04522aSIan Romanick 
1025a04522aSIan Romanick     return err;
1035a04522aSIan Romanick }
1045a04522aSIan Romanick 
1055a04522aSIan Romanick 
1065a04522aSIan Romanick /**
1075a04522aSIan Romanick  * Filter out the names "." and ".." from the scanned sysfs entries.
1085a04522aSIan Romanick  *
1095a04522aSIan Romanick  * \param d  Directory entry being processed by \c scandir.
1105a04522aSIan Romanick  *
1115a04522aSIan Romanick  * \return
1125a04522aSIan Romanick  * Zero if the entry name matches either "." or "..", non-zero otherwise.
1135a04522aSIan Romanick  *
1145a04522aSIan Romanick  * \sa scandir, populate_entries
1155a04522aSIan Romanick  */
1165a04522aSIan Romanick static int
scan_sys_pci_filter(const struct dirent * d)1175a04522aSIan Romanick scan_sys_pci_filter( const struct dirent * d )
1185a04522aSIan Romanick {
1195a04522aSIan Romanick     return !((strcmp( d->d_name, "." ) == 0)
1205a04522aSIan Romanick 	     || (strcmp( d->d_name, ".." ) == 0));
1215a04522aSIan Romanick }
1225a04522aSIan Romanick 
1235a04522aSIan Romanick 
124061a011cSIan Romanick int
populate_entries(struct pci_system * p)1255a04522aSIan Romanick populate_entries( struct pci_system * p )
1265a04522aSIan Romanick {
1277bfc4f80SAdam Jackson     struct dirent ** devices = NULL;
1285a04522aSIan Romanick     int n;
1295a04522aSIan Romanick     int i;
13008ff9f7fSIan Romanick     int err = 0;
1315a04522aSIan Romanick 
1325a04522aSIan Romanick 
1335a04522aSIan Romanick     n = scandir( SYS_BUS_PCI, & devices, scan_sys_pci_filter, alphasort );
1345a04522aSIan Romanick     if ( n > 0 ) {
1355a04522aSIan Romanick 	p->num_devices = n;
1365a04522aSIan Romanick 	p->devices = calloc( n, sizeof( struct pci_device_private ) );
1375a04522aSIan Romanick 
138061a011cSIan Romanick 	if (p->devices != NULL) {
1395a04522aSIan Romanick 	    for (i = 0 ; i < n ; i++) {
140061a011cSIan Romanick 		uint8_t config[48];
141061a011cSIan Romanick 		pciaddr_t bytes;
1425a04522aSIan Romanick 		unsigned dom, bus, dev, func;
143061a011cSIan Romanick 		struct pci_device_private *device =
144061a011cSIan Romanick 			(struct pci_device_private *) &p->devices[i];
1455a04522aSIan Romanick 
1465a04522aSIan Romanick 
1475a04522aSIan Romanick 		sscanf(devices[i]->d_name, "%04x:%02x:%02x.%1u",
1485a04522aSIan Romanick 		       & dom, & bus, & dev, & func);
1495a04522aSIan Romanick 
150061a011cSIan Romanick 		device->base.domain = dom;
151061a011cSIan Romanick 		device->base.bus = bus;
152061a011cSIan Romanick 		device->base.dev = dev;
153061a011cSIan Romanick 		device->base.func = func;
154061a011cSIan Romanick 
155061a011cSIan Romanick 
156061a011cSIan Romanick 		err = pci_device_linux_sysfs_read(& device->base, config, 0,
157061a011cSIan Romanick 						  48, & bytes);
158061a011cSIan Romanick 		if ((bytes == 48) && !err) {
159061a011cSIan Romanick 		    device->base.vendor_id = (uint16_t)config[0]
160061a011cSIan Romanick 			+ ((uint16_t)config[1] << 8);
161061a011cSIan Romanick 		    device->base.device_id = (uint16_t)config[2]
162061a011cSIan Romanick 			+ ((uint16_t)config[3] << 8);
163061a011cSIan Romanick 		    device->base.device_class = (uint32_t)config[9]
164061a011cSIan Romanick 			+ ((uint32_t)config[10] << 8)
165061a011cSIan Romanick 			+ ((uint32_t)config[11] << 16);
166061a011cSIan Romanick 		    device->base.revision = config[8];
167061a011cSIan Romanick 		    device->base.subvendor_id = (uint16_t)config[44]
168061a011cSIan Romanick 			+ ((uint16_t)config[45] << 8);
169061a011cSIan Romanick 		    device->base.subdevice_id = (uint16_t)config[46]
170061a011cSIan Romanick 			+ ((uint16_t)config[47] << 8);
171061a011cSIan Romanick 		}
172061a011cSIan Romanick 
173061a011cSIan Romanick 		if (err) {
174061a011cSIan Romanick 		    break;
1755a04522aSIan Romanick 		}
1765a04522aSIan Romanick 	    }
1775a04522aSIan Romanick 	}
178061a011cSIan Romanick 	else {
179061a011cSIan Romanick 	    err = ENOMEM;
180061a011cSIan Romanick 	}
181061a011cSIan Romanick     }
182061a011cSIan Romanick 
183cdbdfaf6SKeith Packard     for (i = 0; i < n; i++)
184cdbdfaf6SKeith Packard 	free(devices[i]);
185cdbdfaf6SKeith Packard     free(devices);
186cdbdfaf6SKeith Packard 
187061a011cSIan Romanick     if (err) {
188061a011cSIan Romanick 	free(p->devices);
189061a011cSIan Romanick 	p->devices = NULL;
190061a011cSIan Romanick     }
191061a011cSIan Romanick 
192061a011cSIan Romanick     return err;
193061a011cSIan Romanick }
1945a04522aSIan Romanick 
1955a04522aSIan Romanick 
1965a04522aSIan Romanick static int
pci_device_linux_sysfs_probe(struct pci_device * dev)1975a04522aSIan Romanick pci_device_linux_sysfs_probe( struct pci_device * dev )
1985a04522aSIan Romanick {
1995a04522aSIan Romanick     char     name[256];
2005a04522aSIan Romanick     uint8_t  config[256];
2015a04522aSIan Romanick     char     resource[512];
2025a04522aSIan Romanick     int fd;
2035a04522aSIan Romanick     pciaddr_t bytes;
2045a04522aSIan Romanick     unsigned i;
2055a04522aSIan Romanick     int err;
2065a04522aSIan Romanick 
2075a04522aSIan Romanick 
2085a04522aSIan Romanick     err = pci_device_linux_sysfs_read( dev, config, 0, 256, & bytes );
2095a04522aSIan Romanick     if ( bytes >= 64 ) {
2102ba1a0e4SIan Romanick 	struct pci_device_private *priv = (struct pci_device_private *) dev;
2112ba1a0e4SIan Romanick 
2125a04522aSIan Romanick 	dev->irq = config[60];
2132ba1a0e4SIan Romanick 	priv->header_type = config[14];
2142ba1a0e4SIan Romanick 
2155a04522aSIan Romanick 
2165a04522aSIan Romanick 	/* The PCI config registers can be used to obtain information
2175a04522aSIan Romanick 	 * about the memory and I/O regions for the device.  However,
2185a04522aSIan Romanick 	 * doing so requires some tricky parsing (to correctly handle
2195a04522aSIan Romanick 	 * 64-bit memory regions) and requires writing to the config
2205a04522aSIan Romanick 	 * registers.  Since we'd like to avoid having to deal with the
2215a04522aSIan Romanick 	 * parsing issues and non-root users can write to PCI config
2225a04522aSIan Romanick 	 * registers, we use a different file in the device's sysfs
2235a04522aSIan Romanick 	 * directory called "resource".
2245a04522aSIan Romanick 	 *
2255a04522aSIan Romanick 	 * The resource file contains all of the needed information in
2265a04522aSIan Romanick 	 * a format that is consistent across all platforms.  Each BAR
2275a04522aSIan Romanick 	 * and the expansion ROM have a single line of data containing
2285a04522aSIan Romanick 	 * 3, 64-bit hex values:  the first address in the region,
2295a04522aSIan Romanick 	 * the last address in the region, and the region's flags.
2305a04522aSIan Romanick 	 */
2315a04522aSIan Romanick 	snprintf( name, 255, "%s/%04x:%02x:%02x.%1u/resource",
2325a04522aSIan Romanick 		  SYS_BUS_PCI,
2335a04522aSIan Romanick 		  dev->domain,
2345a04522aSIan Romanick 		  dev->bus,
2355a04522aSIan Romanick 		  dev->dev,
2365a04522aSIan Romanick 		  dev->func );
2375a04522aSIan Romanick 	fd = open( name, O_RDONLY );
2385a04522aSIan Romanick 	if ( fd != -1 ) {
2395a04522aSIan Romanick 	    char * next;
2405a04522aSIan Romanick 	    pciaddr_t  low_addr;
2415a04522aSIan Romanick 	    pciaddr_t  high_addr;
2425a04522aSIan Romanick 	    pciaddr_t  flags;
2435a04522aSIan Romanick 
2445a04522aSIan Romanick 
2455a04522aSIan Romanick 	    bytes = read( fd, resource, 512 );
2465a04522aSIan Romanick 	    resource[511] = '\0';
2475a04522aSIan Romanick 
2485a04522aSIan Romanick 	    close( fd );
2495a04522aSIan Romanick 
2505a04522aSIan Romanick 	    next = resource;
2515a04522aSIan Romanick 	    for ( i = 0 ; i < 6 ; i++ ) {
2525a04522aSIan Romanick 
2535a04522aSIan Romanick 		dev->regions[i].base_addr = strtoull( next, & next, 16 );
2545a04522aSIan Romanick 		high_addr = strtoull( next, & next, 16 );
2555a04522aSIan Romanick 		flags = strtoull( next, & next, 16 );
2565a04522aSIan Romanick 
2575a04522aSIan Romanick 		if ( dev->regions[i].base_addr != 0 ) {
2585a04522aSIan Romanick 		    dev->regions[i].size = (high_addr
2595a04522aSIan Romanick 					    - dev->regions[i].base_addr) + 1;
2605a04522aSIan Romanick 
2615a04522aSIan Romanick 		    dev->regions[i].is_IO = (flags & 0x01);
2625a04522aSIan Romanick 		    dev->regions[i].is_64 = (flags & 0x04);
2635a04522aSIan Romanick 		    dev->regions[i].is_prefetchable = (flags & 0x08);
2645a04522aSIan Romanick 		}
2655a04522aSIan Romanick 	    }
2665a04522aSIan Romanick 
2675a04522aSIan Romanick 	    low_addr = strtoull( next, & next, 16 );
2685a04522aSIan Romanick 	    high_addr = strtoull( next, & next, 16 );
2695a04522aSIan Romanick 	    flags = strtoull( next, & next, 16 );
2705a04522aSIan Romanick 	    if ( low_addr != 0 ) {
271a7faac3dSIan Romanick 		priv->rom_base = low_addr;
2725a04522aSIan Romanick 		dev->rom_size = (high_addr - low_addr) + 1;
2735a04522aSIan Romanick 	    }
2745a04522aSIan Romanick 	}
2755a04522aSIan Romanick     }
2765a04522aSIan Romanick 
2775a04522aSIan Romanick     return err;
2785a04522aSIan Romanick }
2795a04522aSIan Romanick 
2805a04522aSIan Romanick 
2815a04522aSIan Romanick static int
pci_device_linux_sysfs_read_rom(struct pci_device * dev,void * buffer)2825a04522aSIan Romanick pci_device_linux_sysfs_read_rom( struct pci_device * dev, void * buffer )
2835a04522aSIan Romanick {
2845a04522aSIan Romanick     char name[256];
2855a04522aSIan Romanick     int fd;
2865a04522aSIan Romanick     struct stat  st;
2875a04522aSIan Romanick     int err = 0;
288a5c86202SStuart Bennett     size_t rom_size;
28964af050cSIan Romanick     size_t total_bytes;
2905a04522aSIan Romanick 
2915a04522aSIan Romanick 
2925a04522aSIan Romanick     snprintf( name, 255, "%s/%04x:%02x:%02x.%1u/rom",
2935a04522aSIan Romanick 	      SYS_BUS_PCI,
2945a04522aSIan Romanick 	      dev->domain,
2955a04522aSIan Romanick 	      dev->bus,
2965a04522aSIan Romanick 	      dev->dev,
2975a04522aSIan Romanick 	      dev->func );
2985a04522aSIan Romanick 
2995a04522aSIan Romanick     fd = open( name, O_RDWR );
3005a04522aSIan Romanick     if ( fd == -1 ) {
30157cf6f1fSDave Airlie #ifdef LINUX_ROM
302a7faac3dSIan Romanick 	/* If reading the ROM using sysfs fails, fall back to the old
303a7faac3dSIan Romanick 	 * /dev/mem based interface.
30457cf6f1fSDave Airlie 	 * disable this for newer kernels using configure
305a7faac3dSIan Romanick 	 */
306a7faac3dSIan Romanick 	return pci_device_linux_devmem_read_rom(dev, buffer);
30757cf6f1fSDave Airlie #else
30857cf6f1fSDave Airlie 	return errno;
30957cf6f1fSDave Airlie #endif
3105a04522aSIan Romanick     }
3115a04522aSIan Romanick 
3125a04522aSIan Romanick 
3135a04522aSIan Romanick     if ( fstat( fd, & st ) == -1 ) {
3145a04522aSIan Romanick 	close( fd );
3155a04522aSIan Romanick 	return errno;
3165a04522aSIan Romanick     }
3175a04522aSIan Romanick 
318a5c86202SStuart Bennett     rom_size = st.st_size;
319a5c86202SStuart Bennett     if ( rom_size == 0 )
320a5c86202SStuart Bennett 	rom_size = 0x10000;
3215a04522aSIan Romanick 
3225a04522aSIan Romanick     /* This is a quirky thing on Linux.  Even though the ROM and the file
3235a04522aSIan Romanick      * for the ROM in sysfs are read-only, the string "1" must be written to
3245a04522aSIan Romanick      * the file to enable the ROM.  After the data has been read, "0" must be
3255a04522aSIan Romanick      * written to the file to disable the ROM.
3265a04522aSIan Romanick      */
3275a04522aSIan Romanick     write( fd, "1", 1 );
3285a04522aSIan Romanick     lseek( fd, 0, SEEK_SET );
3295a04522aSIan Romanick 
330a5c86202SStuart Bennett     for ( total_bytes = 0 ; total_bytes < rom_size ; /* empty */ ) {
33164af050cSIan Romanick 	const int bytes = read( fd, (char *) buffer + total_bytes,
332a5c86202SStuart Bennett 				rom_size - total_bytes );
33364af050cSIan Romanick 	if ( bytes == -1 ) {
3345a04522aSIan Romanick 	    err = errno;
33564af050cSIan Romanick 	    break;
3365a04522aSIan Romanick 	}
33764af050cSIan Romanick 	else if ( bytes == 0 ) {
33864af050cSIan Romanick 	    break;
33964af050cSIan Romanick 	}
34064af050cSIan Romanick 
34164af050cSIan Romanick 	total_bytes += bytes;
34264af050cSIan Romanick     }
34364af050cSIan Romanick 
3445a04522aSIan Romanick 
3455a04522aSIan Romanick     lseek( fd, 0, SEEK_SET );
3465a04522aSIan Romanick     write( fd, "0", 1 );
3475a04522aSIan Romanick 
3485a04522aSIan Romanick     close( fd );
3495a04522aSIan Romanick     return err;
3505a04522aSIan Romanick }
3515a04522aSIan Romanick 
3525a04522aSIan Romanick 
3535a04522aSIan Romanick static int
pci_device_linux_sysfs_read(struct pci_device * dev,void * data,pciaddr_t offset,pciaddr_t size,pciaddr_t * bytes_read)3545a04522aSIan Romanick pci_device_linux_sysfs_read( struct pci_device * dev, void * data,
3555a04522aSIan Romanick 			     pciaddr_t offset, pciaddr_t size,
3565a04522aSIan Romanick 			     pciaddr_t * bytes_read )
3575a04522aSIan Romanick {
3585a04522aSIan Romanick     char name[256];
3595a04522aSIan Romanick     pciaddr_t temp_size = size;
3605a04522aSIan Romanick     int err = 0;
3615a04522aSIan Romanick     int fd;
3625cf29b06SKeith Packard     char *data_bytes = data;
3635a04522aSIan Romanick 
3645a04522aSIan Romanick     if ( bytes_read != NULL ) {
3655a04522aSIan Romanick 	*bytes_read = 0;
3665a04522aSIan Romanick     }
3675a04522aSIan Romanick 
3685a04522aSIan Romanick     /* Each device has a directory under sysfs.  Within that directory there
3695a04522aSIan Romanick      * is a file named "config".  This file used to access the PCI config
3705a04522aSIan Romanick      * space.  It is used here to obtain most of the information about the
3715a04522aSIan Romanick      * device.
3725a04522aSIan Romanick      */
3735a04522aSIan Romanick     snprintf( name, 255, "%s/%04x:%02x:%02x.%1u/config",
3745a04522aSIan Romanick 	      SYS_BUS_PCI,
3755a04522aSIan Romanick 	      dev->domain,
3765a04522aSIan Romanick 	      dev->bus,
3775a04522aSIan Romanick 	      dev->dev,
3785a04522aSIan Romanick 	      dev->func );
3795a04522aSIan Romanick 
3805a04522aSIan Romanick     fd = open( name, O_RDONLY );
3815a04522aSIan Romanick     if ( fd == -1 ) {
3825a04522aSIan Romanick 	return errno;
3835a04522aSIan Romanick     }
3845a04522aSIan Romanick 
3855a04522aSIan Romanick 
3865a04522aSIan Romanick     while ( temp_size > 0 ) {
3875cf29b06SKeith Packard 	const ssize_t bytes = pread64( fd, data_bytes, temp_size, offset );
3885a04522aSIan Romanick 
3895a04522aSIan Romanick 	/* If zero bytes were read, then we assume it's the end of the
3905a04522aSIan Romanick 	 * config file.
3915a04522aSIan Romanick 	 */
392f9159b97SChris Wilson 	if (bytes == 0)
393f9159b97SChris Wilson 	    break;
394f9159b97SChris Wilson 	if ( bytes < 0 ) {
3955a04522aSIan Romanick 	    err = errno;
3965a04522aSIan Romanick 	    break;
3975a04522aSIan Romanick 	}
3985a04522aSIan Romanick 
3995a04522aSIan Romanick 	temp_size -= bytes;
4005a04522aSIan Romanick 	offset += bytes;
4015cf29b06SKeith Packard 	data_bytes += bytes;
4025a04522aSIan Romanick     }
4035a04522aSIan Romanick 
4045a04522aSIan Romanick     if ( bytes_read != NULL ) {
4055a04522aSIan Romanick 	*bytes_read = size - temp_size;
4065a04522aSIan Romanick     }
4075a04522aSIan Romanick 
4085a04522aSIan Romanick     close( fd );
4095a04522aSIan Romanick     return err;
4105a04522aSIan Romanick }
4115a04522aSIan Romanick 
4125a04522aSIan Romanick 
4135a04522aSIan Romanick static int
pci_device_linux_sysfs_write(struct pci_device * dev,const void * data,pciaddr_t offset,pciaddr_t size,pciaddr_t * bytes_written)4145a04522aSIan Romanick pci_device_linux_sysfs_write( struct pci_device * dev, const void * data,
4155a04522aSIan Romanick 			     pciaddr_t offset, pciaddr_t size,
4165a04522aSIan Romanick 			     pciaddr_t * bytes_written )
4175a04522aSIan Romanick {
4185a04522aSIan Romanick     char name[256];
4195a04522aSIan Romanick     pciaddr_t temp_size = size;
4205a04522aSIan Romanick     int err = 0;
4215a04522aSIan Romanick     int fd;
4225cf29b06SKeith Packard     const char *data_bytes = data;
4235a04522aSIan Romanick 
4245a04522aSIan Romanick     if ( bytes_written != NULL ) {
4255a04522aSIan Romanick 	*bytes_written = 0;
4265a04522aSIan Romanick     }
4275a04522aSIan Romanick 
4285a04522aSIan Romanick     /* Each device has a directory under sysfs.  Within that directory there
4295a04522aSIan Romanick      * is a file named "config".  This file used to access the PCI config
4305a04522aSIan Romanick      * space.  It is used here to obtain most of the information about the
4315a04522aSIan Romanick      * device.
4325a04522aSIan Romanick      */
4335a04522aSIan Romanick     snprintf( name, 255, "%s/%04x:%02x:%02x.%1u/config",
4345a04522aSIan Romanick 	      SYS_BUS_PCI,
4355a04522aSIan Romanick 	      dev->domain,
4365a04522aSIan Romanick 	      dev->bus,
4375a04522aSIan Romanick 	      dev->dev,
4385a04522aSIan Romanick 	      dev->func );
4395a04522aSIan Romanick 
4405a04522aSIan Romanick     fd = open( name, O_WRONLY );
4415a04522aSIan Romanick     if ( fd == -1 ) {
4425a04522aSIan Romanick 	return errno;
4435a04522aSIan Romanick     }
4445a04522aSIan Romanick 
4455a04522aSIan Romanick 
4465a04522aSIan Romanick     while ( temp_size > 0 ) {
4475cf29b06SKeith Packard 	const ssize_t bytes = pwrite64( fd, data_bytes, temp_size, offset );
4485a04522aSIan Romanick 
4495a04522aSIan Romanick 	/* If zero bytes were written, then we assume it's the end of the
4505a04522aSIan Romanick 	 * config file.
4515a04522aSIan Romanick 	 */
452f9159b97SChris Wilson 	if ( bytes == 0 )
453f9159b97SChris Wilson 	    break;
454f9159b97SChris Wilson 	if ( bytes < 0 ) {
4555a04522aSIan Romanick 	    err = errno;
4565a04522aSIan Romanick 	    break;
4575a04522aSIan Romanick 	}
4585a04522aSIan Romanick 
4595a04522aSIan Romanick 	temp_size -= bytes;
4605a04522aSIan Romanick 	offset += bytes;
4615cf29b06SKeith Packard 	data_bytes += bytes;
4625a04522aSIan Romanick     }
4635a04522aSIan Romanick 
4645a04522aSIan Romanick     if ( bytes_written != NULL ) {
4655a04522aSIan Romanick 	*bytes_written = size - temp_size;
4665a04522aSIan Romanick     }
4675a04522aSIan Romanick 
4685a04522aSIan Romanick     close( fd );
4695a04522aSIan Romanick     return err;
4705a04522aSIan Romanick }
4715a04522aSIan Romanick 
4727282b53cSJesse Barnes static int
pci_device_linux_sysfs_map_range_wc(struct pci_device * dev,struct pci_device_mapping * map)4737282b53cSJesse Barnes pci_device_linux_sysfs_map_range_wc(struct pci_device *dev,
4747282b53cSJesse Barnes 				    struct pci_device_mapping *map)
4757282b53cSJesse Barnes {
4767282b53cSJesse Barnes     char name[256];
4777282b53cSJesse Barnes     int fd;
4787282b53cSJesse Barnes     const int prot = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE) != 0)
4797282b53cSJesse Barnes         ? (PROT_READ | PROT_WRITE) : PROT_READ;
4807282b53cSJesse Barnes     const int open_flags = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE) != 0)
4817282b53cSJesse Barnes         ? O_RDWR : O_RDONLY;
4827282b53cSJesse Barnes     const off_t offset = map->base - dev->regions[map->region].base_addr;
4837282b53cSJesse Barnes 
4847282b53cSJesse Barnes     snprintf(name, 255, "%s/%04x:%02x:%02x.%1u/resource%u_wc",
4857282b53cSJesse Barnes 	     SYS_BUS_PCI,
4867282b53cSJesse Barnes 	     dev->domain,
4877282b53cSJesse Barnes 	     dev->bus,
4887282b53cSJesse Barnes 	     dev->dev,
4897282b53cSJesse Barnes 	     dev->func,
4907282b53cSJesse Barnes 	     map->region);
4917282b53cSJesse Barnes     fd = open(name, open_flags);
4927282b53cSJesse Barnes     if (fd == -1)
4937282b53cSJesse Barnes 	    return errno;
4947282b53cSJesse Barnes 
4957282b53cSJesse Barnes     map->memory = mmap(NULL, map->size, prot, MAP_SHARED, fd, offset);
4967282b53cSJesse Barnes     if (map->memory == MAP_FAILED) {
4977282b53cSJesse Barnes         map->memory = NULL;
4987282b53cSJesse Barnes 	close(fd);
4997282b53cSJesse Barnes 	return errno;
5007282b53cSJesse Barnes     }
5017282b53cSJesse Barnes 
5027282b53cSJesse Barnes     close(fd);
5037282b53cSJesse Barnes 
5047282b53cSJesse Barnes     return 0;
5057282b53cSJesse Barnes }
5065a04522aSIan Romanick 
5075a04522aSIan Romanick /**
5085a04522aSIan Romanick  * Map a memory region for a device using the Linux sysfs interface.
5095a04522aSIan Romanick  *
5105a04522aSIan Romanick  * \param dev   Device whose memory region is to be mapped.
51108ff9f7fSIan Romanick  * \param map   Parameters of the mapping that is to be created.
5125a04522aSIan Romanick  *
5135a04522aSIan Romanick  * \return
5145a04522aSIan Romanick  * Zero on success or an \c errno value on failure.
5155a04522aSIan Romanick  *
51608ff9f7fSIan Romanick  * \sa pci_device_map_rrange, pci_device_linux_sysfs_unmap_range
5175a04522aSIan Romanick  *
5185a04522aSIan Romanick  * \todo
5195a04522aSIan Romanick  * Some older 2.6.x kernels don't implement the resourceN files.  On those
5205a04522aSIan Romanick  * systems /dev/mem must be used.  On these systems it is also possible that
5215a04522aSIan Romanick  * \c mmap64 may need to be used.
5225a04522aSIan Romanick  */
5235a04522aSIan Romanick static int
pci_device_linux_sysfs_map_range(struct pci_device * dev,struct pci_device_mapping * map)52408ff9f7fSIan Romanick pci_device_linux_sysfs_map_range(struct pci_device *dev,
52508ff9f7fSIan Romanick                                  struct pci_device_mapping *map)
5265a04522aSIan Romanick {
5275a04522aSIan Romanick     char name[256];
5285a04522aSIan Romanick     int fd;
5295a04522aSIan Romanick     int err = 0;
53008ff9f7fSIan Romanick     const int prot = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE) != 0)
53108ff9f7fSIan Romanick         ? (PROT_READ | PROT_WRITE) : PROT_READ;
53208ff9f7fSIan Romanick     const int open_flags = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE) != 0)
53308ff9f7fSIan Romanick         ? O_RDWR : O_RDONLY;
53408ff9f7fSIan Romanick     const off_t offset = map->base - dev->regions[map->region].base_addr;
53508ff9f7fSIan Romanick #ifdef HAVE_MTRR
53608ff9f7fSIan Romanick     struct mtrr_sentry sentry = {
53708ff9f7fSIan Romanick 	.base = map->base,
53808ff9f7fSIan Romanick         .size = map->size,
53908ff9f7fSIan Romanick 	.type = MTRR_TYPE_UNCACHABLE
54008ff9f7fSIan Romanick     };
54108ff9f7fSIan Romanick #endif
5425a04522aSIan Romanick 
5437282b53cSJesse Barnes     /* For WC mappings, try sysfs resourceN_wc file first */
5447282b53cSJesse Barnes     if ((map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE) &&
5457282b53cSJesse Barnes 	!pci_device_linux_sysfs_map_range_wc(dev, map))
5467282b53cSJesse Barnes 	    return 0;
5477282b53cSJesse Barnes 
5485a04522aSIan Romanick     snprintf(name, 255, "%s/%04x:%02x:%02x.%1u/resource%u",
5495a04522aSIan Romanick              SYS_BUS_PCI,
5505a04522aSIan Romanick              dev->domain,
5515a04522aSIan Romanick              dev->bus,
5525a04522aSIan Romanick              dev->dev,
5535a04522aSIan Romanick              dev->func,
55408ff9f7fSIan Romanick              map->region);
5555a04522aSIan Romanick 
55608ff9f7fSIan Romanick     fd = open(name, open_flags);
5575a04522aSIan Romanick     if (fd == -1) {
5585a04522aSIan Romanick         return errno;
5595a04522aSIan Romanick     }
5605a04522aSIan Romanick 
5615a04522aSIan Romanick 
56208ff9f7fSIan Romanick     map->memory = mmap(NULL, map->size, prot, MAP_SHARED, fd, offset);
56308ff9f7fSIan Romanick     if (map->memory == MAP_FAILED) {
56408ff9f7fSIan Romanick         map->memory = NULL;
5655a04522aSIan Romanick 	close(fd);
566e3adc06bSEric Anholt 	return errno;
567e3adc06bSEric Anholt     }
56808ff9f7fSIan Romanick 
56908ff9f7fSIan Romanick #ifdef HAVE_MTRR
57008ff9f7fSIan Romanick     if ((map->flags & PCI_DEV_MAP_FLAG_CACHABLE) != 0) {
57108ff9f7fSIan Romanick         sentry.type = MTRR_TYPE_WRBACK;
57208ff9f7fSIan Romanick     } else if ((map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE) != 0) {
57308ff9f7fSIan Romanick         sentry.type = MTRR_TYPE_WRCOMB;
5745a04522aSIan Romanick     }
5755a04522aSIan Romanick 
5765cf29b06SKeith Packard     if (pci_sys->mtrr_fd != -1 && sentry.type != MTRR_TYPE_UNCACHABLE) {
57708ff9f7fSIan Romanick 	if (ioctl(pci_sys->mtrr_fd, MTRRIOC_ADD_ENTRY, &sentry) < 0) {
57808ff9f7fSIan Romanick 	    /* FIXME: Should we report an error in this case?
5795a04522aSIan Romanick 	     */
58008ff9f7fSIan Romanick 	    fprintf(stderr, "error setting MTRR "
58108ff9f7fSIan Romanick 		    "(base = 0x%08lx, size = 0x%08x, type = %u) %s (%d)\n",
58208ff9f7fSIan Romanick 		    sentry.base, sentry.size, sentry.type,
58308ff9f7fSIan Romanick 		    strerror(errno), errno);
58408ff9f7fSIan Romanick /*            err = errno;*/
58564af050cSIan Romanick 	}
586b30d4582SKeith Packard 	/* KLUDGE ALERT -- rewrite the PTEs to turn off the CD and WT bits */
587b30d4582SKeith Packard 	mprotect (map->memory, map->size, PROT_NONE);
588e3adc06bSEric Anholt 	err = mprotect (map->memory, map->size, PROT_READ|PROT_WRITE);
589e3adc06bSEric Anholt 
590e3adc06bSEric Anholt 	if (err != 0) {
591e3adc06bSEric Anholt 	    fprintf(stderr, "mprotect(PROT_READ | PROT_WRITE) failed: %s\n",
592e3adc06bSEric Anholt 		    strerror(errno));
593f49f6671SStefan Dirsch 	    fprintf(stderr, "remapping without mprotect performance kludge.\n");
594e3adc06bSEric Anholt 
595e3adc06bSEric Anholt 	    munmap(map->memory, map->size);
596e3adc06bSEric Anholt 	    map->memory = mmap(NULL, map->size, prot, MAP_SHARED, fd, offset);
597e3adc06bSEric Anholt 	    if (map->memory == MAP_FAILED) {
598e3adc06bSEric Anholt 		map->memory = NULL;
599e3adc06bSEric Anholt 		close(fd);
600e3adc06bSEric Anholt 		return errno;
601e3adc06bSEric Anholt 	    }
602e3adc06bSEric Anholt 	}
60308ff9f7fSIan Romanick     }
60408ff9f7fSIan Romanick #endif
60564af050cSIan Romanick 
606e3adc06bSEric Anholt     close(fd);
607e3adc06bSEric Anholt 
608e3adc06bSEric Anholt     return 0;
6095a04522aSIan Romanick }
6105cf29b06SKeith Packard 
6115cf29b06SKeith Packard /**
6125cf29b06SKeith Packard  * Unmap a memory region for a device using the Linux sysfs interface.
6135cf29b06SKeith Packard  *
6145cf29b06SKeith Packard  * \param dev   Device whose memory region is to be unmapped.
6155cf29b06SKeith Packard  * \param map   Parameters of the mapping that is to be destroyed.
6165cf29b06SKeith Packard  *
6175cf29b06SKeith Packard  * \return
6185cf29b06SKeith Packard  * Zero on success or an \c errno value on failure.
6195cf29b06SKeith Packard  *
6205cf29b06SKeith Packard  * \sa pci_device_map_rrange, pci_device_linux_sysfs_map_range
6215cf29b06SKeith Packard  *
6225cf29b06SKeith Packard  * \todo
6235cf29b06SKeith Packard  * Some older 2.6.x kernels don't implement the resourceN files.  On those
6245cf29b06SKeith Packard  * systems /dev/mem must be used.  On these systems it is also possible that
6255cf29b06SKeith Packard  * \c mmap64 may need to be used.
6265cf29b06SKeith Packard  */
6275cf29b06SKeith Packard static int
pci_device_linux_sysfs_unmap_range(struct pci_device * dev,struct pci_device_mapping * map)6285cf29b06SKeith Packard pci_device_linux_sysfs_unmap_range(struct pci_device *dev,
6295cf29b06SKeith Packard 				   struct pci_device_mapping *map)
6305cf29b06SKeith Packard {
6315cf29b06SKeith Packard     int err = 0;
6325cf29b06SKeith Packard #ifdef HAVE_MTRR
6335cf29b06SKeith Packard     struct mtrr_sentry sentry = {
6345cf29b06SKeith Packard 	.base = map->base,
6355cf29b06SKeith Packard         .size = map->size,
6365cf29b06SKeith Packard 	.type = MTRR_TYPE_UNCACHABLE
6375cf29b06SKeith Packard     };
6385cf29b06SKeith Packard #endif
6395cf29b06SKeith Packard 
6405cf29b06SKeith Packard     err = pci_device_generic_unmap_range (dev, map);
6415cf29b06SKeith Packard     if (err)
6425cf29b06SKeith Packard 	return err;
6435cf29b06SKeith Packard 
6445cf29b06SKeith Packard #ifdef HAVE_MTRR
6455cf29b06SKeith Packard     if ((map->flags & PCI_DEV_MAP_FLAG_CACHABLE) != 0) {
6465cf29b06SKeith Packard         sentry.type = MTRR_TYPE_WRBACK;
6475cf29b06SKeith Packard     } else if ((map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE) != 0) {
6485cf29b06SKeith Packard         sentry.type = MTRR_TYPE_WRCOMB;
6495cf29b06SKeith Packard     }
6505cf29b06SKeith Packard 
6515cf29b06SKeith Packard     if (pci_sys->mtrr_fd != -1 && sentry.type != MTRR_TYPE_UNCACHABLE) {
6525cf29b06SKeith Packard 	if (ioctl(pci_sys->mtrr_fd, MTRRIOC_DEL_ENTRY, &sentry) < 0) {
6535cf29b06SKeith Packard 	    /* FIXME: Should we report an error in this case?
6545cf29b06SKeith Packard 	     */
6555cf29b06SKeith Packard 	    fprintf(stderr, "error setting MTRR "
6565cf29b06SKeith Packard 		    "(base = 0x%08lx, size = 0x%08x, type = %u) %s (%d)\n",
6575cf29b06SKeith Packard 		    sentry.base, sentry.size, sentry.type,
6585cf29b06SKeith Packard 		    strerror(errno), errno);
6595cf29b06SKeith Packard /*            err = errno;*/
6605cf29b06SKeith Packard 	}
6615cf29b06SKeith Packard     }
6625cf29b06SKeith Packard #endif
6635cf29b06SKeith Packard 
6645cf29b06SKeith Packard     return err;
6655cf29b06SKeith Packard }
6664bc9292fSDave Airlie 
pci_device_linux_sysfs_enable(struct pci_device * dev)6674bc9292fSDave Airlie static void pci_device_linux_sysfs_enable(struct pci_device *dev)
6684bc9292fSDave Airlie {
6694bc9292fSDave Airlie     char name[256];
6704bc9292fSDave Airlie     int fd;
6714bc9292fSDave Airlie 
6724bc9292fSDave Airlie     snprintf( name, 255, "%s/%04x:%02x:%02x.%1u/enable",
6734bc9292fSDave Airlie 	      SYS_BUS_PCI,
6744bc9292fSDave Airlie 	      dev->domain,
6754bc9292fSDave Airlie 	      dev->bus,
6764bc9292fSDave Airlie 	      dev->dev,
6774bc9292fSDave Airlie 	      dev->func );
6784bc9292fSDave Airlie 
6794bc9292fSDave Airlie     fd = open( name, O_RDWR );
6804bc9292fSDave Airlie     if (fd == -1)
6814bc9292fSDave Airlie        return;
6824bc9292fSDave Airlie 
6834bc9292fSDave Airlie     write( fd, "1", 1 );
6844bc9292fSDave Airlie     close(fd);
6854bc9292fSDave Airlie }
686b2838fb6SDave Airlie 
pci_device_linux_sysfs_boot_vga(struct pci_device * dev)687b2838fb6SDave Airlie static int pci_device_linux_sysfs_boot_vga(struct pci_device *dev)
688b2838fb6SDave Airlie {
689b2838fb6SDave Airlie     char name[256];
690b2838fb6SDave Airlie     char reply[3];
691b2838fb6SDave Airlie     int fd, bytes_read;
692b2838fb6SDave Airlie     int ret = 0;
693b2838fb6SDave Airlie 
694b2838fb6SDave Airlie     snprintf( name, 255, "%s/%04x:%02x:%02x.%1u/boot_vga",
695b2838fb6SDave Airlie 	      SYS_BUS_PCI,
696b2838fb6SDave Airlie 	      dev->domain,
697b2838fb6SDave Airlie 	      dev->bus,
698b2838fb6SDave Airlie 	      dev->dev,
699b2838fb6SDave Airlie 	      dev->func );
700b2838fb6SDave Airlie 
7018ba6b02eSAaron Plattner     fd = open( name, O_RDONLY );
702b2838fb6SDave Airlie     if (fd == -1)
703b2838fb6SDave Airlie        return 0;
704b2838fb6SDave Airlie 
705b2838fb6SDave Airlie     bytes_read = read(fd, reply, 1);
706b2838fb6SDave Airlie     if (bytes_read != 1)
707b2838fb6SDave Airlie 	goto out;
708b2838fb6SDave Airlie     if (reply[0] == '1')
709b2838fb6SDave Airlie 	ret = 1;
710b2838fb6SDave Airlie out:
711b2838fb6SDave Airlie     close(fd);
712b2838fb6SDave Airlie     return ret;
713b2838fb6SDave Airlie }
7145d1bdf0cSDave Airlie 
pci_device_linux_sysfs_has_kernel_driver(struct pci_device * dev)7155d1bdf0cSDave Airlie static int pci_device_linux_sysfs_has_kernel_driver(struct pci_device *dev)
7165d1bdf0cSDave Airlie {
7175d1bdf0cSDave Airlie     char name[256];
7185d1bdf0cSDave Airlie     struct stat dummy;
7195d1bdf0cSDave Airlie     int ret;
7205d1bdf0cSDave Airlie 
7215d1bdf0cSDave Airlie     snprintf( name, 255, "%s/%04x:%02x:%02x.%1u/driver",
7225d1bdf0cSDave Airlie 	      SYS_BUS_PCI,
7235d1bdf0cSDave Airlie 	      dev->domain,
7245d1bdf0cSDave Airlie 	      dev->bus,
7255d1bdf0cSDave Airlie 	      dev->dev,
7265d1bdf0cSDave Airlie 	      dev->func );
7275d1bdf0cSDave Airlie 
7285d1bdf0cSDave Airlie     ret = stat(name, &dummy);
7295d1bdf0cSDave Airlie     if (ret < 0)
7305d1bdf0cSDave Airlie 	return 0;
7315d1bdf0cSDave Airlie     return 1;
7325d1bdf0cSDave Airlie }
7335e8d4c19SAdam Jackson 
7345e8d4c19SAdam Jackson static struct pci_io_handle *
pci_device_linux_sysfs_open_device_io(struct pci_io_handle * ret,struct pci_device * dev,int bar,pciaddr_t base,pciaddr_t size)7355e8d4c19SAdam Jackson pci_device_linux_sysfs_open_device_io(struct pci_io_handle *ret,
7365e8d4c19SAdam Jackson 				      struct pci_device *dev, int bar,
7375e8d4c19SAdam Jackson 				      pciaddr_t base, pciaddr_t size)
7385e8d4c19SAdam Jackson {
7395e8d4c19SAdam Jackson     char name[PATH_MAX];
7405e8d4c19SAdam Jackson 
7415e8d4c19SAdam Jackson     snprintf(name, PATH_MAX, "%s/%04x:%02x:%02x.%1u/resource%d",
7425e8d4c19SAdam Jackson 	     SYS_BUS_PCI, dev->domain, dev->bus, dev->dev, dev->func, bar);
7435e8d4c19SAdam Jackson 
7445e8d4c19SAdam Jackson     ret->fd = open(name, O_RDWR);
7455e8d4c19SAdam Jackson 
7465e8d4c19SAdam Jackson     if (ret->fd < 0)
7475e8d4c19SAdam Jackson 	return NULL;
7485e8d4c19SAdam Jackson 
7495e8d4c19SAdam Jackson     ret->base = base;
7505e8d4c19SAdam Jackson     ret->size = size;
7515e8d4c19SAdam Jackson 
7525e8d4c19SAdam Jackson     return ret;
7535e8d4c19SAdam Jackson }
7545e8d4c19SAdam Jackson 
7555e8d4c19SAdam Jackson static struct pci_io_handle *
pci_device_linux_sysfs_open_legacy_io(struct pci_io_handle * ret,struct pci_device * dev,pciaddr_t base,pciaddr_t size)7565e8d4c19SAdam Jackson pci_device_linux_sysfs_open_legacy_io(struct pci_io_handle *ret,
7575e8d4c19SAdam Jackson 				      struct pci_device *dev, pciaddr_t base,
7585e8d4c19SAdam Jackson 				      pciaddr_t size)
7595e8d4c19SAdam Jackson {
7605e8d4c19SAdam Jackson     char name[PATH_MAX];
7615e8d4c19SAdam Jackson 
7625e8d4c19SAdam Jackson     /* First check if there's a legacy io method for the device */
7635e8d4c19SAdam Jackson     while (dev) {
7645e8d4c19SAdam Jackson 	snprintf(name, PATH_MAX, "/sys/class/pci_bus/%04x:%02x/legacy_io",
7655e8d4c19SAdam Jackson 		 dev->domain, dev->bus);
7665e8d4c19SAdam Jackson 
7675e8d4c19SAdam Jackson 	ret->fd = open(name, O_RDWR);
7685e8d4c19SAdam Jackson 	if (ret->fd >= 0)
7695e8d4c19SAdam Jackson 	    break;
7705e8d4c19SAdam Jackson 
7715e8d4c19SAdam Jackson 	dev = pci_device_get_parent_bridge(dev);
7725e8d4c19SAdam Jackson     }
7735e8d4c19SAdam Jackson 
7745e8d4c19SAdam Jackson     /* If not, /dev/port is the best we can do */
7755e8d4c19SAdam Jackson     if (!dev)
7765e8d4c19SAdam Jackson 	ret->fd = open("/dev/port", O_RDWR);
7775e8d4c19SAdam Jackson 
7785e8d4c19SAdam Jackson     if (ret->fd < 0)
7795e8d4c19SAdam Jackson 	return NULL;
7805e8d4c19SAdam Jackson 
7815e8d4c19SAdam Jackson     ret->base = base;
7825e8d4c19SAdam Jackson     ret->size = size;
7835e8d4c19SAdam Jackson 
7845e8d4c19SAdam Jackson     return ret;
7855e8d4c19SAdam Jackson }
7865e8d4c19SAdam Jackson 
7875e8d4c19SAdam Jackson static void
pci_device_linux_sysfs_close_io(struct pci_device * dev,struct pci_io_handle * handle)7885e8d4c19SAdam Jackson pci_device_linux_sysfs_close_io(struct pci_device *dev,
7895e8d4c19SAdam Jackson 				struct pci_io_handle *handle)
7905e8d4c19SAdam Jackson {
7915e8d4c19SAdam Jackson     close(handle->fd);
7925e8d4c19SAdam Jackson }
7935e8d4c19SAdam Jackson 
7945e8d4c19SAdam Jackson static uint32_t
pci_device_linux_sysfs_read32(struct pci_io_handle * handle,uint32_t port)7955e8d4c19SAdam Jackson pci_device_linux_sysfs_read32(struct pci_io_handle *handle, uint32_t port)
7965e8d4c19SAdam Jackson {
7975e8d4c19SAdam Jackson     uint32_t ret;
7985e8d4c19SAdam Jackson 
7995e8d4c19SAdam Jackson     pread(handle->fd, &ret, 4, port + handle->base);
8005e8d4c19SAdam Jackson 
8015e8d4c19SAdam Jackson     return ret;
8025e8d4c19SAdam Jackson }
8035e8d4c19SAdam Jackson 
8045e8d4c19SAdam Jackson static uint16_t
pci_device_linux_sysfs_read16(struct pci_io_handle * handle,uint32_t port)8055e8d4c19SAdam Jackson pci_device_linux_sysfs_read16(struct pci_io_handle *handle, uint32_t port)
8065e8d4c19SAdam Jackson {
8075e8d4c19SAdam Jackson     uint16_t ret;
8085e8d4c19SAdam Jackson 
8095e8d4c19SAdam Jackson     pread(handle->fd, &ret, 2, port + handle->base);
8105e8d4c19SAdam Jackson 
8115e8d4c19SAdam Jackson     return ret;
8125e8d4c19SAdam Jackson }
8135e8d4c19SAdam Jackson 
8145e8d4c19SAdam Jackson static uint8_t
pci_device_linux_sysfs_read8(struct pci_io_handle * handle,uint32_t port)8155e8d4c19SAdam Jackson pci_device_linux_sysfs_read8(struct pci_io_handle *handle, uint32_t port)
8165e8d4c19SAdam Jackson {
8175e8d4c19SAdam Jackson     uint8_t ret;
8185e8d4c19SAdam Jackson 
8195e8d4c19SAdam Jackson     pread(handle->fd, &ret, 1, port + handle->base);
8205e8d4c19SAdam Jackson 
8215e8d4c19SAdam Jackson     return ret;
8225e8d4c19SAdam Jackson }
8235e8d4c19SAdam Jackson 
8245e8d4c19SAdam Jackson static void
pci_device_linux_sysfs_write32(struct pci_io_handle * handle,uint32_t port,uint32_t data)8255e8d4c19SAdam Jackson pci_device_linux_sysfs_write32(struct pci_io_handle *handle, uint32_t port,
8265e8d4c19SAdam Jackson 			       uint32_t data)
8275e8d4c19SAdam Jackson {
8285e8d4c19SAdam Jackson     pwrite(handle->fd, &data, 4, port + handle->base);
8295e8d4c19SAdam Jackson }
8305e8d4c19SAdam Jackson 
8315e8d4c19SAdam Jackson static void
pci_device_linux_sysfs_write16(struct pci_io_handle * handle,uint32_t port,uint16_t data)8325e8d4c19SAdam Jackson pci_device_linux_sysfs_write16(struct pci_io_handle *handle, uint32_t port,
8335e8d4c19SAdam Jackson 			       uint16_t data)
8345e8d4c19SAdam Jackson {
8355e8d4c19SAdam Jackson     pwrite(handle->fd, &data, 2, port + handle->base);
8365e8d4c19SAdam Jackson }
8375e8d4c19SAdam Jackson 
8385e8d4c19SAdam Jackson static void
pci_device_linux_sysfs_write8(struct pci_io_handle * handle,uint32_t port,uint8_t data)8395e8d4c19SAdam Jackson pci_device_linux_sysfs_write8(struct pci_io_handle *handle, uint32_t port,
8405e8d4c19SAdam Jackson 			      uint8_t data)
8415e8d4c19SAdam Jackson {
8425e8d4c19SAdam Jackson     pwrite(handle->fd, &data, 1, port + handle->base);
8435e8d4c19SAdam Jackson }
8445e8d4c19SAdam Jackson 
84558e87933SAdam Jackson static int
pci_device_linux_sysfs_map_legacy(struct pci_device * dev,pciaddr_t base,pciaddr_t size,unsigned map_flags,void ** addr)84658e87933SAdam Jackson pci_device_linux_sysfs_map_legacy(struct pci_device *dev, pciaddr_t base,
84758e87933SAdam Jackson 				  pciaddr_t size, unsigned map_flags, void **addr)
84858e87933SAdam Jackson {
84958e87933SAdam Jackson     char name[PATH_MAX];
85058e87933SAdam Jackson     int flags = O_RDONLY;
85158e87933SAdam Jackson     int prot = PROT_READ;
85258e87933SAdam Jackson     int fd;
85358e87933SAdam Jackson     int ret=0;
85458e87933SAdam Jackson 
85558e87933SAdam Jackson     if (map_flags & PCI_DEV_MAP_FLAG_WRITABLE) {
856af4478c5SJeremy Huddleston 	flags = O_RDWR; /* O_RDWR != O_WRONLY | O_RDONLY */;
85758e87933SAdam Jackson 	prot |= PROT_WRITE;
85858e87933SAdam Jackson     }
85958e87933SAdam Jackson 
86058e87933SAdam Jackson     /* First check if there's a legacy memory method for the device */
86158e87933SAdam Jackson     while (dev) {
86258e87933SAdam Jackson 	snprintf(name, PATH_MAX, "/sys/class/pci_bus/%04x:%02x/legacy_mem",
86358e87933SAdam Jackson 		 dev->domain, dev->bus);
86458e87933SAdam Jackson 
86558e87933SAdam Jackson 	fd = open(name, flags);
86658e87933SAdam Jackson 	if (fd >= 0)
86758e87933SAdam Jackson 	    break;
86858e87933SAdam Jackson 
86958e87933SAdam Jackson 	dev = pci_device_get_parent_bridge(dev);
87058e87933SAdam Jackson     }
87158e87933SAdam Jackson 
87258e87933SAdam Jackson     /* If not, /dev/mem is the best we can do */
87358e87933SAdam Jackson     if (!dev)
87458e87933SAdam Jackson 	fd = open("/dev/mem", flags);
87558e87933SAdam Jackson 
87658e87933SAdam Jackson     if (fd < 0)
87758e87933SAdam Jackson 	return errno;
87858e87933SAdam Jackson 
87958e87933SAdam Jackson     *addr = mmap(NULL, size, prot, MAP_SHARED, fd, base);
88058e87933SAdam Jackson     if (*addr == MAP_FAILED) {
88158e87933SAdam Jackson 	ret = errno;
88258e87933SAdam Jackson     }
88358e87933SAdam Jackson 
88458e87933SAdam Jackson     close(fd);
88558e87933SAdam Jackson     return ret;
88658e87933SAdam Jackson }
88758e87933SAdam Jackson 
88858e87933SAdam Jackson static int
pci_device_linux_sysfs_unmap_legacy(struct pci_device * dev,void * addr,pciaddr_t size)88958e87933SAdam Jackson pci_device_linux_sysfs_unmap_legacy(struct pci_device *dev, void *addr, pciaddr_t size)
89058e87933SAdam Jackson {
89158e87933SAdam Jackson     return munmap(addr, size);
89258e87933SAdam Jackson }
89358e87933SAdam Jackson 
894a0a53a67SNithin Nayak Sujir 
895a0a53a67SNithin Nayak Sujir static void
pci_system_linux_destroy(void)896a0a53a67SNithin Nayak Sujir pci_system_linux_destroy(void)
897a0a53a67SNithin Nayak Sujir {
898a0a53a67SNithin Nayak Sujir #ifdef HAVE_MTRR
899a0a53a67SNithin Nayak Sujir 	if (pci_sys->mtrr_fd != -1)
900a0a53a67SNithin Nayak Sujir 		close(pci_sys->mtrr_fd);
901a0a53a67SNithin Nayak Sujir #endif
902a0a53a67SNithin Nayak Sujir }
903a0a53a67SNithin Nayak Sujir 
9045e8d4c19SAdam Jackson static const struct pci_system_methods linux_sysfs_methods = {
905a0a53a67SNithin Nayak Sujir     .destroy = pci_system_linux_destroy,
9065e8d4c19SAdam Jackson     .destroy_device = NULL,
9075e8d4c19SAdam Jackson     .read_rom = pci_device_linux_sysfs_read_rom,
9085e8d4c19SAdam Jackson     .probe = pci_device_linux_sysfs_probe,
9095e8d4c19SAdam Jackson     .map_range = pci_device_linux_sysfs_map_range,
9105e8d4c19SAdam Jackson     .unmap_range = pci_device_linux_sysfs_unmap_range,
9115e8d4c19SAdam Jackson 
9125e8d4c19SAdam Jackson     .read = pci_device_linux_sysfs_read,
9135e8d4c19SAdam Jackson     .write = pci_device_linux_sysfs_write,
9145e8d4c19SAdam Jackson 
9155e8d4c19SAdam Jackson     .fill_capabilities = pci_fill_capabilities_generic,
9165e8d4c19SAdam Jackson     .enable = pci_device_linux_sysfs_enable,
9175e8d4c19SAdam Jackson     .boot_vga = pci_device_linux_sysfs_boot_vga,
9185e8d4c19SAdam Jackson     .has_kernel_driver = pci_device_linux_sysfs_has_kernel_driver,
9195e8d4c19SAdam Jackson 
9205e8d4c19SAdam Jackson     .open_device_io = pci_device_linux_sysfs_open_device_io,
9215e8d4c19SAdam Jackson     .open_legacy_io = pci_device_linux_sysfs_open_legacy_io,
9225e8d4c19SAdam Jackson     .close_io = pci_device_linux_sysfs_close_io,
9235e8d4c19SAdam Jackson     .read32 = pci_device_linux_sysfs_read32,
9245e8d4c19SAdam Jackson     .read16 = pci_device_linux_sysfs_read16,
9255e8d4c19SAdam Jackson     .read8 = pci_device_linux_sysfs_read8,
9265e8d4c19SAdam Jackson     .write32 = pci_device_linux_sysfs_write32,
9275e8d4c19SAdam Jackson     .write16 = pci_device_linux_sysfs_write16,
9285e8d4c19SAdam Jackson     .write8 = pci_device_linux_sysfs_write8,
92958e87933SAdam Jackson 
93058e87933SAdam Jackson     .map_legacy = pci_device_linux_sysfs_map_legacy,
93158e87933SAdam Jackson     .unmap_legacy = pci_device_linux_sysfs_unmap_legacy,
9325e8d4c19SAdam Jackson };
933