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?
44old_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
53have_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"...
59cl_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
72cl_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
83found_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
94do_edd_skipmbr:
95	movw	$edd_start, %di
96	jmp	cl_skipword
97do_edd_off:
98	movw	$edd_done, %di
99	jmp	cl_skipword
100do_edd_on:
101	movw	$edd_mbr_sig_start, %di
102	jmp	cl_skipword
103
104done_cl:
105	popl	%esi
106	jmpw	*%di
107
108# Read the first sector of each BIOS disk device and store the 4-byte signature
109edd_mbr_sig_start:
110	movb	$0x80, %dl			# from device 80
111	movw	$EDD_MBR_SIG_BUF, %bx		# store buffer ptr in bx
112edd_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
141edd_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
168edd_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
175edd_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
189edd_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.
196edd_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
218edd_legacy_done:
219	popw    %dx
220	popw    %es
221	movw	%si, %ax			# increment si
222	addw	$EDDPARMSIZE+EDDEXTSIZE, %ax
223	movw	%ax, %si
224
225edd_next:
226	incb	%dl				# increment to next device
227	cmpb	$EDDMAXNR, (EDDNR) 		# Out of space?
228	jb	edd_check_ext			# keep looping
229
230edd_done:
231#endif
232