1/******************************************************************************
2 * edd.S
3 *
4 * BIOS Enhanced Disk Drive support
5 *
6 * Copyright (C) 2002, 2003, 2004 Dell, Inc.
7 * by Matt Domsch <Matt_Domsch@dell.com> October 2002
8 * conformant to T13 Committee www.t13.org
9 *   projects 1572D, 1484D, 1386D, 1226DT
10 * disk signature read by Matt Domsch <Matt_Domsch@dell.com>
11 *      and Andrew Wilks <Andrew_Wilks@dell.com> September 2003, June 2004
12 * legacy CHS retrieval by Patrick J. LoPresti <patl@users.sourceforge.net>
13 *      March 2004
14 * Command line option parsing, Matt Domsch, November 2004
15 *
16 * Updated and ported for Xen by Keir Fraser <keir@xensource.com> June 2007
17 */
18
19#include <asm/edd.h>
20
21        .code16
22
23/* Offset of disc signature in the MBR. */
24#define EDD_MBR_SIG_OFFSET      0x1B8
25
26get_edd:
27        cmpb    $2, bootsym(opt_edd)            # edd=off ?
28        je      edd_done
29
30# Do the BIOS Enhanced Disk Drive calls
31# This consists of two calls:
32#    int 13h ah=41h "Check Extensions Present"
33#    int 13h ah=48h "Get Device Parameters"
34#    int 13h ah=08h "Legacy Get Device Parameters"
35#
36# A buffer of size EDD_INFO_MAX*(EDDEXTSIZE+EDDPARMSIZE) is reserved at
37# boot_edd_info, the first four bytes of which are used to store the device
38# number, interface support map and version results from fn41. The next four
39# bytes are used to store the legacy cylinders, heads, and sectors from fn08.
40# The following 74 bytes are used to store the results from fn48.
41# This code is sensitive to the size of the structs in edd.h
42edd_start:
43        /* ds:si points at fn48 results. Fn41 results go immediately before. */
44        movw    $bootsym(boot_edd_info)+EDDEXTSIZE, %si
45        movb    $0x80, %dl                      # BIOS device 0x80
46
47edd_check_ext:
48        movb    $0x41, %ah                      # 0x41 Check Extensions Present
49        movw    $0x55AA, %bx                    # magic
50        int     $0x13                           # make the call
51        jc      edd_done                        # no more BIOS devices
52
53        cmpw    $0xAA55, %bx                    # is magic right?
54        jne     edd_next                        # nope, next...
55
56        movb    %dl, %ds:-8(%si)                # store device number
57        movb    %ah, %ds:-7(%si)                # store version
58        movw    %cx, %ds:-6(%si)                # store extensions
59        incb    bootsym(boot_edd_info_nr)       # note that we stored something
60
61edd_get_device_params:
62        movw    $EDDPARMSIZE, %ds:(%si)         # put size
63        movw    $0x0, %ds:2(%si)                # work around buggy BIOSes
64        movb    $0x48, %ah                      # 0x48 Get Device Parameters
65        int     $0x13                           # make the call
66                                                # Don't check for fail return
67                                                # it doesn't matter.
68edd_get_legacy_chs:
69        xorw    %ax, %ax
70        movw    %ax, %ds:-4(%si)
71        movw    %ax, %ds:-2(%si)
72        # Ralf Brown's Interrupt List says to set ES:DI to
73        # 0000h:0000h "to guard against BIOS bugs"
74        pushw   %es
75        movw    %ax, %es
76        movw    %ax, %di
77        pushw   %dx                             # legacy call clobbers %dl
78        movb    $0x08, %ah                      # 0x08 Legacy Get Device Params
79        int     $0x13                           # make the call
80        jc      edd_legacy_done                 # failed
81        movb    %cl, %al                        # Low 6 bits are max
82        andb    $0x3F, %al                      #   sector number
83        movb    %al, %ds:-1(%si)                # Record max sect
84        movb    %dh, %ds:-2(%si)                # Record max head number
85        movb    %ch, %al                        # Low 8 bits of max cyl
86        shr     $6, %cl
87        movb    %cl, %ah                        # High 2 bits of max cyl
88        movw    %ax, %ds:-4(%si)
89
90edd_legacy_done:
91        popw    %dx
92        popw    %es
93        movw    %si, %ax                        # increment si
94        addw    $EDDPARMSIZE+EDDEXTSIZE, %ax
95        movw    %ax, %si
96
97edd_next:
98        incb    %dl                             # increment to next device
99        jz      edd_done
100        cmpb    $EDD_INFO_MAX,bootsym(boot_edd_info_nr)
101        jb      edd_check_ext
102
103edd_done:
104        cmpb    $1, bootsym(opt_edd)            # edd=skipmbr ?
105        je      .Ledd_mbr_sig_skip
106
107# Read the first sector of each BIOS disk device and store the 4-byte signature
108.Ledd_mbr_sig_start:
109        pushw   %es
110        movb    $0x80, %dl                      # from device 80
111        movw    $bootsym(boot_mbr_signature), %bx # store buffer ptr in bx
112.Ledd_mbr_sig_read:
113        pushw   %bx
114        movw    $bootsym(boot_edd_info), %bx
115        movzbw  bootsym(boot_edd_info_nr), %cx
116        jcxz    .Ledd_mbr_sig_default
117.Ledd_mbr_sig_find_info:
118        cmpb    %dl, (%bx)
119        ja      .Ledd_mbr_sig_default
120        je      .Ledd_mbr_sig_get_size
121        add     $EDDEXTSIZE+EDDPARMSIZE, %bx
122        loop    .Ledd_mbr_sig_find_info
123.Ledd_mbr_sig_default:
124        movw    $(512 >> 4), %bx
125        jmp     .Ledd_mbr_sig_set_buf
126.Ledd_mbr_sig_get_size:
127        movw    EDDEXTSIZE+0x18(%bx), %bx       # sector size
128        shr     $4, %bx                         # convert to paragraphs
129        jz      .Ledd_mbr_sig_default
130.Ledd_mbr_sig_set_buf:
131        movw    %ds, %ax
132        subw    %bx, %ax                        # disk's data goes right ahead
133        movw    %ax, %es                        # of trampoline
134        xorw    %bx, %bx
135        movw    %bx, %es:0x1fe(%bx)             # clear BIOS magic just in case
136        pushw   %dx                             # work around buggy BIOSes
137        stc                                     # work around buggy BIOSes
138        movw    $0x0201, %ax                    # read 1 sector
139        movb    $0, %dh                         # at head 0
140        movw    $1, %cx                         # cylinder 0, sector 0
141        int     $0x13
142        sti                                     # work around buggy BIOSes
143        popw    %dx
144        movw    %es:0x1fe(%bx), %si
145        movl    %es:EDD_MBR_SIG_OFFSET(%bx), %ecx
146        popw    %bx
147        jc      .Ledd_mbr_sig_done              # on failure, we're done.
148        testb   %ah, %ah                        # some BIOSes do not set CF
149        jnz     .Ledd_mbr_sig_done              # on failure, we're done.
150        cmpw    $0xaa55, %si
151        jne     .Ledd_mbr_sig_next
152        movb    %dl, (%bx)                      # store BIOS drive number
153        movl    %ecx, 4(%bx)                    # store signature from MBR
154        incb    bootsym(boot_mbr_signature_nr)  # note that we stored something
155        addw    $8, %bx                         # increment sig buffer ptr
156.Ledd_mbr_sig_next:
157        incb    %dl                             # increment to next device
158        jz      .Ledd_mbr_sig_done
159        cmpb    $EDD_MBR_SIG_MAX, bootsym(boot_mbr_signature_nr)
160        jb      .Ledd_mbr_sig_read
161.Ledd_mbr_sig_done:
162        popw    %es
163.Ledd_mbr_sig_skip:
164        ret
165
166GLOBAL(boot_edd_info_nr)
167        .byte   0
168GLOBAL(boot_mbr_signature_nr)
169        .byte   0
170GLOBAL(boot_mbr_signature)
171        .fill   EDD_MBR_SIG_MAX*8,1,0
172GLOBAL(boot_edd_info)
173        .fill   EDD_INFO_MAX * (EDDEXTSIZE + EDDPARMSIZE), 1, 0
174