mirror of https://github.com/acidanthera/audk.git
1496 lines
48 KiB
C
1496 lines
48 KiB
C
/** @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;
|
|
}
|