1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
|
/*
* BIOS Enhanced Disk Drive support
* Copyright (C) 2002, 2003, 2004 Dell, Inc.
* by Matt Domsch <Matt_Domsch@dell.com> October 2002
* conformant to T13 Committee www.t13.org
* projects 1572D, 1484D, 1386D, 1226DT
* disk signature read by Matt Domsch <Matt_Domsch@dell.com>
* and Andrew Wilks <Andrew_Wilks@dell.com> September 2003, June 2004
* legacy CHS retrieval by Patrick J. LoPresti <patl@users.sourceforge.net>
* March 2004
* Command line option parsing, Matt Domsch, November 2004
*/
#include <linux/edd.h>
#include <asm/setup.h>
#if defined(CONFIG_EDD) || defined(CONFIG_EDD_MODULE)
# It is assumed that %ds == INITSEG here
movb $0, (EDD_MBR_SIG_NR_BUF)
movb $0, (EDDNR)
# Check the command line for options:
# edd=of disables EDD completely (edd=off)
# edd=sk skips the MBR test (edd=skipmbr)
# edd=on re-enables EDD (edd=on)
pushl %esi
movw $edd_mbr_sig_start, %di # Default to edd=on
movl %cs:(cmd_line_ptr), %esi
andl %esi, %esi
jz old_cl # Old boot protocol?
# Convert to a real-mode pointer in fs:si
movl %esi, %eax
shrl $4, %eax
movw %ax, %fs
andw $0xf, %si
jmp have_cl_pointer
# Old-style boot protocol?
old_cl:
push %ds # aka INITSEG
pop %fs
cmpw $0xa33f, (0x20)
jne done_cl # No command line at all?
movw (0x22), %si # Pointer relative to INITSEG
# fs:si has the pointer to the command line now
have_cl_pointer:
# Loop through kernel command line one byte at a time. Just in
# case the loader is buggy and failed to null-terminate the command line
# terminate if we get close enough to the end of the segment that we
# cannot fit "edd=XX"...
cl_atspace:
cmpw $-5, %si # Watch for segment wraparound
jae done_cl
movl %fs:(%si), %eax
andb %al, %al # End of line?
jz done_cl
cmpl $EDD_CL_EQUALS, %eax
jz found_edd_equals
cmpb $0x20, %al # <= space consider whitespace
ja cl_skipword
incw %si
jmp cl_atspace
cl_skipword:
cmpw $-5, %si # Watch for segment wraparound
jae done_cl
movb %fs:(%si), %al # End of string?
andb %al, %al
jz done_cl
cmpb $0x20, %al
jbe cl_atspace
incw %si
jmp cl_skipword
found_edd_equals:
# only looking at first two characters after equals
# late overrides early on the command line, so keep going after finding something
movw %fs:4(%si), %ax
cmpw $EDD_CL_OFF, %ax # edd=of
je do_edd_off
cmpw $EDD_CL_SKIP, %ax # edd=sk
je do_edd_skipmbr
cmpw $EDD_CL_ON, %ax # edd=on
je do_edd_on
jmp cl_skipword
do_edd_skipmbr:
movw $edd_start, %di
jmp cl_skipword
do_edd_off:
movw $edd_done, %di
jmp cl_skipword
do_edd_on:
movw $edd_mbr_sig_start, %di
jmp cl_skipword
done_cl:
popl %esi
jmpw *%di
# Read the first sector of each BIOS disk device and store the 4-byte signature
edd_mbr_sig_start:
movb $0x80, %dl # from device 80
movw $EDD_MBR_SIG_BUF, %bx # store buffer ptr in bx
edd_mbr_sig_read:
movl $0xFFFFFFFF, %eax
movl %eax, (%bx) # assume failure
pushw %bx
movb $READ_SECTORS, %ah
movb $1, %al # read 1 sector
movb $0, %dh # at head 0
movw $1, %cx # cylinder 0, sector 0
pushw %es
pushw %ds
popw %es
movw $EDDBUF, %bx # disk's data goes into EDDBUF
pushw %dx # work around buggy BIOSes
stc # work around buggy BIOSes
int $0x13
sti # work around buggy BIOSes
popw %dx
popw %es
popw %bx
jc edd_mbr_sig_done # on failure, we're done.
cmpb $0, %ah # some BIOSes do not set CF
jne edd_mbr_sig_done # on failure, we're done.
movl (EDDBUF+EDD_MBR_SIG_OFFSET), %eax # read sig out of the MBR
movl %eax, (%bx) # store success
incb (EDD_MBR_SIG_NR_BUF) # note that we stored something
incb %dl # increment to next device
addw $4, %bx # increment sig buffer ptr
cmpb $EDD_MBR_SIG_MAX, (EDD_MBR_SIG_NR_BUF) # Out of space?
jb edd_mbr_sig_read # keep looping
edd_mbr_sig_done:
# Do the BIOS Enhanced Disk Drive calls
# This consists of two calls:
# int 13h ah=41h "Check Extensions Present"
# int 13h ah=48h "Get Device Parameters"
# int 13h ah=08h "Legacy Get Device Parameters"
#
# A buffer of size EDDMAXNR*(EDDEXTSIZE+EDDPARMSIZE) is reserved for our use
# in the boot_params at EDDBUF. The first four bytes of which are
# used to store the device number, interface support map and version
# results from fn41. The next four bytes are used to store the legacy
# cylinders, heads, and sectors from fn08. The following 74 bytes are used to
# store the results from fn48. Starting from device 80h, fn41, then fn48
# are called and their results stored in EDDBUF+n*(EDDEXTSIZE+EDDPARMIZE).
# Then the pointer is incremented to store the data for the next call.
# This repeats until either a device doesn't exist, or until EDDMAXNR
# devices have been stored.
# The one tricky part is that ds:si always points EDDEXTSIZE bytes into
# the structure, and the fn41 and fn08 results are stored at offsets
# from there. This removes the need to increment the pointer for
# every store, and leaves it ready for the fn48 call.
# A second one-byte buffer, EDDNR, in the boot_params stores
# the number of BIOS devices which exist, up to EDDMAXNR.
# In setup.c, copy_edd() stores both boot_params buffers away
# for later use, as they would get overwritten otherwise.
# This code is sensitive to the size of the structs in edd.h
edd_start:
# %ds points to the bootsector
# result buffer for fn48
movw $EDDBUF+EDDEXTSIZE, %si # in ds:si, fn41 results
# kept just before that
movb $0x80, %dl # BIOS device 0x80
edd_check_ext:
movb $CHECKEXTENSIONSPRESENT, %ah # Function 41
movw $EDDMAGIC1, %bx # magic
int $0x13 # make the call
jc edd_done # no more BIOS devices
cmpw $EDDMAGIC2, %bx # is magic right?
jne edd_next # nope, next...
movb %dl, %ds:-8(%si) # store device number
movb %ah, %ds:-7(%si) # store version
movw %cx, %ds:-6(%si) # store extensions
incb (EDDNR) # note that we stored something
edd_get_device_params:
movw $EDDPARMSIZE, %ds:(%si) # put size
movw $0x0, %ds:2(%si) # work around buggy BIOSes
movb $GETDEVICEPARAMETERS, %ah # Function 48
int $0x13 # make the call
# Don't check for fail return
# it doesn't matter.
edd_get_legacy_chs:
xorw %ax, %ax
movw %ax, %ds:-4(%si)
movw %ax, %ds:-2(%si)
# Ralf Brown's Interrupt List says to set ES:DI to
# 0000h:0000h "to guard against BIOS bugs"
pushw %es
movw %ax, %es
movw %ax, %di
pushw %dx # legacy call clobbers %dl
movb $LEGACYGETDEVICEPARAMETERS, %ah # Function 08
int $0x13 # make the call
jc edd_legacy_done # failed
movb %cl, %al # Low 6 bits are max
andb $0x3F, %al # sector number
movb %al, %ds:-1(%si) # Record max sect
movb %dh, %ds:-2(%si) # Record max head number
movb %ch, %al # Low 8 bits of max cyl
shr $6, %cl
movb %cl, %ah # High 2 bits of max cyl
movw %ax, %ds:-4(%si)
edd_legacy_done:
popw %dx
popw %es
movw %si, %ax # increment si
addw $EDDPARMSIZE+EDDEXTSIZE, %ax
movw %ax, %si
edd_next:
incb %dl # increment to next device
cmpb $EDDMAXNR, (EDDNR) # Out of space?
jb edd_check_ext # keep looping
edd_done:
#endif
|