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>
11 * Command line option parsing, Matt Domsch, November 2004
14 #include <linux/edd.h>
15 #include <asm/setup.h>
17 #if defined(CONFIG_EDD) || defined(CONFIG_EDD_MODULE)
19 # It is assumed that %ds == INITSEG here
21 movb $0, (EDD_MBR_SIG_NR_BUF)
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)
30 movw $edd_mbr_sig_start, %di # Default to edd=on
32 movl %cs:(cmd_line_ptr), %esi
34 jz old_cl # Old boot protocol?
36 # Convert to a real-mode pointer in fs:si
43 # Old-style boot protocol?
45 push %ds # aka INITSEG
49 jne done_cl # No command line at all?
50 movw (0x22), %si # Pointer relative to INITSEG
52 # fs:si has the pointer to the command line now
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"...
60 cmpw $-5, %si # Watch for segment wraparound
63 andb %al, %al # End of line?
65 cmpl $EDD_CL_EQUALS, %eax
67 cmpb $0x20, %al # <= space consider whitespace
73 cmpw $-5, %si # Watch for segment wraparound
75 movb %fs:(%si), %al # End of string?
84 # only looking at first two characters after equals
85 # late overrides early on the command line, so keep going after finding something
87 cmpw $EDD_CL_OFF, %ax # edd=of
89 cmpw $EDD_CL_SKIP, %ax # edd=sk
91 cmpw $EDD_CL_ON, %ax # edd=on
101 movw $edd_mbr_sig_start, %di
108 # Read the first sector of each BIOS disk device and store the 4-byte signature
110 movb $0x80, %dl # from device 80
111 movw $EDD_MBR_SIG_BUF, %bx # store buffer ptr in bx
113 movl $0xFFFFFFFF, %eax
114 movl %eax, (%bx) # assume failure
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
123 movw $EDDBUF, %bx # disk's data goes into EDDBUF
124 pushw %dx # work around buggy BIOSes
125 stc # work around buggy BIOSes
127 sti # work around buggy BIOSes
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
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"
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
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
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
181 cmpw $EDDMAGIC2, %bx # is magic right?
182 jne edd_next # nope, next...
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
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
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"
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
215 movb %cl, %ah # High 2 bits of max cyl
216 movw %ax, %ds:-4(%si)
221 movw %si, %ax # increment si
222 addw $EDDPARMSIZE+EDDEXTSIZE, %ax
226 incb %dl # increment to next device
227 cmpb $EDDMAXNR, (EDDNR) # Out of space?
228 jb edd_check_ext # keep looping