/** @file | |
This module is one template module for Incompatible PCI Device Support protocol. | |
It includes one incompatible pci devices list template. | |
Incompatible PCI Device Support protocol allows the PCI bus driver to support | |
resource allocation for some PCI devices that do not comply with the PCI Specification. | |
Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include <PiDxe.h> | |
#include <Protocol/IncompatiblePciDeviceSupport.h> | |
#include <Library/UefiBootServicesTableLib.h> | |
#include <Library/MemoryAllocationLib.h> | |
#include <Library/DebugLib.h> | |
#include <IndustryStandard/Pci.h> | |
#include <IndustryStandard/Acpi.h> | |
typedef struct { | |
UINT64 VendorId; | |
UINT64 DeviceId; | |
UINT64 RevisionId; | |
UINT64 SubsystemVendorId; | |
UINT64 SubsystemDeviceId; | |
} EFI_PCI_DEVICE_HEADER_INFO; | |
typedef struct { | |
UINT64 ResType; | |
UINT64 GenFlag; | |
UINT64 SpecificFlag; | |
UINT64 AddrSpaceGranularity; | |
UINT64 AddrRangeMin; | |
UINT64 AddrRangeMax; | |
UINT64 AddrTranslationOffset; | |
UINT64 AddrLen; | |
} EFI_PCI_RESOUCE_DESCRIPTOR; | |
#define PCI_DEVICE_ID(VendorId, DeviceId, Revision, SubVendorId, SubDeviceId) \ | |
VendorId, DeviceId, Revision, SubVendorId, SubDeviceId | |
#define DEVICE_INF_TAG 0xFFF2 | |
#define DEVICE_RES_TAG 0xFFF1 | |
#define LIST_END_TAG 0x0000 | |
#define EVEN_ALIGN 0xFFFFFFFFFFFFFFFEULL | |
/** | |
Returns a list of ACPI resource descriptors that detail the special | |
resource configuration requirements for an incompatible PCI device. | |
@param This Pointer to the EFI_INCOMPATIBLE_PCI_DEVICE_SUPPORT_PROTOCOL instance. | |
@param VendorId A unique ID to identify the manufacturer of the PCI device. | |
@param DeviceId A unique ID to identify the particular PCI device. | |
@param RevisionId A PCI device-specific revision identifier. | |
@param SubsystemVendorId Specifies the subsystem vendor ID. | |
@param SubsystemDeviceId Specifies the subsystem device ID. | |
@param Configuration A list of ACPI resource descriptors returned that detail | |
the configuration requirement. | |
@retval EFI_SUCCESS Successfully got ACPI resource for specified PCI device. | |
@retval EFI_INVALID_PARAMETER Configuration is NULL. | |
@retval EFI_OUT_OF_RESOURCES No memory available. | |
@retval EFI_UNSUPPORTED The specified PCI device wasn't supported. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
PCheckDevice ( | |
IN EFI_INCOMPATIBLE_PCI_DEVICE_SUPPORT_PROTOCOL *This, | |
IN UINTN VendorId, | |
IN UINTN DeviceId, | |
IN UINTN RevisionId, | |
IN UINTN SubsystemVendorId, | |
IN UINTN SubsystemDeviceId, | |
OUT VOID **Configuration | |
); | |
// | |
// Handle onto which the Incompatible PCI Device List is installed | |
// | |
EFI_HANDLE mIncompatiblePciDeviceSupportHandle = NULL; | |
// | |
// The Incompatible PCI Device Support Protocol instance produced by this driver | |
// | |
EFI_INCOMPATIBLE_PCI_DEVICE_SUPPORT_PROTOCOL mIncompatiblePciDeviceSupport = { | |
PCheckDevice | |
}; | |
// | |
// The incompatible PCI devices list template | |
// | |
GLOBAL_REMOVE_IF_UNREFERENCED UINT64 mIncompatiblePciDeviceList[] = { | |
// | |
// DEVICE_INF_TAG, | |
// PCI_DEVICE_ID (VendorID, DeviceID, Revision, SubVendorId, SubDeviceId), | |
// DEVICE_RES_TAG, | |
// ResType, GFlag , SFlag, Granularity, RangeMin, | |
// RangeMax, Offset, AddrLen | |
// | |
// | |
// Device Adaptec 9004 | |
// | |
DEVICE_INF_TAG, | |
PCI_DEVICE_ID (0x9004, MAX_UINT64, MAX_UINT64, MAX_UINT64, MAX_UINT64), | |
DEVICE_RES_TAG, | |
ACPI_ADDRESS_SPACE_TYPE_IO, | |
0, | |
0, | |
0, | |
0, | |
EVEN_ALIGN, | |
MAX_UINT64, | |
0, | |
// | |
// Device Adaptec 9005 | |
// | |
DEVICE_INF_TAG, | |
PCI_DEVICE_ID (0x9005, MAX_UINT64, MAX_UINT64, MAX_UINT64, MAX_UINT64), | |
DEVICE_RES_TAG, | |
ACPI_ADDRESS_SPACE_TYPE_IO, | |
0, | |
0, | |
0, | |
0, | |
EVEN_ALIGN, | |
MAX_UINT64, | |
0, | |
// | |
// Device QLogic 1007 | |
// | |
DEVICE_INF_TAG, | |
PCI_DEVICE_ID (0x1077, MAX_UINT64, MAX_UINT64, MAX_UINT64, MAX_UINT64), | |
DEVICE_RES_TAG, | |
ACPI_ADDRESS_SPACE_TYPE_IO, | |
0, | |
0, | |
0, | |
0, | |
EVEN_ALIGN, | |
MAX_UINT64, | |
0, | |
// | |
// Device Agilent 103C | |
// | |
DEVICE_INF_TAG, | |
PCI_DEVICE_ID (0x103C, MAX_UINT64, MAX_UINT64, MAX_UINT64, MAX_UINT64), | |
DEVICE_RES_TAG, | |
ACPI_ADDRESS_SPACE_TYPE_IO, | |
0, | |
0, | |
0, | |
0, | |
EVEN_ALIGN, | |
MAX_UINT64, | |
0, | |
// | |
// Device Agilent 15BC | |
// | |
DEVICE_INF_TAG, | |
PCI_DEVICE_ID (0x15BC, MAX_UINT64, MAX_UINT64, MAX_UINT64, MAX_UINT64), | |
DEVICE_RES_TAG, | |
ACPI_ADDRESS_SPACE_TYPE_IO, | |
0, | |
0, | |
0, | |
0, | |
EVEN_ALIGN, | |
MAX_UINT64, | |
0, | |
// | |
// The end of the list | |
// | |
LIST_END_TAG | |
}; | |
/** | |
Entry point of the incompatible pci device support code. Setup an incompatible device list template | |
and install EFI Incompatible PCI Device Support protocol. | |
@param ImageHandle A handle for the image that is initializing this driver. | |
@param SystemTable A pointer to the EFI system table. | |
@retval EFI_SUCCESS Installed EFI Incompatible PCI Device Support Protocol successfully. | |
@retval others Failed to install protocol. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
IncompatiblePciDeviceSupportEntryPoint ( | |
IN EFI_HANDLE ImageHandle, | |
IN EFI_SYSTEM_TABLE *SystemTable | |
) | |
{ | |
EFI_STATUS Status; | |
// | |
// Install EFI Incompatible PCI Device Support Protocol on a new handle | |
// | |
Status = gBS->InstallProtocolInterface ( | |
&mIncompatiblePciDeviceSupportHandle, | |
&gEfiIncompatiblePciDeviceSupportProtocolGuid, | |
EFI_NATIVE_INTERFACE, | |
&mIncompatiblePciDeviceSupport | |
); | |
ASSERT_EFI_ERROR (Status); | |
return Status; | |
} | |
/** | |
Returns a list of ACPI resource descriptors that detail the special | |
resource configuration requirements for an incompatible PCI device. | |
@param This Pointer to the EFI_INCOMPATIBLE_PCI_DEVICE_SUPPORT_PROTOCOL instance. | |
@param VendorId A unique ID to identify the manufacturer of the PCI device. | |
@param DeviceId A unique ID to identify the particular PCI device. | |
@param RevisionId A PCI device-specific revision identifier. | |
@param SubsystemVendorId Specifies the subsystem vendor ID. | |
@param SubsystemDeviceId Specifies the subsystem device ID. | |
@param Configuration A list of ACPI resource descriptors returned that detail | |
the configuration requirement. | |
@retval EFI_SUCCESS Successfully got ACPI resource for specified PCI device. | |
@retval EFI_INVALID_PARAMETER Configuration is NULL. | |
@retval EFI_OUT_OF_RESOURCES No memory available. | |
@retval EFI_UNSUPPORTED The specified PCI device wasn't supported. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
PCheckDevice ( | |
IN EFI_INCOMPATIBLE_PCI_DEVICE_SUPPORT_PROTOCOL *This, | |
IN UINTN VendorId, | |
IN UINTN DeviceId, | |
IN UINTN RevisionId, | |
IN UINTN SubsystemVendorId, | |
IN UINTN SubsystemDeviceId, | |
OUT VOID **Configuration | |
) | |
{ | |
UINT64 Tag; | |
UINT64 *ListPtr; | |
UINT64 *TempListPtr; | |
EFI_PCI_DEVICE_HEADER_INFO *Header; | |
EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *AcpiPtr; | |
EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *OldAcpiPtr; | |
EFI_PCI_RESOUCE_DESCRIPTOR *Dsc; | |
EFI_ACPI_END_TAG_DESCRIPTOR *PtrEnd; | |
UINTN Index; | |
// | |
// Validate the parameters | |
// | |
if (Configuration == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Initialize the return value to NULL | |
// | |
*(VOID **)Configuration = NULL; | |
ListPtr = mIncompatiblePciDeviceList; | |
while (*ListPtr != LIST_END_TAG) { | |
Tag = *ListPtr; | |
switch (Tag) { | |
case DEVICE_INF_TAG: | |
Header = (EFI_PCI_DEVICE_HEADER_INFO *)(ListPtr + 1); | |
ListPtr = ListPtr + 1 + sizeof (EFI_PCI_DEVICE_HEADER_INFO) / sizeof (UINT64); | |
// | |
// See if the Header matches the parameters passed in | |
// | |
if ((Header->VendorId != MAX_UINT64) && (VendorId != MAX_UINTN)) { | |
if (Header->VendorId != VendorId) { | |
continue; | |
} | |
} | |
if ((Header->DeviceId != MAX_UINT64) && (DeviceId != MAX_UINTN)) { | |
if (DeviceId != Header->DeviceId) { | |
continue; | |
} | |
} | |
if ((Header->RevisionId != MAX_UINT64) && (RevisionId != MAX_UINTN)) { | |
if (RevisionId != Header->RevisionId) { | |
continue; | |
} | |
} | |
if ((Header->SubsystemVendorId != MAX_UINT64) && (SubsystemVendorId != MAX_UINTN)) { | |
if (SubsystemVendorId != Header->SubsystemVendorId) { | |
continue; | |
} | |
} | |
if ((Header->SubsystemDeviceId != MAX_UINT64) && (SubsystemDeviceId != MAX_UINTN)) { | |
if (SubsystemDeviceId != Header->SubsystemDeviceId) { | |
continue; | |
} | |
} | |
// | |
// Matched an item, so construct the ACPI descriptor for the resource. | |
// | |
// | |
// Count the resource items so that to allocate space | |
// | |
for (Index = 0, TempListPtr = ListPtr; *TempListPtr == DEVICE_RES_TAG; Index++) { | |
TempListPtr = TempListPtr + 1 + ((sizeof (EFI_PCI_RESOUCE_DESCRIPTOR)) / sizeof (UINT64)); | |
} | |
// | |
// If there is at least one type of resource request, | |
// allocate an acpi resource node | |
// | |
if (Index == 0) { | |
return EFI_UNSUPPORTED; | |
} | |
AcpiPtr = AllocateZeroPool (sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR) * Index + sizeof (EFI_ACPI_END_TAG_DESCRIPTOR)); | |
if (AcpiPtr == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
OldAcpiPtr = AcpiPtr; | |
// | |
// Fill the EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR structure | |
// according to the EFI_PCI_RESOUCE_DESCRIPTOR structure | |
// | |
for ( ; *ListPtr == DEVICE_RES_TAG;) { | |
Dsc = (EFI_PCI_RESOUCE_DESCRIPTOR *)(ListPtr + 1); | |
AcpiPtr->Desc = ACPI_ADDRESS_SPACE_DESCRIPTOR; | |
AcpiPtr->Len = (UINT16)sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR) - 3; | |
AcpiPtr->ResType = (UINT8)Dsc->ResType; | |
AcpiPtr->GenFlag = (UINT8)Dsc->GenFlag; | |
AcpiPtr->SpecificFlag = (UINT8)Dsc->SpecificFlag; | |
AcpiPtr->AddrSpaceGranularity = Dsc->AddrSpaceGranularity; | |
AcpiPtr->AddrRangeMin = Dsc->AddrRangeMin; | |
AcpiPtr->AddrRangeMax = Dsc->AddrRangeMax; | |
AcpiPtr->AddrTranslationOffset = Dsc->AddrTranslationOffset; | |
AcpiPtr->AddrLen = Dsc->AddrLen; | |
ListPtr = ListPtr + 1 + ((sizeof (EFI_PCI_RESOUCE_DESCRIPTOR)) / sizeof (UINT64)); | |
AcpiPtr++; | |
} | |
// | |
// Put the checksum | |
// | |
PtrEnd = (EFI_ACPI_END_TAG_DESCRIPTOR *)(AcpiPtr); | |
PtrEnd->Desc = ACPI_END_TAG_DESCRIPTOR; | |
PtrEnd->Checksum = 0; | |
*(VOID **)Configuration = OldAcpiPtr; | |
return EFI_SUCCESS; | |
case DEVICE_RES_TAG: | |
// | |
// Adjust the pointer to the next PCI resource descriptor item | |
// | |
ListPtr = ListPtr + 1 + ((sizeof (EFI_PCI_RESOUCE_DESCRIPTOR)) / sizeof (UINT64)); | |
break; | |
default: | |
return EFI_UNSUPPORTED; | |
} | |
} | |
return EFI_UNSUPPORTED; | |
} |