aboutsummaryrefslogtreecommitdiffstats
path: root/arch/i386/boot/edd.S
blob: d8d69f2b911d62c909b651de0d9e61a7c7b3b461 (plain) (blame)
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
/*
 * 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)
	movb	$0, (EDD_MBR_SIG_NR_BUF)
	movb	$0, (EDDNR)

# Check the command line for two options:
# edd=of  disables EDD completely  (edd=off)
# edd=sk  skips the MBR test    (edd=skipmbr)
	pushl	%esi
    	cmpl	$0, %cs:cmd_line_ptr
	jz	done_cl
	movl	%cs:(cmd_line_ptr), %esi
# ds:esi has the pointer to the command line now
	movl	$(COMMAND_LINE_SIZE-7), %ecx
# loop through kernel command line one byte at a time
cl_loop:
	cmpl	$EDD_CL_EQUALS, (%si)
	jz	found_edd_equals
	incl	%esi
	loop	cl_loop
	jmp	done_cl
found_edd_equals:
# only looking at first two characters after equals
    	addl	$4, %esi
	cmpw	$EDD_CL_OFF, (%si)	# edd=of
	jz	do_edd_off
	cmpw	$EDD_CL_SKIP, (%si)	# edd=sk
	jz	do_edd_skipmbr
	jmp	done_cl
do_edd_skipmbr:
    	popl	%esi
	jmp	edd_start
do_edd_off:
	popl	%esi
	jmp	edd_done
done_cl:
	popl	%esi


# 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.
	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