Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[linux] Add support for accessing PCI configuration space via /proc/b…
…us/pci Signed-off-by: Michael Brown <mcb30@ipxe.org>
- Loading branch information
Showing
8 changed files
with
332 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
#ifndef _IPXE_LINUX_PCI_H | ||
#define _IPXE_LINUX_PCI_H | ||
|
||
/** @file | ||
* | ||
* iPXE PCI API for Linux | ||
* | ||
*/ | ||
|
||
FILE_LICENCE ( GPL2_OR_LATER ); | ||
|
||
#ifdef PCIAPI_LINUX | ||
#define PCIAPI_PREFIX_linux | ||
#else | ||
#define PCIAPI_PREFIX_linux __linux_ | ||
#endif | ||
|
||
struct pci_device; | ||
|
||
extern int linux_pci_read ( struct pci_device *pci, unsigned long where, | ||
unsigned long *value, size_t len ); | ||
extern int linux_pci_write ( struct pci_device *pci, unsigned long where, | ||
unsigned long value, size_t len ); | ||
|
||
/** | ||
* Read byte from PCI configuration space | ||
* | ||
* @v pci PCI device | ||
* @v where Location within PCI configuration space | ||
* @v value Value read | ||
* @ret rc Return status code | ||
*/ | ||
static inline __always_inline int | ||
PCIAPI_INLINE ( linux, pci_read_config_byte ) ( struct pci_device *pci, | ||
unsigned int where, | ||
uint8_t *value ) { | ||
int rc; | ||
unsigned long tmp; | ||
|
||
rc = linux_pci_read ( pci, where, &tmp, sizeof ( *value ) ); | ||
*value = tmp; | ||
return rc; | ||
} | ||
|
||
/** | ||
* Read word from PCI configuration space | ||
* | ||
* @v pci PCI device | ||
* @v where Location within PCI configuration space | ||
* @v value Value read | ||
* @ret rc Return status code | ||
*/ | ||
static inline __always_inline int | ||
PCIAPI_INLINE ( linux, pci_read_config_word ) ( struct pci_device *pci, | ||
unsigned int where, | ||
uint16_t *value ) { | ||
int rc; | ||
unsigned long tmp; | ||
|
||
rc = linux_pci_read ( pci, where, &tmp, sizeof ( *value ) ); | ||
*value = tmp; | ||
return rc; | ||
} | ||
|
||
/** | ||
* Read dword from PCI configuration space | ||
* | ||
* @v pci PCI device | ||
* @v where Location within PCI configuration space | ||
* @v value Value read | ||
* @ret rc Return status code | ||
*/ | ||
static inline __always_inline int | ||
PCIAPI_INLINE ( linux, pci_read_config_dword ) ( struct pci_device *pci, | ||
unsigned int where, | ||
uint32_t *value ) { | ||
int rc; | ||
unsigned long tmp; | ||
|
||
rc = linux_pci_read ( pci, where, &tmp, sizeof ( *value ) ); | ||
*value = tmp; | ||
return rc; | ||
} | ||
|
||
/** | ||
* Write byte to PCI configuration space | ||
* | ||
* @v pci PCI device | ||
* @v where Location within PCI configuration space | ||
* @v value Value to be written | ||
* @ret rc Return status code | ||
*/ | ||
static inline __always_inline int | ||
PCIAPI_INLINE ( linux, pci_write_config_byte ) ( struct pci_device *pci, | ||
unsigned int where, | ||
uint8_t value ) { | ||
return linux_pci_write ( pci, where, value, sizeof ( value ) ); | ||
} | ||
|
||
/** | ||
* Write word to PCI configuration space | ||
* | ||
* @v pci PCI device | ||
* @v where Location within PCI configuration space | ||
* @v value Value to be written | ||
* @ret rc Return status code | ||
*/ | ||
static inline __always_inline int | ||
PCIAPI_INLINE ( linux, pci_write_config_word ) ( struct pci_device *pci, | ||
unsigned int where, | ||
uint16_t value ) { | ||
return linux_pci_write ( pci, where, value, sizeof ( value ) ); | ||
} | ||
|
||
/** | ||
* Write dword to PCI configuration space | ||
* | ||
* @v pci PCI device | ||
* @v where Location within PCI configuration space | ||
* @v value Value to be written | ||
* @ret rc Return status code | ||
*/ | ||
static inline __always_inline int | ||
PCIAPI_INLINE ( linux, pci_write_config_dword ) ( struct pci_device *pci, | ||
unsigned int where, | ||
uint32_t value ) { | ||
return linux_pci_write ( pci, where, value, sizeof ( value ) ); | ||
} | ||
|
||
#endif /* _IPXE_LINUX_PCI_H */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
/* | ||
* Copyright (C) 2013 Michael Brown <mbrown@fensystems.co.uk>. | ||
* | ||
* This program is free software; you can redistribute it and/or | ||
* modify it under the terms of the GNU General Public License as | ||
* published by the Free Software Foundation; either version 2 of the | ||
* License, or (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, but | ||
* WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
* General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program; if not, write to the Free Software | ||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
* 02110-1301, USA. | ||
*/ | ||
|
||
FILE_LICENCE ( GPL2_OR_LATER ); | ||
|
||
#include <stdio.h> | ||
#include <errno.h> | ||
#include <byteswap.h> | ||
#include <linux_api.h> | ||
#include <ipxe/linux.h> | ||
#include <ipxe/pci.h> | ||
|
||
/** @file | ||
* | ||
* iPXE PCI API for Linux | ||
* | ||
*/ | ||
|
||
/** | ||
* Open PCI configuration space | ||
* | ||
* @v pci PCI device | ||
* @v flags Access mode flags | ||
* @v where Address within configuration space | ||
* @ret fd File handle, or negative error | ||
*/ | ||
static int linux_pci_open ( struct pci_device *pci, int flags, | ||
unsigned long where ) { | ||
char filename[ 22 /* "/proc/bus/pci/xx/xx.x" + NUL */ ]; | ||
int fd; | ||
int rc; | ||
|
||
/* Construct filename */ | ||
snprintf ( filename, sizeof ( filename ), "/proc/bus/pci/%02x/%02x.%x", | ||
PCI_BUS ( pci->busdevfn ), PCI_SLOT ( pci->busdevfn ), | ||
PCI_FUNC ( pci->busdevfn ) ); | ||
|
||
/* Open file */ | ||
fd = linux_open ( filename, flags ); | ||
if ( fd < 0 ) { | ||
DBGC ( pci, "PCI could not open %s: %s\n", filename, | ||
linux_strerror ( linux_errno ) ); | ||
rc = -ELINUX ( linux_errno ); | ||
goto err_open; | ||
} | ||
|
||
/* Seek to location */ | ||
if ( linux_lseek ( fd, where, SEEK_SET ) < 0 ) { | ||
DBGC ( pci, "PCI could not seek to %s offset %#02lx: %s\n", | ||
filename, where, linux_strerror ( linux_errno ) ); | ||
rc = -ELINUX ( linux_errno ); | ||
goto err_seek; | ||
} | ||
|
||
return fd; | ||
|
||
err_seek: | ||
linux_close ( fd ); | ||
err_open: | ||
return rc; | ||
} | ||
|
||
/** | ||
* Read from PCI configuration space | ||
* | ||
* @v pci PCI device | ||
* @v where Address within configuration space | ||
* @v value Data buffer | ||
* @v len Length to read | ||
* @ret rc Return status code | ||
*/ | ||
int linux_pci_read ( struct pci_device *pci, unsigned long where, | ||
unsigned long *value, size_t len ) { | ||
uint32_t tmp = 0; | ||
int fd; | ||
int check_len; | ||
int rc; | ||
|
||
/* Return "missing device" in case of error */ | ||
*value = -1UL; | ||
|
||
/* Open configuration space */ | ||
fd = linux_pci_open ( pci, O_RDONLY, where ); | ||
if ( fd < 0 ) { | ||
rc = fd; | ||
goto err_open; | ||
} | ||
|
||
/* Read value */ | ||
check_len = linux_read ( fd, &tmp, len ); | ||
if ( check_len < 0 ) { | ||
DBGC ( pci, "PCI could not read from " PCI_FMT " %#02lx+%#zx: " | ||
"%s\n", PCI_ARGS ( pci ), where, len, | ||
linux_strerror ( linux_errno ) ); | ||
rc = -ELINUX ( linux_errno ); | ||
goto err_read; | ||
} | ||
if ( ( size_t ) check_len != len ) { | ||
DBGC ( pci, "PCI read only %#x bytes from " PCI_FMT | ||
" %#02lx+%#zx\n", check_len, PCI_ARGS ( pci ), | ||
where, len ); | ||
rc = -EIO; | ||
goto err_read; | ||
} | ||
|
||
/* Return value */ | ||
*value = le32_to_cpu ( tmp ); | ||
|
||
/* Success */ | ||
rc = 0; | ||
|
||
err_read: | ||
linux_close ( fd ); | ||
err_open: | ||
return rc; | ||
} | ||
|
||
/** | ||
* Write to PCI configuration space | ||
* | ||
* @v pci PCI device | ||
* @v where Address within configuration space | ||
* @v value Value to write | ||
* @v len Length of value | ||
* @ret rc Return status code | ||
*/ | ||
int linux_pci_write ( struct pci_device *pci, unsigned long where, | ||
unsigned long value, size_t len ) { | ||
uint32_t tmp; | ||
int fd; | ||
int check_len; | ||
int rc; | ||
|
||
/* Open configuration space */ | ||
fd = linux_pci_open ( pci, O_WRONLY, where ); | ||
if ( fd < 0 ) { | ||
rc = fd; | ||
goto err_open; | ||
} | ||
|
||
/* Prepare value for writing */ | ||
tmp = cpu_to_le32 ( value ); | ||
assert ( len <= sizeof ( tmp ) ); | ||
|
||
/* Write value */ | ||
check_len = linux_write ( fd, &tmp, len ); | ||
if ( check_len < 0 ) { | ||
DBGC ( pci, "PCI could not write to " PCI_FMT " %#02lx+%#zx: " | ||
"%s\n", PCI_ARGS ( pci ), where, len, | ||
linux_strerror ( linux_errno ) ); | ||
rc = -ELINUX ( linux_errno ); | ||
goto err_write; | ||
} | ||
if ( ( size_t ) check_len != len ) { | ||
DBGC ( pci, "PCI wrote only %#x bytes to " PCI_FMT | ||
" %#02lx+%#zx\n", check_len, PCI_ARGS ( pci ), | ||
where, len ); | ||
rc = -EIO; | ||
goto err_write; | ||
} | ||
|
||
/* Success */ | ||
rc = 0; | ||
|
||
err_write: | ||
linux_close ( fd ); | ||
err_open: | ||
return rc; | ||
} |