1185029Spjd/*
2185029Spjd * Copyright (c) 1998 Robert Nordier
3185029Spjd * All rights reserved.
4185029Spjd *
5185029Spjd * Redistribution and use in source and binary forms are freely
6185029Spjd * permitted provided that the above copyright notice and this
7185029Spjd * paragraph and the following disclaimer are duplicated in all
8185029Spjd * such forms.
9185029Spjd *
10185029Spjd * This software is provided "AS IS" and without any express or
11185029Spjd * implied warranties, including, without limitation, the implied
12185029Spjd * warranties of merchantability and fitness for a particular
13185029Spjd * purpose.
14185029Spjd *
15185029Spjd * $FreeBSD: stable/11/stand/i386/zfsboot/zfsldr.S 329192 2018-02-13 04:28:13Z kevans $
16185029Spjd */
17185029Spjd
18185029Spjd/* Memory Locations */
19185029Spjd		.set MEM_ARG,0x900		# Arguments
20185029Spjd		.set MEM_ORG,0x7c00		# Origin
21185029Spjd		.set MEM_BUF,0x8000		# Load area
22185029Spjd		.set MEM_BTX,0x9000		# BTX start
23185029Spjd		.set MEM_JMP,0x9010		# BTX entry point
24185029Spjd		.set MEM_USR,0xa000		# Client start
25185029Spjd		.set BDA_BOOT,0x472		# Boot howto flag
26185029Spjd
27185029Spjd/* Partition Constants */
28185029Spjd		.set PRT_OFF,0x1be		# Partition offset
29185029Spjd		.set PRT_NUM,0x4		# Partitions
30185029Spjd		.set PRT_BSD,0xa5		# Partition type
31185029Spjd
32185029Spjd/* Misc. Constants */
33185029Spjd		.set SIZ_PAG,0x1000		# Page size
34185029Spjd		.set SIZ_SEC,0x200		# Sector size
35328866Skevans		.set COPY_BLKS,0x8		# Number of blocks
36329192Skevans						# to copy for boot2 (<= 15)
37328866Skevans		.set COPY_BLK_SZ,0x8000		# Copy in 32k blocks; must be
38328866Skevans						# a multiple of 16 bytes
39328866Skevans		.set NSECT,(COPY_BLK_SZ / SIZ_SEC * COPY_BLKS)
40223611Sjhb		.globl start
41185029Spjd		.code16
42185029Spjd
43185029Spjd/*
44221177Sjhb * Load the rest of zfsboot2 and BTX up, copy the parts to the right locations,
45185029Spjd * and start it all up.
46185029Spjd */
47185029Spjd
48185029Spjd/*
49185029Spjd * Setup the segment registers to flat addressing (segment 0) and setup the
50185029Spjd * stack to end just below the start of our code.
51185029Spjd */
52223611Sjhbstart:		cld				# String ops inc
53185029Spjd		xor %cx,%cx			# Zero
54185029Spjd		mov %cx,%es			# Address
55185029Spjd		mov %cx,%ds			#  data
56185029Spjd		mov %cx,%ss			# Set up
57185029Spjd		mov $start,%sp			#  stack
58185029Spjd/*
59223597Sjhb * Load the MBR and look for the first FreeBSD slice.  We use the fake
60223597Sjhb * partition entry below that points to the MBR when we call read.
61223597Sjhb * The first pass looks for the first active FreeBSD slice.  The
62223597Sjhb * second pass looks for the first non-active FreeBSD slice if the
63223597Sjhb * first one fails.
64185029Spjd */
65223597Sjhb		call check_edd		 	# Make sure EDD works
66223477Sjhb		mov $part4,%si			# Dummy partition
67223477Sjhb		xor %eax,%eax			# Read MBR
68223477Sjhb		movl $MEM_BUF,%ebx		#  from first
69223597Sjhb		call read			#  sector
70185029Spjd		mov $0x1,%cx	 		# Two passes
71185029Spjdmain.1: 	mov $MEM_BUF+PRT_OFF,%si	# Partition table
72185029Spjd		movb $0x1,%dh			# Partition
73185029Spjdmain.2: 	cmpb $PRT_BSD,0x4(%si)		# Our partition type?
74185029Spjd		jne main.3			# No
75185029Spjd		jcxz main.5			# If second pass
76185029Spjd		testb $0x80,(%si)		# Active?
77185029Spjd		jnz main.5			# Yes
78185029Spjdmain.3: 	add $0x10,%si	 		# Next entry
79185029Spjd		incb %dh			# Partition
80185029Spjd		cmpb $0x1+PRT_NUM,%dh		# In table?
81185029Spjd		jb main.2			# Yes
82185029Spjd		dec %cx				# Do two
83185029Spjd		jcxz main.1			#  passes
84185029Spjd/*
85185029Spjd * If we get here, we didn't find any FreeBSD slices at all, so print an
86185029Spjd * error message and die.
87185029Spjd */
88185029Spjd		mov $msg_part,%si		# Message
89185029Spjd		jmp error			# Error
90185029Spjd
91185029Spjd/*
92185029Spjd * Ok, we have a slice and drive in %dx now, so use that to locate and
93185029Spjd * load boot2.  %si references the start of the slice we are looking
94328866Skevans * for, so go ahead and load up the COPY_BLKS*COPY_BLK_SZ/SIZ_SEC sectors
95328866Skevans * starting at sector 1024 (i.e. after the two vdev labels).  We don't
96328866Skevans * have do anything fancy here to allow for an extra copy of boot1 and
97328866Skevans * a partition table (compare to this section of the UFS bootstrap) so we
98328866Skevans * just load it all at 0x9000. The first part of boot2 is BTX, which wants
99328866Skevans * to run at 0x9000. The boot2.bin binary starts right after the end of BTX,
100185029Spjd * so we have to figure out where the start of it is and then move the
101223477Sjhb * binary to 0xc000.  Normally, BTX clients start at MEM_USR, or 0xa000,
102223477Sjhb * but when we use btxld to create zfsboot2, we use an entry point of
103223477Sjhb * 0x2000.  That entry point is relative to MEM_USR; thus boot2.bin
104223477Sjhb * starts at 0xc000.
105185029Spjd *
106185029Spjd * The load area and the target area for the client overlap so we have
107185029Spjd * to use a decrementing string move. We also play segment register
108185029Spjd * games with the destination address for the move so that the client
109185029Spjd * can be larger than 16k (which would overflow the zero segment since
110223477Sjhb * the client starts at 0xc000).
111185029Spjd */
112185029Spjdmain.5: 	mov %dx,MEM_ARG			# Save args
113223477Sjhb		mov $NSECT,%cx			# Sector count
114199579Sjhb		movl $1024,%eax			# Offset to boot2
115223477Sjhb		mov $MEM_BTX,%ebx		# Destination buffer
116223477Sjhbmain.6:		pushal				# Save params
117223597Sjhb		call read			# Read disk
118223477Sjhb		popal				# Restore
119223477Sjhb		incl %eax			# Advance to
120223477Sjhb		add $SIZ_SEC,%ebx		#  next sector
121223477Sjhb		loop main.6			# If not last, read another
122328866Skevans
123328866Skevans		mov $MEM_BTX,%bx		# BTX
124328866Skevans		mov 0xa(%bx),%si		# Get BTX length and set
125328866Skevans		add %bx,%si			#  %si to start of boot2
126328866Skevans		dec %si				# Set %ds:%si to point at the
127328866Skevans		mov %si,%ax			# last byte we want to copy
128328866Skevans		shr $4,%ax			# from boot2, with %si made as
129328866Skevans		add $(COPY_BLKS*COPY_BLK_SZ/16),%ax	# small as possible.
130328866Skevans		and $0xf,%si			#
131328866Skevans		mov %ax,%ds			#
132328866Skevans		mov $(MEM_USR+2*SIZ_PAG)/16,%ax # Set %es:(-1) to point at
133328866Skevans		add $(COPY_BLKS*COPY_BLK_SZ/16),%ax	# the last byte we
134328866Skevans		mov %ax,%es			# want to copy boot2 into.
135328866Skevans		mov $COPY_BLKS,%bx		# Copy COPY_BLKS 32k blocks
136328866Skevanscopyloop:
137328866Skevans		add $COPY_BLK_SZ,%si		# Adjust %ds:%si to point at
138328866Skevans		mov %ds,%ax			# the end of the next 32k to
139328866Skevans		sub $COPY_BLK_SZ/16,%ax		# copy from boot2
140328866Skevans		mov %ax,%ds
141328866Skevans		mov $COPY_BLK_SZ-1,%di		# Adjust %es:%di to point at
142328866Skevans		mov %es,%ax			# the end of the next 32k into
143328866Skevans		sub $COPY_BLK_SZ/16,%ax		# which we want boot2 copied
144328866Skevans		mov %ax,%es
145328866Skevans		mov $COPY_BLK_SZ,%cx		# Copy 32k
146328866Skevans		std
147328866Skevans		rep movsb
148328866Skevans		dec %bx
149328866Skevans		jnz copyloop
150328866Skevans		mov %cx,%ds			# Reset %ds and %es
151328866Skevans		mov %cx,%es
152223477Sjhb		cld				# Back to increment
153185029Spjd
154185029Spjd/*
155185029Spjd * Enable A20 so we can access memory above 1 meg.
156185029Spjd * Use the zero-valued %cx as a timeout for embedded hardware which do not
157185029Spjd * have a keyboard controller.
158185029Spjd */
159185029Spjdseta20: 	cli				# Disable interrupts
160185029Spjdseta20.1:	dec %cx				# Timeout?
161185029Spjd		jz seta20.3			# Yes
162185029Spjd		inb $0x64,%al			# Get status
163185029Spjd		testb $0x2,%al			# Busy?
164185029Spjd		jnz seta20.1			# Yes
165185029Spjd		movb $0xd1,%al			# Command: Write
166185029Spjd		outb %al,$0x64			#  output port
167185029Spjdseta20.2:	inb $0x64,%al			# Get status
168185029Spjd		testb $0x2,%al			# Busy?
169185029Spjd		jnz seta20.2			# Yes
170185029Spjd		movb $0xdf,%al			# Enable
171185029Spjd		outb %al,$0x60			#  A20
172185029Spjdseta20.3:	sti				# Enable interrupts
173185029Spjd
174185029Spjd		jmp start+MEM_JMP-MEM_ORG	# Start BTX
175185029Spjd
176185029Spjd
177185029Spjd/*
178223597Sjhb * Read a sector from the disk.  Sets up an EDD packet on the stack
179223597Sjhb * and passes it to read.  We assume that the destination address is
180223597Sjhb * always segment-aligned.
181221177Sjhb *
182221177Sjhb * %eax		- int     - LBA to read in relative to partition start
183223477Sjhb * %ebx		- ptr	  - destination address
184221177Sjhb * %dl		- byte    - drive to read from
185221177Sjhb * %si		- ptr     - MBR partition entry
186185029Spjd */
187223597Sjhbread:		xor %ecx,%ecx			# Get
188199579Sjhb		addl 0x8(%si),%eax		#  LBA
189199579Sjhb		adc $0,%ecx
190221177Sjhb		pushl %ecx			# Starting absolute block
191221177Sjhb		pushl %eax			#  block number
192223477Sjhb		shr $4,%ebx			# Convert to segment
193223477Sjhb		push %bx			# Address of
194223477Sjhb		push $0				#  transfer buffer
195223477Sjhb		push $0x1			# Read 1 sector
196221177Sjhb		push $0x10			# Size of packet
197223597Sjhb		mov %sp,%si			# Packet pointer
198223597Sjhb		mov $0x42,%ah			# BIOS: Extended
199223597Sjhb		int $0x13			#  read
200223597Sjhb		jc read.1			# If error, fail
201223597Sjhb		lea 0x10(%si),%sp		# Clear stack
202223477Sjhb		ret				# If success, return
203223597Sjhbread.1:		mov %ah,%al			# Format
204223477Sjhb		mov $read_err,%di		#  error
205223477Sjhb		call hex8			#  code
206223477Sjhb		mov $msg_read,%si		# Set the error message and
207223477Sjhb						#  fall through to the error
208223477Sjhb						#  routine
209185029Spjd/*
210185029Spjd * Print out the error message pointed to by %ds:(%si) followed
211185029Spjd * by a prompt, wait for a keypress, and then reboot the machine.
212185029Spjd */
213185029Spjderror:		callw putstr			# Display message
214185029Spjd		mov $prompt,%si			# Display
215185029Spjd		callw putstr			#  prompt
216185029Spjd		xorb %ah,%ah			# BIOS: Get
217185029Spjd		int $0x16			#  keypress
218185029Spjd		movw $0x1234, BDA_BOOT		# Do a warm boot
219185029Spjd		ljmp $0xffff,$0x0		# reboot the machine
220185029Spjd/*
221185029Spjd * Display a null-terminated string using the BIOS output.
222185029Spjd */
223185029Spjdputstr.0:	mov $0x7,%bx	 		# Page:attribute
224185029Spjd		movb $0xe,%ah			# BIOS: Display
225185029Spjd		int $0x10			#  character
226185029Spjdputstr: 	lodsb				# Get char
227185029Spjd		testb %al,%al			# End of string?
228185029Spjd		jne putstr.0			# No
229223597Sjhb		ret				# To caller
230185029Spjd/*
231223597Sjhb * Check to see if the disk supports EDD.  zfsboot requires EDD and does not
232223597Sjhb * support older C/H/S disk I/O.
233185029Spjd */
234223597Sjhbcheck_edd:	cmpb $0x80,%dl			# Hard drive?
235223597Sjhb		jb check_edd.1 			# No, fail to boot
236185029Spjd		mov $0x55aa,%bx			# Magic
237185029Spjd		push %dx			# Save
238185029Spjd		movb $0x41,%ah			# BIOS: Check
239185029Spjd		int $0x13			#  extensions present
240185029Spjd		pop %dx				# Restore
241223597Sjhb		jc check_edd.1			# If error, fail
242185029Spjd		cmp $0xaa55,%bx			# Magic?
243223597Sjhb		jne check_edd.1			# No, so fail
244185029Spjd		testb $0x1,%cl			# Packet interface?
245223597Sjhb		jz check_edd.1			# No, so fail
246223597Sjhb		ret				# EDD ok, keep booting
247223597Sjhbcheck_edd.1:	mov $msg_chs,%si		# Warn that CHS is
248223597Sjhb		jmp error			#  unsupported and fail
249223477Sjhb/*
250223477Sjhb * AL to hex, saving the result to [EDI].
251223477Sjhb */
252223477Sjhbhex8:		push %ax			# Save
253223477Sjhb		shrb $0x4,%al			# Do upper
254223477Sjhb		call hex8.1			#  4
255223477Sjhb		pop %ax				# Restore
256223477Sjhbhex8.1: 	andb $0xf,%al			# Get lower 4
257223477Sjhb		cmpb $0xa,%al			# Convert
258223477Sjhb		sbbb $0x69,%al			#  to hex
259223477Sjhb		das				#  digit
260223477Sjhb		orb $0x20,%al			# To lower case
261223477Sjhb		stosb				# Save char
262223477Sjhb		ret				# (Recursive)
263223477Sjhb
264185029Spjd/* Messages */
265185029Spjd
266223477Sjhbmsg_chs:	.asciz "CHS not supported"
267223477Sjhbmsg_read:	.ascii "Read error: "
268223477Sjhbread_err:	.asciz "XX"
269223477Sjhbmsg_part:	.asciz "Boot error"
270185029Spjd
271223477Sjhbprompt: 	.asciz "\r\n"
272185029Spjd
273185029Spjd		.org PRT_OFF,0x90
274185029Spjd
275185029Spjd/* Partition table */
276185029Spjd
277185029Spjd		.fill 0x30,0x1,0x0
278185029Spjdpart4:		.byte 0x80, 0x00, 0x01, 0x00
279185029Spjd		.byte 0xa5, 0xfe, 0xff, 0xff
280185029Spjd		.byte 0x00, 0x00, 0x00, 0x00
281185029Spjd		.byte 0x50, 0xc3, 0x00, 0x00	# 50000 sectors long, bleh
282185029Spjd
283185029Spjd		.word 0xaa55			# Magic number
284