2019-05-30 02:57:35 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2010-10-27 20:55:04 +04:00
/*
* Copyright ( c ) 2009 , Intel Corporation .
*
* Author : Weidong Han < weidong . han @ intel . com >
*/
# include <linux/pci.h>
2011-09-22 12:17:57 +04:00
# include <linux/acpi.h>
2015-04-09 10:05:10 +03:00
# include <linux/pci-acpi.h>
2010-10-27 20:55:04 +04:00
# include <xen/xen.h>
# include <xen/interface/physdev.h>
# include <xen/interface/xen.h>
# include <asm/xen/hypervisor.h>
# include <asm/xen/hypercall.h>
# include "../pci/pci.h"
2013-12-31 23:46:27 +04:00
# ifdef CONFIG_PCI_MMCONFIG
2013-10-26 00:26:02 +04:00
# include <asm/pci_x86.h>
2019-09-12 21:31:51 +03:00
static int xen_mcfg_late ( void ) ;
2013-12-31 23:46:27 +04:00
# endif
2010-10-27 20:55:04 +04:00
2011-09-22 12:17:57 +04:00
static bool __read_mostly pci_seg_supported = true ;
2010-10-27 20:55:04 +04:00
static int xen_add_device ( struct device * dev )
{
int r ;
struct pci_dev * pci_dev = to_pci_dev ( dev ) ;
2011-09-22 12:17:57 +04:00
# ifdef CONFIG_PCI_IOV
struct pci_dev * physfn = pci_dev - > physfn ;
# endif
2019-09-12 21:31:51 +03:00
# ifdef CONFIG_PCI_MMCONFIG
static bool pci_mcfg_reserved = false ;
/*
* Reserve MCFG areas in Xen on first invocation due to this being
* potentially called from inside of acpi_init immediately after
* MCFG table has been finally parsed .
*/
if ( ! pci_mcfg_reserved ) {
xen_mcfg_late ( ) ;
pci_mcfg_reserved = true ;
}
# endif
2011-09-22 12:17:57 +04:00
if ( pci_seg_supported ) {
2014-08-05 02:17:23 +04:00
struct {
struct physdev_pci_device_add add ;
uint32_t pxm ;
} add_ext = {
. add . seg = pci_domain_nr ( pci_dev - > bus ) ,
. add . bus = pci_dev - > bus - > number ,
. add . devfn = pci_dev - > devfn
2011-09-22 12:17:57 +04:00
} ;
2014-08-05 02:17:23 +04:00
struct physdev_pci_device_add * add = & add_ext . add ;
2011-09-22 12:17:57 +04:00
# ifdef CONFIG_ACPI
acpi_handle handle ;
# endif
2010-10-27 20:55:04 +04:00
# ifdef CONFIG_PCI_IOV
2011-09-22 12:17:57 +04:00
if ( pci_dev - > is_virtfn ) {
2014-08-05 02:17:23 +04:00
add - > flags = XEN_PCI_DEV_VIRTFN ;
add - > physfn . bus = physfn - > bus - > number ;
add - > physfn . devfn = physfn - > devfn ;
2011-09-22 12:17:57 +04:00
} else
# endif
if ( pci_ari_enabled ( pci_dev - > bus ) & & PCI_SLOT ( pci_dev - > devfn ) )
2014-08-05 02:17:23 +04:00
add - > flags = XEN_PCI_DEV_EXTFN ;
2011-09-22 12:17:57 +04:00
# ifdef CONFIG_ACPI
2013-11-15 02:17:21 +04:00
handle = ACPI_HANDLE ( & pci_dev - > dev ) ;
2011-09-22 12:17:57 +04:00
# ifdef CONFIG_PCI_IOV
if ( ! handle & & pci_dev - > is_virtfn )
2013-11-15 02:17:21 +04:00
handle = ACPI_HANDLE ( physfn - > bus - > bridge ) ;
2011-09-22 12:17:57 +04:00
# endif
2015-04-09 10:05:10 +03:00
if ( ! handle ) {
/*
* This device was not listed in the ACPI name space at
* all . Try to get acpi handle of parent pci bus .
*/
struct pci_bus * pbus ;
for ( pbus = pci_dev - > bus ; pbus ; pbus = pbus - > parent ) {
handle = acpi_pci_get_bridge_handle ( pbus ) ;
if ( handle )
break ;
}
}
2011-09-22 12:17:57 +04:00
if ( handle ) {
acpi_status status ;
do {
unsigned long long pxm ;
status = acpi_evaluate_integer ( handle , " _PXM " ,
NULL , & pxm ) ;
if ( ACPI_SUCCESS ( status ) ) {
2014-08-05 02:17:23 +04:00
add - > optarr [ 0 ] = pxm ;
add - > flags | = XEN_PCI_DEV_PXM ;
2011-09-22 12:17:57 +04:00
break ;
}
status = acpi_get_parent ( handle , & handle ) ;
} while ( ACPI_SUCCESS ( status ) ) ;
}
# endif /* CONFIG_ACPI */
2014-08-05 02:17:23 +04:00
r = HYPERVISOR_physdev_op ( PHYSDEVOP_pci_device_add , add ) ;
2011-09-22 12:17:57 +04:00
if ( r ! = - ENOSYS )
return r ;
pci_seg_supported = false ;
}
if ( pci_domain_nr ( pci_dev - > bus ) )
r = - ENOSYS ;
# ifdef CONFIG_PCI_IOV
else if ( pci_dev - > is_virtfn ) {
2010-10-27 20:55:04 +04:00
struct physdev_manage_pci_ext manage_pci_ext = {
. bus = pci_dev - > bus - > number ,
. devfn = pci_dev - > devfn ,
. is_virtfn = 1 ,
2011-09-22 12:17:57 +04:00
. physfn . bus = physfn - > bus - > number ,
. physfn . devfn = physfn - > devfn ,
2010-10-27 20:55:04 +04:00
} ;
r = HYPERVISOR_physdev_op ( PHYSDEVOP_manage_pci_add_ext ,
& manage_pci_ext ) ;
2011-09-22 12:17:57 +04:00
}
2010-10-27 20:55:04 +04:00
# endif
2011-09-22 12:17:57 +04:00
else if ( pci_ari_enabled ( pci_dev - > bus ) & & PCI_SLOT ( pci_dev - > devfn ) ) {
2010-10-27 20:55:04 +04:00
struct physdev_manage_pci_ext manage_pci_ext = {
. bus = pci_dev - > bus - > number ,
. devfn = pci_dev - > devfn ,
. is_extfn = 1 ,
} ;
r = HYPERVISOR_physdev_op ( PHYSDEVOP_manage_pci_add_ext ,
& manage_pci_ext ) ;
} else {
struct physdev_manage_pci manage_pci = {
2011-07-26 15:16:38 +04:00
. bus = pci_dev - > bus - > number ,
2010-10-27 20:55:04 +04:00
. devfn = pci_dev - > devfn ,
} ;
r = HYPERVISOR_physdev_op ( PHYSDEVOP_manage_pci_add ,
& manage_pci ) ;
}
return r ;
}
static int xen_remove_device ( struct device * dev )
{
int r ;
struct pci_dev * pci_dev = to_pci_dev ( dev ) ;
2011-09-22 12:17:57 +04:00
if ( pci_seg_supported ) {
struct physdev_pci_device device = {
. seg = pci_domain_nr ( pci_dev - > bus ) ,
. bus = pci_dev - > bus - > number ,
. devfn = pci_dev - > devfn
} ;
2010-10-27 20:55:04 +04:00
2011-09-22 12:17:57 +04:00
r = HYPERVISOR_physdev_op ( PHYSDEVOP_pci_device_remove ,
& device ) ;
} else if ( pci_domain_nr ( pci_dev - > bus ) )
r = - ENOSYS ;
else {
struct physdev_manage_pci manage_pci = {
. bus = pci_dev - > bus - > number ,
. devfn = pci_dev - > devfn
} ;
r = HYPERVISOR_physdev_op ( PHYSDEVOP_manage_pci_remove ,
& manage_pci ) ;
}
2010-10-27 20:55:04 +04:00
return r ;
}
static int xen_pci_notifier ( struct notifier_block * nb ,
unsigned long action , void * data )
{
struct device * dev = data ;
int r = 0 ;
switch ( action ) {
case BUS_NOTIFY_ADD_DEVICE :
r = xen_add_device ( dev ) ;
break ;
case BUS_NOTIFY_DEL_DEVICE :
r = xen_remove_device ( dev ) ;
break ;
default :
2011-08-17 12:32:32 +04:00
return NOTIFY_DONE ;
2010-10-27 20:55:04 +04:00
}
2011-08-17 12:32:32 +04:00
if ( r )
dev_err ( dev , " Failed to %s - passthrough or MSI/MSI-X might fail! \n " ,
action = = BUS_NOTIFY_ADD_DEVICE ? " add " :
( action = = BUS_NOTIFY_DEL_DEVICE ? " delete " : " ? " ) ) ;
return NOTIFY_OK ;
2010-10-27 20:55:04 +04:00
}
2011-08-17 12:32:32 +04:00
static struct notifier_block device_nb = {
2010-10-27 20:55:04 +04:00
. notifier_call = xen_pci_notifier ,
} ;
static int __init register_xen_pci_notifier ( void )
{
if ( ! xen_initial_domain ( ) )
return 0 ;
return bus_register_notifier ( & pci_bus_type , & device_nb ) ;
}
arch_initcall ( register_xen_pci_notifier ) ;
2013-10-26 00:26:02 +04:00
# ifdef CONFIG_PCI_MMCONFIG
2019-09-12 21:31:51 +03:00
static int xen_mcfg_late ( void )
2013-10-26 00:26:02 +04:00
{
struct pci_mmcfg_region * cfg ;
int rc ;
if ( ! xen_initial_domain ( ) )
return 0 ;
if ( ( pci_probe & PCI_PROBE_MMCONF ) = = 0 )
return 0 ;
if ( list_empty ( & pci_mmcfg_list ) )
return 0 ;
/* Check whether they are in the right area. */
list_for_each_entry ( cfg , & pci_mmcfg_list , list ) {
struct physdev_pci_mmcfg_reserved r ;
r . address = cfg - > address ;
r . segment = cfg - > segment ;
r . start_bus = cfg - > start_bus ;
r . end_bus = cfg - > end_bus ;
r . flags = XEN_PCI_MMCFG_RESERVED ;
rc = HYPERVISOR_physdev_op ( PHYSDEVOP_pci_mmcfg_reserved , & r ) ;
switch ( rc ) {
case 0 :
case - ENOSYS :
continue ;
default :
pr_warn ( " Failed to report MMCONFIG reservation "
" state for %s to hypervisor "
" (%d) \n " ,
cfg - > name , rc ) ;
}
}
return 0 ;
}
# endif