/** @file Implementation the driver used to add SPCR device info to ACPI table. SPCR is abbreviation of Serial Port Console Redirection Table (SPCR). Copyright (c) 2004 - 2020, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "SpcrAcpi.h" #define PCANSITYPE 0 #define VT100TYPE 1 #define VT100PLUSTYPE 2 #define VTUTF8TYPE 3 #define PCI_MAX_BAR_NO 6 UINT8 TerminalTypeValue[4] = { PCANSITYPE, VT100TYPE, VT100PLUSTYPE, VTUTF8TYPE }; /// /// Guid for messaging path, used in Serial port setting. /// EFI_GUID TerminalTypeGuid[4] = { DEVICE_PATH_MESSAGING_PC_ANSI, DEVICE_PATH_MESSAGING_VT_100, DEVICE_PATH_MESSAGING_VT_100_PLUS, DEVICE_PATH_MESSAGING_VT_UTF8 }; // // Serial Port Console Redirection Table // Please modify all values in Spcr.h only. // EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE gSpcrInfo = { { EFI_ACPI_2_0_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_SIGNATURE, sizeof (EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE), EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_REVISION, // // Checksum will be updated at runtime // 0x00, // // It is expected that these values will be programmed at runtime // {'I', 'N', 'T', 'E', 'L', ' '}, SIGNATURE_64 ('S', 'P', 'C', 'R', 'T', 'a', 'b', 'l'), 0x00, // Spcr revision, SIGNATURE_32 ('I', 'N', 'T', 'L'), 0x01 }, // // Beginning of SPCR specific fields // 0x00, //INTERFACE_TYPE, { 0x00, //RESERVED_BYTE, 0x00, //RESERVED_BYTE, 0x00 //RESERVED_BYTE, }, // // Base Address Block // { EFI_ACPI_3_0_SYSTEM_IO, 0x08, //BASE_ADDRESS_BIT_WIDTH, 0x00, //BASE_ADDRESS_BIT_OFFSET, 0x00, //RESERVED_BYTE, 0x0000000000000000 //BASE_ADDRESS_ADDRESS, }, 0x03, //INTERRUPT_TYPE, 0x04, //IRQ, 0x04, //GLOBAL_SYSTEM_INTERRUPT, 0x07, //BAUD_RATE, 0x00, //PARITY, 0x01, //STOP_BITS, 0x00, //FLOW_CONTROL, 0x03, //TERMINAL_TYPE, 0x00, //LANGUAGE, 0xFFFF, //PCI_DEVICE_ID, 0xFFFF, //PCI_VENDOR_ID, 0x00, //PCI_BUS_NUMBER, 0x00, //PCI_DEVICE_NUMBER, 0x00, //PCI_FUNCTION_NUMBER, 0x00000000, //PCI_FLAGS, 0x00, //PCI_SEGMENT, EFI_ACPI_RESERVED_DWORD }; /** Install Boot Device Selection Protocol @param ImageHandle The image handle. @param SystemTable The system table. @retval EFI_SUCEESS BDS has finished initializing. Return the dispatcher and recall BDS.Entry @retval Other Return status from AllocatePool() or gBS->InstallProtocolInterface **/ EFI_STATUS EFIAPI SpcrInitialize ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { EFI_STATUS Status; EFI_EVENT ReadyToBootEvent; // // Install protocol interface // Status = EfiCreateEventReadyToBootEx ( TPL_CALLBACK, OutOfBandACPITableConstruction, NULL, &ReadyToBootEvent ); return Status; } /** Check whether the device path node is ISA Serial Node. @param DevicePath Device path node to be checked @retval TRUE It's ISA Serial Node. @retval FALSE It's NOT ISA Serial Node. **/ BOOLEAN HasIsaSerialNode ( IN EFI_DEVICE_PATH_PROTOCOL *DevicePath ) { EFI_DEVICE_PATH_PROTOCOL *Node; ACPI_HID_DEVICE_PATH *Acpi; for (Node = DevicePath; !IsDevicePathEnd (Node); Node = NextDevicePathNode (Node)) { Acpi = (ACPI_HID_DEVICE_PATH *) Node; if ((DevicePathType (Acpi) == ACPI_DEVICE_PATH) && (DevicePathSubType (Acpi) == ACPI_DP) && (ReadUnaligned32 (&Acpi->HID) == EISA_PNP_ID (0x0501))) { return TRUE; } } return FALSE; } /** Get Irq and resource info from isa type console device. @param SavedDevicePath Device path for isa device. **/ VOID GetIsaTypeInfo ( IN EFI_DEVICE_PATH_PROTOCOL *SavedDevicePath ) { EFI_STATUS Status; EFI_HANDLE Handle; ACPI_RESOURCE_HEADER_PTR Resources; EFI_SIO_PROTOCOL *Sio; EFI_ACPI_IRQ_NOFLAG_DESCRIPTOR *Irq; EFI_ACPI_FIXED_LOCATION_IO_PORT_DESCRIPTOR *FixedLocation; // // Try to get IRP and BaseAddress info. // Status = gBS->LocateDevicePath( &gEfiSioProtocolGuid, &SavedDevicePath, &Handle ); if (EFI_ERROR(Status)) { return; } Status = gBS->HandleProtocol ( Handle, &gEfiSioProtocolGuid, (VOID **) &Sio ); if (EFI_ERROR(Status)) { return; } Status = Sio->GetResources (Sio, &Resources); if (EFI_ERROR(Status)) { return; } while (Resources.SmallHeader->Byte != ACPI_END_TAG_DESCRIPTOR) { switch (Resources.SmallHeader->Byte) { case ACPI_IRQ_DESCRIPTOR: case ACPI_IRQ_NOFLAG_DESCRIPTOR: Irq = (EFI_ACPI_IRQ_NOFLAG_DESCRIPTOR *) Resources.SmallHeader; gSpcrInfo.Irq = 0; while (Irq->Mask > 1) { gSpcrInfo.Irq ++; Irq->Mask = Irq->Mask >> 1; } break; case ACPI_FIXED_LOCATION_IO_PORT_DESCRIPTOR: FixedLocation = (EFI_ACPI_FIXED_LOCATION_IO_PORT_DESCRIPTOR *)Resources.SmallHeader; gSpcrInfo.BaseAddress.Address = FixedLocation->BaseAddress; break; default: break; } if (Resources.SmallHeader->Bits.Type == 0) { Resources.SmallHeader = (ACPI_SMALL_RESOURCE_HEADER *) ((UINT8 *) Resources.SmallHeader + Resources.SmallHeader->Bits.Length + sizeof (*Resources.SmallHeader)); } else { Resources.LargeHeader = (ACPI_LARGE_RESOURCE_HEADER *) ((UINT8 *) Resources.LargeHeader + Resources.LargeHeader->Length + sizeof (*Resources.LargeHeader)); } } } /** Get Irq and resource info from Pci type console device. @param SavedDevicePath Device path for PCI device. **/ VOID GetPciTypeInfo ( IN EFI_DEVICE_PATH_PROTOCOL *SavedDevicePath ) { EFI_STATUS Status; EFI_HANDLE PciHandle; EFI_PCI_IO_PROTOCOL *PciIo; UINTN Bus; UINTN Device; UINTN Function; UINTN SegNum; UINTN Irq; UINT16 BaseAddress; UINT8 Count; UINT64 Supports; UINT16 VendorId; UINT16 DeviceId; EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Resources; PciIo = NULL; Resources = NULL; Supports = 0x01; Bus = 0; Device = 0; Function = 0; SegNum = 0; Irq = 0; BaseAddress = 0; VendorId = 0; DeviceId = 0; Status = gBS->LocateDevicePath (&gEfiPciIoProtocolGuid, &SavedDevicePath, (VOID **) &PciHandle); if (EFI_ERROR (Status)) { Status = EFI_NOT_FOUND; return; } Status = gBS->HandleProtocol(PciHandle, &gEfiPciIoProtocolGuid, (VOID **) &PciIo); if (EFI_ERROR (Status) || (PciIo == NULL)) { Status = EFI_NOT_FOUND; return; } PciIo->GetLocation(PciIo, &SegNum, &Bus, &Device, &Function); if (EFI_ERROR (Status)) { Status = EFI_NOT_FOUND; return; } if (Bus == 0x00 && Device == 0x16 && Function == 3) { for (Count = 0; Count < PCI_MAX_BAR_NO; Count++) { Status = PciIo->GetBarAttributes (PciIo, Count, &Supports, (VOID **) &Resources); if (!EFI_ERROR(Status)) { if (Resources->ResType == 1) { // TYPE_IO BaseAddress= (UINT16)Resources->AddrRangeMin; break; } FreePool (Resources); Resources = NULL; } } } Status = PciIo->Pci.Read(PciIo, EfiPciIoWidthUint8, 0x3c, 1, &Irq); if (EFI_ERROR(Status)) { goto Done; } Status = PciIo->Pci.Read(PciIo, EfiPciIoWidthUint16, 0x00, 1, &VendorId); if (EFI_ERROR(Status)) { goto Done; } Status = PciIo->Pci.Read(PciIo, EfiPciIoWidthUint16, 0x02, 1, &DeviceId); if (EFI_ERROR(Status)) { goto Done; } gSpcrInfo.BaseAddress.Address = BaseAddress; gSpcrInfo.Irq = (UINT8) Irq; gSpcrInfo.PciDeviceId = DeviceId; gSpcrInfo.PciVendorId = VendorId; gSpcrInfo.PciBusNumber = (UINT8) Bus; gSpcrInfo.PciDeviceNumber = (UINT8) Device; gSpcrInfo.PciFunctionNumber = (UINT8) Function; gSpcrInfo.PciSegment = (UINT8) SegNum; Done: if (Resources != NULL) { FreePool (Resources); } } /** Installs the Smbios Table to the System Table. This function gets called when the EFI_EVENT_SIGNAL_READY_TO_BOOT gets signaled @param Event The event to signal @param Context Event contex **/ VOID EFIAPI OutOfBandACPITableConstruction ( IN EFI_EVENT Event, IN VOID *Context ) { EFI_DEVICE_PATH_PROTOCOL *SavedDevicePath; EFI_DEVICE_PATH_PROTOCOL *TmpDevicePath; EFI_DEVICE_PATH_PROTOCOL *Tmp2DevicePath; EFI_ACPI_TABLE_PROTOCOL *AcpiTablProtocol; UINTN TurnKey; EFI_STATUS Status; EFI_HANDLE Handle; EFI_SERIAL_IO_PROTOCOL *SerialIo; UINT32 FlowControl; VENDOR_DEVICE_PATH *Vendor; UINT8 Index; Handle = NULL; gBS->CloseEvent (Event); SavedDevicePath = GetSpcrDevice(); if (SavedDevicePath == NULL) { return; } // // Get TerminalType info from the last device path node. // TmpDevicePath = SavedDevicePath; Tmp2DevicePath = TmpDevicePath; while (!IsDevicePathEnd (TmpDevicePath)) { Tmp2DevicePath = TmpDevicePath; TmpDevicePath = NextDevicePathNode(TmpDevicePath); } Vendor = (VENDOR_DEVICE_PATH *) Tmp2DevicePath; for (Index=0; Index < 4; Index ++) { if (CompareGuid (&Vendor->Guid, &TerminalTypeGuid[Index])) { gSpcrInfo.TerminalType = TerminalTypeValue[Index]; break; } } SetDevicePathEndNode (Tmp2DevicePath); TmpDevicePath = SavedDevicePath; // // Get Console attribute from the serial io protocol. // Status = gBS->LocateDevicePath( &gEfiSerialIoProtocolGuid, &TmpDevicePath, &Handle ); if (EFI_ERROR(Status)) { goto out; } Status = gBS->HandleProtocol ( Handle, &gEfiSerialIoProtocolGuid, (VOID **) &SerialIo ); if (EFI_ERROR(Status)) { goto out; } switch (SerialIo->Mode->BaudRate) { case 9600: gSpcrInfo.BaudRate = 3; break; case 19200: gSpcrInfo.BaudRate = 4; break; case 57600: gSpcrInfo.BaudRate = 6; break; case 115200: default: gSpcrInfo.BaudRate = 7; break; } gSpcrInfo.FlowControl = 0; SerialIo->GetControl(SerialIo, &FlowControl); if ((FlowControl & EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE) != 0) { gSpcrInfo.FlowControl = UART_FLOW_CONTROL_HARDWARE; } if (HasIsaSerialNode(SavedDevicePath)) { GetIsaTypeInfo (SavedDevicePath); } else { GetPciTypeInfo (SavedDevicePath); } // // Not create before, create new Spcr ACPI table. // Status = gBS->LocateProtocol ( &gEfiAcpiTableProtocolGuid, NULL, (VOID**)&AcpiTablProtocol ); if (EFI_ERROR (Status)) { goto out; } // // Add table // Status = AcpiTablProtocol->InstallAcpiTable ( AcpiTablProtocol, &gSpcrInfo, sizeof (EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE), &TurnKey ); ASSERT_EFI_ERROR (Status); out: FreePool(SavedDevicePath); }