Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

virtio changes 5/2016 #55

Closed
wants to merge 8 commits into from
58 changes: 48 additions & 10 deletions src/drivers/bus/pci.c
Expand Up @@ -143,29 +143,67 @@ static void pci_read_bases ( struct pci_device *pci ) {
*
* @v pci PCI device
*
* Set device to be a busmaster in case BIOS neglected to do so. Also
* adjust PCI latency timer to a reasonable value, 32.
* Set device to be a bus master in case BIOS neglected to do so. Enable
* both memory and I/O space access. Also adjust PCI latency timer to a
* reasonable value, 32.
*/
void adjust_pci_device ( struct pci_device *pci ) {
pci_enable_device ( pci,
PCI_COMMAND_MASTER | PCI_COMMAND_MEM |
PCI_COMMAND_IO,
32 );
}

/**
* Enable PCI device
*
* @v pci PCI device
* @v flags PCI command flags to enable
* @v latency Minimum desired PCI latency timer or 0 to not adjust it
* @ret old_flags Original PCI command flags
*
* Enable device by setting bits in the command register in case BIOS neglected
* to do so. Also optionally adjust the PCI latency timer.
*/
unsigned pci_enable_device ( struct pci_device *pci, unsigned flags,
unsigned char latency ) {
unsigned short new_command, pci_command;
unsigned char pci_latency;

pci_read_config_word ( pci, PCI_COMMAND, &pci_command );
new_command = ( pci_command | PCI_COMMAND_MASTER |
PCI_COMMAND_MEM | PCI_COMMAND_IO );
new_command = ( pci_command | flags );
if ( pci_command != new_command ) {
DBGC ( pci, PCI_FMT " device not enabled by BIOS! Updating "
DBGC ( pci, PCI_FMT " device not fully enabled by BIOS! Updating "
"PCI command %04x->%04x\n",
PCI_ARGS ( pci ), pci_command, new_command );
pci_write_config_word ( pci, PCI_COMMAND, new_command );
}

pci_read_config_byte ( pci, PCI_LATENCY_TIMER, &pci_latency);
if ( pci_latency < 32 ) {
DBGC ( pci, PCI_FMT " latency timer is unreasonably low at "
"%d. Setting to 32.\n", PCI_ARGS ( pci ), pci_latency );
pci_write_config_byte ( pci, PCI_LATENCY_TIMER, 32);
if ( latency ) {
pci_read_config_byte ( pci, PCI_LATENCY_TIMER, &pci_latency );
if ( pci_latency < latency ) {
DBGC ( pci, PCI_FMT " latency timer is unreasonably low at "
"%d. Setting to %d.\n", PCI_ARGS ( pci ),
pci_latency, latency );
pci_write_config_byte ( pci, PCI_LATENCY_TIMER, latency );
}
}
return pci_command;
}

/**
* Restore PCI device
*
* @v pci PCI device
* @v old_flags Flags to restore, returned from pci_enable_device
*
* Restore the command register of a PCI device to state it had before we
* changed it.
*/
void pci_restore_device ( struct pci_device *pci, unsigned old_flags ) {
DBGC ( pci, PCI_FMT " restoring PCI command to %04x\n",
PCI_ARGS ( pci ), old_flags );
pci_write_config_word ( pci, PCI_COMMAND, old_flags );
}

/**
Expand Down
10 changes: 10 additions & 0 deletions src/drivers/bus/virtio-pci.c
Expand Up @@ -387,6 +387,16 @@ int vpm_find_vqs(struct virtio_pci_modern_device *vdev,
if (err) {
goto err_map_notify;
}

/* enable memory or I/O access if not already enabled */
switch (vq->notification.flags & VIRTIO_PCI_REGION_TYPE_MASK) {
case VIRTIO_PCI_REGION_PORT:
pci_enable_device(vdev->pci, PCI_COMMAND_IO, 0);
break;
case VIRTIO_PCI_REGION_MEMORY:
pci_enable_device(vdev->pci, PCI_COMMAND_MEM, 0);
break;
}
}

/* Select and activate all queues. Has to be done last: once we do
Expand Down
41 changes: 39 additions & 2 deletions src/drivers/net/virtio-net.c
Expand Up @@ -92,6 +92,9 @@ struct virtnet_nic {
/** 0 for legacy, 1 for virtio 1.0 */
int virtio_version;

/** Content of the command register before we initialized the device */
unsigned old_pci_command;

/** Virtio 1.0 device data */
struct virtio_pci_modern_device vdev;

Expand Down Expand Up @@ -463,7 +466,8 @@ static int virtnet_probe_legacy ( struct pci_device *pci ) {
virtnet, pci->dev.name, ioaddr, pci->irq );

/* Enable PCI bus master and reset NIC */
adjust_pci_device ( pci );
virtnet->old_pci_command = pci_enable_device ( pci,
PCI_COMMAND_MASTER | PCI_COMMAND_IO, 0 );
vp_reset ( ioaddr );

/* Load MAC address */
Expand All @@ -487,11 +491,33 @@ static int virtnet_probe_legacy ( struct pci_device *pci ) {
unregister_netdev ( netdev );
err_register_netdev:
vp_reset ( ioaddr );
pci_restore_device ( pci, virtnet->old_pci_command );
netdev_nullify ( netdev );
netdev_put ( netdev );
return rc;
}

/**
* Determine if the device uses at least one region of the given type
*
* @v vdev Virtio-net device data
* @v type The region type to test
* @ret result Non-zero if the type is used, zero otherwise
*/
static int virtnet_uses_region_type ( struct virtio_pci_modern_device *vdev,
unsigned type ) {
if ( ( vdev->common.flags & VIRTIO_PCI_REGION_TYPE_MASK ) == type ) {
return 1;
}
if ( ( vdev->isr.flags & VIRTIO_PCI_REGION_TYPE_MASK ) == type ) {
return 1;
}
if ( ( vdev->device.flags & VIRTIO_PCI_REGION_TYPE_MASK ) == type ) {
return 1;
}
return 0;
}

/**
* Probe PCI device, modern virtio 1.0
*
Expand All @@ -504,6 +530,7 @@ static int virtnet_probe_modern ( struct pci_device *pci, int *found_dev ) {
struct virtnet_nic *virtnet;
u64 features;
int rc, common, isr, notify, config, device;
unsigned pci_command;

common = virtio_pci_find_capability ( pci, VIRTIO_PCI_CAP_COMMON_CFG );
if ( ! common ) {
Expand Down Expand Up @@ -562,7 +589,14 @@ static int virtnet_probe_modern ( struct pci_device *pci, int *found_dev ) {
}

/* Enable the PCI device */
adjust_pci_device ( pci );
pci_command = PCI_COMMAND_MASTER;
if ( virtnet_uses_region_type ( &virtnet->vdev, VIRTIO_PCI_REGION_PORT ) ) {
pci_command |= PCI_COMMAND_IO;
}
if ( virtnet_uses_region_type ( &virtnet->vdev, VIRTIO_PCI_REGION_MEMORY ) ) {
pci_command |= PCI_COMMAND_MEM;
}
virtnet->old_pci_command = pci_enable_device ( pci, pci_command, 0 );

/* Reset the device and set initial status bits */
vpm_reset ( &virtnet->vdev );
Expand Down Expand Up @@ -601,6 +635,7 @@ static int virtnet_probe_modern ( struct pci_device *pci, int *found_dev ) {
err_register_netdev:
err_mac_address:
vpm_reset ( &virtnet->vdev );
pci_restore_device ( pci, virtnet->old_pci_command );
netdev_nullify ( netdev );
netdev_put ( netdev );

Expand Down Expand Up @@ -638,6 +673,8 @@ static void virtnet_remove ( struct pci_device *pci ) {
struct net_device *netdev = pci_get_drvdata ( pci );
struct virtnet_nic *virtnet = netdev->priv;

pci_restore_device ( pci, virtnet->old_pci_command );

virtio_pci_unmap_capability ( &virtnet->vdev.device );
virtio_pci_unmap_capability ( &virtnet->vdev.isr );
virtio_pci_unmap_capability ( &virtnet->vdev.common );
Expand Down
3 changes: 3 additions & 0 deletions src/include/ipxe/pci.h
Expand Up @@ -281,6 +281,9 @@ struct pci_driver {
PCI_SLOT ( (pci)->busdevfn ), PCI_FUNC ( (pci)->busdevfn )

extern void adjust_pci_device ( struct pci_device *pci );
extern unsigned pci_enable_device ( struct pci_device *pci, unsigned flags,
unsigned char latency );
extern void pci_restore_device ( struct pci_device *pci, unsigned old_flags );
extern unsigned long pci_bar_start ( struct pci_device *pci,
unsigned int reg );
extern int pci_read_config ( struct pci_device *pci );
Expand Down