Pull style into test branch
[linux-2.6] / arch / i386 / boot / edd.S
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?
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
66         jz      found_edd_equals
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
83 found_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
94 do_edd_skipmbr:
95         movw    $edd_start, %di
96         jmp     cl_skipword
97 do_edd_off:
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
104 done_cl:
105         popl    %esi
106         jmpw    *%di
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.
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
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