Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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 |
Domen Puncer | f454944 | 2005-06-25 14:58:59 -0700 | [diff] [blame] | 9 | * legacy CHS retrieval by Patrick J. LoPresti <patl@users.sourceforge.net> |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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) |
H. Peter Anvin | 575400d | 2006-09-26 10:52:38 +0200 | [diff] [blame] | 18 | |
| 19 | # It is assumed that %ds == INITSEG here |
| 20 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 21 | movb $0, (EDD_MBR_SIG_NR_BUF) |
| 22 | movb $0, (EDDNR) |
| 23 | |
H. Peter Anvin | 575400d | 2006-09-26 10:52:38 +0200 | [diff] [blame] | 24 | # Check the command line for options: |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 25 | # edd=of disables EDD completely (edd=off) |
| 26 | # edd=sk skips the MBR test (edd=skipmbr) |
H. Peter Anvin | 575400d | 2006-09-26 10:52:38 +0200 | [diff] [blame] | 27 | # edd=on re-enables EDD (edd=on) |
| 28 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 29 | pushl %esi |
H. Peter Anvin | 575400d | 2006-09-26 10:52:38 +0200 | [diff] [blame] | 30 | movw $edd_mbr_sig_start, %di # Default to edd=on |
| 31 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 32 | movl %cs:(cmd_line_ptr), %esi |
H. Peter Anvin | 575400d | 2006-09-26 10:52:38 +0200 | [diff] [blame] | 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? |
| 44 | old_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 |
| 53 | have_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"... |
| 59 | cl_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 |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 66 | jz found_edd_equals |
H. Peter Anvin | 575400d | 2006-09-26 10:52:38 +0200 | [diff] [blame] | 67 | cmpb $0x20, %al # <= space consider whitespace |
| 68 | ja cl_skipword |
| 69 | incw %si |
| 70 | jmp cl_atspace |
| 71 | |
| 72 | cl_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 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 83 | found_edd_equals: |
| 84 | # only looking at first two characters after equals |
H. Peter Anvin | 575400d | 2006-09-26 10:52:38 +0200 | [diff] [blame] | 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 |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 94 | do_edd_skipmbr: |
H. Peter Anvin | 575400d | 2006-09-26 10:52:38 +0200 | [diff] [blame] | 95 | movw $edd_start, %di |
| 96 | jmp cl_skipword |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 97 | do_edd_off: |
H. Peter Anvin | 575400d | 2006-09-26 10:52:38 +0200 | [diff] [blame] | 98 | movw $edd_done, %di |
| 99 | jmp cl_skipword |
| 100 | do_edd_on: |
| 101 | movw $edd_mbr_sig_start, %di |
| 102 | jmp cl_skipword |
| 103 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 104 | done_cl: |
| 105 | popl %esi |
H. Peter Anvin | 575400d | 2006-09-26 10:52:38 +0200 | [diff] [blame] | 106 | jmpw *%di |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 107 | |
| 108 | # Read the first sector of each BIOS disk device and store the 4-byte signature |
| 109 | edd_mbr_sig_start: |
| 110 | movb $0x80, %dl # from device 80 |
| 111 | movw $EDD_MBR_SIG_BUF, %bx # store buffer ptr in bx |
| 112 | edd_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. |
Andrey Borzenkov | 2c3ca07 | 2006-03-25 03:07:07 -0800 | [diff] [blame] | 132 | cmpb $0, %ah # some BIOSes do not set CF |
| 133 | jne edd_mbr_sig_done # on failure, we're done. |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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 |
| 141 | edd_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 |
| 168 | edd_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 | |
| 175 | edd_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 | |
| 189 | edd_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. |
| 196 | edd_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 | |
| 218 | edd_legacy_done: |
| 219 | popw %dx |
| 220 | popw %es |
| 221 | movw %si, %ax # increment si |
| 222 | addw $EDDPARMSIZE+EDDEXTSIZE, %ax |
| 223 | movw %ax, %si |
| 224 | |
| 225 | edd_next: |
| 226 | incb %dl # increment to next device |
| 227 | cmpb $EDDMAXNR, (EDDNR) # Out of space? |
| 228 | jb edd_check_ext # keep looping |
| 229 | |
| 230 | edd_done: |
| 231 | #endif |