diff --git a/MdeModulePkg/Bus/Pci/XhciDxe/Xhci.c b/MdeModulePkg/Bus/Pci/XhciDxe/Xhci.c index 62682dd27c..7a2e32a9dd 100644 --- a/MdeModulePkg/Bus/Pci/XhciDxe/Xhci.c +++ b/MdeModulePkg/Bus/Pci/XhciDxe/Xhci.c @@ -1,6 +1,7 @@ /** @file The XHCI controller driver. +(C) Copyright 2023 Hewlett Packard Enterprise Development LP
Copyright (c) 2011 - 2022, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent @@ -85,6 +86,11 @@ EFI_USB2_HC_PROTOCOL gXhciUsb2HcTemplate = { 0x0 }; +UINT64 mPerformanceCounterStartValue; +UINT64 mPerformanceCounterEndValue; +UINT64 mPerformanceCounterFrequency; +BOOLEAN mPerformanceCounterValuesCached = FALSE; + /** Retrieves the capability of root hub ports. @@ -2294,3 +2300,114 @@ XhcDriverBindingStop ( return EFI_SUCCESS; } + +/** + Converts a time in nanoseconds to a performance counter tick count. + + @param Time The time in nanoseconds to be converted to performance counter ticks. + @return Time in nanoseconds converted to ticks. +**/ +UINT64 +XhcConvertTimeToTicks ( + IN UINT64 Time + ) +{ + UINT64 Ticks; + UINT64 Remainder; + UINT64 Divisor; + UINTN Shift; + + // Cache the return values to avoid repeated calls to GetPerformanceCounterProperties () + if (!mPerformanceCounterValuesCached) { + mPerformanceCounterFrequency = GetPerformanceCounterProperties ( + &mPerformanceCounterStartValue, + &mPerformanceCounterEndValue + ); + + mPerformanceCounterValuesCached = TRUE; + } + + // Prevent returning a tick value of 0, unless Time is already 0 + if (0 == mPerformanceCounterFrequency) { + return Time; + } + + // Nanoseconds per second + Divisor = 1000000000; + + // + // Frequency + // Ticks = ------------- x Time + // 1,000,000,000 + // + Ticks = MultU64x64 ( + DivU64x64Remainder ( + mPerformanceCounterFrequency, + Divisor, + &Remainder + ), + Time + ); + + // + // Ensure (Remainder * Time) will not overflow 64-bit. + // + // HighBitSet64 (Remainder) + 1 + HighBitSet64 (Time) + 1 <= 64 + // + Shift = MAX (0, HighBitSet64 (Remainder) + HighBitSet64 (Time) - 62); + Remainder = RShiftU64 (Remainder, (UINTN)Shift); + Divisor = RShiftU64 (Divisor, (UINTN)Shift); + Ticks += DivU64x64Remainder (MultU64x64 (Remainder, Time), Divisor, NULL); + + return Ticks; +} + +/** + Computes and returns the elapsed ticks since PreviousTick. The + value of PreviousTick is overwritten with the current performance + counter value. + + @param PreviousTick Pointer to PreviousTick count. + @return The elapsed ticks since PreviousCount. PreviousCount is + overwritten with the current performance counter value. +**/ +UINT64 +XhcGetElapsedTicks ( + IN OUT UINT64 *PreviousTick + ) +{ + UINT64 CurrentTick; + UINT64 Delta; + + CurrentTick = GetPerformanceCounter (); + + // + // Determine if the counter is counting up or down + // + if (mPerformanceCounterStartValue < mPerformanceCounterEndValue) { + // + // Counter counts upwards, check for an overflow condition + // + if (*PreviousTick > CurrentTick) { + Delta = (mPerformanceCounterEndValue - *PreviousTick) + CurrentTick; + } else { + Delta = CurrentTick - *PreviousTick; + } + } else { + // + // Counter counts downwards, check for an underflow condition + // + if (*PreviousTick < CurrentTick) { + Delta = (mPerformanceCounterStartValue - CurrentTick) + *PreviousTick; + } else { + Delta = *PreviousTick - CurrentTick; + } + } + + // + // Set PreviousTick to CurrentTick + // + *PreviousTick = CurrentTick; + + return Delta; +} diff --git a/MdeModulePkg/Bus/Pci/XhciDxe/Xhci.h b/MdeModulePkg/Bus/Pci/XhciDxe/Xhci.h index ca223bd20c..4401675872 100644 --- a/MdeModulePkg/Bus/Pci/XhciDxe/Xhci.h +++ b/MdeModulePkg/Bus/Pci/XhciDxe/Xhci.h @@ -2,6 +2,7 @@ Provides some data structure definitions used by the XHCI host controller driver. +(C) Copyright 2023 Hewlett Packard Enterprise Development LP
Copyright (c) 2011 - 2017, Intel Corporation. All rights reserved.
Copyright (c) Microsoft Corporation.
SPDX-License-Identifier: BSD-2-Clause-Patent @@ -26,6 +27,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include #include #include +#include #include @@ -37,6 +39,11 @@ typedef struct _USB_DEV_CONTEXT USB_DEV_CONTEXT; #include "ComponentName.h" #include "UsbHcMem.h" +// +// Converts a count from microseconds to nanoseconds +// +#define XHC_MICROSECOND_TO_NANOSECOND(Time) (MultU64x32((Time), 1000)) + // // The unit is microsecond, setting it as 1us. // @@ -720,4 +727,29 @@ XhcAsyncIsochronousTransfer ( IN VOID *Context ); +/** + Converts a time in nanoseconds to a performance counter tick count. + + @param Time The time in nanoseconds to be converted to performance counter ticks. + @return Time in nanoseconds converted to ticks. +**/ +UINT64 +XhcConvertTimeToTicks ( + UINT64 Time + ); + +/** + Computes and returns the elapsed ticks since PreviousTick. The + value of PreviousTick is overwritten with the current performance + counter value. + + @param PreviousTick Pointer to PreviousTick count. + @return The elapsed ticks since PreviousCount. PreviousCount is + overwritten with the current performance counter value. +**/ +UINT64 +XhcGetElapsedTicks ( + IN OUT UINT64 *PreviousTick + ); + #endif diff --git a/MdeModulePkg/Bus/Pci/XhciDxe/XhciDxe.inf b/MdeModulePkg/Bus/Pci/XhciDxe/XhciDxe.inf index 5865d86822..18ef87916a 100644 --- a/MdeModulePkg/Bus/Pci/XhciDxe/XhciDxe.inf +++ b/MdeModulePkg/Bus/Pci/XhciDxe/XhciDxe.inf @@ -3,6 +3,7 @@ # It implements the interfaces of monitoring the status of all ports and transferring # Control, Bulk, Interrupt and Isochronous requests to those attached usb LS/FS/HS/SS devices. # +# (C) Copyright 2023 Hewlett Packard Enterprise Development LP
# Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.
# # SPDX-License-Identifier: BSD-2-Clause-Patent @@ -54,6 +55,7 @@ BaseMemoryLib DebugLib ReportStatusCodeLib + TimerLib [Guids] gEfiEventExitBootServicesGuid ## SOMETIMES_CONSUMES ## Event diff --git a/MdeModulePkg/Bus/Pci/XhciDxe/XhciReg.c b/MdeModulePkg/Bus/Pci/XhciDxe/XhciReg.c index 5700fc5fb8..40f2f1f227 100644 --- a/MdeModulePkg/Bus/Pci/XhciDxe/XhciReg.c +++ b/MdeModulePkg/Bus/Pci/XhciDxe/XhciReg.c @@ -2,6 +2,7 @@ The XHCI register operation routines. +(C) Copyright 2023 Hewlett Packard Enterprise Development LP
Copyright (c) 2011 - 2017, Intel Corporation. All rights reserved.
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; } /** diff --git a/MdeModulePkg/Bus/Pci/XhciDxe/XhciSched.c b/MdeModulePkg/Bus/Pci/XhciDxe/XhciSched.c index 53421e64a8..613b1485f1 100644 --- a/MdeModulePkg/Bus/Pci/XhciDxe/XhciSched.c +++ b/MdeModulePkg/Bus/Pci/XhciDxe/XhciSched.c @@ -2,6 +2,7 @@ XHCI transfer scheduling routines. +(C) Copyright 2023 Hewlett Packard Enterprise Development LP
Copyright (c) 2011 - 2020, Intel Corporation. All rights reserved.
Copyright (c) Microsoft Corporation.
Copyright (C) 2022 Advanced Micro Devices, Inc. All rights reserved.
@@ -1276,15 +1277,14 @@ EXIT: /** Execute the transfer by polling the URB. This is a synchronous operation. - @param Xhc The XHCI Instance. - @param CmdTransfer The executed URB is for cmd transfer or not. - @param Urb The URB to execute. - @param Timeout The time to wait before abort, in millisecond. + @param Xhc The XHCI Instance. + @param CmdTransfer The executed URB is for cmd transfer or not. + @param Urb The URB to execute. + @param Timeout The time to wait before abort, in millisecond. - @return EFI_DEVICE_ERROR The transfer failed due to transfer error. - @return EFI_TIMEOUT The transfer failed due to time out. - @return EFI_SUCCESS The transfer finished OK. - @retval EFI_OUT_OF_RESOURCES Memory for the timer event could not be allocated. + @return EFI_DEVICE_ERROR The transfer failed due to transfer error. + @return EFI_TIMEOUT The transfer failed due to time out. + @return EFI_SUCCESS The transfer finished OK. **/ EFI_STATUS @@ -1299,12 +1299,14 @@ XhcExecTransfer ( UINT8 SlotId; UINT8 Dci; BOOLEAN Finished; - EFI_EVENT TimeoutEvent; + UINT64 TimeoutTicks; + UINT64 ElapsedTicks; + UINT64 TicksDelta; + UINT64 CurrentTick; BOOLEAN IndefiniteTimeout; Status = EFI_SUCCESS; Finished = FALSE; - TimeoutEvent = NULL; IndefiniteTimeout = FALSE; if (CmdTransfer) { @@ -1322,34 +1324,18 @@ XhcExecTransfer ( if (Timeout == 0) { IndefiniteTimeout = TRUE; - goto RINGDOORBELL; } - 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; - } - -RINGDOORBELL: XhcRingDoorBell (Xhc, SlotId, Dci); + TimeoutTicks = XhcConvertTimeToTicks ( + XHC_MICROSECOND_TO_NANOSECOND ( + Timeout * XHC_1_MILLISECOND + ) + ); + ElapsedTicks = 0; + CurrentTick = GetPerformanceCounter (); + do { Finished = XhcCheckUrbResult (Xhc, Urb); if (Finished) { @@ -1357,22 +1343,22 @@ RINGDOORBELL: } gBS->Stall (XHC_1_MICROSECOND); - } while (IndefiniteTimeout || 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)); + } -DONE: - if (EFI_ERROR (Status)) { - Urb->Result = EFI_USB_ERR_NOTEXECUTE; - } else if (!Finished) { + ElapsedTicks += TicksDelta; + } while (IndefiniteTimeout || ElapsedTicks < TimeoutTicks); + + if (!Finished) { Urb->Result = EFI_USB_ERR_TIMEOUT; Status = EFI_TIMEOUT; } else if (Urb->Result != EFI_USB_NOERROR) { Status = EFI_DEVICE_ERROR; } - if (TimeoutEvent != NULL) { - gBS->CloseEvent (TimeoutEvent); - } - return Status; }