From f24a2794e1b527e45efbbcad1c272f176f3d9df0 Mon Sep 17 00:00:00 2001 From: Aaron Young Date: Wed, 27 Oct 2021 16:05:43 -0700 Subject: [virtio] Update driver to use DMA API Signed-off-by: Aaron Young --- src/drivers/net/virtio-net.c | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) (limited to 'src/drivers/net') diff --git a/src/drivers/net/virtio-net.c b/src/drivers/net/virtio-net.c index 78ec9ac4e..0c4541924 100644 --- a/src/drivers/net/virtio-net.c +++ b/src/drivers/net/virtio-net.c @@ -29,6 +29,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include #include #include @@ -99,8 +100,9 @@ struct virtnet_nic { /** Pending rx packet count */ unsigned int rx_num_iobufs; - /** Virtio net dummy packet headers */ - struct virtio_net_hdr_modern empty_header[QUEUE_NB]; + /** DMA device */ + struct dma_device *dma; + }; /** Add an iobuf to a virtqueue @@ -115,7 +117,7 @@ static void virtnet_enqueue_iob ( struct net_device *netdev, int vq_idx, struct io_buffer *iobuf ) { struct virtnet_nic *virtnet = netdev->priv; struct vring_virtqueue *vq = &virtnet->virtqueue[vq_idx]; - struct virtio_net_hdr_modern *header = &virtnet->empty_header[vq_idx]; + struct virtio_net_hdr_modern *header = vq->empty_header; unsigned int out = ( vq_idx == TX_INDEX ) ? 2 : 0; unsigned int in = ( vq_idx == TX_INDEX ) ? 0 : 2; size_t header_len = ( virtnet->virtio_version ? @@ -132,11 +134,11 @@ static void virtnet_enqueue_iob ( struct net_device *netdev, * to header->flags for received packets. Work around * this by using separate RX and TX headers. */ - .addr = ( char* ) header, + .addr = dma ( &vq->map, header ), .length = header_len, }, { - .addr = ( char* ) iobuf->data, + .addr = iob_dma ( iobuf ), .length = iob_len ( iobuf ), }, }; @@ -161,7 +163,7 @@ static void virtnet_refill_rx_virtqueue ( struct net_device *netdev ) { struct io_buffer *iobuf; /* Try to allocate a buffer, stop for now if out of memory */ - iobuf = alloc_iob ( len ); + iobuf = alloc_rx_iob ( len, virtnet->dma ); if ( ! iobuf ) break; @@ -215,7 +217,8 @@ static int virtnet_open_legacy ( struct net_device *netdev ) { /* Initialize rx/tx virtqueues */ for ( i = 0; i < QUEUE_NB; i++ ) { - if ( vp_find_vq ( ioaddr, i, &virtnet->virtqueue[i] ) == -1 ) { + if ( vp_find_vq ( ioaddr, i, &virtnet->virtqueue[i], virtnet->dma, + sizeof ( struct virtio_net_hdr_modern ) ) == -1 ) { DBGC ( virtnet, "VIRTIO-NET %p cannot register queue %d\n", virtnet, i ); virtnet_free_virtqueues ( netdev ); @@ -280,7 +283,8 @@ static int virtnet_open_modern ( struct net_device *netdev ) { } /* Initialize rx/tx virtqueues */ - if ( vpm_find_vqs ( &virtnet->vdev, QUEUE_NB, virtnet->virtqueue ) ) { + if ( vpm_find_vqs ( &virtnet->vdev, QUEUE_NB, virtnet->virtqueue, + virtnet->dma, sizeof ( struct virtio_net_hdr_modern ) ) ) { DBGC ( virtnet, "VIRTIO-NET %p cannot register queues\n", virtnet ); virtnet_free_virtqueues ( netdev ); @@ -335,7 +339,7 @@ static void virtnet_close ( struct net_device *netdev ) { /* Free rx iobufs */ list_for_each_entry_safe ( iobuf, next_iobuf, &virtnet->rx_iobufs, list ) { - free_iob ( iobuf ); + free_rx_iob ( iobuf ); } INIT_LIST_HEAD ( &virtnet->rx_iobufs ); virtnet->rx_num_iobufs = 0; @@ -478,6 +482,12 @@ static int virtnet_probe_legacy ( struct pci_device *pci ) { /* Enable PCI bus master and reset NIC */ adjust_pci_device ( pci ); + + /* Configure DMA */ + virtnet->dma = &pci->dma; + dma_set_mask_64bit ( virtnet->dma ); + netdev->dma = virtnet->dma; + vp_reset ( ioaddr ); /* Load MAC address and MTU */ @@ -506,7 +516,7 @@ static int virtnet_probe_legacy ( struct pci_device *pci ) { return 0; unregister_netdev ( netdev ); - err_register_netdev: +err_register_netdev: vp_reset ( ioaddr ); netdev_nullify ( netdev ); netdev_put ( netdev ); @@ -586,6 +596,11 @@ static int virtnet_probe_modern ( struct pci_device *pci, int *found_dev ) { /* Enable the PCI device */ adjust_pci_device ( pci ); + /* Configure DMA */ + virtnet->dma = &pci->dma; + dma_set_mask_64bit ( virtnet->dma ); + netdev->dma = virtnet->dma; + /* Reset the device and set initial status bits */ vpm_reset ( &virtnet->vdev ); vpm_add_status ( &virtnet->vdev, VIRTIO_CONFIG_S_ACKNOWLEDGE ); @@ -633,7 +648,6 @@ err_mac_address: vpm_reset ( &virtnet->vdev ); netdev_nullify ( netdev ); netdev_put ( netdev ); - virtio_pci_unmap_capability ( &virtnet->vdev.device ); err_map_device: virtio_pci_unmap_capability ( &virtnet->vdev.isr ); -- cgit v1.2.3-55-g7522 From 3ad27fbe78fec22e9af754242586f2ef621c8d2f Mon Sep 17 00:00:00 2001 From: Benedikt Braunger Date: Tue, 16 Nov 2021 13:42:14 +0100 Subject: [intel] Add PCI ID for Intel X553 0x15e4 Modified-by: Michael Brown Signed-off-by: Michael Brown --- src/drivers/net/intelx.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/drivers/net') diff --git a/src/drivers/net/intelx.c b/src/drivers/net/intelx.c index ccf6b0648..f4dad8859 100644 --- a/src/drivers/net/intelx.c +++ b/src/drivers/net/intelx.c @@ -481,6 +481,7 @@ static struct pci_device_id intelx_nics[] = { PCI_ROM ( 0x8086, 0x15ab, "x552", "X552", 0 ), PCI_ROM ( 0x8086, 0x15c8, "x553t", "X553/X557-AT", 0 ), PCI_ROM ( 0x8086, 0x15ce, "x553-sfp", "X553 (SFP+)", 0 ), + PCI_ROM ( 0x8086, 0x15e4, "x553a", "X553", 0 ), PCI_ROM ( 0x8086, 0x15e5, "x553", "X553", 0 ), }; -- cgit v1.2.3-55-g7522 From 562c74e1ea52a399f7a6c416e35d98f4b37888cf Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 22 Nov 2021 14:53:33 +0000 Subject: [efi] Run ExitBootServices shutdown hook at TPL_NOTIFY On some systems (observed with the Thunderbolt ports on a ThinkPad X1 Extreme Gen3 and a ThinkPad P53), if the IOMMU is enabled then the system firmware will install an ExitBootServices notification event that disables bus mastering on the Thunderbolt xHCI controller and all PCI bridges, and destroys any extant IOMMU mappings. This leaves the xHCI controller unable to perform any DMA operations. As described in commit 236299b ("[xhci] Avoid DMA during shutdown if firmware has disabled bus mastering"), any subsequent DMA operation attempted by the xHCI controller will end up completing after the operating system kernel has reenabled bus mastering, resulting in a DMA operation to an area of memory that the hardware is no longer permitted to access and, on Windows with the Driver Verifier enabled, a STOP 0xE6 (DRIVER_VERIFIER_DMA_VIOLATION). That commit avoids triggering any DMA attempts during the shutdown of the xHCI controller itself. However, this is not a complete solution since any attached and opened USB device (e.g. a USB NIC) may asynchronously trigger DMA attempts that happen to occur after bus mastering has been disabled but before we reset the xHCI controller. Avoid this problem by installing our own ExitBootServices notification event at TPL_NOTIFY, thereby causing it to be invoked before the firmware's own ExitBootServices notification event that disables bus mastering. This unsurprisingly causes the shutdown hook itself to be invoked at TPL_NOTIFY, which causes a fatal error when later code attempts to raise the TPL to TPL_CALLBACK (which is a lower TPL). Work around this problem by redefining the "internal" iPXE TPL to be variable, and set this internal TPL to TPL_NOTIFY when the shutdown hook is invoked. Avoid calling into an underlying SNP protocol instance from within our shutdown hook at TPL_NOTIFY, since the underlying SNP driver may attempt to raise the TPL to TPL_CALLBACK (which would cause a fatal error). Failing to shut down the underlying SNP device is safe to do since the underlying device must, in any case, have installed its own ExitBootServices hook if any shutdown actions are required. Reported-by: Andreas Hammarskjöld Tested-by: Andreas Hammarskjöld Signed-off-by: Michael Brown --- src/drivers/net/efi/nii.c | 2 +- src/drivers/net/efi/snpnet.c | 18 ++++++++++++++---- src/include/ipxe/efi/efi.h | 1 + src/interface/efi/efi_entropy.c | 4 ++-- src/interface/efi/efi_init.c | 20 +++++++++++++++++--- src/interface/efi/efi_timer.c | 2 +- 6 files changed, 36 insertions(+), 11 deletions(-) (limited to 'src/drivers/net') diff --git a/src/drivers/net/efi/nii.c b/src/drivers/net/efi/nii.c index b9f34650e..833462e77 100644 --- a/src/drivers/net/efi/nii.c +++ b/src/drivers/net/efi/nii.c @@ -576,7 +576,7 @@ static int nii_issue_cpb_db ( struct nii_nic *nii, unsigned int op, void *cpb, cdb.IFnum = nii->nii->IfNum; /* Raise task priority level */ - tpl = bs->RaiseTPL ( TPL_CALLBACK ); + tpl = bs->RaiseTPL ( efi_internal_tpl ); /* Issue command */ DBGC2 ( nii, "NII %s issuing %02x:%04x ifnum %d%s%s\n", diff --git a/src/drivers/net/efi/snpnet.c b/src/drivers/net/efi/snpnet.c index fb5240277..69ec6f5e5 100644 --- a/src/drivers/net/efi/snpnet.c +++ b/src/drivers/net/efi/snpnet.c @@ -164,6 +164,10 @@ static int snpnet_transmit ( struct net_device *netdev, EFI_STATUS efirc; int rc; + /* Do nothing if shutdown is in progress */ + if ( efi_shutdown_in_progress ) + return -ECANCELED; + /* Defer the packet if there is already a transmission in progress */ if ( snp->txbuf ) { netdev_tx_defer ( netdev, iobuf ); @@ -283,6 +287,10 @@ static void snpnet_poll_rx ( struct net_device *netdev ) { */ static void snpnet_poll ( struct net_device *netdev ) { + /* Do nothing if shutdown is in progress */ + if ( efi_shutdown_in_progress ) + return; + /* Process any TX completions */ snpnet_poll_tx ( netdev ); @@ -426,8 +434,9 @@ static void snpnet_close ( struct net_device *netdev ) { EFI_STATUS efirc; int rc; - /* Shut down NIC */ - if ( ( efirc = snp->snp->Shutdown ( snp->snp ) ) != 0 ) { + /* Shut down NIC (unless whole system shutdown is in progress) */ + if ( ( ! efi_shutdown_in_progress ) && + ( ( efirc = snp->snp->Shutdown ( snp->snp ) ) != 0 ) ) { rc = -EEFI ( efirc ); DBGC ( snp, "SNP %s could not shut down: %s\n", netdev->name, strerror ( rc ) ); @@ -589,8 +598,9 @@ void snpnet_stop ( struct efi_device *efidev ) { /* Unregister network device */ unregister_netdev ( netdev ); - /* Stop SNP protocol */ - if ( ( efirc = snp->snp->Stop ( snp->snp ) ) != 0 ) { + /* Stop SNP protocol (unless whole system shutdown is in progress) */ + if ( ( ! efi_shutdown_in_progress ) && + ( ( efirc = snp->snp->Stop ( snp->snp ) ) != 0 ) ) { rc = -EEFI ( efirc ); DBGC ( device, "SNP %s could not stop: %s\n", efi_handle_name ( device ), strerror ( rc ) ); diff --git a/src/include/ipxe/efi/efi.h b/src/include/ipxe/efi/efi.h index a83fa0f27..1dd0d4453 100644 --- a/src/include/ipxe/efi/efi.h +++ b/src/include/ipxe/efi/efi.h @@ -223,6 +223,7 @@ extern EFI_HANDLE efi_image_handle; extern EFI_LOADED_IMAGE_PROTOCOL *efi_loaded_image; extern EFI_DEVICE_PATH_PROTOCOL *efi_loaded_image_path; extern EFI_SYSTEM_TABLE *efi_systab; +extern EFI_TPL efi_internal_tpl; extern EFI_TPL efi_external_tpl; extern int efi_shutdown_in_progress; diff --git a/src/interface/efi/efi_entropy.c b/src/interface/efi/efi_entropy.c index 70cd06293..1e8ddfb68 100644 --- a/src/interface/efi/efi_entropy.c +++ b/src/interface/efi/efi_entropy.c @@ -104,8 +104,8 @@ static void efi_entropy_disable ( void ) { /* Close timer tick event */ bs->CloseEvent ( tick ); - /* Return to TPL_CALLBACK */ - bs->RaiseTPL ( TPL_CALLBACK ); + /* Return to internal TPL */ + bs->RaiseTPL ( efi_internal_tpl ); } /** diff --git a/src/interface/efi/efi_init.c b/src/interface/efi/efi_init.c index 1c6e9d440..5d98f9ff7 100644 --- a/src/interface/efi/efi_init.c +++ b/src/interface/efi/efi_init.c @@ -47,6 +47,9 @@ EFI_DEVICE_PATH_PROTOCOL *efi_loaded_image_path; */ EFI_SYSTEM_TABLE * _C2 ( PLATFORM, _systab ); +/** Internal task priority level */ +EFI_TPL efi_internal_tpl = TPL_CALLBACK; + /** External task priority level */ EFI_TPL efi_external_tpl = TPL_APPLICATION; @@ -79,6 +82,17 @@ static EFI_STATUS EFIAPI efi_unload ( EFI_HANDLE image_handle ); static EFIAPI void efi_shutdown_hook ( EFI_EVENT event __unused, void *context __unused ) { + /* This callback is invoked at TPL_NOTIFY in order to ensure + * that we have an opportunity to shut down cleanly before + * other shutdown hooks perform destructive operations such as + * disabling the IOMMU. + * + * Modify the internal task priority level so that no code + * attempts to raise from TPL_NOTIFY to TPL_CALLBACK (which + * would trigger a fatal exception). + */ + efi_internal_tpl = TPL_NOTIFY; + /* Mark shutdown as being in progress, to indicate that large * parts of the system (e.g. timers) are no longer functional. */ @@ -273,7 +287,7 @@ EFI_STATUS efi_init ( EFI_HANDLE image_handle, * bother doing so when ExitBootServices() is called. */ if ( ( efirc = bs->CreateEvent ( EVT_SIGNAL_EXIT_BOOT_SERVICES, - TPL_CALLBACK, efi_shutdown_hook, + TPL_NOTIFY, efi_shutdown_hook, NULL, &efi_shutdown_event ) ) != 0 ) { rc = -EEFI ( efirc ); DBGC ( systab, "EFI could not create ExitBootServices event: " @@ -373,7 +387,7 @@ __attribute__ (( noreturn )) void __stack_chk_fail ( void ) { } /** - * Raise task priority level to TPL_CALLBACK + * Raise task priority level to internal level * * @v tpl Saved TPL */ @@ -384,7 +398,7 @@ void efi_raise_tpl ( struct efi_saved_tpl *tpl ) { tpl->previous = efi_external_tpl; /* Raise TPL and record previous TPL as new external TPL */ - tpl->current = bs->RaiseTPL ( TPL_CALLBACK ); + tpl->current = bs->RaiseTPL ( efi_internal_tpl ); efi_external_tpl = tpl->current; } diff --git a/src/interface/efi/efi_timer.c b/src/interface/efi/efi_timer.c index 405cd3454..6427eb1d8 100644 --- a/src/interface/efi/efi_timer.c +++ b/src/interface/efi/efi_timer.c @@ -137,7 +137,7 @@ static unsigned long efi_currticks ( void ) { efi_jiffies++; } else { bs->RestoreTPL ( efi_external_tpl ); - bs->RaiseTPL ( TPL_CALLBACK ); + bs->RaiseTPL ( efi_internal_tpl ); } return ( efi_jiffies * ( TICKS_PER_SEC / EFI_JIFFIES_PER_SEC ) ); -- cgit v1.2.3-55-g7522