1207753Smm/*
2207753Smm * Copyright (c) 1998 Robert Nordier
3207753Smm * All rights reserved.
4207753Smm *
5207753Smm * Redistribution and use in source and binary forms are freely
6207753Smm * permitted provided that the above copyright notice and this
7207753Smm * paragraph and the following disclaimer are duplicated in all
8207753Smm * such forms.
9207753Smm *
10207753Smm * This software is provided "AS IS" and without any express or
11207753Smm * implied warranties, including, without limitation, the implied
12207753Smm * warranties of merchantability and fitness for a particular
13207753Smm * purpose.
14207753Smm *
15207753Smm * $FreeBSD$
16207753Smm */
17207753Smm
18207753Smm/* Memory Locations */
19213700Smm		.set MEM_ARG,0x900		# Arguments
20213700Smm		.set MEM_ORG,0x7c00		# Origin
21213700Smm		.set MEM_BUF,0x8000		# Load area
22213700Smm		.set MEM_BTX,0x9000		# BTX start
23213700Smm		.set MEM_JMP,0x9010		# BTX entry point
24213700Smm		.set MEM_USR,0xa000		# Client start
25207753Smm		.set BDA_BOOT,0x472		# Boot howto flag
26207753Smm
27207753Smm/* Partition Constants */
28207753Smm		.set PRT_OFF,0x1be		# Partition offset
29207753Smm		.set PRT_NUM,0x4		# Partitions
30207753Smm		.set PRT_BSD,0xa5		# Partition type
31207753Smm
32207753Smm/* Misc. Constants */
33207753Smm		.set SIZ_PAG,0x1000		# Page size
34207753Smm		.set SIZ_SEC,0x200		# Sector size
35213700Smm
36213700Smm		.set NSECT,0x80
37213700Smm		.globl start
38213700Smm		.code16
39207753Smm
40207753Smm/*
41207753Smm * Load the rest of zfsboot2 and BTX up, copy the parts to the right locations,
42213700Smm * and start it all up.
43213700Smm */
44213700Smm
45213700Smm/*
46207753Smm * Setup the segment registers to flat addressing (segment 0) and setup the
47207753Smm * stack to end just below the start of our code.
48207753Smm */
49207753Smmstart:		cld				# String ops inc
50207753Smm		xor %cx,%cx			# Zero
51207753Smm		mov %cx,%es			# Address
52207753Smm		mov %cx,%ds			#  data
53207753Smm		mov %cx,%ss			# Set up
54207753Smm		mov $start,%sp			#  stack
55207753Smm/*
56207753Smm * Load the MBR and look for the first FreeBSD slice.  We use the fake
57207753Smm * partition entry below that points to the MBR when we call read.
58207753Smm * The first pass looks for the first active FreeBSD slice.  The
59207753Smm * second pass looks for the first non-active FreeBSD slice if the
60207753Smm * first one fails.
61207753Smm */
62207753Smm		call check_edd		 	# Make sure EDD works
63207753Smm		mov $part4,%si			# Dummy partition
64207753Smm		xor %eax,%eax			# Read MBR
65207753Smm		movl $MEM_BUF,%ebx		#  from first
66207753Smm		call read			#  sector
67207753Smm		mov $0x1,%cx	 		# Two passes
68207753Smmmain.1: 	mov $MEM_BUF+PRT_OFF,%si	# Partition table
69207753Smm		movb $0x1,%dh			# Partition
70207753Smmmain.2: 	cmpb $PRT_BSD,0x4(%si)		# Our partition type?
71207753Smm		jne main.3			# No
72207753Smm		jcxz main.5			# If second pass
73207753Smm		testb $0x80,(%si)		# Active?
74207753Smm		jnz main.5			# Yes
75207753Smmmain.3: 	add $0x10,%si	 		# Next entry
76207753Smm		incb %dh			# Partition
77207753Smm		cmpb $0x1+PRT_NUM,%dh		# In table?
78207753Smm		jb main.2			# Yes
79207753Smm		dec %cx				# Do two
80207753Smm		jcxz main.1			#  passes
81207753Smm/*
82207753Smm * If we get here, we didn't find any FreeBSD slices at all, so print an
83207753Smm * error message and die.
84207753Smm */
85207753Smm		mov $msg_part,%si		# Message
86207753Smm		jmp error			# Error
87207753Smm
88207753Smm/*
89207753Smm * Ok, we have a slice and drive in %dx now, so use that to locate and
90207753Smm * load boot2.  %si references the start of the slice we are looking
91207753Smm * for, so go ahead and load up the 128 sectors starting at sector 1024
92207753Smm * (i.e. after the two vdev labels).  We don't have do anything fancy
93207753Smm * here to allow for an extra copy of boot1 and a partition table
94207753Smm * (compare to this section of the UFS bootstrap) so we just load it
95207753Smm * all at 0x9000. The first part of boot2 is BTX, which wants to run
96207753Smm * at 0x9000. The boot2.bin binary starts right after the end of BTX,
97207753Smm * so we have to figure out where the start of it is and then move the
98207753Smm * binary to 0xc000.  Normally, BTX clients start at MEM_USR, or 0xa000,
99207753Smm * but when we use btxld to create zfsboot2, we use an entry point of
100207753Smm * 0x2000.  That entry point is relative to MEM_USR; thus boot2.bin
101207753Smm * starts at 0xc000.
102207753Smm *
103207753Smm * The load area and the target area for the client overlap so we have
104207753Smm * to use a decrementing string move. We also play segment register
105207753Smm * games with the destination address for the move so that the client
106207753Smm * can be larger than 16k (which would overflow the zero segment since
107207753Smm * the client starts at 0xc000).
108207753Smm */
109207753Smmmain.5: 	mov %dx,MEM_ARG			# Save args
110207753Smm		mov $NSECT,%cx			# Sector count
111207753Smm		movl $1024,%eax			# Offset to boot2
112207753Smm		mov $MEM_BTX,%ebx		# Destination buffer
113207753Smmmain.6:		pushal				# Save params
114207753Smm		call read			# Read disk
115207753Smm		popal				# Restore
116207753Smm		incl %eax			# Advance to
117207753Smm		add $SIZ_SEC,%ebx		#  next sector
118207753Smm		loop main.6			# If not last, read another
119207753Smm		mov MEM_BTX+0xa,%bx		# Get BTX length
120207753Smm		mov $NSECT*SIZ_SEC-1,%di	# Size of load area (less one)
121207753Smm		mov %di,%si			# End of load area, 0x9000 rel
122207753Smm		sub %bx,%di			# End of client, 0xc000 rel
123207753Smm		mov %di,%cx			# Size of
124207753Smm		inc %cx				#  client
125207753Smm		mov $(MEM_BTX)>>4,%dx		# Segment
126207753Smm		mov %dx,%ds			#   addressing 0x9000
127207753Smm		mov $(MEM_USR+2*SIZ_PAG)>>4,%dx	# Segment
128207753Smm		mov %dx,%es			#   addressing 0xc000
129207753Smm		std				# Move with decrement
130207753Smm		rep				# Relocate
131207753Smm		movsb				#  client
132207753Smm		cld				# Back to increment
133207753Smm		xor %dx,%dx			# Back
134207753Smm		mov %ds,%dx			#  to zero
135207753Smm		mov %dx,%es			#  segment
136207753Smm
137207753Smm/*
138207753Smm * Enable A20 so we can access memory above 1 meg.
139207753Smm * Use the zero-valued %cx as a timeout for embedded hardware which do not
140207753Smm * have a keyboard controller.
141207753Smm */
142207753Smmseta20: 	cli				# Disable interrupts
143207753Smmseta20.1:	dec %cx				# Timeout?
144207753Smm		jz seta20.3			# Yes
145213700Smm		inb $0x64,%al			# Get status
146213700Smm		testb $0x2,%al			# Busy?
147213700Smm		jnz seta20.1			# Yes
148213700Smm		movb $0xd1,%al			# Command: Write
149207753Smm		outb %al,$0x64			#  output port
150207753Smmseta20.2:	inb $0x64,%al			# Get status
151207753Smm		testb $0x2,%al			# Busy?
152207753Smm		jnz seta20.2			# Yes
153207753Smm		movb $0xdf,%al			# Enable
154207753Smm		outb %al,$0x60			#  A20
155207753Smmseta20.3:	sti				# Enable interrupts
156207753Smm
157207753Smm		jmp start+MEM_JMP-MEM_ORG	# Start BTX
158207753Smm
159207753Smm
160207753Smm/*
161207753Smm * Read a sector from the disk.  Sets up an EDD packet on the stack
162207753Smm * and passes it to read.  We assume that the destination address is
163207753Smm * always segment-aligned.
164207753Smm *
165207753Smm * %eax		- int     - LBA to read in relative to partition start
166207753Smm * %ebx		- ptr	  - destination address
167207753Smm * %dl		- byte    - drive to read from
168207753Smm * %si		- ptr     - MBR partition entry
169207753Smm */
170207753Smmread:		xor %ecx,%ecx			# Get
171207753Smm		addl 0x8(%si),%eax		#  LBA
172207753Smm		adc $0,%ecx
173207753Smm		pushl %ecx			# Starting absolute block
174207753Smm		pushl %eax			#  block number
175207753Smm		shr $4,%ebx			# Convert to segment
176207753Smm		push %bx			# Address of
177207753Smm		push $0				#  transfer buffer
178207753Smm		push $0x1			# Read 1 sector
179207753Smm		push $0x10			# Size of packet
180207753Smm		mov %sp,%si			# Packet pointer
181207753Smm		mov $0x42,%ah			# BIOS: Extended
182207753Smm		int $0x13			#  read
183207753Smm		jc read.1			# If error, fail
184207753Smm		lea 0x10(%si),%sp		# Clear stack
185207753Smm		ret				# If success, return
186207753Smmread.1:		mov %ah,%al			# Format
187207753Smm		mov $read_err,%di		#  error
188207753Smm		call hex8			#  code
189207753Smm		mov $msg_read,%si		# Set the error message and
190207753Smm						#  fall through to the error
191207753Smm						#  routine
192207753Smm/*
193207753Smm * Print out the error message pointed to by %ds:(%si) followed
194207753Smm * by a prompt, wait for a keypress, and then reboot the machine.
195207753Smm */
196207753Smmerror:		callw putstr			# Display message
197207753Smm		mov $prompt,%si			# Display
198207753Smm		callw putstr			#  prompt
199207753Smm		xorb %ah,%ah			# BIOS: Get
200207753Smm		int $0x16			#  keypress
201207753Smm		movw $0x1234, BDA_BOOT		# Do a warm boot
202207753Smm		ljmp $0xffff,$0x0		# reboot the machine
203207753Smm/*
204207753Smm * Display a null-terminated string using the BIOS output.
205207753Smm */
206207753Smmputstr.0:	mov $0x7,%bx	 		# Page:attribute
207207753Smm		movb $0xe,%ah			# BIOS: Display
208207753Smm		int $0x10			#  character
209207753Smmputstr: 	lodsb				# Get char
210207753Smm		testb %al,%al			# End of string?
211207753Smm		jne putstr.0			# No
212207753Smm		ret				# To caller
213207753Smm/*
214207753Smm * Check to see if the disk supports EDD.  zfsboot requires EDD and does not
215207753Smm * support older C/H/S disk I/O.
216207753Smm */
217207753Smmcheck_edd:	cmpb $0x80,%dl			# Hard drive?
218207753Smm		jb check_edd.1 			# No, fail to boot
219207753Smm		mov $0x55aa,%bx			# Magic
220207753Smm		push %dx			# Save
221207753Smm		movb $0x41,%ah			# BIOS: Check
222207753Smm		int $0x13			#  extensions present
223207753Smm		pop %dx				# Restore
224207753Smm		jc check_edd.1			# If error, fail
225207753Smm		cmp $0xaa55,%bx			# Magic?
226207753Smm		jne check_edd.1			# No, so fail
227207753Smm		testb $0x1,%cl			# Packet interface?
228207753Smm		jz check_edd.1			# No, so fail
229207753Smm		ret				# EDD ok, keep booting
230207753Smmcheck_edd.1:	mov $msg_chs,%si		# Warn that CHS is
231207753Smm		jmp error			#  unsupported and fail
232207753Smm/*
233207753Smm * AL to hex, saving the result to [EDI].
234207753Smm */
235207753Smmhex8:		push %ax			# Save
236207753Smm		shrb $0x4,%al			# Do upper
237207753Smm		call hex8.1			#  4
238207753Smm		pop %ax				# Restore
239207753Smmhex8.1: 	andb $0xf,%al			# Get lower 4
240207753Smm		cmpb $0xa,%al			# Convert
241207753Smm		sbbb $0x69,%al			#  to hex
242207753Smm		das				#  digit
243207753Smm		orb $0x20,%al			# To lower case
244207753Smm		stosb				# Save char
245207753Smm		ret				# (Recursive)
246207753Smm
247207753Smm/* Messages */
248207753Smm
249207753Smmmsg_chs:	.asciz "CHS not supported"
250207753Smmmsg_read:	.ascii "Read error: "
251207753Smmread_err:	.asciz "XX"
252207753Smmmsg_part:	.asciz "Boot error"
253207753Smm
254207753Smmprompt: 	.asciz "\r\n"
255207753Smm
256207753Smm		.org PRT_OFF,0x90
257207753Smm
258207753Smm/* Partition table */
259207753Smm
260207753Smm		.fill 0x30,0x1,0x0
261207753Smmpart4:		.byte 0x80, 0x00, 0x01, 0x00
262207753Smm		.byte 0xa5, 0xfe, 0xff, 0xff
263207753Smm		.byte 0x00, 0x00, 0x00, 0x00
264207753Smm		.byte 0x50, 0xc3, 0x00, 0x00	# 50000 sectors long, bleh
265207753Smm
266207753Smm		.word 0xaa55			# Magic number
267207753Smm