MdeModulePkg/XhciDxe: Use Performance Timer for XHCI Timeouts
REF:https://bugzilla.tianocore.org/show_bug.cgi?id=2948 XhciDxe uses the timer functionality provided by the boot services table to detect timeout conditions. This breaks the driver's ExitBootServices call back, as CoreExitBootServices halts the timer before signaling the ExitBootServices event. If the host controller fails to halt in the call back, the timeout condition will never occur and the boot gets stuck in an indefinite spin loop. Use the free running timer provided by TimerLib to calculate timeouts, avoiding the potential hang. Cc: Hao A Wu <hao.a.wu@intel.com> Cc: Ray Ni <ray.ni@intel.com> Signed-off-by: Patrick Henz <patrick.henz@hpe.com> Reviewed-by: Hao A Wu <hao.a.wu@intel.com>
This commit is contained in:
committed by
mergify[bot]
parent
8b2e6b90b8
commit
43dcf453fc
@@ -2,6 +2,7 @@
|
||||
|
||||
The XHCI register operation routines.
|
||||
|
||||
(C) Copyright 2023 Hewlett Packard Enterprise Development LP<BR>
|
||||
Copyright (c) 2011 - 2017, Intel Corporation. All rights reserved.<BR>
|
||||
SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
|
||||
@@ -417,15 +418,14 @@ XhcClearOpRegBit (
|
||||
Wait the operation register's bit as specified by Bit
|
||||
to become set (or clear).
|
||||
|
||||
@param Xhc The XHCI Instance.
|
||||
@param Offset The offset of the operation register.
|
||||
@param Bit The bit of the register to wait for.
|
||||
@param WaitToSet Wait the bit to set or clear.
|
||||
@param Timeout The time to wait before abort (in millisecond, ms).
|
||||
@param Xhc The XHCI Instance.
|
||||
@param Offset The offset of the operation register.
|
||||
@param Bit The bit of the register to wait for.
|
||||
@param WaitToSet Wait the bit to set or clear.
|
||||
@param Timeout The time to wait before abort (in millisecond, ms).
|
||||
|
||||
@retval EFI_SUCCESS The bit successfully changed by host controller.
|
||||
@retval EFI_TIMEOUT The time out occurred.
|
||||
@retval EFI_OUT_OF_RESOURCES Memory for the timer event could not be allocated.
|
||||
@retval EFI_SUCCESS The bit successfully changed by host controller.
|
||||
@retval EFI_TIMEOUT The time out occurred.
|
||||
|
||||
**/
|
||||
EFI_STATUS
|
||||
@@ -437,54 +437,34 @@ XhcWaitOpRegBit (
|
||||
IN UINT32 Timeout
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
EFI_EVENT TimeoutEvent;
|
||||
|
||||
TimeoutEvent = NULL;
|
||||
UINT64 TimeoutTicks;
|
||||
UINT64 ElapsedTicks;
|
||||
UINT64 TicksDelta;
|
||||
UINT64 CurrentTick;
|
||||
|
||||
if (Timeout == 0) {
|
||||
return EFI_TIMEOUT;
|
||||
}
|
||||
|
||||
Status = gBS->CreateEvent (
|
||||
EVT_TIMER,
|
||||
TPL_CALLBACK,
|
||||
NULL,
|
||||
NULL,
|
||||
&TimeoutEvent
|
||||
);
|
||||
|
||||
if (EFI_ERROR (Status)) {
|
||||
goto DONE;
|
||||
}
|
||||
|
||||
Status = gBS->SetTimer (
|
||||
TimeoutEvent,
|
||||
TimerRelative,
|
||||
EFI_TIMER_PERIOD_MILLISECONDS (Timeout)
|
||||
);
|
||||
|
||||
if (EFI_ERROR (Status)) {
|
||||
goto DONE;
|
||||
}
|
||||
|
||||
TimeoutTicks = XhcConvertTimeToTicks (XHC_MICROSECOND_TO_NANOSECOND (Timeout * XHC_1_MILLISECOND));
|
||||
ElapsedTicks = 0;
|
||||
CurrentTick = GetPerformanceCounter ();
|
||||
do {
|
||||
if (XHC_REG_BIT_IS_SET (Xhc, Offset, Bit) == WaitToSet) {
|
||||
Status = EFI_SUCCESS;
|
||||
goto DONE;
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
gBS->Stall (XHC_1_MICROSECOND);
|
||||
} while (EFI_ERROR (gBS->CheckEvent (TimeoutEvent)));
|
||||
TicksDelta = XhcGetElapsedTicks (&CurrentTick);
|
||||
// Ensure that ElapsedTicks is always incremented to avoid indefinite hangs
|
||||
if (TicksDelta == 0) {
|
||||
TicksDelta = XhcConvertTimeToTicks (XHC_MICROSECOND_TO_NANOSECOND (XHC_1_MICROSECOND));
|
||||
}
|
||||
|
||||
Status = EFI_TIMEOUT;
|
||||
ElapsedTicks += TicksDelta;
|
||||
} while (ElapsedTicks < TimeoutTicks);
|
||||
|
||||
DONE:
|
||||
if (TimeoutEvent != NULL) {
|
||||
gBS->CloseEvent (TimeoutEvent);
|
||||
}
|
||||
|
||||
return Status;
|
||||
return EFI_TIMEOUT;
|
||||
}
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user