summaryrefslogtreecommitdiff
path: root/Core/EM/Ahci/AhciInt13Smm.c
diff options
context:
space:
mode:
Diffstat (limited to 'Core/EM/Ahci/AhciInt13Smm.c')
-rw-r--r--Core/EM/Ahci/AhciInt13Smm.c1157
1 files changed, 1157 insertions, 0 deletions
diff --git a/Core/EM/Ahci/AhciInt13Smm.c b/Core/EM/Ahci/AhciInt13Smm.c
new file mode 100644
index 0000000..8715bce
--- /dev/null
+++ b/Core/EM/Ahci/AhciInt13Smm.c
@@ -0,0 +1,1157 @@
+//**********************************************************************
+//**********************************************************************
+//** **
+//** (C)Copyright 1985-2014, American Megatrends, Inc. **
+//** **
+//** All Rights Reserved. **
+//** **
+//** 5555 Oakbrook Pkwy, Suite 200, Norcross, GA 30093 **
+//** **
+//** Phone: (770)-246-8600 **
+//** **
+//**********************************************************************
+//**********************************************************************
+//**********************************************************************
+// $Header: /Alaska/SOURCE/Modules/AHCI/INT13/AhciInt13Smm.c 2 12/08/14 5:39a Anbuprakashp $Revision:
+//
+// $Date: 12/08/14 5:39a $Log: /Alaska/SOURCE/Modules/AHCI/INT13/AhciInt13Smm.c $
+//
+//
+//
+//****************************************************************************
+
+//<AMI_FHDR_START>
+//****************************************************************************
+//
+// Name: AhciInt13Smm.C
+//
+// Description: This file contains code for SMI handler for AHCI INT13.
+//****************************************************************************
+//<AMI_FHDR_END>
+
+#include <Token.h>
+#include <AmiDxeLib.h>
+#include <AmiBufferValidationLib.h>
+
+#if defined(PI_SPECIFICATION_VERSION)&&(PI_SPECIFICATION_VERSION>=0x0001000A)&&(CORE_COMBINED_VERSION>=0x4028B)
+#include <Protocol/SmmBase2.h>
+#include <Protocol/SmmSwDispatch2.h>
+#include <Protocol/SmmCpu.h>
+#include <Protocol/LegacyBios.h>
+#include <Protocol/PciIo.h>
+#include <Protocol/BlockIo.h>
+#include <Protocol/PDiskInfo.h>
+#include <Protocol/PIDEController.h>
+#include <Protocol/PIDEBus.h>
+#include <Protocol/AhciSmmProtocol.h>
+#include "AhciInt13Smm.h"
+
+EFI_SMM_CPU_PROTOCOL *gSmmCpuProtocol = NULL;
+AHCI_BUS_SMM_PROTOCOL *gAhciBusSmmProtocol = NULL;
+DLIST gDriveInfoList;
+EFI_GUID gAint13SmmDataGuid = AHCI_INT13_SMM_DATA_GUID;
+EFI_GUID gAhciSmmProtocolGuid = AHCI_SMM_PROTOCOL_GUID;
+EFI_GUID gEfiSmmCpuProtocolGuid = EFI_SMM_CPU_PROTOCOL_GUID;
+EFI_GUID gEfiSmmSwDispatch2ProtocolGuid = EFI_SMM_SW_DISPATCH2_PROTOCOL_GUID;
+UINT64 PciExpressBaseAddress = 0;
+UINT8 *gBuffer = NULL;
+#endif
+
+//<AMI_PHDR_START>
+//----------------------------------------------------------------------
+// Procedure: AhciMmioRead
+//
+// Description: Read from AHCI MMIO address
+//
+// Input: IN UINT32 - AHCI MMIO address
+//
+// Output: OUT UINT32 - Value read from AHCI MMIO address
+//
+//----------------------------------------------------------------------
+//<AMI_PHDR_END>
+
+EFI_STATUS
+AhciMmioRead (
+ IN UINT32 AhciMmioAddress,
+ OUT UINT32 *ReadValue
+)
+{
+ EFI_STATUS Status;
+ // Validate AhciBaseAddress is valid MMIO address and not reside in SMRAM region
+ Status = AmiValidateMmioBuffer( (VOID*)AhciMmioAddress, 4 );
+ if( EFI_ERROR(Status) ) {
+ return Status;
+ }
+
+ *ReadValue = *(UINT32*)(AhciMmioAddress);
+ return Status;
+}
+
+//<AMI_PHDR_START>
+//----------------------------------------------------------------------
+// Procedure: AhciMmioWrite
+//
+// Description: Write to the AHCI MMIO Address
+//
+// Input: IN UINT32 - AHCI MMIO address
+// IN UINT32 - Value to be written
+//
+// Output: EFI_STATUS - EFI_NOT_FOUND: Invalid address, EFI_SUCCESS: Success
+//
+//----------------------------------------------------------------------
+//<AMI_PHDR_END>
+
+EFI_STATUS
+AhciMmioWrite (
+ IN UINT32 AhciMmioAddress,
+ IN UINT32 WriteValue
+)
+{
+ EFI_STATUS Status;
+
+ // Validate AhciBaseAddress is valid MMIO address and not reside in SMRAM region
+ Status = AmiValidateMmioBuffer( (VOID*)AhciMmioAddress, 4 );
+ if( EFI_ERROR(Status) ) {
+ return Status;
+ }
+
+ *(UINT32*)(AhciMmioAddress) = WriteValue;
+ return Status;
+}
+
+#if defined(PI_SPECIFICATION_VERSION)&&(PI_SPECIFICATION_VERSION>=0x0001000A)&&(CORE_COMBINED_VERSION>=0x4028B)
+
+//---------------------------------------------------------------------------
+
+//<AMI_PHDR_START>
+//---------------------------------------------------------------------------
+//
+// Name: Is48BitCommand
+//
+// Description: Check if input command is a LBA48 command
+//
+// Input: UINT8 - Command
+//
+// Output: BOOLEAN - TRUE - LBA48 command
+// FALSE - Not a LBA48 command
+//
+//---------------------------------------------------------------------------
+//<AMI_PHDR_END>
+
+BOOLEAN
+Is48BitCommand (
+ IN UINT8 Command
+ )
+{
+ if ( Command == READ_SECTORS_EXT ||
+ Command == READ_MULTIPLE_EXT ||
+ Command == WRITE_SECTORS_EXT ||
+ Command == WRITE_MULTIPLE_EXT ||
+ Command == READ_DMA_EXT ||
+ Command == WRITE_DMA_EXT )
+ return TRUE;
+ else
+ return FALSE;
+}
+
+//<AMI_PHDR_START>
+//---------------------------------------------------------------------------
+//
+// Name: IsDmaCommand
+//
+// Description: Check if input command is a DMA command
+//
+// Input: UINT8 - Command
+//
+// Output: BOOLEAN - TRUE - DMA command
+// FALSE - Not a DMA command
+//
+//---------------------------------------------------------------------------
+//<AMI_PHDR_END>
+
+BOOLEAN
+IsDmaCommand (
+ IN UINT8 Command
+ )
+{
+ if ( Command == READ_DMA ||
+ Command == READ_DMA_EXT ||
+ Command == WRITE_DMA ||
+ Command == WRITE_DMA_EXT )
+ return TRUE;
+ else
+ return FALSE;
+}
+
+//<AMI_PHDR_START>
+//---------------------------------------------------------------------------
+//
+// Name: CheckErrorCode
+//
+// Description: It maps EFI_STATUS code to corresponding INT13 error code
+//
+// Input: EFI_STATUS - Status
+//
+// Output: UINT8 - INT13 error code
+//
+//---------------------------------------------------------------------------
+//<AMI_PHDR_END>
+
+UINT8
+CheckErrorCode (
+ IN EFI_STATUS Status
+ )
+{
+ switch(Status){
+ case EFI_SUCCESS:
+ return 0x0; // successful completion
+ break;
+ case EFI_INVALID_PARAMETER:
+ return 0x01; // invalid function in AH or invalid parameter
+ break;
+ case EFI_UNSUPPORTED:
+ return 0x01; // invalid function in AH or invalid parameter
+ break;
+ case EFI_NOT_READY:
+ return 0xAA; // drive not ready (hard disk)
+ break;
+ case EFI_DEVICE_ERROR:
+ return 0xE0; // status register error (hard disk)
+ break;
+ case EFI_WRITE_PROTECTED:
+ return 0x03; // disk write-protected
+ break;
+ case EFI_NO_MEDIA:
+ return 0x31; // no media in drive (IBM/MS INT 13 extensions)
+ break;
+ case EFI_MEDIA_CHANGED:
+ return 0x06; // disk changed
+ break;
+ case EFI_NOT_FOUND:
+ return 0x01; // invalid function in AH or invalid parameter
+ break;
+ case EFI_ACCESS_DENIED:
+ return 0xB6; // volume present but read protected (INT 13 extensions)
+ break;
+ case EFI_TIMEOUT:
+ return 0x80; // timeout (not ready)
+ break;
+ case EFI_ABORTED:
+ return 0xBB; // undefined error (hard disk)
+ break;
+ default:
+ break;
+ }
+ return 0xBB;
+}
+
+//<AMI_PHDR_START>
+//---------------------------------------------------------------------------
+//
+// Name: GetDriveInfoByDriveNum
+//
+// Description: It returns the drive information corresponding to input drive
+// number, if found.
+//
+// Input: IN UINT8 - INT13 drive number
+// IN OUT VOID** - Pointer to SMM_AINT13_DRIVE_INFO variable
+// It will be filled with corresponding drive
+// information
+//
+// Output: EFI_STATUS
+// EFI_SUCCESS - DriveInfo is valid
+// EFI_UNSUPPORTED - Can't find the corresponding data.
+//
+//---------------------------------------------------------------------------
+//<AMI_PHDR_END>
+
+EFI_STATUS
+GetDriveInfoByDriveNum(
+ IN UINT8 DriveNum,
+ IN OUT VOID **DriveInfo
+)
+{
+ DLINK *DriveInfoLink = NULL;
+ SMM_AINT13_DRIVE_INFO *pDriveInfo = NULL;
+
+ // Look for drive information corresponding to DriveNum in gDriveInfoList
+ DriveInfoLink = gDriveInfoList.pHead;
+ for(;DriveInfoLink;DriveInfoLink=DriveInfoLink->pNext){
+ pDriveInfo = OUTTER(DriveInfoLink, dLink, SMM_AINT13_DRIVE_INFO);
+ if(DriveNum == pDriveInfo->DriveNum) {
+ // Return the information if found, also set status as success
+ *DriveInfo = pDriveInfo;
+ return EFI_SUCCESS;
+ }
+ }
+
+ // No drive information corresponding to DriveNum in gDriveInfoList
+ return EFI_UNSUPPORTED;
+}
+
+//<AMI_PHDR_START>
+//---------------------------------------------------------------------------
+//
+// Name: ProcessInt13Function
+//
+// Description: Worker function to service AHCI INT13 request. Currently it
+// supports Read/Write function only.
+// Operation:
+// 1. Parse information passed as IA registers to parameter required
+// by AMI_AHCI_BUS_SMM_PROTOCOL APIs.
+// 2. Call appropriate AMI_AHCI_BUS_SMM_PROTOCOL API.
+//
+// Input: EFI_IA32_REGISTER_SET* - Pointer of EFI_IA32_REGISTER_SET
+//
+// Output: EFI_STATUS
+// EFI_SUCCESS - Int13 request complete
+// EFI_UNSUPPORTED - This Int13 request is unsupported
+//
+//---------------------------------------------------------------------------
+//<AMI_PHDR_END>
+
+EFI_STATUS
+ProcessInt13Function(
+ IN EFI_IA32_REGISTER_SET *ExRegs
+)
+{
+ EFI_STATUS Status;
+ BOOLEAN IsSupported = TRUE;
+ VOID *Buffer = NULL;
+ UINT8 *BufferBackup = NULL;
+ UINT32 ByteCount = 0;
+ UINT16 SectorCount = 0;
+ UINT8 Command = 0;
+ EFI_LBA Lba = 0;
+ UINT16 SkipBytesBefore = 0, SkipBytesAfter = 0;
+ UINT16 Header = 0, Cylinder = 0, Sector = 0;
+ UINT8 ReadWrite = 0; // 0 : read, 1: write
+ UINT8 *UserBuf = NULL;
+ UINT64 bAhciBaseAddress = 0;
+ UINTN i = 0;
+ UINT16 BlksPerTransfer;
+ VOID *AhciBuffer;
+ BOOLEAN UnalignedTransfer = FALSE;
+ DISK_ADDRESS_PACKAGE *Package = NULL;
+ SMM_AINT13_DRIVE_INFO *pDriveInfo = NULL;
+ COMMAND_STRUCTURE CommandStructure;
+
+ // Get drive information based on drive number
+ Status = GetDriveInfoByDriveNum(ExRegs->H.DL,&pDriveInfo);
+
+ if(!EFI_ERROR(Status)){
+ if(pDriveInfo->DeviceType == ATA) {
+ // Calculate AHCI parameter to be filled in COMMAND_STRUCTURE based on INT13 function
+ if(ExRegs->H.AH == READ_SECTOR || ExRegs->H.AH == EXT_READ){
+ Command = pDriveInfo->RCommand;
+ ReadWrite = 0; // read
+ }else if(ExRegs->H.AH == WRITE_SECTOR || ExRegs->H.AH == EXT_WRITE){
+ Command = pDriveInfo->WCommand;
+ ReadWrite = 1; // write
+ }
+ switch(ExRegs->H.AH){
+ case READ_SECTOR:
+ case WRITE_SECTOR:
+ Cylinder = ((UINT16)(ExRegs->H.CL & 0xC0 ) << 2) +ExRegs->H.CH; // cylinder: bit 6-7(CL) + CH
+ Header = (UINT16)ExRegs->H.DH; // header : DH
+ Sector = (UINT16)(ExRegs->H.CL & 0x3F); // sector : bit 0-5(CL)
+ Lba = (Cylinder*(pDriveInfo->bMAXHN) + Header) * (pDriveInfo->bMAXSN) + Sector - 1;
+ SectorCount = ExRegs->H.AL;
+ Buffer = (VOID*)(((ExRegs->X.ES) << 4 ) + ExRegs->X.BX);
+ ByteCount = SectorCount*HDD_BLOCK_SIZE;
+ break;
+ case EXT_READ:
+ case EXT_WRITE:
+ Package = (DISK_ADDRESS_PACKAGE*)(((ExRegs->X.DS) << 4 ) + ExRegs->X.SI);
+ Lba = Package->StartLba;
+ SectorCount = Package->XferSector;
+ Buffer = (VOID*)(((Package->Buffer >> 16 & 0xFFFF) << 4) + (UINT16)Package->Buffer);
+ ByteCount = SectorCount*HDD_BLOCK_SIZE;
+ break;
+ default:
+ IsSupported = FALSE;
+ break;
+ }
+ } else if(pDriveInfo->DeviceType == ATAPI){ // Only read command support is required
+ Command = pDriveInfo->RCommand;
+ ReadWrite = 0; // read
+ Buffer = (VOID*)((((ExRegs->E.EDI) >> 16 & 0xFFFF) << 4) + (UINT16)(ExRegs->E.EDI));
+ Lba = ExRegs->E.EAX;
+ SectorCount = ExRegs->X.CX; // CX
+ SkipBytesAfter = ((ExRegs->E.ECX) >> 24) * 512; // CH+ (Higher byte of higher word of ECX)
+ SkipBytesBefore = (((ExRegs->E.ECX) >> 16) & 0xFF) * 512; // CL+ (Lower byte of higher word of ECX)
+ ByteCount = SectorCount * pDriveInfo->BlockSize; // 2048
+ if(SkipBytesBefore || SkipBytesAfter) {
+ Status = pSmst->SmmAllocatePool(EfiRuntimeServicesData, sizeof(UINT8)*(SkipBytesBefore + SkipBytesAfter), &BufferBackup);
+ if (EFI_ERROR(Status)) {
+ ASSERT(TRUE);
+ IsSupported = FALSE;
+ } else {
+ // Backup bytes to be preserved.
+ for(i = 0;i<(SkipBytesBefore + SkipBytesAfter);i++) {
+ BufferBackup[i] = *(((UINT8*)Buffer)+i + (ByteCount - SkipBytesBefore - SkipBytesAfter));
+ }
+ }
+ }
+ } else {
+ IsSupported = FALSE;
+ }
+ } // if(!EFI_ERROR(Status))
+ else {
+ IsSupported = FALSE;
+ }
+
+ if(IsSupported){
+ // Backup AHCI base address from gAhciBusSmmProtocol
+ bAhciBaseAddress = gAhciBusSmmProtocol->AhciBaseAddress;
+
+ // Save current AHCI base address from AHCI controller.
+ gAhciBusSmmProtocol->AhciBaseAddress = *(UINT32*)PCI_CFG_ADDR(pDriveInfo->BusNo, pDriveInfo->DevNo, pDriveInfo->FuncNo, PCI_ABAR);
+
+ BlksPerTransfer = SectorCount;
+ AhciBuffer = Buffer;
+
+ //If Buffer isn't aligned use internal buffer
+ if(((UINT32)Buffer) & 0x1) {
+ BlksPerTransfer = 1;
+ AhciBuffer = gBuffer;
+ UnalignedTransfer = TRUE;
+ }
+
+ if(pDriveInfo->DeviceType == ATA) {
+ ByteCount = BlksPerTransfer * HDD_BLOCK_SIZE;
+ } else if(pDriveInfo->DeviceType == ATAPI){
+ ByteCount = BlksPerTransfer * pDriveInfo->BlockSize;
+ }
+
+ UserBuf = (UINT8*)Buffer;
+
+ for ( ; SectorCount; SectorCount -= BlksPerTransfer){
+
+ if (ReadWrite == 1 && UnalignedTransfer) {
+ for(i = 0; i < ByteCount; i++) {
+ *(((UINT8*)AhciBuffer)+i) = *(((UINT8*)Buffer)+i);
+ }
+ }
+
+ // clear Command structure
+ MemSet (&CommandStructure, sizeof(COMMAND_STRUCTURE), 0);
+
+ // Fill CommandStructure buffer.
+ CommandStructure.Buffer = AhciBuffer;
+ CommandStructure.ByteCount = ByteCount;
+ if(pDriveInfo->DeviceType == ATA) { // ATA
+ CommandStructure.Features = 0;
+ CommandStructure.FeaturesExp = 0;
+ CommandStructure.SectorCount = BlksPerTransfer;
+ CommandStructure.LBALow = (UINT8)Lba;
+ CommandStructure.LBAMid = (UINT8) (((UINT32)Lba >>8) & 0xff);
+ CommandStructure.LBAHigh = (UINT8) (((UINT32)Lba >>16) & 0xff);
+ if(Is48BitCommand(Command)){ // if support LBA48 feature?
+ CommandStructure.LBALowExp = (UINT8) (UINT8)Shr64(Lba,24);
+ CommandStructure.LBAMidExp = (UINT8) (UINT8)Shr64(Lba,32);
+ CommandStructure.LBAHighExp = (UINT8) (UINT8)Shr64(Lba,40);
+ CommandStructure.Device = 0x40; // LBA48
+ }else{
+ CommandStructure.Device = ((UINT8)Shr64(Lba,24) & 0x0f) | 0x40; // LBA28
+ }
+ CommandStructure.Command = Command;
+ CommandStructure.Control = 0;
+ } else if(pDriveInfo->DeviceType == ATAPI) {
+ CommandStructure.AtapiCmd.Ahci_Atapi_Command[0] = Command;
+ CommandStructure.AtapiCmd.Ahci_Atapi_Command[1] = pDriveInfo->Lun << 5;
+ CommandStructure.AtapiCmd.Ahci_Atapi_Command[2] = (UINT8)(((UINT32) Lba) >> 24);
+ CommandStructure.AtapiCmd.Ahci_Atapi_Command[3] = (UINT8)(((UINT32) Lba) >> 16);
+ CommandStructure.AtapiCmd.Ahci_Atapi_Command[4] = (UINT8)(((UINT16) Lba) >> 8);
+ CommandStructure.AtapiCmd.Ahci_Atapi_Command[5] = (UINT8)(((UINT8) Lba) & 0xff);
+ CommandStructure.AtapiCmd.Ahci_Atapi_Command[7] = (UINT8) (BlksPerTransfer >> 8); // MSB
+ CommandStructure.AtapiCmd.Ahci_Atapi_Command[8] = (UINT8) (BlksPerTransfer & 0xff); // LSB
+ }
+
+ // Send ATA/ATAPI command in AHCI mode
+ if(pDriveInfo->DeviceType == ATA) { // ATA
+ if(IsDmaCommand(Command)) {
+ Status = gAhciBusSmmProtocol->AhciSmmExecuteDmaDataCommand( gAhciBusSmmProtocol,
+ &CommandStructure,
+ pDriveInfo->PortNum,
+ pDriveInfo->PMPortNum,
+ pDriveInfo->DeviceType,
+ ReadWrite);
+ } else {
+ Status = gAhciBusSmmProtocol->AhciSmmExecutePioDataCommand( gAhciBusSmmProtocol,
+ &CommandStructure,
+ pDriveInfo->PortNum,
+ pDriveInfo->PMPortNum,
+ pDriveInfo->DeviceType,
+ ReadWrite);
+ }
+ } else { // ATAPI
+ Status = gAhciBusSmmProtocol->AhciSmmExecutePacketCommand( gAhciBusSmmProtocol,
+ &CommandStructure,
+ ReadWrite,
+ pDriveInfo->PortNum,
+ pDriveInfo->PMPortNum,
+ pDriveInfo->DeviceType);
+ }
+
+ if (EFI_ERROR(Status)) {
+ break;
+ }
+
+ if (ReadWrite == 0 && UnalignedTransfer) {
+ for(i = 0; i < ByteCount; i++) {
+ *(((UINT8*)Buffer)+i) = *(((UINT8*)AhciBuffer)+i);
+ }
+ }
+
+ (UINTN)Buffer = (UINTN)Buffer + ByteCount;
+ Lba += BlksPerTransfer;
+
+ }
+
+ Buffer = UserBuf;
+
+ // Restore base address to gAhciBusSmmProtocol
+ gAhciBusSmmProtocol->AhciBaseAddress = bAhciBaseAddress;
+
+ if(pDriveInfo->DeviceType == ATAPI){
+ // fill output buffer with requested data only.
+ if(SkipBytesBefore || SkipBytesAfter) {
+ UserBuf = (UINT8*)Buffer;
+ // Move requested data at start of the buffer
+ if(SkipBytesBefore != 0)
+ for(i = 0;i<ByteCount - SkipBytesBefore - SkipBytesAfter;i++) {
+ UserBuf[i] = UserBuf[i+SkipBytesBefore];
+ }
+ // Keep rest of the buffer intact. Restore the backup.
+ for(i = 0;i<SkipBytesBefore + SkipBytesAfter;i++) {
+ UserBuf[i + (ByteCount - SkipBytesBefore - SkipBytesAfter)] = BufferBackup[i];
+ }
+ pSmst->SmmFreePool(BufferBackup);
+ }
+ }
+
+ // update return register data whatever success or error!!
+ if(!EFI_ERROR(Status)){
+ // AHCI success
+ ExRegs->X.Flags.CF = 0x0; // clear if successful
+ ExRegs->H.AH = CheckErrorCode(Status); // successful completion
+ }
+ else{
+ // AHCI error
+ ExRegs->X.Flags.CF = 0x1; // set on error
+ ExRegs->H.AH = CheckErrorCode(Status); // return error code
+ }
+ }
+
+ // return EFI_SUCCESS: Int13 request is complete.
+ // return EFI_UNSUPPORTED: This function isn't supported by this routine.
+ return (IsSupported)? EFI_SUCCESS : EFI_UNSUPPORTED;
+}
+
+//<AMI_PHDR_START>
+//---------------------------------------------------------------------------
+//
+// Name: AhciInt13SmiHandler
+//
+// Description: This is the SWSMI handler to service AHCI INT13 request.
+// Operation:
+// 1. Take INT13 parameters stored on real mode stack from CPU save state.
+// 2. Call a sub-function to process INT13 request.
+// 3. Update output parameters (IA registers) on real mode stack.
+//
+// Input: UINTN - Index of CPU which triggered SW SMI
+//
+// Output: EFI_STATUS
+//
+//---------------------------------------------------------------------------
+//<AMI_PHDR_END>
+
+EFI_STATUS
+AhciInt13SmiHandler (
+ IN UINTN CpuIndex
+)
+{
+ EFI_STATUS Status = EFI_SUCCESS;
+ UINT16 StackSegment = 0;
+ UINT16 StackOffset = 0;
+ EFI_IA32_REGISTER_SET ExRegs;
+ INT13_TO_SMI_EXREGS *Int13ToSmiExRegs = NULL;
+TRACE((-1, "\nKAPIL: AhciInt13SmiHandler."));
+ // Read SS/ESP from CPU save state
+ gSmmCpuProtocol->ReadSaveState ( gSmmCpuProtocol,
+ 2,
+ EFI_SMM_SAVE_STATE_REGISTER_RSP,
+ CpuIndex,
+ &StackOffset );
+
+ gSmmCpuProtocol->ReadSaveState ( gSmmCpuProtocol,
+ 2,
+ EFI_SMM_SAVE_STATE_REGISTER_SS,
+ CpuIndex,
+ &StackSegment );
+
+ // Get base address of real mode stack
+ Int13ToSmiExRegs = (INT13_TO_SMI_EXREGS*)(((StackSegment << 4) + StackOffset) + 2);
+
+ MemSet (&ExRegs, sizeof(EFI_IA32_REGISTER_SET), 0);
+
+ // Initialize the SMM THUNK registers
+ ExRegs.E.EAX = Int13ToSmiExRegs->StackEAX;
+ ExRegs.E.EBX = Int13ToSmiExRegs->StackEBX;
+ ExRegs.E.ECX = Int13ToSmiExRegs->StackECX;
+ ExRegs.E.EDX = Int13ToSmiExRegs->StackEDX;
+ ExRegs.E.EDI = Int13ToSmiExRegs->StackEDI;
+ ExRegs.E.ESI = Int13ToSmiExRegs->StackESI;
+ ExRegs.E.EBP = Int13ToSmiExRegs->StackEBP;
+ ExRegs.E.DS = Int13ToSmiExRegs->StackDS;
+ ExRegs.E.ES = Int13ToSmiExRegs->StackES;
+ ExRegs.E.FS = Int13ToSmiExRegs->StackFS;
+ ExRegs.E.GS = Int13ToSmiExRegs->StackGS;
+ ExRegs.X.Flags = Int13ToSmiExRegs->StackFlags;
+
+ if(gAhciBusSmmProtocol && gDriveInfoList.pHead){
+ // Execute Int13 function by AhciSmmProtocol and update ExRegs for return caller.
+ // Note: Function will return non-EFI_SUCCESS value if Int13 function isn't
+ // supported by ProcessInt13Function().
+ Status = ProcessInt13Function(&ExRegs);
+ }
+
+ // Update the registers before go back caller.
+ Int13ToSmiExRegs->StackEAX = ExRegs.E.EAX;
+ Int13ToSmiExRegs->StackEBX = ExRegs.E.EBX;
+ Int13ToSmiExRegs->StackECX = ExRegs.E.ECX;
+ Int13ToSmiExRegs->StackEDX = ExRegs.E.EDX;
+ Int13ToSmiExRegs->StackEDI = ExRegs.E.EDI;
+ Int13ToSmiExRegs->StackESI = ExRegs.E.ESI;
+ Int13ToSmiExRegs->StackEBP = ExRegs.E.EBP;
+ Int13ToSmiExRegs->StackDS = ExRegs.E.DS;
+ Int13ToSmiExRegs->StackES = ExRegs.E.ES;
+ Int13ToSmiExRegs->StackFS = ExRegs.E.FS;
+ Int13ToSmiExRegs->StackGS = ExRegs.E.GS;
+ Int13ToSmiExRegs->StackFlags = ExRegs.X.Flags;
+
+ return Status;
+}
+
+//<AMI_PHDR_START>
+//----------------------------------------------------------------------
+// Procedure: AhciMmioSmiHandler
+//
+// Description: SMI handler for the AHCI_MMIO_SWSMI SW SMI
+//
+// Input: IN UINTN - Index of CPU which triggered SW SMI
+// IN UINT32 - 1/2: Read or Write MMIO operation
+//
+// Output: EFI_STATUS - EFI_SUCCESS
+//
+//----------------------------------------------------------------------
+//<AMI_PHDR_END>
+
+EFI_STATUS
+AhciMmioSmiHandler (
+ IN UINTN CpuIndex,
+ IN UINT32 FunctionNo
+)
+{
+ EFI_STATUS Status;
+ UINT32 AhciMmioAddress;
+ UINT32 WriteValue;
+ UINT32 ReadValue;
+ UINT32 ReturnStatus = 0x0FF;
+
+ gSmmCpuProtocol->ReadSaveState (gSmmCpuProtocol,
+ 4,
+ EFI_SMM_SAVE_STATE_REGISTER_RSI,
+ CpuIndex,
+ &AhciMmioAddress );
+
+
+ if(FunctionNo == 1) {
+ Status=AhciMmioRead(AhciMmioAddress, &ReadValue);
+
+ if(!EFI_ERROR(Status)) {
+ gSmmCpuProtocol->WriteSaveState(gSmmCpuProtocol,
+ 4,
+ EFI_SMM_SAVE_STATE_REGISTER_RAX,
+ CpuIndex,
+ &ReadValue);
+ ReturnStatus = 0; // Update success
+ }
+
+ gSmmCpuProtocol->WriteSaveState(gSmmCpuProtocol,
+ 4,
+ EFI_SMM_SAVE_STATE_REGISTER_RCX,
+ CpuIndex,
+ &ReturnStatus);
+
+ } else if(FunctionNo == 2) {
+
+ gSmmCpuProtocol->ReadSaveState (gSmmCpuProtocol,
+ 4,
+ EFI_SMM_SAVE_STATE_REGISTER_RBX,
+ CpuIndex,
+ &WriteValue );
+
+ Status = AhciMmioWrite(AhciMmioAddress, WriteValue);
+
+ if(!EFI_ERROR(Status)) {
+ ReturnStatus = 0; // Update success
+ }
+
+ gSmmCpuProtocol->WriteSaveState(gSmmCpuProtocol,
+ 4,
+ EFI_SMM_SAVE_STATE_REGISTER_RCX,
+ CpuIndex,
+ &ReturnStatus);
+ } else {
+ // Invalid function number, return Error(i.e. ReturnStatus==0xFF)
+ gSmmCpuProtocol->WriteSaveState(gSmmCpuProtocol,
+ 4,
+ EFI_SMM_SAVE_STATE_REGISTER_RCX,
+ CpuIndex,
+ &FunctionNo);
+ }
+
+ return EFI_SUCCESS;
+}
+
+//<AMI_PHDR_START>
+//----------------------------------------------------------------------
+// Procedure: AhciCommonSmmHandler
+//
+// Description: Common SMI handler for AHCI INT13 SMIs
+//
+// Input: IN EFI_HANDLE - EFI Handle
+// IN VOID* - Pointer to the EFI_SMM_SW_REGISTER_CONTEXT
+// IN VOID* - Pointer to Communication data
+// IN UINTN* - Pointer to size of Communication data
+//
+// Output: EFI_STATUS
+//
+//----------------------------------------------------------------------
+//<AMI_PHDR_END>
+
+EFI_STATUS
+AhciCommonSmmHandler (
+ IN EFI_HANDLE DispatchHandle,
+ IN CONST VOID *DispatchContext OPTIONAL,
+ IN OUT VOID *CommBuffer OPTIONAL,
+ IN OUT UINTN *CommBufferSize OPTIONAL )
+{
+ EFI_STATUS Status = EFI_SUCCESS;
+ UINTN CpuIndex = (UINTN)-1;
+ UINT32 FunctionNo = 0;
+
+ // Get CPU number of CPU which generated this SWSMI
+ if (CommBuffer != NULL && CommBufferSize != NULL) {
+ CpuIndex = ((EFI_SMM_SW_CONTEXT*)CommBuffer)->SwSmiCpuIndex;
+ }
+
+ // Return if CPU number is invalid
+ if(CpuIndex == (UINTN)-1) return Status;
+
+ // Read ECX from CPU save state
+ gSmmCpuProtocol->ReadSaveState ( gSmmCpuProtocol,
+ 4,
+ EFI_SMM_SAVE_STATE_REGISTER_RCX,
+ CpuIndex,
+ &FunctionNo );
+
+ switch(FunctionNo) {
+ case 0x1:
+ case 0x2:
+ Status = AhciMmioSmiHandler(CpuIndex, FunctionNo);
+ break;
+
+ case 0x3:
+ Status = AhciInt13SmiHandler(CpuIndex);
+ break;
+
+ default:
+ // Invalid Function. Return Error.
+ FunctionNo = 0xFF;
+ gSmmCpuProtocol->WriteSaveState(gSmmCpuProtocol,
+ 4,
+ EFI_SMM_SAVE_STATE_REGISTER_RCX,
+ CpuIndex,
+ &FunctionNo);
+ Status = EFI_UNSUPPORTED;
+ break;
+ }
+
+ return Status;
+}
+
+//<AMI_PHDR_START>
+//----------------------------------------------------------------------
+// Procedure: GetAhciInt13SmmData
+//
+// Description: Saves AINT13 information passed from Non-SMM mode using
+// EFI_SMM_COMMUNICATION_PROTOCOL API. This is required for below reason:
+// 1)AhciSmmProtocol use port number, but Int13 service uses drive number.
+// 2)AhciSmmProtocol use LBA addressing on HDD, but Int13 Read/Write function
+// uses Cylinder, Header and Sector addressing on HDD.
+//
+// Input: IN EFI_HANDLE - EFI Handle
+// IN VOID* - Pointer to the EFI_SMM_SW_REGISTER_CONTEXT
+// IN VOID* - Pointer to Communication data
+// IN UINTN* - Pointer to size of Communication data
+//
+// Output: EFI_STATUS
+//
+//----------------------------------------------------------------------
+//<AMI_PHDR_END>
+
+EFI_STATUS
+GetAhciInt13SmmData (
+ IN EFI_HANDLE DispatchHandle,
+ IN CONST VOID *DispatchContext OPTIONAL,
+ IN OUT VOID *CommBuffer OPTIONAL,
+ IN OUT UINTN *CommBufferSize OPTIONAL
+)
+{
+ EFI_STATUS Status;
+ UINTN i = 0, j = 0;
+ AHCI_INT13_SMM_DATA *AhciInt13SmmData = NULL;
+ SMM_AINT13_DRIVE_INFO *pDriveInfo = NULL;
+ SMM_AINT13_DRIVE_INFO *pSmmDriveInfo = NULL;
+
+ // Confirm that communication buffer contains required data
+ AhciInt13SmmData = (AHCI_INT13_SMM_DATA *)CommBuffer;
+ if (!AhciInt13SmmData || AhciInt13SmmData->DriveCount == 0) {
+ return EFI_SUCCESS;
+ }
+
+ // Save all information from AhciInt13SmmData to gDriveInfoList
+ for(j=0;j<AhciInt13SmmData->DriveCount;j++){
+
+ // Allocate SMM memory
+ Status = pSmst->SmmAllocatePool(EfiRuntimeServicesData, sizeof(SMM_AINT13_DRIVE_INFO), &pSmmDriveInfo);
+ if (EFI_ERROR(Status)) {
+ continue;
+ }
+ // Copy all data
+ pDriveInfo = &(AhciInt13SmmData->DriveInfo[j]);
+ for(i=0;i<sizeof(SMM_AINT13_DRIVE_INFO);++i){
+ *((UINT8*)pSmmDriveInfo + i) = *((UINT8*)pDriveInfo + i);
+ }
+
+ // Add data to list
+ DListAdd(&gDriveInfoList, &pSmmDriveInfo->dLink);
+ }
+
+ // Locate AMI_AHCI_BUS_SMM_PROTOCOL
+ if(gAhciBusSmmProtocol == NULL) {
+ gAhciBusSmmProtocol = (AHCI_BUS_SMM_PROTOCOL*) GetSmstConfigurationTablePi(&gAhciSmmProtocolGuid);
+ }
+
+ return EFI_SUCCESS;
+}
+
+//<AMI_PHDR_START>
+//---------------------------------------------------------------------------
+//
+// Name: AhciInt13SmmEntry
+//
+// Description: Driver entry point function. It does following tasks:
+// 1. Initializes global variables (gDriveInfoList, PciExpressBaseAddress etc.)
+// 2. Register SMI handler to get information passed through SmmCommunicationProtocol API.
+// 3. Register SW SMI handler to process AHCI INT13 requests.
+// 4. Locate EFI_SMM_CPU_PROTOCOL for Read/Write from/to CPU save state
+//
+// Input: EFI_HANDLE - Standard EFI Image handle
+// EFI_SYSTEM_TABLE* - Pointer to System Table
+//
+// Output: EFI_STATUS
+//
+//---------------------------------------------------------------------------
+//<AMI_PHDR_END>
+
+EFI_STATUS
+AhciInt13SmmEntry (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+)
+{
+ EFI_STATUS Status;
+ EFI_SMM_SW_DISPATCH2_PROTOCOL *SwDispatch2;
+ EFI_SMM_SW_REGISTER_CONTEXT AhciInt13SwSmiContext = {AHCI_INT13_SMM_SWSMI_VALUE};
+ EFI_HANDLE AhciInt13SmmHandle;
+ EFI_HANDLE AhciInt13SmmDataHandle;
+
+ InitAmiSmmLibPi(ImageHandle, SystemTable);
+
+ InitAmiBufferValidationLib( ImageHandle, SystemTable );
+
+ // Initialize global drive info list
+ DListInit(&gDriveInfoList);
+
+ // Get the PCI Express Base Address from the PCD
+ PciExpressBaseAddress = PCIEX_BASE_ADDRESS;
+
+ // Return error if PSmstPi is NULL
+ if(pSmmBasePi == NULL || pSmstPi == NULL) {
+ ASSERT(TRUE);
+ return EFI_NOT_FOUND;
+ }
+
+ // Register SMI handler to save AHCI_INT13_SMM_DATA passed from DXE through SmmCommunicationProtocol
+ Status = pSmstPi->SmiHandlerRegister(
+ (VOID *)GetAhciInt13SmmData,
+ &gAint13SmmDataGuid,
+ &AhciInt13SmmDataHandle
+ );
+ if (EFI_ERROR(Status)) {
+ ASSERT_EFI_ERROR(Status);
+ return Status;
+ }
+
+ // Locate EFI_SMM_SW_DISPATCH2_PROTOCOL
+ Status = pSmstPi->SmmLocateProtocol(
+ &gEfiSmmSwDispatch2ProtocolGuid,
+ NULL,
+ &SwDispatch2
+ );
+ if (EFI_ERROR(Status)) {
+ ASSERT_EFI_ERROR(Status);
+ return Status;
+ }
+
+ // Register SMI handler to handle AHCI INT13 operations
+ Status = SwDispatch2->Register(
+ SwDispatch2,
+ AhciCommonSmmHandler,
+ &AhciInt13SwSmiContext,
+ &AhciInt13SmmHandle
+ );
+ if (EFI_ERROR(Status)) {
+ ASSERT_EFI_ERROR(Status);
+ return Status;
+ }
+
+ // Locate EFI_SMM_CPU_PROTOCOL for Read/Write from/to CPU save state
+ Status = pSmstPi->SmmLocateProtocol(
+ &gEfiSmmCpuProtocolGuid,
+ NULL,
+ &gSmmCpuProtocol
+ );
+ if (EFI_ERROR(Status)) {
+ ASSERT_EFI_ERROR(Status);
+ return Status;
+ }
+
+ // Use this buffer for unaligned read or write
+ Status = pBS->AllocatePages (
+ AllocateAnyPages,
+ EfiReservedMemoryType,
+ EFI_SIZE_TO_PAGES(2048), // 512 for ATA and 2048 for ATAPI, so taking 2048
+ (EFI_PHYSICAL_ADDRESS*)&(gBuffer));
+ if (EFI_ERROR(Status)) {
+ ASSERT_EFI_ERROR(Status);
+ return Status;
+ }
+
+ return Status;
+}
+
+//<AMI_PHDR_START>
+//---------------------------------------------------------------------------
+//
+// Name: AhciInt13SmmEntryPoint
+//
+// Description: Entry point function for both DXE and SMM driver.
+//
+// Input: EFI_HANDLE - Standard EFI Image handle
+// EFI_SYSTEM_TABLE* - Pointer to System Table
+//
+// Output: EFI_STATUS
+//
+//---------------------------------------------------------------------------
+//<AMI_PHDR_END>
+
+EFI_STATUS
+AhciInt13SmmEntryPoint(
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+)
+{
+ EFI_STATUS Status;
+
+ InitAmiLib(ImageHandle, SystemTable);
+
+ Status = InitSmmHandler(ImageHandle, SystemTable, AhciInt13SmmEntry, AhciInt13DxeEntry);
+
+ return Status;
+}
+
+#else
+#include <Protocol/SmmBase.h>
+#include <Protocol/SmmSwDispatch.h>
+#include <Protocol/DevicePath.h>
+#include <Protocol/LoadedImage.h>
+#include <AmiSmm.h>
+
+EFI_GUID gEfiSmmSwDispatchProtocolGuid = EFI_SMM_SW_DISPATCH_PROTOCOL_GUID;
+EFI_GUID gSwSmiCpuTriggerGuid = SW_SMI_CPU_TRIGGER_GUID;
+
+//<AMI_PHDR_START>
+//----------------------------------------------------------------------------
+// Procedure: AhciMmioSmmSMIHandler
+//
+// Description: Smi handler for the AHCI_MMIO_SWSMI Sw Smi
+//
+// Input: DispatchHandle - EFI Handle
+// DispatchContext - Pointer to the EFI_SMM_SW_DISPATCH_CONTEXT
+//
+// Output:
+//
+//----------------------------------------------------------------------------
+//<AMI_PHDR_END>
+
+VOID
+AhciMmioSmmSMIHandler (
+ IN EFI_HANDLE DispatchHandle,
+ IN EFI_SMM_SW_DISPATCH_CONTEXT *DispatchContext
+)
+{
+ UINTN Cpu = (UINTN)-1;
+ UINT32 FunctionNo;
+ UINT32 AhciBaseAddress;
+ UINT32 Value;
+ EFI_SMM_CPU_SAVE_STATE *pCpuSaveState;
+ SW_SMI_CPU_TRIGGER *SwSmiCpuTrigger;
+ UINTN i;
+
+ for (i = 0; i < pSmst->NumberOfTableEntries; ++i) {
+ if (guidcmp(&pSmst->SmmConfigurationTable[i].VendorGuid,&gSwSmiCpuTriggerGuid) == 0) {
+ break;
+ }
+ }
+
+ //If found table, check for the CPU that caused the software Smi.
+ if (i != pSmst->NumberOfTableEntries) {
+ SwSmiCpuTrigger = pSmst->SmmConfigurationTable[i].VendorTable;
+ Cpu = SwSmiCpuTrigger->Cpu;
+ }
+
+ // Found Invalid CPU number, return
+ if(Cpu == (UINTN) -1) {
+ return ;
+ }
+
+ pCpuSaveState = (EFI_SMM_CPU_SAVE_STATE*)pSmst->CpuSaveState;
+
+ FunctionNo = pCpuSaveState[Cpu].Ia32SaveState.ECX;
+ AhciBaseAddress = pCpuSaveState[Cpu].Ia32SaveState.ESI;
+
+ switch(FunctionNo) {
+ case 0x1:
+ Status=AhciMmioRead(AhciBaseAddress, &Value);
+ if( EFI_ERROR(Status) ) {
+ // Return Error.
+ pCpuSaveState[Cpu].Ia32SaveState.ECX = 0xFF;
+ break;
+ }
+ pCpuSaveState[Cpu].Ia32SaveState.EAX = Value;
+ pCpuSaveState[Cpu].Ia32SaveState.ECX = 0;
+ break;
+
+ case 0x2:
+
+ Status = AhciMmioWrite ( AhciBaseAddress, pCpuSaveState[Cpu].Ia32SaveState.EBX );
+ if( EFI_ERROR(Status) ) {
+ // Return Error.
+ pCpuSaveState[Cpu].Ia32SaveState.ECX = 0xFF;
+ break;
+ }
+ pCpuSaveState[Cpu].Ia32SaveState.ECX = 0;
+ break;
+ default:
+ // Invalid Function. Return Error.
+ pCpuSaveState[Cpu].Ia32SaveState.ECX = 0xFF;
+ break;
+ }
+
+ return;
+}
+
+//<AMI_PHDR_START>
+//---------------------------------------------------------------------------
+//
+// Name: AhciMmioSmmInSmmFunction
+//
+// Description: Regsiter the AHCI_MMIO_SWSMI SMI
+//
+// Input: Standard EFI Image entry - EFI_IMAGE_ENTRY_POINT
+// EFI System Table - Pointer to System Table
+//
+// Output: EFI_STATUS OR EFI_NOT_FOUND
+//
+//---------------------------------------------------------------------------
+//<AMI_PHDR_END>
+
+EFI_STATUS
+AhciMmioSmmInSmmFunction(
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_HANDLE Handle;
+ EFI_STATUS Status;
+ EFI_SMM_SW_DISPATCH_PROTOCOL *pSwDispatch;
+ EFI_SMM_SW_DISPATCH_CONTEXT SwContext;
+
+ InitAmiBufferValidationLib( ImageHandle, SystemTable );
+
+ Status = pBS->LocateProtocol(&gEfiSmmSwDispatchProtocolGuid, NULL, &pSwDispatch);
+ ASSERT_EFI_ERROR(Status);
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ SwContext.SwSmiInputValue = AHCI_INT13_SMM_SWSMI_VALUE;
+ Status = pSwDispatch->Register(pSwDispatch, AhciMmioSmmSMIHandler, &SwContext, &Handle);
+ return EFI_SUCCESS;
+}
+
+//<AMI_PHDR_START>
+//---------------------------------------------------------------------------
+//
+// Name: AhciMmioSmmEntryPoint
+//
+// Description: Ahci MMIO access module entry Point
+//
+// Input: Standard EFI Image entry - EFI_IMAGE_ENTRY_POINT
+// EFI System Table - Pointer to System Table
+//
+// Output: EFI_STATUS OR EFI_NOT_FOUND
+//
+//---------------------------------------------------------------------------
+//<AMI_PHDR_END>
+
+EFI_STATUS
+AhciInt13SmmEntryPoint(
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+)
+{
+ InitAmiLib(ImageHandle, SystemTable);
+ return InitSmmHandler(ImageHandle, SystemTable, AhciMmioSmmInSmmFunction, NULL);
+
+}
+#endif
+
+//**********************************************************************
+//**********************************************************************
+//** **
+//** (C)Copyright 1985-2014, American Megatrends, Inc. **
+//** **
+//** All Rights Reserved. **
+//** **
+//** 5555 Oakbrook Pkwy, Suite 200, Norcross, GA 30093 **
+//** **
+//** Phone: (770)-246-8600 **
+//** **
+//**********************************************************************
+//**********************************************************************