/** @file USB Status Code related definitions and fundtions ;****************************************************************************** ;* Copyright (c) 2013 - 2017, Insyde Software Corporation. All Rights Reserved. ;* ;* You may not reproduce, distribute, publish, display, perform, modify, adapt, ;* transmit, broadcast, present, recite, release, license or otherwise exploit ;* any part of this publication in any form, by any means, without the prior ;* written permission of Insyde Software Corporation. ;* ;****************************************************************************** */ #include #include #include #include #include #include #include #include "UsbDebug.h" STATIC UINT8 DataPID[2] = { DATA0_PID, DATA1_PID }; typedef struct { UINT32 Bypass; UINT32 EhciControl; UINT32 UsbDebugPort; UINT32 DebugPort; UINT32 BypassTimeout; } DEBUGIO_CONFIG; struct { DEBUGIO_CONFIG Config; UINT32 EhciBar; } DebugIoConfig = { { 1, // 0:Bypass connection off, 1:Bypass connection on FixedPcdGet8 (PcdUsbDebugEhciControl), // Ehci port 1, // USB debug port 0x0080, // Debug port BYPASS_TIMEOUT // Bypass timeout }, FixedPcdGet32 (PcdUsbDebugEhciBaseAddress) }; typedef struct _DeviceTable{ UINT16 VenderID; UINT8 EhciHcDev[2]; UINT8 EhciHcFun; UINT8 PwrSts; UINT8 DbgBase; UINT8 DbgPort; } DeviceTable; DeviceTable EhciTable[] = { // // Intel ICH // { 0x8086, { 0x1d, 0x1a }, 0x07, 0x54, 0x5a, 0x00 }, // // SIS 964 // { 0x1039, { 0x03, 0x00 }, 0x03, 0x54, 0x5a, 0x00 }, // // VIA 8251 // { 0x1106, { 0x10, 0x00 }, 0x04, 0x84, 0x8a, 0x00 }, // // ATI SB600 // { 0x1002, { 0x13, 0x00 }, 0x05, 0xc4, 0xe6, 0x00 }, // // ATI SB700 // { 0x1002, { 0x12, 0x13 }, 0x02, 0xc4, 0xe6, 0x00 }, // // AMD Hudson-2/Hudson-3, SB900 // { 0x1022, { 0x12, 0x13 }, 0x02, 0xc4, 0xe6, 0x00 }, // // nVidia MCP // { 0x10de, { 0x02, 0x00 }, 0x01, 0x84, 0x46, 0x74 }, // // Empty for ending // { 0x0000, { 0x00, 0x00 }, 0x00, 0x00, 0x00, 0x00 }, }; #define DEVICE_NUMBER (sizeof (EhciTable) / sizeof (DeviceTable)) #define NOT_FOUND_DEVICE DEVICE_NUMBER #define TIME_FRAMES 8 UINT32 GetEhciDevice ( IN UINTN Number, IN BOOLEAN DeviceData ) { UINT32 EhciAddress; UINT32 EhciVender; UINT32 ClassCode; UINT16 VenderID; UINTN Dev; UINTN Fun; UINT32 HCNum; UINT32 Index; EhciVender = 0; // // EHCI Auto-detection // for (Dev = 0, HCNum = 0; Dev < 0x20; Dev ++) { for (Fun = 0; Fun < 8; Fun ++) { EhciAddress = PCI_LIB_ADDRESS(0, Dev, Fun, 0); ClassCode = PciRead32 (EhciAddress + PLATFORM_USB20_RID); if ((ClassCode & 0xFFFFFF00) == 0x0C032000) { VenderID = PciRead16 (EhciAddress + PLATFORM_USB20_VID); // // Check vender ID // for (Index = 0; EhciTable[Index].VenderID != 0; Index ++) { if (EhciTable[Index].VenderID == VenderID) { if (VenderID != 0x10de) { // // Non-nVidia method // if (EhciTable[Index].EhciHcDev[Number] == Dev) { EhciVender = Index; if (DeviceData) { return EhciAddress; } else { return EhciVender; } } } else { // // nVidia method // if (HCNum == Number) { EhciVender = Index; if (DeviceData) { return EhciAddress; } else { return EhciVender; } } } } } HCNum ++; } } } if (DeviceData) { return 0; } else { return NOT_FOUND_DEVICE; } } /** Get EHCI PCI address. @param [in] Register PCI register @retval None **/ UINT32 GetEhciAddress ( IN UINT32 Register ) { UINT32 EhciAddress; EhciAddress = 0; EhciAddress = GetEhciDevice (DebugIoConfig.Config.EhciControl, TRUE); if (EhciAddress != 0) return (EhciAddress + Register); return 0; } /** Read BAR address. @param None @retval None **/ UINT32 ReadBAR ( VOID ) { UINT32 BarAddress; UINT32 PciAddress; PciAddress = GetEhciAddress(PLATFORM_USB20_MEMBASE); BarAddress = PciRead32 (PciAddress); if (BarAddress != 0) { if (BarAddress != DebugIoConfig.EhciBar) { if ((*(UINT32*)(UINTN)BarAddress & 0xff000000) == 0x01000000 && (*(UINT32*)(UINTN)BarAddress & 0x000000ff) > 0) { return BarAddress; } } } PciWrite32 (PciAddress, DebugIoConfig.EhciBar); return DebugIoConfig.EhciBar; } /** GetCtrlPointer 0x20. @param None @retval Address **/ EFI_USB_CTRL_REG* GetCtrlPointer ( VOID ) { UINT32 Address; Address = ReadBAR (); Address += ((EFI_USB_CAP_REG*)(UINTN)Address)->CapLength; return (EFI_USB_CTRL_REG *)(UINTN)Address; } /** GetDebugPointer 0xA0. @param None @retval Address 0x98080400 + 0xA0 **/ EFI_USB_DBG_REG* GetDebugPointer ( VOID ) { UINT16 Offset; UINT32 Address; UINT32 PciAddress; UINT32 EhciVender; EhciVender = 0; EhciVender = GetEhciDevice (DebugIoConfig.Config.EhciControl, FALSE); if (EhciVender == NOT_FOUND_DEVICE) return 0; PciAddress = GetEhciAddress (EhciTable[EhciVender].DbgBase); Offset = PciRead16 (PciAddress); Address = ReadBAR () + (Offset & 0x1fff); return (EFI_USB_DBG_REG *)(UINTN)Address; } /** Get USB debug port number. @param None @return Port Number **/ UINTN GetUsbDebugPortNum ( VOID ) { UINT32 Address; UINTN PortNum; Address = ReadBAR (); PortNum = (((EFI_USB_CAP_REG*)(UINTN)Address)->HcsParams >> 20) & 0x0f; if (PortNum) PortNum --; return PortNum; } /** Set USB debug port number. @param [in] PortNum Port Number @retval None **/ VOID SetUsbDebugPortNum ( IN UINTN PortNum ) { UINT32 Data; UINT32 PciAddress; UINT32 EhciVender; EhciVender = 0; EhciVender = GetEhciDevice (DebugIoConfig.Config.EhciControl, FALSE); if (EhciVender == NOT_FOUND_DEVICE) return; if (EhciTable[EhciVender].VenderID == 0x10de) { // // nVidia MCP // PciAddress = GetEhciAddress (EhciTable[EhciVender].DbgPort); Data = PciRead32 (PciAddress); Data &= ~0x0000f000; Data |= (PortNum + 1) << 12; PciWrite32 (PciAddress, Data); } } /** DetectEhciExist. @param None TRUE : USB 2.0 exist FALSE : USB 2.0 not exist **/ BOOLEAN DetectEhciExist ( VOID ) { if (GetEhciAddress(PLATFORM_USB20_RID) == 0) return FALSE; return TRUE; } /** Enable Ehci Reg0x04->0x06. @param None @retval None **/ VOID EnableEhci ( VOID ) { UINT32 Data; UINT32 PciAddress; UINT32 EhciVender; EhciVender = 0; EhciVender = GetEhciDevice (DebugIoConfig.Config.EhciControl, FALSE); if (EhciVender == NOT_FOUND_DEVICE) return; // // Set power state to D0 // Data = 0x00; PciAddress = GetEhciAddress (EhciTable[EhciVender].PwrSts); PciWrite16 (PciAddress, (UINT16)Data); // // Enable BME and MSE // Data = 0x06; PciAddress = GetEhciAddress (PLATFORM_USB20_PCICMD); PciWrite8 (PciAddress, (UINT8)Data); } /** Disable Ehci Reg0x04->0x00. @param None @retval None **/ VOID DisableEhci ( VOID ) { UINT32 PciAddress; // // Disable BME and MSE // PciAddress = GetEhciAddress (PLATFORM_USB20_PCICMD); PciWrite8 (PciAddress, 0x00); } /** Sets EHCI Bar @param None @retval None **/ VOID SetMemBAR ( VOID ) { UINT32 EhciBar; UINT32 PciAddress; PciAddress = GetEhciAddress(PLATFORM_USB20_MEMBASE); EhciBar = PciRead32 (PciAddress); if (EhciBar != 0) return; EhciBar = DebugIoConfig.EhciBar; PciWrite32 (PciAddress, EhciBar); } /** UsbDebugPortOut. Check DebugPtr->Sc.Done = 1 @param [in] Buffer @param [in] Length @param [in] TokenPid @param [in] Toggle @param [in] DebugPtr @retval EFI_SUCCESS @retval EFI_TIMEOUT **/ EFI_STATUS UsbDebugPortOut ( IN UINT8 *Buffer, IN UINTN Length, IN UINT8 TokenPid, IN UINT8 Toggle, IN EFI_USB_DBG_REG *DebugPtr ) { UINTN Index; UINTN Timeout; UINT8 Toggle0; UINT8 Toggle1; EFI_USB_DBG_SC DebugSC; // // Set TOKEN_PID_CNT // *(UINT32*)&DebugPtr->Pid = (Toggle << 8) | TokenPid; // // Fill the Data to Debug Buffer // if (Length != 0) { DebugPtr->Data0 = *(UINT32*)&Buffer[0]; DebugPtr->Data1 = *(UINT32*)&Buffer[4]; } // // Set WRITE_READ#=1 and GO_CNT=1 // DebugSC = DebugPtr->Sc; DebugSC.DataLength = (UINT32)Length; DebugSC.Write = DEBUG_WRITE; DebugSC.Go = 1; DebugPtr->Sc = DebugSC; for(Index = 0, Toggle0 = 0, Timeout = PEI_TIMEOUT_TICKS * 2; Index < Timeout;) { if (DebugPtr->Sc.Done) { if (DebugPtr->Sc.Error && DebugPtr->Sc.Exception > 0) { return EFI_DEVICE_ERROR; } break; } Toggle1 = IoRead8(0x61); if (((Toggle0 ^ Toggle1) & 0x10) != 0) { Toggle0 = Toggle1; Index ++; } } if (Index == Timeout) { return EFI_TIMEOUT; } return EFI_SUCCESS; } /** UsbDebugPortIN. Check DebugPtr->Sc.Done = 1 *** CAUTION : The size of Buffer must large then 8 bytes *** @param [in] Buffer @param [in, out] Length @param [in] TokenPid @param [in] Toggle @param [in] DebugPtr @retval EFI_SUCCESS @retval EFI_TIMEOUT **/ EFI_STATUS UsbDebugPortIN ( IN UINT8 *Buffer, IN OUT UINTN *Length, IN UINT8 TokenPid, IN UINT8 Toggle, IN EFI_USB_DBG_REG *DebugPtr ) { UINTN Index; UINTN Timeout; UINT8 Toggle0; UINT8 Toggle1; EFI_USB_DBG_SC DebugSC; // // Set TOKEN_PID_CNT // *(UINT32*)&DebugPtr->Pid = (Toggle << 8) | TokenPid; // // Set WRITE_READ#=0 and GO_CNT=1 // DebugSC = DebugPtr->Sc; DebugSC.DataLength = 0; DebugSC.Write = DEBUG_READ; DebugSC.Go = 1; DebugPtr->Sc = DebugSC; for(Index = 0, Toggle0 = 0, Timeout = PEI_TIMEOUT_TICKS * 2; Index < Timeout;) { if (DebugPtr->Sc.Done) { if (DebugPtr->Sc.Error && DebugPtr->Sc.Exception > 0) { return EFI_DEVICE_ERROR; } break; } Toggle1 = IoRead8(0x61); if (((Toggle0 ^ Toggle1) & 0x10) != 0) { Toggle0 = Toggle1; Index ++; } } if (Index == Timeout) { return EFI_TIMEOUT; } if (DebugPtr->Sc.DataLength > 8) { return EFI_ABORTED; } // // Fill the Data to Buffer // *Length = DebugPtr->Sc.DataLength; if (Buffer && DebugPtr->Sc.DataLength) { *(UINT32*)&Buffer[0] = DebugPtr->Data0; *(UINT32*)&Buffer[4] = DebugPtr->Data1; } return EFI_SUCCESS; } /** Control Transfer. @param [in] Request @param [in] DeviceAddress @param [in] Buffer @param [in] DebugPtr @retval EFI_SUCCESS @retval EFI_TIMEOUT **/ EFI_STATUS ControlTransfer ( IN EFI_USB_DEVICE_REQUEST *Request, IN UINT32 DeviceAddress, IN UINT8 *Buffer, IN EFI_USB_DBG_REG *DebugPtr ) { EFI_STATUS Status; UINTN DataLength = 0; DebugPtr->Addr.Address = DeviceAddress; DebugPtr->Addr.Endpoint = 0; // // Setup Stage // Status = UsbDebugPortOut ((UINT8*)Request, sizeof (EFI_USB_DEVICE_REQUEST), SETUP_TOKEN_ID, DATA0_PID, DebugPtr); if (Status != EFI_SUCCESS) { return EFI_TIMEOUT; } // // Data stage // if (Request->Length != 0) { Status = UsbDebugPortIN (Buffer, &DataLength, INPUT_TOKEN_ID, DATA1_PID, DebugPtr); if (Status != EFI_SUCCESS) { return EFI_TIMEOUT; } } // // State stage // if ((Request->RequestType & 0x80) == 0) { // // This Control Transfer is Set, So its State stage is IN // Status = UsbDebugPortIN (Buffer, &DataLength, INPUT_TOKEN_ID, DATA1_PID, DebugPtr); } else { // // This Control Transfer is Get, So its State stage is OUT // Status = UsbDebugPortOut (Buffer, 0, OUTPUT_TOKEN_ID, DATA1_PID, DebugPtr); // // Return error if returned length is zero in data stage. // if (DataLength == 0) { return EFI_DEVICE_ERROR; } } if (Status != EFI_SUCCESS) { return EFI_TIMEOUT; } return EFI_SUCCESS; } /** UsbIoWrite. @param [in, out] BufferSize @param [in] Buffer @retval EFI_SUCCESS **/ EFI_STATUS DebugUsbWrite( IN OUT UINTN *BufferSize, IN VOID *Buffer ) { EFI_STATUS Status; INTN StringLength; UINTN NumBytesToSend; UINTN Count0; UINT8 *Ptr; DEBUG_BULK Bulk; EFI_USB_DBG_REG *DebugPtr; UINT32 PciIndexSave; Status = EFI_SUCCESS; // // Save PCI index // PciIndexSave = IoRead32 (0xcf8); DebugPtr = GetDebugPointer (); if (DebugPtr == NULL) { return EFI_DEVICE_ERROR; } // // Because Dxe Core will disable EHCI controller, so we should check it // if ((DebugPtr->Data0 == 0xFFFFFFFF) && (DebugPtr->Data1 == 0xFFFFFFFF)) { EnableEhci (); } // // Restore PCI index // IoWrite32 (0xcf8, PciIndexSave); // // If Usb Debug Port not been enable, don't send // if (DebugPtr->Sc.Enabled != 1) { return EFI_DEVICE_ERROR; } if ((DebugPtr->Sc.Error !=0) && (DebugPtr->Sc.Exception != 0)) { return EFI_DEVICE_ERROR; } // // Load Variable to Data Buffer // *(UINT32*)&Bulk = DebugPtr->Data0; // // Calculation DataLength // Ptr = Buffer; StringLength = *BufferSize; // // Set Device and Endpoint Address // DebugPtr->Addr.Address = DEBUG_DEVICE_ADDR; for (Count0 = 0; Count0 < PEI_TIMEOUT_TICKS; Count0 ++) { DebugPtr->Addr.Endpoint = Bulk.EndpointOut; NumBytesToSend = (StringLength < 8) ? StringLength : 8; Status = UsbDebugPortOut (Ptr, NumBytesToSend, OUTPUT_TOKEN_ID, DataPID[Bulk.ToggleOut], DebugPtr); if (Status == EFI_SUCCESS) { Bulk.ToggleOut ^= 1; if (StringLength <= (INTN)NumBytesToSend) goto Finish; StringLength -= NumBytesToSend; Ptr += NumBytesToSend; Count0 = 0; DebugPtr->Addr.Endpoint = Bulk.EndpointIn; } else if (Status != EFI_TIMEOUT) { if ((DebugPtr->Sc.Error !=0) && (DebugPtr->Sc.Exception != 0)) { return EFI_DEVICE_ERROR; } MicroSecondDelay (1000); } } if (Count0 == PEI_TIMEOUT_TICKS) { Status = EFI_TIMEOUT; } Finish: // // Store Variable to Data Buffer // DebugPtr->Data0 = *(UINT32*)&Bulk; return Status; } /** Prints a string to the USB port @param [in] OutputString Ascii string to print to serial port. @retval None **/ VOID EFIAPI UsbDebugPrint ( IN CHAR8 *OutputString ) { EFI_STATUS Status; UINTN StringLength; UINT8 TempBuffer[8]; Status = EFI_SUCCESS; StringLength = 0; ZeroMem (TempBuffer, 8); for (; *OutputString != 0; OutputString++) { TempBuffer[StringLength] = *OutputString; StringLength++; if (StringLength == 8){ DebugUsbWrite (&StringLength, TempBuffer); ZeroMem (TempBuffer, 8); MicroSecondDelay (1000); StringLength = 0; } } DebugUsbWrite (&StringLength, TempBuffer); }