alder_lake_bios/Insyde/InsydeModulePkg/Universal/Acpi/AcpiPlatformDxe/SpcrUpdateCommon.c

756 lines
19 KiB
C

/** @file
;******************************************************************************
;* Copyright (c) 2012 - 2021, Insyde Software Corp. 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 <PiDxe.h>
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/DebugLib.h>
#include <Library/PcdLib.h>
#include <Library/DevicePathLib.h>
#include <Library/H2OCpLib.h>
#include <Library/KernelConfigLib.h>
#include <Guid/H2OBdsCheckPoint.h>
#include <Guid/ConsoleOutDevice.h>
#include <Guid/PcAnsi.h>
#include <Protocol/AcpiSupport.h>
#include <Protocol/SerialIo.h>
#include <Protocol/DevicePath.h>
#include <Protocol/IsaIo.h>
#include <Protocol/PciIo.h>
#include <IndustryStandard/Acpi.h>
#include <IndustryStandard/SerialPortConsoleRedirectionTable.h>
#include <IndustryStandard/Pci22.h>
#include <KernelSetupConfig.h>
#define EISA_SERIAL_DEVICE_ID 0x0501
//
// ACPI table information used to initialize tables.
//
#define ACPI_SPCR_ENABLE 1
#define SPCR_DEVICE_ISA 1
#define SPCR_DEVICE_PCI 2
#define SPCR_DEVICE_NONE 128
#define SPCR_VT100_TYPE 0
#define SPCR_VT100PLUS_TYPE 1
#define SPCR_VTUTF8_TYPE 2
#define SPCR_PCANSI_TYPE 3
#define PREFER_SPCR_DEVICE_TYPE SPCR_DEVICE_ISA
typedef struct {
UINT8 DeviceType;
UINT8 TerminalType;
UINT8 FlowControl;
EFI_SERIAL_IO_MODE *Mode;
UINT32 IoAddress;
UINT32 IrqNum;
UINTN PciSegment;
UINTN PciBus;
UINTN PciDevice;
UINTN PciFunction;
PCI_TYPE00 PciHeader;
} SPCR_SERIAL_PARAMETER;
EFI_ACPI_SUPPORT_PROTOCOL *gAcpiSupport = NULL;
/**
Update SPCR table content
@param VOID
@return EFI_SUCCESS Update table success
@return EFI_NOT_FOUND Not found the table
**/
EFI_STATUS
GetSpcrTable (
OUT EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE **Table,
OUT EFI_ACPI_TABLE_VERSION *Version,
OUT UINTN *TableKey
)
{
EFI_STATUS Status;
INTN Index;
EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE *SpcrTable;
if (Table == NULL || Version == NULL || TableKey == NULL) {
ASSERT (FALSE);
return EFI_INVALID_PARAMETER;
}
//
// Search SPCR table
//
Index = 0;
while (1) {
Status = gAcpiSupport->GetAcpiTable (gAcpiSupport, Index, (VOID **)&SpcrTable, Version, TableKey);
if (EFI_ERROR(Status)) {
return Status;
}
//
// Check Signture and update SPCR table
//
if (SpcrTable->Header.Signature == EFI_ACPI_3_0_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_SIGNATURE) {
*Table = SpcrTable;
return EFI_SUCCESS;
}
//
// Not found, get next
//
FreePool (SpcrTable);
SpcrTable = NULL;
Index++;
}
}
/**
Update SPCR table content
@param [in] SerialParameter The serial parameter
@return EFI_SUCCESS Update table success
@return EFI_NOT_FOUND Not found the table
**/
EFI_STATUS
UpdateSpcrTable (
IN SPCR_SERIAL_PARAMETER *SerialParameter
)
{
UINT8 BaudRateValue;
//- EFI_ACPI_SUPPORT_PROTOCOL *AcpiSupport;
EFI_STATUS Status;
//- INTN Index;
UINTN TableKey;
EFI_ACPI_TABLE_VERSION Version;
EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE *Table;
Status = GetSpcrTable (&Table, &Version, &TableKey);
if (EFI_ERROR(Status)) {
return Status;
}
if (SerialParameter->DeviceType == SPCR_DEVICE_ISA) {
//
// isa serial device
//
Table->BaseAddress.Address = SerialParameter->IoAddress;
Table->Irq = (UINT8) SerialParameter->IrqNum;
} else if (SerialParameter->DeviceType == SPCR_DEVICE_PCI) {
//
// pci serial device
//
Table->PciVendorId = SerialParameter->PciHeader.Hdr.VendorId;
Table->PciDeviceId = SerialParameter->PciHeader.Hdr.DeviceId;
Table->PciSegment = (UINT8) SerialParameter->PciSegment;
Table->PciBusNumber = (UINT8) SerialParameter->PciBus;
Table->PciDeviceNumber = (UINT8) SerialParameter->PciDevice;
Table->PciFunctionNumber = (UINT8) SerialParameter->PciFunction;
Table->PciFlags = 0x01;
} else {
return EFI_UNSUPPORTED;
}
switch (SerialParameter->Mode->BaudRate) {
case 115200 :
BaudRateValue = 7;
break;
case 57600 :
BaudRateValue = 6;
break;
case 19200 :
BaudRateValue = 4;
break;
case 9200 :
BaudRateValue = 3;
break;
default :
ASSERT (FALSE);
BaudRateValue = 7;
}
Table->BaudRate = BaudRateValue;
Table->Parity = (UINT8) SerialParameter->Mode->Parity;
if (Table->Parity != NoParity) {
ASSERT(FALSE);
}
Table->Parity = 0;
Table->StopBits = (UINT8) SerialParameter->Mode->StopBits;
if (Table->StopBits != 1) {
ASSERT(FALSE);
Table->StopBits = 1;
}
Table->TerminalType = SerialParameter->TerminalType;
//Table->FlowControl = SerialParameter->FlowControl;
Status = gAcpiSupport->SetAcpiTable (gAcpiSupport, Table, FALSE, Version, &TableKey);
return Status;
}
VOID
TruncateLastNode (
IN OUT EFI_DEVICE_PATH_PROTOCOL *DevicePath
)
{
EFI_DEVICE_PATH_PROTOCOL *LastNode;
EFI_DEVICE_PATH_PROTOCOL *DevPath;
LastNode = NULL;
DevPath = DevicePath;
while (!IsDevicePathEnd(DevPath)) {
LastNode = DevPath;
DevPath = NextDevicePathNode (DevPath);
}
if (LastNode != NULL) {
CopyMem( LastNode, DevPath, sizeof(EFI_DEVICE_PATH_PROTOCOL));
}
return;
}
EFI_STATUS
GetParentHandle (
EFI_HANDLE ChildHandle,
EFI_HANDLE *ParentHandle
)
{
EFI_STATUS Status;
EFI_HANDLE Handle;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
EFI_DEVICE_PATH_PROTOCOL *TmpDevPath;
EFI_DEVICE_PATH_PROTOCOL *OrgDevPath;
Status = gBS->HandleProtocol (ChildHandle, &gEfiDevicePathProtocolGuid, (VOID **)&DevicePath);
if (EFI_ERROR(Status)) {
return Status;
}
TmpDevPath = DuplicateDevicePath (DevicePath);
if (TmpDevPath == NULL) {
return EFI_OUT_OF_RESOURCES;
}
OrgDevPath = TmpDevPath;
TruncateLastNode (TmpDevPath);
Status = gBS->LocateDevicePath (&gEfiDevicePathProtocolGuid, &TmpDevPath, &Handle);
if (EFI_ERROR(Status)) {
goto Exit;
}
*ParentHandle = Handle;
Exit:
FreePool (OrgDevPath);
return Status;
}
/**
Check whether a give device path is an ISA serial device
@param [in] DevicePath The device path to be processed
@return TRUE The device path is an ISA serial device
@retval FALSE The device path is not an ISA serial device
**/
BOOLEAN
IsIsaSerialDevice (
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
)
{
ACPI_HID_DEVICE_PATH *AcpiDevPath;
if (DevicePath == NULL)
return FALSE;
while (!IsDevicePathEnd(DevicePath)) {
if ((DevicePathType(DevicePath) == ACPI_DEVICE_PATH)&&(DevicePathSubType(DevicePath) == ACPI_DP)) {
AcpiDevPath = (ACPI_HID_DEVICE_PATH *) DevicePath;
if (AcpiDevPath->HID == EISA_PNP_ID(EISA_SERIAL_DEVICE_ID)) {
return TRUE;
}
}
DevicePath = NextDevicePathNode(DevicePath);
}
return FALSE;
}
/**
Check whether a give device path is a PCI serial device
@param [in] DevicePath The device path to be processed
@return TRUE The device path is a PCI serial device
@retval FALSE The device path is not a PCI serial device
**/
BOOLEAN
IsPciSerialDevice (
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
)
{
EFI_DEVICE_PATH_PROTOCOL *NextNode;
if (DevicePath == NULL)
return FALSE;
while (!IsDevicePathEnd(DevicePath)) {
if ((DevicePathType(DevicePath) == HARDWARE_DEVICE_PATH) && (DevicePathSubType(DevicePath) == HW_PCI_DP)) {
NextNode = NextDevicePathNode(DevicePath);
if ((DevicePathType(NextNode) == MESSAGING_DEVICE_PATH) && (DevicePathSubType(NextNode) == MSG_UART_DP)) {
return TRUE;
}
}
DevicePath = NextDevicePathNode (DevicePath);
}
return FALSE;
}
/**
Check whether a give Handle is a platform console out device
@param [in] Handle The handle to be processed
@return TRUE The handle is a platform console out device
@retval FALSE The handle is not a platform console out device
**/
BOOLEAN
IsPlatformConOutDevice (
EFI_HANDLE Handle
)
{
EFI_STATUS Status;
EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *EntryBuffer;
UINTN EntryCount;
UINTN Index;
VOID *Interface;
Status = gBS->OpenProtocolInformation (
Handle,
&gEfiSerialIoProtocolGuid,
&EntryBuffer,
&EntryCount
);
if (EFI_ERROR(Status)) {
return FALSE;
}
for (Index=0; Index< EntryCount; Index++) {
if ((EntryBuffer[Index].Attributes & EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) != 0) {
Status = gBS->HandleProtocol ( EntryBuffer[Index].ControllerHandle, &gEfiConsoleOutDeviceGuid, &Interface);
if (!EFI_ERROR(Status)) {
FreePool(EntryBuffer);
return TRUE;
}
}
}
FreePool(EntryBuffer);
return FALSE;
}
EFI_STATUS
SpcrLocateSerialHandle (
OUT EFI_HANDLE *Handle
)
{
EFI_STATUS Status;
UINTN NumHandles;
EFI_HANDLE *HandleBuffer;
UINTN Index;
EFI_DEVICE_PATH_PROTOCOL *DevPath;
UINT16 *Priority;
UINT16 SelectedValue;
UINTN SelectedIndex;
UINT16 IsaDevWeighted;
UINT16 PciDevWeighted;
UINT8 SpcrDeviceIsa;
if (Handle == NULL) {
return EFI_INVALID_PARAMETER;
}
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiSerialIoProtocolGuid,
NULL,
&NumHandles,
&HandleBuffer
);
if (EFI_ERROR(Status)) {
return Status;
}
//
// Initialize variable
//
IsaDevWeighted = BIT2;
PciDevWeighted = BIT2;
SpcrDeviceIsa = SPCR_DEVICE_ISA;
if (PREFER_SPCR_DEVICE_TYPE == SpcrDeviceIsa) {
IsaDevWeighted = BIT3;
} else {
PciDevWeighted = BIT3;
}
Priority = AllocateZeroPool(sizeof(UINT16) * NumHandles);
if (Priority == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// Evaluate priority of every serial handle.
// The priority is as list :
// 1. Console device
// 2. Isa device
// 3. Pci device
//
for (Index = 0; Index < NumHandles; Index++) {
// if that is platform conout device add weighted
if (IsPlatformConOutDevice(HandleBuffer[Index])) {
Priority[Index] = BIT7;
}
// SPCR serial device must be physical controller
Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiDevicePathProtocolGuid, &DevPath);
if (EFI_ERROR(Status)) {
continue;
}
// add weighted by device type
if (IsIsaSerialDevice (DevPath)) {
Priority[Index] = IsaDevWeighted;
} else if (IsPciSerialDevice(DevPath)){
Priority[Index] = PciDevWeighted;
} else {
Priority[Index] = 0;
}
}
SelectedIndex = 0;
SelectedValue = 0;
for (Index = 0; Index < NumHandles; Index++) {
if (Priority[Index] > SelectedValue) {
SelectedValue = Priority[Index];
SelectedIndex = Index;
}
}
if (SelectedValue == 0) {
return EFI_NOT_FOUND;
}
*Handle = HandleBuffer[SelectedIndex];
return EFI_SUCCESS;
}
UINT8
GetSerialTerminalType (
EFI_HANDLE Handle
)
{
EFI_STATUS Status;
EFI_SERIAL_IO_PROTOCOL *SerialIo;
EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *EntryBuffer;
UINTN EntryCount;
UINTN Index;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
VENDOR_DEVICE_PATH *Node;
UINT8 TerminalType;
TerminalType = SPCR_VT100_TYPE;
Status = gBS->HandleProtocol (Handle, &gEfiSerialIoProtocolGuid, &SerialIo);
ASSERT_EFI_ERROR(Status);
Status = gBS->OpenProtocolInformation (
Handle,
&gEfiSerialIoProtocolGuid,
&EntryBuffer,
&EntryCount
);
if (EFI_ERROR(Status)) {
ASSERT (FALSE);
return SPCR_VT100_TYPE;
}
for (Index = 0; Index < EntryCount; Index++) {
if ((EntryBuffer[Index].Attributes & EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) == 0) {
continue;
}
Status = gBS->HandleProtocol (EntryBuffer[Index].ControllerHandle, &gEfiDevicePathProtocolGuid, &DevicePath);
if (EFI_ERROR(Status)) {
ASSERT (FALSE);
continue;
}
while (!IsDevicePathEnd(DevicePath)) {
if ((DevicePathType(DevicePath) == MESSAGING_DEVICE_PATH) && (DevicePathSubType(DevicePath) == MSG_VENDOR_DP)) {
Node = (VENDOR_DEVICE_PATH *)DevicePath;
if ( CompareGuid (&Node->Guid, &gEfiVT100Guid)) {
TerminalType = SPCR_VT100_TYPE;
} else if (CompareGuid (&Node->Guid, &gEfiVT100PlusGuid)) {
TerminalType = SPCR_VT100PLUS_TYPE;
} else if (CompareGuid (&Node->Guid, &gEfiVTUTF8Guid)) {
TerminalType = SPCR_VTUTF8_TYPE;
} else if (CompareGuid (&Node->Guid, &gEfiPcAnsiGuid)) {
TerminalType = SPCR_PCANSI_TYPE;
} else {
ASSERT(FALSE);
TerminalType = SPCR_VT100_TYPE;
}
goto Exit;
}
DevicePath = NextDevicePathNode(DevicePath);
}
}
Exit:
FreePool (EntryBuffer);
return TerminalType;
}
EFI_STATUS
RetriveSerialParameter (
IN EFI_HANDLE SerialHandle,
OUT SPCR_SERIAL_PARAMETER *SerialParameter
)
{
EFI_STATUS Status;
EFI_SERIAL_IO_PROTOCOL *SerialIo;
EFI_HANDLE ParentHandle;
EFI_ISA_IO_PROTOCOL *IsaIo;
EFI_PCI_IO_PROTOCOL *PciIo;
EFI_ISA_ACPI_RESOURCE *IsaResource;
UINTN Segment;
UINTN Bus;
UINTN Device;
UINTN Function;
Status = gBS->HandleProtocol ( SerialHandle, &gEfiSerialIoProtocolGuid, &SerialIo);
ASSERT_EFI_ERROR(Status);
if (EFI_ERROR(Status)) {
return Status;
}
SerialParameter->TerminalType = GetSerialTerminalType (SerialHandle);
SerialParameter->Mode = SerialIo->Mode;
Status = GetParentHandle (SerialHandle, &ParentHandle);
ASSERT_EFI_ERROR(Status);
if (EFI_ERROR(Status)) {
return Status;
}
if (!EFI_ERROR(gBS->HandleProtocol (ParentHandle, &gEfiIsaIoProtocolGuid, &IsaIo))) {
//
// Isa serial device
//
SerialParameter->DeviceType = SPCR_DEVICE_ISA;
IsaResource = IsaIo->ResourceList->ResourceItem;
while (IsaResource->Type != EfiIsaAcpiResourceEndOfList) {
switch (IsaResource->Type) {
case EfiIsaAcpiResourceIo :
SerialParameter->IoAddress = IsaResource->StartRange;
break;
case EfiIsaAcpiResourceInterrupt :
SerialParameter->IrqNum = IsaResource->StartRange;
break;
}
IsaResource++;
}
return EFI_SUCCESS;
} else if (!EFI_ERROR(gBS->HandleProtocol (ParentHandle, &gEfiPciIoProtocolGuid, &PciIo))) {
//
// Pci Serial device
//
SerialParameter->DeviceType = SPCR_DEVICE_PCI;
Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint8, 0, sizeof (PCI_TYPE00), &SerialParameter->PciHeader);
if (EFI_ERROR(Status)) {
return Status;
}
Status = PciIo->GetLocation (PciIo, &Segment, &Bus, &Device, &Function);
if (EFI_ERROR(Status)) {
return Status;
}
SerialParameter->PciSegment = Segment;
SerialParameter->PciBus = Bus;
SerialParameter->PciDevice = Device;
SerialParameter->PciFunction = Function;
return EFI_SUCCESS;
} else {
//
// Unknow device
//
ASSERT(FALSE);
return EFI_UNSUPPORTED;
}
}
EFI_STATUS
UnInstallSpcr (
VOID
)
{
EFI_STATUS Status;
EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE *Table;
EFI_ACPI_TABLE_VERSION Version;
UINTN TableKey;
Status = GetSpcrTable (&Table, &Version, &TableKey);
if (EFI_ERROR(Status)) {
return EFI_SUCCESS;
}
Status = gAcpiSupport->SetAcpiTable ( gAcpiSupport, NULL, FALSE, Version, &TableKey);
return EFI_SUCCESS;
}
/**
Console Redirection Callback
@param Event
@param *Context
@return VOID
**/
VOID
EFIAPI
AfterConnectAllCp (
IN EFI_EVENT Event,
IN H2O_CP_HANDLE CpHandle
)
{
EFI_STATUS Status;
EFI_HANDLE Handle;
SPCR_SERIAL_PARAMETER SerialParameter;
// locate the primary serial device handle
Status = SpcrLocateSerialHandle (&Handle);
if (EFI_ERROR(Status)) {
UnInstallSpcr ();
return;
}
// Retrive serial parameter
Status = RetriveSerialParameter (Handle, &SerialParameter);
ASSERT_EFI_ERROR(Status);
if (EFI_ERROR(Status)) {
UnInstallSpcr ();
return;
}
// Use serial parameter to modify SPCR table
Status = UpdateSpcrTable (&SerialParameter);
ASSERT_EFI_ERROR(Status);
return;
}
/**
Update SPCR table content
Depends on Setup Variable to update SPCR table.
@param Table The table to update
@param SetupVariable SETUP Variable pointer
@return EFI_SUCCESS Update table success
**/
EFI_STATUS
SpcrUpdateCommon (
IN OUT EFI_ACPI_COMMON_HEADER *Table
)
{
EFI_STATUS Status;
EFI_HANDLE CpHandle;
KERNEL_CONFIGURATION SetupVariable;
Status = GetKernelConfiguration (&SetupVariable);
if (EFI_ERROR (Status)) {
SetupVariable.ACPISpcr = 0;
}
if (SetupVariable.ACPISpcr == ACPI_SPCR_ENABLE) {
Status = gBS->LocateProtocol (&gEfiAcpiSupportProtocolGuid, NULL, &gAcpiSupport);
if (EFI_ERROR(Status)) {
return Status;
}
if (FeaturePcdGet (PcdH2OBdsCpConnectAllAfterSupported)) {
Status = H2OCpRegisterHandler (
&gH2OBdsCpConnectAllAfterGuid,
AfterConnectAllCp,
H2O_CP_MEDIUM,
&CpHandle
);
if (EFI_ERROR (Status)) {
DEBUG_CP ((DEBUG_ERROR, "Checkpoint Register Fail: %g (%r)\n", &gH2OBdsCpConnectAllAfterGuid, Status));
return Status;
}
DEBUG_CP ((DEBUG_INFO, "Checkpoint Registered: %g (%r)\n", &gH2OBdsCpConnectAllAfterGuid, Status));
}
} else {
Status = EFI_UNSUPPORTED;
}
return Status;
}