xref: /freebsd/stand/i386/zfsboot/zfsldr.S (revision b3e7694832e81d7a904a10f525f8797b753bf0d3)
1ca987d46SWarner Losh/*
2ca987d46SWarner Losh * Copyright (c) 1998 Robert Nordier
3ca987d46SWarner Losh * All rights reserved.
4ca987d46SWarner Losh *
5ca987d46SWarner Losh * Redistribution and use in source and binary forms are freely
6ca987d46SWarner Losh * permitted provided that the above copyright notice and this
7ca987d46SWarner Losh * paragraph and the following disclaimer are duplicated in all
8ca987d46SWarner Losh * such forms.
9ca987d46SWarner Losh *
10ca987d46SWarner Losh * This software is provided "AS IS" and without any express or
11ca987d46SWarner Losh * implied warranties, including, without limitation, the implied
12ca987d46SWarner Losh * warranties of merchantability and fitness for a particular
13ca987d46SWarner Losh * purpose.
14ca987d46SWarner Losh */
15ca987d46SWarner Losh
16ca987d46SWarner Losh/* Memory Locations */
17ca987d46SWarner Losh		.set MEM_ARG,0x900		# Arguments
18ca987d46SWarner Losh		.set MEM_ORG,0x7c00		# Origin
19ca987d46SWarner Losh		.set MEM_BUF,0x8000		# Load area
20ca987d46SWarner Losh		.set MEM_BTX,0x9000		# BTX start
21ca987d46SWarner Losh		.set MEM_JMP,0x9010		# BTX entry point
22ca987d46SWarner Losh		.set MEM_USR,0xa000		# Client start
23ca987d46SWarner Losh		.set BDA_BOOT,0x472		# Boot howto flag
24ca987d46SWarner Losh
25ca987d46SWarner Losh/* Partition Constants */
26ca987d46SWarner Losh		.set PRT_OFF,0x1be		# Partition offset
27ca987d46SWarner Losh		.set PRT_NUM,0x4		# Partitions
28ca987d46SWarner Losh		.set PRT_BSD,0xa5		# Partition type
29ca987d46SWarner Losh
30ca987d46SWarner Losh/* Misc. Constants */
31ca987d46SWarner Losh		.set SIZ_PAG,0x1000		# Page size
32ca987d46SWarner Losh		.set SIZ_SEC,0x200		# Sector size
33ca987d46SWarner Losh		.set COPY_BLKS,0x8		# Number of blocks
34*86375a7eSWarner Losh						# to copy for boot2 (<= 15)
35ca987d46SWarner Losh		.set COPY_BLK_SZ,0x8000		# Copy in 32k blocks; must be
36ca987d46SWarner Losh						# a multiple of 16 bytes
37ca987d46SWarner Losh		.set NSECT,(COPY_BLK_SZ / SIZ_SEC * COPY_BLKS)
38ca987d46SWarner Losh		.globl start
39ca987d46SWarner Losh		.code16
40ca987d46SWarner Losh
41ca987d46SWarner Losh/*
42ca987d46SWarner Losh * Load the rest of zfsboot2 and BTX up, copy the parts to the right locations,
43ca987d46SWarner Losh * and start it all up.
44ca987d46SWarner Losh */
45ca987d46SWarner Losh
46ca987d46SWarner Losh/*
47ca987d46SWarner Losh * Setup the segment registers to flat addressing (segment 0) and setup the
48ca987d46SWarner Losh * stack to end just below the start of our code.
49ca987d46SWarner Losh */
50ca987d46SWarner Loshstart:		cld				# String ops inc
51ca987d46SWarner Losh		xor %cx,%cx			# Zero
52ca987d46SWarner Losh		mov %cx,%es			# Address
53ca987d46SWarner Losh		mov %cx,%ds			#  data
54ca987d46SWarner Losh		mov %cx,%ss			# Set up
55ca987d46SWarner Losh		mov $start,%sp			#  stack
56ca987d46SWarner Losh/*
57ca987d46SWarner Losh * Load the MBR and look for the first FreeBSD slice.  We use the fake
58ca987d46SWarner Losh * partition entry below that points to the MBR when we call read.
59ca987d46SWarner Losh * The first pass looks for the first active FreeBSD slice.  The
60ca987d46SWarner Losh * second pass looks for the first non-active FreeBSD slice if the
61ca987d46SWarner Losh * first one fails.
62ca987d46SWarner Losh */
63ca987d46SWarner Losh		call check_edd		 	# Make sure EDD works
64ca987d46SWarner Losh		mov $part4,%si			# Dummy partition
65ca987d46SWarner Losh		xor %eax,%eax			# Read MBR
66ca987d46SWarner Losh		movl $MEM_BUF,%ebx		#  from first
67ca987d46SWarner Losh		call read			#  sector
68ca987d46SWarner Losh		mov $0x1,%cx	 		# Two passes
69ca987d46SWarner Loshmain.1: 	mov $MEM_BUF+PRT_OFF,%si	# Partition table
70ca987d46SWarner Losh		movb $0x1,%dh			# Partition
71ca987d46SWarner Loshmain.2: 	cmpb $PRT_BSD,0x4(%si)		# Our partition type?
72ca987d46SWarner Losh		jne main.3			# No
73ca987d46SWarner Losh		jcxz main.5			# If second pass
74ca987d46SWarner Losh		testb $0x80,(%si)		# Active?
75ca987d46SWarner Losh		jnz main.5			# Yes
76ca987d46SWarner Loshmain.3: 	add $0x10,%si	 		# Next entry
77ca987d46SWarner Losh		incb %dh			# Partition
78ca987d46SWarner Losh		cmpb $0x1+PRT_NUM,%dh		# In table?
79ca987d46SWarner Losh		jb main.2			# Yes
80ca987d46SWarner Losh		dec %cx				# Do two
81ca987d46SWarner Losh		jcxz main.1			#  passes
82ca987d46SWarner Losh/*
83ca987d46SWarner Losh * If we get here, we didn't find any FreeBSD slices at all, so print an
84ca987d46SWarner Losh * error message and die.
85ca987d46SWarner Losh */
86ca987d46SWarner Losh		mov $msg_part,%si		# Message
87ca987d46SWarner Losh		jmp error			# Error
88ca987d46SWarner Losh
89ca987d46SWarner Losh/*
90ca987d46SWarner Losh * Ok, we have a slice and drive in %dx now, so use that to locate and
91ca987d46SWarner Losh * load boot2.  %si references the start of the slice we are looking
92ca987d46SWarner Losh * for, so go ahead and load up the COPY_BLKS*COPY_BLK_SZ/SIZ_SEC sectors
93ca987d46SWarner Losh * starting at sector 1024 (i.e. after the two vdev labels).  We don't
94ca987d46SWarner Losh * have do anything fancy here to allow for an extra copy of boot1 and
95ca987d46SWarner Losh * a partition table (compare to this section of the UFS bootstrap) so we
96ca987d46SWarner Losh * just load it all at 0x9000. The first part of boot2 is BTX, which wants
97ca987d46SWarner Losh * to run at 0x9000. The boot2.bin binary starts right after the end of BTX,
98ca987d46SWarner Losh * so we have to figure out where the start of it is and then move the
99ca987d46SWarner Losh * binary to 0xc000.  Normally, BTX clients start at MEM_USR, or 0xa000,
100ca987d46SWarner Losh * but when we use btxld to create zfsboot2, we use an entry point of
101ca987d46SWarner Losh * 0x2000.  That entry point is relative to MEM_USR; thus boot2.bin
102ca987d46SWarner Losh * starts at 0xc000.
103ca987d46SWarner Losh *
104ca987d46SWarner Losh * The load area and the target area for the client overlap so we have
105ca987d46SWarner Losh * to use a decrementing string move. We also play segment register
106ca987d46SWarner Losh * games with the destination address for the move so that the client
107ca987d46SWarner Losh * can be larger than 16k (which would overflow the zero segment since
108ca987d46SWarner Losh * the client starts at 0xc000).
109ca987d46SWarner Losh */
110ca987d46SWarner Loshmain.5: 	mov %dx,MEM_ARG			# Save args
111ca987d46SWarner Losh		mov $NSECT,%cx			# Sector count
112ca987d46SWarner Losh		movl $1024,%eax			# Offset to boot2
113ca987d46SWarner Losh		mov $MEM_BTX,%ebx		# Destination buffer
114ca987d46SWarner Loshmain.6:		pushal				# Save params
115ca987d46SWarner Losh		call read			# Read disk
116ca987d46SWarner Losh		popal				# Restore
117ca987d46SWarner Losh		incl %eax			# Advance to
118ca987d46SWarner Losh		add $SIZ_SEC,%ebx		#  next sector
119ca987d46SWarner Losh		loop main.6			# If not last, read another
120ca987d46SWarner Losh
121ca987d46SWarner Losh		mov $MEM_BTX,%bx		# BTX
122ca987d46SWarner Losh		mov 0xa(%bx),%si		# Get BTX length and set
123ca987d46SWarner Losh		add %bx,%si			#  %si to start of boot2
124ca987d46SWarner Losh		dec %si				# Set %ds:%si to point at the
125ca987d46SWarner Losh		mov %si,%ax			# last byte we want to copy
126ca987d46SWarner Losh		shr $4,%ax			# from boot2, with %si made as
127ca987d46SWarner Losh		add $(COPY_BLKS*COPY_BLK_SZ/16),%ax	# small as possible.
128ca987d46SWarner Losh		and $0xf,%si			#
129ca987d46SWarner Losh		mov %ax,%ds			#
130ca987d46SWarner Losh		mov $(MEM_USR+2*SIZ_PAG)/16,%ax # Set %es:(-1) to point at
131ca987d46SWarner Losh		add $(COPY_BLKS*COPY_BLK_SZ/16),%ax	# the last byte we
132ca987d46SWarner Losh		mov %ax,%es			# want to copy boot2 into.
133ca987d46SWarner Losh		mov $COPY_BLKS,%bx		# Copy COPY_BLKS 32k blocks
134ca987d46SWarner Loshcopyloop:
135ca987d46SWarner Losh		add $COPY_BLK_SZ,%si		# Adjust %ds:%si to point at
136ca987d46SWarner Losh		mov %ds,%ax			# the end of the next 32k to
137ca987d46SWarner Losh		sub $COPY_BLK_SZ/16,%ax		# copy from boot2
138ca987d46SWarner Losh		mov %ax,%ds
139ca987d46SWarner Losh		mov $COPY_BLK_SZ-1,%di		# Adjust %es:%di to point at
140ca987d46SWarner Losh		mov %es,%ax			# the end of the next 32k into
141ca987d46SWarner Losh		sub $COPY_BLK_SZ/16,%ax		# which we want boot2 copied
142ca987d46SWarner Losh		mov %ax,%es
143ca987d46SWarner Losh		mov $COPY_BLK_SZ,%cx		# Copy 32k
144ca987d46SWarner Losh		std
145ca987d46SWarner Losh		rep movsb
146ca987d46SWarner Losh		dec %bx
147ca987d46SWarner Losh		jnz copyloop
148ca987d46SWarner Losh		mov %cx,%ds			# Reset %ds and %es
149ca987d46SWarner Losh		mov %cx,%es
150ca987d46SWarner Losh		cld				# Back to increment
151ca987d46SWarner Losh
152ca987d46SWarner Losh/*
153ca987d46SWarner Losh * Enable A20 so we can access memory above 1 meg.
154ca987d46SWarner Losh * Use the zero-valued %cx as a timeout for embedded hardware which do not
155ca987d46SWarner Losh * have a keyboard controller.
156ca987d46SWarner Losh */
157ca987d46SWarner Loshseta20: 	cli				# Disable interrupts
158ca987d46SWarner Loshseta20.1:	dec %cx				# Timeout?
159ca987d46SWarner Losh		jz seta20.3			# Yes
160ca987d46SWarner Losh		inb $0x64,%al			# Get status
161ca987d46SWarner Losh		testb $0x2,%al			# Busy?
162ca987d46SWarner Losh		jnz seta20.1			# Yes
163ca987d46SWarner Losh		movb $0xd1,%al			# Command: Write
164ca987d46SWarner Losh		outb %al,$0x64			#  output port
165ca987d46SWarner Loshseta20.2:	inb $0x64,%al			# Get status
166ca987d46SWarner Losh		testb $0x2,%al			# Busy?
167ca987d46SWarner Losh		jnz seta20.2			# Yes
168ca987d46SWarner Losh		movb $0xdf,%al			# Enable
169ca987d46SWarner Losh		outb %al,$0x60			#  A20
170ca987d46SWarner Loshseta20.3:	sti				# Enable interrupts
171ca987d46SWarner Losh
172ca987d46SWarner Losh		jmp start+MEM_JMP-MEM_ORG	# Start BTX
173ca987d46SWarner Losh
174ca987d46SWarner Losh
175ca987d46SWarner Losh/*
176ca987d46SWarner Losh * Read a sector from the disk.  Sets up an EDD packet on the stack
177ca987d46SWarner Losh * and passes it to read.  We assume that the destination address is
178ca987d46SWarner Losh * always segment-aligned.
179ca987d46SWarner Losh *
180ca987d46SWarner Losh * %eax		- int     - LBA to read in relative to partition start
181ca987d46SWarner Losh * %ebx		- ptr	  - destination address
182ca987d46SWarner Losh * %dl		- byte    - drive to read from
183ca987d46SWarner Losh * %si		- ptr     - MBR partition entry
184ca987d46SWarner Losh */
185ca987d46SWarner Loshread:		xor %ecx,%ecx			# Get
186ca987d46SWarner Losh		addl 0x8(%si),%eax		#  LBA
187ca987d46SWarner Losh		adc $0,%ecx
188ca987d46SWarner Losh		pushl %ecx			# Starting absolute block
189ca987d46SWarner Losh		pushl %eax			#  block number
190ca987d46SWarner Losh		shr $4,%ebx			# Convert to segment
191ca987d46SWarner Losh		push %bx			# Address of
192ca987d46SWarner Losh		push $0				#  transfer buffer
193ca987d46SWarner Losh		push $0x1			# Read 1 sector
194ca987d46SWarner Losh		push $0x10			# Size of packet
195ca987d46SWarner Losh		mov %sp,%si			# Packet pointer
196ca987d46SWarner Losh		mov $0x42,%ah			# BIOS: Extended
197ca987d46SWarner Losh		int $0x13			#  read
198ca987d46SWarner Losh		jc read.1			# If error, fail
199ca987d46SWarner Losh		lea 0x10(%si),%sp		# Clear stack
200ca987d46SWarner Losh		ret				# If success, return
201ca987d46SWarner Loshread.1:		mov %ah,%al			# Format
202ca987d46SWarner Losh		mov $read_err,%di		#  error
203ca987d46SWarner Losh		call hex8			#  code
204ca987d46SWarner Losh		mov $msg_read,%si		# Set the error message and
205ca987d46SWarner Losh						#  fall through to the error
206ca987d46SWarner Losh						#  routine
207ca987d46SWarner Losh/*
208ca987d46SWarner Losh * Print out the error message pointed to by %ds:(%si) followed
209ca987d46SWarner Losh * by a prompt, wait for a keypress, and then reboot the machine.
210ca987d46SWarner Losh */
211ca987d46SWarner Losherror:		callw putstr			# Display message
212ca987d46SWarner Losh		mov $prompt,%si			# Display
213ca987d46SWarner Losh		callw putstr			#  prompt
214ca987d46SWarner Losh		xorb %ah,%ah			# BIOS: Get
215ca987d46SWarner Losh		int $0x16			#  keypress
216ca987d46SWarner Losh		movw $0x1234, BDA_BOOT		# Do a warm boot
217ca987d46SWarner Losh		ljmp $0xffff,$0x0		# reboot the machine
218ca987d46SWarner Losh/*
219ca987d46SWarner Losh * Display a null-terminated string using the BIOS output.
220ca987d46SWarner Losh */
221ca987d46SWarner Loshputstr.0:	mov $0x7,%bx	 		# Page:attribute
222ca987d46SWarner Losh		movb $0xe,%ah			# BIOS: Display
223ca987d46SWarner Losh		int $0x10			#  character
224ca987d46SWarner Loshputstr: 	lodsb				# Get char
225ca987d46SWarner Losh		testb %al,%al			# End of string?
226ca987d46SWarner Losh		jne putstr.0			# No
227ca987d46SWarner Losh		ret				# To caller
228ca987d46SWarner Losh/*
229ca987d46SWarner Losh * Check to see if the disk supports EDD.  zfsboot requires EDD and does not
230ca987d46SWarner Losh * support older C/H/S disk I/O.
231ca987d46SWarner Losh */
232ca987d46SWarner Loshcheck_edd:	cmpb $0x80,%dl			# Hard drive?
233ca987d46SWarner Losh		jb check_edd.1 			# No, fail to boot
234ca987d46SWarner Losh		mov $0x55aa,%bx			# Magic
235ca987d46SWarner Losh		push %dx			# Save
236ca987d46SWarner Losh		movb $0x41,%ah			# BIOS: Check
237ca987d46SWarner Losh		int $0x13			#  extensions present
238ca987d46SWarner Losh		pop %dx				# Restore
239ca987d46SWarner Losh		jc check_edd.1			# If error, fail
240ca987d46SWarner Losh		cmp $0xaa55,%bx			# Magic?
241ca987d46SWarner Losh		jne check_edd.1			# No, so fail
242ca987d46SWarner Losh		testb $0x1,%cl			# Packet interface?
243ca987d46SWarner Losh		jz check_edd.1			# No, so fail
244ca987d46SWarner Losh		ret				# EDD ok, keep booting
245ca987d46SWarner Loshcheck_edd.1:	mov $msg_chs,%si		# Warn that CHS is
246ca987d46SWarner Losh		jmp error			#  unsupported and fail
247ca987d46SWarner Losh/*
248ca987d46SWarner Losh * AL to hex, saving the result to [EDI].
249ca987d46SWarner Losh */
250ca987d46SWarner Loshhex8:		push %ax			# Save
251ca987d46SWarner Losh		shrb $0x4,%al			# Do upper
252ca987d46SWarner Losh		call hex8.1			#  4
253ca987d46SWarner Losh		pop %ax				# Restore
254ca987d46SWarner Loshhex8.1: 	andb $0xf,%al			# Get lower 4
255ca987d46SWarner Losh		cmpb $0xa,%al			# Convert
256ca987d46SWarner Losh		sbbb $0x69,%al			#  to hex
257ca987d46SWarner Losh		das				#  digit
258ca987d46SWarner Losh		orb $0x20,%al			# To lower case
259ca987d46SWarner Losh		stosb				# Save char
260ca987d46SWarner Losh		ret				# (Recursive)
261ca987d46SWarner Losh
262ca987d46SWarner Losh/* Messages */
263ca987d46SWarner Losh
264ca987d46SWarner Loshmsg_chs:	.asciz "CHS not supported"
265ca987d46SWarner Loshmsg_read:	.ascii "Read error: "
266ca987d46SWarner Loshread_err:	.asciz "XX"
267ca987d46SWarner Loshmsg_part:	.asciz "Boot error"
268ca987d46SWarner Losh
269ca987d46SWarner Loshprompt: 	.asciz "\r\n"
270ca987d46SWarner Losh
271ca987d46SWarner Losh		.org PRT_OFF,0x90
272ca987d46SWarner Losh
273ca987d46SWarner Losh/* Partition table */
274ca987d46SWarner Losh
275ca987d46SWarner Losh		.fill 0x30,0x1,0x0
276ca987d46SWarner Loshpart4:		.byte 0x80, 0x00, 0x01, 0x00
277ca987d46SWarner Losh		.byte 0xa5, 0xfe, 0xff, 0xff
278ca987d46SWarner Losh		.byte 0x00, 0x00, 0x00, 0x00
279ca987d46SWarner Losh		.byte 0x50, 0xc3, 0x00, 0x00	# 50000 sectors long, bleh
280ca987d46SWarner Losh
281ca987d46SWarner Losh		.word 0xaa55			# Magic number
282