summaryrefslogtreecommitdiffstats
path: root/src/interface/efi/efi_snp.c
diff options
context:
space:
mode:
authorMichael Brown2018-02-20 11:56:31 +0100
committerMichael Brown2018-02-20 11:56:31 +0100
commitc89a446cf09f30a121ae21d91f4a1aa071044084 (patch)
treed9b8a253fc68ac4e97221ad5dfae8707c5bab873 /src/interface/efi/efi_snp.c
parent[xhci] Consume event TRB before reporting completion to USB core (diff)
downloadipxe-c89a446cf09f30a121ae21d91f4a1aa071044084.tar.gz
ipxe-c89a446cf09f30a121ae21d91f4a1aa071044084.tar.xz
ipxe-c89a446cf09f30a121ae21d91f4a1aa071044084.zip
[efi] Run at TPL_CALLBACK to protect against UEFI timers
As noted in the comments, UEFI manages to combines the all of the worst aspects of both a polling design (inefficiency and inability to sleep until something interesting happens) and of an interrupt-driven design (the complexity of code that could be preempted at any time, thanks to UEFI timers). This causes problems in particular for UEFI USB keyboards: the keyboard driver calls UsbAsyncInterruptTransfer() to set up a periodic timer which is used to poll the USB bus. This poll may interrupt a critical section within iPXE, typically resulting in list corruption and either a hang or reboot. Work around this problem by mirroring the BIOS design, in which we run with interrupts disabled almost all of the time. Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/interface/efi/efi_snp.c')
-rw-r--r--src/interface/efi/efi_snp.c12
1 files changed, 12 insertions, 0 deletions
diff --git a/src/interface/efi/efi_snp.c b/src/interface/efi/efi_snp.c
index 263a25ac6..88d999bf0 100644
--- a/src/interface/efi/efi_snp.c
+++ b/src/interface/efi/efi_snp.c
@@ -45,6 +45,9 @@ static LIST_HEAD ( efi_snp_devices );
/** Network devices are currently claimed for use by iPXE */
static int efi_snp_claimed;
+/** TPL prior to network devices being claimed */
+static EFI_TPL efi_snp_old_tpl;
+
/* Downgrade user experience if configured to do so
*
* The default UEFI user experience for network boot is somewhat
@@ -1895,8 +1898,13 @@ struct efi_snp_device * last_opened_snpdev ( void ) {
* @v delta Claim count change
*/
void efi_snp_add_claim ( int delta ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
struct efi_snp_device *snpdev;
+ /* Raise TPL if we are about to claim devices */
+ if ( ! efi_snp_claimed )
+ efi_snp_old_tpl = bs->RaiseTPL ( TPL_CALLBACK );
+
/* Claim SNP devices */
efi_snp_claimed += delta;
assert ( efi_snp_claimed >= 0 );
@@ -1904,4 +1912,8 @@ void efi_snp_add_claim ( int delta ) {
/* Update SNP mode state for each interface */
list_for_each_entry ( snpdev, &efi_snp_devices, list )
efi_snp_set_state ( snpdev );
+
+ /* Restore TPL if we have released devices */
+ if ( ! efi_snp_claimed )
+ bs->RestoreTPL ( efi_snp_old_tpl );
}