boot1.S revision 85805
140269Srnordier#
240269Srnordier# Copyright (c) 1998 Robert Nordier
340269Srnordier# All rights reserved.
440269Srnordier#
540269Srnordier# Redistribution and use in source and binary forms are freely
640269Srnordier# permitted provided that the above copyright notice and this
740269Srnordier# paragraph and the following disclaimer are duplicated in all
840269Srnordier# such forms.
940269Srnordier#
1040269Srnordier# This software is provided "AS IS" and without any express or
1140269Srnordier# implied warranties, including, without limitation, the implied
1240269Srnordier# warranties of merchantability and fitness for a particular
1340269Srnordier# purpose.
1440269Srnordier#
1540269Srnordier
1650477Speter# $FreeBSD: head/sys/boot/i386/boot2/boot1.S 85805 2001-11-01 06:19:32Z peter $
1740269Srnordier
1861836Sjhb# Memory Locations
1942478Speter		.set MEM_REL,0x700		# Relocation address
2042478Speter		.set MEM_ARG,0x900		# Arguments
2140269Srnordier		.set MEM_ORG,0x7c00		# Origin
2240269Srnordier		.set MEM_BUF,0x8c00		# Load area
2340269Srnordier		.set MEM_BTX,0x9000		# BTX start
2440269Srnordier		.set MEM_JMP,0x9010		# BTX entry point
2540269Srnordier		.set MEM_USR,0xa000		# Client start
2662138Sjhb		.set BDA_BOOT,0x472		# Boot howto flag
2761836Sjhb
2861836Sjhb# Partition Constants
2940269Srnordier		.set PRT_OFF,0x1be		# Partition offset
3040269Srnordier		.set PRT_NUM,0x4		# Partitions
3140269Srnordier		.set PRT_BSD,0xa5		# Partition type
3240269Srnordier
3361836Sjhb# Flag Bits
3461836Sjhb		.set FL_PACKET,0x80		# Packet mode
3561836Sjhb
3661836Sjhb# Misc. Constants
3740269Srnordier		.set SIZ_PAG,0x1000		# Page size
3840269Srnordier		.set SIZ_SEC,0x200		# Sector size
3940269Srnordier
4040269Srnordier		.globl start
4140269Srnordier		.globl xread
4260821Sjhb		.code16
4340269Srnordier
4440269Srnordierstart:		jmp main			# Start recognizably
4540269Srnordier
4680751Sjhb# This is the start of a standard BIOS Parameter Block (BPB). Most bootable
4780751Sjhb# FAT disks have this at the start of their MBR. While normal BIOS's will
4880751Sjhb# work fine without this section, IBM's El Torito emulation "fixes" up the
4980751Sjhb# BPB by writing into the memory copy of the MBR. Rather than have data
5080751Sjhb# written into our xread routine, we'll define a BPB to work around it.
5180751Sjhb# The data marked with (T) indicates a field required for a ThinkPad to
5280751Sjhb# recognize the disk and (W) indicates fields written from IBM BIOS code.
5380751Sjhb# The use of the BPB is based on what OpenBSD and NetBSD implemented in
5480751Sjhb# their boot code but the required fields were determined by trial and error.
5580751Sjhb#
5680751Sjhb# Note: If additional space is needed in boot1, one solution would be to
5780751Sjhb# move the "prompt" message data (below) to replace the OEM ID.
5880751Sjhb
5980751Sjhb		.org 0x03, 0x00
6080751Sjhboemid:		.space 0x08, 0x00	# OEM ID
6180751Sjhb
6280751Sjhb		.org 0x0b, 0x00
6380751Sjhbbpb:		.word   512		# sector size (T)
6480751Sjhb		.byte	0		# sectors/clustor
6580751Sjhb		.word	0		# reserved sectors
6680751Sjhb		.byte	0		# number of FATs
6780751Sjhb		.word	0		# root entries
6880751Sjhb		.word	0		# small sectors
6980751Sjhb		.byte	0		# media type (W)
7080751Sjhb		.word	0		# sectors/fat
7180751Sjhb		.word	18		# sectors per track (T)
7280751Sjhb		.word	2		# number of heads (T)
7380751Sjhb		.long	0		# hidden sectors (W)
7480751Sjhb		.long	0		# large sectors
7580751Sjhb
7680751Sjhb		.org 0x24, 0x00
7780751Sjhbebpb:		.byte	0		# BIOS physical drive number (W)
7880751Sjhb
7980751Sjhb		.org 0x25,0x90
8061836Sjhb#
8161836Sjhb# Trampoline used by boot2 to call read to read data from the disk via
8261836Sjhb# the BIOS.  Call with:
8361836Sjhb#
8461836Sjhb# %cx:%ax	- long    - LBA to read in
8561836Sjhb# %es:(%bx)	- caddr_t - buffer to read data into
8661836Sjhb# %dl		- byte    - drive to read from
8761836Sjhb# %dh		- byte    - num sectors to read
8861836Sjhb#
8940269Srnordier
9060821Sjhbxread:		push %ss			# Address
9160821Sjhb		pop %ds				#  data
9261836Sjhb#
9361836Sjhb# Setup an EDD disk packet and pass it to read
9461836Sjhb#
9560821Sjhbxread.1:					# Starting
9660821Sjhb		pushl $0x0			#  absolute
9760821Sjhb		push %cx			#  block
9860821Sjhb		push %ax			#  number
9960821Sjhb		push %es			# Address of
10060821Sjhb		push %bx			#  transfer buffer
10160821Sjhb		xor %ax,%ax			# Number of
10248919Srnordier		movb %dh,%al			#  blocks to
10360821Sjhb		push %ax			#  transfer
10460821Sjhb		push $0x10			# Size of packet
10560821Sjhb		mov %sp,%bp			# Packet pointer
10660821Sjhb		callw read			# Read from disk
10760821Sjhb		lea 0x10(%bp),%sp		# Clear stack
10841008Srnordier		lret				# To far caller
10961836Sjhb#
11061836Sjhb# Load the rest of boot2 and BTX up, copy the parts to the right locations,
11161836Sjhb# and start it all up.
11261836Sjhb#
11340269Srnordier
11461836Sjhb#
11561836Sjhb# Setup the segment registers to flat addressing (segment 0) and setup the
11661836Sjhb# stack to end just below the start of our code.
11761836Sjhb#
11840269Srnordiermain:		cld				# String ops inc
11960821Sjhb		xor %cx,%cx			# Zero
12060821Sjhb		mov %cx,%es			# Address
12160821Sjhb		mov %cx,%ds			#  data
12260821Sjhb		mov %cx,%ss			# Set up
12360821Sjhb		mov $start,%sp			#  stack
12461836Sjhb#
12561836Sjhb# Relocate ourself to MEM_REL.  Since %cx == 0, the inc %ch sets
12661836Sjhb# %cx == 0x100.
12761836Sjhb#
12860821Sjhb		mov %sp,%si			# Source
12960821Sjhb		mov $MEM_REL,%di		# Destination
13041008Srnordier		incb %ch			# Word count
13140269Srnordier		rep				# Copy
13260821Sjhb		movsw				#  code
13361836Sjhb#
13461836Sjhb# If we are on a hard drive, then load the MBR and look for the first
13561836Sjhb# FreeBSD slice.  We use the fake partition entry below that points to
13661836Sjhb# the MBR when we call nread.  The first pass looks for the first active
13761836Sjhb# FreeBSD slice.  The second pass looks for the first non-active FreeBSD
13861836Sjhb# slice if the first one fails.
13961836Sjhb#
14060821Sjhb		mov $part4,%si			# Partition
14140269Srnordier		cmpb $0x80,%dl			# Hard drive?
14240269Srnordier		jb main.4			# No
14341008Srnordier		movb $0x1,%dh			# Block count
14460821Sjhb		callw nread			# Read MBR
14560821Sjhb		mov $0x1,%cx	 		# Two passes
14660821Sjhbmain.1: 	mov $MEM_BUF+PRT_OFF,%si	# Partition table
14740269Srnordier		movb $0x1,%dh			# Partition
14860821Sjhbmain.2: 	cmpb $PRT_BSD,0x4(%si)		# Our partition type?
14940269Srnordier		jne main.3			# No
15060821Sjhb		jcxz main.5			# If second pass
15160821Sjhb		testb $0x80,(%si)		# Active?
15240269Srnordier		jnz main.5			# Yes
15360821Sjhbmain.3: 	add $0x10,%si	 		# Next entry
15440269Srnordier		incb %dh			# Partition
15541008Srnordier		cmpb $0x1+PRT_NUM,%dh		# In table?
15641008Srnordier		jb main.2			# Yes
15760821Sjhb		dec %cx				# Do two
15860821Sjhb		jcxz main.1			#  passes
15961836Sjhb#
16061836Sjhb# If we get here, we didn't find any FreeBSD slices at all, so print an
16161836Sjhb# error message and die.
16261836Sjhb#
16360821Sjhb		mov $msg_part,%si		# Message
16440269Srnordier		jmp error			# Error
16561836Sjhb#
16661836Sjhb# Floppies use partition 0 of drive 0.
16761836Sjhb#
16860821Sjhbmain.4: 	xor %dx,%dx			# Partition:drive
16961836Sjhb#
17061836Sjhb# Ok, we have a slice and drive in %dx now, so use that to locate and load
17161836Sjhb# boot2.  %si references the start of the slice we are looking for, so go
17261836Sjhb# ahead and load up the first 16 sectors (boot1 + boot2) from that.  When
17361836Sjhb# we read it in, we conveniently use 0x8c00 as our transfer buffer.  Thus,
17461836Sjhb# boot1 ends up at 0x8c00, and boot2 starts at 0x8c00 + 0x200 = 0x8e00.
17562660Sjhb# The first part of boot2 is the disklabel, which is 0x200 bytes long.
17661836Sjhb# The second part is BTX, which is thus loaded into 0x9000, which is where
17761836Sjhb# it also runs from.  The boot2.bin binary starts right after the end of
17861836Sjhb# BTX, so we have to figure out where the start of it is and then move the
17961836Sjhb# binary to 0xb000.  Normally, BTX clients start at MEM_USR, or 0xa000, but
18061836Sjhb# when we use btxld create boot2, we use an entry point of 0x1000.  That
18162658Sjhb# entry point is relative to MEM_USR; thus boot2.bin starts at 0xb000.
18261836Sjhb#
18360821Sjhbmain.5: 	mov %dx,MEM_ARG			# Save args
18441008Srnordier		movb $0x10,%dh			# Sector count
18560821Sjhb		callw nread			# Read disk
18660821Sjhb		mov $MEM_BTX,%bx		# BTX
18761836Sjhb		mov 0xa(%bx),%si		# Get BTX length and set
18861836Sjhb		add %bx,%si			#  %si to start of boot2.bin
18960821Sjhb		mov $MEM_USR+SIZ_PAG,%di	# Client page 1
19060821Sjhb		mov $MEM_BTX+0xe*SIZ_SEC,%cx	# Byte
19160821Sjhb		sub %si,%cx			#  count
19240269Srnordier		rep				# Relocate
19340269Srnordier		movsb				#  client
19460821Sjhb		sub %di,%cx			# Byte count
19561836Sjhb		xorb %al,%al			# Zero assumed bss from
19661836Sjhb		rep				#  the end of boot2.bin
19761836Sjhb		stosb				#  up to 0x10000
19860821Sjhb		callw seta20			# Enable A20
19960821Sjhb		jmp start+MEM_JMP-MEM_ORG	# Start BTX
20061836Sjhb#
20161836Sjhb# Enable A20 so we can access memory above 1 meg.
20261836Sjhb#
20340675Srnordierseta20: 	cli				# Disable interrupts
20440675Srnordierseta20.1:	inb $0x64,%al			# Get status
20540269Srnordier		testb $0x2,%al			# Busy?
20640675Srnordier		jnz seta20.1			# Yes
20740269Srnordier		movb $0xd1,%al			# Command: Write
20840269Srnordier		outb %al,$0x64			#  output port
20940675Srnordierseta20.2:	inb $0x64,%al			# Get status
21040269Srnordier		testb $0x2,%al			# Busy?
21140675Srnordier		jnz seta20.2			# Yes
21240269Srnordier		movb $0xdf,%al			# Enable
21340269Srnordier		outb %al,$0x60			#  A20
21440675Srnordier		sti				# Enable interrupts
21560821Sjhb		retw				# To caller
21661836Sjhb#
21761836Sjhb# Trampoline used to call read from within boot1.
21861836Sjhb#
21960821Sjhbnread:		mov $MEM_BUF,%bx		# Transfer buffer
22060821Sjhb		mov 0x8(%si),%ax		# Get
22160821Sjhb		mov 0xa(%si),%cx		#  LBA
22260821Sjhb		push %cs			# Read from
22360821Sjhb		callw xread.1	 		#  disk
22461836Sjhb		jnc return			# If success, return
22561836Sjhb		mov $msg_read,%si		# Otherwise, set the error
22661836Sjhb						#  message and fall through to
22761836Sjhb						#  the error routine
22861836Sjhb#
22961836Sjhb# Print out the error message pointed to by %ds:(%si) followed
23061836Sjhb# by a prompt, wait for a keypress, and then reboot the machine.
23161836Sjhb#
23260821Sjhberror:		callw putstr			# Display message
23360821Sjhb		mov $prompt,%si			# Display
23460821Sjhb		callw putstr			#  prompt
23540269Srnordier		xorb %ah,%ah			# BIOS: Get
23640269Srnordier		int $0x16			#  keypress
23762138Sjhb		movw $0x1234, BDA_BOOT		# Do a warm boot
23862138Sjhb		ljmp $0xffff,$0x0		# reboot the machine
23961836Sjhb#
24061836Sjhb# Display a null-terminated string using the BIOS output.
24161836Sjhb#
24260821Sjhbputstr.0:	mov $0x7,%bx	 		# Page:attribute
24340269Srnordier		movb $0xe,%ah			# BIOS: Display
24440269Srnordier		int $0x10			#  character
24540269Srnordierputstr: 	lodsb				# Get char
24640269Srnordier		testb %al,%al			# End of string?
24740269Srnordier		jne putstr.0			# No
24840269Srnordier
24961836Sjhb#
25061836Sjhb# Overused return code.  ereturn is used to return an error from the
25161836Sjhb# read function.  Since we assume putstr succeeds, we (ab)use the
25261836Sjhb# same code when we return from putstr.
25361836Sjhb#
25441008Srnordierereturn:	movb $0x1,%ah			# Invalid
25541008Srnordier		stc				#  argument
25660821Sjhbreturn: 	retw				# To caller
25761836Sjhb#
25861836Sjhb# Reads sectors from the disk.  If EDD is enabled, then check if it is
25961836Sjhb# installed and use it if it is.  If it is not installed or not enabled, then
26061836Sjhb# fall back to using CHS.  Since we use a LBA, if we are using CHS, we have to
26161836Sjhb# fetch the drive parameters from the BIOS and divide it out ourselves.
26261836Sjhb# Call with:
26361836Sjhb#
26461836Sjhb# %dl	- byte     - drive number
26561836Sjhb# stack - 10 bytes - EDD Packet
26662138Sjhb#
26762138Sjhbread:	 	push %dx			# Save
26840269Srnordier		movb $0x8,%ah			# BIOS: Get drive
26940269Srnordier		int $0x13			#  parameters
27040269Srnordier		movb %dh,%ch			# Max head number
27160821Sjhb		pop %dx				# Restore
27241008Srnordier		jc return			# If error
27340269Srnordier		andb $0x3f,%cl			# Sectors per track
27441008Srnordier		jz ereturn			# If zero
27541008Srnordier		cli				# Disable interrupts
27660821Sjhb		mov 0x8(%bp),%eax		# Get LBA
27760821Sjhb		push %dx			# Save
27860821Sjhb		movzbl %cl,%ebx			# Divide by
27960821Sjhb		xor %edx,%edx			#  sectors
28060821Sjhb		div %ebx			#  per track
28140269Srnordier		movb %ch,%bl			# Max head number
28240269Srnordier		movb %dl,%ch			# Sector number
28360821Sjhb		inc %bx				# Divide by
28441008Srnordier		xorb %dl,%dl			#  number
28560821Sjhb		div %ebx			#  of heads
28640269Srnordier		movb %dl,%bh			# Head number
28760821Sjhb		pop %dx				# Restore
28860821Sjhb		cmpl $0x3ff,%eax		# Cylinder number supportable?
28941008Srnordier		sti				# Enable interrupts
29062138Sjhb		ja read.7			# No, try EDD
29140269Srnordier		xchgb %al,%ah			# Set up cylinder
29240269Srnordier		rorb $0x2,%al			#  number
29340269Srnordier		orb %ch,%al			# Merge
29460821Sjhb		inc %ax				#  sector
29560821Sjhb		xchg %ax,%cx	 		#  number
29640269Srnordier		movb %bh,%dh			# Head number
29740269Srnordier		subb %ah,%al			# Sectors this track
29860821Sjhb		mov 0x2(%bp),%ah		# Blocks to read
29940269Srnordier		cmpb %ah,%al			# To read
30048919Srnordier		jb read.2			#  this
30140269Srnordier		movb %ah,%al			#  track
30260821Sjhbread.2: 	mov $0x5,%di	 		# Try count
30360821Sjhbread.3: 	les 0x4(%bp),%bx		# Transfer buffer
30460821Sjhb		push %ax			# Save
30541008Srnordier		movb $0x2,%ah			# BIOS: Read
30641008Srnordier		int $0x13			#  from disk
30760821Sjhb		pop %bx				# Restore
30848919Srnordier		jnc read.4			# If success
30960821Sjhb		dec %di				# Retry?
31048919Srnordier		jz read.6			# No
31140269Srnordier		xorb %ah,%ah			# BIOS: Reset
31240269Srnordier		int $0x13			#  disk system
31360821Sjhb		xchg %bx,%ax	 		# Block count
31448919Srnordier		jmp read.3			# Continue
31560821Sjhbread.4: 	movzbw %bl,%ax	 		# Sectors read
31660821Sjhb		add %ax,0x8(%bp)		# Adjust
31748919Srnordier		jnc read.5			#  LBA,
31860821Sjhb		incw 0xa(%bp)	 		#  transfer
31948919Srnordierread.5: 	shlb %bl			#  buffer
32060821Sjhb		add %bl,0x5(%bp)		#  pointer,
32160821Sjhb		sub %al,0x2(%bp)		#  block count
32262138Sjhb		ja read				# If not done
32360821Sjhbread.6: 	retw				# To caller
32462138Sjhbread.7:		testb $FL_PACKET,%cs:MEM_REL+flags-start # LBA support enabled?
32562138Sjhb		jz ereturn			# No, so return an error
32662138Sjhb		mov $0x55aa,%bx			# Magic
32762138Sjhb		push %dx			# Save
32862138Sjhb		movb $0x41,%ah			# BIOS: Check
32962138Sjhb		int $0x13			#  extensions present
33062138Sjhb		pop %dx				# Restore
33162138Sjhb		jc return			# If error, return an error
33262138Sjhb		cmp $0xaa55,%bx			# Magic?
33362138Sjhb		jne ereturn			# No, so return an error
33462138Sjhb		testb $0x1,%cl			# Packet interface?
33562138Sjhb		jz ereturn			# No, so return an error
33662138Sjhb		mov %bp,%si			# Disk packet
33762138Sjhb		movb $0x42,%ah			# BIOS: Extended
33862138Sjhb		int $0x13			#  read
33962138Sjhb		retw				# To caller
34040269Srnordier
34140269Srnordier# Messages
34240269Srnordier
34341008Srnordiermsg_read:	.asciz "Read"
34441008Srnordiermsg_part:	.asciz "Boot"
34540269Srnordier
34641085Srnordierprompt: 	.asciz " error\r\n"
34740940Srnordier
34848919Srnordierflags:		.byte FLAGS			# Flags
34948919Srnordier
35040269Srnordier		.org PRT_OFF,0x90
35140269Srnordier
35240269Srnordier# Partition table
35340269Srnordier
35440269Srnordier		.fill 0x30,0x1,0x0
35540269Srnordierpart4:		.byte 0x80, 0x00, 0x01, 0x00
35685805Speter		.byte 0xa5, 0xfe, 0xff, 0xff
35740269Srnordier		.byte 0x00, 0x00, 0x00, 0x00
35861836Sjhb		.byte 0x50, 0xc3, 0x00, 0x00	# 50000 sectors long, bleh
35940269Srnordier
36040269Srnordier		.word 0xaa55			# Magic number
361