aboutsummaryrefslogtreecommitdiffstats
path: root/src/interface/efi/efi_snp.c
diff options
context:
space:
mode:
authorMichael Brown <mcb30@ipxe.org>2020-11-20 15:15:15 +0000
committerMichael Brown <mcb30@ipxe.org>2020-11-20 16:57:50 +0000
commite10a40d41fa082ddbd5ccca1d1cc415815759f02 (patch)
treebb7607d0bb3daab858854a688c2ded975b45137d /src/interface/efi/efi_snp.c
parent062711f1cfacd0708107933b6a977476a7f7479e (diff)
downloadipxe-e10a40d41fa082ddbd5ccca1d1cc415815759f02.tar.gz
[efi] Avoid dropping below TPL as at entry to iPXE
iPXE will currently drop to TPL_APPLICATION whenever the current system time is obtained via currticks(), since the system time mechanism relies on a timer that can fire only when the TPL is below TPL_CALLBACK. This can cause unexpected behaviour if the system time is obtained in the middle of an API call into iPXE by external code. For example, MnpDxe sets up a 10ms periodic timer running at TPL_CALLBACK to poll the underling EFI_SIMPLE_NETWORK_PROTOCOL device for received packets. If the resulting poll within iPXE happens to hit a code path that requires obtaining the current system time (e.g. due to reception of an STP packet, which affects iPXE's blocked link timer), then iPXE will end up temporarily dropping to TPL_APPLICATION. This can potentially result in retriggering the MnpDxe periodic timer, causing code to be unexpectedly re-entered. Fix by recording the external TPL at any entry point into iPXE and dropping only as far as this external TPL, rather than dropping unconditionally to TPL_APPLICATION. The side effect of this change is that iPXE's view of the current system time will be frozen for the duration of any API calls made into iPXE by external code at TPL_CALLBACK or above. Since any such external code is already responsible for allowing execution at TPL_APPLICATION to occur, then this should not cause a problem in practice. 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.c58
1 files changed, 25 insertions, 33 deletions
diff --git a/src/interface/efi/efi_snp.c b/src/interface/efi/efi_snp.c
index 5285d322b..c1ce90427 100644
--- a/src/interface/efi/efi_snp.c
+++ b/src/interface/efi/efi_snp.c
@@ -48,7 +48,7 @@ static LIST_HEAD ( efi_snp_devices );
static int efi_snp_claimed;
/** TPL prior to network devices being claimed */
-static EFI_TPL efi_snp_old_tpl;
+static struct efi_saved_tpl efi_snp_saved_tpl;
/* Downgrade user experience if configured to do so
*
@@ -235,10 +235,9 @@ efi_snp_stop ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) {
static EFI_STATUS EFIAPI
efi_snp_initialize ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
UINTN extra_rx_bufsize, UINTN extra_tx_bufsize ) {
- EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
struct efi_snp_device *snpdev =
container_of ( snp, struct efi_snp_device, snp );
- EFI_TPL saved_tpl;
+ struct efi_saved_tpl tpl;
int rc;
DBGC ( snpdev, "SNPDEV %p INITIALIZE (%ld extra RX, %ld extra TX)\n",
@@ -252,7 +251,7 @@ efi_snp_initialize ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
}
/* Raise TPL */
- saved_tpl = bs->RaiseTPL ( TPL_CALLBACK );
+ efi_raise_tpl ( &tpl );
/* Open network device */
if ( ( rc = netdev_open ( snpdev->netdev ) ) != 0 ) {
@@ -263,7 +262,7 @@ efi_snp_initialize ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
efi_snp_set_state ( snpdev );
err_open:
- bs->RestoreTPL ( saved_tpl );
+ efi_restore_tpl ( &tpl );
err_claimed:
return EFIRC ( rc );
}
@@ -277,10 +276,9 @@ efi_snp_initialize ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
*/
static EFI_STATUS EFIAPI
efi_snp_reset ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN ext_verify ) {
- EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
struct efi_snp_device *snpdev =
container_of ( snp, struct efi_snp_device, snp );
- EFI_TPL saved_tpl;
+ struct efi_saved_tpl tpl;
int rc;
DBGC ( snpdev, "SNPDEV %p RESET (%s extended verification)\n",
@@ -293,7 +291,7 @@ efi_snp_reset ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN ext_verify ) {
}
/* Raise TPL */
- saved_tpl = bs->RaiseTPL ( TPL_CALLBACK );
+ efi_raise_tpl ( &tpl );
/* Close network device */
netdev_close ( snpdev->netdev );
@@ -309,7 +307,7 @@ efi_snp_reset ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN ext_verify ) {
efi_snp_set_state ( snpdev );
err_open:
- bs->RestoreTPL ( saved_tpl );
+ efi_restore_tpl ( &tpl );
err_claimed:
return EFIRC ( rc );
}
@@ -322,10 +320,9 @@ efi_snp_reset ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN ext_verify ) {
*/
static EFI_STATUS EFIAPI
efi_snp_shutdown ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) {
- EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
struct efi_snp_device *snpdev =
container_of ( snp, struct efi_snp_device, snp );
- EFI_TPL saved_tpl;
+ struct efi_saved_tpl tpl;
DBGC ( snpdev, "SNPDEV %p SHUTDOWN\n", snpdev );
@@ -334,7 +331,7 @@ efi_snp_shutdown ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) {
return EFI_NOT_READY;
/* Raise TPL */
- saved_tpl = bs->RaiseTPL ( TPL_CALLBACK );
+ efi_raise_tpl ( &tpl );
/* Close network device */
netdev_close ( snpdev->netdev );
@@ -342,7 +339,7 @@ efi_snp_shutdown ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) {
efi_snp_flush ( snpdev );
/* Restore TPL */
- bs->RestoreTPL ( saved_tpl );
+ efi_restore_tpl ( &tpl );
return 0;
}
@@ -546,10 +543,9 @@ efi_snp_nvdata ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN read,
static EFI_STATUS EFIAPI
efi_snp_get_status ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
UINT32 *interrupts, VOID **txbuf ) {
- EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
struct efi_snp_device *snpdev =
container_of ( snp, struct efi_snp_device, snp );
- EFI_TPL saved_tpl;
+ struct efi_saved_tpl tpl;
DBGC2 ( snpdev, "SNPDEV %p GET_STATUS", snpdev );
@@ -560,7 +556,7 @@ efi_snp_get_status ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
}
/* Raise TPL */
- saved_tpl = bs->RaiseTPL ( TPL_CALLBACK );
+ efi_raise_tpl ( &tpl );
/* Poll the network device */
efi_snp_poll ( snpdev );
@@ -585,7 +581,7 @@ efi_snp_get_status ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
}
/* Restore TPL */
- bs->RestoreTPL ( saved_tpl );
+ efi_restore_tpl ( &tpl );
DBGC2 ( snpdev, "\n" );
return 0;
@@ -608,14 +604,13 @@ efi_snp_transmit ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
UINTN ll_header_len, UINTN len, VOID *data,
EFI_MAC_ADDRESS *ll_src, EFI_MAC_ADDRESS *ll_dest,
UINT16 *net_proto ) {
- EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
struct efi_snp_device *snpdev =
container_of ( snp, struct efi_snp_device, snp );
struct ll_protocol *ll_protocol = snpdev->netdev->ll_protocol;
+ struct efi_saved_tpl tpl;
struct io_buffer *iobuf;
size_t payload_len;
unsigned int tx_fill;
- EFI_TPL saved_tpl;
int rc;
DBGC2 ( snpdev, "SNPDEV %p TRANSMIT %p+%lx", snpdev, data,
@@ -642,7 +637,7 @@ efi_snp_transmit ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
}
/* Raise TPL */
- saved_tpl = bs->RaiseTPL ( TPL_CALLBACK );
+ efi_raise_tpl ( &tpl );
/* Sanity checks */
if ( ll_header_len ) {
@@ -727,7 +722,7 @@ efi_snp_transmit ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
snpdev->interrupts |= EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT;
/* Restore TPL */
- bs->RestoreTPL ( saved_tpl );
+ efi_restore_tpl ( &tpl );
return 0;
@@ -737,7 +732,7 @@ efi_snp_transmit ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
free_iob ( iobuf );
err_alloc_iob:
err_sanity:
- bs->RestoreTPL ( saved_tpl );
+ efi_restore_tpl ( &tpl );
err_claimed:
return EFIRC ( rc );
}
@@ -759,17 +754,16 @@ efi_snp_receive ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
UINTN *ll_header_len, UINTN *len, VOID *data,
EFI_MAC_ADDRESS *ll_src, EFI_MAC_ADDRESS *ll_dest,
UINT16 *net_proto ) {
- EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
struct efi_snp_device *snpdev =
container_of ( snp, struct efi_snp_device, snp );
struct ll_protocol *ll_protocol = snpdev->netdev->ll_protocol;
+ struct efi_saved_tpl tpl;
struct io_buffer *iobuf;
const void *iob_ll_dest;
const void *iob_ll_src;
uint16_t iob_net_proto;
unsigned int iob_flags;
size_t copy_len;
- EFI_TPL saved_tpl;
int rc;
DBGC2 ( snpdev, "SNPDEV %p RECEIVE %p(+%lx)", snpdev, data,
@@ -782,7 +776,7 @@ efi_snp_receive ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
}
/* Raise TPL */
- saved_tpl = bs->RaiseTPL ( TPL_CALLBACK );
+ efi_raise_tpl ( &tpl );
/* Poll the network device */
efi_snp_poll ( snpdev );
@@ -831,7 +825,7 @@ efi_snp_receive ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
out_bad_ll_header:
free_iob ( iobuf );
out_no_packet:
- bs->RestoreTPL ( saved_tpl );
+ efi_restore_tpl ( &tpl );
err_claimed:
return EFIRC ( rc );
}
@@ -844,9 +838,8 @@ efi_snp_receive ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
*/
static VOID EFIAPI efi_snp_wait_for_packet ( EFI_EVENT event __unused,
VOID *context ) {
- EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
struct efi_snp_device *snpdev = context;
- EFI_TPL saved_tpl;
+ struct efi_saved_tpl tpl;
DBGCP ( snpdev, "SNPDEV %p WAIT_FOR_PACKET\n", snpdev );
@@ -859,13 +852,13 @@ static VOID EFIAPI efi_snp_wait_for_packet ( EFI_EVENT event __unused,
return;
/* Raise TPL */
- saved_tpl = bs->RaiseTPL ( TPL_CALLBACK );
+ efi_raise_tpl ( &tpl );
/* Poll the network device */
efi_snp_poll ( snpdev );
/* Restore TPL */
- bs->RestoreTPL ( saved_tpl );
+ efi_restore_tpl ( &tpl );
}
/** SNP interface */
@@ -1967,12 +1960,11 @@ 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 );
+ efi_raise_tpl ( &efi_snp_saved_tpl );
/* Claim SNP devices */
efi_snp_claimed += delta;
@@ -1984,5 +1976,5 @@ void efi_snp_add_claim ( int delta ) {
/* Restore TPL if we have released devices */
if ( ! efi_snp_claimed )
- bs->RestoreTPL ( efi_snp_old_tpl );
+ efi_restore_tpl ( &efi_snp_saved_tpl );
}