diff options
-rw-r--r-- | src/drivers/net/efi/nii.c | 2 | ||||
-rw-r--r-- | src/drivers/net/efi/snpnet.c | 18 | ||||
-rw-r--r-- | src/include/ipxe/efi/efi.h | 1 | ||||
-rw-r--r-- | src/interface/efi/efi_entropy.c | 4 | ||||
-rw-r--r-- | src/interface/efi/efi_init.c | 20 | ||||
-rw-r--r-- | src/interface/efi/efi_timer.c | 2 |
6 files changed, 36 insertions, 11 deletions
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 ) ); |