@ -15,11 +15,12 @@
# include "hw/pci/pcie.h"
# include "hw/pci/pcie.h"
# include "hw/pci/pci_bus.h"
# include "hw/pci/pci_bus.h"
# include "hw/qdev-properties.h"
# include "hw/qdev-properties.h"
# include "qemu/error-report.h"
# include "qemu/range.h"
# include "qemu/range.h"
# include "qapi/error.h"
# include "qapi/error.h"
# include "trace.h"
# include "trace.h"
static GHashTable * pfs ;
static void unparent_vfs ( PCIDevice * dev , uint16_t total_vfs )
static void unparent_vfs ( PCIDevice * dev , uint16_t total_vfs )
{
{
for ( uint16_t i = 0 ; i < total_vfs ; i + + ) {
for ( uint16_t i = 0 ; i < total_vfs ; i + + ) {
@ -31,13 +32,43 @@ static void unparent_vfs(PCIDevice *dev, uint16_t total_vfs)
dev - > exp . sriov_pf . vf = NULL ;
dev - > exp . sriov_pf . vf = NULL ;
}
}
bool pcie_sriov_pf_init ( PCIDevice * dev , uint16_t offset ,
static void register_vfs ( PCIDevice * dev )
const char * vfname , uint16_t vf_dev_id ,
{
uint16_t init_vfs , uint16_t total_vfs ,
uint16_t num_vfs ;
uint16_t vf_offset , uint16_t vf_stride ,
uint16_t i ;
Error * * errp )
uint16_t sriov_cap = dev - > exp . sriov_cap ;
assert ( sriov_cap > 0 ) ;
num_vfs = pci_get_word ( dev - > config + sriov_cap + PCI_SRIOV_NUM_VF ) ;
trace_sriov_register_vfs ( dev - > name , PCI_SLOT ( dev - > devfn ) ,
PCI_FUNC ( dev - > devfn ) , num_vfs ) ;
for ( i = 0 ; i < num_vfs ; i + + ) {
pci_set_enabled ( dev - > exp . sriov_pf . vf [ i ] , true ) ;
}
pci_set_word ( dev - > wmask + sriov_cap + PCI_SRIOV_NUM_VF , 0 ) ;
}
static void unregister_vfs ( PCIDevice * dev )
{
uint8_t * cfg = dev - > config + dev - > exp . sriov_cap ;
uint16_t i ;
trace_sriov_unregister_vfs ( dev - > name , PCI_SLOT ( dev - > devfn ) ,
PCI_FUNC ( dev - > devfn ) ) ;
for ( i = 0 ; i < pci_get_word ( cfg + PCI_SRIOV_TOTAL_VF ) ; i + + ) {
pci_set_enabled ( dev - > exp . sriov_pf . vf [ i ] , false ) ;
}
pci_set_word ( dev - > wmask + dev - > exp . sriov_cap + PCI_SRIOV_NUM_VF , 0xffff ) ;
}
static bool pcie_sriov_pf_init_common ( PCIDevice * dev , uint16_t offset ,
uint16_t vf_dev_id , uint16_t init_vfs ,
uint16_t total_vfs , uint16_t vf_offset ,
uint16_t vf_stride , Error * * errp )
{
{
BusState * bus = qdev_get_parent_bus ( & dev - > qdev ) ;
int32_t devfn = dev - > devfn + vf_offset ;
int32_t devfn = dev - > devfn + vf_offset ;
uint8_t * cfg = dev - > config + offset ;
uint8_t * cfg = dev - > config + offset ;
uint8_t * wmask ;
uint8_t * wmask ;
@ -94,6 +125,28 @@ bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset,
qdev_prop_set_bit ( & dev - > qdev , " multifunction " , true ) ;
qdev_prop_set_bit ( & dev - > qdev , " multifunction " , true ) ;
return true ;
}
bool pcie_sriov_pf_init ( PCIDevice * dev , uint16_t offset ,
const char * vfname , uint16_t vf_dev_id ,
uint16_t init_vfs , uint16_t total_vfs ,
uint16_t vf_offset , uint16_t vf_stride ,
Error * * errp )
{
BusState * bus = qdev_get_parent_bus ( & dev - > qdev ) ;
int32_t devfn = dev - > devfn + vf_offset ;
if ( pfs & & g_hash_table_contains ( pfs , dev - > qdev . id ) ) {
error_setg ( errp , " attaching user-created SR-IOV VF unsupported " ) ;
return false ;
}
if ( ! pcie_sriov_pf_init_common ( dev , offset , vf_dev_id , init_vfs ,
total_vfs , vf_offset , vf_stride , errp ) ) {
return false ;
}
dev - > exp . sriov_pf . vf = g_new ( PCIDevice * , total_vfs ) ;
dev - > exp . sriov_pf . vf = g_new ( PCIDevice * , total_vfs ) ;
for ( uint16_t i = 0 ; i < total_vfs ; i + + ) {
for ( uint16_t i = 0 ; i < total_vfs ; i + + ) {
@ -123,7 +176,22 @@ void pcie_sriov_pf_exit(PCIDevice *dev)
{
{
uint8_t * cfg = dev - > config + dev - > exp . sriov_cap ;
uint8_t * cfg = dev - > config + dev - > exp . sriov_cap ;
unparent_vfs ( dev , pci_get_word ( cfg + PCI_SRIOV_TOTAL_VF ) ) ;
if ( dev - > exp . sriov_pf . vf_user_created ) {
uint16_t ven_id = pci_get_word ( dev - > config + PCI_VENDOR_ID ) ;
uint16_t total_vfs = pci_get_word ( dev - > config + PCI_SRIOV_TOTAL_VF ) ;
uint16_t vf_dev_id = pci_get_word ( dev - > config + PCI_SRIOV_VF_DID ) ;
unregister_vfs ( dev ) ;
for ( uint16_t i = 0 ; i < total_vfs ; i + + ) {
dev - > exp . sriov_pf . vf [ i ] - > exp . sriov_vf . pf = NULL ;
pci_config_set_vendor_id ( dev - > exp . sriov_pf . vf [ i ] - > config , ven_id ) ;
pci_config_set_device_id ( dev - > exp . sriov_pf . vf [ i ] - > config , vf_dev_id ) ;
}
} else {
unparent_vfs ( dev , pci_get_word ( cfg + PCI_SRIOV_TOTAL_VF ) ) ;
}
}
}
void pcie_sriov_pf_init_vf_bar ( PCIDevice * dev , int region_num ,
void pcie_sriov_pf_init_vf_bar ( PCIDevice * dev , int region_num ,
@ -156,69 +224,173 @@ void pcie_sriov_pf_init_vf_bar(PCIDevice *dev, int region_num,
void pcie_sriov_vf_register_bar ( PCIDevice * dev , int region_num ,
void pcie_sriov_vf_register_bar ( PCIDevice * dev , int region_num ,
MemoryRegion * memory )
MemoryRegion * memory )
{
{
PCIIORegion * r ;
PCIBus * bus = pci_get_bus ( dev ) ;
uint8_t type ;
uint8_t type ;
pcibus_t size = memory_region_size ( memory ) ;
assert ( pci_is_vf ( dev ) ) ; /* PFs must use pci_register_bar */
assert ( dev - > exp . sriov_vf . pf ) ;
assert ( region_num > = 0 ) ;
assert ( region_num < PCI_NUM_REGIONS ) ;
type = dev - > exp . sriov_vf . pf - > exp . sriov_pf . vf_bar_type [ region_num ] ;
type = dev - > exp . sriov_vf . pf - > exp . sriov_pf . vf_bar_type [ region_num ] ;
if ( ! is_power_of_2 ( size ) ) {
return pci_register_bar ( dev , region_num , type , memory ) ;
error_report ( " %s: PCI region size must be a power "
}
" of two - type=0x%x, size=0x% " FMT_PCIBUS ,
__func__ , type , size ) ;
exit ( 1 ) ;
}
r = & dev - > io_regions [ region_num ] ;
static gint compare_vf_devfns ( gconstpointer a , gconstpointer b )
r - > memory = memory ;
{
r - > address_space =
return ( * ( PCIDevice * * ) a ) - > devfn - ( * ( PCIDevice * * ) b ) - > devfn ;
type & PCI_BASE_ADDRESS_SPACE_IO
? bus - > address_space_io
: bus - > address_space_mem ;
r - > size = size ;
r - > type = type ;
r - > addr = pci_bar_address ( dev , region_num , r - > type , r - > size ) ;
if ( r - > addr ! = PCI_BAR_UNMAPPED ) {
memory_region_add_subregion_overlap ( r - > address_space ,
r - > addr , r - > memory , 1 ) ;
}
}
}
static void register_vfs ( PCIDevice * dev )
int16_t pcie_sriov_pf_init_from_user_created_vfs ( PCIDevice * dev ,
uint16_t offset ,
Error * * errp )
{
{
uint16_t num_vfs ;
GPtrArray * pf ;
PCIDevice * * vfs ;
BusState * bus = qdev_get_parent_bus ( DEVICE ( dev ) ) ;
uint16_t ven_id = pci_get_word ( dev - > config + PCI_VENDOR_ID ) ;
uint16_t vf_dev_id ;
uint16_t vf_offset ;
uint16_t vf_stride ;
uint16_t i ;
uint16_t i ;
uint16_t sriov_cap = dev - > exp . sriov_cap ;
assert ( sriov_cap > 0 ) ;
if ( ! pfs | | ! dev - > qdev . id ) {
num_vfs = pci_get_word ( dev - > config + sriov_cap + PCI_SRIOV_NUM_VF ) ;
return 0 ;
}
trace_sriov_register_vfs ( dev - > name , PCI_SLOT ( dev - > devfn ) ,
pf = g_hash_table_lookup ( pfs , dev - > qdev . id ) ;
PCI_FUNC ( dev - > devfn ) , num_vfs ) ;
if ( ! pf ) {
for ( i = 0 ; i < num_vfs ; i + + ) {
return 0 ;
pci_set_enabled ( dev - > exp . sriov_pf . vf [ i ] , true ) ;
}
}
pci_set_word ( dev - > wmask + sriov_cap + PCI_SRIOV_NUM_VF , 0 ) ;
if ( pf - > len > UINT16_MAX ) {
error_setg ( errp , " too many VFs " ) ;
return - 1 ;
}
g_ptr_array_sort ( pf , compare_vf_devfns ) ;
vfs = ( void * ) pf - > pdata ;
if ( vfs [ 0 ] - > devfn < = dev - > devfn ) {
error_setg ( errp , " a VF function number is less than the PF function number " ) ;
return - 1 ;
}
vf_dev_id = pci_get_word ( vfs [ 0 ] - > config + PCI_DEVICE_ID ) ;
vf_offset = vfs [ 0 ] - > devfn - dev - > devfn ;
vf_stride = pf - > len < 2 ? 0 : vfs [ 1 ] - > devfn - vfs [ 0 ] - > devfn ;
for ( i = 0 ; i < pf - > len ; i + + ) {
if ( bus ! = qdev_get_parent_bus ( & vfs [ i ] - > qdev ) ) {
error_setg ( errp , " SR-IOV VF parent bus mismatches with PF " ) ;
return - 1 ;
}
if ( ven_id ! = pci_get_word ( vfs [ i ] - > config + PCI_VENDOR_ID ) ) {
error_setg ( errp , " SR-IOV VF vendor ID mismatches with PF " ) ;
return - 1 ;
}
if ( vf_dev_id ! = pci_get_word ( vfs [ i ] - > config + PCI_DEVICE_ID ) ) {
error_setg ( errp , " inconsistent SR-IOV VF device IDs " ) ;
return - 1 ;
}
for ( size_t j = 0 ; j < PCI_NUM_REGIONS ; j + + ) {
if ( vfs [ i ] - > io_regions [ j ] . size ! = vfs [ 0 ] - > io_regions [ j ] . size | |
vfs [ i ] - > io_regions [ j ] . type ! = vfs [ 0 ] - > io_regions [ j ] . type ) {
error_setg ( errp , " inconsistent SR-IOV BARs " ) ;
return - 1 ;
}
}
if ( vfs [ i ] - > devfn - vfs [ 0 ] - > devfn ! = vf_stride * i ) {
error_setg ( errp , " inconsistent SR-IOV stride " ) ;
return - 1 ;
}
}
if ( ! pcie_sriov_pf_init_common ( dev , offset , vf_dev_id , pf - > len ,
pf - > len , vf_offset , vf_stride , errp ) ) {
return - 1 ;
}
for ( i = 0 ; i < pf - > len ; i + + ) {
vfs [ i ] - > exp . sriov_vf . pf = dev ;
vfs [ i ] - > exp . sriov_vf . vf_number = i ;
/* set vid/did according to sr/iov spec - they are not used */
pci_config_set_vendor_id ( vfs [ i ] - > config , 0xffff ) ;
pci_config_set_device_id ( vfs [ i ] - > config , 0xffff ) ;
}
dev - > exp . sriov_pf . vf = vfs ;
dev - > exp . sriov_pf . vf_user_created = true ;
for ( i = 0 ; i < PCI_NUM_REGIONS ; i + + ) {
PCIIORegion * region = & vfs [ 0 ] - > io_regions [ i ] ;
if ( region - > size ) {
pcie_sriov_pf_init_vf_bar ( dev , i , region - > type , region - > size ) ;
}
}
return PCI_EXT_CAP_SRIOV_SIZEOF ;
}
}
static void unregister_vfs ( PCIDevice * dev )
bool pcie_sriov_register_device ( PCIDevice * dev , Error * * errp )
{
{
uint8_t * cfg = dev - > config + dev - > exp . sriov_cap ;
if ( ! dev - > exp . sriov_pf . vf & & dev - > qdev . id & &
uint16_t i ;
pfs & & g_hash_table_contains ( pfs , dev - > qdev . id ) ) {
error_setg ( errp , " attaching user-created SR-IOV VF unsupported " ) ;
return false ;
}
trace_sriov_unregister_vfs ( dev - > name , PCI_SLOT ( dev - > devfn ) ,
if ( dev - > sriov_pf ) {
PCI_FUNC ( dev - > devfn ) ) ;
PCIDevice * pci_pf ;
for ( i = 0 ; i < pci_get_word ( cfg + PCI_SRIOV_TOTAL_VF ) ; i + + ) {
GPtrArray * pf ;
pci_set_enabled ( dev - > exp . sriov_pf . vf [ i ] , false ) ;
if ( ! PCI_DEVICE_GET_CLASS ( dev ) - > sriov_vf_user_creatable ) {
error_setg ( errp , " user cannot create SR-IOV VF with this device type " ) ;
return false ;
}
if ( ! pci_is_express ( dev ) ) {
error_setg ( errp , " PCI Express is required for SR-IOV VF " ) ;
return false ;
}
if ( ! pci_qdev_find_device ( dev - > sriov_pf , & pci_pf ) ) {
error_setg ( errp , " PCI device specified as SR-IOV PF already exists " ) ;
return false ;
}
if ( ! pfs ) {
pfs = g_hash_table_new_full ( g_str_hash , g_str_equal , g_free , NULL ) ;
}
pf = g_hash_table_lookup ( pfs , dev - > sriov_pf ) ;
if ( ! pf ) {
pf = g_ptr_array_new ( ) ;
g_hash_table_insert ( pfs , g_strdup ( dev - > sriov_pf ) , pf ) ;
}
g_ptr_array_add ( pf , dev ) ;
}
}
pci_set_word ( dev - > wmask + dev - > exp . sriov_cap + PCI_SRIOV_NUM_VF , 0xffff ) ;
return true ;
}
void pcie_sriov_unregister_device ( PCIDevice * dev )
{
if ( dev - > sriov_pf & & pfs ) {
GPtrArray * pf = g_hash_table_lookup ( pfs , dev - > sriov_pf ) ;
if ( pf ) {
g_ptr_array_remove_fast ( pf , dev ) ;
if ( ! pf - > len ) {
g_hash_table_remove ( pfs , dev - > sriov_pf ) ;
g_ptr_array_free ( pf , FALSE ) ;
}
}
}
}
}
void pcie_sriov_config_write ( PCIDevice * dev , uint32_t address ,
void pcie_sriov_config_write ( PCIDevice * dev , uint32_t address ,
@ -314,7 +486,7 @@ void pcie_sriov_pf_add_sup_pgsize(PCIDevice *dev, uint16_t opt_sup_pgsize)
uint16_t pcie_sriov_vf_number ( PCIDevice * dev )
uint16_t pcie_sriov_vf_number ( PCIDevice * dev )
{
{
assert ( pci_is_vf ( dev ) ) ;
assert ( dev - > exp . sriov_vf . pf ) ;
return dev - > exp . sriov_vf . vf_number ;
return dev - > exp . sriov_vf . vf_number ;
}
}