boot1.S revision 104683
1//
2// Copyright (c) 1998 Robert Nordier
3// All rights reserved.
4//
5// Redistribution and use in source and binary forms are freely
6// permitted provided that the above copyright notice and this
7// paragraph and the following disclaimer are duplicated in all
8// such forms.
9//
10// This software is provided "AS IS" and without any express or
11// 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 104683 2002-10-08 18:19:02Z 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#ifdef UFS1_ONLY
41		.set NSECT,0x10
42#else
43		.set NSECT,0x14
44#endif
45		.globl start
46		.globl xread
47		.code16
48
49start:		jmp main			// Start recognizably
50
51// This is the start of a standard BIOS Parameter Block (BPB). Most bootable
52// FAT disks have this at the start of their MBR. While normal BIOS's will
53// work fine without this section, IBM's El Torito emulation "fixes" up the
54// BPB by writing into the memory copy of the MBR. Rather than have data
55// written into our xread routine, we'll define a BPB to work around it.
56// The data marked with (T) indicates a field required for a ThinkPad to
57// recognize the disk and (W) indicates fields written from IBM BIOS code.
58// The use of the BPB is based on what OpenBSD and NetBSD implemented in
59// their boot code but the required fields were determined by trial and error.
60//
61// Note: If additional space is needed in boot1, one solution would be to
62// move the "prompt" message data (below) to replace the OEM ID.
63
64		.org 0x03, 0x00
65oemid:		.space 0x08, 0x00	// OEM ID
66
67		.org 0x0b, 0x00
68bpb:		.word   512		// sector size (T)
69		.byte	0		// sectors/clustor
70		.word	0		// reserved sectors
71		.byte	0		// number of FATs
72		.word	0		// root entries
73		.word	0		// small sectors
74		.byte	0		// media type (W)
75		.word	0		// sectors/fat
76		.word	18		// sectors per track (T)
77		.word	2		// number of heads (T)
78		.long	0		// hidden sectors (W)
79		.long	0		// large sectors
80
81		.org 0x24, 0x00
82ebpb:		.byte	0		// BIOS physical drive number (W)
83
84		.org 0x25,0x90
85//
86// Trampoline used by boot2 to call read to read data from the disk via
87// the BIOS.  Call with:
88//
89// %cx:%ax	- long    - LBA to read in
90// %es:(%bx)	- caddr_t - buffer to read data into
91// %dl		- byte    - drive to read from
92// %dh		- byte    - num sectors to read
93//
94
95xread:		push %ss			// Address
96		pop %ds				//  data
97//
98// Setup an EDD disk packet and pass it to read
99//
100xread.1:					// Starting
101		pushl $0x0			//  absolute
102		push %cx			//  block
103		push %ax			//  number
104		push %es			// Address of
105		push %bx			//  transfer buffer
106		xor %ax,%ax			// Number of
107		movb %dh,%al			//  blocks to
108		push %ax			//  transfer
109		push $0x10			// Size of packet
110		mov %sp,%bp			// Packet pointer
111		callw read			// Read from disk
112		lea 0x10(%bp),%sp		// Clear stack
113		lret				// To far caller
114//
115// Load the rest of boot2 and BTX up, copy the parts to the right locations,
116// and start it all up.
117//
118
119//
120// Setup the segment registers to flat addressing (segment 0) and setup the
121// stack to end just below the start of our code.
122//
123main:		cld				// String ops inc
124		xor %cx,%cx			// Zero
125		mov %cx,%es			// Address
126		mov %cx,%ds			//  data
127		mov %cx,%ss			// Set up
128		mov $start,%sp			//  stack
129//
130// Relocate ourself to MEM_REL.  Since %cx == 0, the inc %ch sets
131// %cx == 0x100.
132//
133		mov %sp,%si			// Source
134		mov $MEM_REL,%di		// Destination
135		incb %ch			// Word count
136		rep				// Copy
137		movsw				//  code
138//
139// If we are on a hard drive, then load the MBR and look for the first
140// FreeBSD slice.  We use the fake partition entry below that points to
141// the MBR when we call nread.  The first pass looks for the first active
142// FreeBSD slice.  The second pass looks for the first non-active FreeBSD
143// slice if the first one fails.
144//
145		mov $part4,%si			// Partition
146		cmpb $0x80,%dl			// Hard drive?
147		jb main.4			// No
148		movb $0x1,%dh			// Block count
149		callw nread			// Read MBR
150		mov $0x1,%cx	 		// Two passes
151main.1: 	mov $MEM_BUF+PRT_OFF,%si	// Partition table
152		movb $0x1,%dh			// Partition
153main.2: 	cmpb $PRT_BSD,0x4(%si)		// Our partition type?
154		jne main.3			// No
155		jcxz main.5			// If second pass
156		testb $0x80,(%si)		// Active?
157		jnz main.5			// Yes
158main.3: 	add $0x10,%si	 		// Next entry
159		incb %dh			// Partition
160		cmpb $0x1+PRT_NUM,%dh		// In table?
161		jb main.2			// Yes
162		dec %cx				// Do two
163		jcxz main.1			//  passes
164//
165// If we get here, we didn't find any FreeBSD slices at all, so print an
166// error message and die.
167//
168		mov $msg_part,%si		// Message
169		jmp error			// Error
170//
171// Floppies use partition 0 of drive 0.
172//
173main.4: 	xor %dx,%dx			// Partition:drive
174//
175// Ok, we have a slice and drive in %dx now, so use that to locate and load
176// boot2.  %si references the start of the slice we are looking for, so go
177// ahead and load up the first 16 sectors (boot1 + boot2) from that.  When
178// we read it in, we conveniently use 0x8c00 as our transfer buffer.  Thus,
179// boot1 ends up at 0x8c00, and boot2 starts at 0x8c00 + 0x200 = 0x8e00.
180// The first part of boot2 is the disklabel, which is 0x200 bytes long.
181// The second part is BTX, which is thus loaded into 0x9000, which is where
182// it also runs from.  The boot2.bin binary starts right after the end of
183// BTX, so we have to figure out where the start of it is and then move the
184// binary to 0xc000.  Normally, BTX clients start at MEM_USR, or 0xa000, but
185// when we use btxld to create boot2, we use an entry point of 0x2000.  That
186// entry point is relative to MEM_USR; thus boot2.bin starts at 0xc000.
187//
188main.5: 	mov %dx,MEM_ARG			// Save args
189		movb $NSECT,%dh			// Sector count
190		callw nread			// Read disk
191		mov $MEM_BTX,%bx		// BTX
192		mov 0xa(%bx),%si		// Get BTX length and set
193		add %bx,%si			//  %si to start of boot2.bin
194		mov $MEM_USR+SIZ_PAG*2,%di	// Client page 2
195		mov $MEM_BTX+(NSECT-2)*SIZ_SEC,%cx // Byte
196		sub %si,%cx			//  count
197		rep				// Relocate
198		movsb				//  client
199		sub %di,%cx			// Byte count
200		xorb %al,%al			// Zero assumed bss from
201		rep				//  the end of boot2.bin
202		stosb				//  up to 0x10000
203		callw seta20			// Enable A20
204		jmp start+MEM_JMP-MEM_ORG	// Start BTX
205//
206// Enable A20 so we can access memory above 1 meg.
207//
208seta20: 	cli				// Disable interrupts
209seta20.1:	inb $0x64,%al			// Get status
210		testb $0x2,%al			// Busy?
211		jnz seta20.1			// Yes
212		movb $0xd1,%al			// Command: Write
213		outb %al,$0x64			//  output port
214seta20.2:	inb $0x64,%al			// Get status
215		testb $0x2,%al			// Busy?
216		jnz seta20.2			// Yes
217		movb $0xdf,%al			// Enable
218		outb %al,$0x60			//  A20
219		sti				// Enable interrupts
220		retw				// To caller
221//
222// Trampoline used to call read from within boot1.
223//
224nread:		mov $MEM_BUF,%bx		// Transfer buffer
225		mov 0x8(%si),%ax		// Get
226		mov 0xa(%si),%cx		//  LBA
227		push %cs			// Read from
228		callw xread.1	 		//  disk
229		jnc return			// If success, return
230		mov $msg_read,%si		// Otherwise, set the error
231						//  message and fall through to
232						//  the error routine
233//
234// Print out the error message pointed to by %ds:(%si) followed
235// by a prompt, wait for a keypress, and then reboot the machine.
236//
237error:		callw putstr			// Display message
238		mov $prompt,%si			// Display
239		callw putstr			//  prompt
240		xorb %ah,%ah			// BIOS: Get
241		int $0x16			//  keypress
242		movw $0x1234, BDA_BOOT		// Do a warm boot
243		ljmp $0xffff,$0x0		// reboot the machine
244//
245// Display a null-terminated string using the BIOS output.
246//
247putstr.0:	mov $0x7,%bx	 		// Page:attribute
248		movb $0xe,%ah			// BIOS: Display
249		int $0x10			//  character
250putstr: 	lodsb				// Get char
251		testb %al,%al			// End of string?
252		jne putstr.0			// No
253
254//
255// Overused return code.  ereturn is used to return an error from the
256// read function.  Since we assume putstr succeeds, we (ab)use the
257// same code when we return from putstr.
258//
259ereturn:	movb $0x1,%ah			// Invalid
260		stc				//  argument
261return: 	retw				// To caller
262//
263// Reads sectors from the disk.  If EDD is enabled, then check if it is
264// installed and use it if it is.  If it is not installed or not enabled, then
265// fall back to using CHS.  Since we use a LBA, if we are using CHS, we have to
266// fetch the drive parameters from the BIOS and divide it out ourselves.
267// Call with:
268//
269// %dl	- byte     - drive number
270// stack - 10 bytes - EDD Packet
271//
272read:	 	push %dx			// Save
273		movb $0x8,%ah			// BIOS: Get drive
274		int $0x13			//  parameters
275		movb %dh,%ch			// Max head number
276		pop %dx				// Restore
277		jc return			// If error
278		andb $0x3f,%cl			// Sectors per track
279		jz ereturn			// If zero
280		cli				// Disable interrupts
281		mov 0x8(%bp),%eax		// Get LBA
282		push %dx			// Save
283		movzbl %cl,%ebx			// Divide by
284		xor %edx,%edx			//  sectors
285		div %ebx			//  per track
286		movb %ch,%bl			// Max head number
287		movb %dl,%ch			// Sector number
288		inc %bx				// Divide by
289		xorb %dl,%dl			//  number
290		div %ebx			//  of heads
291		movb %dl,%bh			// Head number
292		pop %dx				// Restore
293		cmpl $0x3ff,%eax		// Cylinder number supportable?
294		sti				// Enable interrupts
295		ja read.7			// No, try EDD
296		xchgb %al,%ah			// Set up cylinder
297		rorb $0x2,%al			//  number
298		orb %ch,%al			// Merge
299		inc %ax				//  sector
300		xchg %ax,%cx	 		//  number
301		movb %bh,%dh			// Head number
302		subb %ah,%al			// Sectors this track
303		mov 0x2(%bp),%ah		// Blocks to read
304		cmpb %ah,%al			// To read
305		jb read.2			//  this
306		movb %ah,%al			//  track
307read.2: 	mov $0x5,%di	 		// Try count
308read.3: 	les 0x4(%bp),%bx		// Transfer buffer
309		push %ax			// Save
310		movb $0x2,%ah			// BIOS: Read
311		int $0x13			//  from disk
312		pop %bx				// Restore
313		jnc read.4			// If success
314		dec %di				// Retry?
315		jz read.6			// No
316		xorb %ah,%ah			// BIOS: Reset
317		int $0x13			//  disk system
318		xchg %bx,%ax	 		// Block count
319		jmp read.3			// Continue
320read.4: 	movzbw %bl,%ax	 		// Sectors read
321		add %ax,0x8(%bp)		// Adjust
322		jnc read.5			//  LBA,
323		incw 0xa(%bp)	 		//  transfer
324read.5: 	shlb %bl			//  buffer
325		add %bl,0x5(%bp)		//  pointer,
326		sub %al,0x2(%bp)		//  block count
327		ja read				// If not done
328read.6: 	retw				// To caller
329read.7:		testb $FL_PACKET,%cs:MEM_REL+flags-start // LBA support enabled?
330		jz ereturn			// No, so return an error
331		mov $0x55aa,%bx			// Magic
332		push %dx			// Save
333		movb $0x41,%ah			// BIOS: Check
334		int $0x13			//  extensions present
335		pop %dx				// Restore
336		jc return			// If error, return an error
337		cmp $0xaa55,%bx			// Magic?
338		jne ereturn			// No, so return an error
339		testb $0x1,%cl			// Packet interface?
340		jz ereturn			// No, so return an error
341		mov %bp,%si			// Disk packet
342		movb $0x42,%ah			// BIOS: Extended
343		int $0x13			//  read
344		retw				// To caller
345
346// Messages
347
348msg_read:	.asciz "Read"
349msg_part:	.asciz "Boot"
350
351prompt: 	.asciz " error\r\n"
352
353flags:		.byte FLAGS			// Flags
354
355		.org PRT_OFF,0x90
356
357// Partition table
358
359		.fill 0x30,0x1,0x0
360part4:		.byte 0x80, 0x00, 0x01, 0x00
361		.byte 0xa5, 0xfe, 0xff, 0xff
362		.byte 0x00, 0x00, 0x00, 0x00
363		.byte 0x50, 0xc3, 0x00, 0x00	// 50000 sectors long, bleh
364
365		.word 0xaa55			// Magic number
366