2015年1月5日 星期一

[UEFI/EDK II Sample Code] SATA/sSATA Hard Disk Drive (HDD) Information

This sample demonstrates how to  read the model name of the SATA/sSATA HDD and calculate the capacity of the HDD. There's no need to direct use ATA/ATAPI command any more.

HDDinfo.inf

##
[Defines]
  INF_VERSION                    = 0x00010005
  BASE_NAME                      = HDDinfo
  FILE_GUID                      = 4aA8A36E-ED34-44db-AE97-1FA5E4ED7776
  MODULE_TYPE                    = UEFI_APPLICATION
  VERSION_STRING                 = 1.0
  ENTRY_POINT                    = UefiMain

[Sources]  
 HDDinfo.c

  
[Packages]
  MdePkg/MdePkg.dec
  ShellPkg/ShellPkg.dec
  MdeModulePkg/MdeModulePkg.dec
  
[LibraryClasses]
  UefiBootServicesTableLib
  UefiLib
  UefiRuntimeServicesTableLib
  UefiApplicationEntryPoint
  PrintLib
  DebugLib
  
[Protocols]
  gEfiDiskInfoProtocolGuid

HDDinfo.c

#include <Library/UefiLib.h>
#include <Library/UefiBootServicesTableLib.h> 
#include <Library/MemoryAllocationLib.h>
#include <Library/DebugLib.h>
#include <Protocol/DiskInfo.h>
#include <Protocol/IdeControllerInit.h>
#include <Include/Base.h>

#define MODEL_NAME_SIZE      40
#define SECTOR_SIZE       512
#define BYTE_SIZE       8
#define MILLION        1000000
#define KILOBYTE       1024
#define MEGABYTE       (1024*1024)
#define GIGABYTE       (1024*1024*1024)
#define TERABYTE       (1024*1024*1024*1024)
#define MAX_VALUE_48_BIT_ADDRESSING   0x0FFFFFFFFFFFF

typedef enum {SATADrive,sSATADrive,SASDrive,OtherDrive} Storage_Drive_Type;
typedef struct{
 //I didn't implement a function to identify the current port number in this sample.
 UINT8    PortNumber;
 //I didn't implement a function to identify the Storage_Drive_Type in this sample.
 Storage_Drive_Type  DriveType;  
 CHAR16    DriveName[40];
 UINTN               DriveCapacity;  //Byte
} Storage_Drive_Information;


VOID
AsciiToUnicode (CHAR8 *AsciiString,CHAR16 *UnicodeString)
{
  UINT8 i;

  i = 0;
  while (AsciiString[i] != 0) {
    UnicodeString[i] = (CHAR16) AsciiString[i];
    i++;
  }
  UnicodeString[i] = '\0';
}

VOID Swap(UINT8* a,UINT8* b)
{
 UINT8 temp;
 temp = *b;
 *b = *a;
 *a = temp;
}

VOID InitIdentifyDriveInformation(EFI_DISK_INFO_PROTOCOL *DiskInfo,EFI_IDENTIFY_DATA *IdentifyDriveInfo)
{
 UINT32    Size = sizeof (EFI_IDENTIFY_DATA);
 EFI_STATUS    Status;
 Status = DiskInfo->Identify (
                DiskInfo,
                IdentifyDriveInfo,
                &Size
                );
 ASSERT_EFI_ERROR(Status);
}

VOID GetStorageDeviceName(EFI_IDENTIFY_DATA *IdentifyDriveInfo,Storage_Drive_Information* DriveInfo)
{
 UINTN     i;
 
 for(i = 0;i < sizeof(IdentifyDriveInfo->AtaData.ModelName);i += 2)
  Swap(&IdentifyDriveInfo->AtaData.ModelName[i],&IdentifyDriveInfo->AtaData.ModelName[i+1]);
 AsciiToUnicode ((CHAR8 *) &IdentifyDriveInfo->AtaData.ModelName, DriveInfo->DriveName);
 
 for(i = 0;i < MODEL_NAME_SIZE; i++)
  if(DriveInfo->DriveName[i]==0x20 && DriveInfo->DriveName[i+1]==0x20){
   DriveInfo->DriveName[i] = '\0';
   break;
 }
}

UINTN GetStorageDeviceSize(EFI_IDENTIFY_DATA  *IdentifyDriveInfo)
{
 UINTN                  DriveCapacity = 0;
 UINTN       offset;
 UINTN       ShiftCount;
 //Devices that conform to this standard shall clear bit 15 to zero.
 if ((IdentifyDriveInfo->AtaData.config & BIT15) == 0) {
  //48-bit Address feature set is supported
  if (IdentifyDriveInfo->AtaData.command_set_supported_83 & BIT10) {
   ShiftCount = sizeof(IdentifyDriveInfo->AtaData.maximum_lba_for_48bit_addressing[0]) * BYTE_SIZE;
   DriveCapacity = IdentifyDriveInfo->AtaData.maximum_lba_for_48bit_addressing[0];
   for (offset = 1; offset < (sizeof(IdentifyDriveInfo->AtaData.maximum_lba_for_48bit_addressing)/ 
       sizeof(IdentifyDriveInfo->AtaData.maximum_lba_for_48bit_addressing[0])); offset++) { 
    DriveCapacity |= (UINTN)IdentifyDriveInfo->AtaData.maximum_lba_for_48bit_addressing[offset] <<
        (ShiftCount * offset);
   }
   DriveCapacity = (DriveCapacity&MAX_VALUE_48_BIT_ADDRESSING) * SECTOR_SIZE;
  } 
  else {
   ShiftCount = sizeof(IdentifyDriveInfo->AtaData.user_addressable_sectors_hi) * BYTE_SIZE;
   DriveCapacity =(IdentifyDriveInfo->AtaData.user_addressable_sectors_hi << ShiftCount) |
       IdentifyDriveInfo->AtaData.user_addressable_sectors_lo * SECTOR_SIZE;
  } 
 }
 return DriveCapacity;
}

EFI_STATUS
EFIAPI
UefiMain (
          IN EFI_HANDLE        ImageHandle,
          IN EFI_SYSTEM_TABLE  *SystemTable
          ) 
{    
 EFI_STATUS      Status;
 EFI_IDENTIFY_DATA   *IdentifyDriveInfo = NULL;
 UINTN                    HandleCount;
 EFI_HANDLE               *HandleBuffer;
 EFI_DISK_INFO_PROTOCOL   *DiskInfo;
 UINTN                    Index;
 Storage_Drive_Information *DriveInfo;

 Status = gBS->LocateHandleBuffer (
   ByProtocol,
   &gEfiDiskInfoProtocolGuid,
   NULL,
   &HandleCount,
   &HandleBuffer
   );
 ASSERT_EFI_ERROR(Status);
 
 DriveInfo = AllocatePool (sizeof(Storage_Drive_Information)*HandleCount);
 for (Index = 0; Index < HandleCount; Index++) {
  Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiDiskInfoProtocolGuid, &DiskInfo);
  ASSERT_EFI_ERROR(Status); 
  IdentifyDriveInfo = AllocatePool (sizeof (EFI_IDENTIFY_DATA));
  InitIdentifyDriveInformation(DiskInfo,IdentifyDriveInfo);
  GetStorageDeviceName(IdentifyDriveInfo,&DriveInfo[Index]);
  DriveInfo[Index].DriveCapacity  = GetStorageDeviceSize(IdentifyDriveInfo);
  gBS->FreePool (IdentifyDriveInfo);
 }
 for(Index = 0; Index < HandleCount;Index ++)
 {
  Print(L"Drive Model Name: %s , ",DriveInfo[Index].DriveName);
  Print(L"Drive Size: %d GB\n",DriveInfo[Index].DriveCapacity/GIGABYTE);
 }
 
 gBS->FreePool(DriveInfo);
    return EFI_SUCCESS;
}

Execution Result:
Fig. 1. Result.
 Q: How to read the current port number and identify the drive type?
 A: Please check the chipset  Spec. of your platform and use EFI_DISK_INFO_PROTOCOL (WhichIde() function) as well as EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL.

 Reference:
 1. ATA_IDENTIFY_DATA Struct Reference
 2. AT Attachment 8 - ATA/ATAPI Command Set (ATA8-ACS)
 3. UEFI/PI Spec

4 則留言:

  1. Hi, I was reading this and it is very informative and useful.
    I have one question, as I have not being able get the Port Number for the SATA Drive; How do you get the Port Number?

    回覆刪除
    回覆
    1. Hi, It is based on your platform. So you should check the chipsets specification of your platform, and then, you can use the protocol to get the port number.

      刪除
  2. Hello Wei 大大,

    If i want to implement port number, i need what information to decode?

    回覆刪除
    回覆
    1. 每個平台的讀法不盡相同,得看Spec.才能知道

      刪除