summaryrefslogtreecommitdiffstats
path: root/sys/boot/i386/zfsboot/zfsldr.S
blob: 87e04d5278f3050f986f2dd0dd0140c303b6a260 (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
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
/*
 * Copyright (c) 1998 Robert Nordier
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are freely
 * permitted provided that the above copyright notice and this
 * paragraph and the following disclaimer are duplicated in all
 * such forms.
 *
 * This software is provided "AS IS" and without any express or
 * implied warranties, including, without limitation, the implied
 * warranties of merchantability and fitness for a particular
 * purpose.
 *
 * $FreeBSD$
 */

/* Memory Locations */
		.set MEM_ARG,0x900		# Arguments
		.set MEM_ORG,0x7c00		# Origin
		.set MEM_BUF,0x8000		# Load area
		.set MEM_BTX,0x9000		# BTX start
		.set MEM_JMP,0x9010		# BTX entry point
		.set MEM_USR,0xa000		# Client start
		.set BDA_BOOT,0x472		# Boot howto flag
	
/* Partition Constants */
		.set PRT_OFF,0x1be		# Partition offset
		.set PRT_NUM,0x4		# Partitions
		.set PRT_BSD,0xa5		# Partition type

/* Misc. Constants */
		.set SIZ_PAG,0x1000		# Page size
		.set SIZ_SEC,0x200		# Sector size

		.set NSECT,0x80
		.globl main
		.code16

/*
 * Load the rest of zfsboot2 and BTX up, copy the parts to the right locations,
 * and start it all up.
 */

/*
 * Setup the segment registers to flat addressing (segment 0) and setup the
 * stack to end just below the start of our code.
 */
main:		cld				# String ops inc
		xor %cx,%cx			# Zero
		mov %cx,%es			# Address
		mov %cx,%ds			#  data
		mov %cx,%ss			# Set up
		mov $start,%sp			#  stack
/*
 * Load the MBR and look for the first FreeBSD slice.  We use the fake
 * partition entry below that points to the MBR when we call read.
 * The first pass looks for the first active FreeBSD slice.  The
 * second pass looks for the first non-active FreeBSD slice if the
 * first one fails.
 */
		call check_edd		 	# Make sure EDD works
		mov $part4,%si			# Dummy partition
		xor %eax,%eax			# Read MBR
		movl $MEM_BUF,%ebx		#  from first
		call read			#  sector
		mov $0x1,%cx	 		# Two passes
main.1: 	mov $MEM_BUF+PRT_OFF,%si	# Partition table
		movb $0x1,%dh			# Partition
main.2: 	cmpb $PRT_BSD,0x4(%si)		# Our partition type?
		jne main.3			# No
		jcxz main.5			# If second pass
		testb $0x80,(%si)		# Active?
		jnz main.5			# Yes
main.3: 	add $0x10,%si	 		# Next entry
		incb %dh			# Partition
		cmpb $0x1+PRT_NUM,%dh		# In table?
		jb main.2			# Yes
		dec %cx				# Do two
		jcxz main.1			#  passes
/*
 * If we get here, we didn't find any FreeBSD slices at all, so print an
 * error message and die.
 */
		mov $msg_part,%si		# Message
		jmp error			# Error

/*
 * Ok, we have a slice and drive in %dx now, so use that to locate and
 * load boot2.  %si references the start of the slice we are looking
 * for, so go ahead and load up the 128 sectors starting at sector 1024
 * (i.e. after the two vdev labels).  We don't have do anything fancy
 * here to allow for an extra copy of boot1 and a partition table
 * (compare to this section of the UFS bootstrap) so we just load it
 * all at 0x9000. The first part of boot2 is BTX, which wants to run
 * at 0x9000. The boot2.bin binary starts right after the end of BTX,
 * so we have to figure out where the start of it is and then move the
 * binary to 0xc000.  Normally, BTX clients start at MEM_USR, or 0xa000,
 * but when we use btxld to create zfsboot2, we use an entry point of
 * 0x2000.  That entry point is relative to MEM_USR; thus boot2.bin
 * starts at 0xc000.
 *
 * The load area and the target area for the client overlap so we have
 * to use a decrementing string move. We also play segment register
 * games with the destination address for the move so that the client
 * can be larger than 16k (which would overflow the zero segment since
 * the client starts at 0xc000).
 */
main.5: 	mov %dx,MEM_ARG			# Save args
		mov $NSECT,%cx			# Sector count
		movl $1024,%eax			# Offset to boot2
		mov $MEM_BTX,%ebx		# Destination buffer
main.6:		pushal				# Save params
		call read			# Read disk
		popal				# Restore
		incl %eax			# Advance to
		add $SIZ_SEC,%ebx		#  next sector
		loop main.6			# If not last, read another
		mov MEM_BTX+0xa,%bx		# Get BTX length
		mov $NSECT*SIZ_SEC-1,%di	# Size of load area (less one)
		mov %di,%si			# End of load area, 0x9000 rel
		sub %bx,%di			# End of client, 0xc000 rel
		mov %di,%cx			# Size of
		inc %cx				#  client
		mov $(MEM_BTX)>>4,%dx		# Segment
		mov %dx,%ds			#   addressing 0x9000
		mov $(MEM_USR+2*SIZ_PAG)>>4,%dx	# Segment
		mov %dx,%es			#   addressing 0xc000
		std				# Move with decrement
		rep				# Relocate
		movsb				#  client
		cld				# Back to increment
		xor %dx,%dx			# Back
		mov %ds,%dx			#  to zero
		mov %dx,%es			#  segment

/*
 * Enable A20 so we can access memory above 1 meg.
 * Use the zero-valued %cx as a timeout for embedded hardware which do not
 * have a keyboard controller.
 */
seta20: 	cli				# Disable interrupts
seta20.1:	dec %cx				# Timeout?
		jz seta20.3			# Yes
		inb $0x64,%al			# Get status
		testb $0x2,%al			# Busy?
		jnz seta20.1			# Yes
		movb $0xd1,%al			# Command: Write
		outb %al,$0x64			#  output port
seta20.2:	inb $0x64,%al			# Get status
		testb $0x2,%al			# Busy?
		jnz seta20.2			# Yes
		movb $0xdf,%al			# Enable
		outb %al,$0x60			#  A20
seta20.3:	sti				# Enable interrupts

		jmp start+MEM_JMP-MEM_ORG	# Start BTX


/*
 * Read a sector from the disk.  Sets up an EDD packet on the stack
 * and passes it to read.  We assume that the destination address is
 * always segment-aligned.
 *
 * %eax		- int     - LBA to read in relative to partition start
 * %ebx		- ptr	  - destination address
 * %dl		- byte    - drive to read from
 * %si		- ptr     - MBR partition entry
 */
read:		xor %ecx,%ecx			# Get
		addl 0x8(%si),%eax		#  LBA
		adc $0,%ecx
		pushl %ecx			# Starting absolute block
		pushl %eax			#  block number
		shr $4,%ebx			# Convert to segment
		push %bx			# Address of
		push $0				#  transfer buffer
		push $0x1			# Read 1 sector
		push $0x10			# Size of packet
		mov %sp,%si			# Packet pointer
		mov $0x42,%ah			# BIOS: Extended
		int $0x13			#  read
		jc read.1			# If error, fail
		lea 0x10(%si),%sp		# Clear stack
		ret				# If success, return
read.1:		mov %ah,%al			# Format
		mov $read_err,%di		#  error
		call hex8			#  code
		mov $msg_read,%si		# Set the error message and
						#  fall through to the error
						#  routine
/*
 * Print out the error message pointed to by %ds:(%si) followed
 * by a prompt, wait for a keypress, and then reboot the machine.
 */
error:		callw putstr			# Display message
		mov $prompt,%si			# Display
		callw putstr			#  prompt
		xorb %ah,%ah			# BIOS: Get
		int $0x16			#  keypress
		movw $0x1234, BDA_BOOT		# Do a warm boot
		ljmp $0xffff,$0x0		# reboot the machine
/*
 * Display a null-terminated string using the BIOS output.
 */
putstr.0:	mov $0x7,%bx	 		# Page:attribute
		movb $0xe,%ah			# BIOS: Display
		int $0x10			#  character
putstr: 	lodsb				# Get char
		testb %al,%al			# End of string?
		jne putstr.0			# No
		ret				# To caller
/*
 * Check to see if the disk supports EDD.  zfsboot requires EDD and does not
 * support older C/H/S disk I/O.
 */
check_edd:	cmpb $0x80,%dl			# Hard drive?
		jb check_edd.1 			# No, fail to boot
		mov $0x55aa,%bx			# Magic
		push %dx			# Save
		movb $0x41,%ah			# BIOS: Check
		int $0x13			#  extensions present
		pop %dx				# Restore
		jc check_edd.1			# If error, fail
		cmp $0xaa55,%bx			# Magic?
		jne check_edd.1			# No, so fail
		testb $0x1,%cl			# Packet interface?
		jz check_edd.1			# No, so fail
		ret				# EDD ok, keep booting
check_edd.1:	mov $msg_chs,%si		# Warn that CHS is
		jmp error			#  unsupported and fail
/*
 * AL to hex, saving the result to [EDI].
 */
hex8:		push %ax			# Save
		shrb $0x4,%al			# Do upper
		call hex8.1			#  4
		pop %ax				# Restore
hex8.1: 	andb $0xf,%al			# Get lower 4
		cmpb $0xa,%al			# Convert
		sbbb $0x69,%al			#  to hex
		das				#  digit
		orb $0x20,%al			# To lower case
		stosb				# Save char
		ret				# (Recursive)

/* Messages */

msg_chs:	.asciz "CHS not supported"
msg_read:	.ascii "Read error: "
read_err:	.asciz "XX"
msg_part:	.asciz "Boot error"

prompt: 	.asciz "\r\n"

		.org PRT_OFF,0x90

/* Partition table */

		.fill 0x30,0x1,0x0
part4:		.byte 0x80, 0x00, 0x01, 0x00
		.byte 0xa5, 0xfe, 0xff, 0xff
		.byte 0x00, 0x00, 0x00, 0x00
		.byte 0x50, 0xc3, 0x00, 0x00	# 50000 sectors long, bleh

		.word 0xaa55			# Magic number
OpenPOWER on IntegriCloud