2004-11-09 23:36:20 +00:00
/*
* FreeLoader
* Copyright ( C ) 1998 - 2003 Brian Palmer < brianp @ sginet . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include <freeldr.h>
# include <disk.h>
# include <rtl.h>
# include <arch.h>
# include <debug.h>
# include <portio.h>
typedef struct
{
U8 PacketSize ; // 00h - Size of packet (10h or 18h)
U8 Reserved ; // 01h - Reserved (0)
U16 LBABlockCount ; // 02h - Number of blocks to transfer (max 007Fh for Phoenix EDD)
U16 TransferBufferOffset ; // 04h - Transfer buffer offset (seg:off)
U16 TransferBufferSegment ; // Transfer buffer segment (seg:off)
U64 LBAStartBlock ; // 08h - Starting absolute block number
U64 TransferBuffer64 ; // 10h - (EDD-3.0, optional) 64-bit flat address of transfer buffer
// used if DWORD at 04h is FFFFh:FFFFh
} PACKED I386_DISK_ADDRESS_PACKET , * PI386_DISK_ADDRESS_PACKET ;
/////////////////////////////////////////////////////////////////////////////////////////////
// FUNCTIONS
/////////////////////////////////////////////////////////////////////////////////////////////
static BOOL PcDiskResetController ( U32 DriveNumber )
{
REGS RegsIn ;
REGS RegsOut ;
DbgPrint ( ( DPRINT_DISK , " PcDiskResetController(0x%x) DISK OPERATION FAILED -- RESETTING CONTROLLER \n " , DriveNumber ) ) ;
// BIOS Int 13h, function 0 - Reset disk system
// AH = 00h
// DL = drive (if bit 7 is set both hard disks and floppy disks reset)
// Return:
// AH = status
// CF clear if successful
// CF set on error
RegsIn . b . ah = 0x00 ;
RegsIn . b . dl = DriveNumber ;
// Reset the disk controller
Int386 ( 0x13 , & RegsIn , & RegsOut ) ;
return INT386_SUCCESS ( RegsOut ) ;
}
static BOOL PcDiskReadLogicalSectorsLBA ( U32 DriveNumber , U64 SectorNumber , U32 SectorCount , PVOID Buffer )
{
REGS RegsIn ;
REGS RegsOut ;
U32 RetryCount ;
PI386_DISK_ADDRESS_PACKET Packet = ( PI386_DISK_ADDRESS_PACKET ) ( BIOSCALLBUFFER ) ;
DbgPrint ( ( DPRINT_DISK , " PcDiskReadLogicalSectorsLBA() DriveNumber: 0x%x SectorNumber: %I64d SectorCount: %d Buffer: 0x%x \n " , DriveNumber , SectorNumber , SectorCount , Buffer ) ) ;
// BIOS int 0x13, function 42h - IBM/MS INT 13 Extensions - EXTENDED READ
RegsIn . b . ah = 0x42 ; // Subfunction 42h
RegsIn . b . dl = DriveNumber ; // Drive number in DL (0 - floppy, 0x80 - harddisk)
RegsIn . x . ds = BIOSCALLBUFSEGMENT ; // DS:SI -> disk address packet
RegsIn . w . si = BIOSCALLBUFOFFSET ;
// Setup disk address packet
RtlZeroMemory ( Packet , sizeof ( I386_DISK_ADDRESS_PACKET ) ) ;
Packet - > PacketSize = sizeof ( I386_DISK_ADDRESS_PACKET ) ;
Packet - > Reserved = 0 ;
Packet - > LBABlockCount = SectorCount ;
Packet - > TransferBufferOffset = ( ( U32 ) Buffer ) & 0x0F ;
Packet - > TransferBufferSegment = ( ( U32 ) Buffer ) > > 4 ;
Packet - > LBAStartBlock = SectorNumber ;
Packet - > TransferBuffer64 = 0 ;
// BIOS int 0x13, function 42h - IBM/MS INT 13 Extensions - EXTENDED READ
// Return:
// CF clear if successful
// AH = 00h
// CF set on error
// AH = error code
// disk address packet's block count field set to the
// number of blocks successfully transferred
// Retry 3 times
for ( RetryCount = 0 ; RetryCount < 3 ; RetryCount + + )
{
Int386 ( 0x13 , & RegsIn , & RegsOut ) ;
// If it worked return TRUE
if ( INT386_SUCCESS ( RegsOut ) )
{
return TRUE ;
}
// If it was a corrected ECC error then the data is still good
else if ( RegsOut . b . ah = = 0x11 )
{
return TRUE ;
}
// If it failed the do the next retry
else
{
PcDiskResetController ( DriveNumber ) ;
continue ;
}
}
// If we get here then the read failed
DiskError ( " Disk Read Failed " , RegsOut . b . ah ) ;
return FALSE ;
}
static BOOL PcDiskReadLogicalSectorsCHS ( U32 DriveNumber , U64 SectorNumber , U32 SectorCount , PVOID Buffer )
{
U32 PhysicalSector ;
U32 PhysicalHead ;
U32 PhysicalTrack ;
GEOMETRY DriveGeometry ;
U32 NumberOfSectorsToRead ;
REGS RegsIn ;
REGS RegsOut ;
U32 RetryCount ;
DbgPrint ( ( DPRINT_DISK , " PcDiskReadLogicalSectorsCHS() \n " ) ) ;
//
// Get the drive geometry
//
if ( ! DiskGetDriveGeometry ( DriveNumber , & DriveGeometry ) )
{
return FALSE ;
}
while ( SectorCount )
{
//
// Calculate the physical disk offsets
//
PhysicalSector = 1 + ( SectorNumber % DriveGeometry . Sectors ) ;
PhysicalHead = ( SectorNumber / DriveGeometry . Sectors ) % DriveGeometry . Heads ;
PhysicalTrack = ( SectorNumber / DriveGeometry . Sectors ) / DriveGeometry . Heads ;
//
// Calculate how many sectors we need to read this round
//
if ( PhysicalSector > 1 )
{
if ( SectorCount > = ( DriveGeometry . Sectors - ( PhysicalSector - 1 ) ) )
NumberOfSectorsToRead = ( DriveGeometry . Sectors - ( PhysicalSector - 1 ) ) ;
else
NumberOfSectorsToRead = SectorCount ;
}
else
{
if ( SectorCount > = DriveGeometry . Sectors )
NumberOfSectorsToRead = DriveGeometry . Sectors ;
else
NumberOfSectorsToRead = SectorCount ;
}
//
// Make sure the read is within the geometry boundaries
//
if ( ( PhysicalHead > = DriveGeometry . Heads ) | |
( PhysicalTrack > = DriveGeometry . Cylinders ) | |
( ( NumberOfSectorsToRead + PhysicalSector ) > ( DriveGeometry . Sectors + 1 ) ) | |
( PhysicalSector > DriveGeometry . Sectors ) )
{
DiskError ( " Disk read exceeds drive geometry limits. " , 0 ) ;
return FALSE ;
}
// BIOS Int 13h, function 2 - Read Disk Sectors
// AH = 02h
// AL = number of sectors to read (must be nonzero)
// CH = low eight bits of cylinder number
// CL = sector number 1-63 (bits 0-5)
// high two bits of cylinder (bits 6-7, hard disk only)
// DH = head number
// DL = drive number (bit 7 set for hard disk)
// ES:BX -> data buffer
// Return:
// CF set on error
// if AH = 11h (corrected ECC error), AL = burst length
// CF clear if successful
// AH = status
// AL = number of sectors transferred
// (only valid if CF set for some BIOSes)
RegsIn . b . ah = 0x02 ;
RegsIn . b . al = NumberOfSectorsToRead ;
RegsIn . b . ch = ( PhysicalTrack & 0xFF ) ;
RegsIn . b . cl = ( PhysicalSector + ( ( PhysicalTrack & 0x300 ) > > 2 ) ) ;
RegsIn . b . dh = PhysicalHead ;
RegsIn . b . dl = DriveNumber ;
RegsIn . w . es = ( ( U32 ) Buffer ) > > 4 ;
RegsIn . w . bx = ( ( U32 ) Buffer ) & 0x0F ;
//
// Perform the read
// Retry 3 times
//
for ( RetryCount = 0 ; RetryCount < 3 ; RetryCount + + )
{
Int386 ( 0x13 , & RegsIn , & RegsOut ) ;
// If it worked break out
if ( INT386_SUCCESS ( RegsOut ) )
{
break ;
}
// If it was a corrected ECC error then the data is still good
else if ( RegsOut . b . ah = = 0x11 )
{
break ;
}
// If it failed the do the next retry
else
{
PcDiskResetController ( DriveNumber ) ;
continue ;
}
}
// If we retried 3 times then fail
if ( RetryCount > = 3 )
{
DiskError ( " Disk Read Failed " , RegsOut . b . ah ) ;
return FALSE ;
}
// I have learned that not all bioses return
// the sector read count in the AL register (at least mine doesn't)
// even if the sectors were read correctly. So instead
// of checking the sector read count we will rely solely
// on the carry flag being set on error
Buffer + = ( NumberOfSectorsToRead * DriveGeometry . BytesPerSector ) ;
SectorCount - = NumberOfSectorsToRead ;
SectorNumber + = NumberOfSectorsToRead ;
}
return TRUE ;
}
static BOOL PcDiskInt13ExtensionsSupported ( U32 DriveNumber )
{
REGS RegsIn ;
REGS RegsOut ;
DbgPrint ( ( DPRINT_DISK , " PcDiskInt13ExtensionsSupported() \n " ) ) ;
// IBM/MS INT 13 Extensions - INSTALLATION CHECK
// AH = 41h
// BX = 55AAh
// DL = drive (80h-FFh)
// Return:
// CF set on error (extensions not supported)
// AH = 01h (invalid function)
// CF clear if successful
// BX = AA55h if installed
// AH = major version of extensions
// 01h = 1.x
// 20h = 2.0 / EDD-1.0
// 21h = 2.1 / EDD-1.1
// 30h = EDD-3.0
// AL = internal use
// CX = API subset support bitmap
// DH = extension version (v2.0+ ??? -- not present in 1.x)
//
// Bitfields for IBM/MS INT 13 Extensions API support bitmap
// Bit 0, extended disk access functions (AH=42h-44h,47h,48h) supported
// Bit 1, removable drive controller functions (AH=45h,46h,48h,49h,INT 15/AH=52h) supported
// Bit 2, enhanced disk drive (EDD) functions (AH=48h,AH=4Eh) supported
// extended drive parameter table is valid
// Bits 3-15 reserved
RegsIn . b . ah = 0x41 ;
RegsIn . w . bx = 0x55AA ;
RegsIn . b . dl = DriveNumber ;
// Reset the disk controller
Int386 ( 0x13 , & RegsIn , & RegsOut ) ;
if ( ! INT386_SUCCESS ( RegsOut ) )
{
// CF set on error (extensions not supported)
return FALSE ;
}
if ( RegsOut . w . bx ! = 0xAA55 )
{
// BX = AA55h if installed
return FALSE ;
}
// Note:
// The original check is too strict because some BIOSes report that
// extended disk access functions are not suported when booting
// from a CD (e.g. Phoenix BIOS v6.00PG). Argh!
#if 0
if ( ! ( RegsOut . w . cx & 0x0001 ) )
{
// CX = API subset support bitmap
// Bit 0, extended disk access functions (AH=42h-44h,47h,48h) supported
return FALSE ;
}
# endif
// Use this relaxed check instead
if ( RegsOut . w . cx = = 0x0000 )
{
// CX = API subset support bitmap
return FALSE ;
}
return TRUE ;
}
BOOL PcDiskReadLogicalSectors ( U32 DriveNumber , U64 SectorNumber , U32 SectorCount , PVOID Buffer )
{
DbgPrint ( ( DPRINT_DISK , " PcDiskReadLogicalSectors() DriveNumber: 0x%x SectorNumber: %I64d SectorCount: %d Buffer: 0x%x \n " , DriveNumber , SectorNumber , SectorCount , Buffer ) ) ;
//
// Check to see if it is a fixed disk drive
// If so then check to see if Int13 extensions work
// If they do then use them, otherwise default back to BIOS calls
//
if ( ( DriveNumber > = 0x80 ) & & PcDiskInt13ExtensionsSupported ( DriveNumber ) )
{
DbgPrint ( ( DPRINT_DISK , " Using Int 13 Extensions for read. PcDiskInt13ExtensionsSupported(%d) = %s \n " , DriveNumber , PcDiskInt13ExtensionsSupported ( DriveNumber ) ? " TRUE " : " FALSE " ) ) ;
//
// LBA is easy, nothing to calculate
// Just do the read
//
return PcDiskReadLogicalSectorsLBA ( DriveNumber , SectorNumber , SectorCount , Buffer ) ;
}
else
{
// LBA is not supported default to the CHS calls
return PcDiskReadLogicalSectorsCHS ( DriveNumber , SectorNumber , SectorCount , Buffer ) ;
}
return TRUE ;
}
2004-11-10 23:45:37 +00:00
BOOL
PcDiskGetPartitionEntry ( U32 DriveNumber , U32 PartitionNumber , PPARTITION_TABLE_ENTRY PartitionTableEntry )
{
/* Just use the standard routine */
return DiskGetPartitionEntry ( DriveNumber , PartitionNumber , PartitionTableEntry ) ;
}
/* EOF */