@ -53,6 +53,9 @@
# include "hw/pci/pci.h"
# include "qemu/timer.h"
# include "exec/memory.h"
# include "hw/pci/msi.h"
# include "hw/pci/msix.h"
# include "hw/remote/vfio-user-obj.h"
# define TYPE_VFU_OBJECT "x-vfio-user-server"
OBJECT_DECLARE_TYPE ( VfuObject , VfuObjectClass , VFU_OBJECT )
@ -96,6 +99,10 @@ struct VfuObject {
Error * unplug_blocker ;
int vfu_poll_fd ;
MSITriggerFunc * default_msi_trigger ;
MSIPrepareMessageFunc * default_msi_prepare_message ;
MSIxPrepareMessageFunc * default_msix_prepare_message ;
} ;
static void vfu_object_init_ctx ( VfuObject * o , Error * * errp ) ;
@ -520,6 +527,155 @@ static void vfu_object_register_bars(vfu_ctx_t *vfu_ctx, PCIDevice *pdev)
}
}
static int vfu_object_map_irq ( PCIDevice * pci_dev , int intx )
{
int pci_bdf = PCI_BUILD_BDF ( pci_bus_num ( pci_get_bus ( pci_dev ) ) ,
pci_dev - > devfn ) ;
return pci_bdf ;
}
static void vfu_object_set_irq ( void * opaque , int pirq , int level )
{
PCIBus * pci_bus = opaque ;
PCIDevice * pci_dev = NULL ;
vfu_ctx_t * vfu_ctx = NULL ;
int pci_bus_num , devfn ;
if ( level ) {
pci_bus_num = PCI_BUS_NUM ( pirq ) ;
devfn = PCI_BDF_TO_DEVFN ( pirq ) ;
/*
* pci_find_device ( ) performs at O ( 1 ) if the device is attached
* to the root PCI bus . Whereas , if the device is attached to a
* secondary PCI bus ( such as when a root port is involved ) ,
* finding the parent PCI bus could take O ( n )
*/
pci_dev = pci_find_device ( pci_bus , pci_bus_num , devfn ) ;
vfu_ctx = pci_dev - > irq_opaque ;
g_assert ( vfu_ctx ) ;
vfu_irq_trigger ( vfu_ctx , 0 ) ;
}
}
static MSIMessage vfu_object_msi_prepare_msg ( PCIDevice * pci_dev ,
unsigned int vector )
{
MSIMessage msg ;
msg . address = 0 ;
msg . data = vector ;
return msg ;
}
static void vfu_object_msi_trigger ( PCIDevice * pci_dev , MSIMessage msg )
{
vfu_ctx_t * vfu_ctx = pci_dev - > irq_opaque ;
vfu_irq_trigger ( vfu_ctx , msg . data ) ;
}
static void vfu_object_setup_msi_cbs ( VfuObject * o )
{
o - > default_msi_trigger = o - > pci_dev - > msi_trigger ;
o - > default_msi_prepare_message = o - > pci_dev - > msi_prepare_message ;
o - > default_msix_prepare_message = o - > pci_dev - > msix_prepare_message ;
o - > pci_dev - > msi_trigger = vfu_object_msi_trigger ;
o - > pci_dev - > msi_prepare_message = vfu_object_msi_prepare_msg ;
o - > pci_dev - > msix_prepare_message = vfu_object_msi_prepare_msg ;
}
static void vfu_object_restore_msi_cbs ( VfuObject * o )
{
o - > pci_dev - > msi_trigger = o - > default_msi_trigger ;
o - > pci_dev - > msi_prepare_message = o - > default_msi_prepare_message ;
o - > pci_dev - > msix_prepare_message = o - > default_msix_prepare_message ;
}
static void vfu_msix_irq_state ( vfu_ctx_t * vfu_ctx , uint32_t start ,
uint32_t count , bool mask )
{
VfuObject * o = vfu_get_private ( vfu_ctx ) ;
Error * err = NULL ;
uint32_t vector ;
for ( vector = start ; vector < count ; vector + + ) {
msix_set_mask ( o - > pci_dev , vector , mask , & err ) ;
if ( err ) {
VFU_OBJECT_ERROR ( o , " vfu: %s: %s " , o - > device ,
error_get_pretty ( err ) ) ;
error_free ( err ) ;
err = NULL ;
}
}
}
static void vfu_msi_irq_state ( vfu_ctx_t * vfu_ctx , uint32_t start ,
uint32_t count , bool mask )
{
VfuObject * o = vfu_get_private ( vfu_ctx ) ;
Error * err = NULL ;
uint32_t vector ;
for ( vector = start ; vector < count ; vector + + ) {
msi_set_mask ( o - > pci_dev , vector , mask , & err ) ;
if ( err ) {
VFU_OBJECT_ERROR ( o , " vfu: %s: %s " , o - > device ,
error_get_pretty ( err ) ) ;
error_free ( err ) ;
err = NULL ;
}
}
}
static int vfu_object_setup_irqs ( VfuObject * o , PCIDevice * pci_dev )
{
vfu_ctx_t * vfu_ctx = o - > vfu_ctx ;
int ret ;
ret = vfu_setup_device_nr_irqs ( vfu_ctx , VFU_DEV_INTX_IRQ , 1 ) ;
if ( ret < 0 ) {
return ret ;
}
if ( msix_nr_vectors_allocated ( pci_dev ) ) {
ret = vfu_setup_device_nr_irqs ( vfu_ctx , VFU_DEV_MSIX_IRQ ,
msix_nr_vectors_allocated ( pci_dev ) ) ;
vfu_setup_irq_state_callback ( vfu_ctx , VFU_DEV_MSIX_IRQ ,
& vfu_msix_irq_state ) ;
} else if ( msi_nr_vectors_allocated ( pci_dev ) ) {
ret = vfu_setup_device_nr_irqs ( vfu_ctx , VFU_DEV_MSI_IRQ ,
msi_nr_vectors_allocated ( pci_dev ) ) ;
vfu_setup_irq_state_callback ( vfu_ctx , VFU_DEV_MSI_IRQ ,
& vfu_msi_irq_state ) ;
}
if ( ret < 0 ) {
return ret ;
}
vfu_object_setup_msi_cbs ( o ) ;
pci_dev - > irq_opaque = vfu_ctx ;
return 0 ;
}
void vfu_object_set_bus_irq ( PCIBus * pci_bus )
{
int bus_num = pci_bus_num ( pci_bus ) ;
int max_bdf = PCI_BUILD_BDF ( bus_num , PCI_DEVFN_MAX - 1 ) ;
pci_bus_irqs ( pci_bus , vfu_object_set_irq , vfu_object_map_irq , pci_bus ,
max_bdf ) ;
}
/*
* TYPE_VFU_OBJECT depends on the availability of the ' socket ' and ' device '
* properties . It also depends on devices instantiated in QEMU . These
@ -632,6 +788,13 @@ static void vfu_object_init_ctx(VfuObject *o, Error **errp)
vfu_object_register_bars ( o - > vfu_ctx , o - > pci_dev ) ;
ret = vfu_object_setup_irqs ( o , o - > pci_dev ) ;
if ( ret < 0 ) {
error_setg ( errp , " vfu: Failed to setup interrupts for %s " ,
o - > device ) ;
goto fail ;
}
ret = vfu_realize_ctx ( o - > vfu_ctx ) ;
if ( ret < 0 ) {
error_setg ( errp , " vfu: Failed to realize device %s- %s " ,
@ -657,6 +820,8 @@ fail:
o - > unplug_blocker = NULL ;
}
if ( o - > pci_dev ) {
vfu_object_restore_msi_cbs ( o ) ;
o - > pci_dev - > irq_opaque = NULL ;
object_unref ( OBJECT ( o - > pci_dev ) ) ;
o - > pci_dev = NULL ;
}
@ -716,6 +881,8 @@ static void vfu_object_finalize(Object *obj)
}
if ( o - > pci_dev ) {
vfu_object_restore_msi_cbs ( o ) ;
o - > pci_dev - > irq_opaque = NULL ;
object_unref ( OBJECT ( o - > pci_dev ) ) ;
o - > pci_dev = NULL ;
}