| /** @file | |
| The implementation of PCI incompatible device support libary. | |
| Copyright (c) 2007 Intel Corporation. All rights reserved. <BR> | |
| This software and associated documentation (if any) is furnished | |
| under a license and may only be used or copied in accordance | |
| with the terms of the license. Except as permitted by such | |
| license, no part of this software or documentation may be | |
| reproduced, stored in a retrieval system, or transmitted in any | |
| form or by any means without the express written consent of | |
| Intel Corporation. | |
| **/ | |
| #include "IncompatiblePciDeviceList.h" | |
| /** | |
| Check whether two PCI devices matched | |
| @param PciDeviceInfo A pointer to EFI_PCI_DEVICE_INFO. | |
| @param Header A pointer to EFI_PCI_DEVICE_INFO. | |
| @retval returns EFI_SUCCESS if two PCI device matched. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| DeviceCheck ( | |
| IN EFI_PCI_DEVICE_INFO *PciDeviceInfo, | |
| IN EFI_PCI_DEVICE_INFO *Header | |
| ) | |
| { | |
| // | |
| // See if the Header matches the parameters passed in | |
| // | |
| if (Header->VendorID != DEVICE_ID_NOCARE) { | |
| if (PciDeviceInfo->VendorID != Header->VendorID) { | |
| return EFI_UNSUPPORTED; | |
| } | |
| } | |
| if (Header->DeviceID != DEVICE_ID_NOCARE) { | |
| if (PciDeviceInfo->DeviceID != Header->DeviceID) { | |
| return EFI_UNSUPPORTED; | |
| } | |
| } | |
| if (Header->RevisionID != DEVICE_ID_NOCARE) { | |
| if (PciDeviceInfo->RevisionID != Header->RevisionID) { | |
| return EFI_UNSUPPORTED; | |
| } | |
| } | |
| if (Header->SubsystemVendorID != DEVICE_ID_NOCARE) { | |
| if (PciDeviceInfo->SubsystemVendorID != Header->SubsystemVendorID) { | |
| return EFI_UNSUPPORTED; | |
| } | |
| } | |
| if (Header->SubsystemID != DEVICE_ID_NOCARE) { | |
| if (PciDeviceInfo->SubsystemID != Header->SubsystemID) { | |
| return EFI_UNSUPPORTED; | |
| } | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Check the incompatible device list for ACPI resource update and return | |
| the configuration | |
| This function searches the incompatible device list according to request | |
| information. If the PCI device belongs to the devices list, corresponding | |
| configuration informtion will be returned, in the meantime return EFI_SUCCESS. | |
| @param PciDeviceInfo A pointer to PCI device information. | |
| @param Configuration Returned information. | |
| @retval returns EFI_SUCCESS if check incompatible device ok. | |
| Otherwise return EFI_UNSUPPORTED. | |
| **/ | |
| RETURN_STATUS | |
| EFIAPI | |
| PciResourceUpdateCheck ( | |
| IN EFI_PCI_DEVICE_INFO *PciDeviceInfo, | |
| OUT VOID *Configuration | |
| ) | |
| { | |
| UINT64 Tag; | |
| UINT64 *ListPtr; | |
| UINT64 *TempListPtr; | |
| EFI_PCI_DEVICE_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; | |
| ASSERT (PciDeviceInfo != NULL); | |
| // | |
| // Initialize the return value to NULL | |
| // | |
| * (VOID **) Configuration = NULL; | |
| ListPtr = IncompatiblePciDeviceListForResource; | |
| while (*ListPtr != LIST_END_TAG) { | |
| Tag = *ListPtr; | |
| switch (Tag) { | |
| case DEVICE_INF_TAG: | |
| Header = (EFI_PCI_DEVICE_INFO *) (ListPtr + 1); | |
| ListPtr = ListPtr + 1 + sizeof (EFI_PCI_DEVICE_INFO) / sizeof (UINT64); | |
| if (DeviceCheck (PciDeviceInfo, Header) != EFI_SUCCESS) { | |
| 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 a acpi resource node | |
| // | |
| if (Index == 0) { | |
| return EFI_ABORTED; | |
| } | |
| AcpiPtr = AllocateZeroPool ( | |
| sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR) * Index + sizeof (EFI_ACPI_END_TAG_DESCRIPTOR) | |
| ); | |
| 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 = sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR); | |
| 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; | |
| } | |
| /** | |
| Check the incompatible device list and return configuraton register mask values. | |
| This function searches the incompatible device list according to request | |
| information. If the PCI device belongs to the devices list, corresponding | |
| configuration informtion will be returned, in the meantime return EFI_SUCCESS. | |
| @param PciDeviceInfo A pointer to EFI_PCI_DEVICE_INFO. | |
| @param AccessType Access Type, READ or WRITE. | |
| @param Offset The address within the PCI configuration space. | |
| @param Configuration Returned information. | |
| @retval returns EFI_SUCCESS if check incompatible device ok. | |
| Otherwise return EFI_UNSUPPORTED. | |
| **/ | |
| RETURN_STATUS | |
| EFIAPI | |
| PciRegisterUpdateCheck ( | |
| IN EFI_PCI_DEVICE_INFO *PciDeviceInfo, | |
| IN UINT64 AccessType, | |
| IN UINT64 Offset, | |
| OUT VOID *Configuration | |
| ) | |
| { | |
| EFI_PCI_DEVICE_INFO *Header; | |
| UINT64 Tag; | |
| UINT64 *ListPtr; | |
| EFI_PCI_REGISTER_VALUE_DATA *RegisterPtr; | |
| EFI_PCI_REGISTER_VALUE_DATA *Dsc; | |
| ASSERT (PciDeviceInfo != NULL); | |
| ListPtr = IncompatiblePciDeviceListForRegister; | |
| // | |
| // Initialize the return value to NULL | |
| // | |
| * (VOID **) Configuration = NULL; | |
| while (*ListPtr != LIST_END_TAG) { | |
| Tag = *ListPtr; | |
| switch (Tag) { | |
| case DEVICE_INF_TAG: | |
| Header = (EFI_PCI_DEVICE_INFO *) (ListPtr + 1); | |
| ListPtr = ListPtr + 1 + sizeof (EFI_PCI_DEVICE_INFO) / sizeof (UINT64); | |
| // | |
| // Check whether the PCI device matches the device in the incompatible devices list? | |
| // If not, ship next | |
| // | |
| if (DeviceCheck (PciDeviceInfo, Header) != EFI_SUCCESS) { | |
| continue; | |
| } | |
| // | |
| // Matched an item, check whether access matches? | |
| // | |
| for (; *ListPtr == DEVICE_RES_TAG;) { | |
| ListPtr ++; | |
| if (((EFI_PCI_REGISTER_VALUE_DESCRIPTOR *)ListPtr)->Offset == (Offset & 0xfc)) { | |
| if (((EFI_PCI_REGISTER_VALUE_DESCRIPTOR *)ListPtr)->AccessType == AccessType) { | |
| Dsc = (EFI_PCI_REGISTER_VALUE_DATA *) (ListPtr + 2); | |
| RegisterPtr = AllocateZeroPool (sizeof (EFI_PCI_REGISTER_VALUE_DATA)); | |
| RegisterPtr->AndValue = Dsc->AndValue; | |
| RegisterPtr->OrValue = Dsc->OrValue; | |
| *(VOID **) Configuration = RegisterPtr; | |
| return EFI_SUCCESS; | |
| } | |
| } | |
| ListPtr += sizeof (EFI_PCI_REGISTER_VALUE_DESCRIPTOR) / (sizeof (UINT64)); | |
| } | |
| return EFI_UNSUPPORTED; | |
| case DEVICE_RES_TAG: | |
| // | |
| // Adjust the pointer to the next item | |
| // | |
| ListPtr = ListPtr + 1 + ((sizeof (EFI_PCI_REGISTER_VALUE_DESCRIPTOR)) / sizeof (UINT64)); | |
| break; | |
| default: | |
| return EFI_UNSUPPORTED; | |
| } | |
| } | |
| return EFI_UNSUPPORTED; | |
| } | |
| /** | |
| Check the incompatible device list for access width incompatibility and | |
| return the configuration | |
| This function searches the incompatible device list for access width | |
| incompatibility according to request information. If the PCI device | |
| belongs to the devices list, corresponding configuration informtion | |
| will be returned, in the meantime return EFI_SUCCESS. | |
| @param PciDeviceInfo A pointer to PCI device information. | |
| @param AccessType Access type, READ or WRITE. | |
| @param Offset The address within the PCI configuration space. | |
| @param AccessWidth Access width needs to check incompatibility. | |
| @param Configuration Returned information. | |
| @retval returns EFI_SUCCESS if check incompatible device ok. | |
| Otherwise return EFI_UNSUPPORTED. | |
| **/ | |
| RETURN_STATUS | |
| EFIAPI | |
| PciRegisterAccessCheck ( | |
| IN EFI_PCI_DEVICE_INFO *PciDeviceInfo, | |
| IN UINT64 AccessType, | |
| IN UINT64 Offset, | |
| IN UINT64 AccessWidth, | |
| OUT VOID *Configuration | |
| ) | |
| { | |
| EFI_PCI_DEVICE_INFO *Header; | |
| UINT64 Tag; | |
| UINT64 *ListPtr; | |
| EFI_PCI_REGISTER_ACCESS_DATA *RegisterPtr; | |
| EFI_PCI_REGISTER_ACCESS_DATA *Dsc; | |
| ASSERT (PciDeviceInfo != NULL); | |
| ListPtr = DeviceListForAccessWidth; | |
| // | |
| // Initialize the return value to NULL | |
| // | |
| * (VOID **) Configuration = NULL; | |
| while (*ListPtr != LIST_END_TAG) { | |
| Tag = *ListPtr; | |
| switch (Tag) { | |
| case DEVICE_INF_TAG: | |
| Header = (EFI_PCI_DEVICE_INFO *) (ListPtr + 1); | |
| ListPtr = ListPtr + 1 + sizeof (EFI_PCI_DEVICE_INFO) / sizeof (UINT64); | |
| // | |
| // Check whether the PCI device matches the device in the incompatible devices list? | |
| // If not, ship next | |
| // | |
| if (DeviceCheck (PciDeviceInfo, Header) != EFI_SUCCESS) { | |
| continue; | |
| } | |
| // | |
| // Matched an item, check whether access matches? | |
| // | |
| for (; *ListPtr == DEVICE_RES_TAG;) { | |
| ListPtr ++; | |
| if (((EFI_PCI_REGISTER_ACCESS_DESCRIPTOR *) ListPtr)->AccessType == AccessType && | |
| ((EFI_PCI_REGISTER_ACCESS_DESCRIPTOR *) ListPtr)->AccessWidth == AccessWidth ) { | |
| Dsc = (EFI_PCI_REGISTER_ACCESS_DATA *) (ListPtr + 2); | |
| if((Dsc->StartOffset <= Offset) && (Dsc->EndOffset > Offset)) { | |
| RegisterPtr = AllocateZeroPool (sizeof (EFI_PCI_REGISTER_ACCESS_DATA)); | |
| RegisterPtr->StartOffset = Dsc->StartOffset; | |
| RegisterPtr->EndOffset = Dsc->EndOffset; | |
| RegisterPtr->Width = Dsc->Width; | |
| *(VOID **) Configuration = RegisterPtr; | |
| return EFI_SUCCESS; | |
| } | |
| } | |
| ListPtr += sizeof (EFI_PCI_REGISTER_ACCESS_DESCRIPTOR) / (sizeof (UINT64)); | |
| } | |
| return EFI_UNSUPPORTED; | |
| case DEVICE_RES_TAG: | |
| // | |
| // Adjust the pointer to the next item | |
| // | |
| ListPtr = ListPtr + 1 + ((sizeof (EFI_PCI_REGISTER_ACCESS_DESCRIPTOR)) / sizeof (UINT64)); | |
| break; | |
| default: | |
| return EFI_UNSUPPORTED; | |
| } | |
| } | |
| return EFI_UNSUPPORTED; | |
| } | |