2013-10-23 17:44:40 +04:00
/*
* Copyright ( C ) 2013 Red Hat , Inc .
*
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation ; either
* version 2.1 of the License , or ( at your option ) any later version .
*
* This library 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
* Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library . If not , see
* < http : //www.gnu.org/licenses/>.
*/
# include <config.h>
2018-04-30 17:04:39 +03:00
# if defined(__linux__) || defined(__FreeBSD__)
2016-05-13 13:32:00 +03:00
# include "virmock.h"
2013-10-23 17:44:40 +04:00
# include <unistd.h>
# include <fcntl.h>
# include <sys / stat.h>
# include <stdarg.h>
2013-12-24 22:07:27 +04:00
# include <dirent.h>
2013-10-23 17:44:40 +04:00
# include "viralloc.h"
# include "virstring.h"
# include "virfile.h"
2013-10-23 19:55:02 +04:00
# include "dirname.h"
2013-10-23 17:44:40 +04:00
2016-05-13 13:32:00 +03:00
static int ( * real_access ) ( const char * path , int mode ) ;
static int ( * real_lstat ) ( const char * path , struct stat * sb ) ;
static int ( * real___lxstat ) ( int ver , const char * path , struct stat * sb ) ;
static int ( * real_stat ) ( const char * path , struct stat * sb ) ;
static int ( * real___xstat ) ( int ver , const char * path , struct stat * sb ) ;
static int ( * real_open ) ( const char * path , int flags , . . . ) ;
static int ( * real_close ) ( int fd ) ;
static DIR * ( * real_opendir ) ( const char * name ) ;
2018-05-03 11:01:04 +03:00
static char * ( * real_virFileCanonicalizePath ) ( const char * path ) ;
2013-10-23 17:44:40 +04:00
/* Don't make static, since it causes problems with clang
* when passed as an arg to virAsprintf ( )
* vircgroupmock . c : 462 : 22 : error : static variable ' fakesysfsdir ' is used in an inline function with external linkage [ - Werror , - Wstatic - in - inline ]
*/
2015-12-04 12:05:56 +03:00
char * fakerootdir ;
2015-12-04 16:51:08 +03:00
char * fakesysfspcidir ;
2013-10-23 17:44:40 +04:00
2015-12-04 16:51:08 +03:00
# define SYSFS_PCI_PREFIX " / sys / bus / pci / "
2013-10-23 17:44:40 +04:00
2017-11-03 15:09:47 +03:00
# define STDERR(...) \
fprintf ( stderr , " %s %zu: " , __FUNCTION__ , ( size_t ) __LINE__ ) ; \
fprintf ( stderr , __VA_ARGS__ ) ; \
fprintf ( stderr , " \n " ) ; \
# define ABORT(...) \
do { \
STDERR ( __VA_ARGS__ ) ; \
abort ( ) ; \
2013-10-23 17:44:40 +04:00
} while ( 0 )
2017-11-03 15:09:47 +03:00
# define ABORT_OOM() \
2013-10-23 17:44:40 +04:00
ABORT ( " Out of memory " )
/*
* The plan :
*
* Mock some file handling functions . Redirect them into a stub tree passed via
2015-12-04 15:38:16 +03:00
* LIBVIRT_FAKE_ROOT_DIR env variable . All files and links within stub tree is
2013-10-23 19:55:02 +04:00
* created by us . There are some actions that we must take if some special
* files are written to . Here ' s the list of files we watch :
*
* / sys / bus / pci / drivers / < driver > / new_id :
* Learn the driver new vendor and device combination .
* Data in format " VVVV DDDD " .
*
* / sys / bus / pci / drivers / < driver > / remove_id
* Make the driver forget about vendor and device .
* Data in format " VVVV DDDD " .
*
* / sys / bus / pci / drivers / < driver > / bind
* Check if driver supports the device and bind driver to it ( create symlink
* called ' driver ' pointing to the / sys / but / pci / drivers / < driver > ) .
* Data in format " DDDD:BB:DD.F " ( Domain : Bus : Device . Function ) .
*
* / sys / bus / pci / drivers / < driver > / unbind
* Unbind driver from the device .
* Data in format " DDDD:BB:DD.F " ( Domain : Bus : Device . Function ) .
*
2014-01-16 15:28:12 +04:00
* / sys / bus / pci / drivers_probe
* Probe for a driver that handles the specified device .
* Data in format " DDDD:BB:DD.F " ( Domain : Bus : Device . Function ) .
*
2013-10-23 19:55:02 +04:00
* As a little hack , we are not mocking write to these files , but close ( )
* instead . The advantage is we don ' t need any self growing array to hold the
* partial writes and construct them back . We can let all the writes finish ,
* and then just read the file content back .
2013-10-23 17:44:40 +04:00
*/
/*
*
* Functions to model kernel behavior
*
*/
2014-01-16 17:05:19 +04:00
enum driverActions {
PCI_ACTION_BIND = 1 < < 0 ,
PCI_ACTION_UNBIND = 1 < < 1 ,
PCI_ACTION_NEW_ID = 1 < < 2 ,
PCI_ACTION_REMOVE_ID = 1 < < 3 ,
} ;
2013-10-23 19:55:02 +04:00
struct pciDriver {
char * name ;
int * vendor ; /* List of vendor:device IDs the driver can handle */
int * device ;
size_t len ; /* @len is used for both @vendor and @device */
2014-01-16 17:05:19 +04:00
unsigned int fail ; /* Bitwise-OR of driverActions that should fail */
2013-10-23 19:55:02 +04:00
} ;
2013-10-23 17:44:40 +04:00
struct pciDevice {
char * id ;
int vendor ;
int device ;
2019-03-12 11:08:00 +03:00
int klass ;
2017-06-01 19:48:52 +03:00
int iommuGroup ;
2013-10-23 19:55:02 +04:00
struct pciDriver * driver ; /* Driver attached. NULL if attached to no driver */
} ;
struct fdCallback {
int fd ;
char * path ;
2013-10-23 17:44:40 +04:00
} ;
struct pciDevice * * pciDevices = NULL ;
2014-03-13 15:59:32 +04:00
size_t nPCIDevices = 0 ;
2013-10-23 17:44:40 +04:00
2013-10-23 19:55:02 +04:00
struct pciDriver * * pciDrivers = NULL ;
2014-03-13 15:59:32 +04:00
size_t nPCIDrivers = 0 ;
2013-10-23 19:55:02 +04:00
struct fdCallback * callbacks = NULL ;
size_t nCallbacks = 0 ;
2013-10-23 17:44:40 +04:00
static void init_env ( void ) ;
2013-10-23 19:55:02 +04:00
static int pci_device_autobind ( struct pciDevice * dev ) ;
2013-10-23 17:44:40 +04:00
static void pci_device_new_from_stub ( const struct pciDevice * data ) ;
2013-10-23 19:55:02 +04:00
static struct pciDevice * pci_device_find_by_id ( const char * id ) ;
static struct pciDevice * pci_device_find_by_content ( const char * path ) ;
2014-01-16 17:05:19 +04:00
static void pci_driver_new ( const char * name , int fail , . . . ) ;
2013-10-23 19:55:02 +04:00
static struct pciDriver * pci_driver_find_by_dev ( struct pciDevice * dev ) ;
static struct pciDriver * pci_driver_find_by_path ( const char * path ) ;
static int pci_driver_bind ( struct pciDriver * driver , struct pciDevice * dev ) ;
static int pci_driver_unbind ( struct pciDriver * driver , struct pciDevice * dev ) ;
static int pci_driver_handle_change ( int fd , const char * path ) ;
static int pci_driver_handle_bind ( const char * path ) ;
static int pci_driver_handle_unbind ( const char * path ) ;
static int pci_driver_handle_new_id ( const char * path ) ;
static int pci_driver_handle_remove_id ( const char * path ) ;
2013-10-23 17:44:40 +04:00
/*
* Helper functions
*/
static void
make_file ( const char * path ,
const char * name ,
2013-11-28 01:59:52 +04:00
const char * value ,
ssize_t len )
2013-10-23 17:44:40 +04:00
{
int fd = - 1 ;
char * filepath = NULL ;
2013-11-28 01:59:52 +04:00
if ( value & & len = = - 1 )
len = strlen ( value ) ;
2013-10-23 17:44:40 +04:00
if ( virAsprintfQuiet ( & filepath , " %s/%s " , path , name ) < 0 )
ABORT_OOM ( ) ;
2016-05-13 13:32:00 +03:00
if ( ( fd = real_open ( filepath , O_CREAT | O_WRONLY , 0666 ) ) < 0 )
2013-10-23 17:44:40 +04:00
ABORT ( " Unable to open: %s " , filepath ) ;
2013-11-28 01:59:52 +04:00
if ( value & & safewrite ( fd , value , len ) ! = len )
2013-10-23 17:44:40 +04:00
ABORT ( " Unable to write: %s " , filepath ) ;
VIR_FORCE_CLOSE ( fd ) ;
VIR_FREE ( filepath ) ;
}
2017-06-01 19:48:52 +03:00
static void
make_symlink ( const char * path ,
const char * name ,
const char * target )
{
char * filepath = NULL ;
if ( virAsprintfQuiet ( & filepath , " %s/%s " , path , name ) < 0 )
ABORT_OOM ( ) ;
if ( symlink ( target , filepath ) < 0 )
ABORT ( " Unable to create symlink filepath -> target " ) ;
VIR_FREE ( filepath ) ;
}
2013-10-23 19:55:02 +04:00
static int
pci_read_file ( const char * path ,
char * buf ,
size_t buf_size )
{
int ret = - 1 ;
int fd = - 1 ;
char * newpath ;
if ( virAsprintfQuiet ( & newpath , " %s/%s " ,
2015-12-04 16:51:08 +03:00
fakesysfspcidir ,
path + strlen ( SYSFS_PCI_PREFIX ) ) < 0 ) {
2013-10-23 19:55:02 +04:00
errno = ENOMEM ;
goto cleanup ;
}
2016-05-13 13:32:00 +03:00
if ( ( fd = real_open ( newpath , O_RDWR ) ) < 0 )
2013-10-23 19:55:02 +04:00
goto cleanup ;
bzero ( buf , buf_size ) ;
if ( saferead ( fd , buf , buf_size - 1 ) < 0 ) {
STDERR ( " Unable to read from %s " , newpath ) ;
goto cleanup ;
}
if ( ftruncate ( fd , 0 ) < 0 )
goto cleanup ;
ret = 0 ;
2014-03-25 10:53:44 +04:00
cleanup :
2013-10-23 19:55:02 +04:00
VIR_FREE ( newpath ) ;
2016-05-13 13:32:00 +03:00
real_close ( fd ) ;
2013-10-23 19:55:02 +04:00
return ret ;
}
2013-10-23 17:44:40 +04:00
static int
getrealpath ( char * * newpath ,
const char * path )
{
2015-12-03 17:27:28 +03:00
init_env ( ) ;
2013-10-23 17:44:40 +04:00
2015-12-04 16:51:08 +03:00
if ( STRPREFIX ( path , SYSFS_PCI_PREFIX ) ) {
2013-10-23 17:44:40 +04:00
if ( virAsprintfQuiet ( newpath , " %s/%s " ,
2015-12-04 16:51:08 +03:00
fakesysfspcidir ,
path + strlen ( SYSFS_PCI_PREFIX ) ) < 0 ) {
2013-10-23 17:44:40 +04:00
errno = ENOMEM ;
return - 1 ;
}
} else {
if ( VIR_STRDUP_QUIET ( * newpath , path ) < 0 )
return - 1 ;
}
return 0 ;
}
2013-10-23 19:55:02 +04:00
static bool
find_fd ( int fd , size_t * indx )
{
size_t i ;
for ( i = 0 ; i < nCallbacks ; i + + ) {
if ( callbacks [ i ] . fd = = fd ) {
if ( indx )
* indx = i ;
return true ;
}
}
return false ;
}
static int
add_fd ( int fd , const char * path )
{
int ret = - 1 ;
size_t i ;
if ( find_fd ( fd , & i ) ) {
struct fdCallback cb = callbacks [ i ] ;
ABORT ( " FD %d %s already present in the array as %d %s " ,
fd , path , cb . fd , cb . path ) ;
}
if ( VIR_REALLOC_N_QUIET ( callbacks , nCallbacks + 1 ) < 0 | |
VIR_STRDUP_QUIET ( callbacks [ nCallbacks ] . path , path ) < 0 ) {
errno = ENOMEM ;
goto cleanup ;
}
callbacks [ nCallbacks + + ] . fd = fd ;
ret = 0 ;
2014-03-25 10:53:44 +04:00
cleanup :
2013-10-23 19:55:02 +04:00
return ret ;
}
static int
remove_fd ( int fd )
{
int ret = - 1 ;
size_t i ;
if ( find_fd ( fd , & i ) ) {
struct fdCallback cb = callbacks [ i ] ;
if ( pci_driver_handle_change ( cb . fd , cb . path ) < 0 )
goto cleanup ;
VIR_FREE ( cb . path ) ;
if ( VIR_DELETE_ELEMENT ( callbacks , i , nCallbacks ) < 0 ) {
errno = EINVAL ;
goto cleanup ;
}
}
ret = 0 ;
2014-03-25 10:53:44 +04:00
cleanup :
2013-10-23 19:55:02 +04:00
return ret ;
}
2013-10-23 17:44:40 +04:00
/*
* PCI Device functions
*/
static void
pci_device_new_from_stub ( const struct pciDevice * data )
{
struct pciDevice * dev ;
char * devpath ;
2014-01-23 01:26:48 +04:00
char * id ;
char * c ;
2013-11-28 01:59:52 +04:00
char * configSrc ;
2017-06-01 19:48:52 +03:00
char tmp [ 256 ] ;
2013-11-05 18:07:49 +04:00
struct stat sb ;
2018-04-30 17:04:39 +03:00
bool configSrcExists = false ;
2013-10-23 17:44:40 +04:00
2014-01-23 01:26:48 +04:00
if ( VIR_STRDUP_QUIET ( id , data - > id ) < 0 )
ABORT_OOM ( ) ;
/* Replace ':' with '-' to create the config filename from the
* device ID . The device ID cannot be used directly as filename
* because it contains ' : ' and Windows does not allow ' : ' in
* filenames . */
c = strchr ( id , ' : ' ) ;
while ( c ) {
* c = ' - ' ;
c = strchr ( c , ' : ' ) ;
}
2013-10-23 17:44:40 +04:00
if ( VIR_ALLOC_QUIET ( dev ) < 0 | |
2013-11-28 01:31:53 +04:00
virAsprintfQuiet ( & configSrc , " %s/virpcitestdata/%s.config " ,
2014-01-23 01:26:48 +04:00
abs_srcdir , id ) < 0 | |
2015-12-04 16:51:08 +03:00
virAsprintfQuiet ( & devpath , " %s/devices/%s " , fakesysfspcidir , data - > id ) < 0 )
2013-10-23 17:44:40 +04:00
ABORT_OOM ( ) ;
2014-01-23 01:26:48 +04:00
VIR_FREE ( id ) ;
2013-10-23 17:44:40 +04:00
memcpy ( dev , data , sizeof ( * dev ) ) ;
if ( virFileMakePath ( devpath ) < 0 )
ABORT ( " Unable to create: %s " , devpath ) ;
2018-04-30 17:04:39 +03:00
if ( real_stat & & real_stat ( configSrc , & sb ) = = 0 )
configSrcExists = true ;
# ifdef HAVE___XSTAT
if ( ! configSrcExists & &
real___xstat & & real___xstat ( _STAT_VER , configSrc , & sb ) = = 0 )
configSrcExists = true ;
# endif
2013-11-05 18:07:49 +04:00
/* If there is a config file for the device within virpcitestdata dir,
* symlink it . Otherwise create a dummy config file . */
2018-04-30 17:04:39 +03:00
if ( configSrcExists ) {
2013-11-28 01:59:52 +04:00
/* On success, copy @configSrc into the destination (a copy,
* rather than a symlink , is required since we write into the
* file , and parallel VPATH builds must not stomp on the
* original ; besides , ' make distcheck ' requires the original
* to be read - only */
char * buf ;
ssize_t len ;
if ( ( len = virFileReadAll ( configSrc , 4096 , & buf ) ) < 0 )
ABORT ( " Unable to read config file '%s' " , configSrc ) ;
make_file ( devpath , " config " , buf , len ) ;
VIR_FREE ( buf ) ;
2013-11-05 18:07:49 +04:00
} else {
/* If there's no config data in the virpcitestdata dir, create a dummy
* config file */
2013-11-28 01:59:52 +04:00
make_file ( devpath , " config " , " some dummy config " , - 1 ) ;
2013-11-05 18:07:49 +04:00
}
2013-10-23 17:44:40 +04:00
if ( snprintf ( tmp , sizeof ( tmp ) , " 0x%.4x " , dev - > vendor ) < 0 )
ABORT ( " @tmp overflow " ) ;
2013-11-28 01:59:52 +04:00
make_file ( devpath , " vendor " , tmp , - 1 ) ;
2013-10-23 17:44:40 +04:00
if ( snprintf ( tmp , sizeof ( tmp ) , " 0x%.4x " , dev - > device ) < 0 )
ABORT ( " @tmp overflow " ) ;
2013-11-28 01:59:52 +04:00
make_file ( devpath , " device " , tmp , - 1 ) ;
2013-10-23 17:44:40 +04:00
2019-03-12 11:08:00 +03:00
if ( snprintf ( tmp , sizeof ( tmp ) , " 0x%.4x " , dev - > klass ) < 0 )
2013-12-24 22:07:27 +04:00
ABORT ( " @tmp overflow " ) ;
make_file ( devpath , " class " , tmp , - 1 ) ;
2017-06-01 19:48:52 +03:00
if ( snprintf ( tmp , sizeof ( tmp ) ,
" %s/../../../kernel/iommu_groups/%d " ,
devpath , dev - > iommuGroup ) < 0 ) {
ABORT ( " @tmp overflow " ) ;
}
if ( virFileMakePath ( tmp ) < 0 )
ABORT ( " Unable to create %s " , tmp ) ;
if ( snprintf ( tmp , sizeof ( tmp ) ,
" ../../../kernel/iommu_groups/%d " , dev - > iommuGroup ) < 0 ) {
ABORT ( " @tmp overflow " ) ;
}
make_symlink ( devpath , " iommu_group " , tmp ) ;
2013-10-23 19:55:02 +04:00
if ( pci_device_autobind ( dev ) < 0 )
ABORT ( " Unable to bind: %s " , data - > id ) ;
2014-03-13 15:59:32 +04:00
if ( VIR_APPEND_ELEMENT_QUIET ( pciDevices , nPCIDevices , dev ) < 0 )
2013-10-23 17:44:40 +04:00
ABORT_OOM ( ) ;
VIR_FREE ( devpath ) ;
2013-11-28 01:59:52 +04:00
VIR_FREE ( configSrc ) ;
2013-10-23 17:44:40 +04:00
}
2013-10-23 19:55:02 +04:00
static struct pciDevice *
pci_device_find_by_id ( const char * id )
{
size_t i ;
2014-03-13 15:59:32 +04:00
for ( i = 0 ; i < nPCIDevices ; i + + ) {
2013-10-23 19:55:02 +04:00
struct pciDevice * dev = pciDevices [ i ] ;
if ( STREQ ( dev - > id , id ) )
return dev ;
}
return NULL ;
}
static struct pciDevice *
pci_device_find_by_content ( const char * path )
{
char tmp [ 32 ] ;
if ( pci_read_file ( path , tmp , sizeof ( tmp ) ) < 0 )
return NULL ;
return pci_device_find_by_id ( tmp ) ;
}
static int
pci_device_autobind ( struct pciDevice * dev )
{
struct pciDriver * driver = pci_driver_find_by_dev ( dev ) ;
if ( ! driver ) {
/* No driver found. Nothing to do */
return 0 ;
}
return pci_driver_bind ( driver , dev ) ;
}
/*
* PCI Driver functions
*/
static void
2014-01-16 17:05:19 +04:00
pci_driver_new ( const char * name , int fail , . . . )
2013-10-23 19:55:02 +04:00
{
struct pciDriver * driver ;
va_list args ;
int vendor , device ;
char * driverpath ;
if ( VIR_ALLOC_QUIET ( driver ) < 0 | |
VIR_STRDUP_QUIET ( driver - > name , name ) < 0 | |
2015-12-04 16:51:08 +03:00
virAsprintfQuiet ( & driverpath , " %s/drivers/%s " , fakesysfspcidir , name ) < 0 )
2013-10-23 19:55:02 +04:00
ABORT_OOM ( ) ;
2014-01-16 17:05:19 +04:00
driver - > fail = fail ;
2013-10-23 19:55:02 +04:00
if ( virFileMakePath ( driverpath ) < 0 )
ABORT ( " Unable to create: %s " , driverpath ) ;
2014-01-16 17:05:19 +04:00
va_start ( args , fail ) ;
2013-10-23 19:55:02 +04:00
while ( ( vendor = va_arg ( args , int ) ) ! = - 1 ) {
if ( ( device = va_arg ( args , int ) ) = = - 1 )
ABORT ( " Invalid vendor device pair for driver %s " , name ) ;
if ( VIR_REALLOC_N_QUIET ( driver - > vendor , driver - > len + 1 ) < 0 | |
VIR_REALLOC_N_QUIET ( driver - > device , driver - > len + 1 ) < 0 )
ABORT_OOM ( ) ;
driver - > vendor [ driver - > len ] = vendor ;
driver - > device [ driver - > len ] = device ;
driver - > len + + ;
}
2013-11-05 19:37:38 +04:00
va_end ( args ) ;
2013-11-28 01:59:52 +04:00
make_file ( driverpath , " bind " , NULL , - 1 ) ;
make_file ( driverpath , " unbind " , NULL , - 1 ) ;
make_file ( driverpath , " new_id " , NULL , - 1 ) ;
make_file ( driverpath , " remove_id " , NULL , - 1 ) ;
2013-10-23 19:55:02 +04:00
2014-03-13 15:59:32 +04:00
if ( VIR_APPEND_ELEMENT_QUIET ( pciDrivers , nPCIDrivers , driver ) < 0 )
2013-10-23 19:55:02 +04:00
ABORT_OOM ( ) ;
2017-07-15 06:35:36 +03:00
VIR_FREE ( driverpath ) ;
2013-10-23 19:55:02 +04:00
}
static struct pciDriver *
pci_driver_find_by_dev ( struct pciDevice * dev )
{
size_t i ;
2014-03-13 15:59:32 +04:00
for ( i = 0 ; i < nPCIDrivers ; i + + ) {
2013-10-23 19:55:02 +04:00
struct pciDriver * driver = pciDrivers [ i ] ;
size_t j ;
for ( j = 0 ; j < driver - > len ; j + + ) {
if ( driver - > vendor [ j ] = = dev - > vendor & &
driver - > device [ j ] = = dev - > device )
return driver ;
}
}
return NULL ;
}
static struct pciDriver *
pci_driver_find_by_path ( const char * path )
{
size_t i ;
2014-03-13 15:59:32 +04:00
for ( i = 0 ; i < nPCIDrivers ; i + + ) {
2013-10-23 19:55:02 +04:00
struct pciDriver * driver = pciDrivers [ i ] ;
if ( strstr ( path , driver - > name ) )
return driver ;
}
return NULL ;
}
static int
pci_driver_bind ( struct pciDriver * driver ,
struct pciDevice * dev )
{
int ret = - 1 ;
char * devpath = NULL , * driverpath = NULL ;
2014-01-16 17:05:19 +04:00
if ( dev - > driver | | PCI_ACTION_BIND & driver - > fail ) {
/* Device already bound or failing driver requested */
2013-10-23 19:55:02 +04:00
errno = ENODEV ;
return ret ;
}
/* Make symlink under device tree */
if ( virAsprintfQuiet ( & devpath , " %s/devices/%s/driver " ,
2015-12-04 16:51:08 +03:00
fakesysfspcidir , dev - > id ) < 0 | |
2013-10-23 19:55:02 +04:00
virAsprintfQuiet ( & driverpath , " %s/drivers/%s " ,
2015-12-04 16:51:08 +03:00
fakesysfspcidir , driver - > name ) < 0 ) {
2013-10-23 19:55:02 +04:00
errno = ENOMEM ;
goto cleanup ;
}
if ( symlink ( driverpath , devpath ) < 0 )
goto cleanup ;
/* Make symlink under driver tree */
VIR_FREE ( devpath ) ;
VIR_FREE ( driverpath ) ;
if ( virAsprintfQuiet ( & devpath , " %s/devices/%s " ,
2015-12-04 16:51:08 +03:00
fakesysfspcidir , dev - > id ) < 0 | |
2013-10-23 19:55:02 +04:00
virAsprintfQuiet ( & driverpath , " %s/drivers/%s/%s " ,
2015-12-04 16:51:08 +03:00
fakesysfspcidir , driver - > name , dev - > id ) < 0 ) {
2013-10-23 19:55:02 +04:00
errno = ENOMEM ;
goto cleanup ;
}
if ( symlink ( devpath , driverpath ) < 0 )
goto cleanup ;
dev - > driver = driver ;
ret = 0 ;
2014-03-25 10:53:44 +04:00
cleanup :
2013-10-23 19:55:02 +04:00
VIR_FREE ( devpath ) ;
VIR_FREE ( driverpath ) ;
return ret ;
}
static int
pci_driver_unbind ( struct pciDriver * driver ,
struct pciDevice * dev )
{
int ret = - 1 ;
char * devpath = NULL , * driverpath = NULL ;
2014-01-16 17:05:19 +04:00
if ( dev - > driver ! = driver | | PCI_ACTION_UNBIND & driver - > fail ) {
/* Device not bound to the @driver or failing driver used */
2013-10-23 19:55:02 +04:00
errno = ENODEV ;
return ret ;
}
/* Make symlink under device tree */
if ( virAsprintfQuiet ( & devpath , " %s/devices/%s/driver " ,
2015-12-04 16:51:08 +03:00
fakesysfspcidir , dev - > id ) < 0 | |
2013-10-23 19:55:02 +04:00
virAsprintfQuiet ( & driverpath , " %s/drivers/%s/%s " ,
2015-12-04 16:51:08 +03:00
fakesysfspcidir , driver - > name , dev - > id ) < 0 ) {
2013-10-23 19:55:02 +04:00
errno = ENOMEM ;
goto cleanup ;
}
if ( unlink ( devpath ) < 0 | |
unlink ( driverpath ) < 0 )
goto cleanup ;
dev - > driver = NULL ;
ret = 0 ;
2014-03-25 10:53:44 +04:00
cleanup :
2013-10-23 19:55:02 +04:00
VIR_FREE ( devpath ) ;
VIR_FREE ( driverpath ) ;
return ret ;
}
2014-01-16 15:28:12 +04:00
static int
pci_driver_handle_drivers_probe ( const char * path )
{
struct pciDevice * dev ;
if ( ! ( dev = pci_device_find_by_content ( path ) ) ) {
errno = ENODEV ;
return - 1 ;
}
if ( dev - > driver )
return 0 ;
return pci_device_autobind ( dev ) ;
}
2013-10-23 19:55:02 +04:00
static int
pci_driver_handle_change ( int fd ATTRIBUTE_UNUSED , const char * path )
{
int ret ;
const char * file = last_component ( path ) ;
2014-01-16 15:28:12 +04:00
if ( STREQ ( file , " bind " ) )
2013-10-23 19:55:02 +04:00
ret = pci_driver_handle_bind ( path ) ;
2014-01-16 15:28:12 +04:00
else if ( STREQ ( file , " unbind " ) )
2013-10-23 19:55:02 +04:00
ret = pci_driver_handle_unbind ( path ) ;
2014-01-16 15:28:12 +04:00
else if ( STREQ ( file , " new_id " ) )
2013-10-23 19:55:02 +04:00
ret = pci_driver_handle_new_id ( path ) ;
2014-01-16 15:28:12 +04:00
else if ( STREQ ( file , " remove_id " ) )
2013-10-23 19:55:02 +04:00
ret = pci_driver_handle_remove_id ( path ) ;
2014-01-16 15:28:12 +04:00
else if ( STREQ ( file , " drivers_probe " ) )
ret = pci_driver_handle_drivers_probe ( path ) ;
else
2013-10-23 19:55:02 +04:00
ABORT ( " Not handled write to: %s " , path ) ;
return ret ;
}
static int
pci_driver_handle_bind ( const char * path )
{
int ret = - 1 ;
struct pciDevice * dev = pci_device_find_by_content ( path ) ;
struct pciDriver * driver = pci_driver_find_by_path ( path ) ;
if ( ! driver | | ! dev ) {
/* This should never happen (TM) */
errno = ENODEV ;
goto cleanup ;
}
ret = pci_driver_bind ( driver , dev ) ;
2014-03-25 10:53:44 +04:00
cleanup :
2013-10-23 19:55:02 +04:00
return ret ;
}
static int
pci_driver_handle_unbind ( const char * path )
{
int ret = - 1 ;
struct pciDevice * dev = pci_device_find_by_content ( path ) ;
if ( ! dev | | ! dev - > driver ) {
/* This should never happen (TM) */
errno = ENODEV ;
goto cleanup ;
}
ret = pci_driver_unbind ( dev - > driver , dev ) ;
2014-03-25 10:53:44 +04:00
cleanup :
2013-10-23 19:55:02 +04:00
return ret ;
}
static int
pci_driver_handle_new_id ( const char * path )
{
int ret = - 1 ;
struct pciDriver * driver = pci_driver_find_by_path ( path ) ;
size_t i ;
int vendor , device ;
char buf [ 32 ] ;
2014-01-16 17:05:19 +04:00
if ( ! driver | | PCI_ACTION_NEW_ID & driver - > fail ) {
2013-10-23 19:55:02 +04:00
errno = ENODEV ;
goto cleanup ;
}
if ( pci_read_file ( path , buf , sizeof ( buf ) ) < 0 )
goto cleanup ;
if ( sscanf ( buf , " %x %x " , & vendor , & device ) < 2 ) {
errno = EINVAL ;
goto cleanup ;
}
for ( i = 0 ; i < driver - > len ; i + + ) {
if ( driver - > vendor [ i ] = = vendor & &
driver - > device [ i ] = = device )
break ;
}
if ( i = = driver - > len ) {
if ( VIR_REALLOC_N_QUIET ( driver - > vendor , driver - > len + 1 ) < 0 | |
VIR_REALLOC_N_QUIET ( driver - > device , driver - > len + 1 ) < 0 ) {
errno = ENOMEM ;
goto cleanup ;
}
driver - > vendor [ driver - > len ] = vendor ;
driver - > device [ driver - > len ] = device ;
driver - > len + + ;
}
2014-03-13 15:59:32 +04:00
for ( i = 0 ; i < nPCIDevices ; i + + ) {
2013-10-23 19:55:02 +04:00
struct pciDevice * dev = pciDevices [ i ] ;
if ( ! dev - > driver & &
dev - > vendor = = vendor & &
dev - > device = = device & &
pci_driver_bind ( driver , dev ) < 0 )
goto cleanup ;
}
ret = 0 ;
2014-03-25 10:53:44 +04:00
cleanup :
2013-10-23 19:55:02 +04:00
return ret ;
}
static int
pci_driver_handle_remove_id ( const char * path )
{
int ret = - 1 ;
struct pciDriver * driver = pci_driver_find_by_path ( path ) ;
size_t i ;
int vendor , device ;
char buf [ 32 ] ;
2014-01-16 17:05:19 +04:00
if ( ! driver | | PCI_ACTION_REMOVE_ID & driver - > fail ) {
2013-10-23 19:55:02 +04:00
errno = ENODEV ;
goto cleanup ;
}
if ( pci_read_file ( path , buf , sizeof ( buf ) ) < 0 )
goto cleanup ;
if ( sscanf ( buf , " %x %x " , & vendor , & device ) < 2 ) {
errno = EINVAL ;
goto cleanup ;
}
for ( i = 0 ; i < driver - > len ; i + + ) {
if ( driver - > vendor [ i ] = = vendor & &
driver - > device [ i ] = = device )
continue ;
}
if ( i ! = driver - > len ) {
if ( VIR_DELETE_ELEMENT ( driver - > vendor , i , driver - > len ) < 0 )
goto cleanup ;
driver - > len + + ;
if ( VIR_DELETE_ELEMENT ( driver - > device , i , driver - > len ) < 0 )
goto cleanup ;
}
ret = 0 ;
2014-03-25 10:53:44 +04:00
cleanup :
2013-10-23 19:55:02 +04:00
return ret ;
}
2013-10-23 17:44:40 +04:00
/*
* Functions to load the symbols and init the environment
*/
static void
init_syms ( void )
{
2016-05-13 13:32:00 +03:00
if ( real_access )
2013-10-23 17:44:40 +04:00
return ;
2016-05-13 13:32:00 +03:00
VIR_MOCK_REAL_INIT ( access ) ;
VIR_MOCK_REAL_INIT_ALT ( lstat , __lxstat ) ;
VIR_MOCK_REAL_INIT_ALT ( stat , __xstat ) ;
VIR_MOCK_REAL_INIT ( open ) ;
VIR_MOCK_REAL_INIT ( close ) ;
VIR_MOCK_REAL_INIT ( opendir ) ;
2018-05-03 11:01:04 +03:00
VIR_MOCK_REAL_INIT ( virFileCanonicalizePath ) ;
2013-10-23 17:44:40 +04:00
}
static void
init_env ( void )
{
2015-12-04 16:51:08 +03:00
if ( fakerootdir & & fakesysfspcidir )
2013-10-23 17:44:40 +04:00
return ;
2015-12-04 15:38:16 +03:00
if ( ! ( fakerootdir = getenv ( " LIBVIRT_FAKE_ROOT_DIR " ) ) )
ABORT ( " Missing LIBVIRT_FAKE_ROOT_DIR env variable \n " ) ;
2013-10-23 17:44:40 +04:00
2015-12-04 16:51:08 +03:00
if ( virAsprintfQuiet ( & fakesysfspcidir , " %s%s " ,
fakerootdir , SYSFS_PCI_PREFIX ) < 0 )
2015-12-04 12:05:56 +03:00
ABORT_OOM ( ) ;
2015-12-04 16:51:08 +03:00
if ( virFileMakePath ( fakesysfspcidir ) < 0 )
ABORT ( " Unable to create: %s " , fakesysfspcidir ) ;
2015-12-04 12:05:56 +03:00
2015-12-04 16:51:08 +03:00
make_file ( fakesysfspcidir , " drivers_probe " , NULL , - 1 ) ;
2014-01-16 15:28:12 +04:00
2017-11-03 15:09:47 +03:00
# define MAKE_PCI_DRIVER(name, ...) \
2014-01-16 17:05:19 +04:00
pci_driver_new ( name , 0 , __VA_ARGS__ , - 1 , - 1 )
2013-10-23 19:55:02 +04:00
MAKE_PCI_DRIVER ( " iwlwifi " , 0x8086 , 0x0044 ) ;
MAKE_PCI_DRIVER ( " i915 " , 0x8086 , 0x0046 , 0x8086 , 0x0047 ) ;
MAKE_PCI_DRIVER ( " pci-stub " , - 1 , - 1 ) ;
2014-01-16 17:05:19 +04:00
pci_driver_new ( " vfio-pci " , PCI_ACTION_BIND , - 1 , - 1 ) ;
2013-10-23 19:55:02 +04:00
2017-11-03 15:09:47 +03:00
# define MAKE_PCI_DEVICE(Id, Vendor, Device, ...) \
do { \
struct pciDevice dev = { . id = ( char * ) Id , . vendor = Vendor , \
. device = Device , __VA_ARGS__ } ; \
pci_device_new_from_stub ( & dev ) ; \
2013-10-23 17:44:40 +04:00
} while ( 0 )
MAKE_PCI_DEVICE ( " 0000:00:00.0 " , 0x8086 , 0x0044 ) ;
2013-10-23 19:55:02 +04:00
MAKE_PCI_DEVICE ( " 0000:00:01.0 " , 0x8086 , 0x0044 ) ;
MAKE_PCI_DEVICE ( " 0000:00:02.0 " , 0x8086 , 0x0046 ) ;
MAKE_PCI_DEVICE ( " 0000:00:03.0 " , 0x8086 , 0x0048 ) ;
2019-03-12 11:08:00 +03:00
MAKE_PCI_DEVICE ( " 0001:00:00.0 " , 0x1014 , 0x03b9 , . klass = 0x060400 ) ;
2017-06-01 19:48:52 +03:00
MAKE_PCI_DEVICE ( " 0001:01:00.0 " , 0x8086 , 0x105e , . iommuGroup = 0 ) ;
MAKE_PCI_DEVICE ( " 0001:01:00.1 " , 0x8086 , 0x105e , . iommuGroup = 0 ) ;
2019-03-12 11:08:00 +03:00
MAKE_PCI_DEVICE ( " 0005:80:00.0 " , 0x10b5 , 0x8112 , . klass = 0x060400 ) ;
2017-06-01 19:48:52 +03:00
MAKE_PCI_DEVICE ( " 0005:90:01.0 " , 0x1033 , 0x0035 , . iommuGroup = 1 ) ;
MAKE_PCI_DEVICE ( " 0005:90:01.1 " , 0x1033 , 0x0035 , . iommuGroup = 1 ) ;
MAKE_PCI_DEVICE ( " 0005:90:01.2 " , 0x1033 , 0x00e0 , . iommuGroup = 1 ) ;
2014-01-15 13:20:55 +04:00
MAKE_PCI_DEVICE ( " 0000:0a:01.0 " , 0x8086 , 0x0047 ) ;
MAKE_PCI_DEVICE ( " 0000:0a:02.0 " , 0x8286 , 0x0048 ) ;
MAKE_PCI_DEVICE ( " 0000:0a:03.0 " , 0x8386 , 0x0048 ) ;
2013-10-23 17:44:40 +04:00
}
/*
*
* Mocked functions
*
*/
int
access ( const char * path , int mode )
{
int ret ;
init_syms ( ) ;
2015-12-04 16:51:08 +03:00
if ( STRPREFIX ( path , SYSFS_PCI_PREFIX ) ) {
2013-10-23 17:44:40 +04:00
char * newpath ;
if ( getrealpath ( & newpath , path ) < 0 )
return - 1 ;
2016-05-13 13:32:00 +03:00
ret = real_access ( newpath , mode ) ;
2013-10-23 17:44:40 +04:00
VIR_FREE ( newpath ) ;
} else {
2016-05-13 13:32:00 +03:00
ret = real_access ( path , mode ) ;
2013-10-23 17:44:40 +04:00
}
return ret ;
}
2018-04-30 17:04:39 +03:00
# ifdef HAVE___LXSTAT
2013-10-23 17:44:40 +04:00
int
__lxstat ( int ver , const char * path , struct stat * sb )
{
int ret ;
init_syms ( ) ;
2015-12-04 16:51:08 +03:00
if ( STRPREFIX ( path , SYSFS_PCI_PREFIX ) ) {
2013-10-23 17:44:40 +04:00
char * newpath ;
if ( getrealpath ( & newpath , path ) < 0 )
return - 1 ;
2016-05-16 18:15:46 +03:00
ret = real___lxstat ( ver , newpath , sb ) ;
2013-10-23 17:44:40 +04:00
VIR_FREE ( newpath ) ;
} else {
2016-05-16 18:15:46 +03:00
ret = real___lxstat ( ver , path , sb ) ;
2013-10-23 17:44:40 +04:00
}
return ret ;
}
2018-04-30 17:04:39 +03:00
# endif /* HAVE___LXSTAT */
2013-10-23 17:44:40 +04:00
int
lstat ( const char * path , struct stat * sb )
{
int ret ;
init_syms ( ) ;
2015-12-04 16:51:08 +03:00
if ( STRPREFIX ( path , SYSFS_PCI_PREFIX ) ) {
2013-10-23 17:44:40 +04:00
char * newpath ;
if ( getrealpath ( & newpath , path ) < 0 )
return - 1 ;
2016-05-16 18:15:46 +03:00
ret = real_lstat ( newpath , sb ) ;
2013-10-23 17:44:40 +04:00
VIR_FREE ( newpath ) ;
} else {
2016-05-16 18:15:46 +03:00
ret = real_lstat ( path , sb ) ;
2013-10-23 17:44:40 +04:00
}
return ret ;
}
2018-04-30 17:04:39 +03:00
# ifdef HAVE___XSTAT
2013-10-23 19:55:02 +04:00
int
__xstat ( int ver , const char * path , struct stat * sb )
{
int ret ;
init_syms ( ) ;
2015-12-04 16:51:08 +03:00
if ( STRPREFIX ( path , SYSFS_PCI_PREFIX ) ) {
2013-10-23 19:55:02 +04:00
char * newpath ;
if ( getrealpath ( & newpath , path ) < 0 )
return - 1 ;
2016-05-16 18:15:46 +03:00
ret = real___xstat ( ver , newpath , sb ) ;
2013-10-23 19:55:02 +04:00
VIR_FREE ( newpath ) ;
} else {
2016-05-16 18:15:46 +03:00
ret = real___xstat ( ver , path , sb ) ;
2013-10-23 19:55:02 +04:00
}
return ret ;
}
2018-04-30 17:04:39 +03:00
# endif /* HAVE___XSTAT */
2013-10-23 19:55:02 +04:00
int
stat ( const char * path , struct stat * sb )
{
int ret ;
init_syms ( ) ;
2015-12-04 16:51:08 +03:00
if ( STRPREFIX ( path , SYSFS_PCI_PREFIX ) ) {
2013-10-23 19:55:02 +04:00
char * newpath ;
if ( getrealpath ( & newpath , path ) < 0 )
return - 1 ;
2016-05-16 18:15:46 +03:00
ret = real_stat ( newpath , sb ) ;
2013-10-23 19:55:02 +04:00
VIR_FREE ( newpath ) ;
} else {
2016-05-16 18:15:46 +03:00
ret = real_stat ( path , sb ) ;
2013-10-23 19:55:02 +04:00
}
return ret ;
}
2013-10-23 17:44:40 +04:00
int
open ( const char * path , int flags , . . . )
{
int ret ;
char * newpath = NULL ;
init_syms ( ) ;
2015-12-04 16:51:08 +03:00
if ( STRPREFIX ( path , SYSFS_PCI_PREFIX ) & &
2013-10-23 17:44:40 +04:00
getrealpath ( & newpath , path ) < 0 )
return - 1 ;
if ( flags & O_CREAT ) {
va_list ap ;
mode_t mode ;
va_start ( ap , flags ) ;
2018-04-30 18:30:12 +03:00
mode = ( mode_t ) va_arg ( ap , int ) ;
2013-10-23 17:44:40 +04:00
va_end ( ap ) ;
2016-05-13 13:32:00 +03:00
ret = real_open ( newpath ? newpath : path , flags , mode ) ;
2013-10-23 17:44:40 +04:00
} else {
2016-05-13 13:32:00 +03:00
ret = real_open ( newpath ? newpath : path , flags ) ;
2013-10-23 17:44:40 +04:00
}
2013-10-23 19:55:02 +04:00
/* Catch both: /sys/bus/pci/drivers/... and
* / sys / bus / pci / device / . . . / driver / . . . */
2015-12-04 16:51:08 +03:00
if ( ret > = 0 & & STRPREFIX ( path , SYSFS_PCI_PREFIX ) & &
2013-10-23 19:55:02 +04:00
strstr ( path , " driver " ) & & add_fd ( ret , path ) < 0 ) {
2016-05-13 13:32:00 +03:00
real_close ( ret ) ;
2013-10-23 19:55:02 +04:00
ret = - 1 ;
}
2013-10-23 17:44:40 +04:00
VIR_FREE ( newpath ) ;
return ret ;
}
2013-12-24 22:07:27 +04:00
DIR *
opendir ( const char * path )
{
DIR * ret ;
char * newpath = NULL ;
init_syms ( ) ;
2015-12-04 16:51:08 +03:00
if ( STRPREFIX ( path , SYSFS_PCI_PREFIX ) & &
2013-12-24 22:07:27 +04:00
getrealpath ( & newpath , path ) < 0 )
return NULL ;
2016-05-13 13:32:00 +03:00
ret = real_opendir ( newpath ? newpath : path ) ;
2013-12-24 22:07:27 +04:00
VIR_FREE ( newpath ) ;
return ret ;
}
2013-10-23 19:55:02 +04:00
int
close ( int fd )
{
if ( remove_fd ( fd ) < 0 )
return - 1 ;
2016-05-13 13:32:00 +03:00
return real_close ( fd ) ;
2013-10-23 19:55:02 +04:00
}
2018-05-03 11:01:04 +03:00
char *
virFileCanonicalizePath ( const char * path )
{
char * ret ;
init_syms ( ) ;
if ( STRPREFIX ( path , SYSFS_PCI_PREFIX ) ) {
char * newpath ;
if ( getrealpath ( & newpath , path ) < 0 )
return NULL ;
ret = real_virFileCanonicalizePath ( newpath ) ;
VIR_FREE ( newpath ) ;
} else {
ret = real_virFileCanonicalizePath ( path ) ;
}
return ret ;
}
2013-10-23 17:44:40 +04:00
# else
2018-04-30 17:04:39 +03:00
/* Nothing to override on this platform */
2013-10-23 17:44:40 +04:00
# endif