/** @file | |
Routines that use BIOS to support INT 13 devices. | |
Copyright (c) 1999 - 2015, Intel Corporation. All rights reserved.<BR> | |
This program and the accompanying materials | |
are licensed and made available under the terms and conditions | |
of the BSD License which accompanies this distribution. The | |
full text of the license may be found at | |
http://opensource.org/licenses/bsd-license.php | |
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
**/ | |
#include "BiosBlkIo.h" | |
// | |
// Module global variables | |
// | |
// | |
// Address packet is a buffer under 1 MB for all version EDD calls | |
// | |
extern EDD_DEVICE_ADDRESS_PACKET *mEddBufferUnder1Mb; | |
// | |
// This is a buffer for INT 13h func 48 information | |
// | |
extern BIOS_LEGACY_DRIVE *mLegacyDriverUnder1Mb; | |
// | |
// Buffer of 0xFE00 bytes for EDD 1.1 transfer must be under 1 MB | |
// 0xFE00 bytes is the max transfer size supported. | |
// | |
extern VOID *mEdd11Buffer; | |
/** | |
Initialize block I/O device instance | |
@param Dev Instance of block I/O device instance | |
@retval TRUE Initialization succeeds. | |
@retval FALSE Initialization fails. | |
**/ | |
BOOLEAN | |
BiosInitBlockIo ( | |
IN BIOS_BLOCK_IO_DEV *Dev | |
) | |
{ | |
EFI_BLOCK_IO_PROTOCOL *BlockIo; | |
EFI_BLOCK_IO_MEDIA *BlockMedia; | |
BIOS_LEGACY_DRIVE *Bios; | |
BlockIo = &Dev->BlockIo; | |
BlockIo->Media = &Dev->BlockMedia; | |
BlockMedia = BlockIo->Media; | |
Bios = &Dev->Bios; | |
if (Int13GetDeviceParameters (Dev, Bios) != 0) { | |
if (Int13Extensions (Dev, Bios) != 0) { | |
BlockMedia->LastBlock = (EFI_LBA) Bios->Parameters.PhysicalSectors - 1; | |
BlockMedia->BlockSize = (UINT32) Bios->Parameters.BytesPerSector; | |
if ((Bios->Parameters.Flags & EDD_DEVICE_REMOVABLE) == EDD_DEVICE_REMOVABLE) { | |
BlockMedia->RemovableMedia = TRUE; | |
} | |
} else { | |
// | |
// Legacy Interfaces | |
// | |
BlockMedia->BlockSize = 512; | |
BlockMedia->LastBlock = (Bios->MaxHead + 1) * Bios->MaxSector * (Bios->MaxCylinder + 1) - 1; | |
} | |
DEBUG ((DEBUG_INIT, "BlockSize = %d LastBlock = %d\n", BlockMedia->BlockSize, BlockMedia->LastBlock)); | |
BlockMedia->LogicalPartition = FALSE; | |
BlockMedia->WriteCaching = FALSE; | |
// | |
// BugBug: Need to set this for removable media devices if they do not | |
// have media present | |
// | |
BlockMedia->ReadOnly = FALSE; | |
BlockMedia->MediaPresent = TRUE; | |
BlockIo->Reset = BiosBlockIoReset; | |
BlockIo->FlushBlocks = BiosBlockIoFlushBlocks; | |
if (!Bios->ExtendedInt13) { | |
// | |
// Legacy interfaces | |
// | |
BlockIo->ReadBlocks = BiosReadLegacyDrive; | |
BlockIo->WriteBlocks = BiosWriteLegacyDrive; | |
} else if ((Bios->EddVersion == EDD_VERSION_30) && (Bios->Extensions64Bit)) { | |
// | |
// EDD 3.0 Required for Device path, but extended reads are not required. | |
// | |
BlockIo->ReadBlocks = Edd30BiosReadBlocks; | |
BlockIo->WriteBlocks = Edd30BiosWriteBlocks; | |
} else { | |
// | |
// Assume EDD 1.1 - Read and Write functions. | |
// This could be EDD 3.0 without Extensions64Bit being set. | |
// If it's EDD 1.1 this will work, but the device path will not | |
// be correct. This will cause confusion to EFI OS installation. | |
// | |
BlockIo->ReadBlocks = Edd11BiosReadBlocks; | |
BlockIo->WriteBlocks = Edd11BiosWriteBlocks; | |
} | |
BlockMedia->LogicalPartition = FALSE; | |
BlockMedia->WriteCaching = FALSE; | |
return TRUE; | |
} | |
return FALSE; | |
} | |
/** | |
Gets parameters of block I/O device. | |
@param BiosBlockIoDev Instance of block I/O device. | |
@param Drive Legacy drive. | |
@return Result of device parameter retrieval. | |
**/ | |
UINTN | |
Int13GetDeviceParameters ( | |
IN BIOS_BLOCK_IO_DEV *BiosBlockIoDev, | |
IN BIOS_LEGACY_DRIVE *Drive | |
) | |
{ | |
UINTN CarryFlag; | |
UINT16 Cylinder; | |
EFI_IA32_REGISTER_SET Regs; | |
ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET)); | |
Regs.H.AH = 0x08; | |
Regs.H.DL = Drive->Number; | |
CarryFlag = BiosBlockIoDev->LegacyBios->Int86 (BiosBlockIoDev->LegacyBios, 0x13, &Regs); | |
DEBUG ((DEBUG_INIT, "Int13GetDeviceParameters: INT 13 08 DL=%02x : CF=%d AH=%02x\n", Drive->Number, CarryFlag, Regs.H.AH)); | |
if (CarryFlag != 0 || Regs.H.AH != 0x00) { | |
Drive->ErrorCode = Regs.H.AH; | |
return FALSE; | |
} | |
if (Drive->Floppy) { | |
if (Regs.H.BL == 0x10) { | |
Drive->AtapiFloppy = TRUE; | |
} else { | |
Drive->MaxHead = Regs.H.DH; | |
Drive->MaxSector = Regs.H.CL; | |
Drive->MaxCylinder = Regs.H.CH; | |
if (Drive->MaxSector == 0) { | |
// | |
// BugBug: You can not trust the Carry flag. | |
// | |
return FALSE; | |
} | |
} | |
} else { | |
Drive->MaxHead = (UINT8) (Regs.H.DH & 0x3f); | |
Cylinder = (UINT16) (((UINT16) Regs.H.DH & 0xc0) << 4); | |
Cylinder = (UINT16) (Cylinder | ((UINT16) Regs.H.CL & 0xc0) << 2); | |
Drive->MaxCylinder = (UINT16) (Cylinder + Regs.H.CH); | |
Drive->MaxSector = (UINT8) (Regs.H.CL & 0x3f); | |
} | |
return TRUE; | |
} | |
/** | |
Extension of INT13 call. | |
@param BiosBlockIoDev Instance of block I/O device. | |
@param Drive Legacy drive. | |
@return Result of this extension. | |
**/ | |
UINTN | |
Int13Extensions ( | |
IN BIOS_BLOCK_IO_DEV *BiosBlockIoDev, | |
IN BIOS_LEGACY_DRIVE *Drive | |
) | |
{ | |
INTN CarryFlag; | |
EFI_IA32_REGISTER_SET Regs; | |
ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET)); | |
Regs.H.AH = 0x41; | |
Regs.X.BX = 0x55aa; | |
Regs.H.DL = Drive->Number; | |
CarryFlag = BiosBlockIoDev->LegacyBios->Int86 (BiosBlockIoDev->LegacyBios, 0x13, &Regs); | |
DEBUG ((DEBUG_INIT, "Int13Extensions: INT 13 41 DL=%02x : CF=%d BX=%04x\n", Drive->Number, CarryFlag, Regs.X.BX)); | |
if (CarryFlag != 0 || Regs.X.BX != 0xaa55) { | |
Drive->ExtendedInt13 = FALSE; | |
Drive->DriveLockingAndEjecting = FALSE; | |
Drive->Edd = FALSE; | |
return FALSE; | |
} | |
Drive->EddVersion = Regs.H.AH; | |
Drive->ExtendedInt13 = (BOOLEAN) ((Regs.X.CX & 0x01) == 0x01); | |
Drive->DriveLockingAndEjecting = (BOOLEAN) ((Regs.X.CX & 0x02) == 0x02); | |
Drive->Edd = (BOOLEAN) ((Regs.X.CX & 0x04) == 0x04); | |
Drive->Extensions64Bit = (BOOLEAN) (Regs.X.CX & 0x08); | |
Drive->ParametersValid = (UINT8) GetDriveParameters (BiosBlockIoDev, Drive); | |
return TRUE; | |
} | |
/** | |
Gets parameters of legacy drive. | |
@param BiosBlockIoDev Instance of block I/O device. | |
@param Drive Legacy drive. | |
@return Result of drive parameter retrieval. | |
**/ | |
UINTN | |
GetDriveParameters ( | |
IN BIOS_BLOCK_IO_DEV *BiosBlockIoDev, | |
IN BIOS_LEGACY_DRIVE *Drive | |
) | |
{ | |
INTN CarryFlag; | |
EFI_IA32_REGISTER_SET Regs; | |
UINTN PointerMath; | |
ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET)); | |
Regs.H.AH = 0x48; | |
Regs.H.DL = Drive->Number; | |
// | |
// EDD Buffer must be passed in with max buffer size as first entry in the buffer | |
// | |
mLegacyDriverUnder1Mb->Parameters.StructureSize = (UINT16) sizeof (EDD_DRIVE_PARAMETERS); | |
Regs.X.DS = EFI_SEGMENT ((UINTN)(&mLegacyDriverUnder1Mb->Parameters)); | |
Regs.X.SI = EFI_OFFSET ((UINTN)(&mLegacyDriverUnder1Mb->Parameters)); | |
CarryFlag = BiosBlockIoDev->LegacyBios->Int86 (BiosBlockIoDev->LegacyBios, 0x13, &Regs); | |
DEBUG ((DEBUG_INIT, "GetDriveParameters: INT 13 48 DL=%02x : CF=%d AH=%02x\n", Drive->Number, CarryFlag, Regs.H.AH)); | |
if (CarryFlag != 0 || Regs.H.AH != 0x00) { | |
Drive->ErrorCode = Regs.H.AH; | |
SetMem (&Drive->Parameters, sizeof (Drive->Parameters), 0xaf); | |
return FALSE; | |
} | |
// | |
// We only have one buffer < 1MB, so copy into our instance data | |
// | |
CopyMem ( | |
&Drive->Parameters, | |
&mLegacyDriverUnder1Mb->Parameters, | |
sizeof (Drive->Parameters) | |
); | |
if (Drive->AtapiFloppy) { | |
// | |
// Sense Media Type | |
// | |
Regs.H.AH = 0x20; | |
Regs.H.DL = Drive->Number; | |
CarryFlag = BiosBlockIoDev->LegacyBios->Int86 (BiosBlockIoDev->LegacyBios, 0x13, &Regs); | |
DEBUG ((DEBUG_INIT, "GetDriveParameters: INT 13 20 DL=%02x : CF=%d AL=%02x\n", Drive->Number, CarryFlag, Regs.H.AL)); | |
if (CarryFlag != 0) { | |
// | |
// Media not present or unknown media present | |
// | |
if ((Drive->Parameters.Flags & EDD_GEOMETRY_VALID) == EDD_GEOMETRY_VALID) { | |
Drive->MaxHead = (UINT8) (Drive->Parameters.MaxHeads - 1); | |
Drive->MaxSector = (UINT8) Drive->Parameters.SectorsPerTrack; | |
ASSERT (Drive->MaxSector != 0); | |
Drive->MaxCylinder = (UINT16) (Drive->Parameters.MaxCylinders - 1); | |
} else { | |
Drive->MaxHead = 0; | |
Drive->MaxSector = 1; | |
Drive->MaxCylinder = 0; | |
} | |
} else { | |
// | |
// Media Present | |
// | |
switch (Regs.H.AL) { | |
case 0x03: | |
// | |
// 720 KB | |
// | |
Drive->MaxHead = 1; | |
Drive->MaxSector = 9; | |
Drive->MaxCylinder = 79; | |
break; | |
case 0x04: | |
// | |
// 1.44MB | |
// | |
Drive->MaxHead = 1; | |
Drive->MaxSector = 18; | |
Drive->MaxCylinder = 79; | |
break; | |
case 0x06: | |
// | |
// 2.88MB | |
// | |
Drive->MaxHead = 1; | |
Drive->MaxSector = 36; | |
Drive->MaxCylinder = 79; | |
break; | |
case 0x0C: | |
// | |
// 360 KB | |
// | |
Drive->MaxHead = 1; | |
Drive->MaxSector = 9; | |
Drive->MaxCylinder = 39; | |
break; | |
case 0x0D: | |
// | |
// 1.2 MB | |
// | |
Drive->MaxHead = 1; | |
Drive->MaxSector = 15; | |
Drive->MaxCylinder = 79; | |
break; | |
case 0x0E: | |
// | |
// Toshiba 3 mode | |
// | |
case 0x0F: | |
// | |
// NEC 3 mode | |
// | |
case 0x10: | |
// | |
// Default Media | |
// | |
if ((Drive->Parameters.Flags & EDD_GEOMETRY_VALID) == EDD_GEOMETRY_VALID) { | |
Drive->MaxHead = (UINT8) (Drive->Parameters.MaxHeads - 1); | |
Drive->MaxSector = (UINT8) Drive->Parameters.SectorsPerTrack; | |
ASSERT (Drive->MaxSector != 0); | |
Drive->MaxCylinder = (UINT16) (Drive->Parameters.MaxCylinders - 1); | |
} else { | |
Drive->MaxHead = 0; | |
Drive->MaxSector = 1; | |
Drive->MaxCylinder = 0; | |
} | |
break; | |
default: | |
// | |
// Unknown media type. | |
// | |
Drive->MaxHead = 0; | |
Drive->MaxSector = 1; | |
Drive->MaxCylinder = 0; | |
break; | |
} | |
} | |
Drive->Parameters.PhysicalSectors = (Drive->MaxHead + 1) * Drive->MaxSector * (Drive->MaxCylinder + 1); | |
Drive->Parameters.BytesPerSector = 512; | |
} | |
// | |
// This data comes from the BIOS so it may not allways be valid | |
// since the BIOS may reuse this buffer for future accesses | |
// | |
PointerMath = EFI_SEGMENT (Drive->Parameters.Fdpt) << 4; | |
PointerMath += EFI_OFFSET (Drive->Parameters.Fdpt); | |
Drive->FdptPointer = (VOID *) PointerMath; | |
return TRUE; | |
} | |
// | |
// Block IO Routines | |
// | |
/** | |
Read BufferSize bytes from Lba into Buffer. | |
@param This Indicates a pointer to the calling context. | |
@param MediaId Id of the media, changes every time the media is replaced. | |
@param Lba The starting Logical Block Address to read from | |
@param BufferSize Size of Buffer, must be a multiple of device block size. | |
@param Buffer A pointer to the destination buffer for the data. The caller is | |
responsible for either having implicit or explicit ownership of the buffer. | |
@retval EFI_SUCCESS The data was read correctly from the device. | |
@retval EFI_DEVICE_ERROR The device reported an error while performing the read. | |
@retval EFI_NO_MEDIA There is no media in the device. | |
@retval EFI_MEDIA_CHANGED The MediaId does not matched the current device. | |
@retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. | |
@retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid, | |
or the buffer is not on proper alignment. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
Edd30BiosReadBlocks ( | |
IN EFI_BLOCK_IO_PROTOCOL *This, | |
IN UINT32 MediaId, | |
IN EFI_LBA Lba, | |
IN UINTN BufferSize, | |
OUT VOID *Buffer | |
) | |
{ | |
EFI_BLOCK_IO_MEDIA *Media; | |
BIOS_BLOCK_IO_DEV *BiosBlockIoDev; | |
EDD_DEVICE_ADDRESS_PACKET *AddressPacket; | |
// | |
// I exist only for readability | |
// | |
EFI_IA32_REGISTER_SET Regs; | |
UINT64 TransferBuffer; | |
UINTN NumberOfBlocks; | |
UINTN TransferByteSize; | |
UINTN BlockSize; | |
BIOS_LEGACY_DRIVE *Bios; | |
UINTN CarryFlag; | |
UINTN MaxTransferBlocks; | |
EFI_BLOCK_IO_PROTOCOL *BlockIo; | |
Media = This->Media; | |
BlockSize = Media->BlockSize; | |
ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET)); | |
if (MediaId != Media->MediaId) { | |
return EFI_MEDIA_CHANGED; | |
} | |
if (Lba > Media->LastBlock) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if ((Lba + (BufferSize / BlockSize) - 1) > Media->LastBlock) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (BufferSize % BlockSize != 0) { | |
return EFI_BAD_BUFFER_SIZE; | |
} | |
if (Buffer == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (BufferSize == 0) { | |
return EFI_SUCCESS; | |
} | |
BiosBlockIoDev = BIOS_BLOCK_IO_FROM_THIS (This); | |
AddressPacket = mEddBufferUnder1Mb; | |
MaxTransferBlocks = MAX_EDD11_XFER / BlockSize; | |
TransferBuffer = (UINT64)(UINTN) Buffer; | |
for (; BufferSize > 0;) { | |
NumberOfBlocks = BufferSize / BlockSize; | |
NumberOfBlocks = NumberOfBlocks > MaxTransferBlocks ? MaxTransferBlocks : NumberOfBlocks; | |
// | |
// Max transfer MaxTransferBlocks | |
// | |
AddressPacket->PacketSizeInBytes = (UINT8) sizeof (EDD_DEVICE_ADDRESS_PACKET); | |
AddressPacket->Zero = 0; | |
AddressPacket->NumberOfBlocks = (UINT8) NumberOfBlocks; | |
AddressPacket->Zero2 = 0; | |
AddressPacket->SegOffset = 0xffffffff; | |
AddressPacket->Lba = (UINT64) Lba; | |
AddressPacket->TransferBuffer = TransferBuffer; | |
Regs.H.AH = 0x42; | |
Regs.H.DL = BiosBlockIoDev->Bios.Number; | |
Regs.X.SI = EFI_OFFSET (AddressPacket); | |
Regs.X.DS = EFI_SEGMENT (AddressPacket); | |
CarryFlag = BiosBlockIoDev->LegacyBios->Int86 (BiosBlockIoDev->LegacyBios, 0x13, &Regs); | |
DEBUG ( | |
( | |
DEBUG_BLKIO, "Edd30BiosReadBlocks: INT 13 42 DL=%02x : CF=%d AH=%02x\n", BiosBlockIoDev->Bios.Number, | |
CarryFlag, Regs.H.AH | |
) | |
); | |
Media->MediaPresent = TRUE; | |
if (CarryFlag != 0) { | |
// | |
// Return Error Status | |
// | |
BiosBlockIoDev->Bios.ErrorCode = Regs.H.AH; | |
if (BiosBlockIoDev->Bios.ErrorCode == BIOS_DISK_CHANGED) { | |
Media->MediaId++; | |
Bios = &BiosBlockIoDev->Bios; | |
if (Int13GetDeviceParameters (BiosBlockIoDev, Bios) != 0) { | |
if (Int13Extensions (BiosBlockIoDev, Bios) != 0) { | |
Media->LastBlock = (EFI_LBA) Bios->Parameters.PhysicalSectors - 1; | |
Media->BlockSize = (UINT32) Bios->Parameters.BytesPerSector; | |
} else { | |
ASSERT (FALSE); | |
} | |
Media->ReadOnly = FALSE; | |
gBS->HandleProtocol (BiosBlockIoDev->Handle, &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo); | |
gBS->ReinstallProtocolInterface (BiosBlockIoDev->Handle, &gEfiBlockIoProtocolGuid, BlockIo, BlockIo); | |
return EFI_MEDIA_CHANGED; | |
} | |
} | |
if (Media->RemovableMedia) { | |
Media->MediaPresent = FALSE; | |
} | |
return EFI_DEVICE_ERROR; | |
} | |
TransferByteSize = NumberOfBlocks * BlockSize; | |
BufferSize = BufferSize - TransferByteSize; | |
TransferBuffer += TransferByteSize; | |
Lba += NumberOfBlocks; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Write BufferSize bytes from Lba into Buffer. | |
@param This Indicates a pointer to the calling context. | |
@param MediaId The media ID that the write request is for. | |
@param Lba The starting logical block address to be written. The caller is | |
responsible for writing to only legitimate locations. | |
@param BufferSize Size of Buffer, must be a multiple of device block size. | |
@param Buffer A pointer to the source buffer for the data. | |
@retval EFI_SUCCESS The data was written correctly to the device. | |
@retval EFI_WRITE_PROTECTED The device can not be written to. | |
@retval EFI_DEVICE_ERROR The device reported an error while performing the write. | |
@retval EFI_NO_MEDIA There is no media in the device. | |
@retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device. | |
@retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. | |
@retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid, | |
or the buffer is not on proper alignment. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
Edd30BiosWriteBlocks ( | |
IN EFI_BLOCK_IO_PROTOCOL *This, | |
IN UINT32 MediaId, | |
IN EFI_LBA Lba, | |
IN UINTN BufferSize, | |
OUT VOID *Buffer | |
) | |
{ | |
EFI_BLOCK_IO_MEDIA *Media; | |
BIOS_BLOCK_IO_DEV *BiosBlockIoDev; | |
EDD_DEVICE_ADDRESS_PACKET *AddressPacket; | |
// | |
// I exist only for readability | |
// | |
EFI_IA32_REGISTER_SET Regs; | |
UINT64 TransferBuffer; | |
UINTN NumberOfBlocks; | |
UINTN TransferByteSize; | |
UINTN BlockSize; | |
BIOS_LEGACY_DRIVE *Bios; | |
UINTN CarryFlag; | |
UINTN MaxTransferBlocks; | |
EFI_BLOCK_IO_PROTOCOL *BlockIo; | |
Media = This->Media; | |
BlockSize = Media->BlockSize; | |
ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET)); | |
if (MediaId != Media->MediaId) { | |
return EFI_MEDIA_CHANGED; | |
} | |
if (Lba > Media->LastBlock) { | |
return EFI_DEVICE_ERROR; | |
} | |
if ((Lba + (BufferSize / BlockSize) - 1) > Media->LastBlock) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (BufferSize % BlockSize != 0) { | |
return EFI_BAD_BUFFER_SIZE; | |
} | |
if (Buffer == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (BufferSize == 0) { | |
return EFI_SUCCESS; | |
} | |
BiosBlockIoDev = BIOS_BLOCK_IO_FROM_THIS (This); | |
AddressPacket = mEddBufferUnder1Mb; | |
MaxTransferBlocks = MAX_EDD11_XFER / BlockSize; | |
TransferBuffer = (UINT64)(UINTN) Buffer; | |
for (; BufferSize > 0;) { | |
NumberOfBlocks = BufferSize / BlockSize; | |
NumberOfBlocks = NumberOfBlocks > MaxTransferBlocks ? MaxTransferBlocks : NumberOfBlocks; | |
// | |
// Max transfer MaxTransferBlocks | |
// | |
AddressPacket->PacketSizeInBytes = (UINT8) sizeof (EDD_DEVICE_ADDRESS_PACKET); | |
AddressPacket->Zero = 0; | |
AddressPacket->NumberOfBlocks = (UINT8) NumberOfBlocks; | |
AddressPacket->Zero2 = 0; | |
AddressPacket->SegOffset = 0xffffffff; | |
AddressPacket->Lba = (UINT64) Lba; | |
AddressPacket->TransferBuffer = TransferBuffer; | |
Regs.H.AH = 0x43; | |
Regs.H.AL = 0x00; | |
// | |
// Write Verify Off | |
// | |
Regs.H.DL = (UINT8) (BiosBlockIoDev->Bios.Number); | |
Regs.X.SI = EFI_OFFSET (AddressPacket); | |
Regs.X.DS = EFI_SEGMENT (AddressPacket); | |
CarryFlag = BiosBlockIoDev->LegacyBios->Int86 (BiosBlockIoDev->LegacyBios, 0x13, &Regs); | |
DEBUG ( | |
( | |
DEBUG_BLKIO, "Edd30BiosWriteBlocks: INT 13 43 DL=%02x : CF=%d AH=%02x\n", BiosBlockIoDev->Bios.Number, | |
CarryFlag, Regs.H.AH | |
) | |
); | |
Media->MediaPresent = TRUE; | |
if (CarryFlag != 0) { | |
// | |
// Return Error Status | |
// | |
BiosBlockIoDev->Bios.ErrorCode = Regs.H.AH; | |
if (BiosBlockIoDev->Bios.ErrorCode == BIOS_DISK_CHANGED) { | |
Media->MediaId++; | |
Bios = &BiosBlockIoDev->Bios; | |
if (Int13GetDeviceParameters (BiosBlockIoDev, Bios) != 0) { | |
if (Int13Extensions (BiosBlockIoDev, Bios) != 0) { | |
Media->LastBlock = (EFI_LBA) Bios->Parameters.PhysicalSectors - 1; | |
Media->BlockSize = (UINT32) Bios->Parameters.BytesPerSector; | |
} else { | |
ASSERT (FALSE); | |
} | |
Media->ReadOnly = FALSE; | |
gBS->HandleProtocol (BiosBlockIoDev->Handle, &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo); | |
gBS->ReinstallProtocolInterface (BiosBlockIoDev->Handle, &gEfiBlockIoProtocolGuid, BlockIo, BlockIo); | |
return EFI_MEDIA_CHANGED; | |
} | |
} else if (BiosBlockIoDev->Bios.ErrorCode == BIOS_WRITE_PROTECTED) { | |
Media->ReadOnly = TRUE; | |
return EFI_WRITE_PROTECTED; | |
} | |
if (Media->RemovableMedia) { | |
Media->MediaPresent = FALSE; | |
} | |
return EFI_DEVICE_ERROR; | |
} | |
Media->ReadOnly = FALSE; | |
TransferByteSize = NumberOfBlocks * BlockSize; | |
BufferSize = BufferSize - TransferByteSize; | |
TransferBuffer += TransferByteSize; | |
Lba += NumberOfBlocks; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Flush the Block Device. | |
@param This Indicates a pointer to the calling context. | |
@retval EFI_SUCCESS All outstanding data was written to the device | |
@retval EFI_DEVICE_ERROR The device reported an error while writting back the data | |
@retval EFI_NO_MEDIA There is no media in the device. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
BiosBlockIoFlushBlocks ( | |
IN EFI_BLOCK_IO_PROTOCOL *This | |
) | |
{ | |
return EFI_SUCCESS; | |
} | |
/** | |
Reset the Block Device. | |
@param This Indicates a pointer to the calling context. | |
@param ExtendedVerification Driver may perform diagnostics on reset. | |
@retval EFI_SUCCESS The device was reset. | |
@retval EFI_DEVICE_ERROR The device is not functioning properly and could | |
not be reset. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
BiosBlockIoReset ( | |
IN EFI_BLOCK_IO_PROTOCOL *This, | |
IN BOOLEAN ExtendedVerification | |
) | |
{ | |
BIOS_BLOCK_IO_DEV *BiosBlockIoDev; | |
EFI_IA32_REGISTER_SET Regs; | |
UINTN CarryFlag; | |
BiosBlockIoDev = BIOS_BLOCK_IO_FROM_THIS (This); | |
ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET)); | |
Regs.H.AH = 0x00; | |
Regs.H.DL = BiosBlockIoDev->Bios.Number; | |
CarryFlag = BiosBlockIoDev->LegacyBios->Int86 (BiosBlockIoDev->LegacyBios, 0x13, &Regs); | |
DEBUG ( | |
( | |
DEBUG_INIT, "BiosBlockIoReset: INT 13 00 DL=%02x : CF=%d AH=%02x\n", BiosBlockIoDev->Bios.Number, CarryFlag, | |
Regs.H.AH | |
) | |
); | |
if (CarryFlag != 0) { | |
if (Regs.H.AL == BIOS_RESET_FAILED) { | |
Regs.H.AH = 0x00; | |
Regs.H.DL = BiosBlockIoDev->Bios.Number; | |
CarryFlag = BiosBlockIoDev->LegacyBios->Int86 (BiosBlockIoDev->LegacyBios, 0x13, &Regs); | |
DEBUG ( | |
( | |
DEBUG_INIT, "BiosBlockIoReset: INT 13 00 DL=%02x : CF=%d AH=%02x\n", BiosBlockIoDev->Bios.Number, CarryFlag, | |
Regs.H.AH | |
) | |
); | |
if (CarryFlag != 0) { | |
BiosBlockIoDev->Bios.ErrorCode = Regs.H.AH; | |
return EFI_DEVICE_ERROR; | |
} | |
} | |
} | |
return EFI_SUCCESS; | |
} | |
// | |
// | |
// These functions need to double buffer all data under 1MB! | |
// | |
// | |
/** | |
Read BufferSize bytes from Lba into Buffer. | |
@param This Indicates a pointer to the calling context. | |
@param MediaId Id of the media, changes every time the media is replaced. | |
@param Lba The starting Logical Block Address to read from | |
@param BufferSize Size of Buffer, must be a multiple of device block size. | |
@param Buffer A pointer to the destination buffer for the data. The caller is | |
responsible for either having implicit or explicit ownership of the buffer. | |
@retval EFI_SUCCESS The data was read correctly from the device. | |
@retval EFI_DEVICE_ERROR The device reported an error while performing the read. | |
@retval EFI_NO_MEDIA There is no media in the device. | |
@retval EFI_MEDIA_CHANGED The MediaId does not matched the current device. | |
@retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. | |
@retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid, | |
or the buffer is not on proper alignment. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
Edd11BiosReadBlocks ( | |
IN EFI_BLOCK_IO_PROTOCOL *This, | |
IN UINT32 MediaId, | |
IN EFI_LBA Lba, | |
IN UINTN BufferSize, | |
OUT VOID *Buffer | |
) | |
{ | |
EFI_BLOCK_IO_MEDIA *Media; | |
BIOS_BLOCK_IO_DEV *BiosBlockIoDev; | |
EDD_DEVICE_ADDRESS_PACKET *AddressPacket; | |
// | |
// I exist only for readability | |
// | |
EFI_IA32_REGISTER_SET Regs; | |
UINT64 TransferBuffer; | |
UINTN NumberOfBlocks; | |
UINTN TransferByteSize; | |
UINTN BlockSize; | |
BIOS_LEGACY_DRIVE *Bios; | |
UINTN CarryFlag; | |
UINTN MaxTransferBlocks; | |
EFI_BLOCK_IO_PROTOCOL *BlockIo; | |
Media = This->Media; | |
BlockSize = Media->BlockSize; | |
ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET)); | |
if (MediaId != Media->MediaId) { | |
return EFI_MEDIA_CHANGED; | |
} | |
if (Lba > Media->LastBlock) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if ((Lba + (BufferSize / BlockSize) - 1) > Media->LastBlock) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (BufferSize % BlockSize != 0) { | |
return EFI_BAD_BUFFER_SIZE; | |
} | |
if (Buffer == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (BufferSize == 0) { | |
return EFI_SUCCESS; | |
} | |
BiosBlockIoDev = BIOS_BLOCK_IO_FROM_THIS (This); | |
AddressPacket = mEddBufferUnder1Mb; | |
MaxTransferBlocks = MAX_EDD11_XFER / BlockSize; | |
TransferBuffer = (UINT64)(UINTN) mEdd11Buffer; | |
for (; BufferSize > 0;) { | |
NumberOfBlocks = BufferSize / BlockSize; | |
NumberOfBlocks = NumberOfBlocks > MaxTransferBlocks ? MaxTransferBlocks : NumberOfBlocks; | |
// | |
// Max transfer MaxTransferBlocks | |
// | |
AddressPacket->PacketSizeInBytes = (UINT8) sizeof (EDD_DEVICE_ADDRESS_PACKET); | |
AddressPacket->Zero = 0; | |
AddressPacket->NumberOfBlocks = (UINT8) NumberOfBlocks; | |
AddressPacket->Zero2 = 0; | |
// | |
// TransferBuffer has been 4KB alignment. Normalize TransferBuffer to make offset as 0 in seg:offset | |
// format to transfer maximum 127 blocks of data. | |
// Otherwise when offset adding data size exceeds 0xFFFF, if OpROM does not normalize TransferBuffer, | |
// INT13 function 42H will return data boundary error 09H. | |
// | |
AddressPacket->SegOffset = (UINT32) LShiftU64 (RShiftU64(TransferBuffer, 4), 16); | |
AddressPacket->Lba = (UINT64) Lba; | |
Regs.H.AH = 0x42; | |
Regs.H.DL = BiosBlockIoDev->Bios.Number; | |
Regs.X.SI = EFI_OFFSET (AddressPacket); | |
Regs.X.DS = EFI_SEGMENT (AddressPacket); | |
CarryFlag = BiosBlockIoDev->LegacyBios->Int86 (BiosBlockIoDev->LegacyBios, 0x13, &Regs); | |
DEBUG ( | |
( | |
DEBUG_BLKIO, "Edd11BiosReadBlocks: INT 13 42 DL=%02x : CF=%d AH=%02x : LBA 0x%lx Block(s) %0d \n", | |
BiosBlockIoDev->Bios.Number, CarryFlag, Regs.H.AH, Lba, NumberOfBlocks | |
) | |
); | |
Media->MediaPresent = TRUE; | |
if (CarryFlag != 0) { | |
// | |
// Return Error Status | |
// | |
BiosBlockIoDev->Bios.ErrorCode = Regs.H.AH; | |
if (BiosBlockIoDev->Bios.ErrorCode == BIOS_DISK_CHANGED) { | |
Media->MediaId++; | |
Bios = &BiosBlockIoDev->Bios; | |
if (Int13GetDeviceParameters (BiosBlockIoDev, Bios) != 0) { | |
if (Int13Extensions (BiosBlockIoDev, Bios) != 0) { | |
Media->LastBlock = (EFI_LBA) Bios->Parameters.PhysicalSectors - 1; | |
Media->BlockSize = (UINT32) Bios->Parameters.BytesPerSector; | |
} else { | |
ASSERT (FALSE); | |
} | |
Media->ReadOnly = FALSE; | |
gBS->HandleProtocol (BiosBlockIoDev->Handle, &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo); | |
gBS->ReinstallProtocolInterface (BiosBlockIoDev->Handle, &gEfiBlockIoProtocolGuid, BlockIo, BlockIo); | |
return EFI_MEDIA_CHANGED; | |
} | |
} | |
if (Media->RemovableMedia) { | |
Media->MediaPresent = FALSE; | |
} | |
return EFI_DEVICE_ERROR; | |
} | |
TransferByteSize = NumberOfBlocks * BlockSize; | |
CopyMem (Buffer, (VOID *) (UINTN) TransferBuffer, TransferByteSize); | |
BufferSize = BufferSize - TransferByteSize; | |
Buffer = (VOID *) ((UINT8 *) Buffer + TransferByteSize); | |
Lba += NumberOfBlocks; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Write BufferSize bytes from Lba into Buffer. | |
@param This Indicates a pointer to the calling context. | |
@param MediaId The media ID that the write request is for. | |
@param Lba The starting logical block address to be written. The caller is | |
responsible for writing to only legitimate locations. | |
@param BufferSize Size of Buffer, must be a multiple of device block size. | |
@param Buffer A pointer to the source buffer for the data. | |
@retval EFI_SUCCESS The data was written correctly to the device. | |
@retval EFI_WRITE_PROTECTED The device can not be written to. | |
@retval EFI_DEVICE_ERROR The device reported an error while performing the write. | |
@retval EFI_NO_MEDIA There is no media in the device. | |
@retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device. | |
@retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. | |
@retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid, | |
or the buffer is not on proper alignment. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
Edd11BiosWriteBlocks ( | |
IN EFI_BLOCK_IO_PROTOCOL *This, | |
IN UINT32 MediaId, | |
IN EFI_LBA Lba, | |
IN UINTN BufferSize, | |
OUT VOID *Buffer | |
) | |
{ | |
EFI_BLOCK_IO_MEDIA *Media; | |
BIOS_BLOCK_IO_DEV *BiosBlockIoDev; | |
EDD_DEVICE_ADDRESS_PACKET *AddressPacket; | |
// | |
// I exist only for readability | |
// | |
EFI_IA32_REGISTER_SET Regs; | |
UINT64 TransferBuffer; | |
UINTN NumberOfBlocks; | |
UINTN TransferByteSize; | |
UINTN BlockSize; | |
BIOS_LEGACY_DRIVE *Bios; | |
UINTN CarryFlag; | |
UINTN MaxTransferBlocks; | |
EFI_BLOCK_IO_PROTOCOL *BlockIo; | |
Media = This->Media; | |
BlockSize = Media->BlockSize; | |
ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET)); | |
if (MediaId != Media->MediaId) { | |
return EFI_MEDIA_CHANGED; | |
} | |
if (Lba > Media->LastBlock) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if ((Lba + (BufferSize / BlockSize) - 1) > Media->LastBlock) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (BufferSize % BlockSize != 0) { | |
return EFI_BAD_BUFFER_SIZE; | |
} | |
if (Buffer == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (BufferSize == 0) { | |
return EFI_SUCCESS; | |
} | |
BiosBlockIoDev = BIOS_BLOCK_IO_FROM_THIS (This); | |
AddressPacket = mEddBufferUnder1Mb; | |
MaxTransferBlocks = MAX_EDD11_XFER / BlockSize; | |
TransferBuffer = (UINT64)(UINTN) mEdd11Buffer; | |
for (; BufferSize > 0;) { | |
NumberOfBlocks = BufferSize / BlockSize; | |
NumberOfBlocks = NumberOfBlocks > MaxTransferBlocks ? MaxTransferBlocks : NumberOfBlocks; | |
// | |
// Max transfer MaxTransferBlocks | |
// | |
AddressPacket->PacketSizeInBytes = (UINT8) sizeof (EDD_DEVICE_ADDRESS_PACKET); | |
AddressPacket->Zero = 0; | |
AddressPacket->NumberOfBlocks = (UINT8) NumberOfBlocks; | |
AddressPacket->Zero2 = 0; | |
// | |
// TransferBuffer has been 4KB alignment. Normalize TransferBuffer to make offset as 0 in seg:offset | |
// format to transfer maximum 127 blocks of data. | |
// Otherwise when offset adding data size exceeds 0xFFFF, if OpROM does not normalize TransferBuffer, | |
// INT13 function 42H will return data boundary error 09H. | |
// | |
AddressPacket->SegOffset = (UINT32) LShiftU64 (RShiftU64(TransferBuffer, 4), 16); | |
AddressPacket->Lba = (UINT64) Lba; | |
Regs.H.AH = 0x43; | |
Regs.H.AL = 0x00; | |
// | |
// Write Verify disable | |
// | |
Regs.H.DL = BiosBlockIoDev->Bios.Number; | |
Regs.X.SI = EFI_OFFSET (AddressPacket); | |
Regs.X.DS = EFI_SEGMENT (AddressPacket); | |
TransferByteSize = NumberOfBlocks * BlockSize; | |
CopyMem ((VOID *) (UINTN) TransferBuffer, Buffer, TransferByteSize); | |
CarryFlag = BiosBlockIoDev->LegacyBios->Int86 (BiosBlockIoDev->LegacyBios, 0x13, &Regs); | |
DEBUG ( | |
( | |
DEBUG_BLKIO, "Edd11BiosWriteBlocks: INT 13 43 DL=%02x : CF=%d AH=%02x\n: LBA 0x%lx Block(s) %0d \n", | |
BiosBlockIoDev->Bios.Number, CarryFlag, Regs.H.AH, Lba, NumberOfBlocks | |
) | |
); | |
Media->MediaPresent = TRUE; | |
if (CarryFlag != 0) { | |
// | |
// Return Error Status | |
// | |
BiosBlockIoDev->Bios.ErrorCode = Regs.H.AH; | |
if (BiosBlockIoDev->Bios.ErrorCode == BIOS_DISK_CHANGED) { | |
Media->MediaId++; | |
Bios = &BiosBlockIoDev->Bios; | |
if (Int13GetDeviceParameters (BiosBlockIoDev, Bios) != 0) { | |
if (Int13Extensions (BiosBlockIoDev, Bios) != 0) { | |
Media->LastBlock = (EFI_LBA) Bios->Parameters.PhysicalSectors - 1; | |
Media->BlockSize = (UINT32) Bios->Parameters.BytesPerSector; | |
} else { | |
ASSERT (FALSE); | |
} | |
Media->ReadOnly = FALSE; | |
gBS->HandleProtocol (BiosBlockIoDev->Handle, &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo); | |
gBS->ReinstallProtocolInterface (BiosBlockIoDev->Handle, &gEfiBlockIoProtocolGuid, BlockIo, BlockIo); | |
return EFI_MEDIA_CHANGED; | |
} | |
} else if (BiosBlockIoDev->Bios.ErrorCode == BIOS_WRITE_PROTECTED) { | |
Media->ReadOnly = TRUE; | |
return EFI_WRITE_PROTECTED; | |
} | |
if (Media->RemovableMedia) { | |
Media->MediaPresent = FALSE; | |
} | |
return EFI_DEVICE_ERROR; | |
} | |
Media->ReadOnly = FALSE; | |
BufferSize = BufferSize - TransferByteSize; | |
Buffer = (VOID *) ((UINT8 *) Buffer + TransferByteSize); | |
Lba += NumberOfBlocks; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Read BufferSize bytes from Lba into Buffer. | |
@param This Indicates a pointer to the calling context. | |
@param MediaId Id of the media, changes every time the media is replaced. | |
@param Lba The starting Logical Block Address to read from | |
@param BufferSize Size of Buffer, must be a multiple of device block size. | |
@param Buffer A pointer to the destination buffer for the data. The caller is | |
responsible for either having implicit or explicit ownership of the buffer. | |
@retval EFI_SUCCESS The data was read correctly from the device. | |
@retval EFI_DEVICE_ERROR The device reported an error while performing the read. | |
@retval EFI_NO_MEDIA There is no media in the device. | |
@retval EFI_MEDIA_CHANGED The MediaId does not matched the current device. | |
@retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. | |
@retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid, | |
or the buffer is not on proper alignment. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
BiosReadLegacyDrive ( | |
IN EFI_BLOCK_IO_PROTOCOL *This, | |
IN UINT32 MediaId, | |
IN EFI_LBA Lba, | |
IN UINTN BufferSize, | |
OUT VOID *Buffer | |
) | |
{ | |
EFI_BLOCK_IO_MEDIA *Media; | |
BIOS_BLOCK_IO_DEV *BiosBlockIoDev; | |
EFI_IA32_REGISTER_SET Regs; | |
UINTN UpperCylinder; | |
UINTN Temp; | |
UINTN Cylinder; | |
UINTN Head; | |
UINTN Sector; | |
UINTN NumberOfBlocks; | |
UINTN TransferByteSize; | |
UINTN ShortLba; | |
UINTN CheckLba; | |
UINTN BlockSize; | |
BIOS_LEGACY_DRIVE *Bios; | |
UINTN CarryFlag; | |
UINTN Retry; | |
EFI_BLOCK_IO_PROTOCOL *BlockIo; | |
Media = This->Media; | |
BlockSize = Media->BlockSize; | |
ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET)); | |
if (MediaId != Media->MediaId) { | |
return EFI_MEDIA_CHANGED; | |
} | |
if (Lba > Media->LastBlock) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if ((Lba + (BufferSize / BlockSize) - 1) > Media->LastBlock) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (BufferSize % BlockSize != 0) { | |
return EFI_BAD_BUFFER_SIZE; | |
} | |
if (Buffer == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (BufferSize == 0) { | |
return EFI_SUCCESS; | |
} | |
BiosBlockIoDev = BIOS_BLOCK_IO_FROM_THIS (This); | |
ShortLba = (UINTN) Lba; | |
while (BufferSize != 0) { | |
// | |
// Compute I/O location in Sector, Head, Cylinder format | |
// | |
Sector = (ShortLba % BiosBlockIoDev->Bios.MaxSector) + 1; | |
Temp = ShortLba / BiosBlockIoDev->Bios.MaxSector; | |
Head = Temp % (BiosBlockIoDev->Bios.MaxHead + 1); | |
Cylinder = Temp / (BiosBlockIoDev->Bios.MaxHead + 1); | |
// | |
// Limit transfer to this Head & Cylinder | |
// | |
NumberOfBlocks = BufferSize / BlockSize; | |
Temp = BiosBlockIoDev->Bios.MaxSector - Sector + 1; | |
NumberOfBlocks = NumberOfBlocks > Temp ? Temp : NumberOfBlocks; | |
Retry = 3; | |
do { | |
// | |
// Perform the IO | |
// | |
Regs.H.AH = 2; | |
Regs.H.AL = (UINT8) NumberOfBlocks; | |
Regs.H.DL = BiosBlockIoDev->Bios.Number; | |
UpperCylinder = (Cylinder & 0x0f00) >> 2; | |
CheckLba = Cylinder * (BiosBlockIoDev->Bios.MaxHead + 1) + Head; | |
CheckLba = CheckLba * BiosBlockIoDev->Bios.MaxSector + Sector - 1; | |
DEBUG ( | |
(DEBUG_BLKIO, | |
"RLD: LBA %x (%x), Sector %x (%x), Head %x (%x), Cyl %x, UCyl %x\n", | |
ShortLba, | |
CheckLba, | |
Sector, | |
BiosBlockIoDev->Bios.MaxSector, | |
Head, | |
BiosBlockIoDev->Bios.MaxHead, | |
Cylinder, | |
UpperCylinder) | |
); | |
ASSERT (CheckLba == ShortLba); | |
Regs.H.CL = (UINT8) ((Sector & 0x3f) + (UpperCylinder & 0xff)); | |
Regs.H.DH = (UINT8) (Head & 0x3f); | |
Regs.H.CH = (UINT8) (Cylinder & 0xff); | |
Regs.X.BX = EFI_OFFSET (mEdd11Buffer); | |
Regs.X.ES = EFI_SEGMENT (mEdd11Buffer); | |
DEBUG ( | |
(DEBUG_BLKIO, | |
"INT 13h: AX:(02%02x) DX:(%02x%02x) CX:(%02x%02x) BX:(%04x) ES:(%04x)\n", | |
Regs.H.AL, | |
(UINT8) (Head & 0x3f), | |
Regs.H.DL, | |
(UINT8) (Cylinder & 0xff), | |
(UINT8) ((Sector & 0x3f) + (UpperCylinder & 0xff)), | |
EFI_OFFSET (mEdd11Buffer), | |
EFI_SEGMENT (mEdd11Buffer)) | |
); | |
CarryFlag = BiosBlockIoDev->LegacyBios->Int86 (BiosBlockIoDev->LegacyBios, 0x13, &Regs); | |
DEBUG ( | |
( | |
DEBUG_BLKIO, "BiosReadLegacyDrive: INT 13 02 DL=%02x : CF=%d AH=%02x\n", BiosBlockIoDev->Bios.Number, | |
CarryFlag, Regs.H.AH | |
) | |
); | |
Retry--; | |
} while (CarryFlag != 0 && Retry != 0 && Regs.H.AH != BIOS_DISK_CHANGED); | |
Media->MediaPresent = TRUE; | |
if (CarryFlag != 0) { | |
// | |
// Return Error Status | |
// | |
BiosBlockIoDev->Bios.ErrorCode = Regs.H.AH; | |
if (BiosBlockIoDev->Bios.ErrorCode == BIOS_DISK_CHANGED) { | |
Media->MediaId++; | |
Bios = &BiosBlockIoDev->Bios; | |
if (Int13GetDeviceParameters (BiosBlockIoDev, Bios) != 0) { | |
// | |
// If the size of the media changed we need to reset the disk geometry | |
// | |
if (Int13Extensions (BiosBlockIoDev, Bios) != 0) { | |
Media->LastBlock = (EFI_LBA) Bios->Parameters.PhysicalSectors - 1; | |
Media->BlockSize = (UINT32) Bios->Parameters.BytesPerSector; | |
} else { | |
// | |
// Legacy Interfaces | |
// | |
Media->LastBlock = (Bios->MaxHead + 1) * Bios->MaxSector * (Bios->MaxCylinder + 1) - 1; | |
Media->BlockSize = 512; | |
} | |
Media->ReadOnly = FALSE; | |
gBS->HandleProtocol (BiosBlockIoDev->Handle, &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo); | |
gBS->ReinstallProtocolInterface (BiosBlockIoDev->Handle, &gEfiBlockIoProtocolGuid, BlockIo, BlockIo); | |
return EFI_MEDIA_CHANGED; | |
} | |
} | |
if (Media->RemovableMedia) { | |
Media->MediaPresent = FALSE; | |
} | |
return EFI_DEVICE_ERROR; | |
} | |
TransferByteSize = NumberOfBlocks * BlockSize; | |
CopyMem (Buffer, mEdd11Buffer, TransferByteSize); | |
ShortLba = ShortLba + NumberOfBlocks; | |
BufferSize = BufferSize - TransferByteSize; | |
Buffer = (VOID *) ((UINT8 *) Buffer + TransferByteSize); | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Write BufferSize bytes from Lba into Buffer. | |
@param This Indicates a pointer to the calling context. | |
@param MediaId The media ID that the write request is for. | |
@param Lba The starting logical block address to be written. The caller is | |
responsible for writing to only legitimate locations. | |
@param BufferSize Size of Buffer, must be a multiple of device block size. | |
@param Buffer A pointer to the source buffer for the data. | |
@retval EFI_SUCCESS The data was written correctly to the device. | |
@retval EFI_WRITE_PROTECTED The device can not be written to. | |
@retval EFI_DEVICE_ERROR The device reported an error while performing the write. | |
@retval EFI_NO_MEDIA There is no media in the device. | |
@retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device. | |
@retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. | |
@retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid, | |
or the buffer is not on proper alignment. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
BiosWriteLegacyDrive ( | |
IN EFI_BLOCK_IO_PROTOCOL *This, | |
IN UINT32 MediaId, | |
IN EFI_LBA Lba, | |
IN UINTN BufferSize, | |
OUT VOID *Buffer | |
) | |
{ | |
EFI_BLOCK_IO_MEDIA *Media; | |
BIOS_BLOCK_IO_DEV *BiosBlockIoDev; | |
EFI_IA32_REGISTER_SET Regs; | |
UINTN UpperCylinder; | |
UINTN Temp; | |
UINTN Cylinder; | |
UINTN Head; | |
UINTN Sector; | |
UINTN NumberOfBlocks; | |
UINTN TransferByteSize; | |
UINTN ShortLba; | |
UINTN CheckLba; | |
UINTN BlockSize; | |
BIOS_LEGACY_DRIVE *Bios; | |
UINTN CarryFlag; | |
UINTN Retry; | |
EFI_BLOCK_IO_PROTOCOL *BlockIo; | |
Media = This->Media; | |
BlockSize = Media->BlockSize; | |
ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET)); | |
if (MediaId != Media->MediaId) { | |
return EFI_MEDIA_CHANGED; | |
} | |
if (Lba > Media->LastBlock) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if ((Lba + (BufferSize / BlockSize) - 1) > Media->LastBlock) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (BufferSize % BlockSize != 0) { | |
return EFI_BAD_BUFFER_SIZE; | |
} | |
if (Buffer == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (BufferSize == 0) { | |
return EFI_SUCCESS; | |
} | |
BiosBlockIoDev = BIOS_BLOCK_IO_FROM_THIS (This); | |
ShortLba = (UINTN) Lba; | |
while (BufferSize != 0) { | |
// | |
// Compute I/O location in Sector, Head, Cylinder format | |
// | |
Sector = (ShortLba % BiosBlockIoDev->Bios.MaxSector) + 1; | |
Temp = ShortLba / BiosBlockIoDev->Bios.MaxSector; | |
Head = Temp % (BiosBlockIoDev->Bios.MaxHead + 1); | |
Cylinder = Temp / (BiosBlockIoDev->Bios.MaxHead + 1); | |
// | |
// Limit transfer to this Head & Cylinder | |
// | |
NumberOfBlocks = BufferSize / BlockSize; | |
Temp = BiosBlockIoDev->Bios.MaxSector - Sector + 1; | |
NumberOfBlocks = NumberOfBlocks > Temp ? Temp : NumberOfBlocks; | |
Retry = 3; | |
do { | |
// | |
// Perform the IO | |
// | |
Regs.H.AH = 3; | |
Regs.H.AL = (UINT8) NumberOfBlocks; | |
Regs.H.DL = BiosBlockIoDev->Bios.Number; | |
UpperCylinder = (Cylinder & 0x0f00) >> 2; | |
CheckLba = Cylinder * (BiosBlockIoDev->Bios.MaxHead + 1) + Head; | |
CheckLba = CheckLba * BiosBlockIoDev->Bios.MaxSector + Sector - 1; | |
DEBUG ( | |
(DEBUG_BLKIO, | |
"RLD: LBA %x (%x), Sector %x (%x), Head %x (%x), Cyl %x, UCyl %x\n", | |
ShortLba, | |
CheckLba, | |
Sector, | |
BiosBlockIoDev->Bios.MaxSector, | |
Head, | |
BiosBlockIoDev->Bios.MaxHead, | |
Cylinder, | |
UpperCylinder) | |
); | |
ASSERT (CheckLba == ShortLba); | |
Regs.H.CL = (UINT8) ((Sector & 0x3f) + (UpperCylinder & 0xff)); | |
Regs.H.DH = (UINT8) (Head & 0x3f); | |
Regs.H.CH = (UINT8) (Cylinder & 0xff); | |
Regs.X.BX = EFI_OFFSET (mEdd11Buffer); | |
Regs.X.ES = EFI_SEGMENT (mEdd11Buffer); | |
TransferByteSize = NumberOfBlocks * BlockSize; | |
CopyMem (mEdd11Buffer, Buffer, TransferByteSize); | |
DEBUG ( | |
(DEBUG_BLKIO, | |
"INT 13h: AX:(03%02x) DX:(%02x%02x) CX:(%02x%02x) BX:(%04x) ES:(%04x)\n", | |
Regs.H.AL, | |
(UINT8) (Head & 0x3f), | |
Regs.H.DL, | |
(UINT8) (Cylinder & 0xff), | |
(UINT8) ((Sector & 0x3f) + (UpperCylinder & 0xff)), | |
EFI_OFFSET (mEdd11Buffer), | |
EFI_SEGMENT (mEdd11Buffer)) | |
); | |
CarryFlag = BiosBlockIoDev->LegacyBios->Int86 (BiosBlockIoDev->LegacyBios, 0x13, &Regs); | |
DEBUG ( | |
( | |
DEBUG_BLKIO, "BiosWriteLegacyDrive: INT 13 03 DL=%02x : CF=%d AH=%02x\n", BiosBlockIoDev->Bios.Number, | |
CarryFlag, Regs.H.AH | |
) | |
); | |
Retry--; | |
} while (CarryFlag != 0 && Retry != 0 && Regs.H.AH != BIOS_DISK_CHANGED); | |
Media->MediaPresent = TRUE; | |
if (CarryFlag != 0) { | |
// | |
// Return Error Status | |
// | |
BiosBlockIoDev->Bios.ErrorCode = Regs.H.AH; | |
if (BiosBlockIoDev->Bios.ErrorCode == BIOS_DISK_CHANGED) { | |
Media->MediaId++; | |
Bios = &BiosBlockIoDev->Bios; | |
if (Int13GetDeviceParameters (BiosBlockIoDev, Bios) != 0) { | |
if (Int13Extensions (BiosBlockIoDev, Bios) != 0) { | |
Media->LastBlock = (EFI_LBA) Bios->Parameters.PhysicalSectors - 1; | |
Media->BlockSize = (UINT32) Bios->Parameters.BytesPerSector; | |
} else { | |
// | |
// Legacy Interfaces | |
// | |
Media->LastBlock = (Bios->MaxHead + 1) * Bios->MaxSector * (Bios->MaxCylinder + 1) - 1; | |
Media->BlockSize = 512; | |
} | |
// | |
// If the size of the media changed we need to reset the disk geometry | |
// | |
Media->ReadOnly = FALSE; | |
gBS->HandleProtocol (BiosBlockIoDev->Handle, &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo); | |
gBS->ReinstallProtocolInterface (BiosBlockIoDev->Handle, &gEfiBlockIoProtocolGuid, BlockIo, BlockIo); | |
return EFI_MEDIA_CHANGED; | |
} | |
} else if (BiosBlockIoDev->Bios.ErrorCode == BIOS_WRITE_PROTECTED) { | |
Media->ReadOnly = TRUE; | |
return EFI_WRITE_PROTECTED; | |
} | |
if (Media->RemovableMedia) { | |
Media->MediaPresent = FALSE; | |
} | |
return EFI_DEVICE_ERROR; | |
} | |
Media->ReadOnly = FALSE; | |
ShortLba = ShortLba + NumberOfBlocks; | |
BufferSize = BufferSize - TransferByteSize; | |
Buffer = (VOID *) ((UINT8 *) Buffer + TransferByteSize); | |
} | |
return EFI_SUCCESS; | |
} |