From 2cb6eabe0b358fe0d4e4dd602f84b5f5d3e39a6c Mon Sep 17 00:00:00 2001 From: Elvin Li Date: Wed, 22 Oct 2014 05:01:58 +0000 Subject: [PATCH] Add DebugCommunicationLibUsb3 for USB3.0 source level debug support. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Elvin Li Reviewed-by: Ruiyu Ni Reviewed-by: Feng Tian Reviewed-by: Jeff Fan git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@16224 6f19259b-4bc3-4df7-8a09-765794883524 --- .../DebugCommunicationLibUsb3Common.c | 1236 +++++++++++++++++ .../DebugCommunicationLibUsb3Dxe.c | 48 + .../DebugCommunicationLibUsb3Dxe.inf | 69 + .../DebugCommunicationLibUsb3Dxe.uni | Bin 0 -> 2032 bytes .../DebugCommunicationLibUsb3Internal.h | 763 ++++++++++ .../DebugCommunicationLibUsb3Pei.c | 45 + .../DebugCommunicationLibUsb3Pei.inf | 70 + .../DebugCommunicationLibUsb3Pei.uni | Bin 0 -> 1968 bytes .../DebugCommunicationLibUsb3Transfer.c | 606 ++++++++ SourceLevelDebugPkg/SourceLevelDebugPkg.dec | 22 + SourceLevelDebugPkg/SourceLevelDebugPkg.dsc | 8 + 11 files changed, 2867 insertions(+) create mode 100644 SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Common.c create mode 100644 SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Dxe.c create mode 100644 SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Dxe.inf create mode 100644 SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Dxe.uni create mode 100644 SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Internal.h create mode 100644 SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Pei.c create mode 100644 SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Pei.inf create mode 100644 SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Pei.uni create mode 100644 SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Transfer.c diff --git a/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Common.c b/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Common.c new file mode 100644 index 0000000000..fe6aec1ca5 --- /dev/null +++ b/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Common.c @@ -0,0 +1,1236 @@ +/** @file + Debug Port Library implementation based on usb3 debug port. + + Copyright (c) 2014, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "DebugCommunicationLibUsb3Internal.h" + +// +// The global variable which can be used after memory is ready. +// +USB3_DEBUG_PORT_HANDLE mDebugCommunicationLibUsb3DebugPortHandle; + +UINT16 mString0Desc[] = { + // String Descriptor Type + Length + ( USB_DESC_TYPE_STRING << 8 ) + STRING0_DESC_LEN, + 0x0409 +}; + +UINT16 mManufacturerStrDesc[] = { + // String Descriptor Type + Length + ( USB_DESC_TYPE_STRING << 8 ) + MANU_DESC_LEN, + 'I', 'n', 't', 'e', 'l' +}; + +UINT16 mProductStrDesc[] = { + // String Descriptor Type + Length + ( USB_DESC_TYPE_STRING << 8 ) + PRODUCT_DESC_LEN, + 'U', 'S', 'B', ' ', '3', '.', '0', ' ', 'D', 'e', 'b', 'u', 'g', ' ', 'C', 'a', 'b', 'l', 'e' +}; + +UINT16 mSerialNumberStrDesc[] = { + // String Descriptor Type + Length + ( USB_DESC_TYPE_STRING << 8 ) + SERIAL_DESC_LEN, + '1' +}; + +/** + Sets bits as per the enabled bit positions in the mask. + + @param[in, out] Register UINTN register + @param[in] BitMask 32-bit mask +**/ +VOID +XhcSetR32Bit( + IN OUT UINTN Register, + IN UINT32 BitMask + ) +{ + UINT32 RegisterValue; + + RegisterValue = MmioRead32 (Register); + RegisterValue |= (UINT32)(BitMask); + MmioWrite32 (Register, RegisterValue); +} + +/** + Clears bits as per the enabled bit positions in the mask. + + @param[in, out] Register UINTN register + @param[in] BitMask 32-bit mask +**/ +VOID +XhcClearR32Bit( + IN OUT UINTN Register, + IN UINT32 BitMask + ) +{ + UINT32 RegisterValue; + + RegisterValue = MmioRead32 (Register); + RegisterValue &= ~BitMask; + MmioWrite32 (Register, RegisterValue); +} + +/** + Write the data to the XHCI debug register. + + @param Handle Debug port handle. + @param Offset The offset of the runtime register. + @param Data The data to write. + +**/ +VOID +XhcWriteDebugReg ( + IN USB3_DEBUG_PORT_HANDLE *Handle, + IN UINT32 Offset, + IN UINT32 Data + ) +{ + EFI_PHYSICAL_ADDRESS DebugCapabilityBase; + + DebugCapabilityBase = Handle->DebugCapabilityBase; + MmioWrite32 ((UINTN)(DebugCapabilityBase + Offset), Data); + + return; +} + +/** + Read XHCI debug register. + + @param Handle Debug port handle. + @param Offset The offset of the runtime register. + + @return The register content read + +**/ +UINT32 +XhcReadDebugReg ( + IN USB3_DEBUG_PORT_HANDLE *Handle, + IN UINT32 Offset + ) +{ + UINT32 Data; + EFI_PHYSICAL_ADDRESS DebugCapabilityBase; + + DebugCapabilityBase = Handle->DebugCapabilityBase; + Data = MmioRead32 ((UINTN)(DebugCapabilityBase + Offset)); + + return Data; +} + +/** + Set one bit of the runtime register while keeping other bits. + + @param Handle Debug port handle. + @param Offset The offset of the runtime register. + @param Bit The bit mask of the register to set. + +**/ +VOID +XhcSetDebugRegBit ( + IN USB3_DEBUG_PORT_HANDLE *Handle, + IN UINT32 Offset, + IN UINT32 Bit + ) +{ + UINT32 Data; + + Data = XhcReadDebugReg (Handle, Offset); + Data |= Bit; + XhcWriteDebugReg (Handle, Offset, Data); +} + +/** + Program and eanble XHCI MMIO base address. + + @return XHCI MMIO base address. + +**/ +EFI_PHYSICAL_ADDRESS +ProgramXhciBaseAddress ( + VOID + ) +{ + UINT16 PciCmd; + UINT32 Low; + UINT32 High; + EFI_PHYSICAL_ADDRESS XhciMmioBase; + + Low = PciRead32 (PcdGet32(PcdUsbXhciPciAddress) + PCI_BASE_ADDRESSREG_OFFSET); + High = PciRead32 (PcdGet32(PcdUsbXhciPciAddress) + PCI_BASE_ADDRESSREG_OFFSET + 4); + XhciMmioBase = (EFI_PHYSICAL_ADDRESS) (LShiftU64 ((UINT64) High, 32) | Low); + XhciMmioBase &= XHCI_BASE_ADDRESS_64_BIT_MASK; + + if ((XhciMmioBase == 0) || (XhciMmioBase == XHCI_BASE_ADDRESS_64_BIT_MASK)) { + XhciMmioBase = PcdGet64(PcdUsbXhciMemorySpaceBase); + PciWrite32(PcdGet32(PcdUsbXhciPciAddress) + PCI_BASE_ADDRESSREG_OFFSET, XhciMmioBase & 0xFFFFFFFF); + PciWrite32(PcdGet32(PcdUsbXhciPciAddress) + PCI_BASE_ADDRESSREG_OFFSET + 4, (RShiftU64 (XhciMmioBase, 32) & 0xFFFFFFFF)); + } + + PciCmd = PciRead16 (PcdGet32(PcdUsbXhciPciAddress) + PCI_COMMAND_OFFSET); + if (((PciCmd & EFI_PCI_COMMAND_MEMORY_SPACE) == 0) || ((PciCmd & EFI_PCI_COMMAND_BUS_MASTER) == 0)) { + PciCmd |= EFI_PCI_COMMAND_MEMORY_SPACE | EFI_PCI_COMMAND_BUS_MASTER; + PciWrite16(PcdGet32(PcdUsbXhciPciAddress) + PCI_COMMAND_OFFSET, PciCmd); + } + + return XhciMmioBase; +} + +/** + Check if the timer is timeout. + + @param[in] UsbDebugPortHandle Pointer to USB Debug port handle + @param[in] Timer The start timer from the begin. + @param[in] TimeoutTicker Ticker number need time out. + + @return TRUE Timer time out occurs. + @retval FALSE Timer does not time out. + +**/ +BOOLEAN +IsTimerTimeout ( + IN USB3_DEBUG_PORT_HANDLE *UsbDebugPortHandle, + IN UINT64 Timer, + IN UINT64 TimeoutTicker + ) +{ + UINT64 CurrentTimer; + UINT64 Delta; + + CurrentTimer = GetPerformanceCounter (); + + if (UsbDebugPortHandle->TimerCountDown) { + // + // The timer counter counts down. Check for roll over condition. + // + if (CurrentTimer < Timer) { + Delta = Timer - CurrentTimer; + } else { + // + // Handle one roll-over. + // + Delta = UsbDebugPortHandle->TimerCycle - (CurrentTimer - Timer); + } + } else { + // + // The timer counter counts up. Check for roll over condition. + // + if (CurrentTimer > Timer) { + Delta = CurrentTimer - Timer; + } else { + // + // Handle one roll-over. + // + Delta = UsbDebugPortHandle->TimerCycle - (Timer - CurrentTimer); + } + } + + return (BOOLEAN) (Delta >= TimeoutTicker); +} + +/** + Update XHC MMIO base address when MMIO base address is changed. + + @param Handle Debug port handle. + @param XhciMmioBase XHCI MMIO base address. + +**/ +VOID +UpdateXhcResource ( + IN OUT USB3_DEBUG_PORT_HANDLE *Handle, + IN EFI_PHYSICAL_ADDRESS XhciMmioBase + ) +{ + if ((Handle == NULL) || (Handle->XhciMmioBase == XhciMmioBase)) { + return; + } + + // + // Need fix Handle data according to new XHCI MMIO base address. + // + Handle->XhciMmioBase = XhciMmioBase; + Handle->DebugCapabilityBase = XhciMmioBase + Handle->DebugCapabilityOffset; + Handle->XhciOpRegister = XhciMmioBase + MmioRead8 ((UINTN)XhciMmioBase); +} + +/** + Calculate the usb debug port bar address. + + @param Handle Debug port handle. + + @retval RETURN_UNSUPPORTED The usb host controller does not supported usb debug port capability. + @retval RETURN_SUCCESS Get bar and offset successfully. + +**/ +RETURN_STATUS +EFIAPI +CalculateUsbDebugPortMmioBase ( + USB3_DEBUG_PORT_HANDLE *Handle + ) +{ + UINT16 VendorId; + UINT16 DeviceId; + UINT8 ProgInterface; + UINT8 SubClassCode; + UINT8 BaseCode; + BOOLEAN Flag; + UINT32 Capability; + EFI_PHYSICAL_ADDRESS CapabilityPointer; + UINT8 CapLength; + + VendorId = PciRead16 (PcdGet32(PcdUsbXhciPciAddress) + PCI_VENDOR_ID_OFFSET); + DeviceId = PciRead16 (PcdGet32(PcdUsbXhciPciAddress) + PCI_DEVICE_ID_OFFSET); + + if ((VendorId == 0xFFFF) || (DeviceId == 0xFFFF)) { + goto Done; + } + + ProgInterface = PciRead8 (PcdGet32(PcdUsbXhciPciAddress) + PCI_CLASSCODE_OFFSET); + SubClassCode = PciRead8 (PcdGet32(PcdUsbXhciPciAddress) + PCI_CLASSCODE_OFFSET + 1); + BaseCode = PciRead8 (PcdGet32(PcdUsbXhciPciAddress) + PCI_CLASSCODE_OFFSET + 2); + + if ((ProgInterface != PCI_IF_XHCI) || (SubClassCode != PCI_CLASS_SERIAL_USB) || (BaseCode != PCI_CLASS_SERIAL)) { + goto Done; + } + + CapLength = MmioRead8 ((UINTN) Handle->XhciMmioBase); + + // + // Get capability pointer from HCCPARAMS at offset 0x10 + // + CapabilityPointer = Handle->XhciMmioBase + (MmioRead32 ((UINTN)(Handle->XhciMmioBase + XHC_HCCPARAMS_OFFSET)) >> 16) * 4; + + // + // Search XHCI debug capability + // + Flag = FALSE; + Capability = MmioRead32 ((UINTN)CapabilityPointer); + while (TRUE) { + if ((Capability & XHC_CAPABILITY_ID_MASK) == PCI_CAPABILITY_ID_DEBUG_PORT) { + Flag = TRUE; + break; + } + if ((((Capability & XHC_NEXT_CAPABILITY_MASK) >> 8) & XHC_CAPABILITY_ID_MASK) == 0) { + // + // Reach the end of capability list, quit + // + break; + } + CapabilityPointer += ((Capability & XHC_NEXT_CAPABILITY_MASK) >> 8) * 4; + Capability = MmioRead32 ((UINTN)CapabilityPointer); + } + + if (!Flag) { + goto Done; + } + + // + // USB3 debug capability is supported. + // + Handle->DebugCapabilityBase = CapabilityPointer; + Handle->DebugCapabilityOffset = CapabilityPointer - Handle->XhciMmioBase; + Handle->XhciOpRegister = Handle->XhciMmioBase + CapLength; + Handle->Initialized = USB3DBG_DBG_CAB; + return RETURN_SUCCESS; + +Done: + Handle->Initialized = USB3DBG_NO_DBG_CAB; + return RETURN_UNSUPPORTED; +} + +/** + Check if it needs to re-initialize usb debug port hardware. + + During different phases switch, such as SEC to PEI or PEI to DXE or DXE to SMM, we should check + whether the usb debug port hardware configuration is changed. Such case can be triggerred by + Pci bus resource allocation and so on. + + @param Handle Debug port handle. + + @retval TRUE The usb debug port hardware configuration is changed. + @retval FALSE The usb debug port hardware configuration is not changed. + +**/ +BOOLEAN +EFIAPI +NeedReinitializeHardware( + IN USB3_DEBUG_PORT_HANDLE *Handle + ) +{ + BOOLEAN Result; + volatile UINT32 Dcctrl; + + Result = FALSE; + + // + // If DCE bit, it means USB3 debug is not enabled. + // + Dcctrl = XhcReadDebugReg (Handle, XHC_DC_DCCTRL); + if ((Dcctrl & BIT0) == 0) { + Result = TRUE; + } + + return Result; +} + +/** + Create XHCI event ring. + + @param Handle Debug port handle. + @param EventRing The created event ring. + +**/ +EFI_STATUS +CreateEventRing ( + IN USB3_DEBUG_PORT_HANDLE *Handle, + OUT EVENT_RING *EventRing + ) +{ + VOID *Buf; + EVENT_RING_SEG_TABLE_ENTRY *ERSTBase; + + ASSERT (EventRing != NULL); + + // + // Allocate Event Ring + // + Buf = AllocateAlignBuffer (sizeof (TRB_TEMPLATE) * EVENT_RING_TRB_NUMBER); + ASSERT (Buf != NULL); + ASSERT (((UINTN) Buf & 0x3F) == 0); + ZeroMem (Buf, sizeof (TRB_TEMPLATE) * EVENT_RING_TRB_NUMBER); + + EventRing->EventRingSeg0 = (EFI_PHYSICAL_ADDRESS)(UINTN) Buf; + EventRing->TrbNumber = EVENT_RING_TRB_NUMBER; + EventRing->EventRingDequeue = (EFI_PHYSICAL_ADDRESS)(UINTN) EventRing->EventRingSeg0; + EventRing->EventRingEnqueue = (EFI_PHYSICAL_ADDRESS)(UINTN) EventRing->EventRingSeg0; + + // + // Software maintains an Event Ring Consumer Cycle State (CCS) bit, initializing it to '1' + // and toggling it every time the Event Ring Dequeue Pointer wraps back to the beginning of the Event Ring. + // + EventRing->EventRingCCS = 1; + + // + // Allocate Event Ring Segment Table Entry 0 in Event Ring Segment Table + // + Buf = AllocateAlignBuffer (sizeof (EVENT_RING_SEG_TABLE_ENTRY) * ERST_NUMBER); + ASSERT (Buf != NULL); + ASSERT (((UINTN) Buf & 0x3F) == 0); + ZeroMem (Buf, sizeof (EVENT_RING_SEG_TABLE_ENTRY) * ERST_NUMBER); + + ERSTBase = (EVENT_RING_SEG_TABLE_ENTRY *) Buf; + EventRing->ERSTBase = (EFI_PHYSICAL_ADDRESS)(UINTN) ERSTBase; + + // + // Fill Event Segment address + // + ERSTBase->PtrLo = XHC_LOW_32BIT (EventRing->EventRingSeg0); + ERSTBase->PtrHi = XHC_HIGH_32BIT (EventRing->EventRingSeg0); + ERSTBase->RingTrbSize = EVENT_RING_TRB_NUMBER; + + // + // Program the Interrupter Event Ring Dequeue Pointer (DCERDP) register (7.6.4.1) + // + XhcWriteDebugReg ( + Handle, + XHC_DC_DCERDP, + XHC_LOW_32BIT((UINT64)(UINTN)EventRing->EventRingDequeue) + ); + + XhcWriteDebugReg ( + Handle, + XHC_DC_DCERDP + 4, + XHC_HIGH_32BIT((UINT64)(UINTN)EventRing->EventRingDequeue) + ); + + // + // Program the Debug Capability Event Ring Segment Table Base Address (DCERSTBA) register(7.6.4.1) + // + XhcWriteDebugReg ( + Handle, + XHC_DC_DCERSTBA, + XHC_LOW_32BIT((UINT64)(UINTN)ERSTBase) + ); + + XhcWriteDebugReg ( + Handle, + XHC_DC_DCERSTBA + 4, + XHC_HIGH_32BIT((UINT64)(UINTN)ERSTBase) + ); + + // + // Program the Debug Capability Event Ring Segment Table Size (DCERSTSZ) register(7.6.4.1) + // + XhcWriteDebugReg ( + Handle, + XHC_DC_DCERSTSZ, + ERST_NUMBER + ); + return EFI_SUCCESS; +} + +/** + Create XHCI transfer ring. + + @param Handle Debug port handle. + @param TrbNum The number of TRB in the ring. + @param TransferRing The created transfer ring. + +**/ +VOID +CreateTransferRing ( + IN USB3_DEBUG_PORT_HANDLE *Handle, + IN UINT32 TrbNum, + OUT TRANSFER_RING *TransferRing + ) +{ + VOID *Buf; + LINK_TRB *EndTrb; + + Buf = AllocateAlignBuffer (sizeof (TRB_TEMPLATE) * TrbNum); + ASSERT (Buf != NULL); + ASSERT (((UINTN) Buf & 0xF) == 0); + ZeroMem (Buf, sizeof (TRB_TEMPLATE) * TrbNum); + + TransferRing->RingSeg0 = (EFI_PHYSICAL_ADDRESS)(UINTN) Buf; + TransferRing->TrbNumber = TrbNum; + TransferRing->RingEnqueue = TransferRing->RingSeg0; + TransferRing->RingDequeue = TransferRing->RingSeg0; + TransferRing->RingPCS = 1; + // + // 4.9.2 Transfer Ring Management + // To form a ring (or circular queue) a Link TRB may be inserted at the end of a ring to + // point to the first TRB in the ring. + // + EndTrb = (LINK_TRB *) ((UINTN)Buf + sizeof (TRB_TEMPLATE) * (TrbNum - 1)); + EndTrb->Type = TRB_TYPE_LINK; + EndTrb->PtrLo = XHC_LOW_32BIT (Buf); + EndTrb->PtrHi = XHC_HIGH_32BIT (Buf); + // + // Toggle Cycle (TC). When set to '1', the xHC shall toggle its interpretation of the Cycle bit. + // + EndTrb->TC = 1; + // + // Set Cycle bit as other TRB PCS init value + // + EndTrb->CycleBit = 0; +} + +/** + Create debug capability context for XHC debug device. + + @param Handle Debug port handle. + + @retval EFI_SUCCESS The bit successfully changed by host controller. + @retval EFI_TIMEOUT The time out occurred. + +**/ +EFI_STATUS +CreateDebugCapabilityContext ( + IN USB3_DEBUG_PORT_HANDLE *Handle + ) +{ + VOID *Buf; + XHC_DC_CONTEXT *DebugCapabilityContext; + UINT8 *String0Desc; + UINT8 *ManufacturerStrDesc; + UINT8 *ProductStrDesc; + UINT8 *SerialNumberStrDesc; + + // + // Allocate debug device context + // + Buf = AllocateAlignBuffer (sizeof (XHC_DC_CONTEXT)); + ASSERT (Buf != NULL); + ASSERT (((UINTN) Buf & 0xF) == 0); + ZeroMem (Buf, sizeof (XHC_DC_CONTEXT)); + + DebugCapabilityContext = (XHC_DC_CONTEXT *)(UINTN) Buf; + Handle->DebugCapabilityContext = (EFI_PHYSICAL_ADDRESS)(UINTN) DebugCapabilityContext; + + // + // Initialize DbcInfoContext. + // + DebugCapabilityContext->DbcInfoContext.String0Length = STRING0_DESC_LEN; + DebugCapabilityContext->DbcInfoContext.ManufacturerStrLength = MANU_DESC_LEN; + DebugCapabilityContext->DbcInfoContext.ProductStrLength = PRODUCT_DESC_LEN; + DebugCapabilityContext->DbcInfoContext.SerialNumberStrLength = SERIAL_DESC_LEN; + + // + // Initialize EpOutContext. + // + DebugCapabilityContext->EpOutContext.CErr = 0x3; + DebugCapabilityContext->EpOutContext.EPType = ED_BULK_OUT; + DebugCapabilityContext->EpOutContext.MaxPacketSize = 0x400; + DebugCapabilityContext->EpOutContext.AverageTRBLength = 0x1000; + + // + // Initialize EpInContext. + // + DebugCapabilityContext->EpInContext.CErr = 0x3; + DebugCapabilityContext->EpInContext.EPType = ED_BULK_IN; + DebugCapabilityContext->EpInContext.MaxPacketSize = 0x400; + DebugCapabilityContext->EpInContext.AverageTRBLength = 0x1000; + + // + // Update string descriptor address + // + String0Desc = (UINT8 *) AllocateAlignBuffer (STRING0_DESC_LEN + MANU_DESC_LEN + PRODUCT_DESC_LEN + SERIAL_DESC_LEN); + ASSERT (String0Desc != NULL); + ZeroMem (String0Desc, STRING0_DESC_LEN + MANU_DESC_LEN + PRODUCT_DESC_LEN + SERIAL_DESC_LEN); + CopyMem (String0Desc, mString0Desc, STRING0_DESC_LEN); + DebugCapabilityContext->DbcInfoContext.String0DescAddress = (UINT64)(UINTN)String0Desc; + + ManufacturerStrDesc = String0Desc + STRING0_DESC_LEN; + CopyMem (ManufacturerStrDesc, mManufacturerStrDesc, MANU_DESC_LEN); + DebugCapabilityContext->DbcInfoContext.ManufacturerStrDescAddress = (UINT64)(UINTN)ManufacturerStrDesc; + + ProductStrDesc = ManufacturerStrDesc + MANU_DESC_LEN; + CopyMem (ProductStrDesc, mProductStrDesc, PRODUCT_DESC_LEN); + DebugCapabilityContext->DbcInfoContext.ProductStrDescAddress = (UINT64)(UINTN)ProductStrDesc; + + SerialNumberStrDesc = ProductStrDesc + PRODUCT_DESC_LEN; + CopyMem (SerialNumberStrDesc, mSerialNumberStrDesc, SERIAL_DESC_LEN); + DebugCapabilityContext->DbcInfoContext.SerialNumberStrDescAddress = (UINT64)(UINTN)SerialNumberStrDesc; + + // + // Allocate and initialize the Transfer Ring for the Input Endpoint Context. + // + ZeroMem (&Handle->TransferRingIn, sizeof (TRANSFER_RING)); + CreateTransferRing (Handle, TR_RING_TRB_NUMBER, &Handle->TransferRingIn); + DebugCapabilityContext->EpInContext.PtrLo = XHC_LOW_32BIT (Handle->TransferRingIn.RingSeg0) | BIT0; + DebugCapabilityContext->EpInContext.PtrHi = XHC_HIGH_32BIT (Handle->TransferRingIn.RingSeg0); + + // + // Allocate and initialize the Transfer Ring for the Output Endpoint Context. + // + ZeroMem (&Handle->TransferRingOut, sizeof (TRANSFER_RING)); + CreateTransferRing (Handle, TR_RING_TRB_NUMBER, &Handle->TransferRingOut); + DebugCapabilityContext->EpOutContext.PtrLo = XHC_LOW_32BIT (Handle->TransferRingOut.RingSeg0) | BIT0; + DebugCapabilityContext->EpOutContext.PtrHi = XHC_HIGH_32BIT (Handle->TransferRingOut.RingSeg0); + + // + // Program the Debug Capability Context Pointer (DCCP) register(7.6.8.7) + // + XhcWriteDebugReg ( + Handle, + XHC_DC_DCCP, + XHC_LOW_32BIT((UINT64)(UINTN)DebugCapabilityContext) + ); + XhcWriteDebugReg ( + Handle, + XHC_DC_DCCP + 4, + XHC_HIGH_32BIT((UINT64)(UINTN)DebugCapabilityContext) + ); + return EFI_SUCCESS; +} + +/** + Check if debug device is running. + + @param Handle Debug port handle. + +**/ +VOID +XhcDetectDebugCapabilityReady ( + IN USB3_DEBUG_PORT_HANDLE *Handle + ) +{ + UINT64 TimeOut; + volatile UINT32 Dcctrl; + + TimeOut = 1; + if (Handle->Initialized == USB3DBG_DBG_CAB) { + // + // As detection is slow in seconds, wait for longer timeout for the first time. + // If first initialization is failed, we will try to enable debug device in the + // Poll function invoked by timer. + // + TimeOut = DivU64x32 (PcdGet64 (PcdUsbXhciDebugDetectTimeout), XHC_POLL_DELAY) + 1; + } + + do { + // + // Check if debug device is in configured state + // + Dcctrl = XhcReadDebugReg (Handle, XHC_DC_DCCTRL); + if ((Dcctrl & BIT0) != 0) { + // + // Set the flag to indicate debug device is in configured state + // + Handle->Ready = TRUE; + break; + } + MicroSecondDelay (XHC_POLL_DELAY); + TimeOut--; + } while (TimeOut != 0); +} + +/** + Initialize usb debug port hardware. + + @param Handle Debug port handle. + + @retval TRUE The usb debug port hardware configuration is changed. + @retval FALSE The usb debug port hardware configuration is not changed. + +**/ +RETURN_STATUS +EFIAPI +InitializeUsbDebugHardware ( + IN USB3_DEBUG_PORT_HANDLE *Handle + ) +{ + RETURN_STATUS Status; + UINT8 *Buffer; + UINTN Index; + UINT8 TotalUsb3Port; + EFI_PHYSICAL_ADDRESS XhciOpRegister; + + XhciOpRegister = Handle->XhciOpRegister; + TotalUsb3Port = MmioRead32 (((UINTN) Handle->XhciMmioBase + XHC_HCSPARAMS1_OFFSET)) >> 24; + + if (Handle->Initialized == USB3DBG_NOT_ENABLED) { + // + // If XHCI supports debug capability, hardware resource has been allocated, + // but it has not been enabled, try to enable again. + // + goto Enable; + } + + // + // Initialize for PEI phase when AllocatePages can work + // + Buffer = AllocateAlignBuffer (XHC_DEBUG_PORT_DATA_LENGTH); + if (Buffer == NULL) { + // + // AllocatePages can not still work now, return fail and do not initialize now. + // + return RETURN_NOT_READY; + } + + // + // Reset port to get debug device discovered + // + for (Index = 0; Index < TotalUsb3Port; Index++) { + XhcSetR32Bit ((UINTN)XhciOpRegister + XHC_PORTSC_OFFSET + Index * 0x10, BIT4); + MicroSecondDelay (10 * 1000); + } + + // + // Construct the buffer for URB in and URB out + // + Handle->UrbIn.Data = (EFI_PHYSICAL_ADDRESS)(UINTN) Buffer; + Handle->UrbOut.Data = (EFI_PHYSICAL_ADDRESS)(UINTN) Buffer + XHC_DEBUG_PORT_DATA_LENGTH; + + // + // Initialize event ring + // + ZeroMem (&Handle->EventRing, sizeof (EVENT_RING)); + Status = CreateEventRing (Handle, &Handle->EventRing); + ASSERT_EFI_ERROR (Status); + + // + // Init IN and OUT endpoint context + // + Status = CreateDebugCapabilityContext (Handle); + ASSERT_EFI_ERROR (Status); + + // + // Init DCDDI1 and DCDDI2 + // + XhcWriteDebugReg ( + Handle, + XHC_DC_DCDDI1, + (UINT32)((XHCI_DEBUG_DEVICE_VENDOR_ID << 16) | XHCI_DEBUG_DEVICE_PROTOCOL) + ); + + XhcWriteDebugReg ( + Handle, + XHC_DC_DCDDI2, + (UINT32)((XHCI_DEBUG_DEVICE_REVISION << 16) | XHCI_DEBUG_DEVICE_PRODUCT_ID) + ); + +Enable: + if ((Handle->Initialized == USB3DBG_NOT_ENABLED) && (!Handle->ChangePortPower)) { + // + // If the first time detection is failed, turn port power off and on in order to + // reset port status this time, then try to check if debug device is ready again. + // + for (Index = 0; Index < TotalUsb3Port; Index++) { + XhcClearR32Bit ((UINTN)XhciOpRegister + XHC_PORTSC_OFFSET + Index * 0x10, BIT9); + MicroSecondDelay (XHC_DEBUG_PORT_ON_OFF_DELAY); + XhcSetR32Bit ((UINTN)XhciOpRegister + XHC_PORTSC_OFFSET + Index * 0x10, BIT9); + MicroSecondDelay (XHC_DEBUG_PORT_ON_OFF_DELAY); + Handle->ChangePortPower = TRUE; + } + } + + // + // Set DCE bit and LSE bit to "1" in DCCTRL in first initialization + // + XhcSetDebugRegBit (Handle, XHC_DC_DCCTRL, BIT1|BIT31); + + XhcDetectDebugCapabilityReady (Handle); + + Status = RETURN_SUCCESS; + if (!Handle->Ready) { + Handle->Initialized = USB3DBG_NOT_ENABLED; + Status = RETURN_NOT_READY; + } else { + Handle->Initialized = USB3DBG_ENABLED; + } + + return Status; +} + +/** + Read data from debug device and save the data in buffer. + + Reads NumberOfBytes data bytes from a debug device into the buffer + specified by Buffer. The number of bytes actually read is returned. + If the return value is less than NumberOfBytes, then the rest operation failed. + If NumberOfBytes is zero, then return 0. + + @param Handle Debug port handle. + @param Buffer Pointer to the data buffer to store the data read from the debug device. + @param NumberOfBytes Number of bytes which will be read. + @param Timeout Timeout value for reading from debug device. It unit is Microsecond. + + @retval 0 Read data failed, no data is to be read. + @retval >0 Actual number of bytes read from debug device. + +**/ +UINTN +EFIAPI +DebugPortReadBuffer ( + IN DEBUG_PORT_HANDLE Handle, + IN UINT8 *Buffer, + IN UINTN NumberOfBytes, + IN UINTN Timeout + ) +{ + USB3_DEBUG_PORT_HANDLE *UsbDebugPortHandle; + RETURN_STATUS Status; + UINTN Received; + UINTN Total; + UINTN Remaining; + UINT8 Index; + UINTN Length; + UINT64 Begin; + UINT64 TimeoutTicker; + UINT64 TimerRound; + EFI_PHYSICAL_ADDRESS XhciMmioBase; + + if (NumberOfBytes == 0 || Buffer == NULL) { + return 0; + } + + Received = 0; + Total = 0; + Remaining = 0; + + // + // If Handle is NULL, it means memory is ready for use. + // Use global variable to store handle value. + // + if (Handle == NULL) { + UsbDebugPortHandle = &mDebugCommunicationLibUsb3DebugPortHandle; + } else { + UsbDebugPortHandle = (USB3_DEBUG_PORT_HANDLE *)Handle; + } + + if (UsbDebugPortHandle->Initialized == USB3DBG_NO_DBG_CAB) { + return 0; + } + + XhciMmioBase = ProgramXhciBaseAddress (); + UpdateXhcResource (UsbDebugPortHandle, XhciMmioBase); + + if (NeedReinitializeHardware(UsbDebugPortHandle)) { + Status = InitializeUsbDebugHardware (UsbDebugPortHandle); + if (RETURN_ERROR(Status)) { + return 0; + } + } + + // + // First read data from buffer, then read debug port hw to get received data. + // + if (UsbDebugPortHandle->DataCount > 0) { + if (NumberOfBytes <= UsbDebugPortHandle->DataCount) { + Total = NumberOfBytes; + } else { + Total = UsbDebugPortHandle->DataCount; + } + + for (Index = 0; Index < Total; Index++) { + Buffer[Index] = UsbDebugPortHandle->Data[Index]; + } + + for (Index = 0; Index < UsbDebugPortHandle->DataCount - Total; Index++) { + if (Total + Index >= 8) { + return 0; + } + UsbDebugPortHandle->Data[Index] = UsbDebugPortHandle->Data[Total + Index]; + } + UsbDebugPortHandle->DataCount = (UINT8)(UsbDebugPortHandle->DataCount - (UINT8)Total); + } + + // + // If Timeout is equal to 0, then it means it should always wait until all data required are received. + // + Begin = 0; + TimeoutTicker = 0; + TimerRound = 0; + if (Timeout != 0) { + Begin = GetPerformanceCounter (); + TimeoutTicker = DivU64x32 ( + MultU64x64 ( + UsbDebugPortHandle->TimerFrequency, + Timeout + ), + 1000000u + ); + TimerRound = DivU64x64Remainder ( + TimeoutTicker, + DivU64x32 (UsbDebugPortHandle->TimerCycle, 2), + &TimeoutTicker + ); + } + + // + // Read remaining data by executing one or more usb debug transfer transactions at usb debug port hw. + // + while (Total < NumberOfBytes) { + if (Timeout != 0) { + if (TimerRound == 0) { + if (IsTimerTimeout (UsbDebugPortHandle, Begin, TimeoutTicker)) { + // + // If time out occurs. + // + return 0; + } + } else { + if (IsTimerTimeout (UsbDebugPortHandle, Begin, DivU64x32 (UsbDebugPortHandle->TimerCycle, 2))) { + TimerRound --; + } + } + } + Remaining = NumberOfBytes - Total; + if (Remaining >= USB3_DEBUG_PORT_MAX_PACKET_SIZE) { + Received = USB3_DEBUG_PORT_MAX_PACKET_SIZE; + Status = XhcDataTransfer (UsbDebugPortHandle, EfiUsbDataIn, Buffer + Total, &Received, DATA_TRANSFER_READ_TIMEOUT); + } else { + Received = USB3_DEBUG_PORT_MAX_PACKET_SIZE; + Status = XhcDataTransfer (UsbDebugPortHandle, EfiUsbDataIn, &UsbDebugPortHandle->Data[0], &Received, DATA_TRANSFER_READ_TIMEOUT); + UsbDebugPortHandle->DataCount = (UINT8) Received; + + if (Remaining <= Received) { + // + // The data received are more than required + // + Length = (UINT8)Remaining; + } else { + // + // The data received are less than the remaining bytes + // + Length = (UINT8)Received; + } + + // + // Copy required data from the data buffer to user buffer. + // + for (Index = 0; Index < Length; Index++) { + (Buffer + Total)[Index] = UsbDebugPortHandle->Data[Index]; + UsbDebugPortHandle->DataCount--; + } + + // + // reorder the data buffer to make available data arranged from the beginning of the data buffer. + // + for (Index = 0; Index < Received - Length; Index++) { + if (Length + Index >= 8) { + return 0; + } + UsbDebugPortHandle->Data[Index] = UsbDebugPortHandle->Data[Length + Index]; + } + // + // fixup the real required length of data. + // + Received = Length; + } + Total += Received; + } + return Total; +} + +/** + Write data from buffer to debug device. + + Writes NumberOfBytes data bytes from Buffer to the debug device. + The number of bytes actually written to the debug device is returned. + If the return value is less than NumberOfBytes, then the write operation failed. + If NumberOfBytes is zero, then return 0. + + @param Handle Debug port handle. + @param Buffer Pointer to the data buffer to be written. + @param NumberOfBytes Number of bytes to written to the debug device. + + @retval 0 NumberOfBytes is 0. + @retval >0 The number of bytes written to the debug device. + If this value is less than NumberOfBytes, then the read operation failed. + +**/ +UINTN +EFIAPI +DebugPortWriteBuffer ( + IN DEBUG_PORT_HANDLE Handle, + IN UINT8 *Buffer, + IN UINTN NumberOfBytes + ) +{ + USB3_DEBUG_PORT_HANDLE *UsbDebugPortHandle; + RETURN_STATUS Status; + UINTN Sent; + UINTN Total; + EFI_PHYSICAL_ADDRESS XhciMmioBase; + UINTN Index; + + if (NumberOfBytes == 0 || Buffer == NULL) { + return 0; + } + + Sent = 0; + Total = 0; + + // + // If Handle is NULL, it means memory is ready for use. + // Use global variable to store handle value. + // + if (Handle == NULL) { + UsbDebugPortHandle = &mDebugCommunicationLibUsb3DebugPortHandle; + } else { + UsbDebugPortHandle = (USB3_DEBUG_PORT_HANDLE *)Handle; + } + + if (UsbDebugPortHandle->Initialized == USB3DBG_NO_DBG_CAB) { + return 0; + } + + // + // MMIO base address is possible to clear, set it if it is cleared. (XhciMemorySpaceClose in PchUsbCommon.c) + // + XhciMmioBase = ProgramXhciBaseAddress (); + + UpdateXhcResource (UsbDebugPortHandle, XhciMmioBase); + + if (NeedReinitializeHardware(UsbDebugPortHandle)) { + Status = InitializeUsbDebugHardware (UsbDebugPortHandle); + if (RETURN_ERROR(Status)) { + return 0; + } + } + + // + // When host is trying to send data, write will be blocked. + // Poll to see if there is any data sent by host at first. + // + DebugPortPollBuffer (Handle); + + Index = 0; + while ((Total < NumberOfBytes)) { + if (NumberOfBytes - Total > USB3_DEBUG_PORT_MAX_PACKET_SIZE) { + Sent = USB3_DEBUG_PORT_MAX_PACKET_SIZE; + } else { + Sent = (UINT8)(NumberOfBytes - Total); + } + Status = XhcDataTransfer (UsbDebugPortHandle, EfiUsbDataOut, Buffer + Total, &Sent, DATA_TRANSFER_WRITE_TIMEOUT); + Total += Sent; + } + + return Total; +} + +/** + Polls a debug device to see if there is any data waiting to be read. + + Polls a debug device to see if there is any data waiting to be read. + If there is data waiting to be read from the debug device, then TRUE is returned. + If there is no data waiting to be read from the debug device, then FALSE is returned. + + @param Handle Debug port handle. + + @retval TRUE Data is waiting to be read from the debug device. + @retval FALSE There is no data waiting to be read from the serial device. + +**/ +BOOLEAN +EFIAPI +DebugPortPollBuffer ( + IN DEBUG_PORT_HANDLE Handle + ) +{ + USB3_DEBUG_PORT_HANDLE *UsbDebugPortHandle; + UINTN Length; + RETURN_STATUS Status; + UINT8 Buffer[XHC_DEBUG_PORT_DATA_LENGTH]; + EFI_PHYSICAL_ADDRESS XhciMmioBase; + + // + // If Handle is NULL, it means memory is ready for use. + // Use global variable to store handle value. + // + if (Handle == NULL) { + UsbDebugPortHandle = &mDebugCommunicationLibUsb3DebugPortHandle; + } else { + UsbDebugPortHandle = (USB3_DEBUG_PORT_HANDLE *)Handle; + } + + if (UsbDebugPortHandle->Initialized == USB3DBG_NO_DBG_CAB) { + return 0; + } + + XhciMmioBase = ProgramXhciBaseAddress (); + UpdateXhcResource (UsbDebugPortHandle, XhciMmioBase); + + if (NeedReinitializeHardware(UsbDebugPortHandle)) { + Status = InitializeUsbDebugHardware(UsbDebugPortHandle); + if (RETURN_ERROR(Status)) { + return FALSE; + } + } + + // + // If the data buffer is not empty, then return TRUE directly. + // Otherwise initialize a usb read transaction and read data to internal data buffer. + // + if (UsbDebugPortHandle->DataCount != 0) { + return TRUE; + } + + // + // Read most 8-bytes data + // + Length = XHC_DEBUG_PORT_DATA_LENGTH; + XhcDataTransfer (Handle, EfiUsbDataIn, Buffer, &Length, DATA_TRANSFER_POLL_TIMEOUT); + + if (Length > 8) { + return FALSE; + } + + if (Length == 0) { + return FALSE; + } + + // + // Store data into internal buffer for use later + // + CopyMem (UsbDebugPortHandle->Data, Buffer, Length); + UsbDebugPortHandle->DataCount = (UINT8) Length; + return TRUE; +} + +/** + Initialize the debug port. + + If Function is not NULL, Debug Communication Libary will call this function + by passing in the Context to be the first parameter. If needed, Debug Communication + Library will create one debug port handle to be the second argument passing in + calling the Function, otherwise it will pass NULL to be the second argument of + Function. + + If Function is NULL, and Context is not NULL, the Debug Communication Library could + a) Return the same handle as passed in (as Context parameter). + b) Ignore the input Context parameter and create new hanlde to be returned. + + If parameter Function is NULL and Context is NULL, Debug Communication Library could + created a new handle if needed and return it, otherwise it will return NULL. + + @param[in] Context Context needed by callback function; it was optional. + @param[in] Function Continue function called by Debug Communication library; + it was optional. + + @return The debug port handle created by Debug Communication Library if Function + is not NULL. + +**/ +DEBUG_PORT_HANDLE +EFIAPI +DebugPortInitialize ( + IN VOID *Context, + IN DEBUG_PORT_CONTINUE Function + ) +{ + RETURN_STATUS Status; + USB3_DEBUG_PORT_HANDLE Handle; + USB3_DEBUG_PORT_HANDLE *UsbDebugPortHandle; + UINT64 TimerStartValue; + UINT64 TimerEndValue; + + // + // Validate the PCD PcdDebugPortHandleBufferSize value + // + ASSERT (PcdGet16 (PcdDebugPortHandleBufferSize) == sizeof (USB3_DEBUG_PORT_HANDLE)); + + if (Function == NULL && Context != NULL) { + UsbDebugPortHandle = (USB3_DEBUG_PORT_HANDLE *)Context; + } else { + ZeroMem(&Handle, sizeof (USB3_DEBUG_PORT_HANDLE)); + UsbDebugPortHandle = &Handle; + } + + UsbDebugPortHandle->TimerFrequency = GetPerformanceCounterProperties ( + &TimerStartValue, + &TimerEndValue + ); + + if (TimerEndValue < TimerStartValue) { + UsbDebugPortHandle->TimerCountDown = TRUE; + UsbDebugPortHandle->TimerCycle = TimerStartValue - TimerEndValue; + } else { + UsbDebugPortHandle->TimerCountDown = FALSE; + UsbDebugPortHandle->TimerCycle = TimerEndValue - TimerStartValue; + } + + if (Function == NULL && Context != NULL) { + return (DEBUG_PORT_HANDLE *) Context; + } + + // + // Read 64-bit MMIO base address + // + UsbDebugPortHandle->XhciMmioBase = ProgramXhciBaseAddress (); + + Status = CalculateUsbDebugPortMmioBase (UsbDebugPortHandle); + if (RETURN_ERROR (Status)) { + goto Exit; + } + + if (NeedReinitializeHardware(&Handle)) { + Status = InitializeUsbDebugHardware (&Handle); + if (RETURN_ERROR(Status)) { + goto Exit; + } + } + +Exit: + + if (Function != NULL) { + Function (Context, &Handle); + } else { + CopyMem(&mDebugCommunicationLibUsb3DebugPortHandle, &Handle, sizeof (USB3_DEBUG_PORT_HANDLE)); + } + + return (DEBUG_PORT_HANDLE)(UINTN)&mDebugCommunicationLibUsb3DebugPortHandle; +} diff --git a/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Dxe.c b/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Dxe.c new file mode 100644 index 0000000000..c232323df8 --- /dev/null +++ b/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Dxe.c @@ -0,0 +1,48 @@ +/** @file + Debug Port Library implementation based on usb3 debug port. + + Copyright (c) 2014, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include +#include +#include +#include "DebugCommunicationLibUsb3Internal.h" + +/** + Allocate aligned memory for XHC's usage. + + @param BufferSize The size, in bytes, of the Buffer. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID* +AllocateAlignBuffer ( + IN UINTN BufferSize + ) +{ + VOID *Buf; + + Buf = NULL; + + if (gBS != NULL) { + Buf = (VOID *)(UINTN)0xFFFFFFFF; + gBS->AllocatePages ( + AllocateMaxAddress, + EfiACPIMemoryNVS, + EFI_SIZE_TO_PAGES (BufferSize), + (EFI_PHYSICAL_ADDRESS *) &Buf + ); + } + + return Buf; +} diff --git a/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Dxe.inf b/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Dxe.inf new file mode 100644 index 0000000000..e98462306c --- /dev/null +++ b/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Dxe.inf @@ -0,0 +1,69 @@ +## @file +# Debug Communication Library instance based on usb3 debug port. +# +# Copyright (c) 2014, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php. +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = DebugCommunicationLibUsb3Dxe + MODULE_UNI_FILE = DebugCommunicationLibUsb3Dxe.uni + FILE_GUID = C41F8C82-B3E6-47e0-A61D-0F9E429E6996 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + LIBRARY_CLASS = DebugCommunicationLib|DXE_CORE DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SAL_DRIVER DXE_SMM_DRIVER UEFI_APPLICATION UEFI_DRIVER SMM_CORE + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF +# + +[Sources] + DebugCommunicationLibUsb3Dxe.c + DebugCommunicationLibUsb3Transfer.c + DebugCommunicationLibUsb3Common.c + DebugCommunicationLibUsb3Internal.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + IntelFrameworkPkg/IntelFrameworkPkg.dec + SourceLevelDebugPkg/SourceLevelDebugPkg.dec + +[Pcd] + ## The memory BAR of ehci host controller, in which usb debug feature is enabled. + ## Note that the memory BAR address is only used before Pci bus resource allocation. + gEfiSourceLevelDebugPkgTokenSpaceGuid.PcdUsbXhciMemorySpaceBase ## SOMETIMES_CONSUMES + + ## The pci address of ehci host controller, in which usb debug feature is enabled. + ## The format of pci address please refer to SourceLevelDebugPkg.dec + gEfiSourceLevelDebugPkgTokenSpaceGuid.PcdUsbXhciPciAddress ## CONSUMES + + ## Per XHCI spec, software shall impose a timeout between the detection of the Debug Host + ## connection and the DbC Run transition to 1. This PCD specifies the timeout value in microsecond. + gEfiSourceLevelDebugPkgTokenSpaceGuid.PcdUsbXhciDebugDetectTimeout ## SOMETIMES_CONSUMES + + ## The value of data buffer size used for USB debug port handle. + ## It should be equal to sizeof (USB3_DEBUG_PORT_HANDLE). + gEfiSourceLevelDebugPkgTokenSpaceGuid.PcdDebugPortHandleBufferSize|256 ## SOMETIMES_CONSUMES + +[LibraryClasses] + BaseLib + PcdLib + IoLib + PciLib + TimerLib + UefiBootServicesTableLib + UefiLib + BaseMemoryLib + \ No newline at end of file diff --git a/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Dxe.uni b/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Dxe.uni new file mode 100644 index 0000000000000000000000000000000000000000..111cf79c667988621005f15f4642298abbd0f4e6 GIT binary patch literal 2032 zcmds%TW`}q5QXO%iT_}wFMwKy+Y>^Fgcz`ZCdx&Hr^>lBjg&Oi#%bxF2fj0o?NExu zE0tyM&d%)2*)wPT^GDN~7V$peiR`VN*}|4KvxoM8RrxwtW82!C`8!+Nif3yV#7!)- zoNk~Q|$5pOK6P~R8y;``YFCdYyrC zLd1$l`EFbf($LCL%^XDro=sIpyMpgt5-rV0L4{t*{Ms$8DE5k1YRyoVG#j}t<&|bi zB;qAdRjA7>LMA0Gh${3e?;5?;``W0igDqXxSZZZmI~Nnaf1Y+hO=T2@f-lw5Z4mdm zO=Vs6MXU<_nJZZx66WuGZ)5tMmx4>(wefEMddGke<$JtJIXRtp<#$?|D!5gA@YBVd zKG4r5Ozxj|!^#-sQ#-oliOF)#Ti-gJ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __USB3_DEBUG_PORT_LIB_INTERNAL__ +#define __USB3_DEBUG_PORT_LIB_INTERNAL__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // Todo: remove in future + +// +// Internal serial debug - remove finally +// +#include +#include + +// +// USB Debug GUID value +// +#define USB3_DBG_GUID \ + { \ + 0xb2a56f4d, 0x9177, 0x4fc8, { 0xa6, 0x77, 0xdd, 0x96, 0x3e, 0xb4, 0xcb, 0x1b } \ + } + +// +// The state machine of usb debug port +// +#define USB3DBG_NO_DBG_CAB 0 // The XHCI host controller does not support debug capability +#define USB3DBG_DBG_CAB 1 // The XHCI host controller supports debug capability +#define USB3DBG_ENABLED 2 // The XHCI debug device is enabled +#define USB3DBG_NOT_ENABLED 4 // The XHCI debug device is not enabled + +#define USB3_DEBUG_PORT_MAX_PACKET_SIZE 0x08 + +#define XHCI_DEBUG_DEVICE_VENDOR_ID 0x0525 +#define XHCI_DEBUG_DEVICE_PRODUCT_ID 0x127A +#define XHCI_DEBUG_DEVICE_PROTOCOL 0xFF +#define XHCI_DEBUG_DEVICE_REVISION 0x00 + +#define XHCI_BASE_ADDRESS_64_BIT_MASK 0xFFFFFFFFFFFF0000ULL +#define XHCI_BASE_ADDRESS_32_BIT_MASK 0xFFFF0000 + +#define PCI_CAPABILITY_ID_DEBUG_PORT 0x0A +#define XHC_HCCPARAMS_OFFSET 0x10 +#define XHC_CAPABILITY_ID_MASK 0xFF +#define XHC_NEXT_CAPABILITY_MASK 0xFF00 + +#define XHC_HCSPARAMS1_OFFSET 0x4 // Structural Parameters 1 +#define XHC_USBCMD_OFFSET 0x0 // USB Command Register Offset +#define XHC_USBSTS_OFFSET 0x4 // USB Status Register Offset +#define XHC_PORTSC_OFFSET 0x400 // Port Status and Control Register Offset + +#define XHC_USBCMD_RUN BIT0 // Run/Stop +#define XHC_USBCMD_RESET BIT1 // Host Controller Reset + +#define XHC_USBSTS_HALT BIT0 + +// +// Transfer the data of 8 bytes each time +// +#define XHC_DEBUG_PORT_DATA_LENGTH 8 + +// +// Indicate the timeout when data is transferred in microsecond. 0 means infinite timeout. +// +#define DATA_TRANSFER_WRITE_TIMEOUT 0 +#define DATA_TRANSFER_READ_TIMEOUT 50000 +#define DATA_TRANSFER_POLL_TIMEOUT 1000 + +// +// XHCI port power off/on delay +// +#define XHC_DEBUG_PORT_ON_OFF_DELAY 100000 + +// +// USB debug device string descritpor (header size + unicode string length) +// +#define STRING0_DESC_LEN 4 +#define MANU_DESC_LEN 12 +#define PRODUCT_DESC_LEN 40 +#define SERIAL_DESC_LEN 4 + +// +// Debug Capability Register Offset +// +#define XHC_DC_DCID 0x0 +#define XHC_DC_DCDB 0x4 +#define XHC_DC_DCERSTSZ 0x8 +#define XHC_DC_DCERSTBA 0x10 +#define XHC_DC_DCERDP 0x18 +#define XHC_DC_DCCTRL 0x20 +#define XHC_DC_DCST 0x24 +#define XHC_DC_DCPORTSC 0x28 +#define XHC_DC_DCCP 0x30 +#define XHC_DC_DCDDI1 0x38 +#define XHC_DC_DCDDI2 0x3C + +#define TRB_TYPE_LINK 6 + +#define ERST_NUMBER 0x01 +#define TR_RING_TRB_NUMBER 0x100 +#define EVENT_RING_TRB_NUMBER 0x200 + +#define ED_BULK_OUT 2 +#define ED_BULK_IN 6 + +#define XHC_LOW_32BIT(Addr64) ((UINT32)(((UINTN)(Addr64)) & 0xFFFFFFFF)) +#define XHC_HIGH_32BIT(Addr64) ((UINT32)(RShiftU64((UINT64)(UINTN)(Addr64), 32) & 0xFFFFFFFF)) +#define XHC_BIT_IS_SET(Data, Bit) ((BOOLEAN)(((Data) & (Bit)) == (Bit))) + +// +// Endpoint Type (EP Type). +// +#define ED_NOT_VALID 0 +#define ED_ISOCH_OUT 1 +#define ED_BULK_OUT 2 +#define ED_INTERRUPT_OUT 3 +#define ED_CONTROL_BIDIR 4 +#define ED_ISOCH_IN 5 +#define ED_BULK_IN 6 +#define ED_INTERRUPT_IN 7 + +// +// 6.4.5 TRB Completion Codes +// +#define TRB_COMPLETION_INVALID 0 +#define TRB_COMPLETION_SUCCESS 1 +#define TRB_COMPLETION_DATA_BUFFER_ERROR 2 +#define TRB_COMPLETION_BABBLE_ERROR 3 +#define TRB_COMPLETION_USB_TRANSACTION_ERROR 4 +#define TRB_COMPLETION_TRB_ERROR 5 +#define TRB_COMPLETION_STALL_ERROR 6 +#define TRB_COMPLETION_SHORT_PACKET 13 + +// +// 6.4.6 TRB Types +// +#define TRB_TYPE_NORMAL 1 +#define TRB_TYPE_SETUP_STAGE 2 +#define TRB_TYPE_DATA_STAGE 3 +#define TRB_TYPE_STATUS_STAGE 4 +#define TRB_TYPE_ISOCH 5 +#define TRB_TYPE_LINK 6 +#define TRB_TYPE_EVENT_DATA 7 +#define TRB_TYPE_NO_OP 8 +#define TRB_TYPE_EN_SLOT 9 +#define TRB_TYPE_DIS_SLOT 10 +#define TRB_TYPE_ADDRESS_DEV 11 +#define TRB_TYPE_CON_ENDPOINT 12 +#define TRB_TYPE_EVALU_CONTXT 13 +#define TRB_TYPE_RESET_ENDPOINT 14 +#define TRB_TYPE_STOP_ENDPOINT 15 +#define TRB_TYPE_SET_TR_DEQUE 16 +#define TRB_TYPE_RESET_DEV 17 +#define TRB_TYPE_GET_PORT_BANW 21 +#define TRB_TYPE_FORCE_HEADER 22 +#define TRB_TYPE_NO_OP_COMMAND 23 +#define TRB_TYPE_TRANS_EVENT 32 +#define TRB_TYPE_COMMAND_COMPLT_EVENT 33 +#define TRB_TYPE_PORT_STATUS_CHANGE_EVENT 34 +#define TRB_TYPE_HOST_CONTROLLER_EVENT 37 +#define TRB_TYPE_DEVICE_NOTIFI_EVENT 38 +#define TRB_TYPE_MFINDEX_WRAP_EVENT 39 + +// +// Convert millisecond to microsecond. +// +#define XHC_1_MILLISECOND (1000) +#define XHC_POLL_DELAY (1000) +#define XHC_GENERIC_TIMEOUT (10 * 1000) + +#define EFI_USB_SPEED_FULL 0x0000 ///< 12 Mb/s, USB 1.1 OHCI and UHCI HC. +#define EFI_USB_SPEED_LOW 0x0001 ///< 1 Mb/s, USB 1.1 OHCI and UHCI HC. +#define EFI_USB_SPEED_HIGH 0x0002 ///< 480 Mb/s, USB 2.0 EHCI HC. +#define EFI_USB_SPEED_SUPER 0x0003 ///< 4.8 Gb/s, USB 3.0 XHCI HC. + +// +// Transfer types, used in URB to identify the transfer type +// +#define XHC_CTRL_TRANSFER 0x01 +#define XHC_BULK_TRANSFER 0x02 +#define XHC_INT_TRANSFER_SYNC 0x04 +#define XHC_INT_TRANSFER_ASYNC 0x08 +#define XHC_INT_ONLY_TRANSFER_ASYNC 0x10 + +// +// USB Transfer Results +// +#define EFI_USB_NOERROR 0x00 +#define EFI_USB_ERR_NOTEXECUTE 0x01 +#define EFI_USB_ERR_STALL 0x02 +#define EFI_USB_ERR_BUFFER 0x04 +#define EFI_USB_ERR_BABBLE 0x08 +#define EFI_USB_ERR_NAK 0x10 +#define EFI_USB_ERR_CRC 0x20 +#define EFI_USB_ERR_TIMEOUT 0x40 +#define EFI_USB_ERR_BITSTUFF 0x80 +#define EFI_USB_ERR_SYSTEM 0x100 + +#pragma pack(1) + +// +// 7.6.9 OUT/IN EP Context: 64 bytes +// 7.6.9.2 When used by the DbC it is always a 64 byte data structure +// +typedef struct _ENDPOINT_CONTEXT_64 { + UINT32 EPState:3; + UINT32 RsvdZ1:5; + UINT32 Mult:2; // set to 0 + UINT32 MaxPStreams:5; // set to 0 + UINT32 LSA:1; // set to 0 + UINT32 Interval:8; // set to 0 + UINT32 RsvdZ2:8; + + UINT32 RsvdZ3:1; + UINT32 CErr:2; + UINT32 EPType:3; + UINT32 RsvdZ4:1; + UINT32 HID:1; // set to 0 + UINT32 MaxBurstSize:8; + UINT32 MaxPacketSize:16; + + UINT32 PtrLo; + + UINT32 PtrHi; + + UINT32 AverageTRBLength:16; + UINT32 MaxESITPayload:16; // set to 0 + + UINT32 RsvdZ5; // Reserved + UINT32 RsvdZ6; + UINT32 RsvdZ7; + + UINT32 RsvdZ8; + UINT32 RsvdZ9; + UINT32 RsvdZ10; + UINT32 RsvdZ11; + + UINT32 RsvdZ12; + UINT32 RsvdZ13; + UINT32 RsvdZ14; + UINT32 RsvdZ15; +} ENDPOINT_CONTEXT_64; + +// +// 6.4.1.1 Normal TRB: 16 bytes +// A Normal TRB is used in several ways; exclusively on Bulk and Interrupt Transfer Rings for normal and +// Scatter/Gather operations, to define additional data buffers for Scatter/Gather operations on Isoch Transfer +// Rings, and to define the Data stage information for Control Transfer Rings. +// +typedef struct _TRANSFER_TRB_NORMAL { + UINT32 TRBPtrLo; + + UINT32 TRBPtrHi; + + UINT32 Length:17; + UINT32 TDSize:5; + UINT32 IntTarget:10; + + UINT32 CycleBit:1; + UINT32 ENT:1; + UINT32 ISP:1; + UINT32 NS:1; + UINT32 CH:1; + UINT32 IOC:1; + UINT32 IDT:1; + UINT32 RsvdZ1:2; + UINT32 BEI:1; + UINT32 Type:6; + UINT32 RsvdZ2:16; +} TRANSFER_TRB_NORMAL; + +// +// 6.4.2.1 Transfer Event TRB: 16 bytes +// A Transfer Event provides the completion status associated with a Transfer TRB. Refer to section 4.11.3.1 +// for more information on the use and operation of Transfer Events. +// +typedef struct _EVT_TRB_TRANSFER { + UINT32 TRBPtrLo; + + UINT32 TRBPtrHi; + + UINT32 Length:24; + UINT32 Completecode:8; + + UINT32 CycleBit:1; + UINT32 RsvdZ1:1; + UINT32 ED:1; + UINT32 RsvdZ2:7; + UINT32 Type:6; + UINT32 EndpointId:5; + UINT32 RsvdZ3:3; + UINT32 SlotId:8; +} EVT_TRB_TRANSFER; + +// +// 6.4.4.1 Link TRB: 16 bytes +// A Link TRB provides support for non-contiguous TRB Rings. +// +typedef struct _LINK_TRB { + UINT32 PtrLo; + + UINT32 PtrHi; + + UINT32 RsvdZ1:22; + UINT32 InterTarget:10; + + UINT32 CycleBit:1; + UINT32 TC:1; + UINT32 RsvdZ2:2; + UINT32 CH:1; + UINT32 IOC:1; + UINT32 RsvdZ3:4; + UINT32 Type:6; + UINT32 RsvdZ4:16; +} LINK_TRB; + +// +// TRB Template: 16 bytes +// +typedef struct _TRB_TEMPLATE { + UINT32 Parameter1; + + UINT32 Parameter2; + + UINT32 Status; + + UINT32 CycleBit:1; + UINT32 RsvdZ1:9; + UINT32 Type:6; + UINT32 Control:16; +} TRB_TEMPLATE; + +// +// Refer to XHCI 6.5 Event Ring Segment Table: 16 bytes +// +typedef struct _EVENT_RING_SEG_TABLE_ENTRY { + UINT32 PtrLo; + UINT32 PtrHi; + UINT32 RingTrbSize:16; + UINT32 RsvdZ1:16; + UINT32 RsvdZ2; +} EVENT_RING_SEG_TABLE_ENTRY; + +// +// Size: 40 bytes +// +typedef struct _EVENT_RING { + EFI_PHYSICAL_ADDRESS ERSTBase; + EFI_PHYSICAL_ADDRESS EventRingSeg0; + UINT32 TrbNumber; + EFI_PHYSICAL_ADDRESS EventRingEnqueue; + EFI_PHYSICAL_ADDRESS EventRingDequeue; + UINT32 EventRingCCS; +} EVENT_RING; + +// Size: 32 bytes +typedef struct _TRANSFER_RING { + EFI_PHYSICAL_ADDRESS RingSeg0; + UINT32 TrbNumber; + EFI_PHYSICAL_ADDRESS RingEnqueue; + EFI_PHYSICAL_ADDRESS RingDequeue; + UINT32 RingPCS; +} TRANSFER_RING; + +// +// Size: 64 bytes +// +typedef struct _DBC_INFO_CONTEXT { + UINT64 String0DescAddress; + UINT64 ManufacturerStrDescAddress; + UINT64 ProductStrDescAddress; + UINT64 SerialNumberStrDescAddress; + UINT64 String0Length:8; + UINT64 ManufacturerStrLength:8; + UINT64 ProductStrLength:8; + UINT64 SerialNumberStrLength:8; + UINT64 RsvdZ1:32; + UINT64 RsvdZ2; + UINT64 RsvdZ3; + UINT64 RsvdZ4; +} DBC_INFO_CONTEXT; + +// +// Debug Capability Context Data Structure: 192 bytes +// +typedef struct _XHC_DC_CONTEXT { + DBC_INFO_CONTEXT DbcInfoContext; + ENDPOINT_CONTEXT_64 EpOutContext; + ENDPOINT_CONTEXT_64 EpInContext; +} XHC_DC_CONTEXT; + +// +// Size: 16 bytes +// +typedef union _TRB { + TRB_TEMPLATE TrbTemplate; + TRANSFER_TRB_NORMAL TrbNormal; +} TRB; + +/// +/// USB data transfer direction +/// +typedef enum { + EfiUsbDataIn, + EfiUsbDataOut, + EfiUsbNoData +} EFI_USB_DATA_DIRECTION; + +// +// URB (Usb Request Block) contains information for all kinds of +// usb requests. +// +typedef struct _URB { + // + // Transfer data buffer + // + EFI_PHYSICAL_ADDRESS Data; + UINT32 DataLen; + + // + // Execute result + // + UINT32 Result; + // + // Completed data length + // + UINT32 Completed; + // + // Tranfer Ring info + // + EFI_PHYSICAL_ADDRESS Ring; + EFI_PHYSICAL_ADDRESS Trb; + BOOLEAN Finished; + EFI_USB_DATA_DIRECTION Direction; +} URB; + +typedef struct _USB3_DEBUG_PORT_INSTANCE { + UINT8 Initialized; + + // + // The flag indicates debug device is ready + // + BOOLEAN DebugSupport; + + // + // The flag indicates debug device is ready + // + BOOLEAN Ready; + + // + // The flag indicates if USB 3.0 ports has been turn off/on power + // + BOOLEAN ChangePortPower; + + // + // XHCI MMIO Base address + // + EFI_PHYSICAL_ADDRESS XhciMmioBase; + + // + // XHCI OP RegisterBase address + // + EFI_PHYSICAL_ADDRESS XhciOpRegister; + + // + // XHCI Debug Register Base Address + // + EFI_PHYSICAL_ADDRESS DebugCapabilityBase; + + // + // XHCI Debug Capability offset + // + UINT64 DebugCapabilityOffset; + + // + // XHCI Debug Context Address + // + EFI_PHYSICAL_ADDRESS DebugCapabilityContext; + + // + // Transfer Ring + // + TRANSFER_RING TransferRingOut; + TRANSFER_RING TransferRingIn; + + // + // EventRing + // + EVENT_RING EventRing; + + // + // URB - Read + // + URB UrbOut; + + // + // URB - Write + // + URB UrbIn; + + // + // The available data length in the following data buffer. + // + UINT8 DataCount; + // + // The data buffer. Maximum length is 8 bytes. + // + UINT8 Data[8]; + // + // Timter settings + // + UINT64 TimerFrequency; + UINT64 TimerCycle; + BOOLEAN TimerCountDown; + +} USB3_DEBUG_PORT_HANDLE; + +#pragma pack() + +/** + Read XHCI debug register. + + @param Handle Debug port handle. + @param Offset The offset of the debug register. + + @return The register content read + +**/ +UINT32 +XhcReadDebugReg ( + IN USB3_DEBUG_PORT_HANDLE *Handle, + IN UINT32 Offset + ); + +/** + Set one bit of the debug register while keeping other bits. + + @param Handle Debug port handle. + @param Offset The offset of the debug register. + @param Bit The bit mask of the register to set. + +**/ +VOID +XhcSetDebugRegBit ( + IN USB3_DEBUG_PORT_HANDLE *Handle, + IN UINT32 Offset, + IN UINT32 Bit + ); + +/** + Write the data to the debug register. + + @param Handle Debug port handle. + @param Offset The offset of the debug register. + @param Data The data to write. + +**/ +VOID +XhcWriteDebugReg ( + IN USB3_DEBUG_PORT_HANDLE *Handle, + IN UINT32 Offset, + IN UINT32 Data + ); + +/** + Discover the USB3 debug device. + + @param Handle Debug port handle. + + @retval RETURN_SUCCESS The serial device was initialized. + @retval RETURN_DEVICE_ERROR The serial device could not be initialized. + +**/ +RETURN_STATUS +DiscoverUsb3DebugPort( + USB3_DEBUG_PORT_HANDLE *Handle + ); + +/** + Initialize the Serial Device hardware. + + @param Handle Debug port handle. + + @retval RETURN_SUCCESS The serial device was initialized successfully. + @retval !RETURN_SUCCESS Error. + +**/ +RETURN_STATUS +InitializeUsb3DebugPort ( + USB3_DEBUG_PORT_HANDLE *Handle + ); + +/** + Return XHCI MMIO base address. + +**/ +EFI_PHYSICAL_ADDRESS +GetXhciBaseAddress ( + VOID + ); + +/** + Verifies if the bit positions specified by a mask are set in a register. + + @param[in, out] Register UNITN register + @param[in] BitMask 32-bit mask + + @return BOOLEAN - TRUE if all bits specified by the mask are enabled. + - FALSE even if one of the bits specified by the mask + is not enabled. +**/ +BOOLEAN +XhcIsBitSet( + UINTN Register, + UINT32 BitMask + ); + +/** + Sets bits as per the enabled bit positions in the mask. + + @param[in, out] Register UINTN register + @param[in] BitMask 32-bit mask +**/ +VOID +XhcSetR32Bit( + UINTN Register, + UINT32 BitMask + ); + +/** + Clears bits as per the enabled bit positions in the mask. + + @param[in, out] Register UINTN register + @param[in] BitMask 32-bit mask +**/ +VOID +XhcClearR32Bit( + IN OUT UINTN Register, + IN UINT32 BitMask + ); + +/** + Initialize USB3 debug port. + + This method invokes various internal functions to facilitate + detection and initialization of USB3 debug port. + + @retval RETURN_SUCCESS The serial device was initialized. +**/ +RETURN_STATUS +EFIAPI +USB3Initialize ( + VOID + ); + +/** + Return command register value in XHCI controller. + +**/ +UINT16 +GetXhciPciCommand ( + VOID + ); + +/** + Allocate aligned memory for XHC's usage. + + @param BufferSize The size, in bytes, of the Buffer. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID* +AllocateAlignBuffer ( + IN UINTN BufferSize + ); + +/** + The real function to initialize USB3 debug port. + + This method invokes various internal functions to facilitate + detection and initialization of USB3 debug port. + + @retval RETURN_SUCCESS The serial device was initialized. +**/ +RETURN_STATUS +EFIAPI +USB3InitializeReal ( + VOID + ); + +/** + Submits bulk transfer to a bulk endpoint of a USB device. + + @param Handle The instance of debug device. + @param Direction The direction of data transfer. + @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 Timeout Indicates the maximum time, in millisecond, which + the transfer is allowed to complete. + + @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 Some 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 +XhcDataTransfer ( + IN USB3_DEBUG_PORT_HANDLE *Handle, + IN EFI_USB_DATA_DIRECTION Direction, + IN OUT VOID *Data, + IN OUT UINTN *DataLength, + IN UINTN Timeout + ); + +/** + Check if the timer is timeout. + + @param[in] UsbDebugPortHandle Pointer to USB Debug port handle + @param[in] Timer The start timer from the begin. + @param[in] TimeoutTicker Ticker number need time out. + + @return TRUE Timer time out occurs. + @retval FALSE Timer does not time out. + +**/ +BOOLEAN +IsTimerTimeout ( + IN USB3_DEBUG_PORT_HANDLE *UsbDebugPortHandle, + IN UINT64 Timer, + IN UINT64 TimeoutTicker + ); + +#endif //__SERIAL_PORT_LIB_USB__ diff --git a/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Pei.c b/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Pei.c new file mode 100644 index 0000000000..be3174e75f --- /dev/null +++ b/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Pei.c @@ -0,0 +1,45 @@ +/** @file + Debug Port Library implementation based on usb3 debug port. + + Copyright (c) 2014, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include +#include +#include "DebugCommunicationLibUsb3Internal.h" + +/** + Allocate aligned memory for XHC's usage. + + @param BufferSize The size, in bytes, of the Buffer. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID* +AllocateAlignBuffer ( + IN UINTN BufferSize + ) +{ + VOID *Buf; + EFI_PHYSICAL_ADDRESS Address; + EFI_STATUS Status; + + Buf = NULL; + Status = PeiServicesAllocatePages (EfiACPIMemoryNVS, EFI_SIZE_TO_PAGES (BufferSize), &Address); + if (EFI_ERROR (Status)) { + Buf = NULL; + } else { + Buf = (VOID *)(UINTN) Address; + } + return Buf; +} + diff --git a/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Pei.inf b/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Pei.inf new file mode 100644 index 0000000000..24cd4a0b5e --- /dev/null +++ b/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Pei.inf @@ -0,0 +1,70 @@ +## @file +# Debug Communication Library instance based on usb3 debug port. +# +# Copyright (c) 2014, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php. +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = DebugCommunicationLibUsb3Pei + MODULE_UNI_FILE = DebugCommunicationLibUsb3Pei.uni + FILE_GUID = 106C877F-C2BA-4c46-876C-BDFE6171CD7E + MODULE_TYPE = PEIM + VERSION_STRING = 1.0 + LIBRARY_CLASS = DebugCommunicationLib|PEIM PEI_CORE + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF +# + +[Sources] + DebugCommunicationLibUsb3Pei.c + DebugCommunicationLibUsb3Transfer.c + DebugCommunicationLibUsb3Common.c + DebugCommunicationLibUsb3Internal.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + IntelFrameworkPkg/IntelFrameworkPkg.dec + SourceLevelDebugPkg/SourceLevelDebugPkg.dec + +[Pcd] + ## The memory BAR of ehci host controller, in which usb debug feature is enabled. + ## Note that the memory BAR address is only used before Pci bus resource allocation. + gEfiSourceLevelDebugPkgTokenSpaceGuid.PcdUsbXhciMemorySpaceBase ## SOMETIMES_CONSUMES + + ## The pci address of ehci host controller, in which usb debug feature is enabled. + ## The format of pci address please refer to SourceLevelDebugPkg.dec + gEfiSourceLevelDebugPkgTokenSpaceGuid.PcdUsbXhciPciAddress ## CONSUMES + + ## Per XHCI spec, software shall impose a timeout between the detection of the Debug Host + ## connection and the DbC Run transition to 1. This PCD specifies the timeout value in microsecond. + gEfiSourceLevelDebugPkgTokenSpaceGuid.PcdUsbXhciDebugDetectTimeout ## SOMETIMES_CONSUMES + + ## The value of data buffer size used for USB debug port handle. + ## It should be equal to sizeof (USB3_DEBUG_PORT_HANDLE). + gEfiSourceLevelDebugPkgTokenSpaceGuid.PcdDebugPortHandleBufferSize|256 ## SOMETIMES_CONSUMES + +[LibraryClasses] + BaseLib + PcdLib + IoLib + PciLib + TimerLib + BaseMemoryLib + PeiServicesLib + +[Depex.common.PEIM] + gEfiPeiMemoryDiscoveredPpiGuid diff --git a/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Pei.uni b/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Pei.uni new file mode 100644 index 0000000000000000000000000000000000000000..074b6860044ae7d4c37cc29b3af2fe379c2d7621 GIT binary patch literal 1968 zcmdUvOK%fF5QOWD#D7>h7eI;4>x2-(#tW=~*z!YxQ{-2imFzfL+X?yez*n=e$Cw~- zBwFpx^mI>mO?A!w{83X)3GWl0L~nJbTr*AdP!CwOuZcC(T2tolw9uSqtqbBt8feA1 zz)JL)*-!;*g>JBOR+sn(j3aU+cn7RZG-h7z>8q=TUSN-Rs$AkN*qMOFvqWvB${6R^ zW2zhBQq?WW`-;|lUcy!ZTT`8fGe3f@$DH-Kp6Z!i>X3N`hbzuJW9~f#yZnv(SMk*G zJ0fOV2FqMcSiRyqhPBsl-qD*o*z18ICpsa2L4|3kF~@cn-M1AH?&ScKBO>NR+IJKB zZH>$WD%Z=I&BzwJV{Lf%44*Y&g_L^OEMxbY zYxanBjH{MW?}>HwZ;YSt7`bnt|3BsFeTr0Hw|Qdr3@+N}$u*lhyhTm + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ +#include "DebugCommunicationLibUsb3Internal.h" + +/** + Synchronize the specified transfer ring to update the enqueue and dequeue pointer. + + @param Handle Debug port handle. + @param TrsRing The transfer ring to sync. + + @retval EFI_SUCCESS The transfer ring is synchronized successfully. + +**/ +EFI_STATUS +EFIAPI +XhcSyncTrsRing ( + IN USB3_DEBUG_PORT_HANDLE *Handle, + IN TRANSFER_RING *TrsRing + ) +{ + UINTN Index; + TRB_TEMPLATE *TrsTrb; + UINT32 CycleBit; + + ASSERT (TrsRing != NULL); + + // + // Calculate the latest RingEnqueue and RingPCS + // + TrsTrb = (TRB_TEMPLATE *)(UINTN) TrsRing->RingEnqueue; + + ASSERT (TrsTrb != NULL); + + for (Index = 0; Index < TrsRing->TrbNumber; Index++) { + if (TrsTrb->CycleBit != (TrsRing->RingPCS & BIT0)) { + break; + } + TrsTrb++; + if ((UINT8) TrsTrb->Type == TRB_TYPE_LINK) { + ASSERT (((LINK_TRB*)TrsTrb)->TC != 0); + // + // set cycle bit in Link TRB as normal + // + ((LINK_TRB*)TrsTrb)->CycleBit = TrsRing->RingPCS & BIT0; + // + // Toggle PCS maintained by software + // + TrsRing->RingPCS = (TrsRing->RingPCS & BIT0) ? 0 : 1; + TrsTrb = (TRB_TEMPLATE *)(UINTN)((TrsTrb->Parameter1 | LShiftU64 ((UINT64)TrsTrb->Parameter2, 32)) & ~0x0F); + } + } + ASSERT (Index != TrsRing->TrbNumber); + + if ((EFI_PHYSICAL_ADDRESS)(UINTN) TrsTrb != TrsRing->RingEnqueue) { + TrsRing->RingEnqueue = (EFI_PHYSICAL_ADDRESS)(UINTN) TrsTrb; + } + + // + // Clear the Trb context for enqueue, but reserve the PCS bit which indicates free Trb. + // + CycleBit = TrsTrb->CycleBit; + ZeroMem (TrsTrb, sizeof (TRB_TEMPLATE)); + TrsTrb->CycleBit = CycleBit; + + return EFI_SUCCESS; +} + +/** + Synchronize the specified event ring to update the enqueue and dequeue pointer. + + @param Handle Debug port handle. + @param EvtRing The event ring to sync. + + @retval EFI_SUCCESS The event ring is synchronized successfully. + +**/ +EFI_STATUS +EFIAPI +XhcSyncEventRing ( + IN USB3_DEBUG_PORT_HANDLE *Handle, + IN EVENT_RING *EvtRing + ) +{ + UINTN Index; + TRB_TEMPLATE *EvtTrb1; + + ASSERT (EvtRing != NULL); + + // + // Calculate the EventRingEnqueue and EventRingCCS. + // Note: only support single Segment + // + EvtTrb1 = (TRB_TEMPLATE *)(UINTN) EvtRing->EventRingDequeue; + + for (Index = 0; Index < EvtRing->TrbNumber; Index++) { + if (EvtTrb1->CycleBit != EvtRing->EventRingCCS) { + break; + } + + EvtTrb1++; + + if ((UINTN)EvtTrb1 >= ((UINTN) EvtRing->EventRingSeg0 + sizeof (TRB_TEMPLATE) * EvtRing->TrbNumber)) { + EvtTrb1 = (TRB_TEMPLATE *)(UINTN) EvtRing->EventRingSeg0; + EvtRing->EventRingCCS = (EvtRing->EventRingCCS) ? 0 : 1; + } + } + + if (Index < EvtRing->TrbNumber) { + EvtRing->EventRingEnqueue = (EFI_PHYSICAL_ADDRESS)(UINTN)EvtTrb1; + } else { + ASSERT (FALSE); + } + + return EFI_SUCCESS; +} + +/** + Check if there is a new generated event. + + @param Handle Debug port handle. + @param EvtRing The event ring to check. + @param NewEvtTrb The new event TRB found. + + @retval EFI_SUCCESS Found a new event TRB at the event ring. + @retval EFI_NOT_READY The event ring has no new event. + +**/ +EFI_STATUS +EFIAPI +XhcCheckNewEvent ( + IN USB3_DEBUG_PORT_HANDLE *Handle, + IN EVENT_RING *EvtRing, + OUT TRB_TEMPLATE **NewEvtTrb + ) +{ + EFI_STATUS Status; + TRB_TEMPLATE *EvtTrb; + + ASSERT (EvtRing != NULL); + + EvtTrb = (TRB_TEMPLATE *)(UINTN) EvtRing->EventRingDequeue; + *NewEvtTrb = (TRB_TEMPLATE *)(UINTN) EvtRing->EventRingDequeue; + + if (EvtRing->EventRingDequeue == EvtRing->EventRingEnqueue) { + return EFI_NOT_READY; + } + + Status = EFI_SUCCESS; + + EvtRing->EventRingDequeue += sizeof (TRB_TEMPLATE); + // + // If the dequeue pointer is beyond the ring, then roll-back it to the begining of the ring. + // + if ((UINTN)EvtRing->EventRingDequeue >= ((UINTN) EvtRing->EventRingSeg0 + sizeof (TRB_TEMPLATE) * EvtRing->TrbNumber)) { + EvtRing->EventRingDequeue = EvtRing->EventRingSeg0; + } + + return Status; +} + +/** + Check if the Trb is a transaction of the URB. + + @param Ring The transfer ring to be checked. + @param Trb The TRB to be checked. + + @retval TRUE It is a transaction of the URB. + @retval FALSE It is not any transaction of the URB. + +**/ +BOOLEAN +IsTrbInTrsRing ( + IN TRANSFER_RING *Ring, + IN TRB_TEMPLATE *Trb + ) +{ + TRB_TEMPLATE *CheckedTrb; + UINTN Index; + + CheckedTrb = (TRB_TEMPLATE *)(UINTN) Ring->RingSeg0; + + ASSERT (Ring->TrbNumber == TR_RING_TRB_NUMBER); + + for (Index = 0; Index < Ring->TrbNumber; Index++) { + if (Trb == CheckedTrb) { + return TRUE; + } + CheckedTrb++; + } + + return FALSE; +} + +/** + Check the URB's execution result and update the URB's + result accordingly. + + @param Handle Debug port handle. + @param Urb The URB to check result. + +**/ +VOID +XhcCheckUrbResult ( + IN USB3_DEBUG_PORT_HANDLE *Handle, + IN URB *Urb + ) +{ + EVT_TRB_TRANSFER *EvtTrb; + TRB_TEMPLATE *TRBPtr; + UINTN Index; + EFI_STATUS Status; + URB *CheckedUrb; + UINT64 XhcDequeue; + UINT32 High; + UINT32 Low; + + ASSERT ((Handle != NULL) && (Urb != NULL)); + + if (Urb->Finished) { + goto EXIT; + } + + EvtTrb = NULL; + + // + // Traverse the event ring to find out all new events from the previous check. + // + XhcSyncEventRing (Handle, &Handle->EventRing); + + for (Index = 0; Index < Handle->EventRing.TrbNumber; Index++) { + + Status = XhcCheckNewEvent (Handle, &Handle->EventRing, ((TRB_TEMPLATE **)&EvtTrb)); + if (Status == EFI_NOT_READY) { + // + // All new events are handled, return directly. + // + goto EXIT; + } + + if ((EvtTrb->Type != TRB_TYPE_COMMAND_COMPLT_EVENT) && (EvtTrb->Type != TRB_TYPE_TRANS_EVENT)) { + continue; + } + + TRBPtr = (TRB_TEMPLATE *)(UINTN)(EvtTrb->TRBPtrLo | LShiftU64 ((UINT64) EvtTrb->TRBPtrHi, 32)); + + if (IsTrbInTrsRing ((TRANSFER_RING *)(UINTN)(Urb->Ring), TRBPtr)) { + CheckedUrb = Urb; + } else if (IsTrbInTrsRing ((TRANSFER_RING *)(UINTN)(Handle->UrbIn.Ring), TRBPtr)) { + // + // If it is read event and it should be generated by poll, and current operation is write, we need save data into internal buffer. + // Internal buffer is used by next read. + // + Handle->DataCount = (UINT8) (Handle->UrbIn.DataLen - EvtTrb->Length); + CopyMem (Handle->Data, (VOID *)(UINTN)Handle->UrbIn.Data, Handle->DataCount); + // + // Fill this TRB complete with CycleBit, otherwise next read will fail with old TRB. + // + TRBPtr->CycleBit = (TRBPtr->CycleBit & BIT0) ? 0 : 1; + continue; + } else { + continue; + } + + if ((EvtTrb->Completecode == TRB_COMPLETION_SHORT_PACKET) || + (EvtTrb->Completecode == TRB_COMPLETION_SUCCESS)) { + // + // The length of data which were transferred. + // + CheckedUrb->Completed += (CheckedUrb->DataLen - EvtTrb->Length); + } else { + CheckedUrb->Result |= EFI_USB_ERR_TIMEOUT; + } + // + // This Urb has been processed + // + CheckedUrb->Finished = TRUE; + } + +EXIT: + // + // Advance event ring to last available entry + // + // Some 3rd party XHCI external cards don't support single 64-bytes width register access, + // So divide it to two 32-bytes width register access. + // + Low = XhcReadDebugReg (Handle, XHC_DC_DCERDP); + High = XhcReadDebugReg (Handle, XHC_DC_DCERDP + 4); + XhcDequeue = (UINT64)(LShiftU64((UINT64)High, 32) | Low); + + if ((XhcDequeue & (~0x0F)) != ((UINT64)(UINTN)Handle->EventRing.EventRingDequeue & (~0x0F))) { + // + // Some 3rd party XHCI external cards don't support single 64-bytes width register access, + // So divide it to two 32-bytes width register access. + // + XhcWriteDebugReg (Handle, XHC_DC_DCERDP, XHC_LOW_32BIT (Handle->EventRing.EventRingDequeue)); + XhcWriteDebugReg (Handle, XHC_DC_DCERDP + 4, XHC_HIGH_32BIT (Handle->EventRing.EventRingDequeue)); + } +} + +/** + Ring the door bell to notify XHCI there is a transaction to be executed. + + @param Handle Debug port handle. + @param Urb The pointer to URB. + + @retval EFI_SUCCESS Successfully ring the door bell. + +**/ +EFI_STATUS +EFIAPI +XhcRingDoorBell ( + IN USB3_DEBUG_PORT_HANDLE *Handle, + IN URB *Urb + ) +{ + UINT32 Dcdb; + + // + // 7.6.8.2 DCDB Register + // + Dcdb = (Urb->Direction == EfiUsbDataIn) ? 0x100 : 0x0; + + XhcWriteDebugReg ( + Handle, + XHC_DC_DCDB, + Dcdb + ); + + return EFI_SUCCESS; +} + +/** + Execute the transfer by polling the URB. This is a synchronous operation. + + @param Handle Debug port handle. + @param Urb The URB to execute. + @param Timeout The time to wait before abort, in microsecond. + +**/ +VOID +XhcExecTransfer ( + IN USB3_DEBUG_PORT_HANDLE *Handle, + IN URB *Urb, + IN UINTN Timeout + ) +{ + TRANSFER_RING *Ring; + UINT64 Begin; + UINT64 TimeoutTicker; + UINT64 TimerRound; + TRB_TEMPLATE *Trb; + + Begin = 0; + TimeoutTicker = 0; + TimerRound = 0; + + XhcRingDoorBell (Handle, Urb); + + if (Timeout != 0) { + Begin = GetPerformanceCounter (); + TimeoutTicker = DivU64x32 ( + MultU64x64 ( + Handle->TimerFrequency, + Timeout + ), + 1000000u + ); + TimerRound = DivU64x64Remainder ( + TimeoutTicker, + DivU64x32 (Handle->TimerCycle, 2), + &TimeoutTicker + ); + } + + // + // Event Ring Not Empty bit can only be set to 1 by XHC after ringing door bell with some delay. + // + while (TRUE) { + if (Timeout != 0) { + if (TimerRound == 0) { + if (IsTimerTimeout (Handle, Begin, TimeoutTicker)) { + // + // If time out occurs. + // + Urb->Result |= EFI_USB_ERR_TIMEOUT; + break; + } + } else { + if (IsTimerTimeout (Handle, Begin, DivU64x32 (Handle->TimerCycle, 2))) { + TimerRound --; + } + } + } + XhcCheckUrbResult (Handle, Urb); + if (Urb->Finished) { + break; + } + } + + // + // If URB transfer is error, restore transfer ring to original value before URB transfer + // This will make the current transfer TRB is always at the latest unused one in transfer ring. + // + Ring = (TRANSFER_RING *)(UINTN) Urb->Ring; + if ((Urb->Result != EFI_USB_NOERROR) && (Urb->Direction == EfiUsbDataIn)) { + // + // Adjust Enqueue pointer + // + Ring->RingEnqueue = Urb->Trb; + // + // Clear CCS flag for next use + // + Trb = (TRB_TEMPLATE *)(UINTN) Urb->Trb; + Trb->CycleBit = ((~Ring->RingPCS) & BIT0); + } else { + // + // Update transfer ring for next transfer. + // + XhcSyncTrsRing (Handle, Ring); + } +} + +/** + Create a transfer TRB. + + @param Handle Debug port handle. + @param Urb The urb used to construct the transfer TRB. + + @return Created TRB or NULL + +**/ +EFI_STATUS +XhcCreateTransferTrb ( + IN USB3_DEBUG_PORT_HANDLE *Handle, + IN URB *Urb + ) +{ + TRANSFER_RING *EPRing; + TRB *Trb; + + if (Urb->Direction == EfiUsbDataIn) { + EPRing = &Handle->TransferRingIn; + } else { + EPRing = &Handle->TransferRingOut; + } + + Urb->Ring = (EFI_PHYSICAL_ADDRESS)(UINTN) EPRing; + XhcSyncTrsRing (Handle, EPRing); + + Urb->Trb = EPRing->RingEnqueue; + Trb = (TRB *)(UINTN)EPRing->RingEnqueue; + Trb = (TRB *)(UINTN)EPRing->RingEnqueue; + Trb->TrbNormal.TRBPtrLo = XHC_LOW_32BIT (Urb->Data); + Trb->TrbNormal.TRBPtrHi = XHC_HIGH_32BIT (Urb->Data); + Trb->TrbNormal.Length = Urb->DataLen; + Trb->TrbNormal.TDSize = 0; + Trb->TrbNormal.IntTarget = 0; + Trb->TrbNormal.ISP = 1; + Trb->TrbNormal.IOC = 1; + Trb->TrbNormal.Type = TRB_TYPE_NORMAL; + + // + // Update the cycle bit to indicate this TRB has been consumed. + // + Trb->TrbNormal.CycleBit = EPRing->RingPCS & BIT0; + + return EFI_SUCCESS; +} + +/** + Create a new URB for a new transaction. + + @param Handle Debug port handle. + @param Direction The direction of data flow. + @param Data The user data to transfer + @param DataLen The length of data buffer + + @return Created URB or NULL + +**/ +URB* +XhcCreateUrb ( + IN USB3_DEBUG_PORT_HANDLE *Handle, + IN EFI_USB_DATA_DIRECTION Direction, + IN VOID *Data, + IN UINTN DataLen + ) +{ + EFI_STATUS Status; + URB *Urb; + EFI_PHYSICAL_ADDRESS UrbData; + + if (Direction == EfiUsbDataIn) { + Urb = &Handle->UrbIn; + } else { + Urb = &Handle->UrbOut; + } + + UrbData = Urb->Data; + + ZeroMem (Urb, sizeof (URB)); + Urb->Direction = Direction; + + // + // Allocate memory to move data from CAR or SMRAM to normal memory + // to make XHCI DMA successfully + // re-use the pre-allocate buffer in PEI to avoid DXE memory service or gBS are not ready + // + Urb->Data = UrbData; + + if (Direction == EfiUsbDataIn) { + // + // Do not break URB data in buffer as it may contain the data which were just put in via DMA by XHC + // + Urb->DataLen = (UINT32) DataLen; + } else { + // + // Put data into URB data out buffer which will create TRBs + // + ZeroMem ((VOID*)(UINTN) Urb->Data, DataLen); + CopyMem ((VOID*)(UINTN) Urb->Data, Data, DataLen); + Urb->DataLen = (UINT32) DataLen; + } + + Status = XhcCreateTransferTrb (Handle, Urb); + ASSERT_EFI_ERROR (Status); + + return Urb; +} + +/** + Submits bulk transfer to a bulk endpoint of a USB device. + + @param Handle Debug port handle. + @param Direction The direction of data transfer. + @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 Timeout Indicates the maximum time, in microsecond, which + the transfer is allowed to complete. + + @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 Some 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 +XhcDataTransfer ( + IN USB3_DEBUG_PORT_HANDLE *Handle, + IN EFI_USB_DATA_DIRECTION Direction, + IN OUT VOID *Data, + IN OUT UINTN *DataLength, + IN UINTN Timeout + ) +{ + URB *Urb; + EFI_STATUS Status; + + // + // Validate the parameters + // + if ((DataLength == NULL) || (*DataLength == 0) || (Data == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Create a new URB, insert it into the asynchronous + // schedule list, then poll the execution status. + // + Urb = XhcCreateUrb (Handle, Direction, Data, *DataLength); + ASSERT (Urb != NULL); + + XhcExecTransfer (Handle, Urb, Timeout); + + *DataLength = Urb->Completed; + + Status = EFI_TIMEOUT; + if (Urb->Result == EFI_USB_NOERROR) { + Status = EFI_SUCCESS; + } + + if (Direction == EfiUsbDataIn) { + // + // Move data from internal buffer to outside buffer (outside buffer may be in SMRAM...) + // SMRAM does not allow to do DMA, so we create an internal buffer. + // + CopyMem (Data, (VOID *)(UINTN)Urb->Data, *DataLength); + } + + return Status; +} + diff --git a/SourceLevelDebugPkg/SourceLevelDebugPkg.dec b/SourceLevelDebugPkg/SourceLevelDebugPkg.dec index 5c28e2934e..e634ca7638 100644 --- a/SourceLevelDebugPkg/SourceLevelDebugPkg.dec +++ b/SourceLevelDebugPkg/SourceLevelDebugPkg.dec @@ -91,5 +91,27 @@ # @Prompt Assign debug port buffer size. gEfiSourceLevelDebugPkgTokenSpaceGuid.PcdDebugPortHandleBufferSize|0x0|UINT16|0x00000006 + ## The memory BAR of xhci host controller, in which usb debug feature is enabled. + ## Note that the memory BAR address is only used before Pci bus resource allocation. + # @Prompt Configure ehci host controller memory BAR. + gEfiSourceLevelDebugPkgTokenSpaceGuid.PcdUsbXhciMemorySpaceBase|0xD0000000|UINT64|0x00000007 + + ## The pci address of xhci host controller, in which usb debug feature is enabled. + # The format of pci address is :
+ # -----------------------------------------------------------------------
+ # | Bits 28..31 | Bits 20..27 | Bits 15..19 | Bits 12..14 | Bits 00..11 |
+ # -----------------------------------------------------------------------
+ # | 0 | Bus | Device | Function | 0 |
+ # -----------------------------------------------------------------------
+ # For the value 0x000A0000, it means the pci address at bus 0x0, device 0x14, function 0x0. + # @Prompt Configure xhci host controller pci address. + # @Expression 0x80000001 | (gEfiSourceLevelDebugPkgTokenSpaceGuid.PcdUsbXhciPciAddress & 0xF0000FFF) == 0 + gEfiSourceLevelDebugPkgTokenSpaceGuid.PcdUsbXhciPciAddress|0x000A0000|UINT32|0x00000008 + + ## Per XHCI spec, software shall impose a timeout between the detection of the Debug Host + ## connection and the DbC Run transition to 1. This PCD specifies the timeout value in microsecond. + # @Prompt Configure debug device detection timeout value. + gEfiSourceLevelDebugPkgTokenSpaceGuid.PcdUsbXhciDebugDetectTimeout|3000000|UINT64|0x00000009 + [UserExtensions.TianoCore."ExtraFiles"] SourceLevelDebugPkgExtra.uni diff --git a/SourceLevelDebugPkg/SourceLevelDebugPkg.dsc b/SourceLevelDebugPkg/SourceLevelDebugPkg.dsc index a7be9ad51c..7f17cd37c1 100644 --- a/SourceLevelDebugPkg/SourceLevelDebugPkg.dsc +++ b/SourceLevelDebugPkg/SourceLevelDebugPkg.dsc @@ -51,8 +51,10 @@ !ifdef $(SOURCE_DEBUG_USE_USB) DebugCommunicationLib|SourceLevelDebugPkg/Library/DebugCommunicationLibUsb/DebugCommunicationLibUsb.inf !else +!ifndef $(SOURCE_DEBUG_USE_USB3) DebugCommunicationLib|SourceLevelDebugPkg/Library/DebugCommunicationLibSerialPort/DebugCommunicationLibSerialPort.inf !endif +!endif [LibraryClasses.common.PEIM] PeimEntryPoint|MdePkg/Library/PeimEntryPoint/PeimEntryPoint.inf @@ -61,6 +63,9 @@ HobLib|MdePkg/Library/PeiHobLib/PeiHobLib.inf MemoryAllocationLib|MdePkg/Library/PeiMemoryAllocationLib/PeiMemoryAllocationLib.inf DebugAgentLib|SourceLevelDebugPkg/Library/DebugAgent/SecPeiDebugAgentLib.inf +!ifdef $(SOURCE_DEBUG_USE_USB3) + DebugCommunicationLib|SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Pei.inf +!endif [LibraryClasses.common.DXE_DRIVER] UefiDriverEntryPoint|MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf @@ -71,6 +76,9 @@ HobLib|MdePkg/Library/DxeHobLib/DxeHobLib.inf MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf DebugAgentLib|SourceLevelDebugPkg/Library/DebugAgent/DxeDebugAgentLib.inf +!ifdef $(SOURCE_DEBUG_USE_USB3) + DebugCommunicationLib|SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Dxe.inf +!endif ################################################################################################### # -- 2.39.2