/* * FreeLoader * Copyright (C) 1998-2002 Brian Palmer * * 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 02139, USA. */ .text .code16 #define ASM #include "arch.h" /* * BOOL BiosInt13Read(ULONG Drive, ULONG Head, ULONG Track, ULONG Sector, ULONG SectorCount, PVOID Buffer); */ _biosdisk_drive: .long 0 _biosdisk_head: .long 0 _biosdisk_track: .long 0 _biosdisk_sector: .long 0 _biosdisk_nsects: .long 0 _biosdisk_buffer: .long 0 _biosdisk_retval: .long 0 _biosdisk_retrycount: .byte 0 _biosdisk_error_code: .byte 0 EXTERN(_BiosInt13Read) .code32 push %ebp push %esi push %edi push %ebx push %ecx push %edx /* Get parameters */ movl 0x1c(%esp),%eax movl %eax,_biosdisk_drive movl 0x20(%esp),%eax movl %eax,_biosdisk_head movl 0x24(%esp),%eax movl %eax,_biosdisk_track movl 0x28(%esp),%eax movl %eax,_biosdisk_sector movl 0x2c(%esp),%eax movl %eax,_biosdisk_nsects movl 0x30(%esp),%eax movl %eax,_biosdisk_buffer call switch_to_real .code16 pushw %es // Save this just in case movb $3,_biosdisk_retrycount // Set the retry count to 3 _biosdisk_read: movl _biosdisk_buffer,%eax // Get buffer address in eax shrl $4,%eax // Make linear address into segment movw %ax,%es // Load ES with segment movl _biosdisk_buffer,%ebx // and BX with offset andl $0x0f,%ebx // so that data gets loaded to [ES:BX] movb _biosdisk_sector,%cl // Get the sector in CL movw _biosdisk_track,%ax // Cylinder in AX movb %al,%ch // Now put it in CH rorb $1,%ah // Low 8 bits of cylinder in CH, high 2 bits rorb $1,%ah // in CL shifted to bits 6 & 7 andb $0xc0,%ah // Clear out low six bits orb %ah,%cl // Or with sector number movb _biosdisk_head,%dh // Get the head movb _biosdisk_drive,%dl // Get the drive movb $2,%ah // BIOS int 0x13, function 2 - Read Disk Sectors movb _biosdisk_nsects,%al // Number of sectors to read int $0x13 // Read a sector // I have recently 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 //jmp _biosdisk_done //cmpb _biosdisk_nsects,%al // See how many sectors we actually read //jne _biosdisk_error // Jump if no error movb $1,%al // Set the return value to be one (will be set to zero later if needed) jc _biosdisk_error // Jump if error (CF = 1 on error) jmp _biosdisk_done _biosdisk_error: movb %ah,_biosdisk_error_code// Save the error code cmpb $0x11,%ah // Check and see if it was a corrected ECC error je _biosdisk_done // If so then the data is still good, if not fail movb _biosdisk_retrycount,%al// Get the current retry count decb %al // Decrement it movb %al,_biosdisk_retrycount// Save it cmpb $0,%al // Is it zero? jz _biosdisk_zero // Yes, return zero movb $0,%ah // BIOS int 0x13, function 0 - Reset Disk System movb _biosdisk_drive,%dl // Get the drive int $0x13 // Reset the disk system jmp _biosdisk_read // Try reading again _biosdisk_zero: movb $0,%al // We will return zero _biosdisk_done: movzbl %al,%eax // Put the number of sectors read into EAX movl %eax,_biosdisk_retval // Save it as the return value popw %es // Restore ES call switch_to_prot .code32 movl _biosdisk_retval,%eax // Get return value //movl $1,%eax pop %edx pop %ecx pop %ebx pop %edi pop %esi pop %ebp ret /* * BOOL BiosInt13ReadExtended(ULONG Drive, ULONG Sector, ULONG SectorCount, PVOID Buffer); */ _disk_address_packet: _packet_size: .byte 0x10 _packet_reserved: .byte 0 _packet_sector_count: .word 0 _packet_transfer_buffer_segment: .word 0 _packet_transfer_buffer_offset: .word 0 _packet_lba_sector_number: .quad 0 _packet_64bit_flat_address: .quad 0 _int13_extended_drive: .long 0 _int13_extended_sector_count: .long 0 _int13_extended_retval: .long 0 _int13_extended_retrycount: .byte 0 EXTERN(_BiosInt13ReadExtended) .code32 push %ebp push %esi push %edi push %ebx push %ecx push %edx /* Get parameters */ movl 0x1c(%esp),%eax movl %eax,_int13_extended_drive movl 0x20(%esp),%eax movl %eax,_packet_lba_sector_number movl 0x24(%esp),%eax movw %ax,_packet_sector_count movl %eax,_int13_extended_sector_count movl 0x28(%esp),%eax // Get buffer address in eax shrl $4,%eax // Make linear address into segment movw %ax,_packet_transfer_buffer_segment // Save segment movl 0x28(%esp),%eax // Get buffer address in eax andl $0x0f,%eax // Make linear address into offset movw %ax,_packet_transfer_buffer_offset // Save offset call switch_to_real .code16 pushw %es // Save this just in case movb $3,_int13_extended_retrycount // Set the retry count to 3 _int13_extended_read: movb _int13_extended_drive,%dl // Get the drive movb $42,%ah // BIOS int 0x13, function 42h - IBM/MS INT 13 Extensions - EXTENDED READ movw $_disk_address_packet,%si // DS:SI -> disk address packet int $0x13 // Read sectors movb $1,%al // Set the return value to be one (will be set to zero later if needed) jc _int13_extended_error // Jump if error (CF = 1 on error) movl _int13_extended_sector_count,%eax // Get the sector count in eax cmpw _packet_sector_count,%ax // See how many sectors we actually read (returned in disk address packet sector count) jne _int13_extended_error // Jump if not equal jmp _int13_extended_done _int13_extended_error: movb %ah,_biosdisk_error_code // Save the error code cmpb $0x11,%ah // Check and see if it was a corrected ECC error je _int13_extended_done // If so then the data is still good, if not fail movb _int13_extended_retrycount,%al // Get the current retry count decb %al // Decrement it movb %al,_int13_extended_retrycount // Save it cmpb $0,%al // Is it zero? jz _int13_extended_zero // Yes, return zero movb $0,%ah // BIOS int 0x13, function 0 - Reset Disk System movb _int13_extended_drive,%dl // Get the drive int $0x13 // Reset the disk system jmp _int13_extended_read // Try reading again _int13_extended_zero: movb $0,%al // We will return zero _int13_extended_done: movzbl %al,%eax // Put the number of sectors read into EAX movl %eax,_int13_extended_retval // Save it as the return value popw %es // Restore ES call switch_to_prot .code32 movl _int13_extended_retval,%eax // Get return value //movl $1,%eax pop %edx pop %ecx pop %ebx pop %edi pop %esi pop %ebp ret /* * BOOL BiosInt13ExtensionsSupported(ULONG Drive); */ _int13_extension_check_drive: .long 0 _int13_extension_check_retval: .long 0 EXTERN(_BiosInt13ExtensionsSupported) .code32 push %ebp push %esi push %edi push %ebx push %ecx push %edx /* Get parameters */ movl 0x1c(%esp),%eax movl %eax,_int13_extension_check_drive call switch_to_real .code16 // Now make sure this computer supports extended reads movb $0x41,%ah // AH = 41h movw $0x55aa,%bx // BX = 55AAh movb _int13_extension_check_drive,%dl // DL = drive (80h-FFh) int $0x13 // IBM/MS INT 13 Extensions - INSTALLATION CHECK jc _int13_extension_check_error // CF set on error (extensions not supported) cmpw $0xaa55,%bx // BX = AA55h if installed jne _int13_extension_check_error testb $1,%cl // CX = API subset support bitmap jz _int13_extension_check_error // Bit 0, extended disk access functions (AH=42h-44h,47h,48h) supported // If we get here then we passed all the int13 extension tests movl $1,_int13_extension_check_retval // Set return value to TRUE jmp _int13_extension_check_done _int13_extension_check_error: movl $0,_int13_extension_check_retval // The tests failed so return FALSE _int13_extension_check_done: call switch_to_prot .code32 movl _int13_extension_check_retval,%eax // Get return value pop %edx pop %ecx pop %ebx pop %edi pop %esi pop %ebp ret /* * ULONG BiosInt13GetLastErrorCode(VOID); */ EXTERN(_BiosInt13GetLastErrorCode) .code32 movzbl _biosdisk_error_code,%eax // Get return value ret /* * void stop_floppy(void); * * Stops the floppy drive from spinning, so that other software is * jumped to with a known state. */ EXTERN(_stop_floppy) .code32 push %eax push %edx call switch_to_real .code16 movw $0x3F2, %dx xorb %al, %al outb %al, %dx call switch_to_prot .code32 pop %edx pop %eax ret /* * int get_heads(int drive); */ EXTERN(_get_heads) .code32 push %ebx push %ecx push %edx push %edi push %es /* Get drive */ movl 0x18(%esp),%eax movl %eax,_biosdisk_drive call switch_to_real .code16 movb $0x08,%ah movb _biosdisk_drive,%dl int $0x13 jc _get_heads_error movzbl %dh,%edx incl %edx movl %edx,_biosdisk_retval jmp _get_heads_done _get_heads_error: movl $0xff,_biosdisk_retval _get_heads_done: call switch_to_prot .code32 movl _biosdisk_retval,%eax // Get return value pop %es pop %edi pop %edx pop %ecx pop %ebx ret /* * int get_cylinders(int drive); */ EXTERN(_get_cylinders) .code32 push %ebx push %ecx push %edx push %edi push %es /* Get drive */ movl 0x18(%esp),%eax movl %eax,_biosdisk_drive call switch_to_real .code16 movb $0x08,%ah movb _biosdisk_drive,%dl int $0x13 jc _get_cylinders_error xorl %edx,%edx andb $0xc0,%cl shrb $0x06,%cl movb %cl,%dh movb %ch,%dl incl %edx movl %edx,_biosdisk_retval jmp _get_cylinders_done _get_cylinders_error: movl $0xff,_biosdisk_retval _get_cylinders_done: call switch_to_prot .code32 movl _biosdisk_retval,%eax // Get return value pop %es pop %edi pop %edx pop %ecx pop %ebx ret /* * int get_sectors(int drive); */ EXTERN(_get_sectors) .code32 push %ebx push %ecx push %edx push %edi push %es /* Get drive */ movl 0x18(%esp),%eax movl %eax,_biosdisk_drive call switch_to_real .code16 movb $0x08,%ah movb _biosdisk_drive,%dl int $0x13 jc _get_sectors_error andb $0x3f,%cl movzbl %cl,%ecx movl %ecx,_biosdisk_retval jmp _get_sectors_done _get_sectors_error: movl $0xff,_biosdisk_retval _get_sectors_done: call switch_to_prot .code32 movl _biosdisk_retval,%eax // Get return value pop %es pop %edi pop %edx pop %ecx pop %ebx ret /* * BOOL BiosInt13GetDriveParameters(ULONG Drive, PGEOMETRY Geometry); */ _bios_int13_cylinders: .long 0 _bios_int13_heads: .long 0 _bios_int13_sectors: .long 0 _bios_int13_bytes_per_sector: .long 0 _bios_int13_drive_parameters_struct_address: .long 0 EXTERN(_BiosInt13GetDriveParameters) .code32 push %ebx push %ecx push %edx push %edi push %esi push %es /* Get drive */ movl 0x1c(%esp),%eax movl %eax,_biosdisk_drive movl 0x20(%esp),%eax movl %eax,_bios_int13_drive_parameters_struct_address call switch_to_real .code16 movb $0x08,%ah movb _biosdisk_drive,%dl int $0x13 jc _BiosInt13GetDriveParameters_Error // Get the heads movzbl %dh,%eax incl %eax movl %eax,_bios_int13_heads // Get the sectors movw %cx,%dx andb $0x3f,%dl movzbl %dl,%edx movl %edx,_bios_int13_sectors // Get the cylinders xorl %edx,%edx andb $0xc0,%cl shrb $0x06,%cl movb %cl,%dh movb %ch,%dl incl %edx movl %edx,_bios_int13_cylinders // Get the bytes per sector movl $512,_bios_int13_bytes_per_sector // Just assume 512 bytes per sector movl $0x01,_biosdisk_retval jmp _BiosInt13GetDriveParameters_Done _BiosInt13GetDriveParameters_Error: movl $0x00,_biosdisk_retval _BiosInt13GetDriveParameters_Done: call switch_to_prot .code32 // Copy drive parameters to structure movl $_bios_int13_cylinders,%esi movl _bios_int13_drive_parameters_struct_address,%edi movl $0x04,%ecx cld rep movsl movl _biosdisk_retval,%eax // Get return value pop %es pop %esi pop %edi pop %edx pop %ecx pop %ebx ret