/*++
CsmBlockIo.c -- UEFI CSM Legacy Block I/O Driver
AMD64 / X64 UEFI DXE Driver
Source: HR650X BIOS (AmiModulePkg/CSM/CsmBlockIo/)
This is the CSM Compatibility Support Module's Block I/O driver. It
provides the bridge between UEFI Block I/O protocols and legacy INT 13h
disk services used by CSM booting (e.g., booting legacy OS from UEFI).
Source files referenced:
e:\hs\AmiModulePkg\CSM\CsmBlockIo\CsmBlockIo.c
e:\hs\AmiModulePkg\CSM\CsmBlockIo\CsmInt13.c
--*/
#include <Uefi.h>
#include <Protocol/BlockIo.h>
#include <Protocol/DiskIo.h>
#include <Protocol/LegacyBios.h>
#include <Protocol/LegacyBiosPlatform.h>
#include <Protocol/LegacyInterrupt.h>
#include <Protocol/Legacy8259.h>
#include <Protocol/LegacyRegion.h>
#include <Protocol/LegacyMbr.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Library/DxeHobLib.h>
#include "CsmBlockIo.h"
//
// ============================================================================
// Global Data
// ============================================================================
//
//
// Protocol GUID definitions (.rdata section)
//
EFI_GUID gEfiLegacyInterruptProtocolGuid = EFI_LEGACY_INTERRUPT_PROTOCOL_GUID; // 0x3270
EFI_GUID gEfiLegacy8259ProtocolGuid = EFI_LEGACY_8259_PROTOCOL_GUID; // 0x3250
EFI_GUID gCsmBlockIoInternalGuid = EFI_CSM_BLOCK_IO_INTERNAL_GUID; // 0x3260
EFI_GUID gEfiBlockIoProtocolGuid = EFI_BLOCK_IO_PROTOCOL_GUID; // 0x3280
EFI_GUID gEfiLegacyRegionProtocolGuid = EFI_LEGACY_REGION_PROTOCOL_GUID; // 0x3290
EFI_GUID gEfiDiskIoProtocolGuid = EFI_DISK_IO_PROTOCOL_GUID; // 0x32F0
EFI_GUID gEfiLegacyBiosPlatformProtocolGuid = EFI_LEGACY_BIOS_PLATFORM_PROTOCOL_GUID; // 0x3300
EFI_GUID gEfiLegacyBiosPlatform2ProtocolGuid = EFI_LEGACY_BIOS_PLATFORM_PROTOCOL2_GUID; // 0x3310
EFI_GUID gEfiLegacyMbrProtocolGuid = EFI_LEGACY_MBR_PROTOCOL_GUID; // 0x32D0
//
// Protocol GUIDs installed by this driver
//
EFI_GUID gEfiDiskInfoProtocolGuid = EFI_DISK_INFO_PROTOCOL_GUID; // 0x32B0
EFI_GUID gEfiLegacyBiosExtProtocolGuid = EFI_LEGACY_BIOS_EXT_PROTOCOL_GUID; // 0x32E0
EFI_GUID gEfiLegacyBiosProtocolGuid = EFI_LEGACY_BIOS_PROTOCOL_GUID; // 0x32C0
//
// HOB list pointer (cached, from DxeHobLib)
//
VOID *mHobList = NULL; // 0x33B8
//
// Debug output driver handle (from sub_275C)
//
VOID *mDebugOutputHandle = NULL; // 0x33B0
//
// Run-time CMOS scratch register state (sub_2864)
//
UINT8 mCmosScratchRegister = 0; // 0x33D8
//
// INT13 handler state save/restore
//
UINT32 mInt13SavedEflags = 0; // 0x33DC
//
// Temporary buffer for INT13 data transfer
//
VOID *mInt13TransferBuffer = NULL; // 0x33E0
//
// Segment for INT13 data transfer
//
UINT16 mInt13TransferSegment = 0; // 0x33E8
UINT16 mInt13TransferSegment2 = 0; // 0x33F0
//
// Driver-defined table at 0x3320:
// Contains a CRC/checksum and function pointers for internal dispatch
//
typedef struct {
UINT64 Checksum; // +0x00: 0x46b7bfc6c8bca618
UINT64 Reserved; // +0x08: 0xc16ee5e21483198d
UINT64 EntryPoint1; // +0x10: sub_4C8 (DiskInfo handler)
UINT64 EntryPoint2; // +0x18: sub_6D4 (main INT13 handler entry)
UINT64 EntryPoint3; // +0x20: sub_FA8 (INT13 cleanup/BCV)
UINT64 EntryPoint4; // +0x28: 0x10 (size?)
} CSM_BLOCK_IO_DISPATCH_TABLE;
CSM_BLOCK_IO_DISPATCH_TABLE mDispatchTable = {
0x46b7bfc6c8bca618,
0xc16ee5e21483198d,
(UINT64)sub_4C8,
(UINT64)sub_6D4,
(UINT64)sub_FA8,
0x10
};
//
// Driver-defined table at 0x3360:
// Describes the legacy BIOS extension protocol interface
//
typedef struct {
UINT64 DiskInfoInquiry; // +0x00: sub_1554 (DiskInfo->Inquiry)
UINT64 DiskInfoIdentify; // +0x08: sub_1564 (DiskInfo->Identify)
CHAR8 LanguageCode[4]; // +0x10: "eng"
} CSM_BLOCK_IO_LEGACY_BIOS_TABLE;
CSM_BLOCK_IO_LEGACY_BIOS_TABLE mLegacyBiosExtTable = {
(UINT64)sub_1554,
(UINT64)sub_1564,
"eng"
};
//
// Default BBS type value
//
UINT32 mDefaultBbsType = 0x4FF7F; // 0x3378
//
// IPLDT handle allocation counter
//
UINT64 mIplDtHandleCounter = 0; // 0x3380
//
// Reference count for the IPLDT allocation (sub_FA8)
//
UINT64 mIplDtRefCount = 0; // 0x3388
//
// Dispatch table pointer (off_3330 = &mDispatchTable)
// LegacyBiosExt pointer (off_3360 = &mLegacyBiosExtTable)
//
//
// ============================================================================
// Library Helpers
// ============================================================================
//
//
// sub_2824 -- Debug assert helper
// Calls gDebugOutput->DebugAssert(FileName, LineNumber, Description)
//
VOID
EFIAPI
CsmAssert (
IN CONST CHAR8 *FileName,
IN UINTN LineNumber,
IN CONST CHAR8 *Description
)
{
if (mDebugOutputHandle != NULL) {
DEBUG_OUTPUT_PROTOCOL *Debug = (DEBUG_OUTPUT_PROTOCOL *)mDebugOutputHandle;
Debug->DebugAssert (FileName, LineNumber, Description);
}
}
//
// sub_27DC -- Debug print helper (log with format)
//
EFI_STATUS
EFIAPI
CsmDebugPrint (
IN UINTN ErrorLevel,
IN CONST CHAR8 *Format,
...
)
{
VA_LIST Marker;
EFI_STATUS Status;
if (mDebugOutputHandle == NULL) {
return EFI_NOT_FOUND;
}
Status = CsmCheckDebugLevel (ErrorLevel);
if (EFI_ERROR (Status)) {
return Status;
}
VA_START (Marker, Format);
Status = ((DEBUG_OUTPUT_PROTOCOL *)mDebugOutputHandle)->VDebugPrint (
ErrorLevel, Format, Marker
);
VA_END (Marker);
return Status;
}
//
// sub_275C -- Get debug output protocol handle
// Locates the EFI_DEBUG_OUTPUT_PROTOCOL (at 0x3240) from the HOB list
//
VOID *
GetDebugOutputHandle (
VOID
)
{
UINTN HobSize;
VOID *DebugHandle;
if (mDebugOutputHandle != NULL) {
return mDebugOutputHandle;
}
HobSize = gBS->CalculateHobSize (31);
gBS->AllocateHob (HobSize);
if (HobSize <= 16) {
//
// Locate debug protocol from HOB
//
if (EFI_ERROR (gBS->LocateProtocol (
&gCsmBlockIoInternalGuid, // 0x3240
NULL,
&DebugHandle
))) {
DebugHandle = NULL;
}
mDebugOutputHandle = DebugHandle;
return DebugHandle;
}
return NULL;
}
//
// sub_2864 -- Check CMOS scratch register for debug level
// Reads CMOS offset 0x4B, checks debug enable bit
//
UINT32
CsmCheckDebugLevel (
IN UINTN ErrorLevel
)
{
UINT8 CmosValue;
CmosValue = IoRead8 (0x70);
IoWrite8 (0x70, (CmosValue & 0x80) | 0x4B);
CmosValue = IoRead8 (0x71);
if (CmosValue > 3) {
if (CmosValue == 0) {
CmosValue = (MmioRead8 (0xFDAF0490) & 2) | 1;
}
}
if ((UINT8)(CmosValue - 1) > 0xFD) {
return 0;
}
return (CmosValue == 1) ? 0x80000004 : 0x80000006;
}
//
// sub_28B4 -- Get HOB list pointer (DxeHobLib)
//
VOID *
GetHobList (
VOID
)
{
EFI_STATUS Status;
UINTN Index;
UINT8 *HobList;
if (mHobList != NULL) {
return mHobList;
}
mHobList = NULL;
if (gST->HobList == NULL) {
CsmDebugPrint (0x80000000, "\nASSERT_EFI_ERROR (Status = %r)\n", EFI_NOT_FOUND);
CsmAssert (
"e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
54,
"!EFI_ERROR (Status)"
);
return mHobList;
}
HobList = (UINT8 *)gST->HobList;
for (Index = 0; Index < *(UINTN *)&gST->HobList; Index++) {
if (CsmMatchGuid (Index, (EFI_GUID *)(HobList + 12))) {
mHobList = *(VOID **)(HobList + 24 * Index + 16);
break;
}
}
if (mHobList == NULL) {
CsmAssert (
"e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
55,
"mHobList != ((void *) 0)"
);
}
return mHobList;
}
//
// sub_2B2C -- Compare GUID at HOB entry with target guid
//
BOOLEAN
CsmMatchGuid (
IN UINTN HobEntry,
IN EFI_GUID *TargetGuid
)
{
UINT64 V1, V2;
V1 = CsmReverseQword ((UINT64 *)&Guid1); // 0x32A0
V2 = CsmReverseQword ((UINT64 *)TargetGuid);
if (V1 != V2) {
return FALSE;
}
V1 = CsmReverseQword ((UINT64 *)&Guid2); // 0x32A8
V2 = CsmReverseQword ((UINT64 *)((UINT8 *)TargetGuid + 8));
return (V1 == V2);
}
//
// sub_2B9C -- Reverse 8 bytes for GUID comparison
//
UINT64
CsmReverseQword (
IN UINT64 *Value
)
{
UINT64 Result;
UINT8 *Src, *Dst;
INTN i;
Src = (UINT8 *)Value;
Dst = (UINT8 *)&Result;
for (i = 7; i >= 0; i--) {
Dst[7 - i] = Src[i];
}
return Result;
}
//
// sub_2C20 -- ZeroMemory
//
VOID
CsmZeroMem (
IN VOID *Buffer,
IN UINTN Size
)
{
UINT8 *Ptr;
UINTN i;
Ptr = (UINT8 *)Buffer;
for (i = 0; i < Size; i++) {
Ptr[i] = 0;
}
}
//
// sub_2C80 -- Memory copy (overlap-safe)
//
VOID *
CsmMemCopy (
OUT VOID *Destination,
IN CONST VOID *Source,
IN UINTN Length
)
{
BOOLEAN OverlapReversed;
UINTN Count;
UINTN AlignDelta;
UINTN AlignCheck;
UINT8 *D;
CONST UINT8 *S;
UINTN i;
D = (UINT8 *)Destination;
S = (UINT8 *)Source;
//
// Handle overlapping buffers
//
OverlapReversed = FALSE;
if ((UINTN)S < (UINTN)D) {
if ((UINTN)S + Length >= (UINTN)D) {
S = (UINT8 *)((UINTN)S + Length);
D = (UINT8 *)((UINTN)D + Length);
OverlapReversed = TRUE;
}
}
if (Length < 8 || (UINTN)(S - D) < 8) {
goto ByteCopy;
}
//
// Aligned copy (8-byte aligned)
//
AlignCheck = (UINTN)S & 7;
AlignDelta = (UINTN)D & 7;
if (OverlapReversed) {
S--;
D--;
}
if ((AlignCheck == AlignDelta) && AlignCheck) {
if (!OverlapReversed) {
AlignCheck = 8 - AlignCheck;
}
CopyMem (D, S, AlignCheck);
S += AlignCheck;
D += AlignCheck;
Length -= AlignCheck;
}
if (OverlapReversed) {
D -= 7;
S -= 7;
}
Count = Length >> 3;
CopyMem (D, S, Count * 8);
D += Count * 8;
S += Count * 8;
if (Length & 7) {
if (OverlapReversed) {
S += 2;
D += 8;
}
Length = Length & 7;
CopyMem (D, S, Length);
}
return Destination;
ByteCopy:
if (OverlapReversed) {
S--;
D--;
}
CopyMem (D, S, Length);
return Destination;
}
//
// sub_29B8 -- Get current allocation position from legacy region
// Walks a header-terminated chain of blocks starting at the
// legacy region memory buffer. Each block has 4-byte header:
// byte0 = type (0xFF = end, 0x00 = unused)
// byte1 = 0x7F marker
// byte2-3 = block size (little-endian 16-bit)
// Returns the total payload size allocated so far (skip 4-byte header).
//
UINT64
CsmGetCurrentAllocation (
IN UINT8 *Buffer
)
{
UINT8 *Ptr;
UINT64 Total;
UINTN BlockSize;
if (Buffer == NULL) {
return 0;
}
Ptr = Buffer;
Total = 0;
while (TRUE) {
//
// Check for end-of-table or unused entry marker
//
if (Ptr[0] == 0x7F && Ptr[1] == 0xFF) {
//
// End marker found -- return total + 4 (skip header)
//
return Total + 4;
}
BlockSize = (UINTN)Ptr[2] + ((UINTN)Ptr[3] << 8);
if (Ptr[0] == 0 || BlockSize == 0) {
break;
}
Total += BlockSize;
Ptr += 256 * (UINT64)Ptr[3] + Ptr[2];
}
return Total;
}
//
// sub_298C -- Allocate boot services data pool
// Wrapper for gBS->AllocatePool (EfiBootServicesData, Size, &Buffer)
//
UINT64
CsmLegacyRegionAllocate (
IN UINTN Size
)
{
UINT64 Buffer;
Buffer = 0;
gBS->AllocatePool (EfiBootServicesData, Size, (VOID **)&Buffer);
return Buffer;
}
//
// sub_2AE4 -- Allocate and copy a BBS type table to boot services data pool
//
UINT64
CsmBbsTableCopy (
IN UINT32 *BbsType
)
{
UINT64 Allocation;
UINT64 TableSize;
TableSize = CsmGetCurrentAllocation (BbsType);
Allocation = CsmLegacyRegionAllocate (TableSize);
CsmMemCopy ((VOID *)Allocation, BbsType, TableSize);
return Allocation;
}
//
// sub_2A0C -- Legacy region allocation wrapper
// If pBbsType is provided, allocate and copy the BBS type table.
// If NULL, free the allocation.
//
UINT64
CsmBbsTableAllocate (
IN UINT32 *BbsType,
IN VOID *OptionalCopySource
)
{
UINT64 Allocation;
UINT64 Offset;
UINT64 TableSize;
if (BbsType == NULL) {
if (OptionalCopySource == NULL) {
BbsType = &mDefaultBbsType;
}
CsmLegacyRegionFree ((UINT64)BbsType);
return 0;
}
if (OptionalCopySource != NULL) {
Allocation = CsmGetCurrentAllocation () - 4;
} else {
Allocation = 0;
}
TableSize = (UINT64)((UINT8 *)BbsType)[2] + ((UINT64)((UINT8 *)BbsType)[3] << 8) + 4;
Allocation = CsmLegacyRegionAllocate (Allocation + TableSize);
Offset = Allocation;
if ((UINT64)OptionalCopySource) {
CsmMemCopy ((VOID *)Offset, OptionalCopySource, (UINT64)OptionalCopySource);
Offset += (UINT64)OptionalCopySource;
}
CsmMemCopy (
(VOID *)Offset,
BbsType,
((UINT8 *)BbsType)[2] + ((UINT64)((UINT8 *)BbsType)[3] << 8)
);
*(UINT32 *)(Offset + ((UINT8 *)BbsType)[2] + ((UINT64)((UINT8 *)BbsType)[3] << 8)) = mDefaultBbsType;
return Allocation;
}
//
// ============================================================================
// INT13 Subsystem: State Save/Restore
// ============================================================================
//
//
// sub_26F4 -- Save current interrupt state for a device
// Saves global INT13 eflags into the per-device saved-flags array,
// then replaces it with the device's current flags value.
// The saved-flags array is indexed by the device number stored at
// Private->Int13SavedFlagsIndex (context+0xF8).
//
UINT8
Int13SaveState (
IN CSM_BLOCK_IO_PRIVATE *Private
)
{
UINT8 FlagsIndex;
FlagsIndex = Private->Int13SavedFlagsIndex; // +0xF8
if (FlagsIndex != 0) {
//
// Save current global eflags into the per-device slot
//
mInt13SavedEflags = Private->Int13SavedFlags[FlagsIndex];
//
// Replace with device's current flags
//
Private->Int13SavedFlags[Private->Int13SavedFlagsIndex] = Private->Int13Flags;
}
return FlagsIndex;
}
//
// sub_2734 -- Restore previous interrupt state for a device
// Restores the global INT13 eflags value from the per-device saved array.
//
UINT8
Int13RestoreState (
IN CSM_BLOCK_IO_PRIVATE *Private
)
{
UINT8 FlagsIndex;
FlagsIndex = Private->Int13SavedFlagsIndex; // +0xF8
if (FlagsIndex != 0) {
//
// Restore saved eflags from the per-device slot
//
Private->Int13SavedFlags[FlagsIndex] = mInt13SavedEflags;
}
return FlagsIndex;
}
//
// ============================================================================
// INT13 Handler Functions
// ============================================================================
//
//
// sub_1AB0 -- INT13h disk I/O status check (function 0x01)
//
EFI_STATUS
Int13GetStatus (
IN CSM_BLOCK_IO_PRIVATE *Private
)
{
UINT8 Status;
Int13SaveState (Private);
Status = Private->DeviceStatus; // context+0x89
Private->Int13Function = 0;
(*Private->Int13HandlerEntry) (Private, 0x13, &Status);
if ((Private->Int13Flags & 1) != 0 && Status == 5) {
Private->DeviceStatus = Private->DeviceStatus; // reload context+0x89
Private->Int13Function = 0;
(*Private->Int13HandlerEntry) (Private, 0x13, &Status);
if ((Private->Int13Flags & 1) != 0) {
*Private->Int13ExtendedError = Status;
return EFI_UNSUPPORTED;
}
}
Int13RestoreState (Private);
return EFI_SUCCESS;
}
//
// sub_1AAC -- INT13h error handler (stub)
//
VOID
Int13ErrorStub (
VOID
)
{
// empty -- placeholder for Int13Error function pointer
}
//
// sub_1B48 -- INT13h Read Sectors (CHS mode, function 0x02)
// Used when device has CHS geometry (original IDE/ATA)
//
EFI_STATUS
Int13ReadWriteChs (
IN CSM_BLOCK_IO_PRIVATE *Private,
IN UINT8 Command, // 0x02=read, 0x03=write
IN UINT64 StartLba,
IN UINT64 BlockCount,
IN UINT64 BufferAddress
)
{
INT13_EXT_PACKET Packet;
UINT64 BlockSize;
UINT64 BlocksPerTransfer;
UINT64 RemainingBlocks;
UINT64 Lba;
UINT64 TransferBlocks;
UINT64 TransferBytes;
UINT64 CurrentBuf;
BlockSize = Private->BlockSize;
if (Command != Private->MediaId) {
return EFI_INVALID_PARAMETER;
}
if (BlockCount == 0) {
return EFI_SUCCESS;
}
if (StartLba > Private->MaxSector ||
(BlockCount / BlockSize + StartLba - 1) > Private->MaxSector) {
return EFI_INVALID_PARAMETER;
}
if (BlockCount % BlockSize != 0) {
return EFI_BAD_BUFFER_SIZE;
}
if (BufferAddress == 0 ||
(Private->Alignment > 1 && BufferAddress % Private->Alignment != 0)) {
return EFI_INVALID_PARAMETER;
}
BlocksPerTransfer = 0xFE00 / (UINT32)BlockSize;
Int13SaveState (Private);
Lba = StartLba;
CurrentBuf = BufferAddress;
RemainingBlocks = BlockCount;
while (RemainingBlocks > 0) {
//
// Build INT13 CHS packet
//
Packet.PacketSize = 0x20;
Packet.Reserved = 0;
TransferBlocks = RemainingBlocks;
if (TransferBlocks > BlocksPerTransfer) {
TransferBlocks = BlocksPerTransfer;
}
Packet.BlockCount = (UINT16)TransferBlocks;
Packet.Lba = Lba;
Packet.BufferOffset = (UINT16)(mInt13TransferSegment & 0xF000);
Packet.BufferSegment = (UINT16)(
(mInt13TransferSegment2 >> 4) + Packet.BufferOffset
);
Private->DeviceStatus = Private->DeviceStatus;
Private->Int13Command = 0x42; // extended read
Private->Int13TransferBuffer = (VOID *)(UINTN)Lba;
(*Private->Int13HandlerEntry) (Private, 0x13, &Packet);
if ((Private->Int13Flags & 1) != 0) {
*Private->Int13ExtendedError = Private->Int13Command;
if (Private->Int13Command != 6) {
if (Private->RemovableMedia) {
Private->MediaChanged = 0;
}
return EFI_UNSUPPORTED;
}
//
// Disk changed -- re-detect geometry
//
Private->MediaId++;
if (Int13IdentifyDevice (Private, (UINT8 *)&Private->DeviceInfo)) {
if (Int13GetDriveParameters (Private, (UINT8 *)&Private->DeviceInfo)) {
Private->MaxSector = Private->DeviceInfo.MaxSector;
Private->BlockSize = Private->DeviceInfo.BytesPerSector;
} else {
CsmAssert ("", 804, "("")");
}
gBS->ReinstallProtocolInterface (
Private->BlockIo,
&gEfiDiskIoProtocolGuid,
Private->DiskIo,
Private->DiskIo
);
return EFI_INVALID_PARAMETER;
}
continue;
}
//
// Copy data from transfer buffer
//
if (mCommand == 0x02) // read
CsmMemCopy ((VOID *)CuCurrentBuf, (VOID *)mmInt13TransferBuffer, TrransferBytes);
}
Lba += TrransferBlocks;
CuurrentBuf += TransferBytes;
RemainingBlocks -= TransferBytes;
if ((Private->Int13Flags & 1) != 0) {
break;
}
}
Int13RestoreState (Private);
return EFI_SUCCESS;
}
//
// sub_1E18 -- INT13h Extended Read (function 0x42)
// For LBA mode with extended INT13 support (EDD-1.1+)
//
EFI_STATUS
Int13ExtendedRead (
IN CSM_BLOCK_IO_PRIVATE *Private,
IN UINT8 Command,
IN UINT64 StartLba,
IN UINT64 BlockCount,
IN UINT64 BufferAddress
)
{
INT13_EXT_PACKET Packet;
UINT64 BlockSize;
UINT64 BlocksPerTransfer;
UINT64 RemainingBlocks;
UINT64 Lba;
UINT64 TransferBlocks;
UINT64 TransferBytes;
UINT64 CurrentBuf;
BlockSize = Private->BlockSize;
if (Command != Private->MediaId) {
return EFI_INVALID_PARAMETER;
}
if (BlockCount == 0) {
return EFI_SUCCESS;
}
if (StartLba > Private->MaxSector ||
BlockCount / BlockSize + StartLba - 1 > Private->MaxSector) {
return EFI_INVALID_PARAMETER;
}
if (BlockCount % BlockSize != 0) {
return EFI_BAD_BUFFER_SIZE;
}
if (BufferAddress == 0 ||
(Private->Alignment > 1 && BufferAddress % Private->Alignment != 0)) {
return EFI_INVALID_PARAMETER;
}
BlocksPerTransfer = 0xFE00 / (UINT32)BlockSize;
Int13SaveState (Private);
Lba = StartLba;
CuurrentBuf = BufferAddress;
RemainingBlocks = BlockCount;
while (RemainingBlocks > 0) {
//
// Build extended INT13 packet
//
Packet.PacketSize = 0x20;
Packet.Reserved = 0;
TransferBlocks = RemainingBlocks;
if (TransferBlocks > BlocksPerTransfer) {
TransferBlocks = BlocksPerTransfer;
}
Packet.BlockCount = (UINT16)TransferBlblocks;
Packet.Lba = Lba;
Packet.BufferOffset = (UINT16)((Int13TransferSegement & 0xF000);
Packet.BufferSegement = (UINT16)(
(mInt13TransferSegement2 >> 4) + Packet.BufferOffset
);
Private->Int13Command = 0x42; // extended read
Private->Int13TransferBuffer = (VOID *)(UINTN)Lba;
//
// First copy data into transfer buffer, then issue INT13
//
if (Command == 0x03) { // write
CsmMemCopy ((VOID *)mInt13TransferBuffer, (VOID *)CurrentBuf, TransferBytes);
}
(*Private->Int13HandlerEntry) (Private, 0x13, &Packet);
Private->MediaChanged = 0;
if ((Private->Int13Flags & 1) != 0) {
*Private->Int13ExtendedError = Private->Int13Command;
if (Private->Int13Command == 6) {
//
// Disk changed -- re-detect
//
Private->MediaId++;
if (Int13IdentifyDevice (Private, (UINT8 *)&Private->DeviceInfo)) {
if (Int13GetDriveParameters (Private, (UINT8 *)&Private->DeviceInfo)) {
Private->MaxSector = Private->DeviceInfo.MaxSector;
Private->BlockSize = Private->DeviceInfo.BytesPerSector;
} else {
CsmAssert ("", 947, "((BOOLEAN)(0==1))");
}
gBS->ReinstallProtocolInterface (
Private->BlockIo,
&gEfiDiskIoProtocolGuid,
Private->DiskIo,
Private->DiskIo
);
return EFI_INVALID_PARAMETER;
}
} else if (Private->Int13Command == 3) {
Private->MediaChanged = 1;
return EFI_NO_MEDIA;
}
if (Private->RemovableMedia) {
Private->MediaChanged = 0;
}
return EFI_UNSUPPORTED;
}
CurrentBuf += TransferBytes;
Lba += TransferBlocks;
RemainingBlocks -= TransferBytes;
Private->MediaChanged = 0;
}
Int13RestoreState (Private);
return EFI_SUCCESS;
}
//
// sub_2104 -- INT13h CHS read/write (traditional CHS, function 0x02/0x03)
//
EFI_STATUS
Int13ChsReadWrite (
IN CSM_BLOCK_IO_PRIVATE *Private,
IN UINT8 Command,
IN UINT64 StartLba,
IN UINT64 BlockCount,
IN UINT64 BufferAddress
)
{
UINT64 BlockSize;
UINT64 RemainingBlocks;
UINT64 Lba;
UINT64 TransferBlocks;
UINT64 TransferBytes;
UINT64 CurrentBuf;
UINT64 Head, Cylinder, Sector;
UINT64 SectorsPerHead;
UINT64 HeadsPerCylinder;
UINT64 Retry;
UINT64 TotalSectors;
UINT64 MaxTransfer;
UINT64 SectorOffset, HeadOffset, CylinderOffset;
BlockSize = Private->BlockSize;
if (Command != Private->MediaId) {
return EFI_INVALID_PARAMETER;
}
if (BlockCount == 0) {
return EFI_SUCCESS;
}
if (StartLba > Private->MaxSector ||
(BlockCount / BlockSize + StartLba - 1) > Private->MaxSector) {
return EFI_INVALID_PARAMETER;
}
if (BlockCount % BlockSize != 0) {
return EFI_BAD_BUFFER_SIZE;
}
if (BufferAddress == 0 ||
(Private->Alignment > 1 && BufferAddress % Private->Alignment != 0)) {
return EFI_INVALID_PARAMETER;
}
Int13SaveState (Private);
Lba = StartLba;
CurrentBuf = BufferAddress;
RemainingBlocks = BlockCount;
while (RemainingBlocks > 0) {
//
// Convert LBA to CHS
//
SectorsPerHead = Private->Sectors + 1;
HeadsPerCylinder = Private->Heads + 1;
TotalSectors = SectorsPerHead * HeadsPerCylinder;
SectorOffset = Lba % Private->Sectors;
HeadOffset = (Lba / Private->Sectors) % (Private->Heads + 1);
CylinderOffset = (Lba / Private->Sectors) / (Private->Heads + 1);
MaxTransfer = Private->Sectors - SectorOffset + 1;
TransferBlocks = RemainingBlocks / BlockSize;
if (TransferBlocks > MaxTransfer) {
TransferBlocks = MaxTransfer;
}
//
// Retry loop (up to 3 attempts)
//
Retry = 3;
do {
Private->Int13Sector = (UINT8)(SectorOffset & 0x3F);
Private->Int13Head = (UINT8)((HeadOffset & 0x3F) |
((CylinderOffset >> 2) & 0xC0));
Private->Int13Cylinder = (UINT16)(mInt13TransferSegment);
Private->Int13SectorCount = (UINT8)TransferBlocks;
Private->Int13BufferSegment = ((UINT64)mInt13TransferSegment >> 4) & 0xF000;
(*Private->Int13HandlerEntry) (
Private, 0x13, &Private->Int13ChsPacket
);
Retry--;
if ((Private->Int13Flags & 1) == 0) {
break;
}
if (Retry == 0) {
break;
}
} while (Private->Int13Command != 6);
Private->MediaChanged = 1;
if ((Private->Int13Flags & 1) != 0) {
*Private->Int13ExtendedError = Private->Int13Command;
if (Private->Int13Command == 6) {
Private->MediaId++;
if (Int13IdentifyDevice (Private, (UINT8 *)&Private->DeviceInfo)) {
if (Int13GetDriveParameters (Private, (UINT8 *)&Private->DeviceInfo)) {
Private->MaxSector = Private->DeviceInfo.MaxSector;
Private->BlockSize = Private->DeviceInfo.BytesPerSector;
} else {
Private->BlockSize = 512;
Private->MaxSector = (UINT64)(
(Private->Heads + 1) *
(Private->Cylinders + 1) *
Private->Sectors
) - 1;
}
}
gBS->ReinstallProtocolInterface (
Private->BlockIo,
&gEfiDiskIoProtocolGuid,
Private->DiskIo,
Private->DiskIo
);
return EFI_INVALID_PARAMETER;
}
if (Private->RemovableMedia) {
Private->MediaChanged = 0;
}
return EFI_UNSUPPORTED;
}
CurrentBuf += TransferBlocks * BlockSize;
Lba += TransferBlocks;
RemainingBlocks -= TransferBlocks * BlockSize;
Private->MediaChanged = 0;
}
Int13RestoreState (Private);
return EFI_SUCCESS;
}
//
// sub_23E8 -- INT13h Extended Write (function 0x43), Verify (0x44)
//
EFI_STATUS
Int13ExtendedWrite (
IN CSM_BLOCK_IO_PRIVATE *Private,
IN UINT8 Command,
IN UINT64 StartLba,
IN UINT64 BlockCount,
IN UINT64 BufferAddress
)
{
UINT64 BlockSize;
UINT64 RemainingBlocks;
UINT64 Lba;
UINT64 TransferBlocks;
UINT64 TransferBytes;
UINT64 CurrentBuf;
UINT64 SectorOffset, CylinderOffset, HeadOffset;
UINT64 SectorsPerHead;
UINT64 HeadsPerCylinder;
UINT64 TotalSectors;
UINT64 MaxTransfer;
UINT64 Retry;
UINT64 SectorNum, CylinderHigh;
BlockSize = Private->BlockSize;
if (Command != Private->MediaId) {
return EFI_INVALID_PARAMETER;
}
if (BlockCount == 0) {
return EFI_SUCCESS;
}
if (StartLba > Private->MaxSector ||
(BlockCount / BlockSize + StartLba - 1) > Private->MaxSector) {
return EFI_INVALID_PARAMETER;
}
if (BlockCount % BlockSize != 0) {
return EFI_BAD_BUFFER_SIZE;
}
if (BufferAddress == 0 ||
(Private->Alignment > 1 && BufferAddress % Private->Alignment != 0)) {
return EFI_INVALID_PARAMETER;
}
Int13SaveState (Private);
Lba = StartLba;
CurrentBuf = BufferAddress;
RemainingBlocks = BlockCount;
while (RemainingBlocks > 0) {
SectorsPerHead = Private->Sectors + 1;
HeadsPerCylinder = Private->Heads + 1;
SectorOffset = Lba % Private->Sectors;
SectorNum = SectorOffset + 1;
CylinderOffset = (Lba / Private->Sectors) / (Private->Heads + 1);
HeadOffset = (Lba / Private->Sectors) % (Private->Heads + 1);
CylinderHigh = CylinderOffset >> 2;
MaxTransfer = Private->Sectors - SectorOffset;
TransferBlocks = RemainingBlocks / BlockSize;
if (TransferBlocks > MaxTransfer) {
TransferBlocks = MaxTransfer;
}
TransferBytes = TransferBlocks * BlockSize;
Retry = 3;
do {
//
// Copy data to transfer buffer first for write
//
CsmMemCopy ((VOID *)mInt13TransferBuffer, (VOID *)CurrentBuf, TransferBytes);
Private->Int13SectorCount = (UINT8)TransferBlocks;
Private->Int13Head = (UINT8)((HeadOffset & 0x3F) |
(CylinderHigh & 0xC0));
Private->Int13Sector = (UINT8)(SectorOffset & 0x3F);
Private->Int13CylinderHigh = CylinderHigh & 0xF000;
Private->Int13BufferSegment = (UINT16)mInt13TransferSegment;
(*Private->Int13HandlerEntry) (
Private, 0x13, &Private->Int13ChsPacket
);
Retry--;
if ((Private->Int13Flags & 1) == 0) {
break;
}
if (Retry == 0) {
break;
}
} while (Private->Int13Command != 6);
Private->MediaChanged = 0;
if ((Private->Int13Flags & 1) != 0) {
*Private->Int13ExtendedError = Private->Int13Command;
if (Private->Int13Command == 6) {
Private->MediaId++;
if (Int13IdentifyDevice (Private, (UINT8 *)&Private->DeviceInfo)) {
if (Int13GetDriveParameters (Private, (UINT8 *)&Private->DeviceInfo)) {
Private->MaxSector = Private->DeviceInfo.MaxSector;
Private->BlockSize = Private->DeviceInfo.BytesPerSector;
} else {
Private->BlockSize = 512;
Private->MaxSector = (UINT64)(
(Private->Headss + 1) *
(Private->Cylinders + 1) *
Private->Sectors
) - 1;
}
}
gBS->ReinstallProtocolInterface (
Private->BlockIo,
&gEfiDiskIoProtocolGuid,
Private->DiskIo,
Private->DiskIo
);
return EFI_INVALID_PARAMETER;
}
if (Private->Int13Command != 3) {
if (Private->RemovableMedia) {
Private->MediaChanged = 0;
}
return EFI_UNSUPPORTED;
}
Private->MediaChanged = 1;
return EFI_NO_MEDIA;
}
Lba += TransferBlocks;
CuurrentBuf += TransferBytes;
ReemainingBlocks -= TransferBytes;
Private->MediaChanged = 0;
}
Int13RestoreState (Private);
return EFI_SUCCESS;
}
//
// ===========================================================================
// Disk Info Protocol: Inquiry and Identify
// ============================================================================
//
//
// sub_1554 -- EFI_DISK_INFO.inquiry
//
EFI_STATUS
EFAPI
DiskInfoInquiry (
IN EFI_DISK_INFO_PROTOCOL *This,
IN UINTN *InquiryData
)
{
return EFI_SUCCESS;
}
//
// sub_1564 -- EFI_DISK_INFO.Identify
//
EFI_STATUS
EFIAPI
DiskInfoIdentify (
IN EFI_DISK_INFO_PROTOCOL *This,
IN OUT UINT32 *IdentifyData
)
{
return EFI_SUCCESS;
}
//
// ============================================================================
// Legacy BIOS Protocol / INT13 Dispatch
// ============================================================================
//
//
// sub_4C8 -- Legacy Disk Info handler (first entry point)
// Handles the EFI_DISK_INFO_PROTOCOL interface for identifying disk types
// and locating the appropriate legacy block I/O region.
//
EFI_STATUS
CsmDiskInfoHandler (
IN CSM_BLOCK_IO_PRIVATE *Private,
IN EFI_HANDLE Handle
)
{
EFI_STATUS Status;
EFI_LEGACY_REGION *LegacyRegion;
UINTN RegionSize;
UINT8 MbrBuffer[16];
UINT8 BootSignature[2];
UINT8 MediaType;
UINT8 DeviceType;
//
// Locate the Legacy Interrupt protocol
//
Status = gBS->LocateProtocol (
&gEfiLegacyInterruptProtocolGuid,
NULL,
(VOID **)&Private->LegacyInterrupt
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Open the Block I/O protocol on this handle
//
Status = gBS->OpenProtocol (
Handle,
&gEfiBlockIoProtocolGuid,
(VOID **)&Private->BlockIo,
Private->Handle,
Handle,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (EFI_ERROR (Status)) {
return Status;
}
gBS->CloseProtocol (
Handle,
&gEfiBlockIoProtocolGuid,
Private->Handle,
Handle
);
//
// Determine if Legacy BIOS Platform protocol is available
//
Status = gBS->OpenProtocol (
Handle,
&gEfiLegacyBiosPlatformProtocolGuid,
(VOID **)&Private->LegacyBiosPlatform,
Private->Handle,
Handle,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (!EFI_ERROR (Status)) {
//
// Check for Legacy MBR
//
Status = gBS->OpenProtocol (
Handle,
&gEfiLegacyMbrProtocolGuid,
(VOID **)&Private->LegacyMbr,
Private->Handle,
Handle,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (!EFI_ERROR (Status)) {
//
// Read MBR and check for boot signature
//
LegacyRegion = Private->LegacyRegion;
Status = LegacyRegion->Read (
LegacyRegion,
0LL, // offset
60, // bytes
1, // count
&DeviceType
);
if (EFI_ERROR (Status)) {
CsmDebugPrint (0x80000000, "\nASSERT_EFI_ERROR (Status = %r)\n", Status);
CsmAssert (
"e:\\hs\\AmiModulePkg\\CSM\\CsmBlockIo\\CsmBlockIo.c",
356,
"!EFI_ERROR (Status)"
);
}
//
// Check device type
//
if (DeviceType <= 0x0D) {
Status = gBS->LocateProtocol (
&gCsmBlockIoInternalGuid,
NULL,
(VOID **)&Private->Internal
);
if (EFI_ERROR (Status)) {
CsmDebugPrint (0x80000000, "\nASSERT_EFI_ERROR (Status = %r)\n", Status);
CsmAssert (
"e:\\hs\\AmiModulePkg\\CSM\\CsmBlockIo\\CsmBlockIo.c",
359,
"!EFI_ERROR (Status)"
);
}
Status = Private->Internal->GetDeviceType (
Private->Internal,
DeviceType,
&DeviceType
);
if (EFI_ERROR (Status)) {
CsmDebugPrint (0x80000000, "\nASSERT_EFI_ERROR (Status = %r)\n", Status);
CsmAssert (
"e:\\hs\\AmiModulePkg\\CSM\\CsmBlockIo\\CsmBlockIo.c",
362,
"!EFI_ERROR (Status)"
);
}
}
}
}
//
// Set BBS device type from IPLDT table
//
Private->BbsType = mIplDtTable[DeviceType];
//
// Read transfer buffer base
//
Private->TransferBuffer = Private->LegacyRegion->GetBase (
Private->LegacyRegion, 4, 0
);
//
// Set transfer buffer flags
//
Private->TransferBuffer = Private->LegacyRegion->SetFlags (
Private->LegacyRegion,
2,
Private->TransferBuffer & 0x700
);
// preserve bits 8-10
//
// Initialize INT13 handler entry
//
(*(*Private->Int13HandlerTable->Init)(
Private->Int13HandlerTable,
Handle,
0,
0,
Private->Int13Status
);
//
// Get BBS device table
//
(*Private->BbsTable->GetTable)(
&Private->BbsEntry,
Private->Int13Packet
);
//
// Count active BBS entries
//
for (Index = 0; Index < 256; Index++) {
if (Private->BbsEntry[Index] != 0xFFFF) {
BbsBitmap[Index] = 1;
BbsBeforeCount++;
}
}
//
// Execute INT13 to discover drives
//
Status = Private->BbsTable->Execute (
Private->BbsTable,
Handle,
0,
Private->Int13Status,
&DriveIndex,
Private->Int13Packet,
0,
0
);
if (EFI_ERROR (Status)) {
goto Cleanup;
}
//
// Count post-INT13 BBS entries
//
for (Index = 0; Index < 256; Index++) {
if (Private->BbsEntry[Index] != 0xFFFF) {
if (!BbsBitmap[Index] && !PostCount) {
PostCount = Index;
}
BbsAfterCount++;
}
}
if (BbsAfterCount < BbsBeforeCount) {
CsmAssert (
"e:\hs\\AmiiModulePkg\\CSM\\CsmBlockIo\\CsmBlockIo.c",
435,
"NumberOfBbsEntriesAfterOpron >= NumberOfBbsEntriesBeforeOpron"
);
}
Private->NewBbsEntryCount = BbsAfterCount - BbsBeforeCount;
Private->BbsType = mIplDtTable[DeviceType];
//
// Allocate BBS entry table in legacy region
//
if (mIplDtHandleCounter == 0) {
mIplDtHandleCounter = 0xFFFFF;
Status = gBS->AllocatePool (1, 4, 16, &mIplDtHandleCounter);
if (EFI_ERROR (Status)) {
CsmDebugPrint (0x80000000, "\nASSERT_EFI_ERROR (Status = %r)\n", Status);
CsmAssert (
"e:\\hs\\AmiModulePkg\\CSM\\CsmBlockIo\\CsmBlockIo.c",
635,
"!EFI_ERROR (Status)"
);
}
mIplDtHandleCounter = 0;
}
return EFI_SUCCESS;
Cleanup:
//
// Close protocols and return error
//
gBS->CloseProtocol (
Handle,
&gEfiLegacyRegionProtocolGuid,
Private->Handle,
Handle
);
gBS->CloseProtocol (
Handle,
&gEfiBlockIoProtocolGuid,
Private->Handle,
Handle
);
return Status;
}
//
// ============================================================================
// Entry Point
// ============================================================================
//
EFI_STATUS
EFIAPI
CsmBlockIoEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
//
// Save global protocol pointers
//
gImageHandle = ImageHandle;
gST = SystemTable;
gBS = SystemTable->BootServices;
gRT = SystemTable->RuntimeServices;
//
// Initialize HOB list
//
GetHobList ();
//
// Install three protocol interfaces on the image handle:
// 1. EFI_DISK_INFO_PROTOCOL (0x32B0)
// 2. EFI_LEGACY_BIOS_EXT_PROTOCOL (0x32E0)
// 3. EFI_LEGACY_BIOS_PROTOCOL (0x32C0)
//
return gBS->InstallMultipleProtocolInterfaces (
&gImageHandle,
&gEfiDiskInfoProtocolGuid,
&mDispatchTable, // offf3330 - dispatch table with sub_4C8, sub_6D4, sub_FA8
&gEfiLegacyBiosExtProtocolGuid,
&mLegacyBiosExtTable, // off_3360 - legacy bios ext table
&gEfiLegacyBiosProtocolGuid,
NULL, // no interface (protocol-only notification)
NULL
);
}
//
// ============================================================================
// sub_6D4 -- Main INT13 handler / CSM Block I/O initialization per device
// Called for each disk device that needs CSM legacy support.
// ============================================================================
//
// Parameters:
// a1 - Private context (CSM_BLOCK_IO_PRIVATE *)
// a2 - EFI_HANDLE for the block device
//
EFI_STATUS
CsmBlockIoInitDevice (
IN CSM_BLOCK_IO_PRIVATE *Private,
IN EFI_HANDLE DeviceHandle
)
{
EFI_STATUS Status;
UINT8 DeviceType;
UINT8 DeviceIndex;
EFI_LEGACY_REGION *LegacyRegion;
EFI_BLOCK_IO *BlockIo;
EFI_LEGACY_BIOS_PLATFORM *BiosPlatform;
UINT8 BbsBitmap[256];
UINT8 BbsBeforeCount;
UINT8 BbsAfterCount;
UINT8 BbsNewCount;
//
// Initialize state
//
CsmZeroMem (BbsBitmap, sizeof(BbsBitmap));
BbsBeforeCount = 0;
BbsAfterCount = 0;
BbsNewCount = 0;
Private->DeviceIndex = 0;
Private->BbsType = 0;
//
// Locate Legacy Interrupt protocol
//
Status = gBS->LocateProtocol (
&gEfiLegacyInterruptProtocolGuid,
NULL,
(VOID **)&Private->LegacyInterrupt
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Locate Legacy 8259 protocol
//
Status = gBS->LocateProtocol (
&gEfiLegacy8259ProtocolGuid,
NULL,
(VOID **)&Private->Legacy8259
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Open Block I/O protocol on this handle
//
Status = gBS->OpenProtocol (
DeviceHandle,
&gEfiBlockIoProtocolGuid,
(VOID **)&Private->BlockIo,
Private->Handle,
DeviceHandle,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Try Legacy Bios Platform protocol (optional)
//
v38 = FALSE;
Status = gBS->OpenProtocol (
DeviceHandle,
&gEfiLegacyBiosPlatformProtocolGuid,
(VOID **)&Private->LegacyBiosPlatform,
Private->Handle,
DeviceHandle,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (!EFI_ERROR (Status)) {
v38 = TRUE;
//
// Open for Legacy MBR too
//
Status = gBS->OpenProtocol (
DeviceHandle,
&gEfiLegacyMbrProtocolGuid,
(VOID **)&Private->LegacyMbr,
Private->Handle,
DeviceHandle,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (!EFI_ERROR (Status)) {
//
// Legacy MBR is available -- read device type via Legacy Region
//
LegacyRegion = Private->LegacyRegion;
Status = LegacyRegion->Read (
LegacyRegion,
0LL,
60,
1,
&DeviceType
);
if (EFI_ERROR (Status)) {
CsmDebugPrint (0x80000000, "\nASSERT_EFI_ERROR (Status = %r)\n", Status);
CsmAssert (
"e:\\hs\\AmiModulePkg\\CSM\\CsmBlockIo\\CsmBlockIo.c",
356,
"!EFI_ERROR (Status)"
);
}
if (DeviceType <= 0x0D) {
Status = gBS->LocateProtocol (
&gCsmBlockIoInternalGuid,
NULL,
(VOID **)&Private->Internal
);
if (EFI_ERROR (Status)) {
CsmDebugPrint (0x80000000, "\nASSERT_EFI_ERROR (Status = %r)\n", Status);
CsmAssert (
"e:\\hs\\AmiModulePkg\\CSM\\CsmBlockIo\\CsmBlockIo.c",
359,
"!EFI_ERROR (Status)"
);
}
Status = Private->Internal->GetDeviceType (
Private->Internal,
DeviceType,
&DeviceType
);
if (EFI_ERROR (Status)) {
CsmDebugPrint (0x80000000, "\nASSERT_EFI_ERROR (Status = %r)\n", Status);
CsmAssert (
"e:\\hs\\AmiModulePkg\\CSM\\CsmBlockIo\\CsmBlockIo.c",
362,
"!EFI_ERROR (Status)"
);
}
}
}
}
Private->BbsType = mIplDtTable[DeviceType];
//
// Get transfer buffer base from legacy region (offset 4)
//
Private->TransferBuffer = Private->LegacyRegion->GetBase (
Private->LegacyRegion, 4, 0
);
//
// Configure transfer buffer flags (mask 0x700)
//
Private->TransferBuffer = Private->LegacyRegion->SetFlags (
Private->LegacyRegion,
2,
Private->TransferBuffer & 0x700
);
//
// Call BIOS INT13 handler entry
//
Status = Private->BbsTable->Init (
Private->BbsTable,
DeviceHandle,
0,
0,
Private->Int13Status
);
if (EFI_ERROR (Status)) {
goto Cleanup;
}
//
// Get BBS table
//
Status = Private->BbsTable->GetTable (
&Private->BbsEntry,
Private->Int13Packet
);
if (EFI_ERROR (Status)) {
CsmDebugPrint (0x80000000, "\nASSERT_EFI_ERROR (Status = %r)\n", Status);
CsmAssert (
"e:\\hs\\AmiModulePkg\\CSM\\CsmBlockIo\\CsmBlockIo.c",
397,
"!EFI_ERROR (Status)"
);
}
//
// Count current BBS entries
//
for (Index = 0; Index < 256; Index++) {
if (Private->BbsEntry[Index] != 0xFFFF) {
BbsBitmap[Index] = 1;
BbsBeforeCount++;
}
}
//
// Execute INT13 to discover drives
//
Status = Private->BbsTable->Execute (
Private->BbsTable,
DeviceHandle,
0,
Private->Int13Status,
&DeviceIndex,
Private->Int13Packet,
0,
0
);
if (EFI_ERROR (Status)) {
goto Cleanup;
}
//
// Count post-INT13 BBS entries
//
for (Index = 0; Index < 256; Index++) {
if (Private->BbsEntry[Index] != 0xFFFF) {
if (!BbsBitmap[Index] && !BbsAfterCount) {
BbsAfterCount = Index;
}
BbsNewCount++;
}
}
if (BbsNewCount < BbsBeforeCount) {
CsmAssert (
"e:\hs\\AmiiModulePkg\\CSM\\CsmBlockIo\\CsmBlockIo.c",
435,
"NumberOfBbsEntriesAfterOpron >= NumberOfBbsEntriesBeforeOpron"
);
}
DeviceIndex = BbsNewCount - BbsBeforeCount;
Private->BbsType = mIplDtTable[DeviceType];
if (mIplDtHandleCounter == mIplDtHandleCounter) {
mIplDtHandleCounter = 0xFFFF;
Status = gBS->AllocatePool (
1,
4,
16,
&mIplDtHandleCounter
);
if (EFI_ERROR (Status)) {
CsmDebugPrint (0x80000000, "\nASSERT_EFI_ERROR (Status = %r)\n", Status);
CsmAssert (
"e:\\hs\\AmiModulePkg\\CSM\\CsmBlockIo\\CsmBlockIo.c",
635,
"!EFI_ERROR (Status)"
);
}
mIplDtHandleCounter = 0;
}
return EFI_SUCCESS;
Cleanup:
gBS->CloseProtocol (
DeviceHandle,
&gEfiLegacyRegionProtocolGuid,
Private->Handle,
DeviceHandle
);
gBS->CloseProtocol (
DeviceHandle,
&gEfiBlockIoProtocolGuid,
Private->Handle,
DeviceHandle
);
return Status;
}
//
// ============================================================================
// sub_FA8 -- Close / Cleanup INT13 for a device
// Called when a device handle is removed or when shutting down CSM
// block I/O legacy support.
// ============================================================================
//
EFI_STATUS
CsmBlockIoCloseDevice (
IN CSM_BLOCK_IO_PRIVATE *Private,
IN EFI_HANDLE Handle,
IN UINTN DeviceCount,
IN EFI_HANDLE *DeviceHandles
)
{
EFI_STATUS Status;
BOOLEAN PlatformAvailable;
UINTN Index;
EFI_LEGACY_REGION *LegacyRegion;
//
// Decrement reference count on IPLDT allocation
//
if (--mIplDtRefCount == 0) {
Status = gBS->FreePool (mIplDtHandleCounter, 16);
if (EFI_ERROR (Status)) {
CsmDebugPrint (0x80000000, "\nASSERT_EFI_ERROR (Status = %r)\n", Status);
CsmAssert (
"e:\\hs\\AmiModulePkg\\CSM\\CsmBlockIo\\CsmBlockIo.c",
635,
"!EFI_ERROR (Status)"
);
}
mIplDtHandleCounter = 0;
}
PlatformAvailable = FALSE;
Status = gBS->OpenProtocol (
Handle,
&gEfiLegacyBiosPlatformProtocolGuid,
(VOID **)&Private->LegacyBiosPlatform,
Private->Handle,
Handle,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (!EFI_ERROR (Status)) {
PlatformAvailable = TRUE;
}
//
// Process each device handle
//
for (Index = 0; Index < DeviceCount; Index++) {
//
// Open Disk Info protocol on the child handle
//
Status = gBS->OpenProtocol (
DeviceHandles[Index],
&gEfiDiskInfoProtocolGuid,
(VOID **)&Private->DiskInfo,
Private->Handle,
Handle,
2
);
if (EFI_ERROR (Status)) {
return Status;
}
LegacyRegion = Private->LegacyRegion;
//
// If Legacy BIOS Platform is available, transfer to it
//
if (PlatformAvailable) {
gBS->CloseProtocol (
Handle,
&gEfiBlockIoProtocolGuid,
Private->Handle,
DeviceHandles[Index]
);
}
//
// Free the INT13 handler's memory
//
gBS->FreePool (LegacyRegion->GetInt13Handler (LegacyRegion));
//
// Install the protocol interface on the child handle
//
Status = gBS->InstallProtocolInterface (
&DeviceHandles[Index],
&gEfiDiskInfoProtocolGuid,
(VOID *)LegacyRegion,
LegacyRegion->GetInterface (LegacyRegion),
&gEfiBlockIoProtocolGuid,
LegacyRegion->GetBlockIo (LegacyRegion),
0
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Set legacy region flags
//
LegacyRegion->SetFlags (LegacyRegion, 3, 0x700, 0);
//
// If Platform is not available, close BlockIo
//
if (!PlatformAvailable) {
gBS->CloseProtocol (
Handle,
&gEfiLegacyRegionProtocolGuid,
Private->Handle,
DeviceHandles[Index]
);
}
gBS->FreePool (LegacyRegion);
}
//
// Final cleanup
//
gBS->CloseProtocol (
Handle,
&gEfiBlockIoProtocolGuid,
Private->Handle,
Handle
);
if (!PlatformAvailable) {
gBS->CloseProtocol (
Handle,
&gEfiLegacyRegionProtocolGuid,
Private->Handle,
Handle
);
}
return EFI_SUCCESS;
}
//
// ============================================================================
// sub_1468 -- Find device in IPLDT (Legacy Device Table)
// Searches the IPLDT for a matching bus:device tuple and sets
// the device index.
// ============================================================================
//
VOID
CsmFindInIplDt (
IN CSM_BLOCK_IO_PRIVATE *Private,
IN EFI_HANDLE DeviceHandle
)
{
UINT8 Index;
UINTN EntryOffset;
UINT16 Bus, Device;
Bus = *(UINT16 *)((UINT8 *)DeviceHandle + 264); // Bus number
Device = *(UINT16 *)((UINT8 *)DeviceHandle + 266); // Device number
CsmDebugPrint (0x80000000, "CsmBlockIo L2E: BCV at %x:%x\n", Bus, Device);
EntryOffset = 16 * mIplDtCurrentEntry + 0x460;
for (Index = 0; Index < 32; Index++) {
if (*(UINT16 *)(EntryOffset + 14) == Device &&
*(UINT16 *)(EntryOffset + 16) == Bus) {
break;
}
EntryOffset += 64; // IPLDT_ENTRY_SIZE
}
if (Index == 32) {
CsmDebugPrint (0x80000000, "Entry is not found in IPLDT\n");
return;
}
Private->DeviceIndex = *(UINT8 *)(mIplDtCurrentEntry * 16 + Index + 1);
CsmDebugPrint (0x80000000, "IPLDT[%x], index %x, handle %x\n",
Index, *(UINT8 *)(EntryOffset + 2), Private->DeviceIndex);
if (Private->DeviceIndex <= 0x7F) {
CsmAssert (
"e:\\hs\\AmiModulePkg\\CSM\\CsmBlockIo\\CsmBlockIo.c",
886,
"Handle > 0x7f"
);
}
Private->CsmDeviceIndex = Private->DeviceIndex;
}
//
// ============================================================================
// sub_1570 -- INT13 handler initialization
// Called to set up INT13 handler function table for a device.
// ============================================================================
//
BOOLEAN
CsmInitInt13Handler (
IN CSM_BLOCK_IO_PRIVATE *Private
)
{
//
// Initialize the media descriptor pointer and INT13 saved state array
//
Private->Media = &Private->MediaDescriptor;
Private->Int13SavedFlagsBase = (UINT32 *)&Private->Int13SavedFlags;
//
// Set signature based on system table revision
//
if (gST->Hdr.Revision < 0x2001F) {
Private->Signature = 0x1FFF1;
} else {
Private->Signature = 0x20031;
}
//
// Identify and set up drive parameters
//
if (!Int13IdentifyDevice (Private, (UINT8 *)&Private->DeviceInfo)) {
return FALSE;
}
if (Int13GetDriveParameters (Private, (UINT8 *)&Private->DeviceInfo)) {
//
// LBA device -- use extended INT13
//
Private->LastBlock = Private->DeviceInfo.MaxSector - 1;
Private->BlockSize = Private->DeviceInfo.BytesPerSector;
if ((Private->DeviceInfo.Flags & 4) != 0) {
Private->RemovableMedia = 1;
}
} else {
//
// CHS device
//
Private->BlockSize = 512;
Private->LastBlock = (UINT64)(
(Private->DeviceInfo.Heads + 1) *
(Private->DeviceInfo.Cylinders + 1) *
Private->DeviceInfo.Sectors
) - 1;
}
//
// Only support 512-byte sectors
//
if (Private->BlockSize != 512) {
return FALSE;
}
//
// Set up INT13 handler function table
//
Private->MediaId = 0;
Private->MediaPresent = 1;
Private->Alignment = 1;
Private->Int13DiskIo = (UINT64)Int13GetStatus;
Private->Int13ErrorHandler = (UINT64)Int13ErrorStub;
if (Private->DeviceInfo.ExtInt13Support) {
//
// Extended INT13 device -- use LBA handlers
//
Private->Int13ReadWrite = (UINT64)Int13ReadWriteChs;
Private->Int13Extensions = (UINT64)Int13ExtendedRead;
} else {
//
// CHS-only device
//
Private->Int13ReadWrite = (UINT64)Int13ChsReadWrite;
Private->Int13Extensions = (UINT64)Int13ExtendedWrite;
}
Private->DmaSupported = 0;
Private->Alignment = 0;
if (gST->Hdr.Revision >= 0x2001F) {
Private->Alignment = 0;
Private->AlignmentShift = Private->BlockSize;
Private->AlignmentMask = 1;
}
return TRUE;
}
//
// ============================================================================
// sub_16B0 -- Identify device parameters via INT13
// Issues INT13 function 0x13 to get drive parameters
// ============================================================================
//
BOOLEAN
Int13IdentifyDevice (
IN CSM_BLOCK_IO_PRIVATE *Private,
OUT UINT8 *DeviceInfo
)
{
UINT8 DeviceParam;
DeviceParam = DeviceInfo[1];
DeviceInfo[1] = 8;
Int13SaveState (Private);
(*Private->Int13HandlerEntry) (Private, 0x13, (UINT8 *)&DeviceParam);
Int13RestoreState (Private);
if ((Private->Int13Flags & 1) != 0 || DeviceInfo[1] != 0) {
DeviceInfo[8] = DeviceInfo[1];
return FALSE;
}
if (DeviceInfo[24] == 0) {
//
// CHS device -- extract geometry
//
DeviceInfo[26] = DeviceInfo[13] & 0x3F; // Sectors
*(UINT16 *)&DeviceInfo[28] = (UINT16)(
DeviceInfo[11] + 4 * (DeviceInfo[10] & 0xC0 |
(4 * (DeviceInfo[13] & 0xC0)))
);
DeviceInfo[27] = DeviceInfo[10] & 0x3F; // Heads
return TRUE;
}
if (DeviceInfo[4] == 16) {
DeviceInfo[25] = 1; // ATAPI device
return TRUE;
}
DeviceInfo[26] = DeviceInfo[13]; // Sectors
*(UINT16 *)&DeviceInfo[28] = *(UINT16 *)&DeviceInfo[11]; // Cylinders
DeviceInfo[27] = DeviceInfo[10]; // Heads
if (DeviceInfo[27]) {
return TRUE;
}
return FALSE;
}
//
// ============================================================================
// sub_1784 -- Get extended drive parameters (INT13 function 0x48)
// For EDD-3.0 compatible devices, retrieves full geometry.
// ============================================================================
//
BOOLEAN
Int13GetDriveParameters (
IN CSM_BLOCK_IO_PRIVATE *Private,
OUT UINT8 *DriveParams
)
{
UINT8 ParamPacket;
ParamPacket = 65; // parameter packet size (41h)
DriveParams[2] = 21930; // signature
DriveParams[1] = 8;
Int13SaveState (Private);
(*Private->Int13HandlerEntry) (Private, 0x13, &ParamPacket);
Int13RestoreState (Private);
if ((Private->Int13Flags & 1) != 0 || *(UINT16 *)&DriveParams[2] != (UINT16)~21931) {
DriveParams[3] = 0;
DriveParams[5] = 0;
return FALSE;
}
DriveParams[2] = ParamPacket; // sector size low
DriveParams[4] = (DriveParams[1] & 2) != 0; // DMA
DriveParams[6] = DriveParams[1] & 8; // LBA
DriveParams[5] = (DriveParams[1] & 4) != 0; // removable
DriveParams[3] = DriveParams[1] & 1; // extended INT13
//
// Get CHS geometry from INT13 function
//
DriveParams[7] = Int13ChsGeometry (Private, DriveParams);
return TRUE;
}
//
// ============================================================================
// sub_1834 -- Get CHS geometry from INT13 (function 0x08 or ATA identify)
// ============================================================================
//
BOOLEAN
Int13ChsGeometry (
IN CSM_BLOCK_IO_PRIVATE *Private,
IN UINT8 *DriveParams
)
{
UINT32 CmosParams[8];
UINT8 ParamPacket;
UINT8 FunctionCode;
//
// Initialize CMOS parameter table for geometry decode
//
CmosParams[0] = 0x014F001;
CmosParams[1] = 0x01004012;
CmosParams[2] = 0x4F200000;
CmosParams[3] = 0x100;
CmosParams[4] = 0x1000001;
CmosParams[5] = 0x10000;
CmosParams[6] = 0x1000400;
CmosParams[7] = 0x0F000009;
//
// Set up INT13 function 0x08 parameter block
//
ParamPacket = 'O'; // 79
FunctionCode = 'H'; // 72
*(UINT16 *)(mInt13TransferSegment2 + 32) = 74;
CmosParams[3] = 0x100;
*(UINT16 *)((UINT64)&CmosParams[3] + 2) = ((UINT64)(mInt13TransferSegment2 + 32) >> 4) & 0xF000;
Int13SaveState (Private);
(*Private->Int13HandlerEntry) (Private, 0x13, &ParamPacket);
if ((Private->Int13Flags & 1) == 0 && FunctionCode == 0) {
//
// Success -- read geometry from transfer buffer
//
gBS->SetMem (
(VOID *)((UINTN)DriveParams + 32),
74,
0
);
gBS->CopyMem (
(VOID *)((UINTN)DriveParams + 32),
(VOID *)(mInt13TransferSegment2 + 32),
74
);
if (DriveParams[25] != 0) {
//
// EDD device -- read more geometry
//
DriveParams[1] = 8;
ParamPacket = ' ';
FunctionCode = 32;
(*Private->Int13HandlerEntry) (Private, 0x13, &ParamPacket);
if ((Private->Int13Flags & 1) != 0) {
if ((DriveParams[34] & 2) != 0) {
DriveParams[26] = DriveParams[40] - 1; // Heads
DriveParams[27] = DriveParams[44]; // Sectors
if (DriveParams[27] == 0) {
CsmAssert (
"e:\\hs\\AmiModulePkg\\CSM\\CsmBlockIo\\CsmInt13.c",
344,
"Drive->MaxSector != 0"
);
}
}
} else {
//
// Decode CHS from function 0x08 response
//
if (FunctionCode != 10) {
if ((UINT8)(FunctionCode - 3) <= 10) {
DriveParams[26] = CmosParams[(UINT8)(3 * (FunctionCode - 3))];
DriveParams[27] = CmosParams[(UINT8)(3 * (FunctionCode - 3) + 1)];
*(UINT16 *)&DriveParams[28] = (UINT16)CmosParams[(UINT8)(3 * (FunctionCode - 3) + 2)];
} else {
DriveParams[26] = 256;
}
} else {
if ((DriveParams[34] & 2) != 0) {
DriveParams[26] = DriveParams[40] - 1;
DriveParams[27] = DriveParams[44];
if (DriveParams[27] == 0) {
CsmAssert (
"e:\\hs\\AmiModulePkg\\CSM\\CsmBlockIo\\CsmInt13.c",
369,
"Drive->MaxSector != 0"
);
}
*(UINT16 *)&DriveParams[28] = *(UINT16 *)&DriveParams[36] - 1;
} else {
DriveParams[26] = 256;
}
}
//
// Calculate total sectors
//
*(UINT64 *)&DriveParams[48] =
(UINT64)(DriveParams[26] + 1) *
(*(UINT16 *)&DriveParams[28] + 1) *
DriveParams[27];
*(UINT16 *)&DriveParams[56] = 512; // bytes per sector
}
}
//
// Set total sectors from CHS
//
*(UINT64 *)&DriveParams[16] =
*(UINT16 *)&DriveParams[58] +
16 * (((UINT32)*(UINT16 *)&DriveParams[58] >> 4) & 0xF000);
Int13RestoreState (Private);
return TRUE;
}
Int13RestoreState (Private);
*(UINT8 *)&DriveParams[8] = FunctionCode;
gBS->SetMem (
(VOID *)((UINTN)DriveParams + 32),
74,
(UINT8)(-81)
);
return FALSE;
}
//
// ============================================================================
// sub_11E4 -- Build BBS device type ID
// Parses device path media type string and generates BBS device type ID
// for the IPLDT / BBS table.
// ============================================================================
//
UINT64
EFIAPI
BbsDeviceTypeBuilder (
IN CSM_BLOCK_IO_PRIVATE *Private,
IN VOID *DevicePath,
OUT UINT64 *OutBbsType
)
{
UINT64 Result;
UINT32 TypeId;
UINT8 TypeFields[12];
UINT8 *MediaType;
UINT8 InterfaceType; // offset +2 from device path
UINT16 DevicePathLen;
//
// Default type for unrecognized devices
//
*(UINT64 *)&TypeFields[0] = 0xC51FAC4F3F2C1D11ULL;
*(UINT64 *)&TypeFields[4] = 0xF38500A0C93EC9B3ULL;
//
// Validate device path node type
//
if (*(UINT8 *)((UINTN)DevicePath + 2) != 0x30) { // MESSAGING_DEVICE_PATH
goto UseDefault;
}
if (*(UINT16 *)((UINTN)DevicePath + 32) <= 0x1A) { // length too small
goto UseDefault;
}
MediaType = (UINT8 *)((UINTN)DevicePath + 72);
InterfaceType = *(UINT8 *)((UINTN)DevicePath + 83);
if (*(UINT32 *)((UINTN)DevicePath + 72) == 0) { // no media type
goto UseDefault;
}
//
// Compare media type strings
//
// ATAPI
if (AsciiStrnCmp ((CHAR8 *)MediaType, MEDIA_ATAPI, 5) == 0) {
TypeFields[0] = InterfaceType;
TypeFields[1] = *(UINT8 *)((UINTN)DevicePath + 88);
TypeFields[2] = *(UINT8 *)((UINTN)DevicePath + 89);
TypeId = 0x080247; // ATA master
goto Done;
}
// ATA (same as ATAPI)
if (AsciiStrnCmp ((CHAR8 *)MediaType, MEDIA_ATA, 3) == 0) {
TypeFields[0] = InterfaceType;
TypeFields[1] = *(UINT8 *)((UINTN)DevicePath + 88);
TypeFields[2] = *(UINT8 *)((UINTN)DevicePath + 89);
TypeId = 0x080247;
goto Done;
}
// SCSI
if (AsciiStrnCmp ((CHAR8 *)MediaType, MEDIA_SCSI, 4) != 0) {
// USB
if (AsciiStrnCmp ((CHAR8 *)MediaType, MEDIA_USB, 3) != 0) {
// 1394
if (AsciiStrnCmp ((CHAR8 *)MediaType, MEDIA_1394, 4) != 0) {
// FIBRE
if (AsciiStrnCmp ((CHAR8 *)MediaType, MEDIA_FIBRE, 5) != 0) {
goto UseDefault;
}
// FIBRE
*(UINT64 *)&TypeFields[4] = *(UINT64 *)((UINTN)DevicePath + 88);
TypeId = 0x180403;
goto Done;
}
// 1394
*(UINT64 *)&TypeFields[4] = *(UINT64 *)((UINTN)DevicePath + 88);
TypeId = 0x100403;
goto Done;
}
// USB
TypeFields[0] = *(UINT8 *)((UINTN)DevicePath + 96);
TypeId = 0x060503;
goto Done;
}
// SCSI
*(UINT32 *)&TypeFields[0] = *(UINT32 *)((UINTN)DevicePath + 88);
TypeId = 0x080203;
goto Done;
UseDefault:
TypeFields[0] = *(UINT8 *)((UINTN)DevicePath + 1);
TypeId = 0x150101;
*(UINT64 *)&TypeFields[0] = *(UINT64 *)&TypeFields[0]; // default GUID-like type
goto Done;
Done:
Result = CsmBbsTableAllocate (Private, &TypeId);
*OutBbsType = Result;
return Result;
}