1155 lines
36 KiB
C
1155 lines
36 KiB
C
/** @file
|
|
|
|
Produce DiskInfo protocol for every attached RAID ATA HDD devices.
|
|
|
|
@copyright
|
|
INTEL CONFIDENTIAL
|
|
Copyright 2012 - 2021 Intel Corporation.
|
|
|
|
The source code contained or described herein and all documents related to the
|
|
source code ("Material") are owned by Intel Corporation or its suppliers or
|
|
licensors. Title to the Material remains with Intel Corporation or its suppliers
|
|
and licensors. The Material may contain trade secrets and proprietary and
|
|
confidential information of Intel Corporation and its suppliers and licensors,
|
|
and is protected by worldwide copyright and trade secret laws and treaty
|
|
provisions. No part of the Material may be used, copied, reproduced, modified,
|
|
published, uploaded, posted, transmitted, distributed, or disclosed in any way
|
|
without Intel's prior express written permission.
|
|
|
|
No license under any patent, copyright, trade secret or other intellectual
|
|
property right is granted to or conferred upon you by disclosure or delivery
|
|
of the Materials, either expressly, by implication, inducement, estoppel or
|
|
otherwise. Any license under such intellectual property rights must be
|
|
express and approved by Intel in writing.
|
|
|
|
Unless otherwise agreed by Intel in writing, you may not remove or alter
|
|
this notice or any other notice embedded in Materials by Intel or
|
|
Intel's suppliers or licensors in any way.
|
|
|
|
This file contains a 'Sample Driver' and is licensed as such under the terms
|
|
of your license agreement with Intel or your vendor. This file may be modified
|
|
by the user, subject to the additional terms of the license agreement.
|
|
|
|
@par Specification Reference:
|
|
**/
|
|
|
|
#include "IntelUefiRaidDiskInfo.h"
|
|
|
|
VOID
|
|
UninstallOrphanDiskInfoProtocol (
|
|
VOID
|
|
);
|
|
|
|
//
|
|
// Global data declaration
|
|
//
|
|
//
|
|
// EFI Driver Binding Protocol Instance
|
|
//
|
|
GLOBAL_REMOVE_IF_UNREFERENCED EFI_DRIVER_BINDING_PROTOCOL gIntelUefiRaidDiskInfoDriverBinding = {
|
|
IntelUefiRaidDiskInfoDriverBindingSupported,
|
|
IntelUefiRaidDiskInfoDriverBindingStart,
|
|
IntelUefiRaidDiskInfoDriverBindingStop,
|
|
0x01,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
//
|
|
// Template for SATA device path.
|
|
//
|
|
GLOBAL_REMOVE_IF_UNREFERENCED SATA_DEVICE_PATH mSataDevicePathTemplate = {
|
|
{
|
|
MESSAGING_DEVICE_PATH,
|
|
MSG_SATA_DP,
|
|
{
|
|
(UINT8) (sizeof (SATA_DEVICE_PATH)),
|
|
(UINT8) ((sizeof (SATA_DEVICE_PATH)) >> 8)
|
|
}
|
|
},
|
|
0,
|
|
0,
|
|
0
|
|
};
|
|
|
|
//
|
|
// Template for ATA Hdd Device.
|
|
//
|
|
GLOBAL_REMOVE_IF_UNREFERENCED ATA_DEVICE mAtaDeviceTemplate = {
|
|
ATA_DEVICE_SIGNATURE, // Signature
|
|
{
|
|
NULL, // Link
|
|
NULL
|
|
},
|
|
NULL, // Handle
|
|
{ // DiskInfo
|
|
EFI_DISK_INFO_RAID_INTERFACE_GUID,
|
|
AtaDiskInfoInquiry,
|
|
AtaDiskInfoIdentify,
|
|
AtaDiskInfoSenseData,
|
|
AtaDiskInfoWhichIde
|
|
},
|
|
NULL, // DevicePath
|
|
0, // Port
|
|
0, // PortMultiplierPort
|
|
{ 0, }, // Packet
|
|
{{ 0}, }, // Acb
|
|
NULL, // Asb
|
|
NULL // IdentifyData
|
|
};
|
|
|
|
|
|
/**
|
|
Wrapper for EFI_ATA_PASS_THRU_PROTOCOL.PassThru().
|
|
|
|
This function wraps the PassThru() invocation for ATA pass through function
|
|
for an ATA device. It assembles the ATA pass through command packet for ATA
|
|
transaction.
|
|
|
|
@param AtaDevice The ATA child device involved for the operation.
|
|
|
|
@return The return status from EFI_ATA_PASS_THRU_PROTOCOL.PassThru().
|
|
|
|
**/
|
|
EFI_STATUS
|
|
AtaDevicePassThru (
|
|
IN OUT ATA_DEVICE *AtaDevice,
|
|
IN EFI_ATA_PASS_THRU_PROTOCOL *AtaPassThru
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_ATA_PASS_THRU_COMMAND_PACKET *Packet;
|
|
|
|
//
|
|
// Assemble packet
|
|
//
|
|
Packet = &AtaDevice->Packet;
|
|
Packet->Asb = AtaDevice->Asb;
|
|
Packet->Acb = &AtaDevice->Acb;
|
|
Packet->Timeout = ATA_TIMEOUT;
|
|
|
|
Status = AtaPassThru->PassThru (
|
|
AtaPassThru,
|
|
AtaDevice->Port,
|
|
AtaDevice->PortMultiplierPort,
|
|
Packet,
|
|
NULL
|
|
);
|
|
//
|
|
// Ensure ATA pass through caller and callee have the same
|
|
// interpretation of ATA pass through protocol.
|
|
//
|
|
ASSERT (Status != EFI_INVALID_PARAMETER);
|
|
ASSERT (Status != EFI_BAD_BUFFER_SIZE);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
Identifies ATA device via the Identify data.
|
|
|
|
This function identifies the ATA device and initializes the Media information in
|
|
Block IO protocol interface.
|
|
|
|
@param AtaDevice The ATA child device involved for the operation.
|
|
|
|
@retval EFI_UNSUPPORTED The device is not a valid ATA device (hard disk).
|
|
@retval EFI_SUCCESS The device is successfully identified and Media information
|
|
is correctly initialized.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
IdentifyAtaDevice (
|
|
IN OUT ATA_DEVICE *AtaDevice
|
|
)
|
|
{
|
|
ATA_IDENTIFY_DATA *IdentifyData;
|
|
|
|
IdentifyData = AtaDevice->IdentifyData;
|
|
|
|
if ((IdentifyData->config & BIT15) != 0) {
|
|
//
|
|
// This is not an hard disk
|
|
//
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Discovers whether it is a valid ATA device.
|
|
|
|
This function issues ATA_CMD_IDENTIFY_DRIVE command to the ATA device to identify it.
|
|
If the command is executed successfully, it then identifies it and initializes
|
|
the Media information in Block IO protocol interface.
|
|
|
|
@param AtaDevice The ATA child device involved for the operation.
|
|
|
|
@retval EFI_SUCCESS The device is successfully identified and Media information
|
|
is correctly initialized.
|
|
@return others Some error occurs when discovering the ATA device.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
DiscoverAtaDevice (
|
|
IN OUT ATA_DEVICE *AtaDevice,
|
|
IN EFI_ATA_PASS_THRU_PROTOCOL *AtaPassThru
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_ATA_COMMAND_BLOCK *Acb;
|
|
EFI_ATA_PASS_THRU_COMMAND_PACKET *Packet;
|
|
UINTN Retry;
|
|
|
|
//
|
|
// Prepare for ATA command block.
|
|
//
|
|
Acb = ZeroMem (&AtaDevice->Acb, sizeof (*Acb));
|
|
Acb->AtaCommand = ATA_CMD_IDENTIFY_DRIVE;
|
|
Acb->AtaDeviceHead = (UINT8) (BIT7 | BIT6 | BIT5 | (AtaDevice->PortMultiplierPort << 4));
|
|
|
|
//
|
|
// Prepare for ATA pass through packet.
|
|
//
|
|
Packet = ZeroMem (&AtaDevice->Packet, sizeof (EFI_ATA_PASS_THRU_COMMAND_PACKET));
|
|
Packet->InDataBuffer = AtaDevice->IdentifyData;
|
|
Packet->InTransferLength = sizeof (ATA_IDENTIFY_DATA);
|
|
Packet->Protocol = EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_IN;
|
|
Packet->Length = EFI_ATA_PASS_THRU_LENGTH_BYTES | EFI_ATA_PASS_THRU_LENGTH_SECTOR_COUNT;
|
|
|
|
Retry = 3;
|
|
do {
|
|
Status = AtaDevicePassThru (AtaDevice, AtaPassThru);
|
|
if (!EFI_ERROR (Status)) {
|
|
//
|
|
// The command is issued successfully
|
|
//
|
|
Status = IdentifyAtaDevice (AtaDevice);
|
|
if (!EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
}
|
|
} while (Retry-- > 0);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
Provides inquiry information for the controller type.
|
|
|
|
This function is used by the IDE bus driver to get inquiry data. Data format
|
|
of Identify data is defined by the Interface GUID.
|
|
|
|
@param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL instance.
|
|
@param[in, out] InquiryData Pointer to a buffer for the inquiry data.
|
|
@param[in, out] InquiryDataSize Pointer to the value for the inquiry data size.
|
|
|
|
@retval EFI_SUCCESS The command was accepted without any errors.
|
|
@retval EFI_NOT_FOUND Device does not support this data class
|
|
@retval EFI_DEVICE_ERROR Error reading InquiryData from device
|
|
@retval EFI_BUFFER_TOO_SMALL InquiryDataSize not big enough
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
AtaDiskInfoInquiry (
|
|
IN EFI_DISK_INFO_PROTOCOL *This,
|
|
IN OUT VOID *InquiryData,
|
|
IN OUT UINT32 *InquiryDataSize
|
|
)
|
|
{
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
|
|
/**
|
|
Provides identify information for the controller type.
|
|
|
|
This function is used by the IDE bus driver to get identify data. Data format
|
|
of Identify data is defined by the Interface GUID.
|
|
|
|
@param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL
|
|
instance.
|
|
@param[in, out] IdentifyData Pointer to a buffer for the identify data.
|
|
@param[in, out] IdentifyDataSize Pointer to the value for the identify data
|
|
size.
|
|
|
|
@retval EFI_SUCCESS The command was accepted without any errors.
|
|
@retval EFI_NOT_FOUND Device does not support this data class
|
|
@retval EFI_DEVICE_ERROR Error reading IdentifyData from device
|
|
@retval EFI_BUFFER_TOO_SMALL IdentifyDataSize not big enough
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
AtaDiskInfoIdentify (
|
|
IN EFI_DISK_INFO_PROTOCOL *This,
|
|
IN OUT VOID *IdentifyData,
|
|
IN OUT UINT32 *IdentifyDataSize
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
ATA_DEVICE *AtaDevice;
|
|
|
|
AtaDevice = ATA_DEVICE_FROM_DISK_INFO (This);
|
|
|
|
Status = EFI_BUFFER_TOO_SMALL;
|
|
if (*IdentifyDataSize >= sizeof (*AtaDevice->IdentifyData)) {
|
|
Status = EFI_SUCCESS;
|
|
CopyMem (IdentifyData, AtaDevice->IdentifyData, sizeof (*AtaDevice->IdentifyData));
|
|
}
|
|
*IdentifyDataSize = sizeof (*AtaDevice->IdentifyData);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
Provides sense data information for the controller type.
|
|
|
|
This function is used by the IDE bus driver to get sense data.
|
|
Data format of Sense data is defined by the Interface GUID.
|
|
|
|
@param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL instance.
|
|
@param[in, out] SenseData Pointer to the SenseData.
|
|
@param[in, out] SenseDataSize Size of SenseData in bytes.
|
|
@param[out] SenseDataNumber Pointer to the value for the sense data size.
|
|
|
|
@retval EFI_SUCCESS The command was accepted without any errors.
|
|
@retval EFI_NOT_FOUND Device does not support this data class.
|
|
@retval EFI_DEVICE_ERROR Error reading SenseData from device.
|
|
@retval EFI_BUFFER_TOO_SMALL SenseDataSize not big enough.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
AtaDiskInfoSenseData (
|
|
IN EFI_DISK_INFO_PROTOCOL *This,
|
|
IN OUT VOID *SenseData,
|
|
IN OUT UINT32 *SenseDataSize,
|
|
OUT UINT8 *SenseDataNumber
|
|
)
|
|
{
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
|
|
/**
|
|
This function is used by the IDE bus driver to get controller information.
|
|
|
|
@param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL instance.
|
|
@param[out] IdeChannel Pointer to the Ide Channel number. Primary or secondary.
|
|
@param[out] IdeDevice Pointer to the Ide Device number.
|
|
|
|
@retval EFI_SUCCESS IdeChannel and IdeDevice are valid.
|
|
@retval EFI_UNSUPPORTED This is not an IDE device.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
AtaDiskInfoWhichIde (
|
|
IN EFI_DISK_INFO_PROTOCOL *This,
|
|
OUT UINT32 *IdeChannel,
|
|
OUT UINT32 *IdeDevice
|
|
)
|
|
{
|
|
ATA_DEVICE *AtaDevice;
|
|
|
|
AtaDevice = ATA_DEVICE_FROM_DISK_INFO (This);
|
|
*IdeChannel = AtaDevice->Port;
|
|
*IdeDevice = AtaDevice->PortMultiplierPort;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Get the remaining devicepath if two devicepaths have the same prefix devicepath.
|
|
|
|
@param[in] ParentDevicePath Pointer to parent devicepath.
|
|
@param[in] DevicePath Pointer to object devicepath.
|
|
@param[in, out] RemainingDevicePath Pointer to return the remaining devicepath.
|
|
|
|
@retval EFI_SUCCESS The same prefix devicepath, and return valid remaining devicepath.
|
|
@retval EFI_UNSUPPORTED Prefix devicepath is not the same, and doesn't .
|
|
|
|
**/
|
|
EFI_STATUS
|
|
GetRemainingDevicePath (
|
|
IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath,
|
|
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
|
|
IN EFI_DEVICE_PATH_PROTOCOL **RemainingDevicePath
|
|
)
|
|
{
|
|
EFI_DEVICE_PATH_PROTOCOL *ParentDevicePathNode;
|
|
EFI_DEVICE_PATH_PROTOCOL *DevicePathNode;
|
|
UINTN NodeLength;
|
|
|
|
ParentDevicePathNode = ParentDevicePath;
|
|
DevicePathNode = DevicePath;
|
|
|
|
while (!IsDevicePathEndType (ParentDevicePathNode)) {
|
|
if (IsDevicePathEndType (DevicePathNode)) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
NodeLength = DevicePathNodeLength (ParentDevicePathNode);
|
|
if (CompareMem (ParentDevicePathNode, DevicePathNode, NodeLength)) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
ParentDevicePathNode = NextDevicePathNode (ParentDevicePathNode);
|
|
DevicePathNode = NextDevicePathNode (DevicePathNode);
|
|
}
|
|
|
|
if (IsDevicePathEndType (DevicePathNode)) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
*RemainingDevicePath = DevicePathNode;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Registers an ATA HDD device.
|
|
|
|
There are three cases to install the DiskInfo protocol.
|
|
1. For Raid disks, DiskInfo driver will new the individual device path for each disk and
|
|
install the DiskInfo protocol on the handle.
|
|
2. For non-Raid disks, DiskInfo driver will install the DiskInfo protocol on the corresponding
|
|
device path handle.
|
|
3. For the disk that is hidden by RAID driver, DiskInfo driver will new a device path for it,
|
|
and install DiskInfo protocol on the handle. (In the edge case that the RST cache disk.)
|
|
|
|
@param AtaBusDriverData The parent ATA bus driver data structure.
|
|
@param Port The port number of the ATA device.
|
|
@param PortMultiplierPort The port multiplier port number of the ATA device.
|
|
|
|
@retval EFI_SUCCESS The ATA device is successfully registered.
|
|
@retval EFI_OUT_OF_RESOURCES There is not enough memory to allocate the ATA device
|
|
and related data structures.
|
|
@return Others Some error occurs when registering the ATA device.
|
|
**/
|
|
EFI_STATUS
|
|
RegisterAtaHddDevice (
|
|
IN EFI_HANDLE Controller,
|
|
IN EFI_HANDLE DriverBinding,
|
|
IN EFI_ATA_PASS_THRU_PROTOCOL *AtaPassThru,
|
|
IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath,
|
|
IN UINT16 Port,
|
|
IN UINT16 PortMultiplierPort
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
|
|
EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath;
|
|
EFI_DEVICE_PATH_PROTOCOL *NewDevicePathNode;
|
|
EFI_DEVICE_PATH_PROTOCOL *DevicePathNode;
|
|
SATA_DEVICE_PATH *SataDevicePathNode;
|
|
EFI_HANDLE DeviceHandle;
|
|
ATA_DEVICE *AtaDevice;
|
|
EFI_DISK_INFO_PROTOCOL *DiskInfo;
|
|
UINTN HandleCount;
|
|
EFI_HANDLE *HandleBuffer;
|
|
UINTN Index;
|
|
|
|
AtaDevice = NULL;
|
|
DiskInfo = NULL;
|
|
DevicePath = NULL;
|
|
RemainingDevicePath = NULL;
|
|
NewDevicePathNode = NULL;
|
|
|
|
HandleCount = 0;
|
|
HandleBuffer = NULL;
|
|
DeviceHandle = NULL;
|
|
|
|
//
|
|
// The UEFI RAID driver produces only one Sata device path for the Raid Disks,
|
|
// which the HBAPortNumber is the bitmap for the sata ports.
|
|
// For example, if there are three disks on port0,1,4 with RAID 5, then Raid driver
|
|
// creates DP with HBAPortNumber as 0x13 (bit0, bit1, bit4) and the PortMultiplierPortNumber
|
|
// as 0x8000 (bit15). (In this case, more than one bits are set in HBAPortNumber.)
|
|
// On the other hand, the UEFI RAID driver also produces the Sata device path
|
|
// for each non-Raid disk, and set the HBAPortNumber as bitmap for the port.
|
|
// For example, if there are two non-Raid disks on port0 and port1, then Raid driver
|
|
// creates the HBAPortNumber as 0x01 (bit0) for port0, and HBAPortNumber as 0x02 (bit1)
|
|
// for port1. Also set the PortMultiplierPortNumber as 0x8000 (bit15).
|
|
// (In this case, only one bit is set in HBAPortNumber.)
|
|
//
|
|
// There are three cases to install the DiskInfo protocol.
|
|
// 1. For Raid disks, DiskInfo driver will new the individual device path for each disk and
|
|
// install the DiskInfo protocol on the handle.
|
|
// 2. For non-Raid disks, DiskInfo driver will install the DiskInfo protocol on the corresponding
|
|
// device path handle.
|
|
// 3. For the disk that is hidden by RAID driver, DiskInfo driver will new a device path for it,
|
|
// and install DiskInfo protocol on the handle. (In the edge case that the RST cache disk.)
|
|
//
|
|
Status = gBS->LocateHandleBuffer (
|
|
ByProtocol,
|
|
&gEfiDevicePathProtocolGuid,
|
|
NULL,
|
|
&HandleCount,
|
|
&HandleBuffer
|
|
);
|
|
ASSERT_EFI_ERROR(Status);
|
|
|
|
if (!EFI_ERROR (Status)) {
|
|
for (Index = 0; Index < HandleCount; Index++) {
|
|
Status = gBS->HandleProtocol (
|
|
HandleBuffer[Index],
|
|
&gEfiDevicePathProtocolGuid,
|
|
(VOID *) &DevicePath
|
|
);
|
|
ASSERT_EFI_ERROR(Status);
|
|
|
|
//
|
|
// Check that parent devicepath is identical.
|
|
//
|
|
Status = GetRemainingDevicePath (ParentDevicePath, DevicePath, &RemainingDevicePath);
|
|
if (EFI_ERROR (Status)) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Check the sata devicepath port number
|
|
//
|
|
DevicePathNode = RemainingDevicePath;
|
|
while (!IsDevicePathEndType (DevicePathNode)) {
|
|
if ((DevicePathNode->Type == MESSAGING_DEVICE_PATH) &&
|
|
(DevicePathNode->SubType == MSG_SATA_DP)) {
|
|
SataDevicePathNode = (SATA_DEVICE_PATH *) DevicePathNode;
|
|
//
|
|
// The RAID driver overloads the Sata port number information.
|
|
// Since customers require a mechanism to communicate which physical devices are represented
|
|
// with each Logical Disk (3 physical disks may be exposed as a RAID5 logical disk).
|
|
// It uses the port number of the SATA DEVICE PATH as a bitmap of the ports.
|
|
//
|
|
if (SataDevicePathNode->HBAPortNumber == (1 << Port)) {
|
|
//
|
|
// Only one bit is set, then this is a non-Raid Disk device path.
|
|
// It's case 2.
|
|
//
|
|
DeviceHandle = HandleBuffer[Index];
|
|
break;
|
|
}
|
|
}
|
|
DevicePathNode = NextDevicePathNode (DevicePathNode);
|
|
}
|
|
|
|
if (DeviceHandle != NULL) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (HandleBuffer != NULL) {
|
|
FreePool (HandleBuffer);
|
|
}
|
|
|
|
if (DeviceHandle != NULL) {
|
|
//
|
|
// If DeviceHandle is available, it's case 2 or the device path is generated earlier.
|
|
// If the DiskInfo protocol is existed, it's already started.
|
|
//
|
|
Status = gBS->HandleProtocol (
|
|
DeviceHandle,
|
|
&gEfiDiskInfoProtocolGuid,
|
|
(VOID **) &DiskInfo
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
Status = EFI_ALREADY_STARTED;
|
|
goto Done;
|
|
}
|
|
} else {
|
|
//
|
|
// If no DeviceHandle is available, it's case 1 or case 3.
|
|
// will generate new device path for this sata port.
|
|
//
|
|
NewDevicePathNode = AllocateCopyPool (sizeof (SATA_DEVICE_PATH), &mSataDevicePathTemplate);
|
|
if (NewDevicePathNode == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// The HBAPortNumbe is bitmap for the port.
|
|
// Set bit 15 of PortMultiplierPortNumber to indicate direct connect.
|
|
//
|
|
((EFI_DEV_PATH *)NewDevicePathNode)->Sata.HBAPortNumber = (1 << Port);
|
|
((EFI_DEV_PATH *)NewDevicePathNode)->Sata.PortMultiplierPortNumber = (0x8000 | PortMultiplierPort);
|
|
((EFI_DEV_PATH *)NewDevicePathNode)->Sata.Lun = 0;
|
|
|
|
DevicePath = AppendDevicePathNode (ParentDevicePath, NewDevicePathNode);
|
|
if (DevicePath == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// Relese the resouce
|
|
//
|
|
if (NewDevicePathNode != NULL) {
|
|
FreePool (NewDevicePathNode);
|
|
}
|
|
//
|
|
// Keep the resouce and need to be released later if error.
|
|
//
|
|
NewDevicePathNode = DevicePath;
|
|
}
|
|
|
|
//
|
|
// Allocate ATA device from the template.
|
|
//
|
|
AtaDevice = AllocateCopyPool (sizeof (mAtaDeviceTemplate), &mAtaDeviceTemplate);
|
|
if (AtaDevice == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// Initializes ATA device structures and allocates the required buffer.
|
|
//
|
|
AtaDevice->Handle = DeviceHandle;
|
|
AtaDevice->DevicePath = DevicePath;
|
|
AtaDevice->Port = Port;
|
|
AtaDevice->PortMultiplierPort = PortMultiplierPort;
|
|
AtaDevice->Asb = AllocateAlignedPages (EFI_SIZE_TO_PAGES (sizeof(EFI_ATA_STATUS_BLOCK)), AtaPassThru->Mode->IoAlign);
|
|
if (AtaDevice->Asb == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto Done;
|
|
}
|
|
AtaDevice->IdentifyData = AllocateAlignedPages (EFI_SIZE_TO_PAGES (sizeof(ATA_IDENTIFY_DATA)), AtaPassThru->Mode->IoAlign);
|
|
if (AtaDevice->IdentifyData == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// Try to identify the ATA device via the ATA pass through command.
|
|
//
|
|
Status = DiscoverAtaDevice (AtaDevice, AtaPassThru);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Done;
|
|
}
|
|
|
|
if (DeviceHandle != NULL) {
|
|
//
|
|
// For case 2, install the DiskInfo protocol on existed device path handle.
|
|
//
|
|
Status = gBS->InstallMultipleProtocolInterfaces (
|
|
&AtaDevice->Handle,
|
|
&gEfiDiskInfoProtocolGuid,
|
|
&AtaDevice->DiskInfo,
|
|
NULL
|
|
);
|
|
} else {
|
|
//
|
|
// For case 1 and case 3, new a handle and install the device path nad DiskInfo protocols.
|
|
//
|
|
Status = gBS->InstallMultipleProtocolInterfaces (
|
|
&AtaDevice->Handle,
|
|
&gEfiDevicePathProtocolGuid,
|
|
AtaDevice->DevicePath,
|
|
&gEfiDiskInfoProtocolGuid,
|
|
&AtaDevice->DiskInfo,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
Done:
|
|
if (EFI_ERROR (Status) && (NewDevicePathNode != NULL)) {
|
|
FreePool (NewDevicePathNode);
|
|
}
|
|
if (EFI_ERROR (Status) && (AtaDevice != NULL)) {
|
|
if (AtaDevice->Asb != NULL) {
|
|
FreePages (AtaDevice->Asb, EFI_SIZE_TO_PAGES (sizeof (EFI_ATA_STATUS_BLOCK)));
|
|
}
|
|
|
|
if (AtaDevice->IdentifyData != NULL) {
|
|
FreePages (AtaDevice->IdentifyData, EFI_SIZE_TO_PAGES (sizeof (ATA_IDENTIFY_DATA)));
|
|
}
|
|
FreePool (AtaDevice);
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
Produce DiskInfo protocol interface for every attached ATA HDD physical device.
|
|
|
|
@param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
|
|
@param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This
|
|
parameter is ignored by device drivers, and is optional for bus
|
|
drivers. For a bus driver, if this parameter is NULL, then handles
|
|
for all the children of Controller are created by this driver.
|
|
If this parameter is not NULL and the first Device Path Node is
|
|
not the End of Device Path Node, then only the handle for the
|
|
child device specified by the first Device Path Node of
|
|
RemainingDevicePath is created by this driver.
|
|
If the first Device Path Node of RemainingDevicePath is
|
|
the End of Device Path Node, no child handle is created by this
|
|
driver.
|
|
|
|
@retval EFI_SUCCESS The device was started.
|
|
@retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented.
|
|
@retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
|
|
@retval Others The driver failded to start the device.
|
|
|
|
**/
|
|
VOID
|
|
DiskInfoForPhyDev (
|
|
IN EFI_HANDLE Controller,
|
|
IN EFI_HANDLE DriverBinding,
|
|
IN EFI_ATA_PASS_THRU_PROTOCOL *AtaPassThru,
|
|
IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT16 Port;
|
|
UINT16 PortMultiplierPort;
|
|
|
|
Port = 0xFFFF;
|
|
while (TRUE) {
|
|
Status = AtaPassThru->GetNextPort (AtaPassThru, &Port);
|
|
if (EFI_ERROR (Status)) {
|
|
//
|
|
// We cannot find more legal port then we are done.
|
|
//
|
|
break;
|
|
}
|
|
|
|
PortMultiplierPort = 0xFFFF;
|
|
while (TRUE) {
|
|
Status = AtaPassThru->GetNextDevice (AtaPassThru, Port, &PortMultiplierPort);
|
|
if (EFI_ERROR (Status)) {
|
|
//
|
|
// We cannot find more legal port multiplier port number for ATA device
|
|
// on the port, then we are done.
|
|
//
|
|
break;
|
|
}
|
|
RegisterAtaHddDevice (Controller, DriverBinding, AtaPassThru, ParentDevicePath, Port, PortMultiplierPort);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Uninstall Orphan DiskInfo protocols.
|
|
// In cases where HDD locked password is not entered (Esc key pressed), RST driver re-enumerates the HDDs.
|
|
// RST driver uninstalls the BlockIo and DevicePath protocol for existing handles but does not uninstall the DiskInfo
|
|
// protocol handles. The UefiRaidDiskInfo driver needs to uninstall the same to handle Orphan DiskInfo handles which
|
|
// wont have a corresponding DevicePath installed.
|
|
//
|
|
UninstallOrphanDiskInfoProtocol ();
|
|
}
|
|
|
|
|
|
/**
|
|
Check whether the driver supports this device.
|
|
|
|
@param This The Udriver binding protocol.
|
|
@param Controller The controller handle to check.
|
|
@param RemainingDevicePath The remaining device path.
|
|
|
|
@retval EFI_SUCCESS The driver supports this controller.
|
|
@retval other This device isn't supported.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
IntelUefiRaidDiskInfoDriverBindingSupported (
|
|
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_ATA_PASS_THRU_PROTOCOL *AtaPassThru;
|
|
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
|
|
PCI_TYPE00 PciData;
|
|
|
|
Status = gBS->OpenProtocol (
|
|
Controller,
|
|
&gEfiAtaPassThruProtocolGuid,
|
|
(VOID **) &AtaPassThru,
|
|
This->DriverBindingHandle,
|
|
Controller,
|
|
//todo EFI_OPEN_PROTOCOL_BY_DRIVER
|
|
EFI_OPEN_PROTOCOL_GET_PROTOCOL
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
Status = gBS->OpenProtocol (
|
|
Controller,
|
|
&gEfiDevicePathProtocolGuid,
|
|
(VOID **) &DevicePath,
|
|
This->DriverBindingHandle,
|
|
Controller,
|
|
EFI_OPEN_PROTOCOL_GET_PROTOCOL
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// Open the IO Abstraction(s) needed to perform the supported test
|
|
//
|
|
Status = gBS->OpenProtocol (
|
|
Controller,
|
|
&gEfiPciIoProtocolGuid,
|
|
(VOID **) &PciIo,
|
|
This->DriverBindingHandle,
|
|
Controller,
|
|
EFI_OPEN_PROTOCOL_GET_PROTOCOL
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Done;
|
|
}
|
|
//
|
|
// Now further check the PCI header: Base class (offset 0x0B) and
|
|
// Sub Class (offset 0x0A). This controller should be an ATA controller
|
|
//
|
|
Status = PciIo->Pci.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint8,
|
|
PCI_CLASSCODE_OFFSET,
|
|
sizeof (PciData.Hdr.ClassCode),
|
|
PciData.Hdr.ClassCode
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_UNSUPPORTED;
|
|
goto Done;
|
|
}
|
|
|
|
Status = EFI_UNSUPPORTED;
|
|
if (IS_PCI_RAID (&PciData)) {
|
|
Status = EFI_SUCCESS;
|
|
}
|
|
|
|
Done:
|
|
gBS->CloseProtocol (
|
|
Controller,
|
|
&gEfiAtaPassThruProtocolGuid,
|
|
This->DriverBindingHandle,
|
|
Controller
|
|
);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Starts the device with this driver.
|
|
|
|
@param This The driver binding instance.
|
|
@param Controller Handle of device to bind driver to.
|
|
@param RemainingDevicePath Optional parameter use to pick a specific child
|
|
device to start.
|
|
|
|
@retval EFI_SUCCESS The controller is controlled by the driver.
|
|
@retval Other This controller cannot be started.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
IntelUefiRaidDiskInfoDriverBindingStart (
|
|
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
|
IN EFI_HANDLE Controller,
|
|
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_DEVICE_PATH_PROTOCOL *PciDevPath;
|
|
EFI_ATA_PASS_THRU_PROTOCOL *AtaPassThru;
|
|
|
|
//
|
|
// Initialize variables
|
|
//
|
|
AtaPassThru = NULL;
|
|
PciDevPath = NULL;
|
|
|
|
Status = gBS->OpenProtocol (
|
|
Controller,
|
|
&gEfiAtaPassThruProtocolGuid,
|
|
(VOID **) &AtaPassThru,
|
|
This->DriverBindingHandle,
|
|
Controller,
|
|
//todo EFI_OPEN_PROTOCOL_BY_DRIVER
|
|
EFI_OPEN_PROTOCOL_GET_PROTOCOL
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = gBS->OpenProtocol (
|
|
Controller,
|
|
&gEfiDevicePathProtocolGuid,
|
|
(VOID **) &PciDevPath,
|
|
This->DriverBindingHandle,
|
|
Controller,
|
|
EFI_OPEN_PROTOCOL_GET_PROTOCOL
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
goto Error;
|
|
}
|
|
|
|
//
|
|
// Produce DiskInfo for every physical ATA HDD devices
|
|
//
|
|
DiskInfoForPhyDev (Controller, This->DriverBindingHandle, AtaPassThru, PciDevPath);
|
|
|
|
Error:
|
|
if (EFI_ERROR (Status)) {
|
|
if (AtaPassThru != NULL) {
|
|
gBS->CloseProtocol (
|
|
Controller,
|
|
&gEfiAtaPassThruProtocolGuid,
|
|
This->DriverBindingHandle,
|
|
Controller
|
|
);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
Stop the device handled by this driver.
|
|
|
|
@param This The driver binding protocol.
|
|
@param Controller The controller to release.
|
|
@param NumberOfChildren The number of handles in ChildHandleBuffer.
|
|
@param ChildHandleBuffer The array of child handle.
|
|
|
|
@retval EFI_SUCCESS The device was stopped.
|
|
@retval EFI_DEVICE_ERROR The device could not be stopped due to a device error.
|
|
@retval Others Fail to uninstall protocols attached on the device.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
IntelUefiRaidDiskInfoDriverBindingStop (
|
|
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
|
IN EFI_HANDLE Controller,
|
|
IN UINTN NumberOfChildren,
|
|
IN EFI_HANDLE *ChildHandleBuffer
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
BOOLEAN AllChildrenStopped;
|
|
EFI_DISK_INFO_PROTOCOL *DiskInfo;
|
|
UINTN Index;
|
|
ATA_DEVICE *AtaDevice;
|
|
|
|
AllChildrenStopped = TRUE;
|
|
|
|
for (Index = 0; Index < NumberOfChildren; Index++) {
|
|
Status = gBS->OpenProtocol (
|
|
ChildHandleBuffer[Index],
|
|
&gEfiDiskInfoProtocolGuid,
|
|
(VOID **) &DiskInfo,
|
|
This->DriverBindingHandle,
|
|
Controller,
|
|
EFI_OPEN_PROTOCOL_GET_PROTOCOL
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
//
|
|
// The child handle is only for physical device. Release Device Path and Disk Info protocols on the child handle.
|
|
//
|
|
AtaDevice = ATA_DEVICE_FROM_DISK_INFO (DiskInfo);
|
|
|
|
gBS->CloseProtocol (
|
|
Controller,
|
|
&gEfiAtaPassThruProtocolGuid,
|
|
This->DriverBindingHandle,
|
|
ChildHandleBuffer[Index]
|
|
);
|
|
|
|
Status = gBS->UninstallMultipleProtocolInterfaces (
|
|
ChildHandleBuffer[Index],
|
|
&gEfiDiskInfoProtocolGuid,
|
|
&AtaDevice->DiskInfo,
|
|
&gEfiDevicePathProtocolGuid,
|
|
AtaDevice->DevicePath,
|
|
NULL
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
if (EFI_ERROR (Status)) {
|
|
AllChildrenStopped = FALSE;
|
|
}
|
|
|
|
if (AtaDevice->Asb != NULL) {
|
|
FreePages (AtaDevice->Asb, EFI_SIZE_TO_PAGES (sizeof (EFI_ATA_STATUS_BLOCK)));
|
|
}
|
|
|
|
if (AtaDevice->IdentifyData != NULL) {
|
|
FreePages (AtaDevice->IdentifyData, EFI_SIZE_TO_PAGES (sizeof (ATA_IDENTIFY_DATA)));
|
|
}
|
|
|
|
if (AtaDevice->DevicePath != NULL) {
|
|
FreePool (AtaDevice->DevicePath);
|
|
}
|
|
|
|
FreePool (AtaDevice);
|
|
}
|
|
}
|
|
|
|
if (!AllChildrenStopped) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
Status = gBS->CloseProtocol (
|
|
Controller,
|
|
&gEfiAtaPassThruProtocolGuid,
|
|
This->DriverBindingHandle,
|
|
Controller
|
|
);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Driver entry point.
|
|
|
|
@param ImageHandle Handle of driver image.
|
|
@param SystemTable Pointer to system table.
|
|
|
|
@retval EFI_SUCCESS Entrypoint successfully executed.
|
|
@retval Others Fail to execute entrypoint.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
IntelUefiRaidDiskInfoEntryPoint (
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable
|
|
)
|
|
{
|
|
PCH_SETUP PchSetup;
|
|
UINTN VarSize;
|
|
EFI_STATUS Status;
|
|
|
|
DEBUG ((DEBUG_INFO, "IntelUefiRaidDiskInfoEntryPoint efi driver\n"));
|
|
|
|
VarSize = sizeof (PCH_SETUP);
|
|
Status = gRT->GetVariable (
|
|
L"PchSetup",
|
|
&gPchSetupVariableGuid,
|
|
NULL,
|
|
&VarSize,
|
|
&PchSetup
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
DEBUG ((DEBUG_INFO, "SataInterfaceMode:%x \n", PchSetup.SataInterfaceMode));
|
|
|
|
if (PchSetup.SataInterfaceMode == SATA_MODE_RAID) {
|
|
//
|
|
// Install protocols
|
|
//
|
|
Status = EfiLibInstallDriverBindingComponentName2 (
|
|
ImageHandle,
|
|
SystemTable,
|
|
&gIntelUefiRaidDiskInfoDriverBinding,
|
|
ImageHandle,
|
|
&gIntelUefiRaidDiskInfoComponentName,
|
|
&gIntelUefiRaidDiskInfoComponentName2
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Uninstalls all Orphan DiskInfo protocols whenever there is a re-enumeration of drives by RST driver (ATA_IDENTIFY)
|
|
|
|
@param VOID No Input parameter
|
|
|
|
@retval VOID No Return Value
|
|
|
|
**/
|
|
VOID
|
|
UninstallOrphanDiskInfoProtocol (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN HandleCount;
|
|
EFI_HANDLE *HandleBuffer;
|
|
EFI_DISK_INFO_PROTOCOL *pDiskInfo;
|
|
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
|
|
UINT8 i;
|
|
|
|
HandleCount = 0;
|
|
HandleBuffer = NULL;
|
|
pDiskInfo = NULL;
|
|
DevicePath = NULL;
|
|
|
|
Status = gBS->LocateHandleBuffer (
|
|
ByProtocol,
|
|
&gEfiDiskInfoProtocolGuid,
|
|
NULL,
|
|
&HandleCount,
|
|
&HandleBuffer
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < HandleCount; i++) {
|
|
//
|
|
// Check for Orphan DiskInfo protocols (i.e. any handle without DevicePath). If any found, uninstall the same.
|
|
//
|
|
Status = gBS->HandleProtocol (
|
|
HandleBuffer[i],
|
|
&gEfiDevicePathProtocolGuid,
|
|
(VOID *) &DevicePath
|
|
);
|
|
|
|
if (Status == EFI_SUCCESS) {
|
|
//
|
|
// Not an orphan. Skip it.
|
|
//
|
|
continue;
|
|
}
|
|
|
|
Status = gBS->HandleProtocol (
|
|
HandleBuffer[i],
|
|
&gEfiDiskInfoProtocolGuid,
|
|
(VOID *) &pDiskInfo
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
//
|
|
// Uninstall Orphan DiskInfo protocol
|
|
//
|
|
Status = gBS->UninstallMultipleProtocolInterfaces (
|
|
HandleBuffer[i],
|
|
&gEfiDiskInfoProtocolGuid,
|
|
pDiskInfo,
|
|
NULL
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
}
|
|
|
|
if (HandleBuffer != NULL) {
|
|
FreePool (HandleBuffer);
|
|
}
|
|
|
|
return;
|
|
}
|