/** @file EFI Driver following Driver Binding Protocol. Copyright (c) 2010 - 2020 Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "SioDriver.h" // // This driver is for ACPI(PNP0A03,0)/PCI(0x1f,0) // // // Sio Driver Global Variables // EFI_DRIVER_BINDING_PROTOCOL mSioDriver = { SioDriverSupported, SioDriverStart, SioDriverStop, 1, NULL, NULL }; // // The list of the created SIO_DEV // LIST_ENTRY mSioDevPool = INITIALIZE_LIST_HEAD_VARIABLE (mSioDevPool); // // Template structure to create SIO_DEV // SIO_DEV mSioDevTemplate = { SIO_DEV_SIGNATURE, // Signature NULL, // PciHandle { 0x00000000, // HID 0x00000000 // UID }, NULL, // Handle { // Sio Instance SioRegisterAccess, SioGetResources, SioSetResources, SioPossibleResources, SioModify }, NULL, // DevicePath { NULL, // ForwardLink NULL, // BackLink } }; // // Template ACPI_HID_DEVICE_PATH structure to create device path // ACPI_HID_DEVICE_PATH mAcpiNodeTemplate = { { ACPI_DEVICE_PATH, // Type ACPI_DP, // SubType { sizeof (ACPI_HID_DEVICE_PATH), // Length[0] 0 // Length[1] } }, 0x00000000, // HID 0x00000000 // UID }; /** The user Entry Point for module Lpc47m17x. The user code starts with this function. @param[in] ImageHandle The firmware allocated handle for the EFI image. @param[in] SystemTable A pointer to the EFI System Table. @retval EFI_SUCCESS The entry point is executed successfully. @retval other Some error occurs when executing this entry point. **/ EFI_STATUS EFIAPI SioDriverEntryPoint ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { if (EFI_ERROR (SioInit())) { return EFI_UNSUPPORTED; } else { // // Install protocols // return EfiLibInstallDriverBindingComponentName2 ( ImageHandle, SystemTable, &mSioDriver, ImageHandle, &mSioComponentName, &mSioComponentName2 ); } } /** Compares a PCI to ISA bridge device segment, bus, device and function to the PcdSuperIoPciIsaBridgeDevice values. @param[in] CurrentDevice The device to be compared with the PcdSuperIoPciIsaBridgeDevice information @retval TRUE This device matches PcdSuperIoPciIsaBridgeDevice values @retval FALSE This device does not match the PcdSuperIoPciIsaBridgeDevice values **/ BOOLEAN EFIAPI SioDeviceEnabled ( IN SIO_PCI_ISA_BRIDGE_DEVICE_INFO *CurrentDevice ) { SIO_PCI_ISA_BRIDGE_DEVICE_INFO *Device; Device = (SIO_PCI_ISA_BRIDGE_DEVICE_INFO *) FixedPcdGetPtr (PcdSuperIoPciIsaBridgeDevice); if(CompareMem (Device, CurrentDevice, sizeof (SIO_PCI_ISA_BRIDGE_DEVICE_INFO)) == 0) { return TRUE; } return FALSE; } /** Test to see if this driver supports Controller Handle. @param[in] This Protocol instance pointer. @param[in] Controller Handle of device to test @param[in] RemainingDevicePath Optional parameter use to pick a specific child device to start. @retval EFI_SUCCESS This driver supports this device @retval EFI_ALREADY_STARTED This driver is already running on this device @retval other This driver does not support this device **/ EFI_STATUS EFIAPI SioDriverSupported ( IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE Controller, IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath ) { EFI_STATUS Status; EFI_PCI_IO_PROTOCOL *PciIo; EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; ACPI_HID_DEVICE_PATH *AcpiNode; PCI_TYPE00 Pci; UINTN Index; EFI_SIO_ACPI_DEVICE_ID *Devices; UINTN Count; UINTN SegmentNumber; UINTN BusNumber; UINTN DeviceNumber; UINTN FunctionNumber; SIO_PCI_ISA_BRIDGE_DEVICE_INFO SioDevice; // // If RemainingDevicePath is not NULL, it should verify that the first device // path node in RemainingDevicePath is an ACPI Device path node which is a // legal Device Path Node for this bus driver's children. // if (RemainingDevicePath != NULL) { if (!IsDevicePathEnd (RemainingDevicePath)) { if ((RemainingDevicePath->Type != ACPI_DEVICE_PATH) || (((RemainingDevicePath->SubType != ACPI_DP) || (DevicePathNodeLength (RemainingDevicePath) != sizeof (ACPI_HID_DEVICE_PATH))) && ((RemainingDevicePath->SubType != ACPI_EXTENDED_DP) || (DevicePathNodeLength (RemainingDevicePath) != sizeof (ACPI_EXTENDED_HID_DEVICE_PATH)))) ) { return EFI_UNSUPPORTED; } DeviceGetList (&Devices, &Count); if (Devices == NULL) { return EFI_OUT_OF_RESOURCES; } AcpiNode = (ACPI_HID_DEVICE_PATH *) RemainingDevicePath; for (Index = 0; Index < Count; Index++) { if ((AcpiNode->HID == Devices[Index].HID) && (AcpiNode->UID == Devices[Index].UID)) { break; } } FreePool (Devices); if (Index == Count) { return EFI_UNSUPPORTED; } } } // // See if the parent device path can be opened BY_DRIVER // Status = gBS->OpenProtocol ( Controller, &gEfiDevicePathProtocolGuid, (VOID **) &ParentDevicePath, This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_BY_DRIVER ); if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { return Status; } gBS->CloseProtocol ( Controller, &gEfiDevicePathProtocolGuid, This->DriverBindingHandle, Controller ); // // Get PciIo protocol instance // Status = gBS->OpenProtocol ( Controller, &gEfiPciIoProtocolGuid, (VOID **) &PciIo, This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_BY_DRIVER ); if (!EFI_ERROR (Status)) { Status = PciIo->Pci.Read ( PciIo, EfiPciIoWidthUint32, 0, sizeof (Pci) / sizeof (UINT32), &Pci ); ASSERT_EFI_ERROR (Status); Status = EFI_UNSUPPORTED; if ((Pci.Hdr.Command & (EFI_PCI_COMMAND_IO_SPACE | EFI_PCI_COMMAND_MEMORY_SPACE)) == (EFI_PCI_COMMAND_IO_SPACE | EFI_PCI_COMMAND_MEMORY_SPACE) ) { if (Pci.Hdr.ClassCode[2] == PCI_CLASS_BRIDGE) { // // See if this is a standard PCI to ISA Bridge from the Base Code and Class Code // if (Pci.Hdr.ClassCode[1] == PCI_CLASS_BRIDGE_ISA) { Status = EFI_SUCCESS; } // // See if this is an Intel PCI to ISA Bridge in Positive Decode Mode // if ((Pci.Hdr.ClassCode[1] == PCI_CLASS_BRIDGE_ISA_PDECODE) && (Pci.Hdr.VendorId == 0x8086)) { // // See if this is on Function #0 to avoid false positive on // PCI_CLASS_BRIDGE_OTHER that has the same value as // PCI_CLASS_BRIDGE_ISA_PDECODE // Status = PciIo->GetLocation ( PciIo, &SegmentNumber, &BusNumber, &DeviceNumber, &FunctionNumber ); if (!EFI_ERROR (Status) && (FunctionNumber == 0)) { Status = EFI_SUCCESS; } else { Status = EFI_UNSUPPORTED; } } if(!EFI_ERROR (Status)) { Status = PciIo->GetLocation ( PciIo, &SegmentNumber, &BusNumber, &DeviceNumber, &FunctionNumber ); if(!EFI_ERROR (Status)) { SioDevice.Segment = (UINT8) SegmentNumber; SioDevice.Bus = (UINT8) BusNumber; SioDevice.Device = (UINT8) DeviceNumber; SioDevice.Funtion = (UINT8) FunctionNumber; if(!SioDeviceEnabled (&SioDevice)) { Status = EFI_UNSUPPORTED; } } } } } gBS->CloseProtocol ( Controller, &gEfiPciIoProtocolGuid, This->DriverBindingHandle, Controller ); } if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { return Status; } return EFI_SUCCESS; } /** Destroy the SIO controller handle. @param[in] ChildHandle The SIO controller handle. @retval EFI_SUCCESS The SIO controller handle is destroyed successfully. **/ EFI_STATUS SioDestroyDevice ( IN EFI_HANDLE ChildHandle ) { EFI_STATUS Status; SIO_DEV *SioDev; EFI_SIO_PROTOCOL *Sio; EFI_PCI_IO_PROTOCOL *PciIo; Status = gBS->HandleProtocol ( ChildHandle, &gEfiSioProtocolGuid, (VOID **) &Sio ); ASSERT_EFI_ERROR (Status); SioDev = SIO_DEV_FROM_THIS (Sio); Status = gBS->CloseProtocol ( SioDev->PciHandle, &gEfiPciIoProtocolGuid, mSioDriver.DriverBindingHandle, ChildHandle ); ASSERT_EFI_ERROR (Status); Status = gBS->UninstallMultipleProtocolInterfaces ( ChildHandle, &gEfiDevicePathProtocolGuid, SioDev->DevicePath, &gEfiSioProtocolGuid, &SioDev->Sio, NULL ); if (EFI_ERROR (Status)) { gBS->OpenProtocol ( SioDev->PciHandle, &gEfiPciIoProtocolGuid, (VOID **) &PciIo, mSioDriver.DriverBindingHandle, ChildHandle, EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER ); return Status; } RemoveEntryList (&SioDev->Link); FreePool (SioDev->DevicePath); FreePool (SioDev); return EFI_SUCCESS; } /** Create the SIO controller handle. @param[in] Controller The parent PCI controller handle. @param[in] Device Pointer to EFI_SIO_ACPI_DEVICE_ID. @param[in] ParentDevicePath The device path of the parent controller. @param[out] PciIo The PciIo instance of the parent controller. **/ VOID SioCreateDevice ( IN EFI_HANDLE Controller, IN EFI_SIO_ACPI_DEVICE_ID *Device, IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath, OUT EFI_PCI_IO_PROTOCOL *PciIo ) { EFI_STATUS Status; SIO_DEV *SioDev; DeviceEnable (Device); SioDev = AllocateCopyPool (sizeof (SIO_DEV), &mSioDevTemplate); ASSERT (SioDev != NULL); if (SioDev == NULL) { return; } InsertHeadList (&mSioDevPool, &SioDev->Link); SioDev->PciHandle = Controller; CopyMem (&SioDev->Device, Device, sizeof (*Device)); mAcpiNodeTemplate.HID = Device->HID; mAcpiNodeTemplate.UID = Device->UID; SioDev->DevicePath = AppendDevicePathNode (ParentDevicePath, (EFI_DEVICE_PATH_PROTOCOL *) &mAcpiNodeTemplate); ASSERT (SioDev->DevicePath != NULL); Status = gBS->InstallMultipleProtocolInterfaces ( &SioDev->Handle, &gEfiSioProtocolGuid, &SioDev->Sio, &gEfiDevicePathProtocolGuid, SioDev->DevicePath, NULL ); ASSERT_EFI_ERROR (Status); Status = gBS->OpenProtocol ( Controller, &gEfiPciIoProtocolGuid, (VOID **) &PciIo, mSioDriver.DriverBindingHandle, SioDev->Handle, EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER ); ASSERT_EFI_ERROR (Status); } /** Start this driver on ControllerHandle. @param[in] This Protocol instance pointer. @param[in] Controller Handle of device to bind driver to @param[in] RemainingDevicePath Optional parameter use to pick a specific child device to start. @retval EFI_SUCCESS This driver is added to ControllerHandle @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle @retval other This driver does not support this device **/ EFI_STATUS EFIAPI SioDriverStart ( IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE Controller, IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath ) { EFI_STATUS Status; EFI_PCI_IO_PROTOCOL *PciIo; EFI_DEVICE_PATH_PROTOCOL *DevicePath; EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; EFI_SIO_ACPI_DEVICE_ID *Devices; SIO_DEV *SioDev; UINTN Count; UINTN Index; ACPI_HID_DEVICE_PATH *AcpiNode; BOOLEAN *HasCreated; BOOLEAN *RequestCreate; LIST_ENTRY *Node; HasCreated = NULL; RequestCreate = NULL; // // Get the ISA bridge's Device Path // Status = gBS->OpenProtocol ( Controller, &gEfiDevicePathProtocolGuid, (VOID **) &ParentDevicePath, This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_BY_DRIVER ); if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) { return Status; } // // Get Pci IO // Status = gBS->OpenProtocol ( Controller, &gEfiPciIoProtocolGuid, (VOID **) &PciIo, This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_BY_DRIVER ); if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) { gBS->CloseProtocol ( Controller, &gEfiDevicePathProtocolGuid, This->DriverBindingHandle, Controller ); return Status; } if ((RemainingDevicePath != NULL) && IsDevicePathEnd (RemainingDevicePath)) { return EFI_SUCCESS; } DeviceGetList (&Devices, &Count); if (Devices == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Exit_Start; } HasCreated = AllocatePool (sizeof (BOOLEAN) * Count); ASSERT (HasCreated != NULL); if (HasCreated == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Exit_Start; } RequestCreate = AllocatePool (sizeof (BOOLEAN) * Count); ASSERT (RequestCreate != NULL); if (RequestCreate == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Exit_Start; } // // Assume no children has been created. // Assume the SIO interface hasn't been initialized. // ZeroMem (HasCreated, sizeof (BOOLEAN) * Count); if (Status == EFI_ALREADY_STARTED) { for (Node = GetFirstNode (&mSioDevPool); !IsNull (&mSioDevPool, Node); Node = GetNextNode (&mSioDevPool, Node) ) { SioDev = CR (Node, SIO_DEV, Link, SIO_DEV_SIGNATURE); Status = gBS->HandleProtocol ( SioDev->PciHandle, &gEfiDevicePathProtocolGuid, (VOID **) &DevicePath ); ASSERT_EFI_ERROR (Status); // // See if they are under the same PCI to ISA Bridge // if (CompareMem (DevicePath, ParentDevicePath, GetDevicePathSize (DevicePath)) == 0) { for (Index = 0; Index < Count; Index++) { if (CompareMem (&SioDev->Device, &Devices[Index], sizeof (EFI_SIO_ACPI_DEVICE_ID)) == 0) { HasCreated[Index] = TRUE; break; } } } } } AcpiNode = (ACPI_HID_DEVICE_PATH *) RemainingDevicePath; for (Index = 0; Index < Count; Index++) { if ((AcpiNode == NULL) || ((AcpiNode->HID == Devices[Index].HID) && (AcpiNode->UID == Devices[Index].UID)) ) { RequestCreate[Index] = TRUE; } else { RequestCreate[Index] = FALSE; } } for (Index = 0; Index < Count; Index++) { if (RequestCreate[Index] && !HasCreated[Index]) { SioCreateDevice (Controller, &Devices[Index], ParentDevicePath, PciIo); } } Exit_Start: if (Devices != NULL) { FreePool (Devices); } if (HasCreated != NULL) { FreePool (HasCreated); } if (RequestCreate != NULL) { FreePool (RequestCreate); } return Status; } /** Stop this driver on ControllerHandle. @param[in] This Protocol instance pointer. @param[in] Controller Handle of device to stop driver on @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number of children is zero stop the entire bus driver. @param[in] ChildHandleBuffer List of Child Handles to Stop. @retval EFI_SUCCESS This driver is removed ControllerHandle @retval other This driver was not removed from this device **/ EFI_STATUS EFIAPI SioDriverStop ( IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE Controller, IN UINTN NumberOfChildren, IN EFI_HANDLE *ChildHandleBuffer ) { EFI_STATUS Status; UINTN Index; BOOLEAN AllChildrenStopped; if (NumberOfChildren == 0) { gBS->CloseProtocol ( Controller, &gEfiDevicePathProtocolGuid, This->DriverBindingHandle, Controller ); gBS->CloseProtocol ( Controller, &gEfiPciIoProtocolGuid, This->DriverBindingHandle, Controller ); return EFI_SUCCESS; } AllChildrenStopped = TRUE; for (Index = 0; Index < NumberOfChildren; Index++) { Status = SioDestroyDevice (ChildHandleBuffer[Index]); if (EFI_ERROR (Status)) { AllChildrenStopped = FALSE; } } if (AllChildrenStopped) { return EFI_SUCCESS; } else { return EFI_DEVICE_ERROR; } }