summaryrefslogtreecommitdiffstats
path: root/arch/i386/boot/edd.S
blob: 34321368011ac7b2aadf6111f37d038c274609a8 (plain)
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