/* * BIOS Enhanced Disk Drive support * Copyright (C) 2002, 2003, 2004 Dell, Inc. * by Matt Domsch <Matt_Domsch@dell.com> October 2002 * conformant to T13 Committee www.t13.org * projects 1572D, 1484D, 1386D, 1226DT * disk signature read by Matt Domsch <Matt_Domsch@dell.com> * and Andrew Wilks <Andrew_Wilks@dell.com> September 2003, June 2004 * legacy CHS retrieval by Patrick J. LoPresti <patl@users.sourceforge.net> * March 2004 * Command line option parsing, Matt Domsch, November 2004 */ #include <linux/edd.h> #include <asm/setup.h> #if defined(CONFIG_EDD) || defined(CONFIG_EDD_MODULE) # It is assumed that %ds == INITSEG here movb $0, (EDD_MBR_SIG_NR_BUF) movb $0, (EDDNR) # Check the command line for options: # edd=of disables EDD completely (edd=off) # edd=sk skips the MBR test (edd=skipmbr) # edd=on re-enables EDD (edd=on) pushl %esi movw $edd_mbr_sig_start, %di # Default to edd=on movl %cs:(cmd_line_ptr), %esi andl %esi, %esi jz old_cl # Old boot protocol? # Convert to a real-mode pointer in fs:si movl %esi, %eax shrl $4, %eax movw %ax, %fs andw $0xf, %si jmp have_cl_pointer # Old-style boot protocol? old_cl: push %ds # aka INITSEG pop %fs cmpw $0xa33f, (0x20) jne done_cl # No command line at all? movw (0x22), %si # Pointer relative to INITSEG # fs:si has the pointer to the command line now have_cl_pointer: # Loop through kernel command line one byte at a time. Just in # case the loader is buggy and failed to null-terminate the command line # terminate if we get close enough to the end of the segment that we # cannot fit "edd=XX"... cl_atspace: cmpw $-5, %si # Watch for segment wraparound jae done_cl movl %fs:(%si), %eax andb %al, %al # End of line? jz done_cl cmpl $EDD_CL_EQUALS, %eax jz found_edd_equals cmpb $0x20, %al # <= space consider whitespace ja cl_skipword incw %si jmp cl_atspace cl_skipword: cmpw $-5, %si # Watch for segment wraparound jae done_cl movb %fs:(%si), %al # End of string? andb %al, %al jz done_cl cmpb $0x20, %al jbe cl_atspace incw %si jmp cl_skipword found_edd_equals: # only looking at first two characters after equals # late overrides early on the command line, so keep going after finding something movw %fs:4(%si), %ax cmpw $EDD_CL_OFF, %ax # edd=of je do_edd_off cmpw $EDD_CL_SKIP, %ax # edd=sk je do_edd_skipmbr cmpw $EDD_CL_ON, %ax # edd=on je do_edd_on jmp cl_skipword do_edd_skipmbr: movw $edd_start, %di jmp cl_skipword do_edd_off: movw $edd_done, %di jmp cl_skipword do_edd_on: movw $edd_mbr_sig_start, %di jmp cl_skipword done_cl: popl %esi jmpw *%di # Read the first sector of each BIOS disk device and store the 4-byte signature edd_mbr_sig_start: movb $0x80, %dl # from device 80 movw $EDD_MBR_SIG_BUF, %bx # store buffer ptr in bx edd_mbr_sig_read: movl $0xFFFFFFFF, %eax movl %eax, (%bx) # assume failure pushw %bx movb $READ_SECTORS, %ah movb $1, %al # read 1 sector movb $0, %dh # at head 0 movw $1, %cx # cylinder 0, sector 0 pushw %es pushw %ds popw %es movw $EDDBUF, %bx # disk's data goes into EDDBUF pushw %dx # work around buggy BIOSes stc # work around buggy BIOSes int $0x13 sti # work around buggy BIOSes popw %dx popw %es popw %bx jc edd_mbr_sig_done # on failure, we're done. cmpb $0, %ah # some BIOSes do not set CF jne edd_mbr_sig_done # on failure, we're done. movl (EDDBUF+EDD_MBR_SIG_OFFSET), %eax # read sig out of the MBR movl %eax, (%bx) # store success incb (EDD_MBR_SIG_NR_BUF) # note that we stored something incb %dl # increment to next device addw $4, %bx # increment sig buffer ptr cmpb $EDD_MBR_SIG_MAX, (EDD_MBR_SIG_NR_BUF) # Out of space? jb edd_mbr_sig_read # keep looping edd_mbr_sig_done: # Do the BIOS Enhanced Disk Drive calls # This consists of two calls: # int 13h ah=41h "Check Extensions Present" # int 13h ah=48h "Get Device Parameters" # int 13h ah=08h "Legacy Get Device Parameters" # # A buffer of size EDDMAXNR*(EDDEXTSIZE+EDDPARMSIZE) is reserved for our use # in the boot_params at EDDBUF. The first four bytes of which are # used to store the device number, interface support map and version # results from fn41. The next four bytes are used to store the legacy # cylinders, heads, and sectors from fn08. The following 74 bytes are used to # store the results from fn48. Starting from device 80h, fn41, then fn48 # are called and their results stored in EDDBUF+n*(EDDEXTSIZE+EDDPARMIZE). # Then the pointer is incremented to store the data for the next call. # This repeats until either a device doesn't exist, or until EDDMAXNR # devices have been stored. # The one tricky part is that ds:si always points EDDEXTSIZE bytes into # the structure, and the fn41 and fn08 results are stored at offsets # from there. This removes the need to increment the pointer for # every store, and leaves it ready for the fn48 call. # A second one-byte buffer, EDDNR, in the boot_params stores # the number of BIOS devices which exist, up to EDDMAXNR. # In setup.c, copy_edd() stores both boot_params buffers away # for later use, as they would get overwritten otherwise. # This code is sensitive to the size of the structs in edd.h edd_start: # %ds points to the bootsector # result buffer for fn48 movw $EDDBUF+EDDEXTSIZE, %si # in ds:si, fn41 results # kept just before that movb $0x80, %dl # BIOS device 0x80 edd_check_ext: movb $CHECKEXTENSIONSPRESENT, %ah # Function 41 movw $EDDMAGIC1, %bx # magic int $0x13 # make the call jc edd_done # no more BIOS devices cmpw $EDDMAGIC2, %bx # is magic right? jne edd_next # nope, next... movb %dl, %ds:-8(%si) # store device number movb %ah, %ds:-7(%si) # store version movw %cx, %ds:-6(%si) # store extensions incb (EDDNR) # note that we stored something edd_get_device_params: movw $EDDPARMSIZE, %ds:(%si) # put size movw $0x0, %ds:2(%si) # work around buggy BIOSes movb $GETDEVICEPARAMETERS, %ah # Function 48 int $0x13 # make the call # Don't check for fail return # it doesn't matter. edd_get_legacy_chs: xorw %ax, %ax movw %ax, %ds:-4(%si) movw %ax, %ds:-2(%si) # Ralf Brown's Interrupt List says to set ES:DI to # 0000h:0000h "to guard against BIOS bugs" pushw %es movw %ax, %es movw %ax, %di pushw %dx # legacy call clobbers %dl movb $LEGACYGETDEVICEPARAMETERS, %ah # Function 08 int $0x13 # make the call jc edd_legacy_done # failed movb %cl, %al # Low 6 bits are max andb $0x3F, %al # sector number movb %al, %ds:-1(%si) # Record max sect movb %dh, %ds:-2(%si) # Record max head number movb %ch, %al # Low 8 bits of max cyl shr $6, %cl movb %cl, %ah # High 2 bits of max cyl movw %ax, %ds:-4(%si) edd_legacy_done: popw %dx popw %es movw %si, %ax # increment si addw $EDDPARMSIZE+EDDEXTSIZE, %ax movw %ax, %si edd_next: incb %dl # increment to next device cmpb $EDDMAXNR, (EDDNR) # Out of space? jb edd_check_ext # keep looping edd_done: #endif