Merge branch 'master'
[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         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         cmpb    $0, %ah         # some BIOSes do not set CF
80         jne     edd_mbr_sig_done                # on failure, we're done.
81         movl    (EDDBUF+EDD_MBR_SIG_OFFSET), %eax # read sig out of the MBR
82         movl    %eax, (%bx)                     # store success
83         incb    (EDD_MBR_SIG_NR_BUF)            # note that we stored something
84         incb    %dl                             # increment to next device
85         addw    $4, %bx                         # increment sig buffer ptr
86         cmpb    $EDD_MBR_SIG_MAX, (EDD_MBR_SIG_NR_BUF)  # Out of space?
87         jb      edd_mbr_sig_read                # keep looping
88 edd_mbr_sig_done:
89
90 # Do the BIOS Enhanced Disk Drive calls
91 # This consists of two calls:
92 #    int 13h ah=41h "Check Extensions Present"
93 #    int 13h ah=48h "Get Device Parameters"
94 #    int 13h ah=08h "Legacy Get Device Parameters"
95 #
96 # A buffer of size EDDMAXNR*(EDDEXTSIZE+EDDPARMSIZE) is reserved for our use
97 # in the boot_params at EDDBUF.  The first four bytes of which are
98 # used to store the device number, interface support map and version
99 # results from fn41.  The next four bytes are used to store the legacy
100 # cylinders, heads, and sectors from fn08. The following 74 bytes are used to
101 # store the results from fn48.  Starting from device 80h, fn41, then fn48
102 # are called and their results stored in EDDBUF+n*(EDDEXTSIZE+EDDPARMIZE).
103 # Then the pointer is incremented to store the data for the next call.
104 # This repeats until either a device doesn't exist, or until EDDMAXNR
105 # devices have been stored.
106 # The one tricky part is that ds:si always points EDDEXTSIZE bytes into
107 # the structure, and the fn41 and fn08 results are stored at offsets
108 # from there.  This removes the need to increment the pointer for
109 # every store, and leaves it ready for the fn48 call.
110 # A second one-byte buffer, EDDNR, in the boot_params stores
111 # the number of BIOS devices which exist, up to EDDMAXNR.
112 # In setup.c, copy_edd() stores both boot_params buffers away
113 # for later use, as they would get overwritten otherwise.
114 # This code is sensitive to the size of the structs in edd.h
115 edd_start:
116                                                 # %ds points to the bootsector
117                                                 # result buffer for fn48
118         movw    $EDDBUF+EDDEXTSIZE, %si         # in ds:si, fn41 results
119                                                 # kept just before that
120         movb    $0x80, %dl                      # BIOS device 0x80
121
122 edd_check_ext:
123         movb    $CHECKEXTENSIONSPRESENT, %ah    # Function 41
124         movw    $EDDMAGIC1, %bx                 # magic
125         int     $0x13                           # make the call
126         jc      edd_done                        # no more BIOS devices
127
128         cmpw    $EDDMAGIC2, %bx                 # is magic right?
129         jne     edd_next                        # nope, next...
130
131         movb    %dl, %ds:-8(%si)                # store device number
132         movb    %ah, %ds:-7(%si)                # store version
133         movw    %cx, %ds:-6(%si)                # store extensions
134         incb    (EDDNR)                         # note that we stored something
135
136 edd_get_device_params:
137         movw    $EDDPARMSIZE, %ds:(%si)         # put size
138         movw    $0x0, %ds:2(%si)                # work around buggy BIOSes
139         movb    $GETDEVICEPARAMETERS, %ah       # Function 48
140         int     $0x13                           # make the call
141                                                 # Don't check for fail return
142                                                 # it doesn't matter.
143 edd_get_legacy_chs:
144         xorw    %ax, %ax
145         movw    %ax, %ds:-4(%si)
146         movw    %ax, %ds:-2(%si)
147         # Ralf Brown's Interrupt List says to set ES:DI to
148         # 0000h:0000h "to guard against BIOS bugs"
149         pushw   %es
150         movw    %ax, %es
151         movw    %ax, %di
152         pushw   %dx                             # legacy call clobbers %dl
153         movb    $LEGACYGETDEVICEPARAMETERS, %ah # Function 08
154         int     $0x13                           # make the call
155         jc      edd_legacy_done                 # failed
156         movb    %cl, %al                        # Low 6 bits are max
157         andb    $0x3F, %al                      #   sector number
158         movb    %al, %ds:-1(%si)                # Record max sect
159         movb    %dh, %ds:-2(%si)                # Record max head number
160         movb    %ch, %al                        # Low 8 bits of max cyl
161         shr     $6, %cl
162         movb    %cl, %ah                        # High 2 bits of max cyl
163         movw    %ax, %ds:-4(%si)
164
165 edd_legacy_done:
166         popw    %dx
167         popw    %es
168         movw    %si, %ax                        # increment si
169         addw    $EDDPARMSIZE+EDDEXTSIZE, %ax
170         movw    %ax, %si
171
172 edd_next:
173         incb    %dl                             # increment to next device
174         cmpb    $EDDMAXNR, (EDDNR)              # Out of space?
175         jb      edd_check_ext                   # keep looping
176
177 edd_done:
178 #endif