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