boot1.S revision 62138
1204020Simp#
2204020Simp# Copyright (c) 1998 Robert Nordier
3204020Simp# All rights reserved.
4204020Simp#
5204020Simp# Redistribution and use in source and binary forms are freely
6204020Simp# permitted provided that the above copyright notice and this
7204020Simp# paragraph and the following disclaimer are duplicated in all
8204020Simp# such forms.
9204020Simp#
10204020Simp# This software is provided "AS IS" and without any express or
11204020Simp# implied warranties, including, without limitation, the implied
12# warranties of merchantability and fitness for a particular
13# purpose.
14#
15
16# $FreeBSD: head/sys/boot/i386/boot2/boot1.S 62138 2000-06-26 22:57:16Z jhb $
17
18# Memory Locations
19		.set MEM_REL,0x700		# Relocation address
20		.set MEM_ARG,0x900		# Arguments
21		.set MEM_ORG,0x7c00		# Origin
22		.set MEM_BUF,0x8c00		# Load area
23		.set MEM_BTX,0x9000		# BTX start
24		.set MEM_JMP,0x9010		# BTX entry point
25		.set MEM_USR,0xa000		# Client start
26		.set BDA_BOOT,0x472		# Boot howto flag
27
28# Partition Constants
29		.set PRT_OFF,0x1be		# Partition offset
30		.set PRT_NUM,0x4		# Partitions
31		.set PRT_BSD,0xa5		# Partition type
32
33# Flag Bits
34		.set FL_PACKET,0x80		# Packet mode
35
36# Misc. Constants
37		.set SIZ_PAG,0x1000		# Page size
38		.set SIZ_SEC,0x200		# Sector size
39
40		.globl start
41		.globl xread
42		.code16
43
44start:		jmp main			# Start recognizably
45
46		.org 0x4,0x90
47#
48# Trampoline used by boot2 to call read to read data from the disk via
49# the BIOS.  Call with:
50#
51# %cx:%ax	- long    - LBA to read in
52# %es:(%bx)	- caddr_t - buffer to read data into
53# %dl		- byte    - drive to read from
54# %dh		- byte    - num sectors to read
55#
56
57xread:		push %ss			# Address
58		pop %ds				#  data
59#
60# Setup an EDD disk packet and pass it to read
61#
62xread.1:					# Starting
63		pushl $0x0			#  absolute
64		push %cx			#  block
65		push %ax			#  number
66		push %es			# Address of
67		push %bx			#  transfer buffer
68		xor %ax,%ax			# Number of
69		movb %dh,%al			#  blocks to
70		push %ax			#  transfer
71		push $0x10			# Size of packet
72		mov %sp,%bp			# Packet pointer
73		callw read			# Read from disk
74		lea 0x10(%bp),%sp		# Clear stack
75		lret				# To far caller
76#
77# Load the rest of boot2 and BTX up, copy the parts to the right locations,
78# and start it all up.
79#
80
81#
82# Setup the segment registers to flat addressing (segment 0) and setup the
83# stack to end just below the start of our code.
84#
85main:		cld				# String ops inc
86		xor %cx,%cx			# Zero
87		mov %cx,%es			# Address
88		mov %cx,%ds			#  data
89		mov %cx,%ss			# Set up
90		mov $start,%sp			#  stack
91#
92# Relocate ourself to MEM_REL.  Since %cx == 0, the inc %ch sets
93# %cx == 0x100.
94#
95		mov %sp,%si			# Source
96		mov $MEM_REL,%di		# Destination
97		incb %ch			# Word count
98		rep				# Copy
99		movsw				#  code
100#
101# If we are on a hard drive, then load the MBR and look for the first
102# FreeBSD slice.  We use the fake partition entry below that points to
103# the MBR when we call nread.  The first pass looks for the first active
104# FreeBSD slice.  The second pass looks for the first non-active FreeBSD
105# slice if the first one fails.
106#
107		mov $part4,%si			# Partition
108		cmpb $0x80,%dl			# Hard drive?
109		jb main.4			# No
110		movb $0x1,%dh			# Block count
111		callw nread			# Read MBR
112		mov $0x1,%cx	 		# Two passes
113main.1: 	mov $MEM_BUF+PRT_OFF,%si	# Partition table
114		movb $0x1,%dh			# Partition
115main.2: 	cmpb $PRT_BSD,0x4(%si)		# Our partition type?
116		jne main.3			# No
117		jcxz main.5			# If second pass
118		testb $0x80,(%si)		# Active?
119		jnz main.5			# Yes
120main.3: 	add $0x10,%si	 		# Next entry
121		incb %dh			# Partition
122		cmpb $0x1+PRT_NUM,%dh		# In table?
123		jb main.2			# Yes
124		dec %cx				# Do two
125		jcxz main.1			#  passes
126#
127# If we get here, we didn't find any FreeBSD slices at all, so print an
128# error message and die.
129#
130		mov $msg_part,%si		# Message
131		jmp error			# Error
132#
133# Floppies use partition 0 of drive 0.
134#
135main.4: 	xor %dx,%dx			# Partition:drive
136#
137# Ok, we have a slice and drive in %dx now, so use that to locate and load
138# boot2.  %si references the start of the slice we are looking for, so go
139# ahead and load up the first 16 sectors (boot1 + boot2) from that.  When
140# we read it in, we conveniently use 0x8c00 as our transfer buffer.  Thus,
141# boot1 ends up at 0x8c00, and boot2 starts at 0x8c00 + 0x200 = 0x8e00.
142# The first part of boot2 is boot2.ldr, which is 0x200 bytes of zeros.
143# The second part is BTX, which is thus loaded into 0x9000, which is where
144# it also runs from.  The boot2.bin binary starts right after the end of
145# BTX, so we have to figure out where the start of it is and then move the
146# binary to 0xb000.  Normally, BTX clients start at MEM_USR, or 0xa000, but
147# when we use btxld create boot2, we use an entry point of 0x1000.  That
148# entry point is relative, to MEM_USR, thus boot2.bin starts at 0xb000.
149#
150main.5: 	mov %dx,MEM_ARG			# Save args
151		movb $0x10,%dh			# Sector count
152		callw nread			# Read disk
153		mov $MEM_BTX,%bx		# BTX
154		mov 0xa(%bx),%si		# Get BTX length and set
155		add %bx,%si			#  %si to start of boot2.bin
156		mov $MEM_USR+SIZ_PAG,%di	# Client page 1
157		mov $MEM_BTX+0xe*SIZ_SEC,%cx	# Byte
158		sub %si,%cx			#  count
159		rep				# Relocate
160		movsb				#  client
161		sub %di,%cx			# Byte count
162		xorb %al,%al			# Zero assumed bss from
163		rep				#  the end of boot2.bin
164		stosb				#  up to 0x10000
165		callw seta20			# Enable A20
166		jmp start+MEM_JMP-MEM_ORG	# Start BTX
167#
168# Enable A20 so we can access memory above 1 meg.
169#
170seta20: 	cli				# Disable interrupts
171seta20.1:	inb $0x64,%al			# Get status
172		testb $0x2,%al			# Busy?
173		jnz seta20.1			# Yes
174		movb $0xd1,%al			# Command: Write
175		outb %al,$0x64			#  output port
176seta20.2:	inb $0x64,%al			# Get status
177		testb $0x2,%al			# Busy?
178		jnz seta20.2			# Yes
179		movb $0xdf,%al			# Enable
180		outb %al,$0x60			#  A20
181		sti				# Enable interrupts
182		retw				# To caller
183#
184# Trampoline used to call read from within boot1.
185#
186nread:		mov $MEM_BUF,%bx		# Transfer buffer
187		mov 0x8(%si),%ax		# Get
188		mov 0xa(%si),%cx		#  LBA
189		push %cs			# Read from
190		callw xread.1	 		#  disk
191		jnc return			# If success, return
192		mov $msg_read,%si		# Otherwise, set the error
193						#  message and fall through to
194						#  the error routine
195#
196# Print out the error message pointed to by %ds:(%si) followed
197# by a prompt, wait for a keypress, and then reboot the machine.
198#
199error:		callw putstr			# Display message
200		mov $prompt,%si			# Display
201		callw putstr			#  prompt
202		xorb %ah,%ah			# BIOS: Get
203		int $0x16			#  keypress
204		movw $0x1234, BDA_BOOT		# Do a warm boot
205		ljmp $0xffff,$0x0		# reboot the machine
206#
207# Display a null-terminated string using the BIOS output.
208#
209putstr.0:	mov $0x7,%bx	 		# Page:attribute
210		movb $0xe,%ah			# BIOS: Display
211		int $0x10			#  character
212putstr: 	lodsb				# Get char
213		testb %al,%al			# End of string?
214		jne putstr.0			# No
215
216#
217# Overused return code.  ereturn is used to return an error from the
218# read function.  Since we assume putstr succeeds, we (ab)use the
219# same code when we return from putstr.
220#
221ereturn:	movb $0x1,%ah			# Invalid
222		stc				#  argument
223return: 	retw				# To caller
224#
225# Reads sectors from the disk.  If EDD is enabled, then check if it is
226# installed and use it if it is.  If it is not installed or not enabled, then
227# fall back to using CHS.  Since we use a LBA, if we are using CHS, we have to
228# fetch the drive parameters from the BIOS and divide it out ourselves.
229# Call with:
230#
231# %dl	- byte     - drive number
232# stack - 10 bytes - EDD Packet
233#
234read:	 	push %dx			# Save
235		movb $0x8,%ah			# BIOS: Get drive
236		int $0x13			#  parameters
237		movb %dh,%ch			# Max head number
238		pop %dx				# Restore
239		jc return			# If error
240		andb $0x3f,%cl			# Sectors per track
241		jz ereturn			# If zero
242		cli				# Disable interrupts
243		mov 0x8(%bp),%eax		# Get LBA
244		push %dx			# Save
245		movzbl %cl,%ebx			# Divide by
246		xor %edx,%edx			#  sectors
247		div %ebx			#  per track
248		movb %ch,%bl			# Max head number
249		movb %dl,%ch			# Sector number
250		inc %bx				# Divide by
251		xorb %dl,%dl			#  number
252		div %ebx			#  of heads
253		movb %dl,%bh			# Head number
254		pop %dx				# Restore
255		cmpl $0x3ff,%eax		# Cylinder number supportable?
256		sti				# Enable interrupts
257		ja read.7			# No, try EDD
258		xchgb %al,%ah			# Set up cylinder
259		rorb $0x2,%al			#  number
260		orb %ch,%al			# Merge
261		inc %ax				#  sector
262		xchg %ax,%cx	 		#  number
263		movb %bh,%dh			# Head number
264		subb %ah,%al			# Sectors this track
265		mov 0x2(%bp),%ah		# Blocks to read
266		cmpb %ah,%al			# To read
267		jb read.2			#  this
268		movb %ah,%al			#  track
269read.2: 	mov $0x5,%di	 		# Try count
270read.3: 	les 0x4(%bp),%bx		# Transfer buffer
271		push %ax			# Save
272		movb $0x2,%ah			# BIOS: Read
273		int $0x13			#  from disk
274		pop %bx				# Restore
275		jnc read.4			# If success
276		dec %di				# Retry?
277		jz read.6			# No
278		xorb %ah,%ah			# BIOS: Reset
279		int $0x13			#  disk system
280		xchg %bx,%ax	 		# Block count
281		jmp read.3			# Continue
282read.4: 	movzbw %bl,%ax	 		# Sectors read
283		add %ax,0x8(%bp)		# Adjust
284		jnc read.5			#  LBA,
285		incw 0xa(%bp)	 		#  transfer
286read.5: 	shlb %bl			#  buffer
287		add %bl,0x5(%bp)		#  pointer,
288		sub %al,0x2(%bp)		#  block count
289		ja read				# If not done
290read.6: 	retw				# To caller
291read.7:		testb $FL_PACKET,%cs:MEM_REL+flags-start # LBA support enabled?
292		jz ereturn			# No, so return an error
293		mov $0x55aa,%bx			# Magic
294		push %dx			# Save
295		movb $0x41,%ah			# BIOS: Check
296		int $0x13			#  extensions present
297		pop %dx				# Restore
298		jc return			# If error, return an error
299		cmp $0xaa55,%bx			# Magic?
300		jne ereturn			# No, so return an error
301		testb $0x1,%cl			# Packet interface?
302		jz ereturn			# No, so return an error
303		mov %bp,%si			# Disk packet
304		movb $0x42,%ah			# BIOS: Extended
305		int $0x13			#  read
306		retw				# To caller
307
308# Messages
309
310msg_read:	.asciz "Read"
311msg_part:	.asciz "Boot"
312
313prompt: 	.asciz " error\r\n"
314
315flags:		.byte FLAGS			# Flags
316
317		.org PRT_OFF,0x90
318
319# Partition table
320
321		.fill 0x30,0x1,0x0
322part4:		.byte 0x80, 0x00, 0x01, 0x00
323		.byte 0xa5, 0xff, 0xff, 0xff
324		.byte 0x00, 0x00, 0x00, 0x00
325		.byte 0x50, 0xc3, 0x00, 0x00	# 50000 sectors long, bleh
326
327		.word 0xaa55			# Magic number
328