mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-11-23 11:35:19 +01:00
cd23181296
Signed-off-by: SergeySlice <sergey.slice@gmail.com>
606 lines
11 KiB
C
606 lines
11 KiB
C
/** @file
|
|
Copyright (C) 2016 - 2017, The HermitCrabs Lab. All rights reserved.
|
|
|
|
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 <Uefi.h>
|
|
|
|
#include <IndustryStandard/Pci.h>
|
|
|
|
#include <Protocol/PciIo.h>
|
|
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/DebugLib.h>
|
|
#include <Library/IoLib.h>
|
|
#include <Library/UefiBootServicesTableLib.h>
|
|
|
|
#define XHC_HCCPARAMS_OFFSET 0x10
|
|
#define XHC_NEXT_CAPABILITY_MASK 0xFF00
|
|
#define XHC_CAPABILITY_ID_MASK 0xFF
|
|
#define XHC_USBCMD_OFFSET 0x0 ///< USB Command Register Offset
|
|
#define XHC_USBSTS_OFFSET 0x4 ///< USB Status Register Offset
|
|
#define XHC_POLL_DELAY 1000
|
|
|
|
#define EHC_BAR_INDEX 0x0
|
|
#define EHC_HCCPARAMS_OFFSET 0x8
|
|
#define EHC_USBCMD_OFFSET 0x0 ///< USB Command Register Offset
|
|
#define EHC_USBSTS_OFFSET 0x4 ///< USB Status Register Offset
|
|
#define EHC_USBINT_OFFSET 0x8 ///< USB Interrupt Enable Register
|
|
|
|
/**
|
|
Release XHCI USB controllers ownership.
|
|
|
|
@param[in] PciIo PCI I/O protocol for the device.
|
|
|
|
@retval EFI_NOT_FOUND No XHCI controllers had ownership incorrectly set.
|
|
@retval EFI_SUCCESS Corrected XHCI controllers ownership to OS.
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
XhciReleaseOwnership (
|
|
IN EFI_PCI_IO_PROTOCOL *PciIo
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
UINT32 HcCapParams;
|
|
UINT32 ExtendCap;
|
|
UINT32 Value;
|
|
INT32 TimeOut;
|
|
|
|
//
|
|
// XHCI controller, then disable legacy support, if enabled.
|
|
//
|
|
|
|
Status = PciIo->Mem.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint32,
|
|
XHC_USBCMD_OFFSET,
|
|
XHC_HCCPARAMS_OFFSET,
|
|
1,
|
|
&HcCapParams
|
|
);
|
|
|
|
ExtendCap = EFI_ERROR(Status) ? 0 : ((HcCapParams >> 14U) & 0x3FFFCU);
|
|
|
|
while (ExtendCap) {
|
|
Status = PciIo->Mem.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint32,
|
|
XHC_USBCMD_OFFSET,
|
|
ExtendCap,
|
|
1,
|
|
&Value
|
|
);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
break;
|
|
}
|
|
|
|
if ((Value & XHC_CAPABILITY_ID_MASK) == 1) {
|
|
//
|
|
// Do nothing if BIOS ownership is cleared.
|
|
//
|
|
if (!(Value & BIT16)) {
|
|
break;
|
|
}
|
|
|
|
Value |= BIT24;
|
|
|
|
PciIo->Mem.Write (
|
|
PciIo,
|
|
EfiPciIoWidthUint32,
|
|
XHC_USBCMD_OFFSET,
|
|
ExtendCap,
|
|
1,
|
|
&Value
|
|
);
|
|
|
|
TimeOut = 40;
|
|
while (TimeOut--) {
|
|
gBS->Stall (500);
|
|
|
|
Status = PciIo->Mem.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint32,
|
|
XHC_USBCMD_OFFSET,
|
|
ExtendCap,
|
|
1,
|
|
&Value
|
|
);
|
|
|
|
if (EFI_ERROR(Status) || !(Value & BIT16)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Disable all SMI in USBLEGCTLSTS
|
|
//
|
|
Status = PciIo->Mem.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint32,
|
|
XHC_USBCMD_OFFSET,
|
|
ExtendCap + 4,
|
|
1,
|
|
&Value
|
|
);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
break;
|
|
}
|
|
|
|
Value &= 0x1F1FEEU;
|
|
Value |= 0xE0000000U;
|
|
|
|
PciIo->Mem.Write (
|
|
PciIo,
|
|
EfiPciIoWidthUint32,
|
|
XHC_USBCMD_OFFSET,
|
|
ExtendCap + 4,
|
|
1,
|
|
&Value
|
|
);
|
|
|
|
//
|
|
// Clear all ownership
|
|
//
|
|
Status = PciIo->Mem.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint32,
|
|
XHC_USBCMD_OFFSET,
|
|
ExtendCap,
|
|
1,
|
|
&Value
|
|
);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
break;
|
|
}
|
|
|
|
Value &= ~(BIT24 | BIT16);
|
|
PciIo->Mem.Write (
|
|
PciIo,
|
|
EfiPciIoWidthUint32,
|
|
XHC_USBCMD_OFFSET,
|
|
ExtendCap,
|
|
1,
|
|
&Value
|
|
);
|
|
|
|
break;
|
|
}
|
|
|
|
if (!(Value & XHC_NEXT_CAPABILITY_MASK)) {
|
|
break;
|
|
}
|
|
|
|
ExtendCap += ((Value >> 6U) & 0x3FCU);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Release EHCI USB controllers ownership.
|
|
|
|
@param[in] PciIo PCI I/O protocol for the device.
|
|
|
|
@retval EFI_NOT_FOUND No EHCI controllers had ownership incorrectly set.
|
|
@retval EFI_SUCCESS Corrected EHCI controllers ownership to OS.
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
EhciReleaseOwnership (
|
|
IN EFI_PCI_IO_PROTOCOL *PciIo
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT32 Value;
|
|
UINT32 Base;
|
|
UINT32 OpAddr;
|
|
UINT32 ExtendCap;
|
|
UINT32 UsbCmd;
|
|
UINT32 UsbLegSup;
|
|
UINT32 UsbLegCtlSts;
|
|
UINTN IsOsOwned;
|
|
UINTN IsBiosOwned;
|
|
BOOLEAN IsOwnershipConflict;
|
|
UINT32 HcCapParams;
|
|
INT32 TimeOut;
|
|
|
|
Value = 0x0002;
|
|
|
|
PciIo->Pci.Write (
|
|
PciIo,
|
|
EfiPciIoWidthUint16,
|
|
0x04,
|
|
1,
|
|
&Value
|
|
);
|
|
|
|
Base = 0;
|
|
|
|
Status = PciIo->Pci.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint32,
|
|
0x10,
|
|
1,
|
|
&Base
|
|
);
|
|
|
|
if (MmioRead8 (Base) < 0x0C) {
|
|
//
|
|
// Config space too small: no legacy implementation.
|
|
//
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
//
|
|
// Operational Registers = capaddr + offset (8bit CAPLENGTH in Capability Registers + offset 0).
|
|
//
|
|
OpAddr = Base + MmioRead8 (Base);
|
|
|
|
Status = PciIo->Mem.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint32,
|
|
EHC_BAR_INDEX,
|
|
EHC_HCCPARAMS_OFFSET,
|
|
1,
|
|
&HcCapParams
|
|
);
|
|
|
|
ExtendCap = (HcCapParams >> 8U) & 0xFFU;
|
|
|
|
//
|
|
// Read PCI Config 32bit USBLEGSUP (eecp+0).
|
|
//
|
|
Status = PciIo->Pci.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint32,
|
|
ExtendCap,
|
|
1,
|
|
&UsbLegSup
|
|
);
|
|
|
|
IsBiosOwned = (UsbLegSup & BIT16) != 0;
|
|
if (!IsBiosOwned) {
|
|
//
|
|
// No BIOS ownership, ignore.
|
|
//
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
//
|
|
// Read PCI Config 32bit USBLEGCTLSTS (eecp+4).
|
|
//
|
|
PciIo->Pci.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint32,
|
|
ExtendCap + 0x4,
|
|
1,
|
|
&UsbLegCtlSts
|
|
);
|
|
|
|
//
|
|
// Disable the SMI in USBLEGCTLSTS firstly.
|
|
//
|
|
UsbLegCtlSts &= 0xFFFF0000U;
|
|
PciIo->Pci.Write (
|
|
PciIo,
|
|
EfiPciIoWidthUint32,
|
|
ExtendCap + 0x4,
|
|
1,
|
|
&UsbLegCtlSts
|
|
);
|
|
|
|
UsbCmd = MmioRead32 (OpAddr + EHC_USBCMD_OFFSET);
|
|
|
|
//
|
|
// Clear registers to default.
|
|
//
|
|
UsbCmd = UsbCmd & 0xFFFFFF00U;
|
|
MmioWrite32 (OpAddr + EHC_USBCMD_OFFSET, UsbCmd);
|
|
MmioWrite32 (OpAddr + EHC_USBINT_OFFSET, 0);
|
|
MmioWrite32 (OpAddr + EHC_USBSTS_OFFSET, 0x1000);
|
|
|
|
Value = 1;
|
|
PciIo->Pci.Write (
|
|
PciIo,
|
|
EfiPciIoWidthUint32,
|
|
ExtendCap,
|
|
1,
|
|
&Value
|
|
);
|
|
|
|
//
|
|
// Read 32bit USBLEGSUP (eecp+0).
|
|
//
|
|
PciIo->Pci.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint32,
|
|
ExtendCap,
|
|
1,
|
|
&UsbLegSup
|
|
);
|
|
|
|
IsBiosOwned = (UsbLegSup & BIT16) != 0;
|
|
IsOsOwned = (UsbLegSup & BIT24) != 0;
|
|
|
|
//
|
|
// Read 32bit USBLEGCTLSTS (eecp+4).
|
|
//
|
|
PciIo->Pci.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint32,
|
|
ExtendCap + 0x4,
|
|
1,
|
|
&UsbLegCtlSts
|
|
);
|
|
|
|
//
|
|
// Get EHCI Ownership from legacy bios.
|
|
//
|
|
PciIo->Pci.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint32,
|
|
ExtendCap,
|
|
1,
|
|
&UsbLegSup
|
|
);
|
|
|
|
IsOwnershipConflict = IsBiosOwned && IsOsOwned;
|
|
|
|
if (IsOwnershipConflict) {
|
|
//
|
|
// EHCI - Ownership conflict - attempting soft reset.
|
|
//
|
|
Value = 0;
|
|
PciIo->Pci.Write (
|
|
PciIo,
|
|
EfiPciIoWidthUint8,
|
|
ExtendCap + 3,
|
|
1,
|
|
&Value
|
|
);
|
|
|
|
TimeOut = 40;
|
|
while (TimeOut--) {
|
|
gBS->Stall (500);
|
|
|
|
PciIo->Pci.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint32,
|
|
ExtendCap,
|
|
1,
|
|
&Value
|
|
);
|
|
|
|
if ((Value & BIT24) == 0x0) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
PciIo->Pci.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint32,
|
|
ExtendCap,
|
|
1,
|
|
&Value
|
|
);
|
|
|
|
Value |= BIT24;
|
|
PciIo->Pci.Write (
|
|
PciIo,
|
|
EfiPciIoWidthUint32,
|
|
ExtendCap,
|
|
1,
|
|
&Value
|
|
);
|
|
|
|
TimeOut = 40;
|
|
while (TimeOut--) {
|
|
gBS->Stall (500);
|
|
|
|
PciIo->Pci.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint32,
|
|
ExtendCap,
|
|
1,
|
|
&Value
|
|
);
|
|
|
|
if ((Value & BIT16) == 0x0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
IsOwnershipConflict = (Value & BIT16) != 0x0;
|
|
if (IsOwnershipConflict) {
|
|
//
|
|
// Soft reset has failed. Assume SMI being ignored and do hard reset.
|
|
//
|
|
Value = 0;
|
|
PciIo->Pci.Write (
|
|
PciIo,
|
|
EfiPciIoWidthUint8,
|
|
ExtendCap + 2,
|
|
1,
|
|
&Value
|
|
);
|
|
|
|
TimeOut = 40;
|
|
while (TimeOut--) {
|
|
gBS->Stall (500);
|
|
|
|
PciIo->Pci.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint32,
|
|
ExtendCap,
|
|
1,
|
|
&Value
|
|
);
|
|
|
|
if ((Value & BIT16) == 0x0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Disable further SMI events.
|
|
//
|
|
PciIo->Pci.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint32,
|
|
ExtendCap + 0x4,
|
|
1,
|
|
&UsbLegCtlSts
|
|
);
|
|
|
|
UsbLegCtlSts &= 0xFFFF0000U;
|
|
PciIo->Pci.Write (
|
|
PciIo,
|
|
EfiPciIoWidthUint32,
|
|
ExtendCap + 0x4,
|
|
1,
|
|
&UsbLegCtlSts
|
|
);
|
|
}
|
|
|
|
if (Value & BIT16) {
|
|
//
|
|
// EHCI controller unable to take control from BIOS.
|
|
//
|
|
Status = EFI_NOT_FOUND;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Release UHCI USB controllers ownership.
|
|
|
|
@param[in] PciIo PCI I/O protocol for the device.
|
|
|
|
@retval EFI_NOT_FOUND No UHCI controllers had ownership incorrectly set.
|
|
@retval EFI_SUCCESS Corrected UHCI controllers ownership to OS.
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
UhciReleaseOwnership (
|
|
IN EFI_PCI_IO_PROTOCOL *PciIo
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT32 Base;
|
|
UINT32 PortBase;
|
|
UINT16 Command;
|
|
|
|
Base = 0;
|
|
|
|
Status = PciIo->Pci.Read(
|
|
PciIo,
|
|
EfiPciIoWidthUint32,
|
|
0x20,
|
|
1,
|
|
&Base
|
|
);
|
|
|
|
PortBase = (Base >> 5) & 0x07ff;
|
|
|
|
Command = 0x8f00;
|
|
|
|
Status = PciIo->Pci.Write (
|
|
PciIo,
|
|
EfiPciIoWidthUint16,
|
|
0xC0,
|
|
1,
|
|
&Command
|
|
);
|
|
|
|
if (PortBase != 0 && (PortBase & BIT0) == 0) {
|
|
IoWrite16 (PortBase, 0x0002);
|
|
gBS->Stall (500);
|
|
IoWrite16 (PortBase + 4, 0);
|
|
gBS->Stall (500);
|
|
IoWrite16 (PortBase, 0);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
ReleaseUsbOwnership (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Result;
|
|
EFI_STATUS Status;
|
|
EFI_HANDLE *HandleArray;
|
|
UINTN HandleArrayCount;
|
|
UINTN Index;
|
|
EFI_PCI_IO_PROTOCOL *PciIo;
|
|
PCI_TYPE00 Pci;
|
|
|
|
Status = gBS->LocateHandleBuffer (
|
|
ByProtocol,
|
|
&gEfiPciIoProtocolGuid,
|
|
NULL,
|
|
&HandleArrayCount,
|
|
&HandleArray
|
|
);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Result = EFI_UNSUPPORTED;
|
|
|
|
for (Index = 0; Index < HandleArrayCount; ++Index) {
|
|
Status = gBS->HandleProtocol (
|
|
HandleArray[Index],
|
|
&gEfiPciIoProtocolGuid,
|
|
(VOID **) &PciIo
|
|
);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
continue;
|
|
}
|
|
|
|
Status = PciIo->Pci.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint32,
|
|
0,
|
|
sizeof (Pci) / sizeof (UINT32),
|
|
&Pci
|
|
);
|
|
|
|
if (EFI_ERROR(Status)
|
|
|| Pci.Hdr.ClassCode[1] != PCI_CLASS_SERIAL_USB
|
|
|| Pci.Hdr.ClassCode[2] != PCI_CLASS_SERIAL) {
|
|
continue;
|
|
}
|
|
|
|
if (Pci.Hdr.ClassCode[0] == PCI_IF_XHCI) {
|
|
Result = XhciReleaseOwnership (PciIo);
|
|
} else if (Pci.Hdr.ClassCode[0] == PCI_IF_EHCI) {
|
|
Result = EhciReleaseOwnership (PciIo);
|
|
} else if (Pci.Hdr.ClassCode[0] == PCI_IF_UHCI) {
|
|
Result = UhciReleaseOwnership (PciIo);
|
|
}
|
|
}
|
|
|
|
gBS->FreePool (HandleArray);
|
|
return Result;
|
|
}
|