1/* 2 * BIOS Enhanced Disk Drive support 3 * Copyright (C) 2002, 2003, 2004 Dell, Inc. 4 * by Matt Domsch <Matt_Domsch@dell.com> October 2002 5 * conformant to T13 Committee www.t13.org 6 * projects 1572D, 1484D, 1386D, 1226DT 7 * disk signature read by Matt Domsch <Matt_Domsch@dell.com> 8 * and Andrew Wilks <Andrew_Wilks@dell.com> September 2003, June 2004 9 * legacy CHS retrieval by Patrick J. LoPresti <patl@users.sourceforge.net> 10 * March 2004 11 * Command line option parsing, Matt Domsch, November 2004 12 */ 13 14#include <linux/edd.h> 15#include <asm/setup.h> 16 17#if defined(CONFIG_EDD) || defined(CONFIG_EDD_MODULE) 18 19# It is assumed that %ds == INITSEG here 20 21 movb $0, (EDD_MBR_SIG_NR_BUF) 22 movb $0, (EDDNR) 23 24# Check the command line for options: 25# edd=of disables EDD completely (edd=off) 26# edd=sk skips the MBR test (edd=skipmbr) 27# edd=on re-enables EDD (edd=on) 28 29 pushl %esi 30 movw $edd_mbr_sig_start, %di # Default to edd=on 31 32 movl %cs:(cmd_line_ptr), %esi 33 andl %esi, %esi 34 jz old_cl # Old boot protocol? 35 36# Convert to a real-mode pointer in fs:si 37 movl %esi, %eax 38 shrl $4, %eax 39 movw %ax, %fs 40 andw $0xf, %si 41 jmp have_cl_pointer 42 43# Old-style boot protocol? 44old_cl: 45 push %ds # aka INITSEG 46 pop %fs 47 48 cmpw $0xa33f, (0x20) 49 jne done_cl # No command line at all? 50 movw (0x22), %si # Pointer relative to INITSEG 51 52# fs:si has the pointer to the command line now 53have_cl_pointer: 54 55# Loop through kernel command line one byte at a time. Just in 56# case the loader is buggy and failed to null-terminate the command line 57# terminate if we get close enough to the end of the segment that we 58# cannot fit "edd=XX"... 59cl_atspace: 60 cmpw $-5, %si # Watch for segment wraparound 61 jae done_cl 62 movl %fs:(%si), %eax 63 andb %al, %al # End of line? 64 jz done_cl 65 cmpl $EDD_CL_EQUALS, %eax 66 jz found_edd_equals 67 cmpb $0x20, %al # <= space consider whitespace 68 ja cl_skipword 69 incw %si 70 jmp cl_atspace 71 72cl_skipword: 73 cmpw $-5, %si # Watch for segment wraparound 74 jae done_cl 75 movb %fs:(%si), %al # End of string? 76 andb %al, %al 77 jz done_cl 78 cmpb $0x20, %al 79 jbe cl_atspace 80 incw %si 81 jmp cl_skipword 82 83found_edd_equals: 84# only looking at first two characters after equals 85# late overrides early on the command line, so keep going after finding something 86 movw %fs:4(%si), %ax 87 cmpw $EDD_CL_OFF, %ax # edd=of 88 je do_edd_off 89 cmpw $EDD_CL_SKIP, %ax # edd=sk 90 je do_edd_skipmbr 91 cmpw $EDD_CL_ON, %ax # edd=on 92 je do_edd_on 93 jmp cl_skipword 94do_edd_skipmbr: 95 movw $edd_start, %di 96 jmp cl_skipword 97do_edd_off: 98 movw $edd_done, %di 99 jmp cl_skipword 100do_edd_on: 101 movw $edd_mbr_sig_start, %di 102 jmp cl_skipword 103 104done_cl: 105 popl %esi 106 jmpw *%di 107 108# Read the first sector of each BIOS disk device and store the 4-byte signature 109edd_mbr_sig_start: 110 movb $0x80, %dl # from device 80 111 movw $EDD_MBR_SIG_BUF, %bx # store buffer ptr in bx 112edd_mbr_sig_read: 113 movl $0xFFFFFFFF, %eax 114 movl %eax, (%bx) # assume failure 115 pushw %bx 116 movb $READ_SECTORS, %ah 117 movb $1, %al # read 1 sector 118 movb $0, %dh # at head 0 119 movw $1, %cx # cylinder 0, sector 0 120 pushw %es 121 pushw %ds 122 popw %es 123 movw $EDDBUF, %bx # disk's data goes into EDDBUF 124 pushw %dx # work around buggy BIOSes 125 stc # work around buggy BIOSes 126 int $0x13 127 sti # work around buggy BIOSes 128 popw %dx 129 popw %es 130 popw %bx 131 jc edd_mbr_sig_done # on failure, we're done. 132 cmpb $0, %ah # some BIOSes do not set CF 133 jne edd_mbr_sig_done # on failure, we're done. 134 movl (EDDBUF+EDD_MBR_SIG_OFFSET), %eax # read sig out of the MBR 135 movl %eax, (%bx) # store success 136 incb (EDD_MBR_SIG_NR_BUF) # note that we stored something 137 incb %dl # increment to next device 138 addw $4, %bx # increment sig buffer ptr 139 cmpb $EDD_MBR_SIG_MAX, (EDD_MBR_SIG_NR_BUF) # Out of space? 140 jb edd_mbr_sig_read # keep looping 141edd_mbr_sig_done: 142 143# Do the BIOS Enhanced Disk Drive calls 144# This consists of two calls: 145# int 13h ah=41h "Check Extensions Present" 146# int 13h ah=48h "Get Device Parameters" 147# int 13h ah=08h "Legacy Get Device Parameters" 148# 149# A buffer of size EDDMAXNR*(EDDEXTSIZE+EDDPARMSIZE) is reserved for our use 150# in the boot_params at EDDBUF. The first four bytes of which are 151# used to store the device number, interface support map and version 152# results from fn41. The next four bytes are used to store the legacy 153# cylinders, heads, and sectors from fn08. The following 74 bytes are used to 154# store the results from fn48. Starting from device 80h, fn41, then fn48 155# are called and their results stored in EDDBUF+n*(EDDEXTSIZE+EDDPARMIZE). 156# Then the pointer is incremented to store the data for the next call. 157# This repeats until either a device doesn't exist, or until EDDMAXNR 158# devices have been stored. 159# The one tricky part is that ds:si always points EDDEXTSIZE bytes into 160# the structure, and the fn41 and fn08 results are stored at offsets 161# from there. This removes the need to increment the pointer for 162# every store, and leaves it ready for the fn48 call. 163# A second one-byte buffer, EDDNR, in the boot_params stores 164# the number of BIOS devices which exist, up to EDDMAXNR. 165# In setup.c, copy_edd() stores both boot_params buffers away 166# for later use, as they would get overwritten otherwise. 167# This code is sensitive to the size of the structs in edd.h 168edd_start: 169 # %ds points to the bootsector 170 # result buffer for fn48 171 movw $EDDBUF+EDDEXTSIZE, %si # in ds:si, fn41 results 172 # kept just before that 173 movb $0x80, %dl # BIOS device 0x80 174 175edd_check_ext: 176 movb $CHECKEXTENSIONSPRESENT, %ah # Function 41 177 movw $EDDMAGIC1, %bx # magic 178 int $0x13 # make the call 179 jc edd_done # no more BIOS devices 180 181 cmpw $EDDMAGIC2, %bx # is magic right? 182 jne edd_next # nope, next... 183 184 movb %dl, %ds:-8(%si) # store device number 185 movb %ah, %ds:-7(%si) # store version 186 movw %cx, %ds:-6(%si) # store extensions 187 incb (EDDNR) # note that we stored something 188 189edd_get_device_params: 190 movw $EDDPARMSIZE, %ds:(%si) # put size 191 movw $0x0, %ds:2(%si) # work around buggy BIOSes 192 movb $GETDEVICEPARAMETERS, %ah # Function 48 193 int $0x13 # make the call 194 # Don't check for fail return 195 # it doesn't matter. 196edd_get_legacy_chs: 197 xorw %ax, %ax 198 movw %ax, %ds:-4(%si) 199 movw %ax, %ds:-2(%si) 200 # Ralf Brown's Interrupt List says to set ES:DI to 201 # 0000h:0000h "to guard against BIOS bugs" 202 pushw %es 203 movw %ax, %es 204 movw %ax, %di 205 pushw %dx # legacy call clobbers %dl 206 movb $LEGACYGETDEVICEPARAMETERS, %ah # Function 08 207 int $0x13 # make the call 208 jc edd_legacy_done # failed 209 movb %cl, %al # Low 6 bits are max 210 andb $0x3F, %al # sector number 211 movb %al, %ds:-1(%si) # Record max sect 212 movb %dh, %ds:-2(%si) # Record max head number 213 movb %ch, %al # Low 8 bits of max cyl 214 shr $6, %cl 215 movb %cl, %ah # High 2 bits of max cyl 216 movw %ax, %ds:-4(%si) 217 218edd_legacy_done: 219 popw %dx 220 popw %es 221 movw %si, %ax # increment si 222 addw $EDDPARMSIZE+EDDEXTSIZE, %ax 223 movw %ax, %si 224 225edd_next: 226 incb %dl # increment to next device 227 cmpb $EDDMAXNR, (EDDNR) # Out of space? 228 jb edd_check_ext # keep looping 229 230edd_done: 231#endif 232