summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei')
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/DmaMem.c243
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/EhcPeim.c1314
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/EhcPeim.h316
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/EhciPei.inf67
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/EhciPei.uni17
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/EhciPeiExtra.uni14
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/EhciReg.h303
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/EhciSched.c463
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/EhciSched.h93
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/EhciUrb.c627
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/EhciUrb.h324
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/UsbHcMem.c517
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/UsbHcMem.h86
13 files changed, 4384 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/DmaMem.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/DmaMem.c
new file mode 100644
index 00000000..c3d3bdb3
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/DmaMem.c
@@ -0,0 +1,243 @@
+/** @file
+The DMA memory help functions.
+
+Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
+
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "EhcPeim.h"
+
+/**
+ Provides the controller-specific addresses required to access system memory from a
+ DMA bus master.
+
+ @param IoMmu Pointer to IOMMU PPI.
+ @param Operation Indicates if the bus master is going to read or write to system memory.
+ @param HostAddress The system memory address to map to the PCI controller.
+ @param NumberOfBytes On input the number of bytes to map. On output the number of bytes
+ that were mapped.
+ @param DeviceAddress The resulting map address for the bus master PCI controller to use to
+ access the hosts HostAddress.
+ @param Mapping A resulting value to pass to Unmap().
+
+ @retval EFI_SUCCESS The range was mapped for the returned NumberOfBytes.
+ @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a common buffer.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
+ @retval EFI_DEVICE_ERROR The system hardware could not map the requested address.
+
+**/
+EFI_STATUS
+IoMmuMap (
+ IN EDKII_IOMMU_PPI *IoMmu,
+ IN EDKII_IOMMU_OPERATION Operation,
+ IN VOID *HostAddress,
+ IN OUT UINTN *NumberOfBytes,
+ OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,
+ OUT VOID **Mapping
+ )
+{
+ EFI_STATUS Status;
+ UINT64 Attribute;
+
+ if (IoMmu != NULL) {
+ Status = IoMmu->Map (
+ IoMmu,
+ Operation,
+ HostAddress,
+ NumberOfBytes,
+ DeviceAddress,
+ Mapping
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ switch (Operation) {
+ case EdkiiIoMmuOperationBusMasterRead:
+ case EdkiiIoMmuOperationBusMasterRead64:
+ Attribute = EDKII_IOMMU_ACCESS_READ;
+ break;
+ case EdkiiIoMmuOperationBusMasterWrite:
+ case EdkiiIoMmuOperationBusMasterWrite64:
+ Attribute = EDKII_IOMMU_ACCESS_WRITE;
+ break;
+ case EdkiiIoMmuOperationBusMasterCommonBuffer:
+ case EdkiiIoMmuOperationBusMasterCommonBuffer64:
+ Attribute = EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE;
+ break;
+ default:
+ ASSERT(FALSE);
+ return EFI_INVALID_PARAMETER;
+ }
+ Status = IoMmu->SetAttribute (
+ IoMmu,
+ *Mapping,
+ Attribute
+ );
+ if (EFI_ERROR (Status)) {
+ IoMmu->Unmap (IoMmu, Mapping);
+ *Mapping = NULL;
+ return Status;
+ }
+ } else {
+ *DeviceAddress = (EFI_PHYSICAL_ADDRESS) (UINTN) HostAddress;
+ *Mapping = NULL;
+ Status = EFI_SUCCESS;
+ }
+ return Status;
+}
+
+/**
+ Completes the Map() operation and releases any corresponding resources.
+
+ @param IoMmu Pointer to IOMMU PPI.
+ @param Mapping The mapping value returned from Map().
+
+**/
+VOID
+IoMmuUnmap (
+ IN EDKII_IOMMU_PPI *IoMmu,
+ IN VOID *Mapping
+ )
+{
+ if (IoMmu != NULL) {
+ IoMmu->SetAttribute (IoMmu, Mapping, 0);
+ IoMmu->Unmap (IoMmu, Mapping);
+ }
+}
+
+/**
+ Allocates pages that are suitable for an OperationBusMasterCommonBuffer or
+ OperationBusMasterCommonBuffer64 mapping.
+
+ @param IoMmu Pointer to IOMMU PPI.
+ @param Pages The number of pages to allocate.
+ @param HostAddress A pointer to store the base system memory address of the
+ allocated range.
+ @param DeviceAddress The resulting map address for the bus master PCI controller to use to
+ access the hosts HostAddress.
+ @param Mapping A resulting value to pass to Unmap().
+
+ @retval EFI_SUCCESS The requested memory pages were allocated.
+ @retval EFI_UNSUPPORTED Attributes is unsupported. The only legal attribute bits are
+ MEMORY_WRITE_COMBINE and MEMORY_CACHED.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated.
+
+**/
+EFI_STATUS
+IoMmuAllocateBuffer (
+ IN EDKII_IOMMU_PPI *IoMmu,
+ IN UINTN Pages,
+ OUT VOID **HostAddress,
+ OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,
+ OUT VOID **Mapping
+ )
+{
+ EFI_STATUS Status;
+ UINTN NumberOfBytes;
+ EFI_PHYSICAL_ADDRESS HostPhyAddress;
+
+ *HostAddress = NULL;
+ *DeviceAddress = 0;
+ *Mapping = NULL;
+
+ if (IoMmu != NULL) {
+ Status = IoMmu->AllocateBuffer (
+ IoMmu,
+ EfiBootServicesData,
+ Pages,
+ HostAddress,
+ 0
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ NumberOfBytes = EFI_PAGES_TO_SIZE (Pages);
+ Status = IoMmu->Map (
+ IoMmu,
+ EdkiiIoMmuOperationBusMasterCommonBuffer,
+ *HostAddress,
+ &NumberOfBytes,
+ DeviceAddress,
+ Mapping
+ );
+ if (EFI_ERROR (Status)) {
+ IoMmu->FreeBuffer (IoMmu, Pages, *HostAddress);
+ *HostAddress = NULL;
+ return EFI_OUT_OF_RESOURCES;
+ }
+ Status = IoMmu->SetAttribute (
+ IoMmu,
+ *Mapping,
+ EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE
+ );
+ if (EFI_ERROR (Status)) {
+ IoMmu->Unmap (IoMmu, *Mapping);
+ IoMmu->FreeBuffer (IoMmu, Pages, *HostAddress);
+ *Mapping = NULL;
+ *HostAddress = NULL;
+ return Status;
+ }
+ } else {
+ Status = PeiServicesAllocatePages (
+ EfiBootServicesCode,
+ Pages,
+ &HostPhyAddress
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ *HostAddress = (VOID *) (UINTN) HostPhyAddress;
+ *DeviceAddress = HostPhyAddress;
+ *Mapping = NULL;
+ }
+ return Status;
+}
+
+/**
+ Frees memory that was allocated with AllocateBuffer().
+
+ @param IoMmu Pointer to IOMMU PPI.
+ @param Pages The number of pages to free.
+ @param HostAddress The base system memory address of the allocated range.
+ @param Mapping The mapping value returned from Map().
+
+**/
+VOID
+IoMmuFreeBuffer (
+ IN EDKII_IOMMU_PPI *IoMmu,
+ IN UINTN Pages,
+ IN VOID *HostAddress,
+ IN VOID *Mapping
+ )
+{
+ if (IoMmu != NULL) {
+ IoMmu->SetAttribute (IoMmu, Mapping, 0);
+ IoMmu->Unmap (IoMmu, Mapping);
+ IoMmu->FreeBuffer (IoMmu, Pages, HostAddress);
+ }
+}
+
+/**
+ Initialize IOMMU.
+
+ @param IoMmu Pointer to pointer to IOMMU PPI.
+
+**/
+VOID
+IoMmuInit (
+ OUT EDKII_IOMMU_PPI **IoMmu
+ )
+{
+ PeiServicesLocatePpi (
+ &gEdkiiIoMmuPpiGuid,
+ 0,
+ NULL,
+ (VOID **) IoMmu
+ );
+}
+
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/EhcPeim.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/EhcPeim.c
new file mode 100644
index 00000000..7477f42b
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/EhcPeim.c
@@ -0,0 +1,1314 @@
+/** @file
+PEIM to produce gPeiUsb2HostControllerPpiGuid based on gPeiUsbControllerPpiGuid
+which is used to enable recovery function from USB Drivers.
+
+Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR>
+
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "EhcPeim.h"
+
+//
+// Two arrays used to translate the EHCI port state (change)
+// to the UEFI protocol's port state (change).
+//
+USB_PORT_STATE_MAP mUsbPortStateMap[] = {
+ {PORTSC_CONN, USB_PORT_STAT_CONNECTION},
+ {PORTSC_ENABLED, USB_PORT_STAT_ENABLE},
+ {PORTSC_SUSPEND, USB_PORT_STAT_SUSPEND},
+ {PORTSC_OVERCUR, USB_PORT_STAT_OVERCURRENT},
+ {PORTSC_RESET, USB_PORT_STAT_RESET},
+ {PORTSC_POWER, USB_PORT_STAT_POWER},
+ {PORTSC_OWNER, USB_PORT_STAT_OWNER}
+};
+
+USB_PORT_STATE_MAP mUsbPortChangeMap[] = {
+ {PORTSC_CONN_CHANGE, USB_PORT_STAT_C_CONNECTION},
+ {PORTSC_ENABLE_CHANGE, USB_PORT_STAT_C_ENABLE},
+ {PORTSC_OVERCUR_CHANGE, USB_PORT_STAT_C_OVERCURRENT}
+};
+
+/**
+ Read Ehc Operation register.
+
+ @param Ehc The EHCI device.
+ @param Offset The operation register offset.
+
+ @retval the register content read.
+
+**/
+UINT32
+EhcReadOpReg (
+ IN PEI_USB2_HC_DEV *Ehc,
+ IN UINT32 Offset
+ )
+{
+ UINT32 Data;
+
+ ASSERT (Ehc->CapLen != 0);
+
+ Data = MmioRead32 (Ehc->UsbHostControllerBaseAddress + Ehc->CapLen + Offset);
+
+ return Data;
+}
+
+/**
+ Write the data to the EHCI operation register.
+
+ @param Ehc The EHCI device.
+ @param Offset EHCI operation register offset.
+ @param Data The data to write.
+
+**/
+VOID
+EhcWriteOpReg (
+ IN PEI_USB2_HC_DEV *Ehc,
+ IN UINT32 Offset,
+ IN UINT32 Data
+ )
+{
+
+ ASSERT (Ehc->CapLen != 0);
+
+ MmioWrite32(Ehc->UsbHostControllerBaseAddress + Ehc->CapLen + Offset, Data);
+
+}
+
+/**
+ Set one bit of the operational register while keeping other bits.
+
+ @param Ehc The EHCI device.
+ @param Offset The offset of the operational register.
+ @param Bit The bit mask of the register to set.
+
+**/
+VOID
+EhcSetOpRegBit (
+ IN PEI_USB2_HC_DEV *Ehc,
+ IN UINT32 Offset,
+ IN UINT32 Bit
+ )
+{
+ UINT32 Data;
+
+ Data = EhcReadOpReg (Ehc, Offset);
+ Data |= Bit;
+ EhcWriteOpReg (Ehc, Offset, Data);
+}
+
+/**
+ Clear one bit of the operational register while keeping other bits.
+
+ @param Ehc The EHCI device.
+ @param Offset The offset of the operational register.
+ @param Bit The bit mask of the register to clear.
+
+**/
+VOID
+EhcClearOpRegBit (
+ IN PEI_USB2_HC_DEV *Ehc,
+ IN UINT32 Offset,
+ IN UINT32 Bit
+ )
+{
+ UINT32 Data;
+
+ Data = EhcReadOpReg (Ehc, Offset);
+ Data &= ~Bit;
+ EhcWriteOpReg (Ehc, Offset, Data);
+}
+
+/**
+ Wait the operation register's bit as specified by Bit
+ to become set (or clear).
+
+ @param Ehc The EHCI device.
+ @param Offset The offset of the operational register.
+ @param Bit The bit mask 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).
+
+ @retval EFI_SUCCESS The bit successfully changed by host controller.
+ @retval EFI_TIMEOUT The time out occurred.
+
+**/
+EFI_STATUS
+EhcWaitOpRegBit (
+ IN PEI_USB2_HC_DEV *Ehc,
+ IN UINT32 Offset,
+ IN UINT32 Bit,
+ IN BOOLEAN WaitToSet,
+ IN UINT32 Timeout
+ )
+{
+ UINT32 Index;
+
+ for (Index = 0; Index < Timeout / EHC_SYNC_POLL_INTERVAL + 1; Index++) {
+ if (EHC_REG_BIT_IS_SET (Ehc, Offset, Bit) == WaitToSet) {
+ return EFI_SUCCESS;
+ }
+
+ MicroSecondDelay (EHC_SYNC_POLL_INTERVAL);
+ }
+
+ return EFI_TIMEOUT;
+}
+
+/**
+ Read EHCI capability register.
+
+ @param Ehc The EHCI device.
+ @param Offset Capability register address.
+
+ @retval the register content read.
+
+**/
+UINT32
+EhcReadCapRegister (
+ IN PEI_USB2_HC_DEV *Ehc,
+ IN UINT32 Offset
+ )
+{
+ UINT32 Data;
+
+ Data = MmioRead32(Ehc->UsbHostControllerBaseAddress + Offset);
+
+ return Data;
+}
+
+/**
+ Set door bell and wait it to be ACKed by host controller.
+ This function is used to synchronize with the hardware.
+
+ @param Ehc The EHCI device.
+ @param Timeout The time to wait before abort (in millisecond, ms).
+
+ @retval EFI_TIMEOUT Time out happened while waiting door bell to set.
+ @retval EFI_SUCCESS Synchronized with the hardware.
+
+**/
+EFI_STATUS
+EhcSetAndWaitDoorBell (
+ IN PEI_USB2_HC_DEV *Ehc,
+ IN UINT32 Timeout
+ )
+{
+ EFI_STATUS Status;
+ UINT32 Data;
+
+ EhcSetOpRegBit (Ehc, EHC_USBCMD_OFFSET, USBCMD_IAAD);
+
+ Status = EhcWaitOpRegBit (Ehc, EHC_USBSTS_OFFSET, USBSTS_IAA, TRUE, Timeout);
+
+ //
+ // ACK the IAA bit in USBSTS register. Make sure other
+ // interrupt bits are not ACKed. These bits are WC (Write Clean).
+ //
+ Data = EhcReadOpReg (Ehc, EHC_USBSTS_OFFSET);
+ Data &= ~USBSTS_INTACK_MASK;
+ Data |= USBSTS_IAA;
+
+ EhcWriteOpReg (Ehc, EHC_USBSTS_OFFSET, Data);
+
+ return Status;
+}
+
+/**
+ Clear all the interrutp status bits, these bits
+ are Write-Clean.
+
+ @param Ehc The EHCI device.
+
+**/
+VOID
+EhcAckAllInterrupt (
+ IN PEI_USB2_HC_DEV *Ehc
+ )
+{
+ EhcWriteOpReg (Ehc, EHC_USBSTS_OFFSET, USBSTS_INTACK_MASK);
+}
+
+/**
+ Enable the periodic schedule then wait EHC to
+ actually enable it.
+
+ @param Ehc The EHCI device.
+ @param Timeout The time to wait before abort (in millisecond, ms).
+
+ @retval EFI_TIMEOUT Time out happened while enabling periodic schedule.
+ @retval EFI_SUCCESS The periodical schedule is enabled.
+
+**/
+EFI_STATUS
+EhcEnablePeriodSchd (
+ IN PEI_USB2_HC_DEV *Ehc,
+ IN UINT32 Timeout
+ )
+{
+ EFI_STATUS Status;
+
+ EhcSetOpRegBit (Ehc, EHC_USBCMD_OFFSET, USBCMD_ENABLE_PERIOD);
+
+ Status = EhcWaitOpRegBit (Ehc, EHC_USBSTS_OFFSET, USBSTS_PERIOD_ENABLED, TRUE, Timeout);
+ return Status;
+}
+
+/**
+ Enable asynchrounous schedule.
+
+ @param Ehc The EHCI device.
+ @param Timeout Time to wait before abort.
+
+ @retval EFI_SUCCESS The EHCI asynchronous schedule is enabled.
+ @retval Others Failed to enable the asynchronous scheudle.
+
+**/
+EFI_STATUS
+EhcEnableAsyncSchd (
+ IN PEI_USB2_HC_DEV *Ehc,
+ IN UINT32 Timeout
+ )
+{
+ EFI_STATUS Status;
+
+ EhcSetOpRegBit (Ehc, EHC_USBCMD_OFFSET, USBCMD_ENABLE_ASYNC);
+
+ Status = EhcWaitOpRegBit (Ehc, EHC_USBSTS_OFFSET, USBSTS_ASYNC_ENABLED, TRUE, Timeout);
+ return Status;
+}
+
+/**
+ Check whether Ehc is halted.
+
+ @param Ehc The EHCI device.
+
+ @retval TRUE The controller is halted.
+ @retval FALSE The controller isn't halted.
+
+**/
+BOOLEAN
+EhcIsHalt (
+ IN PEI_USB2_HC_DEV *Ehc
+ )
+{
+ return EHC_REG_BIT_IS_SET (Ehc, EHC_USBSTS_OFFSET, USBSTS_HALT);
+}
+
+/**
+ Check whether system error occurred.
+
+ @param Ehc The EHCI device.
+
+ @retval TRUE System error happened.
+ @retval FALSE No system error.
+
+**/
+BOOLEAN
+EhcIsSysError (
+ IN PEI_USB2_HC_DEV *Ehc
+ )
+{
+ return EHC_REG_BIT_IS_SET (Ehc, EHC_USBSTS_OFFSET, USBSTS_SYS_ERROR);
+}
+
+/**
+ Reset the host controller.
+
+ @param Ehc The EHCI device.
+ @param Timeout Time to wait before abort (in millisecond, ms).
+
+ @retval EFI_TIMEOUT The transfer failed due to time out.
+ @retval Others Failed to reset the host.
+
+**/
+EFI_STATUS
+EhcResetHC (
+ IN PEI_USB2_HC_DEV *Ehc,
+ IN UINT32 Timeout
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Host can only be reset when it is halt. If not so, halt it
+ //
+ if (!EHC_REG_BIT_IS_SET (Ehc, EHC_USBSTS_OFFSET, USBSTS_HALT)) {
+ Status = EhcHaltHC (Ehc, Timeout);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ EhcSetOpRegBit (Ehc, EHC_USBCMD_OFFSET, USBCMD_RESET);
+ Status = EhcWaitOpRegBit (Ehc, EHC_USBCMD_OFFSET, USBCMD_RESET, FALSE, Timeout);
+ return Status;
+}
+
+/**
+ Halt the host controller.
+
+ @param Ehc The EHCI device.
+ @param Timeout Time to wait before abort.
+
+ @retval EFI_TIMEOUT Failed to halt the controller before Timeout.
+ @retval EFI_SUCCESS The EHCI is halt.
+
+**/
+EFI_STATUS
+EhcHaltHC (
+ IN PEI_USB2_HC_DEV *Ehc,
+ IN UINT32 Timeout
+ )
+{
+ EFI_STATUS Status;
+
+ EhcClearOpRegBit (Ehc, EHC_USBCMD_OFFSET, USBCMD_RUN);
+ Status = EhcWaitOpRegBit (Ehc, EHC_USBSTS_OFFSET, USBSTS_HALT, TRUE, Timeout);
+ return Status;
+}
+
+/**
+ Set the EHCI to run.
+
+ @param Ehc The EHCI device.
+ @param Timeout Time to wait before abort.
+
+ @retval EFI_SUCCESS The EHCI is running.
+ @retval Others Failed to set the EHCI to run.
+
+**/
+EFI_STATUS
+EhcRunHC (
+ IN PEI_USB2_HC_DEV *Ehc,
+ IN UINT32 Timeout
+ )
+{
+ EFI_STATUS Status;
+
+ EhcSetOpRegBit (Ehc, EHC_USBCMD_OFFSET, USBCMD_RUN);
+ Status = EhcWaitOpRegBit (Ehc, EHC_USBSTS_OFFSET, USBSTS_HALT, FALSE, Timeout);
+ return Status;
+}
+
+/**
+ Power On All EHCI Ports.
+
+ @param Ehc The EHCI device.
+
+**/
+VOID
+EhcPowerOnAllPorts (
+ IN PEI_USB2_HC_DEV *Ehc
+ )
+{
+ UINT8 PortNumber;
+ UINT8 Index;
+ UINT32 RegVal;
+
+ PortNumber = (UINT8)(Ehc->HcStructParams & HCSP_NPORTS);
+ for (Index = 0; Index < PortNumber; Index++) {
+ //
+ // Do not clear port status bits on initialization. Otherwise devices will
+ // not enumerate properly at startup.
+ //
+ RegVal = EhcReadOpReg(Ehc, EHC_PORT_STAT_OFFSET + 4 * Index);
+ RegVal &= ~PORTSC_CHANGE_MASK;
+ RegVal |= PORTSC_POWER;
+ EhcWriteOpReg (Ehc, EHC_PORT_STAT_OFFSET + 4 * Index, RegVal);
+ }
+}
+
+/**
+ Initialize the HC hardware.
+ EHCI spec lists the five things to do to initialize the hardware.
+ 1. Program CTRLDSSEGMENT.
+ 2. Set USBINTR to enable interrupts.
+ 3. Set periodic list base.
+ 4. Set USBCMD, interrupt threshold, frame list size etc.
+ 5. Write 1 to CONFIGFLAG to route all ports to EHCI.
+
+ @param Ehc The EHCI device.
+
+ @retval EFI_SUCCESS The EHCI has come out of halt state.
+ @retval EFI_TIMEOUT Time out happened.
+
+**/
+EFI_STATUS
+EhcInitHC (
+ IN PEI_USB2_HC_DEV *Ehc
+ )
+{
+ EFI_STATUS Status;
+ EFI_PHYSICAL_ADDRESS TempPtr;
+ UINTN PageNumber;
+
+ ASSERT (EhcIsHalt (Ehc));
+
+ //
+ // Allocate the periodic frame and associated memeory
+ // management facilities if not already done.
+ //
+ if (Ehc->PeriodFrame != NULL) {
+ EhcFreeSched (Ehc);
+ }
+ PageNumber = sizeof(PEI_URB)/PAGESIZE +1;
+ Status = PeiServicesAllocatePages (
+ EfiBootServicesCode,
+ PageNumber,
+ &TempPtr
+ );
+ Ehc->Urb = (PEI_URB *) ((UINTN) TempPtr);
+ if (Ehc->Urb == NULL) {
+ return Status;
+ }
+
+ EhcPowerOnAllPorts (Ehc);
+ MicroSecondDelay (EHC_ROOT_PORT_RECOVERY_STALL);
+
+ Status = EhcInitSched (Ehc);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ //
+ // 1. Program the CTRLDSSEGMENT register with the high 32 bit addr
+ //
+ EhcWriteOpReg (Ehc, EHC_CTRLDSSEG_OFFSET, Ehc->High32bitAddr);
+
+ //
+ // 2. Clear USBINTR to disable all the interrupt. UEFI works by polling
+ //
+ EhcWriteOpReg (Ehc, EHC_USBINTR_OFFSET, 0);
+
+ //
+ // 3. Program periodic frame list, already done in EhcInitSched
+ // 4. Start the Host Controller
+ //
+ EhcSetOpRegBit (Ehc, EHC_USBCMD_OFFSET, USBCMD_RUN);
+
+ //
+ // 5. Set all ports routing to EHC
+ //
+ EhcSetOpRegBit (Ehc, EHC_CONFIG_FLAG_OFFSET, CONFIGFLAG_ROUTE_EHC);
+
+ //
+ // Wait roothub port power stable
+ //
+ MicroSecondDelay (EHC_ROOT_PORT_RECOVERY_STALL);
+
+ Status = EhcEnablePeriodSchd (Ehc, EHC_GENERIC_TIMEOUT);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = EhcEnableAsyncSchd (Ehc, EHC_GENERIC_TIMEOUT);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Submits bulk transfer to a bulk endpoint of a USB device.
+
+ @param PeiServices The pointer of EFI_PEI_SERVICES.
+ @param This The pointer of PEI_USB2_HOST_CONTROLLER_PPI.
+ @param DeviceAddress Target device address.
+ @param EndPointAddress Endpoint number and its direction in bit 7.
+ @param DeviceSpeed Device speed, Low speed device doesn't support
+ bulk transfer.
+ @param MaximumPacketLength Maximum packet size the endpoint is capable of
+ sending or receiving.
+ @param Data Array of pointers to the buffers of data to transmit
+ from or receive into.
+ @param DataLength The lenght of the data buffer.
+ @param DataToggle On input, the initial data toggle for the transfer;
+ On output, it is updated to to next data toggle to use of
+ the subsequent bulk transfer.
+ @param TimeOut Indicates the maximum time, in millisecond, which the
+ transfer is allowed to complete.
+ If Timeout is 0, then the caller must wait for the function
+ to be completed until EFI_SUCCESS or EFI_DEVICE_ERROR is returned.
+ @param Translator A pointr to the transaction translator data.
+ @param TransferResult A pointer to the detailed result information of the
+ bulk transfer.
+
+ @retval EFI_SUCCESS The transfer was completed successfully.
+ @retval EFI_OUT_OF_RESOURCES The transfer failed due to lack of resource.
+ @retval EFI_INVALID_PARAMETER Parameters are invalid.
+ @retval EFI_TIMEOUT The transfer failed due to timeout.
+ @retval EFI_DEVICE_ERROR The transfer failed due to host controller error.
+
+**/
+EFI_STATUS
+EFIAPI
+EhcBulkTransfer (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN PEI_USB2_HOST_CONTROLLER_PPI *This,
+ IN UINT8 DeviceAddress,
+ IN UINT8 EndPointAddress,
+ IN UINT8 DeviceSpeed,
+ IN UINTN MaximumPacketLength,
+ IN OUT VOID *Data[EFI_USB_MAX_BULK_BUFFER_NUM],
+ IN OUT UINTN *DataLength,
+ IN OUT UINT8 *DataToggle,
+ IN UINTN TimeOut,
+ IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator,
+ OUT UINT32 *TransferResult
+ )
+{
+ PEI_USB2_HC_DEV *Ehc;
+ PEI_URB *Urb;
+ EFI_STATUS Status;
+
+ //
+ // Validate the parameters
+ //
+ if ((DataLength == NULL) || (*DataLength == 0) ||
+ (Data == NULL) || (Data[0] == NULL) || (TransferResult == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((*DataToggle != 0) && (*DataToggle != 1)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((DeviceSpeed == EFI_USB_SPEED_LOW) ||
+ ((DeviceSpeed == EFI_USB_SPEED_FULL) && (MaximumPacketLength > 64)) ||
+ ((EFI_USB_SPEED_HIGH == DeviceSpeed) && (MaximumPacketLength > 512))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Ehc =PEI_RECOVERY_USB_EHC_DEV_FROM_EHCI_THIS(This);
+ *TransferResult = EFI_USB_ERR_SYSTEM;
+ Status = EFI_DEVICE_ERROR;
+
+ if (EhcIsHalt (Ehc) || EhcIsSysError (Ehc)) {
+ EhcAckAllInterrupt (Ehc);
+ goto ON_EXIT;
+ }
+
+ EhcAckAllInterrupt (Ehc);
+
+ //
+ // Create a new URB, insert it into the asynchronous
+ // schedule list, then poll the execution status.
+ //
+ Urb = EhcCreateUrb (
+ Ehc,
+ DeviceAddress,
+ EndPointAddress,
+ DeviceSpeed,
+ *DataToggle,
+ MaximumPacketLength,
+ Translator,
+ EHC_BULK_TRANSFER,
+ NULL,
+ Data[0],
+ *DataLength,
+ NULL,
+ NULL,
+ 1
+ );
+
+ if (Urb == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ EhcLinkQhToAsync (Ehc, Urb->Qh);
+ Status = EhcExecTransfer (Ehc, Urb, TimeOut);
+ EhcUnlinkQhFromAsync (Ehc, Urb->Qh);
+
+ *TransferResult = Urb->Result;
+ *DataLength = Urb->Completed;
+ *DataToggle = Urb->DataToggle;
+
+ if (*TransferResult == EFI_USB_NOERROR) {
+ Status = EFI_SUCCESS;
+ }
+
+ EhcAckAllInterrupt (Ehc);
+ EhcFreeUrb (Ehc, Urb);
+
+ON_EXIT:
+ return Status;
+}
+
+/**
+ Retrieves the number of root hub ports.
+
+ @param[in] PeiServices The pointer to the PEI Services Table.
+ @param[in] This The pointer to this instance of the
+ PEI_USB2_HOST_CONTROLLER_PPI.
+ @param[out] PortNumber The pointer to the number of the root hub ports.
+
+ @retval EFI_SUCCESS The port number was retrieved successfully.
+ @retval EFI_INVALID_PARAMETER PortNumber is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+EhcGetRootHubPortNumber (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN PEI_USB2_HOST_CONTROLLER_PPI *This,
+ OUT UINT8 *PortNumber
+ )
+{
+
+ PEI_USB2_HC_DEV *EhcDev;
+ EhcDev = PEI_RECOVERY_USB_EHC_DEV_FROM_EHCI_THIS (This);
+
+ if (PortNumber == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *PortNumber = (UINT8)(EhcDev->HcStructParams & HCSP_NPORTS);
+ return EFI_SUCCESS;
+
+}
+
+/**
+ Clears a feature for the specified root hub port.
+
+ @param PeiServices The pointer of EFI_PEI_SERVICES.
+ @param This The pointer of PEI_USB2_HOST_CONTROLLER_PPI.
+ @param PortNumber Specifies the root hub port whose feature
+ is requested to be cleared.
+ @param PortFeature Indicates the feature selector associated with the
+ feature clear request.
+
+ @retval EFI_SUCCESS The feature specified by PortFeature was cleared
+ for the USB root hub port specified by PortNumber.
+ @retval EFI_INVALID_PARAMETER PortNumber is invalid or PortFeature is invalid.
+
+**/
+EFI_STATUS
+EFIAPI
+EhcClearRootHubPortFeature (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN PEI_USB2_HOST_CONTROLLER_PPI *This,
+ IN UINT8 PortNumber,
+ IN EFI_USB_PORT_FEATURE PortFeature
+ )
+{
+ PEI_USB2_HC_DEV *Ehc;
+ UINT32 Offset;
+ UINT32 State;
+ UINT32 TotalPort;
+ EFI_STATUS Status;
+
+ Ehc = PEI_RECOVERY_USB_EHC_DEV_FROM_EHCI_THIS (This);
+ Status = EFI_SUCCESS;
+
+ TotalPort = (Ehc->HcStructParams & HCSP_NPORTS);
+
+ if (PortNumber >= TotalPort) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ Offset = EHC_PORT_STAT_OFFSET + (4 * PortNumber);
+ State = EhcReadOpReg (Ehc, Offset);
+ State &= ~PORTSC_CHANGE_MASK;
+
+ switch (PortFeature) {
+ case EfiUsbPortEnable:
+ //
+ // Clear PORT_ENABLE feature means disable port.
+ //
+ State &= ~PORTSC_ENABLED;
+ EhcWriteOpReg (Ehc, Offset, State);
+ break;
+
+ case EfiUsbPortSuspend:
+ //
+ // A write of zero to this bit is ignored by the host
+ // controller. The host controller will unconditionally
+ // set this bit to a zero when:
+ // 1. software sets the Forct Port Resume bit to a zero from a one.
+ // 2. software sets the Port Reset bit to a one frome a zero.
+ //
+ State &= ~PORSTSC_RESUME;
+ EhcWriteOpReg (Ehc, Offset, State);
+ break;
+
+ case EfiUsbPortReset:
+ //
+ // Clear PORT_RESET means clear the reset signal.
+ //
+ State &= ~PORTSC_RESET;
+ EhcWriteOpReg (Ehc, Offset, State);
+ break;
+
+ case EfiUsbPortOwner:
+ //
+ // Clear port owner means this port owned by EHC
+ //
+ State &= ~PORTSC_OWNER;
+ EhcWriteOpReg (Ehc, Offset, State);
+ break;
+
+ case EfiUsbPortConnectChange:
+ //
+ // Clear connect status change
+ //
+ State |= PORTSC_CONN_CHANGE;
+ EhcWriteOpReg (Ehc, Offset, State);
+ break;
+
+ case EfiUsbPortEnableChange:
+ //
+ // Clear enable status change
+ //
+ State |= PORTSC_ENABLE_CHANGE;
+ EhcWriteOpReg (Ehc, Offset, State);
+ break;
+
+ case EfiUsbPortOverCurrentChange:
+ //
+ // Clear PortOverCurrent change
+ //
+ State |= PORTSC_OVERCUR_CHANGE;
+ EhcWriteOpReg (Ehc, Offset, State);
+ break;
+
+ case EfiUsbPortPower:
+ case EfiUsbPortSuspendChange:
+ case EfiUsbPortResetChange:
+ //
+ // Not supported or not related operation
+ //
+ break;
+
+ default:
+ Status = EFI_INVALID_PARAMETER;
+ break;
+ }
+
+ON_EXIT:
+ return Status;
+}
+
+/**
+ Sets a feature for the specified root hub port.
+
+ @param PeiServices The pointer of EFI_PEI_SERVICES
+ @param This The pointer of PEI_USB2_HOST_CONTROLLER_PPI
+ @param PortNumber Root hub port to set.
+ @param PortFeature Feature to set.
+
+ @retval EFI_SUCCESS The feature specified by PortFeature was set.
+ @retval EFI_INVALID_PARAMETER PortNumber is invalid or PortFeature is invalid.
+ @retval EFI_TIMEOUT The time out occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EhcSetRootHubPortFeature (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN PEI_USB2_HOST_CONTROLLER_PPI *This,
+ IN UINT8 PortNumber,
+ IN EFI_USB_PORT_FEATURE PortFeature
+ )
+{
+ PEI_USB2_HC_DEV *Ehc;
+ UINT32 Offset;
+ UINT32 State;
+ UINT32 TotalPort;
+ EFI_STATUS Status;
+
+ Ehc = PEI_RECOVERY_USB_EHC_DEV_FROM_EHCI_THIS (This);
+ Status = EFI_SUCCESS;
+
+ TotalPort = (Ehc->HcStructParams & HCSP_NPORTS);
+
+ if (PortNumber >= TotalPort) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ Offset = (UINT32) (EHC_PORT_STAT_OFFSET + (4 * PortNumber));
+ State = EhcReadOpReg (Ehc, Offset);
+
+ //
+ // Mask off the port status change bits, these bits are
+ // write clean bit
+ //
+ State &= ~PORTSC_CHANGE_MASK;
+
+ switch (PortFeature) {
+ case EfiUsbPortEnable:
+ //
+ // Sofeware can't set this bit, Port can only be enable by
+ // EHCI as a part of the reset and enable
+ //
+ State |= PORTSC_ENABLED;
+ EhcWriteOpReg (Ehc, Offset, State);
+ break;
+
+ case EfiUsbPortSuspend:
+ State |= PORTSC_SUSPEND;
+ EhcWriteOpReg (Ehc, Offset, State);
+ break;
+
+ case EfiUsbPortReset:
+ //
+ // Make sure Host Controller not halt before reset it
+ //
+ if (EhcIsHalt (Ehc)) {
+ Status = EhcRunHC (Ehc, EHC_GENERIC_TIMEOUT);
+
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+ }
+
+ //
+ // Set one to PortReset bit must also set zero to PortEnable bit
+ //
+ State |= PORTSC_RESET;
+ State &= ~PORTSC_ENABLED;
+ EhcWriteOpReg (Ehc, Offset, State);
+ break;
+
+ case EfiUsbPortPower:
+ //
+ // Not supported, ignore the operation
+ //
+ Status = EFI_SUCCESS;
+ break;
+
+ case EfiUsbPortOwner:
+ State |= PORTSC_OWNER;
+ EhcWriteOpReg (Ehc, Offset, State);
+ break;
+
+ default:
+ Status = EFI_INVALID_PARAMETER;
+ }
+
+ON_EXIT:
+ return Status;
+}
+
+/**
+ Retrieves the current status of a USB root hub port.
+
+ @param PeiServices The pointer of EFI_PEI_SERVICES.
+ @param This The pointer of PEI_USB2_HOST_CONTROLLER_PPI.
+ @param PortNumber The root hub port to retrieve the state from.
+ @param PortStatus Variable to receive the port state.
+
+ @retval EFI_SUCCESS The status of the USB root hub port specified.
+ by PortNumber was returned in PortStatus.
+ @retval EFI_INVALID_PARAMETER PortNumber is invalid.
+
+**/
+EFI_STATUS
+EFIAPI
+EhcGetRootHubPortStatus (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN PEI_USB2_HOST_CONTROLLER_PPI *This,
+ IN UINT8 PortNumber,
+ OUT EFI_USB_PORT_STATUS *PortStatus
+ )
+{
+ PEI_USB2_HC_DEV *Ehc;
+ UINT32 Offset;
+ UINT32 State;
+ UINT32 TotalPort;
+ UINTN Index;
+ UINTN MapSize;
+ EFI_STATUS Status;
+
+ if (PortStatus == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Ehc = PEI_RECOVERY_USB_EHC_DEV_FROM_EHCI_THIS(This);
+ Status = EFI_SUCCESS;
+
+ TotalPort = (Ehc->HcStructParams & HCSP_NPORTS);
+
+ if (PortNumber >= TotalPort) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ Offset = (UINT32) (EHC_PORT_STAT_OFFSET + (4 * PortNumber));
+ PortStatus->PortStatus = 0;
+ PortStatus->PortChangeStatus = 0;
+
+ State = EhcReadOpReg (Ehc, Offset);
+
+ //
+ // Identify device speed. If in K state, it is low speed.
+ // If the port is enabled after reset, the device is of
+ // high speed. The USB bus driver should retrieve the actual
+ // port speed after reset.
+ //
+ if (EHC_BIT_IS_SET (State, PORTSC_LINESTATE_K)) {
+ PortStatus->PortStatus |= USB_PORT_STAT_LOW_SPEED;
+
+ } else if (EHC_BIT_IS_SET (State, PORTSC_ENABLED)) {
+ PortStatus->PortStatus |= USB_PORT_STAT_HIGH_SPEED;
+ }
+
+ //
+ // Convert the EHCI port/port change state to UEFI status
+ //
+ MapSize = sizeof (mUsbPortStateMap) / sizeof (USB_PORT_STATE_MAP);
+
+ for (Index = 0; Index < MapSize; Index++) {
+ if (EHC_BIT_IS_SET (State, mUsbPortStateMap[Index].HwState)) {
+ PortStatus->PortStatus = (UINT16) (PortStatus->PortStatus | mUsbPortStateMap[Index].UefiState);
+ }
+ }
+
+ MapSize = sizeof (mUsbPortChangeMap) / sizeof (USB_PORT_STATE_MAP);
+
+ for (Index = 0; Index < MapSize; Index++) {
+ if (EHC_BIT_IS_SET (State, mUsbPortChangeMap[Index].HwState)) {
+ PortStatus->PortChangeStatus = (UINT16) (PortStatus->PortChangeStatus | mUsbPortChangeMap[Index].UefiState);
+ }
+ }
+
+ON_EXIT:
+ return Status;
+}
+
+/**
+ Submits control transfer to a target USB device.
+
+ @param PeiServices The pointer of EFI_PEI_SERVICES.
+ @param This The pointer of PEI_USB2_HOST_CONTROLLER_PPI.
+ @param DeviceAddress The target device address.
+ @param DeviceSpeed Target device speed.
+ @param MaximumPacketLength Maximum packet size the default control transfer
+ endpoint is capable of sending or receiving.
+ @param Request USB device request to send.
+ @param TransferDirection Specifies the data direction for the data stage.
+ @param Data Data buffer to be transmitted or received from USB device.
+ @param DataLength The size (in bytes) of the data buffer.
+ @param TimeOut Indicates the maximum timeout, in millisecond.
+ If Timeout is 0, then the caller must wait for the function
+ to be completed until EFI_SUCCESS or EFI_DEVICE_ERROR is returned.
+ @param Translator Transaction translator to be used by this device.
+ @param TransferResult Return the result of this control transfer.
+
+ @retval EFI_SUCCESS Transfer was completed successfully.
+ @retval EFI_OUT_OF_RESOURCES The transfer failed due to lack of resources.
+ @retval EFI_INVALID_PARAMETER Some parameters are invalid.
+ @retval EFI_TIMEOUT Transfer failed due to timeout.
+ @retval EFI_DEVICE_ERROR Transfer failed due to host controller or device error.
+
+**/
+EFI_STATUS
+EFIAPI
+EhcControlTransfer (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN PEI_USB2_HOST_CONTROLLER_PPI *This,
+ IN UINT8 DeviceAddress,
+ IN UINT8 DeviceSpeed,
+ IN UINTN MaximumPacketLength,
+ IN EFI_USB_DEVICE_REQUEST *Request,
+ IN EFI_USB_DATA_DIRECTION TransferDirection,
+ IN OUT VOID *Data,
+ IN OUT UINTN *DataLength,
+ IN UINTN TimeOut,
+ IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator,
+ OUT UINT32 *TransferResult
+ )
+{
+ PEI_USB2_HC_DEV *Ehc;
+ PEI_URB *Urb;
+ UINT8 Endpoint;
+ EFI_STATUS Status;
+
+ //
+ // Validate parameters
+ //
+ if ((Request == NULL) || (TransferResult == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((TransferDirection != EfiUsbDataIn) &&
+ (TransferDirection != EfiUsbDataOut) &&
+ (TransferDirection != EfiUsbNoData)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((TransferDirection == EfiUsbNoData) &&
+ ((Data != NULL) || (*DataLength != 0))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((TransferDirection != EfiUsbNoData) &&
+ ((Data == NULL) || (*DataLength == 0))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((MaximumPacketLength != 8) && (MaximumPacketLength != 16) &&
+ (MaximumPacketLength != 32) && (MaximumPacketLength != 64)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+
+ if ((DeviceSpeed == EFI_USB_SPEED_LOW) ||
+ ((DeviceSpeed == EFI_USB_SPEED_FULL) && (MaximumPacketLength > 64)) ||
+ ((EFI_USB_SPEED_HIGH == DeviceSpeed) && (MaximumPacketLength > 512))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Ehc = PEI_RECOVERY_USB_EHC_DEV_FROM_EHCI_THIS (This);
+
+ Status = EFI_DEVICE_ERROR;
+ *TransferResult = EFI_USB_ERR_SYSTEM;
+
+ if (EhcIsHalt (Ehc) || EhcIsSysError (Ehc)) {
+ EhcAckAllInterrupt (Ehc);
+ goto ON_EXIT;
+ }
+
+ EhcAckAllInterrupt (Ehc);
+
+ //
+ // Create a new URB, insert it into the asynchronous
+ // schedule list, then poll the execution status.
+ //
+ //
+ // Encode the direction in address, although default control
+ // endpoint is bidirectional. EhcCreateUrb expects this
+ // combination of Ep addr and its direction.
+ //
+ Endpoint = (UINT8) (0 | ((TransferDirection == EfiUsbDataIn) ? 0x80 : 0));
+ Urb = EhcCreateUrb (
+ Ehc,
+ DeviceAddress,
+ Endpoint,
+ DeviceSpeed,
+ 0,
+ MaximumPacketLength,
+ Translator,
+ EHC_CTRL_TRANSFER,
+ Request,
+ Data,
+ *DataLength,
+ NULL,
+ NULL,
+ 1
+ );
+
+ if (Urb == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ EhcLinkQhToAsync (Ehc, Urb->Qh);
+ Status = EhcExecTransfer (Ehc, Urb, TimeOut);
+ EhcUnlinkQhFromAsync (Ehc, Urb->Qh);
+
+ //
+ // Get the status from URB. The result is updated in EhcCheckUrbResult
+ // which is called by EhcExecTransfer
+ //
+ *TransferResult = Urb->Result;
+ *DataLength = Urb->Completed;
+
+ if (*TransferResult == EFI_USB_NOERROR) {
+ Status = EFI_SUCCESS;
+ }
+
+ EhcAckAllInterrupt (Ehc);
+ EhcFreeUrb (Ehc, Urb);
+
+ON_EXIT:
+ return Status;
+}
+
+/**
+ One notified function to stop the Host Controller at the end of PEI
+
+ @param[in] PeiServices Pointer to PEI Services Table.
+ @param[in] NotifyDescriptor Pointer to the descriptor for the Notification event that
+ caused this function to execute.
+ @param[in] Ppi Pointer to the PPI data associated with this function.
+
+ @retval EFI_SUCCESS The function completes successfully
+ @retval others
+**/
+EFI_STATUS
+EFIAPI
+EhcEndOfPei (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor,
+ IN VOID *Ppi
+ )
+{
+ PEI_USB2_HC_DEV *Ehc;
+
+ Ehc = PEI_RECOVERY_USB_EHC_DEV_FROM_THIS_NOTIFY (NotifyDescriptor);
+
+ EhcHaltHC (Ehc, EHC_GENERIC_TIMEOUT);
+
+ EhcFreeSched (Ehc);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ @param FileHandle Handle of the file being invoked.
+ @param PeiServices Describes the list of possible PEI Services.
+
+ @retval EFI_SUCCESS PPI successfully installed.
+
+**/
+EFI_STATUS
+EFIAPI
+EhcPeimEntry (
+ IN EFI_PEI_FILE_HANDLE FileHandle,
+ IN CONST EFI_PEI_SERVICES **PeiServices
+ )
+{
+ PEI_USB_CONTROLLER_PPI *ChipSetUsbControllerPpi;
+ EFI_STATUS Status;
+ UINT8 Index;
+ UINTN ControllerType;
+ UINTN BaseAddress;
+ UINTN MemPages;
+ PEI_USB2_HC_DEV *EhcDev;
+ EFI_PHYSICAL_ADDRESS TempPtr;
+
+ //
+ // Shadow this PEIM to run from memory
+ //
+ if (!EFI_ERROR (PeiServicesRegisterForShadow (FileHandle))) {
+ return EFI_SUCCESS;
+ }
+
+ Status = PeiServicesLocatePpi (
+ &gPeiUsbControllerPpiGuid,
+ 0,
+ NULL,
+ (VOID **) &ChipSetUsbControllerPpi
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Index = 0;
+ while (TRUE) {
+ Status = ChipSetUsbControllerPpi->GetUsbController (
+ (EFI_PEI_SERVICES **) PeiServices,
+ ChipSetUsbControllerPpi,
+ Index,
+ &ControllerType,
+ &BaseAddress
+ );
+ //
+ // When status is error, meant no controller is found
+ //
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+
+ //
+ // This PEIM is for UHC type controller.
+ //
+ if (ControllerType != PEI_EHCI_CONTROLLER) {
+ Index++;
+ continue;
+ }
+
+ MemPages = sizeof (PEI_USB2_HC_DEV) / PAGESIZE + 1;
+ Status = PeiServicesAllocatePages (
+ EfiBootServicesCode,
+ MemPages,
+ &TempPtr
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ ZeroMem((VOID *)(UINTN)TempPtr, MemPages*PAGESIZE);
+ EhcDev = (PEI_USB2_HC_DEV *) ((UINTN) TempPtr);
+
+ EhcDev->Signature = USB2_HC_DEV_SIGNATURE;
+
+ IoMmuInit (&EhcDev->IoMmu);
+
+ EhcDev->UsbHostControllerBaseAddress = (UINT32) BaseAddress;
+
+
+ EhcDev->HcStructParams = EhcReadCapRegister (EhcDev, EHC_HCSPARAMS_OFFSET);
+ EhcDev->HcCapParams = EhcReadCapRegister (EhcDev, EHC_HCCPARAMS_OFFSET);
+ EhcDev->CapLen = EhcReadCapRegister (EhcDev, EHC_CAPLENGTH_OFFSET) & 0x0FF;
+ //
+ // Initialize Uhc's hardware
+ //
+ Status = InitializeUsbHC (EhcDev);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ EhcDev->Usb2HostControllerPpi.ControlTransfer = EhcControlTransfer;
+ EhcDev->Usb2HostControllerPpi.BulkTransfer = EhcBulkTransfer;
+ EhcDev->Usb2HostControllerPpi.GetRootHubPortNumber = EhcGetRootHubPortNumber;
+ EhcDev->Usb2HostControllerPpi.GetRootHubPortStatus = EhcGetRootHubPortStatus;
+ EhcDev->Usb2HostControllerPpi.SetRootHubPortFeature = EhcSetRootHubPortFeature;
+ EhcDev->Usb2HostControllerPpi.ClearRootHubPortFeature = EhcClearRootHubPortFeature;
+
+ EhcDev->PpiDescriptor.Flags = (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST);
+ EhcDev->PpiDescriptor.Guid = &gPeiUsb2HostControllerPpiGuid;
+ EhcDev->PpiDescriptor.Ppi = &EhcDev->Usb2HostControllerPpi;
+
+ Status = PeiServicesInstallPpi (&EhcDev->PpiDescriptor);
+ if (EFI_ERROR (Status)) {
+ Index++;
+ continue;
+ }
+
+ EhcDev->EndOfPeiNotifyList.Flags = (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST);
+ EhcDev->EndOfPeiNotifyList.Guid = &gEfiEndOfPeiSignalPpiGuid;
+ EhcDev->EndOfPeiNotifyList.Notify = EhcEndOfPei;
+
+ PeiServicesNotifyPpi (&EhcDev->EndOfPeiNotifyList);
+
+ Index++;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ @param EhcDev EHCI Device.
+
+ @retval EFI_SUCCESS EHCI successfully initialized.
+ @retval EFI_ABORTED EHCI was failed to be initialized.
+
+**/
+EFI_STATUS
+InitializeUsbHC (
+ IN PEI_USB2_HC_DEV *EhcDev
+ )
+{
+ EFI_STATUS Status;
+
+
+ EhcResetHC (EhcDev, EHC_RESET_TIMEOUT);
+
+ Status = EhcInitHC (EhcDev);
+
+ if (EFI_ERROR (Status)) {
+ return EFI_ABORTED;
+ }
+
+ return EFI_SUCCESS;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/EhcPeim.h b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/EhcPeim.h
new file mode 100644
index 00000000..373f8384
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/EhcPeim.h
@@ -0,0 +1,316 @@
+/** @file
+Private Header file for Usb Host Controller PEIM
+
+Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR>
+Copyright (c) Microsoft Corporation.<BR>
+
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _RECOVERY_EHC_H_
+#define _RECOVERY_EHC_H_
+
+#include <PiPei.h>
+
+#include <Ppi/UsbController.h>
+#include <Ppi/Usb2HostController.h>
+#include <Ppi/IoMmu.h>
+#include <Ppi/EndOfPeiPhase.h>
+
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/PeimEntryPoint.h>
+#include <Library/PeiServicesLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/TimerLib.h>
+#include <Library/IoLib.h>
+
+typedef struct _PEI_USB2_HC_DEV PEI_USB2_HC_DEV;
+
+#define EFI_LIST_ENTRY LIST_ENTRY
+
+#include "UsbHcMem.h"
+#include "EhciReg.h"
+#include "EhciUrb.h"
+#include "EhciSched.h"
+
+#define EFI_USB_SPEED_FULL 0x0000
+#define EFI_USB_SPEED_LOW 0x0001
+#define EFI_USB_SPEED_HIGH 0x0002
+
+#define PAGESIZE 4096
+
+#define EHC_1_MICROSECOND 1
+#define EHC_1_MILLISECOND (1000 * EHC_1_MICROSECOND)
+#define EHC_1_SECOND (1000 * EHC_1_MILLISECOND)
+
+//
+// EHCI register operation timeout, set by experience
+//
+#define EHC_RESET_TIMEOUT (1 * EHC_1_SECOND)
+#define EHC_GENERIC_TIMEOUT (10 * EHC_1_MILLISECOND)
+
+
+//
+// Wait for roothub port power stable, refers to Spec[EHCI1.0-2.3.9]
+//
+#define EHC_ROOT_PORT_RECOVERY_STALL (20 * EHC_1_MILLISECOND)
+
+//
+// Sync transfer polling interval, set by experience.
+//
+#define EHC_SYNC_POLL_INTERVAL (6 * EHC_1_MILLISECOND)
+
+#define EFI_LIST_CONTAINER(Entry, Type, Field) BASE_CR(Entry, Type, Field)
+
+
+#define EHC_LOW_32BIT(Addr64) ((UINT32)(((UINTN)(Addr64)) & 0XFFFFFFFF))
+#define EHC_HIGH_32BIT(Addr64) ((UINT32)(RShiftU64((UINTN)(Addr64), 32) & 0XFFFFFFFF))
+#define EHC_BIT_IS_SET(Data, Bit) ((BOOLEAN)(((Data) & (Bit)) == (Bit)))
+
+#define EHC_REG_BIT_IS_SET(Ehc, Offset, Bit) \
+ (EHC_BIT_IS_SET(EhcReadOpReg ((Ehc), (Offset)), (Bit)))
+
+#define USB2_HC_DEV_SIGNATURE SIGNATURE_32 ('e', 'h', 'c', 'i')
+
+struct _PEI_USB2_HC_DEV {
+ UINTN Signature;
+ PEI_USB2_HOST_CONTROLLER_PPI Usb2HostControllerPpi;
+ EDKII_IOMMU_PPI *IoMmu;
+ EFI_PEI_PPI_DESCRIPTOR PpiDescriptor;
+ //
+ // EndOfPei callback is used to stop the EHC DMA operation
+ // after exit PEI phase.
+ //
+ EFI_PEI_NOTIFY_DESCRIPTOR EndOfPeiNotifyList;
+ UINT32 UsbHostControllerBaseAddress;
+ PEI_URB *Urb;
+ USBHC_MEM_POOL *MemPool;
+
+ //
+ // Schedule data shared between asynchronous and periodic
+ // transfers:
+ // ShortReadStop, as its name indicates, is used to terminate
+ // the short read except the control transfer. EHCI follows
+ // the alternative next QTD point when a short read happens.
+ // For control transfer, even the short read happens, try the
+ // status stage.
+ //
+ PEI_EHC_QTD *ShortReadStop;
+ EFI_EVENT PollTimer;
+
+ //
+ // Asynchronous(bulk and control) transfer schedule data:
+ // ReclaimHead is used as the head of the asynchronous transfer
+ // list. It acts as the reclamation header.
+ //
+ PEI_EHC_QH *ReclaimHead;
+
+ //
+ // Periodic (interrupt) transfer schedule data:
+ //
+ VOID *PeriodFrame; // Mapped as common buffer
+ VOID *PeriodFrameMap;
+
+ PEI_EHC_QH *PeriodOne;
+ EFI_LIST_ENTRY AsyncIntTransfers;
+
+ //
+ // EHCI configuration data
+ //
+ UINT32 HcStructParams; // Cache of HC structure parameter, EHC_HCSPARAMS_OFFSET
+ UINT32 HcCapParams; // Cache of HC capability parameter, HCCPARAMS
+ UINT32 CapLen; // Capability length
+ UINT32 High32bitAddr;
+};
+
+#define PEI_RECOVERY_USB_EHC_DEV_FROM_EHCI_THIS(a) CR (a, PEI_USB2_HC_DEV, Usb2HostControllerPpi, USB2_HC_DEV_SIGNATURE)
+#define PEI_RECOVERY_USB_EHC_DEV_FROM_THIS_NOTIFY(a) CR (a, PEI_USB2_HC_DEV, EndOfPeiNotifyList, USB2_HC_DEV_SIGNATURE)
+
+/**
+ @param EhcDev EHCI Device.
+
+ @retval EFI_SUCCESS EHCI successfully initialized.
+ @retval EFI_ABORTED EHCI was failed to be initialized.
+
+**/
+EFI_STATUS
+InitializeUsbHC (
+ IN PEI_USB2_HC_DEV *EhcDev
+ );
+
+/**
+ Initialize the memory management pool for the host controller.
+
+ @param Ehc The EHCI device.
+ @param Check4G Whether the host controller requires allocated memory
+ from one 4G address space.
+ @param Which4G The 4G memory area each memory allocated should be from.
+
+ @retval EFI_SUCCESS The memory pool is initialized.
+ @retval EFI_OUT_OF_RESOURCE Fail to init the memory pool.
+
+**/
+USBHC_MEM_POOL *
+UsbHcInitMemPool (
+ IN PEI_USB2_HC_DEV *Ehc,
+ IN BOOLEAN Check4G,
+ IN UINT32 Which4G
+ )
+;
+
+/**
+ Release the memory management pool.
+
+ @param Ehc The EHCI device.
+ @param Pool The USB memory pool to free.
+
+ @retval EFI_DEVICE_ERROR Fail to free the memory pool.
+ @retval EFI_SUCCESS The memory pool is freed.
+
+**/
+EFI_STATUS
+UsbHcFreeMemPool (
+ IN PEI_USB2_HC_DEV *Ehc,
+ IN USBHC_MEM_POOL *Pool
+ )
+;
+
+/**
+ Allocate some memory from the host controller's memory pool
+ which can be used to communicate with host controller.
+
+ @param Ehc The EHCI device.
+ @param Pool The host controller's memory pool.
+ @param Size Size of the memory to allocate.
+
+ @return The allocated memory or NULL.
+
+**/
+VOID *
+UsbHcAllocateMem (
+ IN PEI_USB2_HC_DEV *Ehc,
+ IN USBHC_MEM_POOL *Pool,
+ IN UINTN Size
+ )
+;
+
+/**
+ Free the allocated memory back to the memory pool.
+
+ @param Ehc The EHCI device.
+ @param Pool The memory pool of the host controller.
+ @param Mem The memory to free.
+ @param Size The size of the memory to free.
+
+**/
+VOID
+UsbHcFreeMem (
+ IN PEI_USB2_HC_DEV *Ehc,
+ IN USBHC_MEM_POOL *Pool,
+ IN VOID *Mem,
+ IN UINTN Size
+ )
+;
+
+/**
+ Provides the controller-specific addresses required to access system memory from a
+ DMA bus master.
+
+ @param IoMmu Pointer to IOMMU PPI.
+ @param Operation Indicates if the bus master is going to read or write to system memory.
+ @param HostAddress The system memory address to map to the PCI controller.
+ @param NumberOfBytes On input the number of bytes to map. On output the number of bytes
+ that were mapped.
+ @param DeviceAddress The resulting map address for the bus master PCI controller to use to
+ access the hosts HostAddress.
+ @param Mapping A resulting value to pass to Unmap().
+
+ @retval EFI_SUCCESS The range was mapped for the returned NumberOfBytes.
+ @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a common buffer.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
+ @retval EFI_DEVICE_ERROR The system hardware could not map the requested address.
+
+**/
+EFI_STATUS
+IoMmuMap (
+ IN EDKII_IOMMU_PPI *IoMmu,
+ IN EDKII_IOMMU_OPERATION Operation,
+ IN VOID *HostAddress,
+ IN OUT UINTN *NumberOfBytes,
+ OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,
+ OUT VOID **Mapping
+ );
+
+/**
+ Completes the Map() operation and releases any corresponding resources.
+
+ @param IoMmu Pointer to IOMMU PPI.
+ @param Mapping The mapping value returned from Map().
+
+**/
+VOID
+IoMmuUnmap (
+ IN EDKII_IOMMU_PPI *IoMmu,
+ IN VOID *Mapping
+ );
+
+/**
+ Allocates pages that are suitable for an OperationBusMasterCommonBuffer or
+ OperationBusMasterCommonBuffer64 mapping.
+
+ @param IoMmu Pointer to IOMMU PPI.
+ @param Pages The number of pages to allocate.
+ @param HostAddress A pointer to store the base system memory address of the
+ allocated range.
+ @param DeviceAddress The resulting map address for the bus master PCI controller to use to
+ access the hosts HostAddress.
+ @param Mapping A resulting value to pass to Unmap().
+
+ @retval EFI_SUCCESS The requested memory pages were allocated.
+ @retval EFI_UNSUPPORTED Attributes is unsupported. The only legal attribute bits are
+ MEMORY_WRITE_COMBINE and MEMORY_CACHED.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated.
+
+**/
+EFI_STATUS
+IoMmuAllocateBuffer (
+ IN EDKII_IOMMU_PPI *IoMmu,
+ IN UINTN Pages,
+ OUT VOID **HostAddress,
+ OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,
+ OUT VOID **Mapping
+ );
+
+/**
+ Frees memory that was allocated with AllocateBuffer().
+
+ @param IoMmu Pointer to IOMMU PPI.
+ @param Pages The number of pages to free.
+ @param HostAddress The base system memory address of the allocated range.
+ @param Mapping The mapping value returned from Map().
+
+**/
+VOID
+IoMmuFreeBuffer (
+ IN EDKII_IOMMU_PPI *IoMmu,
+ IN UINTN Pages,
+ IN VOID *HostAddress,
+ IN VOID *Mapping
+ );
+
+/**
+ Initialize IOMMU.
+
+ @param IoMmu Pointer to pointer to IOMMU PPI.
+
+**/
+VOID
+IoMmuInit (
+ OUT EDKII_IOMMU_PPI **IoMmu
+ );
+
+#endif
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/EhciPei.inf b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/EhciPei.inf
new file mode 100644
index 00000000..2dc41932
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/EhciPei.inf
@@ -0,0 +1,67 @@
+## @file
+# The EhcPeim driver is responsible for managing EHCI host controller at PEI phase.
+#
+# It produces gPeiUsb2HostControllerPpiGuid based on gPeiUsbControllerPpiGuid
+# which is used to enable recovery function from USB Drivers.
+#
+# Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR>
+# Copyright (c) Microsoft Corporation.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = EhciPei
+ MODULE_UNI_FILE = EhciPei.uni
+ FILE_GUID = BAB4F20F-0981-4b5f-A047-6EF83BEEAB3C
+ MODULE_TYPE = PEIM
+ VERSION_STRING = 1.0
+
+ ENTRY_POINT = EhcPeimEntry
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 EBC
+#
+
+[Sources]
+ EhcPeim.c
+ EhcPeim.h
+ EhciUrb.c
+ EhciSched.c
+ UsbHcMem.c
+ EhciReg.h
+ EhciSched.h
+ EhciUrb.h
+ UsbHcMem.h
+ DmaMem.c
+
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+
+[LibraryClasses]
+ IoLib
+ TimerLib
+ BaseLib
+ BaseMemoryLib
+ PeimEntryPoint
+ PeiServicesLib
+
+
+[Ppis]
+ gPeiUsb2HostControllerPpiGuid ## PRODUCES
+ gPeiUsbControllerPpiGuid ## CONSUMES
+ gEdkiiIoMmuPpiGuid ## CONSUMES
+ gEfiEndOfPeiSignalPpiGuid ## CONSUMES
+
+[Depex]
+ gEfiPeiMemoryDiscoveredPpiGuid AND gPeiUsbControllerPpiGuid
+
+[UserExtensions.TianoCore."ExtraFiles"]
+ EhciPeiExtra.uni
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/EhciPei.uni b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/EhciPei.uni
new file mode 100644
index 00000000..798a8cc0
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/EhciPei.uni
@@ -0,0 +1,17 @@
+// /** @file
+// The EhcPeim driver is responsible for managing EHCI host controller at PEI phase.
+//
+// It produces gPeiUsb2HostControllerPpiGuid based on gPeiUsbControllerPpiGuid
+// which is used to enable recovery function from USB Drivers.
+//
+// Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Responsible for managing EHCI host controllers at the PEI phase"
+
+#string STR_MODULE_DESCRIPTION #language en-US "It produces gPeiUsb2HostControllerPpiGuid based on gPeiUsbControllerPpiGuid, which is used to enable recovery function from USB Drivers."
+
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/EhciPeiExtra.uni b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/EhciPeiExtra.uni
new file mode 100644
index 00000000..cf53e805
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/EhciPeiExtra.uni
@@ -0,0 +1,14 @@
+// /** @file
+// EhciPei Localized Strings and Content
+//
+// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"EHCI PEI Module for Recovery"
+
+
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/EhciReg.h b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/EhciReg.h
new file mode 100644
index 00000000..ee6ea6a2
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/EhciReg.h
@@ -0,0 +1,303 @@
+/** @file
+Private Header file for Usb Host Controller PEIM
+
+Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR>
+
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _EFI_EHCI_REG_H_
+#define _EFI_EHCI_REG_H_
+
+
+
+//
+// Capability register offset
+//
+#define EHC_CAPLENGTH_OFFSET 0 // Capability register length offset
+#define EHC_HCSPARAMS_OFFSET 0x04 // Structural Parameters 04-07h
+#define EHC_HCCPARAMS_OFFSET 0x08 // Capability parameters offset
+
+//
+// Capability register bit definition
+//
+#define HCSP_NPORTS 0x0F // Number of root hub port
+#define HCCP_64BIT 0x01 // 64-bit addressing capability
+
+//
+// Operational register offset
+//
+#define EHC_USBCMD_OFFSET 0x0 // USB command register offset
+#define EHC_USBSTS_OFFSET 0x04 // Statue register offset
+#define EHC_USBINTR_OFFSET 0x08 // USB interrutp offset
+#define EHC_FRINDEX_OFFSET 0x0C // Frame index offset
+#define EHC_CTRLDSSEG_OFFSET 0x10 // Control data structure segment offset
+#define EHC_FRAME_BASE_OFFSET 0x14 // Frame list base address offset
+#define EHC_ASYNC_HEAD_OFFSET 0x18 // Next asynchronous list address offset
+#define EHC_CONFIG_FLAG_OFFSET 0x40 // Configure flag register offset
+#define EHC_PORT_STAT_OFFSET 0x44 // Port status/control offset
+
+#define EHC_FRAME_LEN 1024
+
+//
+// Register bit definition
+//
+#define CONFIGFLAG_ROUTE_EHC 0x01 // Route port to EHC
+
+#define USBCMD_RUN 0x01 // Run/stop
+#define USBCMD_RESET 0x02 // Start the host controller reset
+#define USBCMD_ENABLE_PERIOD 0x10 // Enable periodic schedule
+#define USBCMD_ENABLE_ASYNC 0x20 // Enable asynchronous schedule
+#define USBCMD_IAAD 0x40 // Interrupt on async advance doorbell
+
+#define USBSTS_IAA 0x20 // Interrupt on async advance
+#define USBSTS_PERIOD_ENABLED 0x4000 // Periodic schedule status
+#define USBSTS_ASYNC_ENABLED 0x8000 // Asynchronous schedule status
+#define USBSTS_HALT 0x1000 // Host controller halted
+#define USBSTS_SYS_ERROR 0x10 // Host system error
+#define USBSTS_INTACK_MASK 0x003F // Mask for the interrupt ACK, the WC
+ // (write clean) bits in USBSTS register
+
+#define PORTSC_CONN 0x01 // Current Connect Status
+#define PORTSC_CONN_CHANGE 0x02 // Connect Status Change
+#define PORTSC_ENABLED 0x04 // Port Enable / Disable
+#define PORTSC_ENABLE_CHANGE 0x08 // Port Enable / Disable Change
+#define PORTSC_OVERCUR 0x10 // Over current Active
+#define PORTSC_OVERCUR_CHANGE 0x20 // Over current Change
+#define PORSTSC_RESUME 0x40 // Force Port Resume
+#define PORTSC_SUSPEND 0x80 // Port Suspend State
+#define PORTSC_RESET 0x100 // Port Reset
+#define PORTSC_LINESTATE_K 0x400 // Line Status K-state
+#define PORTSC_LINESTATE_J 0x800 // Line Status J-state
+#define PORTSC_POWER 0x1000 // Port Power
+#define PORTSC_OWNER 0x2000 // Port Owner
+#define PORTSC_CHANGE_MASK 0x2A // Mask of the port change bits,
+ // they are WC (write clean)
+//
+// PCI Configuration Registers
+//
+#define EHC_BAR_INDEX 0 // how many bytes away from USB_BASE to 0x10
+
+#define EHC_LINK_TERMINATED(Link) (((Link) & 0x01) != 0)
+
+#define EHC_ADDR(High, QhHw32) \
+ ((VOID *) (UINTN) (LShiftU64 ((High), 32) | ((QhHw32) & 0xFFFFFFF0)))
+
+#define EHCI_IS_DATAIN(EndpointAddr) EHC_BIT_IS_SET((EndpointAddr), 0x80)
+
+//
+// Structure to map the hardware port states to the
+// UEFI's port states.
+//
+typedef struct {
+ UINT16 HwState;
+ UINT16 UefiState;
+} USB_PORT_STATE_MAP;
+
+//
+// Ehci Data and Ctrl Structures
+//
+#pragma pack(1)
+typedef struct {
+ UINT8 Pi;
+ UINT8 SubClassCode;
+ UINT8 BaseCode;
+} USB_CLASSC;
+#pragma pack()
+
+
+/**
+ Read EHCI capability register.
+
+ @param Ehc The EHCI device.
+ @param Offset Capability register address.
+
+ @retval the register content read.
+
+**/
+UINT32
+EhcReadCapRegister (
+ IN PEI_USB2_HC_DEV *Ehc,
+ IN UINT32 Offset
+ )
+;
+
+/**
+ Read Ehc Operation register.
+
+ @param Ehc The EHCI device.
+ @param Offset The operation register offset.
+
+ @retval the register content read.
+
+**/
+UINT32
+EhcReadOpReg (
+ IN PEI_USB2_HC_DEV *Ehc,
+ IN UINT32 Offset
+ )
+;
+
+/**
+ Write the data to the EHCI operation register.
+
+ @param Ehc The EHCI device.
+ @param Offset EHCI operation register offset.
+ @param Data The data to write.
+
+**/
+VOID
+EhcWriteOpReg (
+ IN PEI_USB2_HC_DEV *Ehc,
+ IN UINT32 Offset,
+ IN UINT32 Data
+ )
+;
+
+/**
+ Stop the legacy USB SMI support.
+
+ @param Ehc The EHCI device.
+
+**/
+VOID
+EhcClearLegacySupport (
+ IN PEI_USB2_HC_DEV *Ehc
+ )
+;
+
+/**
+ Set door bell and wait it to be ACKed by host controller.
+ This function is used to synchronize with the hardware.
+
+ @param Ehc The EHCI device.
+ @param Timeout The time to wait before abort (in millisecond, ms).
+
+ @retval EFI_TIMEOUT Time out happened while waiting door bell to set.
+ @retval EFI_SUCCESS Synchronized with the hardware.
+
+**/
+EFI_STATUS
+EhcSetAndWaitDoorBell (
+ IN PEI_USB2_HC_DEV *Ehc,
+ IN UINT32 Timeout
+ )
+;
+
+/**
+ Clear all the interrutp status bits, these bits
+ are Write-Clean.
+
+ @param Ehc The EHCI device.
+
+**/
+VOID
+EhcAckAllInterrupt (
+ IN PEI_USB2_HC_DEV *Ehc
+ )
+;
+
+/**
+ Check whether Ehc is halted.
+
+ @param Ehc The EHCI device.
+
+ @retval TRUE The controller is halted.
+ @retval FALSE The controller isn't halted.
+
+**/
+BOOLEAN
+EhcIsHalt (
+ IN PEI_USB2_HC_DEV *Ehc
+ )
+;
+
+/**
+ Check whether system error occurred.
+
+ @param Ehc The EHCI device.
+
+ @retval TRUE System error happened.
+ @retval FALSE No system error.
+
+**/
+BOOLEAN
+EhcIsSysError (
+ IN PEI_USB2_HC_DEV *Ehc
+ )
+;
+
+/**
+ Reset the host controller.
+
+ @param Ehc The EHCI device.
+ @param Timeout Time to wait before abort (in millisecond, ms).
+
+ @retval EFI_TIMEOUT The transfer failed due to time out.
+ @retval Others Failed to reset the host.
+
+**/
+EFI_STATUS
+EhcResetHC (
+ IN PEI_USB2_HC_DEV *Ehc,
+ IN UINT32 Timeout
+ )
+;
+
+/**
+ Halt the host controller.
+
+ @param Ehc The EHCI device.
+ @param Timeout Time to wait before abort.
+
+ @retval EFI_TIMEOUT Failed to halt the controller before Timeout.
+ @retval EFI_SUCCESS The EHCI is halt.
+
+**/
+EFI_STATUS
+EhcHaltHC (
+ IN PEI_USB2_HC_DEV *Ehc,
+ IN UINT32 Timeout
+ )
+;
+
+/**
+ Set the EHCI to run
+
+ @param Ehc The EHCI device.
+ @param Timeout Time to wait before abort.
+
+ @retval EFI_SUCCESS The EHCI is running.
+ @retval Others Failed to set the EHCI to run.
+
+**/
+EFI_STATUS
+EhcRunHC (
+ IN PEI_USB2_HC_DEV *Ehc,
+ IN UINT32 Timeout
+ )
+;
+
+/**
+ Initialize the HC hardware.
+ EHCI spec lists the five things to do to initialize the hardware.
+ 1. Program CTRLDSSEGMENT.
+ 2. Set USBINTR to enable interrupts.
+ 3. Set periodic list base.
+ 4. Set USBCMD, interrupt threshold, frame list size etc.
+ 5. Write 1 to CONFIGFLAG to route all ports to EHCI.
+
+ @param Ehc The EHCI device.
+
+ @retval EFI_SUCCESS The EHCI has come out of halt state.
+ @retval EFI_TIMEOUT Time out happened.
+
+**/
+EFI_STATUS
+EhcInitHC (
+ IN PEI_USB2_HC_DEV *Ehc
+ )
+;
+
+#endif
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/EhciSched.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/EhciSched.c
new file mode 100644
index 00000000..d0b4c42d
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/EhciSched.c
@@ -0,0 +1,463 @@
+/** @file
+PEIM to produce gPeiUsb2HostControllerPpiGuid based on gPeiUsbControllerPpiGuid
+which is used to enable recovery function from USB Drivers.
+
+Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR>
+Copyright (c) Microsoft Corporation.<BR>
+
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "EhcPeim.h"
+
+/**
+ Create helper QTD/QH for the EHCI device.
+
+ @param Ehc The EHCI device.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for helper QTD/QH.
+ @retval EFI_SUCCESS Helper QH/QTD are created.
+
+**/
+EFI_STATUS
+EhcCreateHelpQ (
+ IN PEI_USB2_HC_DEV *Ehc
+ )
+{
+ USB_ENDPOINT Ep;
+ PEI_EHC_QH *Qh;
+ QH_HW *QhHw;
+ PEI_EHC_QTD *Qtd;
+
+ //
+ // Create an inactive Qtd to terminate the short packet read.
+ //
+ Qtd = EhcCreateQtd (Ehc, NULL, 0, QTD_PID_INPUT, 0, 64);
+
+ if (Qtd == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Qtd->QtdHw.Status = QTD_STAT_HALTED;
+ Ehc->ShortReadStop = Qtd;
+
+ //
+ // Create a QH to act as the EHC reclamation header.
+ // Set the header to loopback to itself.
+ //
+ Ep.DevAddr = 0;
+ Ep.EpAddr = 1;
+ Ep.Direction = EfiUsbDataIn;
+ Ep.DevSpeed = EFI_USB_SPEED_HIGH;
+ Ep.MaxPacket = 64;
+ Ep.HubAddr = 0;
+ Ep.HubPort = 0;
+ Ep.Toggle = 0;
+ Ep.Type = EHC_BULK_TRANSFER;
+ Ep.PollRate = 1;
+
+ Qh = EhcCreateQh (Ehc, &Ep);
+
+ if (Qh == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ QhHw = &Qh->QhHw;
+ QhHw->HorizonLink = QH_LINK (QhHw, EHC_TYPE_QH, FALSE);
+ QhHw->Status = QTD_STAT_HALTED;
+ QhHw->ReclaimHead = 1;
+ Ehc->ReclaimHead = Qh;
+
+ //
+ // Create a dummy QH to act as the terminator for periodical schedule
+ //
+ Ep.EpAddr = 2;
+ Ep.Type = EHC_INT_TRANSFER_SYNC;
+
+ Qh = EhcCreateQh (Ehc, &Ep);
+
+ if (Qh == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Qh->QhHw.Status = QTD_STAT_HALTED;
+ Ehc->PeriodOne = Qh;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Initialize the schedule data structure such as frame list.
+
+ @param Ehc The EHCI device to init schedule data for.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to init schedule data.
+ @retval EFI_SUCCESS The schedule data is initialized.
+
+**/
+EFI_STATUS
+EhcInitSched (
+ IN PEI_USB2_HC_DEV *Ehc
+ )
+{
+ VOID *Buf;
+ EFI_PHYSICAL_ADDRESS PhyAddr;
+ VOID *Map;
+ UINTN Index;
+ UINT32 *Desc;
+ EFI_STATUS Status;
+ EFI_PHYSICAL_ADDRESS PciAddr;
+
+ //
+ // First initialize the periodical schedule data:
+ // 1. Allocate and map the memory for the frame list
+ // 2. Create the help QTD/QH
+ // 3. Initialize the frame entries
+ // 4. Set the frame list register
+ //
+ //
+ // The Frame List ocupies 4K bytes,
+ // and must be aligned on 4-Kbyte boundaries.
+ //
+ Status = IoMmuAllocateBuffer (
+ Ehc->IoMmu,
+ 1,
+ &Buf,
+ &PhyAddr,
+ &Map
+ );
+
+ if (EFI_ERROR (Status) || (Buf == NULL)) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Ehc->PeriodFrame = Buf;
+ Ehc->PeriodFrameMap = Map;
+ Ehc->High32bitAddr = EHC_HIGH_32BIT (PhyAddr);
+
+ //
+ // Init memory pool management then create the helper
+ // QTD/QH. If failed, previously allocated resources
+ // will be freed by EhcFreeSched
+ //
+ Ehc->MemPool = UsbHcInitMemPool (
+ Ehc,
+ EHC_BIT_IS_SET (Ehc->HcCapParams, HCCP_64BIT),
+ Ehc->High32bitAddr
+ );
+
+ if (Ehc->MemPool == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = EhcCreateHelpQ (Ehc);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Initialize the frame list entries then set the registers
+ //
+ Desc = (UINT32 *) Ehc->PeriodFrame;
+ PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Ehc->PeriodOne, sizeof (PEI_EHC_QH));
+ for (Index = 0; Index < EHC_FRAME_LEN; Index++) {
+ Desc[Index] = QH_LINK (PciAddr, EHC_TYPE_QH, FALSE);
+ }
+
+ EhcWriteOpReg (Ehc, EHC_FRAME_BASE_OFFSET, EHC_LOW_32BIT (PhyAddr));
+
+ //
+ // Second initialize the asynchronous schedule:
+ // Only need to set the AsynListAddr register to
+ // the reclamation header
+ //
+ PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Ehc->ReclaimHead, sizeof (PEI_EHC_QH));
+ EhcWriteOpReg (Ehc, EHC_ASYNC_HEAD_OFFSET, EHC_LOW_32BIT (PciAddr));
+ return EFI_SUCCESS;
+}
+
+/**
+ Free the schedule data. It may be partially initialized.
+
+ @param Ehc The EHCI device.
+
+**/
+VOID
+EhcFreeSched (
+ IN PEI_USB2_HC_DEV *Ehc
+ )
+{
+ EhcWriteOpReg (Ehc, EHC_FRAME_BASE_OFFSET, 0);
+ EhcWriteOpReg (Ehc, EHC_ASYNC_HEAD_OFFSET, 0);
+
+ if (Ehc->PeriodOne != NULL) {
+ UsbHcFreeMem (Ehc, Ehc->MemPool, Ehc->PeriodOne, sizeof (PEI_EHC_QH));
+ Ehc->PeriodOne = NULL;
+ }
+
+ if (Ehc->ReclaimHead != NULL) {
+ UsbHcFreeMem (Ehc, Ehc->MemPool, Ehc->ReclaimHead, sizeof (PEI_EHC_QH));
+ Ehc->ReclaimHead = NULL;
+ }
+
+ if (Ehc->ShortReadStop != NULL) {
+ UsbHcFreeMem (Ehc, Ehc->MemPool, Ehc->ShortReadStop, sizeof (PEI_EHC_QTD));
+ Ehc->ShortReadStop = NULL;
+ }
+
+ if (Ehc->MemPool != NULL) {
+ UsbHcFreeMemPool (Ehc, Ehc->MemPool);
+ Ehc->MemPool = NULL;
+ }
+
+ if (Ehc->PeriodFrame != NULL) {
+ IoMmuFreeBuffer (Ehc->IoMmu, 1, Ehc->PeriodFrame, Ehc->PeriodFrameMap);
+ Ehc->PeriodFrame = NULL;
+ }
+}
+
+/**
+ Link the queue head to the asynchronous schedule list.
+ UEFI only supports one CTRL/BULK transfer at a time
+ due to its interfaces. This simplifies the AsynList
+ management: A reclamation header is always linked to
+ the AsyncListAddr, the only active QH is appended to it.
+
+ @param Ehc The EHCI device.
+ @param Qh The queue head to link.
+
+**/
+VOID
+EhcLinkQhToAsync (
+ IN PEI_USB2_HC_DEV *Ehc,
+ IN PEI_EHC_QH *Qh
+ )
+{
+ PEI_EHC_QH *Head;
+
+ //
+ // Append the queue head after the reclaim header, then
+ // fix the hardware visiable parts (EHCI R1.0 page 72).
+ // ReclaimHead is always linked to the EHCI's AsynListAddr.
+ //
+ Head = Ehc->ReclaimHead;
+
+ Qh->NextQh = Head->NextQh;
+ Head->NextQh = Qh;
+
+ Qh->QhHw.HorizonLink = QH_LINK (Head, EHC_TYPE_QH, FALSE);;
+ Head->QhHw.HorizonLink = QH_LINK (Qh, EHC_TYPE_QH, FALSE);
+}
+
+/**
+ Unlink a queue head from the asynchronous schedule list.
+ Need to synchronize with hardware.
+
+ @param Ehc The EHCI device.
+ @param Qh The queue head to unlink.
+
+**/
+VOID
+EhcUnlinkQhFromAsync (
+ IN PEI_USB2_HC_DEV *Ehc,
+ IN PEI_EHC_QH *Qh
+ )
+{
+ PEI_EHC_QH *Head;
+
+ ASSERT (Ehc->ReclaimHead->NextQh == Qh);
+
+ //
+ // Remove the QH from reclamation head, then update the hardware
+ // visiable part: Only need to loopback the ReclaimHead. The Qh
+ // is pointing to ReclaimHead (which is staill in the list).
+ //
+ Head = Ehc->ReclaimHead;
+
+ Head->NextQh = Qh->NextQh;
+ Qh->NextQh = NULL;
+
+ Head->QhHw.HorizonLink = QH_LINK (Head, EHC_TYPE_QH, FALSE);
+
+ //
+ // Set and wait the door bell to synchronize with the hardware
+ //
+ EhcSetAndWaitDoorBell (Ehc, EHC_GENERIC_TIMEOUT);
+
+ return;
+}
+
+/**
+ Check the URB's execution result and update the URB's
+ result accordingly.
+
+ @param Ehc The EHCI device.
+ @param Urb The URB to check result.
+
+ @retval TRUE URB transfer is finialized.
+ @retval FALSE URB transfer is not finialized.
+
+**/
+BOOLEAN
+EhcCheckUrbResult (
+ IN PEI_USB2_HC_DEV *Ehc,
+ IN PEI_URB *Urb
+ )
+{
+ EFI_LIST_ENTRY *Entry;
+ PEI_EHC_QTD *Qtd;
+ QTD_HW *QtdHw;
+ UINT8 State;
+ BOOLEAN Finished;
+
+ ASSERT ((Ehc != NULL) && (Urb != NULL) && (Urb->Qh != NULL));
+
+ Finished = TRUE;
+ Urb->Completed = 0;
+
+ Urb->Result = EFI_USB_NOERROR;
+
+ if (EhcIsHalt (Ehc) || EhcIsSysError (Ehc)) {
+ Urb->Result |= EFI_USB_ERR_SYSTEM;
+ goto ON_EXIT;
+ }
+
+ BASE_LIST_FOR_EACH (Entry, &Urb->Qh->Qtds) {
+ Qtd = EFI_LIST_CONTAINER (Entry, PEI_EHC_QTD, QtdList);
+ QtdHw = &Qtd->QtdHw;
+ State = (UINT8) QtdHw->Status;
+
+ if (EHC_BIT_IS_SET (State, QTD_STAT_HALTED)) {
+ //
+ // EHCI will halt the queue head when met some error.
+ // If it is halted, the result of URB is finialized.
+ //
+ if ((State & QTD_STAT_ERR_MASK) == 0) {
+ Urb->Result |= EFI_USB_ERR_STALL;
+ }
+
+ if (EHC_BIT_IS_SET (State, QTD_STAT_BABBLE_ERR)) {
+ Urb->Result |= EFI_USB_ERR_BABBLE;
+ }
+
+ if (EHC_BIT_IS_SET (State, QTD_STAT_BUFF_ERR)) {
+ Urb->Result |= EFI_USB_ERR_BUFFER;
+ }
+
+ if (EHC_BIT_IS_SET (State, QTD_STAT_TRANS_ERR) && (QtdHw->ErrCnt == 0)) {
+ Urb->Result |= EFI_USB_ERR_TIMEOUT;
+ }
+
+ Finished = TRUE;
+ goto ON_EXIT;
+
+ } else if (EHC_BIT_IS_SET (State, QTD_STAT_ACTIVE)) {
+ //
+ // The QTD is still active, no need to check furthur.
+ //
+ Urb->Result |= EFI_USB_ERR_NOTEXECUTE;
+
+ Finished = FALSE;
+ goto ON_EXIT;
+
+ } else {
+ //
+ // This QTD is finished OK or met short packet read. Update the
+ // transfer length if it isn't a setup.
+ //
+ if (QtdHw->Pid != QTD_PID_SETUP) {
+ Urb->Completed += Qtd->DataLen - QtdHw->TotalBytes;
+ }
+
+ if ((QtdHw->TotalBytes != 0) && (QtdHw->Pid == QTD_PID_INPUT)) {
+ //EHC_DUMP_QH ((Urb->Qh, "Short packet read", FALSE));
+
+ //
+ // Short packet read condition. If it isn't a setup transfer,
+ // no need to check furthur: the queue head will halt at the
+ // ShortReadStop. If it is a setup transfer, need to check the
+ // Status Stage of the setup transfer to get the finial result
+ //
+ if (QtdHw->AltNext == QTD_LINK (Ehc->ShortReadStop, FALSE)) {
+
+ Finished = TRUE;
+ goto ON_EXIT;
+ }
+ }
+ }
+ }
+
+ON_EXIT:
+ //
+ // Return the data toggle set by EHCI hardware, bulk and interrupt
+ // transfer will use this to initialize the next transaction. For
+ // Control transfer, it always start a new data toggle sequence for
+ // new transfer.
+ //
+ // NOTICE: don't move DT update before the loop, otherwise there is
+ // a race condition that DT is wrong.
+ //
+ Urb->DataToggle = (UINT8) Urb->Qh->QhHw.DataToggle;
+
+ return Finished;
+}
+
+/**
+ Execute the transfer by polling the URB. This is a synchronous operation.
+
+ @param Ehc The EHCI device.
+ @param Urb The URB to execute.
+ @param TimeOut The time to wait before abort, in millisecond.
+
+ @retval EFI_DEVICE_ERROR The transfer failed due to transfer error.
+ @retval EFI_TIMEOUT The transfer failed due to time out.
+ @retval EFI_SUCCESS The transfer finished OK.
+
+**/
+EFI_STATUS
+EhcExecTransfer (
+ IN PEI_USB2_HC_DEV *Ehc,
+ IN PEI_URB *Urb,
+ IN UINTN TimeOut
+ )
+{
+ EFI_STATUS Status;
+ UINTN Index;
+ UINTN Loop;
+ BOOLEAN Finished;
+ BOOLEAN InfiniteLoop;
+
+ Status = EFI_SUCCESS;
+ Loop = TimeOut * EHC_1_MILLISECOND;
+ Finished = FALSE;
+ InfiniteLoop = FALSE;
+
+ //
+ // If Timeout is 0, then the caller must wait for the function to be completed
+ // until EFI_SUCCESS or EFI_DEVICE_ERROR is returned.
+ //
+ if (TimeOut == 0) {
+ InfiniteLoop = TRUE;
+ }
+
+ for (Index = 0; InfiniteLoop || (Index < Loop); Index++) {
+ Finished = EhcCheckUrbResult (Ehc, Urb);
+
+ if (Finished) {
+ break;
+ }
+
+ MicroSecondDelay (EHC_1_MICROSECOND);
+ }
+
+ if (!Finished) {
+ Status = EFI_TIMEOUT;
+ } else if (Urb->Result != EFI_USB_NOERROR) {
+ Status = EFI_DEVICE_ERROR;
+ }
+
+ return Status;
+}
+
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/EhciSched.h b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/EhciSched.h
new file mode 100644
index 00000000..060fcc2d
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/EhciSched.h
@@ -0,0 +1,93 @@
+/** @file
+Private Header file for Usb Host Controller PEIM
+
+Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR>
+
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _EFI_EHCI_SCHED_H_
+#define _EFI_EHCI_SCHED_H_
+
+/**
+ Initialize the schedule data structure such as frame list.
+
+ @param Ehc The EHCI device to init schedule data for.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to init schedule data.
+ @retval EFI_SUCCESS The schedule data is initialized.
+
+**/
+EFI_STATUS
+EhcInitSched (
+ IN PEI_USB2_HC_DEV *Ehc
+ )
+;
+
+/**
+ Free the schedule data. It may be partially initialized.
+
+ @param Ehc The EHCI device.
+
+**/
+VOID
+EhcFreeSched (
+ IN PEI_USB2_HC_DEV *Ehc
+ )
+;
+
+/**
+ Link the queue head to the asynchronous schedule list.
+ UEFI only supports one CTRL/BULK transfer at a time
+ due to its interfaces. This simplifies the AsynList
+ management: A reclamation header is always linked to
+ the AsyncListAddr, the only active QH is appended to it.
+
+ @param Ehc The EHCI device.
+ @param Qh The queue head to link.
+
+**/
+VOID
+EhcLinkQhToAsync (
+ IN PEI_USB2_HC_DEV *Ehc,
+ IN PEI_EHC_QH *Qh
+ )
+;
+
+/**
+ Unlink a queue head from the asynchronous schedule list.
+ Need to synchronize with hardware.
+
+ @param Ehc The EHCI device.
+ @param Qh The queue head to unlink.
+
+**/
+VOID
+EhcUnlinkQhFromAsync (
+ IN PEI_USB2_HC_DEV *Ehc,
+ IN PEI_EHC_QH *Qh
+ )
+;
+
+/**
+ Execute the transfer by polling the URB. This is a synchronous operation.
+
+ @param Ehc The EHCI device.
+ @param Urb The URB to execute.
+ @param TimeOut The time to wait before abort, in millisecond.
+
+ @retval EFI_DEVICE_ERROR The transfer failed due to transfer error.
+ @retval EFI_TIMEOUT The transfer failed due to time out.
+ @retval EFI_SUCCESS The transfer finished OK.
+
+**/
+EFI_STATUS
+EhcExecTransfer (
+ IN PEI_USB2_HC_DEV *Ehc,
+ IN PEI_URB *Urb,
+ IN UINTN TimeOut
+ )
+;
+
+#endif
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/EhciUrb.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/EhciUrb.c
new file mode 100644
index 00000000..e00e0231
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/EhciUrb.c
@@ -0,0 +1,627 @@
+/** @file
+PEIM to produce gPeiUsb2HostControllerPpiGuid based on gPeiUsbControllerPpiGuid
+which is used to enable recovery function from USB Drivers.
+
+Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR>
+Copyright (c) Microsoft Corporation.<BR>
+
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "EhcPeim.h"
+
+/**
+ Delete a single asynchronous interrupt transfer for
+ the device and endpoint.
+
+ @param Ehc The EHCI device.
+ @param Data Current data not associated with a QTD.
+ @param DataLen The length of the data.
+ @param PktId Packet ID to use in the QTD.
+ @param Toggle Data toggle to use in the QTD.
+ @param MaxPacket Maximu packet length of the endpoint.
+
+ @retval the pointer to the created QTD or NULL if failed to create one.
+
+**/
+PEI_EHC_QTD *
+EhcCreateQtd (
+ IN PEI_USB2_HC_DEV *Ehc,
+ IN UINT8 *Data,
+ IN UINTN DataLen,
+ IN UINT8 PktId,
+ IN UINT8 Toggle,
+ IN UINTN MaxPacket
+ )
+{
+ PEI_EHC_QTD *Qtd;
+ QTD_HW *QtdHw;
+ UINTN Index;
+ UINTN Len;
+ UINTN ThisBufLen;
+
+ ASSERT (Ehc != NULL);
+
+ Qtd = UsbHcAllocateMem (Ehc, Ehc->MemPool, sizeof (PEI_EHC_QTD));
+
+ if (Qtd == NULL) {
+ return NULL;
+ }
+
+ Qtd->Signature = EHC_QTD_SIG;
+ Qtd->Data = Data;
+ Qtd->DataLen = 0;
+
+ InitializeListHead (&Qtd->QtdList);
+
+ QtdHw = &Qtd->QtdHw;
+ QtdHw->NextQtd = QTD_LINK (NULL, TRUE);
+ QtdHw->AltNext = QTD_LINK (NULL, TRUE);
+ QtdHw->Status = QTD_STAT_ACTIVE;
+ QtdHw->Pid = PktId;
+ QtdHw->ErrCnt = QTD_MAX_ERR;
+ QtdHw->Ioc = 0;
+ QtdHw->TotalBytes = 0;
+ QtdHw->DataToggle = Toggle;
+
+ //
+ // Fill in the buffer points
+ //
+ if (Data != NULL) {
+ Len = 0;
+
+ for (Index = 0; Index <= QTD_MAX_BUFFER; Index++) {
+ //
+ // Set the buffer point (Check page 41 EHCI Spec 1.0). No need to
+ // compute the offset and clear Reserved fields. This is already
+ // done in the data point.
+ //
+ QtdHw->Page[Index] = EHC_LOW_32BIT (Data);
+ QtdHw->PageHigh[Index] = EHC_HIGH_32BIT (Data);
+
+ ThisBufLen = QTD_BUF_LEN - (EHC_LOW_32BIT (Data) & QTD_BUF_MASK);
+
+ if (Len + ThisBufLen >= DataLen) {
+ Len = DataLen;
+ break;
+ }
+
+ Len += ThisBufLen;
+ Data += ThisBufLen;
+ }
+
+ //
+ // Need to fix the last pointer if the Qtd can't hold all the
+ // user's data to make sure that the length is in the unit of
+ // max packets. If it can hold all the data, there is no such
+ // need.
+ //
+ if (Len < DataLen) {
+ Len = Len - Len % MaxPacket;
+ }
+
+ QtdHw->TotalBytes = (UINT32) Len;
+ Qtd->DataLen = Len;
+ }
+
+ return Qtd;
+}
+
+/**
+ Initialize the queue head for interrupt transfer,
+ that is, initialize the following three fields:
+ 1. SplitXState in the Status field.
+ 2. Microframe S-mask.
+ 3. Microframe C-mask.
+
+ @param Ep The queue head's related endpoint.
+ @param QhHw The queue head to initialize.
+
+**/
+VOID
+EhcInitIntQh (
+ IN USB_ENDPOINT *Ep,
+ IN QH_HW *QhHw
+ )
+{
+ //
+ // Because UEFI interface can't utilitize an endpoint with
+ // poll rate faster than 1ms, only need to set one bit in
+ // the queue head. simple. But it may be changed later. If
+ // sub-1ms interrupt is supported, need to update the S-Mask
+ // here
+ //
+ if (Ep->DevSpeed == EFI_USB_SPEED_HIGH) {
+ QhHw->SMask = QH_MICROFRAME_0;
+ return ;
+ }
+
+ //
+ // For low/full speed device, the transfer must go through
+ // the split transaction. Need to update three fields
+ // 1. SplitXState in the status
+ // 2. Microframe S-Mask
+ // 3. Microframe C-Mask
+ // UEFI USB doesn't exercise admission control. It simplely
+ // schedule the high speed transactions in microframe 0, and
+ // full/low speed transactions at microframe 1. This also
+ // avoid the use of FSTN.
+ //
+ QhHw->SMask = QH_MICROFRAME_1;
+ QhHw->CMask = QH_MICROFRAME_3 | QH_MICROFRAME_4 | QH_MICROFRAME_5;
+}
+
+/**
+ Allocate and initialize a EHCI queue head.
+
+ @param Ehci The EHCI device.
+ @param Ep The endpoint to create queue head for.
+
+ @retval the pointer to the created queue head or NULL if failed to create one.
+
+**/
+PEI_EHC_QH *
+EhcCreateQh (
+ IN PEI_USB2_HC_DEV *Ehci,
+ IN USB_ENDPOINT *Ep
+ )
+{
+ PEI_EHC_QH *Qh;
+ QH_HW *QhHw;
+
+ Qh = UsbHcAllocateMem (Ehci, Ehci->MemPool, sizeof (PEI_EHC_QH));
+
+ if (Qh == NULL) {
+ return NULL;
+ }
+
+ Qh->Signature = EHC_QH_SIG;
+ Qh->NextQh = NULL;
+ Qh->Interval = Ep->PollRate;
+
+ InitializeListHead (&Qh->Qtds);
+
+ QhHw = &Qh->QhHw;
+ QhHw->HorizonLink = QH_LINK (NULL, 0, TRUE);
+ QhHw->DeviceAddr = Ep->DevAddr;
+ QhHw->Inactive = 0;
+ QhHw->EpNum = Ep->EpAddr;
+ QhHw->EpSpeed = Ep->DevSpeed;
+ QhHw->DtCtrl = 0;
+ QhHw->ReclaimHead = 0;
+ QhHw->MaxPacketLen = (UINT32) Ep->MaxPacket;
+ QhHw->CtrlEp = 0;
+ QhHw->NakReload = QH_NAK_RELOAD;
+ QhHw->HubAddr = Ep->HubAddr;
+ QhHw->PortNum = Ep->HubPort;
+ QhHw->Multiplier = 1;
+ QhHw->DataToggle = Ep->Toggle;
+
+ if (Ep->DevSpeed != EFI_USB_SPEED_HIGH) {
+ QhHw->Status |= QTD_STAT_DO_SS;
+ }
+
+ switch (Ep->Type) {
+ case EHC_CTRL_TRANSFER:
+ //
+ // Special initialization for the control transfer:
+ // 1. Control transfer initialize data toggle from each QTD
+ // 2. Set the Control Endpoint Flag (C) for low/full speed endpoint.
+ //
+ QhHw->DtCtrl = 1;
+
+ if (Ep->DevSpeed != EFI_USB_SPEED_HIGH) {
+ QhHw->CtrlEp = 1;
+ }
+ break;
+
+ case EHC_INT_TRANSFER_ASYNC:
+ case EHC_INT_TRANSFER_SYNC:
+ //
+ // Special initialization for the interrupt transfer
+ // to set the S-Mask and C-Mask
+ //
+ QhHw->NakReload = 0;
+ EhcInitIntQh (Ep, QhHw);
+ break;
+
+ case EHC_BULK_TRANSFER:
+ if ((Ep->DevSpeed == EFI_USB_SPEED_HIGH) && (Ep->Direction == EfiUsbDataOut)) {
+ QhHw->Status |= QTD_STAT_DO_PING;
+ }
+
+ break;
+ }
+
+ return Qh;
+}
+
+/**
+ Convert the poll interval from application to that
+ be used by EHCI interface data structure. Only need
+ to get the max 2^n that is less than interval. UEFI
+ can't support high speed endpoint with a interval less
+ than 8 microframe because interval is specified in
+ the unit of ms (millisecond).
+
+ @param Interval The interval to convert.
+
+ @retval The converted interval.
+
+**/
+UINTN
+EhcConvertPollRate (
+ IN UINTN Interval
+ )
+{
+ UINTN BitCount;
+
+ if (Interval == 0) {
+ return 1;
+ }
+
+ //
+ // Find the index (1 based) of the highest non-zero bit
+ //
+ BitCount = 0;
+
+ while (Interval != 0) {
+ Interval >>= 1;
+ BitCount++;
+ }
+
+ return (UINTN)1 << (BitCount - 1);
+}
+
+/**
+ Free a list of QTDs.
+
+ @param Ehc The EHCI device.
+ @param Qtds The list head of the QTD.
+
+**/
+VOID
+EhcFreeQtds (
+ IN PEI_USB2_HC_DEV *Ehc,
+ IN EFI_LIST_ENTRY *Qtds
+ )
+{
+ EFI_LIST_ENTRY *Entry;
+ EFI_LIST_ENTRY *Next;
+ PEI_EHC_QTD *Qtd;
+
+ BASE_LIST_FOR_EACH_SAFE (Entry, Next, Qtds) {
+ Qtd = EFI_LIST_CONTAINER (Entry, PEI_EHC_QTD, QtdList);
+
+ RemoveEntryList (&Qtd->QtdList);
+ UsbHcFreeMem (Ehc, Ehc->MemPool, Qtd, sizeof (PEI_EHC_QTD));
+ }
+}
+
+/**
+ Free an allocated URB. It is possible for it to be partially inited.
+
+ @param Ehc The EHCI device.
+ @param Urb The URB to free.
+
+**/
+VOID
+EhcFreeUrb (
+ IN PEI_USB2_HC_DEV *Ehc,
+ IN PEI_URB *Urb
+ )
+{
+ if (Urb->RequestPhy != NULL) {
+ IoMmuUnmap (Ehc->IoMmu, Urb->RequestMap);
+ }
+
+ if (Urb->DataMap != NULL) {
+ IoMmuUnmap (Ehc->IoMmu, Urb->DataMap);
+ }
+
+ if (Urb->Qh != NULL) {
+ //
+ // Ensure that this queue head has been unlinked from the
+ // schedule data structures. Free all the associated QTDs
+ //
+ EhcFreeQtds (Ehc, &Urb->Qh->Qtds);
+ UsbHcFreeMem (Ehc, Ehc->MemPool, Urb->Qh, sizeof (PEI_EHC_QH));
+ }
+}
+
+/**
+ Create a list of QTDs for the URB.
+
+ @param Ehc The EHCI device.
+ @param Urb The URB to create QTDs for.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for QTD.
+ @retval EFI_SUCCESS The QTDs are allocated for the URB.
+
+**/
+EFI_STATUS
+EhcCreateQtds (
+ IN PEI_USB2_HC_DEV *Ehc,
+ IN PEI_URB *Urb
+ )
+{
+ USB_ENDPOINT *Ep;
+ PEI_EHC_QH *Qh;
+ PEI_EHC_QTD *Qtd;
+ PEI_EHC_QTD *StatusQtd;
+ PEI_EHC_QTD *NextQtd;
+ EFI_LIST_ENTRY *Entry;
+ UINT32 AlterNext;
+ UINT8 Toggle;
+ UINTN Len;
+ UINT8 Pid;
+
+ ASSERT ((Urb != NULL) && (Urb->Qh != NULL));
+
+ //
+ // EHCI follows the alternet next QTD pointer if it meets
+ // a short read and the AlterNext pointer is valid. UEFI
+ // EHCI driver should terminate the transfer except the
+ // control transfer.
+ //
+ Toggle = 0;
+ Qh = Urb->Qh;
+ Ep = &Urb->Ep;
+ StatusQtd = NULL;
+ AlterNext = QTD_LINK (NULL, TRUE);
+
+ if (Ep->Direction == EfiUsbDataIn) {
+ AlterNext = QTD_LINK (Ehc->ShortReadStop, FALSE);
+ }
+
+ //
+ // Build the Setup and status packets for control transfer
+ //
+ if (Urb->Ep.Type == EHC_CTRL_TRANSFER) {
+ Len = sizeof (EFI_USB_DEVICE_REQUEST);
+ Qtd = EhcCreateQtd (Ehc, Urb->RequestPhy, Len, QTD_PID_SETUP, 0, Ep->MaxPacket);
+
+ if (Qtd == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ InsertTailList (&Qh->Qtds, &Qtd->QtdList);
+
+ //
+ // Create the status packet now. Set the AlterNext to it. So, when
+ // EHCI meets a short control read, it can resume at the status stage.
+ // Use the opposite direction of the data stage, or IN if there is
+ // no data stage.
+ //
+ if (Ep->Direction == EfiUsbDataIn) {
+ Pid = QTD_PID_OUTPUT;
+ } else {
+ Pid = QTD_PID_INPUT;
+ }
+
+ StatusQtd = EhcCreateQtd (Ehc, NULL, 0, Pid, 1, Ep->MaxPacket);
+
+ if (StatusQtd == NULL) {
+ goto ON_ERROR;
+ }
+
+ if (Ep->Direction == EfiUsbDataIn) {
+ AlterNext = QTD_LINK (StatusQtd, FALSE);
+ }
+
+ Toggle = 1;
+ }
+
+ //
+ // Build the data packets for all the transfers
+ //
+ if (Ep->Direction == EfiUsbDataIn) {
+ Pid = QTD_PID_INPUT;
+ } else {
+ Pid = QTD_PID_OUTPUT;
+ }
+
+ Qtd = NULL;
+ Len = 0;
+
+ while (Len < Urb->DataLen) {
+ Qtd = EhcCreateQtd (
+ Ehc,
+ (UINT8 *) Urb->DataPhy + Len,
+ Urb->DataLen - Len,
+ Pid,
+ Toggle,
+ Ep->MaxPacket
+ );
+
+ if (Qtd == NULL) {
+ goto ON_ERROR;
+ }
+
+ Qtd->QtdHw.AltNext = AlterNext;
+ InsertTailList (&Qh->Qtds, &Qtd->QtdList);
+
+ //
+ // Switch the Toggle bit if odd number of packets are included in the QTD.
+ //
+ if (((Qtd->DataLen + Ep->MaxPacket - 1) / Ep->MaxPacket) % 2) {
+ Toggle = (UINT8) (1 - Toggle);
+ }
+
+ Len += Qtd->DataLen;
+ }
+
+ //
+ // Insert the status packet for control transfer
+ //
+ if (Ep->Type == EHC_CTRL_TRANSFER) {
+ InsertTailList (&Qh->Qtds, &StatusQtd->QtdList);
+ }
+
+ //
+ // OK, all the QTDs needed are created. Now, fix the NextQtd point
+ //
+ BASE_LIST_FOR_EACH (Entry, &Qh->Qtds) {
+ Qtd = EFI_LIST_CONTAINER (Entry, PEI_EHC_QTD, QtdList);
+
+ //
+ // break if it is the last entry on the list
+ //
+ if (Entry->ForwardLink == &Qh->Qtds) {
+ break;
+ }
+
+ NextQtd = EFI_LIST_CONTAINER (Entry->ForwardLink, PEI_EHC_QTD, QtdList);
+ Qtd->QtdHw.NextQtd = QTD_LINK (NextQtd, FALSE);
+ }
+
+ //
+ // Link the QTDs to the queue head
+ //
+ NextQtd = EFI_LIST_CONTAINER (Qh->Qtds.ForwardLink, PEI_EHC_QTD, QtdList);
+ Qh->QhHw.NextQtd = QTD_LINK (NextQtd, FALSE);
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ EhcFreeQtds (Ehc, &Qh->Qtds);
+ return EFI_OUT_OF_RESOURCES;
+}
+
+/**
+ Create a new URB and its associated QTD.
+
+ @param Ehc The EHCI device.
+ @param DevAddr The device address.
+ @param EpAddr Endpoint addrress & its direction.
+ @param DevSpeed The device speed.
+ @param Toggle Initial data toggle to use.
+ @param MaxPacket The max packet length of the endpoint.
+ @param Hub The transaction translator to use.
+ @param Type The transaction type.
+ @param Request The standard USB request for control transfer.
+ @param Data The user data to transfer.
+ @param DataLen The length of data buffer.
+ @param Callback The function to call when data is transferred.
+ @param Context The context to the callback.
+ @param Interval The interval for interrupt transfer.
+
+ @retval the pointer to the created URB or NULL.
+
+**/
+PEI_URB *
+EhcCreateUrb (
+ IN PEI_USB2_HC_DEV *Ehc,
+ IN UINT8 DevAddr,
+ IN UINT8 EpAddr,
+ IN UINT8 DevSpeed,
+ IN UINT8 Toggle,
+ IN UINTN MaxPacket,
+ IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Hub,
+ IN UINTN Type,
+ IN EFI_USB_DEVICE_REQUEST *Request,
+ IN VOID *Data,
+ IN UINTN DataLen,
+ IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback,
+ IN VOID *Context,
+ IN UINTN Interval
+ )
+{
+ USB_ENDPOINT *Ep;
+ EFI_PHYSICAL_ADDRESS PhyAddr;
+ EDKII_IOMMU_OPERATION MapOp;
+ EFI_STATUS Status;
+ UINTN Len;
+ PEI_URB *Urb;
+ VOID *Map;
+
+ Map = NULL;
+
+ Urb = Ehc->Urb;
+ Urb->Signature = EHC_URB_SIG;
+ InitializeListHead (&Urb->UrbList);
+
+ Ep = &Urb->Ep;
+ Ep->DevAddr = DevAddr;
+ Ep->EpAddr = (UINT8) (EpAddr & 0x0F);
+ Ep->Direction = (((EpAddr & 0x80) != 0) ? EfiUsbDataIn : EfiUsbDataOut);
+ Ep->DevSpeed = DevSpeed;
+ Ep->MaxPacket = MaxPacket;
+
+ Ep->HubAddr = 0;
+ Ep->HubPort = 0;
+
+ if (DevSpeed != EFI_USB_SPEED_HIGH) {
+ ASSERT (Hub != NULL);
+
+ Ep->HubAddr = Hub->TranslatorHubAddress;
+ Ep->HubPort = Hub->TranslatorPortNumber;
+ }
+
+ Ep->Toggle = Toggle;
+ Ep->Type = Type;
+ Ep->PollRate = EhcConvertPollRate (Interval);
+
+ Urb->Request = Request;
+ Urb->Data = Data;
+ Urb->DataLen = DataLen;
+ Urb->Callback = Callback;
+ Urb->Context = Context;
+ Urb->Qh = EhcCreateQh (Ehc, &Urb->Ep);
+
+ if (Urb->Qh == NULL) {
+ goto ON_ERROR;
+ }
+
+ Urb->RequestPhy = NULL;
+ Urb->RequestMap = NULL;
+ Urb->DataPhy = NULL;
+ Urb->DataMap = NULL;
+
+ //
+ // Map the request and user data
+ //
+ if (Request != NULL) {
+ Len = sizeof (EFI_USB_DEVICE_REQUEST);
+ MapOp = EdkiiIoMmuOperationBusMasterRead;
+ Status = IoMmuMap (Ehc->IoMmu, MapOp, Request, &Len, &PhyAddr, &Map);
+
+ if (EFI_ERROR (Status) || (Len != sizeof (EFI_USB_DEVICE_REQUEST))) {
+ goto ON_ERROR;
+ }
+
+ Urb->RequestPhy = (VOID *) ((UINTN) PhyAddr);
+ Urb->RequestMap = Map;
+ }
+
+ if (Data != NULL) {
+ Len = DataLen;
+
+ if (Ep->Direction == EfiUsbDataIn) {
+ MapOp = EdkiiIoMmuOperationBusMasterWrite;
+ } else {
+ MapOp = EdkiiIoMmuOperationBusMasterRead;
+ }
+
+ Status = IoMmuMap (Ehc->IoMmu, MapOp, Data, &Len, &PhyAddr, &Map);
+
+ if (EFI_ERROR (Status) || (Len != DataLen)) {
+ goto ON_ERROR;
+ }
+
+ Urb->DataPhy = (VOID *) ((UINTN) PhyAddr);
+ Urb->DataMap = Map;
+ }
+
+ Status = EhcCreateQtds (Ehc, Urb);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ return Urb;
+
+ON_ERROR:
+ EhcFreeUrb (Ehc, Urb);
+ return NULL;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/EhciUrb.h b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/EhciUrb.h
new file mode 100644
index 00000000..a432d1d7
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/EhciUrb.h
@@ -0,0 +1,324 @@
+/** @file
+Private Header file for Usb Host Controller PEIM
+
+Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR>
+
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _EFI_EHCI_URB_H_
+#define _EFI_EHCI_URB_H_
+
+typedef struct _PEI_EHC_QTD PEI_EHC_QTD;
+typedef struct _PEI_EHC_QH PEI_EHC_QH;
+typedef struct _PEI_URB PEI_URB;
+
+#define EHC_CTRL_TRANSFER 0x01
+#define EHC_BULK_TRANSFER 0x02
+#define EHC_INT_TRANSFER_SYNC 0x04
+#define EHC_INT_TRANSFER_ASYNC 0x08
+
+#define EHC_QTD_SIG SIGNATURE_32 ('U', 'S', 'B', 'T')
+#define EHC_QH_SIG SIGNATURE_32 ('U', 'S', 'B', 'H')
+#define EHC_URB_SIG SIGNATURE_32 ('U', 'S', 'B', 'R')
+
+//
+// Hardware related bit definitions
+//
+#define EHC_TYPE_ITD 0x00
+#define EHC_TYPE_QH 0x02
+#define EHC_TYPE_SITD 0x04
+#define EHC_TYPE_FSTN 0x06
+
+#define QH_NAK_RELOAD 3
+#define QH_HSHBW_MULTI 1
+
+#define QTD_MAX_ERR 3
+#define QTD_PID_OUTPUT 0x00
+#define QTD_PID_INPUT 0x01
+#define QTD_PID_SETUP 0x02
+
+#define QTD_STAT_DO_OUT 0
+#define QTD_STAT_DO_SS 0
+#define QTD_STAT_DO_PING 0x01
+#define QTD_STAT_DO_CS 0x02
+#define QTD_STAT_TRANS_ERR 0x08
+#define QTD_STAT_BABBLE_ERR 0x10
+#define QTD_STAT_BUFF_ERR 0x20
+#define QTD_STAT_HALTED 0x40
+#define QTD_STAT_ACTIVE 0x80
+#define QTD_STAT_ERR_MASK (QTD_STAT_TRANS_ERR | QTD_STAT_BABBLE_ERR | QTD_STAT_BUFF_ERR)
+
+#define QTD_MAX_BUFFER 4
+#define QTD_BUF_LEN 4096
+#define QTD_BUF_MASK 0x0FFF
+
+#define QH_MICROFRAME_0 0x01
+#define QH_MICROFRAME_1 0x02
+#define QH_MICROFRAME_2 0x04
+#define QH_MICROFRAME_3 0x08
+#define QH_MICROFRAME_4 0x10
+#define QH_MICROFRAME_5 0x20
+#define QH_MICROFRAME_6 0x40
+#define QH_MICROFRAME_7 0x80
+
+#define USB_ERR_SHORT_PACKET 0x200
+
+//
+// Fill in the hardware link point: pass in a EHC_QH/QH_HW
+// pointer to QH_LINK; A EHC_QTD/QTD_HW pointer to QTD_LINK
+//
+#define QH_LINK(Addr, Type, Term) \
+ ((UINT32) ((EHC_LOW_32BIT (Addr) & 0xFFFFFFE0) | (Type) | ((Term) ? 1 : 0)))
+
+#define QTD_LINK(Addr, Term) QH_LINK((Addr), 0, (Term))
+
+//
+// The defination of EHCI hardware used data structure for
+// little endian architecture. The QTD and QH structures
+// are required to be 32 bytes aligned. Don't add members
+// to the head of the associated software strucuture.
+//
+#pragma pack(1)
+typedef struct {
+ UINT32 NextQtd;
+ UINT32 AltNext;
+
+ UINT32 Status : 8;
+ UINT32 Pid : 2;
+ UINT32 ErrCnt : 2;
+ UINT32 CurPage : 3;
+ UINT32 Ioc : 1;
+ UINT32 TotalBytes : 15;
+ UINT32 DataToggle : 1;
+
+ UINT32 Page[5];
+ UINT32 PageHigh[5];
+} QTD_HW;
+
+typedef struct {
+ UINT32 HorizonLink;
+ //
+ // Endpoint capabilities/Characteristics DWord 1 and DWord 2
+ //
+ UINT32 DeviceAddr : 7;
+ UINT32 Inactive : 1;
+ UINT32 EpNum : 4;
+ UINT32 EpSpeed : 2;
+ UINT32 DtCtrl : 1;
+ UINT32 ReclaimHead : 1;
+ UINT32 MaxPacketLen : 11;
+ UINT32 CtrlEp : 1;
+ UINT32 NakReload : 4;
+
+ UINT32 SMask : 8;
+ UINT32 CMask : 8;
+ UINT32 HubAddr : 7;
+ UINT32 PortNum : 7;
+ UINT32 Multiplier : 2;
+
+ //
+ // Transaction execution overlay area
+ //
+ UINT32 CurQtd;
+ UINT32 NextQtd;
+ UINT32 AltQtd;
+
+ UINT32 Status : 8;
+ UINT32 Pid : 2;
+ UINT32 ErrCnt : 2;
+ UINT32 CurPage : 3;
+ UINT32 Ioc : 1;
+ UINT32 TotalBytes : 15;
+ UINT32 DataToggle : 1;
+
+ UINT32 Page[5];
+ UINT32 PageHigh[5];
+} QH_HW;
+#pragma pack()
+
+
+//
+// Endpoint address and its capabilities
+//
+typedef struct _USB_ENDPOINT {
+ UINT8 DevAddr;
+ UINT8 EpAddr; // Endpoint address, no direction encoded in
+ EFI_USB_DATA_DIRECTION Direction;
+ UINT8 DevSpeed;
+ UINTN MaxPacket;
+ UINT8 HubAddr;
+ UINT8 HubPort;
+ UINT8 Toggle; // Data toggle, not used for control transfer
+ UINTN Type;
+ UINTN PollRate; // Polling interval used by EHCI
+} USB_ENDPOINT;
+
+//
+// Software QTD strcture, this is used to manage all the
+// QTD generated from a URB. Don't add fields before QtdHw.
+//
+struct _PEI_EHC_QTD {
+ QTD_HW QtdHw;
+ UINT32 Signature;
+ EFI_LIST_ENTRY QtdList; // The list of QTDs to one end point
+ UINT8 *Data; // Buffer of the original data
+ UINTN DataLen; // Original amount of data in this QTD
+};
+
+
+
+//
+// Software QH structure. All three different transaction types
+// supported by UEFI USB, that is the control/bulk/interrupt
+// transfers use the queue head and queue token strcuture.
+//
+// Interrupt QHs are linked to periodic frame list in the reversed
+// 2^N tree. Each interrupt QH is linked to the list starting at
+// frame 0. There is a dummy interrupt QH linked to each frame as
+// a sentinental whose polling interval is 1. Synchronous interrupt
+// transfer is linked after this dummy QH.
+//
+// For control/bulk transfer, only synchronous (in the sense of UEFI)
+// transfer is supported. A dummy QH is linked to EHCI AsyncListAddr
+// as the reclamation header. New transfer is inserted after this QH.
+//
+struct _PEI_EHC_QH {
+ QH_HW QhHw;
+ UINT32 Signature;
+ PEI_EHC_QH *NextQh; // The queue head pointed to by horizontal link
+ EFI_LIST_ENTRY Qtds; // The list of QTDs to this queue head
+ UINTN Interval;
+};
+
+//
+// URB (Usb Request Block) contains information for all kinds of
+// usb requests.
+//
+struct _PEI_URB {
+ UINT32 Signature;
+ EFI_LIST_ENTRY UrbList;
+
+ //
+ // Transaction information
+ //
+ USB_ENDPOINT Ep;
+ EFI_USB_DEVICE_REQUEST *Request; // Control transfer only
+ VOID *RequestPhy; // Address of the mapped request
+ VOID *RequestMap;
+ VOID *Data;
+ UINTN DataLen;
+ VOID *DataPhy; // Address of the mapped user data
+ VOID *DataMap;
+ EFI_ASYNC_USB_TRANSFER_CALLBACK Callback;
+ VOID *Context;
+
+ //
+ // Schedule data
+ //
+ PEI_EHC_QH *Qh;
+
+ //
+ // Transaction result
+ //
+ UINT32 Result;
+ UINTN Completed; // completed data length
+ UINT8 DataToggle;
+};
+
+/**
+ Delete a single asynchronous interrupt transfer for
+ the device and endpoint.
+
+ @param Ehc The EHCI device.
+ @param Data Current data not associated with a QTD.
+ @param DataLen The length of the data.
+ @param PktId Packet ID to use in the QTD.
+ @param Toggle Data toggle to use in the QTD.
+ @param MaxPacket Maximu packet length of the endpoint.
+
+ @retval the pointer to the created QTD or NULL if failed to create one.
+
+**/
+PEI_EHC_QTD *
+EhcCreateQtd (
+ IN PEI_USB2_HC_DEV *Ehc,
+ IN UINT8 *Data,
+ IN UINTN DataLen,
+ IN UINT8 PktId,
+ IN UINT8 Toggle,
+ IN UINTN MaxPacket
+ )
+;
+
+/**
+ Allocate and initialize a EHCI queue head.
+
+ @param Ehci The EHCI device.
+ @param Ep The endpoint to create queue head for.
+
+ @retval the pointer to the created queue head or NULL if failed to create one.
+
+**/
+PEI_EHC_QH *
+EhcCreateQh (
+ IN PEI_USB2_HC_DEV *Ehci,
+ IN USB_ENDPOINT *Ep
+ )
+;
+
+/**
+ Free an allocated URB. It is possible for it to be partially inited.
+
+ @param Ehc The EHCI device.
+ @param Urb The URB to free.
+
+**/
+VOID
+EhcFreeUrb (
+ IN PEI_USB2_HC_DEV *Ehc,
+ IN PEI_URB *Urb
+ )
+;
+
+/**
+ Create a new URB and its associated QTD.
+
+ @param Ehc The EHCI device.
+ @param DevAddr The device address.
+ @param EpAddr Endpoint addrress & its direction.
+ @param DevSpeed The device speed.
+ @param Toggle Initial data toggle to use.
+ @param MaxPacket The max packet length of the endpoint.
+ @param Hub The transaction translator to use.
+ @param Type The transaction type.
+ @param Request The standard USB request for control transfer.
+ @param Data The user data to transfer.
+ @param DataLen The length of data buffer.
+ @param Callback The function to call when data is transferred.
+ @param Context The context to the callback.
+ @param Interval The interval for interrupt transfer.
+
+ @retval the pointer to the created URB or NULL.
+
+**/
+PEI_URB *
+EhcCreateUrb (
+ IN PEI_USB2_HC_DEV *Ehc,
+ IN UINT8 DevAddr,
+ IN UINT8 EpAddr,
+ IN UINT8 DevSpeed,
+ IN UINT8 Toggle,
+ IN UINTN MaxPacket,
+ IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Hub,
+ IN UINTN Type,
+ IN EFI_USB_DEVICE_REQUEST *Request,
+ IN VOID *Data,
+ IN UINTN DataLen,
+ IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback,
+ IN VOID *Context,
+ IN UINTN Interval
+ )
+;
+#endif
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/UsbHcMem.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/UsbHcMem.c
new file mode 100644
index 00000000..fd472c53
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/UsbHcMem.c
@@ -0,0 +1,517 @@
+/** @file
+PEIM to produce gPeiUsb2HostControllerPpiGuid based on gPeiUsbControllerPpiGuid
+which is used to enable recovery function from USB Drivers.
+
+Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR>
+
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "EhcPeim.h"
+
+/**
+ Allocate a block of memory to be used by the buffer pool.
+
+ @param Ehc The EHCI device.
+ @param Pool The buffer pool to allocate memory for.
+ @param Pages How many pages to allocate.
+
+ @return The allocated memory block or NULL if failed.
+
+**/
+USBHC_MEM_BLOCK *
+UsbHcAllocMemBlock (
+ IN PEI_USB2_HC_DEV *Ehc,
+ IN USBHC_MEM_POOL *Pool,
+ IN UINTN Pages
+ )
+{
+ USBHC_MEM_BLOCK *Block;
+ VOID *BufHost;
+ VOID *Mapping;
+ EFI_PHYSICAL_ADDRESS MappedAddr;
+ EFI_STATUS Status;
+ UINTN PageNumber;
+ EFI_PHYSICAL_ADDRESS TempPtr;
+
+ Mapping = NULL;
+ PageNumber = sizeof(USBHC_MEM_BLOCK)/PAGESIZE +1;
+ Status = PeiServicesAllocatePages (
+ EfiBootServicesCode,
+ PageNumber,
+ &TempPtr
+ );
+
+ if (EFI_ERROR (Status)) {
+ return NULL;
+ }
+ ZeroMem ((VOID *)(UINTN)TempPtr, PageNumber*EFI_PAGE_SIZE);
+
+ //
+ // each bit in the bit array represents USBHC_MEM_UNIT
+ // bytes of memory in the memory block.
+ //
+ ASSERT (USBHC_MEM_UNIT * 8 <= EFI_PAGE_SIZE);
+
+ Block = (USBHC_MEM_BLOCK*)(UINTN)TempPtr;
+ Block->BufLen = EFI_PAGES_TO_SIZE (Pages);
+ Block->BitsLen = Block->BufLen / (USBHC_MEM_UNIT * 8);
+
+ PageNumber = (Block->BitsLen)/PAGESIZE +1;
+ Status = PeiServicesAllocatePages (
+ EfiBootServicesCode,
+ PageNumber,
+ &TempPtr
+ );
+
+ if (EFI_ERROR (Status)) {
+ return NULL;
+ }
+ ZeroMem ((VOID *)(UINTN)TempPtr, PageNumber*EFI_PAGE_SIZE);
+
+ Block->Bits = (UINT8 *)(UINTN)TempPtr;
+
+ Status = IoMmuAllocateBuffer (
+ Ehc->IoMmu,
+ Pages,
+ (VOID **) &BufHost,
+ &MappedAddr,
+ &Mapping
+ );
+ if (EFI_ERROR (Status)) {
+ return NULL;
+ }
+ ZeroMem (BufHost, Pages*EFI_PAGE_SIZE);
+
+ //
+ // Check whether the data structure used by the host controller
+ // should be restricted into the same 4G
+ //
+ if (Pool->Check4G && (Pool->Which4G != USB_HC_HIGH_32BIT (MappedAddr))) {
+ return NULL;
+ }
+
+ Block->BufHost = BufHost;
+ Block->Buf = (UINT8 *) ((UINTN) MappedAddr);
+ Block->Mapping = Mapping;
+ Block->Next = NULL;
+
+ return Block;
+
+}
+
+/**
+ Free the memory block from the memory pool.
+
+ @param Ehc The EHCI device.
+ @param Pool The memory pool to free the block from.
+ @param Block The memory block to free.
+
+**/
+VOID
+UsbHcFreeMemBlock (
+ IN PEI_USB2_HC_DEV *Ehc,
+ IN USBHC_MEM_POOL *Pool,
+ IN USBHC_MEM_BLOCK *Block
+ )
+{
+ ASSERT ((Pool != NULL) && (Block != NULL));
+
+ IoMmuFreeBuffer (Ehc->IoMmu, EFI_SIZE_TO_PAGES (Block->BufLen), Block->BufHost, Block->Mapping);
+}
+
+/**
+ Alloc some memory from the block.
+
+ @param Block The memory block to allocate memory from.
+ @param Units Number of memory units to allocate.
+
+ @return The pointer to the allocated memory. If couldn't allocate the needed memory,
+ the return value is NULL.
+
+**/
+VOID *
+UsbHcAllocMemFromBlock (
+ IN USBHC_MEM_BLOCK *Block,
+ IN UINTN Units
+ )
+{
+ UINTN Byte;
+ UINT8 Bit;
+ UINTN StartByte;
+ UINT8 StartBit;
+ UINTN Available;
+ UINTN Count;
+
+ ASSERT ((Block != 0) && (Units != 0));
+
+ StartByte = 0;
+ StartBit = 0;
+ Available = 0;
+
+ for (Byte = 0, Bit = 0; Byte < Block->BitsLen;) {
+ //
+ // If current bit is zero, the corresponding memory unit is
+ // available, otherwise we need to restart our searching.
+ // Available counts the consective number of zero bit.
+ //
+ if (!USB_HC_BIT_IS_SET (Block->Bits[Byte], Bit)) {
+ Available++;
+
+ if (Available >= Units) {
+ break;
+ }
+
+ NEXT_BIT (Byte, Bit);
+
+ } else {
+ NEXT_BIT (Byte, Bit);
+
+ Available = 0;
+ StartByte = Byte;
+ StartBit = Bit;
+ }
+ }
+
+ if (Available < Units) {
+ return NULL;
+ }
+
+ //
+ // Mark the memory as allocated
+ //
+ Byte = StartByte;
+ Bit = StartBit;
+
+ for (Count = 0; Count < Units; Count++) {
+ ASSERT (!USB_HC_BIT_IS_SET (Block->Bits[Byte], Bit));
+
+ Block->Bits[Byte] = (UINT8) (Block->Bits[Byte] | (UINT8) USB_HC_BIT (Bit));
+ NEXT_BIT (Byte, Bit);
+ }
+
+ return Block->Buf + (StartByte * 8 + StartBit) * USBHC_MEM_UNIT;
+}
+
+/**
+ Calculate the corresponding pci bus address according to the Mem parameter.
+
+ @param Pool The memory pool of the host controller.
+ @param Mem The pointer to host memory.
+ @param Size The size of the memory region.
+
+ @return the pci memory address
+**/
+EFI_PHYSICAL_ADDRESS
+UsbHcGetPciAddressForHostMem (
+ IN USBHC_MEM_POOL *Pool,
+ IN VOID *Mem,
+ IN UINTN Size
+ )
+{
+ USBHC_MEM_BLOCK *Head;
+ USBHC_MEM_BLOCK *Block;
+ UINTN AllocSize;
+ EFI_PHYSICAL_ADDRESS PhyAddr;
+ UINTN Offset;
+
+ Head = Pool->Head;
+ AllocSize = USBHC_MEM_ROUND (Size);
+
+ if (Mem == NULL) {
+ return 0;
+ }
+
+ for (Block = Head; Block != NULL; Block = Block->Next) {
+ //
+ // scan the memory block list for the memory block that
+ // completely contains the allocated memory.
+ //
+ if ((Block->BufHost <= (UINT8 *) Mem) && (((UINT8 *) Mem + AllocSize) <= (Block->BufHost + Block->BufLen))) {
+ break;
+ }
+ }
+
+ ASSERT ((Block != NULL));
+ //
+ // calculate the pci memory address for host memory address.
+ //
+ Offset = (UINT8 *)Mem - Block->BufHost;
+ PhyAddr = (EFI_PHYSICAL_ADDRESS)(UINTN) (Block->Buf + Offset);
+ return PhyAddr;
+}
+
+/**
+ Insert the memory block to the pool's list of the blocks.
+
+ @param Head The head of the memory pool's block list.
+ @param Block The memory block to insert.
+
+**/
+VOID
+UsbHcInsertMemBlockToPool (
+ IN USBHC_MEM_BLOCK *Head,
+ IN USBHC_MEM_BLOCK *Block
+ )
+{
+ ASSERT ((Head != NULL) && (Block != NULL));
+ Block->Next = Head->Next;
+ Head->Next = Block;
+}
+
+/**
+ Is the memory block empty?
+
+ @param Block The memory block to check.
+
+ @retval TRUE The memory block is empty.
+ @retval FALSE The memory block isn't empty.
+
+**/
+BOOLEAN
+UsbHcIsMemBlockEmpty (
+ IN USBHC_MEM_BLOCK *Block
+ )
+{
+ UINTN Index;
+
+
+ for (Index = 0; Index < Block->BitsLen; Index++) {
+ if (Block->Bits[Index] != 0) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
+/**
+ Initialize the memory management pool for the host controller.
+
+ @param Ehc The EHCI device.
+ @param Check4G Whether the host controller requires allocated memory.
+ from one 4G address space.
+ @param Which4G The 4G memory area each memory allocated should be from.
+
+ @retval EFI_SUCCESS The memory pool is initialized.
+ @retval EFI_OUT_OF_RESOURCE Fail to init the memory pool.
+
+**/
+USBHC_MEM_POOL *
+UsbHcInitMemPool (
+ IN PEI_USB2_HC_DEV *Ehc,
+ IN BOOLEAN Check4G,
+ IN UINT32 Which4G
+ )
+{
+ USBHC_MEM_POOL *Pool;
+ UINTN PageNumber;
+ EFI_STATUS Status;
+ EFI_PHYSICAL_ADDRESS TempPtr;
+
+ PageNumber = sizeof(USBHC_MEM_POOL)/PAGESIZE +1;
+ Status = PeiServicesAllocatePages (
+ EfiBootServicesCode,
+ PageNumber,
+ &TempPtr
+ );
+
+ if (EFI_ERROR (Status)) {
+ return NULL;
+ }
+ ZeroMem ((VOID *)(UINTN)TempPtr, PageNumber*EFI_PAGE_SIZE);
+
+ Pool = (USBHC_MEM_POOL *) ((UINTN) TempPtr);
+
+ Pool->Check4G = Check4G;
+ Pool->Which4G = Which4G;
+ Pool->Head = UsbHcAllocMemBlock (Ehc, Pool, USBHC_MEM_DEFAULT_PAGES);
+
+ if (Pool->Head == NULL) {
+ Pool = NULL;
+ }
+
+ return Pool;
+}
+
+/**
+ Release the memory management pool.
+
+ @param Ehc The EHCI device.
+ @param Pool The USB memory pool to free.
+
+ @retval EFI_DEVICE_ERROR Fail to free the memory pool.
+ @retval EFI_SUCCESS The memory pool is freed.
+
+**/
+EFI_STATUS
+UsbHcFreeMemPool (
+ IN PEI_USB2_HC_DEV *Ehc,
+ IN USBHC_MEM_POOL *Pool
+ )
+{
+ USBHC_MEM_BLOCK *Block;
+
+ ASSERT (Pool->Head != NULL);
+
+ //
+ // Unlink all the memory blocks from the pool, then free them.
+ //
+ for (Block = Pool->Head->Next; Block != NULL; Block = Block->Next) {
+ UsbHcFreeMemBlock (Ehc, Pool, Block);
+ }
+
+ UsbHcFreeMemBlock (Ehc, Pool, Pool->Head);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Allocate some memory from the host controller's memory pool
+ which can be used to communicate with host controller.
+
+ @param Ehc The EHCI device.
+ @param Pool The host controller's memory pool.
+ @param Size Size of the memory to allocate.
+
+ @return The allocated memory or NULL.
+
+**/
+VOID *
+UsbHcAllocateMem (
+ IN PEI_USB2_HC_DEV *Ehc,
+ IN USBHC_MEM_POOL *Pool,
+ IN UINTN Size
+ )
+{
+ USBHC_MEM_BLOCK *Head;
+ USBHC_MEM_BLOCK *Block;
+ USBHC_MEM_BLOCK *NewBlock;
+ VOID *Mem;
+ UINTN AllocSize;
+ UINTN Pages;
+
+ Mem = NULL;
+ AllocSize = USBHC_MEM_ROUND (Size);
+ Head = Pool->Head;
+ ASSERT (Head != NULL);
+
+ //
+ // First check whether current memory blocks can satisfy the allocation.
+ //
+ for (Block = Head; Block != NULL; Block = Block->Next) {
+ Mem = UsbHcAllocMemFromBlock (Block, AllocSize / USBHC_MEM_UNIT);
+
+ if (Mem != NULL) {
+ ZeroMem (Mem, Size);
+ break;
+ }
+ }
+
+ if (Mem != NULL) {
+ return Mem;
+ }
+
+ //
+ // Create a new memory block if there is not enough memory
+ // in the pool. If the allocation size is larger than the
+ // default page number, just allocate a large enough memory
+ // block. Otherwise allocate default pages.
+ //
+ if (AllocSize > EFI_PAGES_TO_SIZE (USBHC_MEM_DEFAULT_PAGES)) {
+ Pages = EFI_SIZE_TO_PAGES (AllocSize) + 1;
+ } else {
+ Pages = USBHC_MEM_DEFAULT_PAGES;
+ }
+ NewBlock = UsbHcAllocMemBlock (Ehc,Pool, Pages);
+
+ if (NewBlock == NULL) {
+ return NULL;
+ }
+
+ //
+ // Add the new memory block to the pool, then allocate memory from it
+ //
+ UsbHcInsertMemBlockToPool (Head, NewBlock);
+ Mem = UsbHcAllocMemFromBlock (NewBlock, AllocSize / USBHC_MEM_UNIT);
+
+ if (Mem != NULL) {
+ ZeroMem (Mem, Size);
+ }
+
+ return Mem;
+}
+
+/**
+ Free the allocated memory back to the memory pool.
+
+ @param Ehc The EHCI device.
+ @param Pool The memory pool of the host controller.
+ @param Mem The memory to free.
+ @param Size The size of the memory to free.
+
+**/
+VOID
+UsbHcFreeMem (
+ IN PEI_USB2_HC_DEV *Ehc,
+ IN USBHC_MEM_POOL *Pool,
+ IN VOID *Mem,
+ IN UINTN Size
+ )
+{
+ USBHC_MEM_BLOCK *Head;
+ USBHC_MEM_BLOCK *Block;
+ UINT8 *ToFree;
+ UINTN AllocSize;
+ UINTN Byte;
+ UINTN Bit;
+ UINTN Count;
+
+ Head = Pool->Head;
+ AllocSize = USBHC_MEM_ROUND (Size);
+ ToFree = (UINT8 *) Mem;
+
+ for (Block = Head; Block != NULL; Block = Block->Next) {
+ //
+ // scan the memory block list for the memory block that
+ // completely contains the memory to free.
+ //
+ if ((Block->Buf <= ToFree) && ((ToFree + AllocSize) <= (Block->Buf + Block->BufLen))) {
+ //
+ // compute the start byte and bit in the bit array
+ //
+ Byte = ((ToFree - Block->Buf) / USBHC_MEM_UNIT) / 8;
+ Bit = ((ToFree - Block->Buf) / USBHC_MEM_UNIT) % 8;
+
+ //
+ // reset associated bits in bit array
+ //
+ for (Count = 0; Count < (AllocSize / USBHC_MEM_UNIT); Count++) {
+ ASSERT (USB_HC_BIT_IS_SET (Block->Bits[Byte], Bit));
+
+ Block->Bits[Byte] = (UINT8) (Block->Bits[Byte] ^ USB_HC_BIT (Bit));
+ NEXT_BIT (Byte, Bit);
+ }
+
+ break;
+ }
+ }
+
+ //
+ // If Block == NULL, it means that the current memory isn't
+ // in the host controller's pool. This is critical because
+ // the caller has passed in a wrong memory point
+ //
+ ASSERT (Block != NULL);
+
+ //
+ // Release the current memory block if it is empty and not the head
+ //
+ if ((Block != Head) && UsbHcIsMemBlockEmpty (Block)) {
+ UsbHcFreeMemBlock (Ehc, Pool, Block);
+ }
+
+ return ;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/UsbHcMem.h b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/UsbHcMem.h
new file mode 100644
index 00000000..fc05521e
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/EhciPei/UsbHcMem.h
@@ -0,0 +1,86 @@
+/** @file
+Private Header file for Usb Host Controller PEIM
+
+Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR>
+
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _EFI_EHCI_MEM_H_
+#define _EFI_EHCI_MEM_H_
+
+#include <Uefi.h>
+#include <IndustryStandard/Pci22.h>
+
+#define USB_HC_BIT(a) ((UINTN)(1 << (a)))
+
+#define USB_HC_BIT_IS_SET(Data, Bit) \
+ ((BOOLEAN)(((Data) & USB_HC_BIT(Bit)) == USB_HC_BIT(Bit)))
+
+#define USB_HC_HIGH_32BIT(Addr64) \
+ ((UINT32)(RShiftU64((UINTN)(Addr64), 32) & 0XFFFFFFFF))
+
+typedef struct _USBHC_MEM_BLOCK USBHC_MEM_BLOCK;
+
+struct _USBHC_MEM_BLOCK {
+ UINT8 *Bits; // Bit array to record which unit is allocated
+ UINTN BitsLen;
+ UINT8 *Buf;
+ UINT8 *BufHost;
+ UINTN BufLen; // Memory size in bytes
+ VOID *Mapping;
+ USBHC_MEM_BLOCK *Next;
+};
+
+//
+// USBHC_MEM_POOL is used to manage the memory used by USB
+// host controller. EHCI requires the control memory and transfer
+// data to be on the same 4G memory.
+//
+typedef struct _USBHC_MEM_POOL {
+ BOOLEAN Check4G;
+ UINT32 Which4G;
+ USBHC_MEM_BLOCK *Head;
+} USBHC_MEM_POOL;
+
+//
+// Memory allocation unit, must be 2^n, n>4
+//
+#define USBHC_MEM_UNIT 64
+
+#define USBHC_MEM_UNIT_MASK (USBHC_MEM_UNIT - 1)
+#define USBHC_MEM_DEFAULT_PAGES 16
+
+#define USBHC_MEM_ROUND(Len) (((Len) + USBHC_MEM_UNIT_MASK) & (~USBHC_MEM_UNIT_MASK))
+
+//
+// Advance the byte and bit to the next bit, adjust byte accordingly.
+//
+#define NEXT_BIT(Byte, Bit) \
+ do { \
+ (Bit)++; \
+ if ((Bit) > 7) { \
+ (Byte)++; \
+ (Bit) = 0; \
+ } \
+ } while (0)
+
+
+/**
+ Calculate the corresponding pci bus address according to the Mem parameter.
+
+ @param Pool The memory pool of the host controller.
+ @param Mem The pointer to host memory.
+ @param Size The size of the memory region.
+
+ @return the pci memory address
+**/
+EFI_PHYSICAL_ADDRESS
+UsbHcGetPciAddressForHostMem (
+ IN USBHC_MEM_POOL *Pool,
+ IN VOID *Mem,
+ IN UINTN Size
+ );
+
+#endif