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 |
| 9 | * legacy CHS retreival 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 | movb $0, (EDD_MBR_SIG_NR_BUF) |
| 19 | movb $0, (EDDNR) |
| 20 | |
| 21 | # Check the command line for two options: |
| 22 | # edd=of disables EDD completely (edd=off) |
| 23 | # edd=sk skips the MBR test (edd=skipmbr) |
| 24 | pushl %esi |
| 25 | cmpl $0, %cs:cmd_line_ptr |
| 26 | jz done_cl |
| 27 | movl %cs:(cmd_line_ptr), %esi |
| 28 | # ds:esi has the pointer to the command line now |
| 29 | movl $(COMMAND_LINE_SIZE-7), %ecx |
| 30 | # loop through kernel command line one byte at a time |
| 31 | cl_loop: |
| 32 | cmpl $EDD_CL_EQUALS, (%si) |
| 33 | jz found_edd_equals |
| 34 | incl %esi |
| 35 | loop cl_loop |
| 36 | jmp done_cl |
| 37 | found_edd_equals: |
| 38 | # only looking at first two characters after equals |
| 39 | addl $4, %esi |
| 40 | cmpw $EDD_CL_OFF, (%si) # edd=of |
| 41 | jz do_edd_off |
| 42 | cmpw $EDD_CL_SKIP, (%si) # edd=sk |
| 43 | jz do_edd_skipmbr |
| 44 | jmp done_cl |
| 45 | do_edd_skipmbr: |
| 46 | popl %esi |
| 47 | jmp edd_start |
| 48 | do_edd_off: |
| 49 | popl %esi |
| 50 | jmp edd_done |
| 51 | done_cl: |
| 52 | popl %esi |
| 53 | |
| 54 | |
| 55 | # Read the first sector of each BIOS disk device and store the 4-byte signature |
| 56 | edd_mbr_sig_start: |
| 57 | movb $0x80, %dl # from device 80 |
| 58 | movw $EDD_MBR_SIG_BUF, %bx # store buffer ptr in bx |
| 59 | edd_mbr_sig_read: |
| 60 | movl $0xFFFFFFFF, %eax |
| 61 | movl %eax, (%bx) # assume failure |
| 62 | pushw %bx |
| 63 | movb $READ_SECTORS, %ah |
| 64 | movb $1, %al # read 1 sector |
| 65 | movb $0, %dh # at head 0 |
| 66 | movw $1, %cx # cylinder 0, sector 0 |
| 67 | pushw %es |
| 68 | pushw %ds |
| 69 | popw %es |
| 70 | movw $EDDBUF, %bx # disk's data goes into EDDBUF |
| 71 | pushw %dx # work around buggy BIOSes |
| 72 | stc # work around buggy BIOSes |
| 73 | int $0x13 |
| 74 | sti # work around buggy BIOSes |
| 75 | popw %dx |
| 76 | popw %es |
| 77 | popw %bx |
| 78 | jc edd_mbr_sig_done # on failure, we're done. |
| 79 | movl (EDDBUF+EDD_MBR_SIG_OFFSET), %eax # read sig out of the MBR |
| 80 | movl %eax, (%bx) # store success |
| 81 | incb (EDD_MBR_SIG_NR_BUF) # note that we stored something |
| 82 | incb %dl # increment to next device |
| 83 | addw $4, %bx # increment sig buffer ptr |
| 84 | cmpb $EDD_MBR_SIG_MAX, (EDD_MBR_SIG_NR_BUF) # Out of space? |
| 85 | jb edd_mbr_sig_read # keep looping |
| 86 | edd_mbr_sig_done: |
| 87 | |
| 88 | # Do the BIOS Enhanced Disk Drive calls |
| 89 | # This consists of two calls: |
| 90 | # int 13h ah=41h "Check Extensions Present" |
| 91 | # int 13h ah=48h "Get Device Parameters" |
| 92 | # int 13h ah=08h "Legacy Get Device Parameters" |
| 93 | # |
| 94 | # A buffer of size EDDMAXNR*(EDDEXTSIZE+EDDPARMSIZE) is reserved for our use |
| 95 | # in the boot_params at EDDBUF. The first four bytes of which are |
| 96 | # used to store the device number, interface support map and version |
| 97 | # results from fn41. The next four bytes are used to store the legacy |
| 98 | # cylinders, heads, and sectors from fn08. The following 74 bytes are used to |
| 99 | # store the results from fn48. Starting from device 80h, fn41, then fn48 |
| 100 | # are called and their results stored in EDDBUF+n*(EDDEXTSIZE+EDDPARMIZE). |
| 101 | # Then the pointer is incremented to store the data for the next call. |
| 102 | # This repeats until either a device doesn't exist, or until EDDMAXNR |
| 103 | # devices have been stored. |
| 104 | # The one tricky part is that ds:si always points EDDEXTSIZE bytes into |
| 105 | # the structure, and the fn41 and fn08 results are stored at offsets |
| 106 | # from there. This removes the need to increment the pointer for |
| 107 | # every store, and leaves it ready for the fn48 call. |
| 108 | # A second one-byte buffer, EDDNR, in the boot_params stores |
| 109 | # the number of BIOS devices which exist, up to EDDMAXNR. |
| 110 | # In setup.c, copy_edd() stores both boot_params buffers away |
| 111 | # for later use, as they would get overwritten otherwise. |
| 112 | # This code is sensitive to the size of the structs in edd.h |
| 113 | edd_start: |
| 114 | # %ds points to the bootsector |
| 115 | # result buffer for fn48 |
| 116 | movw $EDDBUF+EDDEXTSIZE, %si # in ds:si, fn41 results |
| 117 | # kept just before that |
| 118 | movb $0x80, %dl # BIOS device 0x80 |
| 119 | |
| 120 | edd_check_ext: |
| 121 | movb $CHECKEXTENSIONSPRESENT, %ah # Function 41 |
| 122 | movw $EDDMAGIC1, %bx # magic |
| 123 | int $0x13 # make the call |
| 124 | jc edd_done # no more BIOS devices |
| 125 | |
| 126 | cmpw $EDDMAGIC2, %bx # is magic right? |
| 127 | jne edd_next # nope, next... |
| 128 | |
| 129 | movb %dl, %ds:-8(%si) # store device number |
| 130 | movb %ah, %ds:-7(%si) # store version |
| 131 | movw %cx, %ds:-6(%si) # store extensions |
| 132 | incb (EDDNR) # note that we stored something |
| 133 | |
| 134 | edd_get_device_params: |
| 135 | movw $EDDPARMSIZE, %ds:(%si) # put size |
| 136 | movw $0x0, %ds:2(%si) # work around buggy BIOSes |
| 137 | movb $GETDEVICEPARAMETERS, %ah # Function 48 |
| 138 | int $0x13 # make the call |
| 139 | # Don't check for fail return |
| 140 | # it doesn't matter. |
| 141 | edd_get_legacy_chs: |
| 142 | xorw %ax, %ax |
| 143 | movw %ax, %ds:-4(%si) |
| 144 | movw %ax, %ds:-2(%si) |
| 145 | # Ralf Brown's Interrupt List says to set ES:DI to |
| 146 | # 0000h:0000h "to guard against BIOS bugs" |
| 147 | pushw %es |
| 148 | movw %ax, %es |
| 149 | movw %ax, %di |
| 150 | pushw %dx # legacy call clobbers %dl |
| 151 | movb $LEGACYGETDEVICEPARAMETERS, %ah # Function 08 |
| 152 | int $0x13 # make the call |
| 153 | jc edd_legacy_done # failed |
| 154 | movb %cl, %al # Low 6 bits are max |
| 155 | andb $0x3F, %al # sector number |
| 156 | movb %al, %ds:-1(%si) # Record max sect |
| 157 | movb %dh, %ds:-2(%si) # Record max head number |
| 158 | movb %ch, %al # Low 8 bits of max cyl |
| 159 | shr $6, %cl |
| 160 | movb %cl, %ah # High 2 bits of max cyl |
| 161 | movw %ax, %ds:-4(%si) |
| 162 | |
| 163 | edd_legacy_done: |
| 164 | popw %dx |
| 165 | popw %es |
| 166 | movw %si, %ax # increment si |
| 167 | addw $EDDPARMSIZE+EDDEXTSIZE, %ax |
| 168 | movw %ax, %si |
| 169 | |
| 170 | edd_next: |
| 171 | incb %dl # increment to next device |
| 172 | cmpb $EDDMAXNR, (EDDNR) # Out of space? |
| 173 | jb edd_check_ext # keep looping |
| 174 | |
| 175 | edd_done: |
| 176 | #endif |