xref: /dragonfly/stand/boot/pc32/boot2/boot1.S (revision 655933d6)
1/*
2 * Copyright (c) 2003,2004 The DragonFly Project.  All rights reserved.
3 *
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in
15 *    the documentation and/or other materials provided with the
16 *    distribution.
17 * 3. Neither the name of The DragonFly Project nor the names of its
18 *    contributors may be used to endorse or promote products derived
19 *    from this software without specific, prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 * Copyright (c) 1998 Robert Nordier
35 * All rights reserved.
36 *
37 * Redistribution and use in source and binary forms are freely
38 * permitted provided that the above copyright notice and this
39 * paragraph and the following disclaimer are duplicated in all
40 * such forms.
41 *
42 * This software is provided "AS IS" and without any express or
43 * implied warranties, including, without limitation, the implied
44 * warranties of merchantability and fitness for a particular
45 * purpose.
46 *
47 * $FreeBSD: src/sys/boot/i386/boot2/boot1.s,v 1.23 2003/08/22 01:59:28 imp Exp $
48 * $DragonFly: src/sys/boot/pc32/boot2/boot1.S,v 1.10 2008/09/13 11:45:45 corecode Exp $
49 */
50
51#include "../bootasm.h"
52
53// Partition Constants
54		.set PRT_OFF,0x1be		// Partition offset
55		.set PRT_NUM,0x4		// Partitions
56		.set PRT_BSD,0xa5		// Partition type
57		.set PRT_DFLY,0x6c		// Partition type
58
59// Flag Bits
60		.set FL_PACKET,0x80		// Packet mode
61
62// Misc. Constants
63		.set SIZ_PAG,0x1000		// Page size
64		.set SIZ_SEC,0x200		// Sector size
65#ifndef NSECT
66		.set NSECT,0x10
67#endif
68
69		.globl start
70		.globl xread
71		.code16
72
73start:		jmp main			// Start recognizably
74
75// This is the start of a standard BIOS Parameter Block (BPB). Most bootable
76// FAT disks have this at the start of their MBR. While normal BIOS's will
77// work fine without this section, IBM's El Torito emulation "fixes" up the
78// BPB by writing into the memory copy of the MBR. Rather than have data
79// written into our xread routine, we'll define a BPB to work around it.
80// The data marked with (T) indicates a field required for a ThinkPad to
81// recognize the disk and (W) indicates fields written from IBM BIOS code.
82// The use of the BPB is based on what OpenBSD and NetBSD implemented in
83// their boot code but the required fields were determined by trial and error.
84//
85// Note: If additional space is needed in boot1, one solution would be to
86// move the "prompt" message data (below) to replace the OEM ID.
87
88		.org 0x03, 0x00
89oemid:		.space 0x08, 0x00	// OEM ID
90
91		.org 0x0b, 0x00
92bpb:		.word   512		// sector size (T)
93		.byte	0		// sectors/clustor
94		.word	0		// reserved sectors
95		.byte	0		// number of FATs
96		.word	0		// root entries
97		.word	0		// small sectors
98		.byte	0		// media type (W)
99		.word	0		// sectors/fat
100		.word	18		// sectors per track (T)
101		.word	2		// number of heads (T)
102		.long	0		// hidden sectors (W)
103		.long	0		// large sectors
104
105		.org 0x24, 0x00
106ebpb:		.byte	0		// BIOS physical drive number (W)
107
108		.org 0x25,0x90
109//
110// Trampoline used by boot2 to call read to read data from the disk via
111// the BIOS.  Call with:
112//
113// %cx:%ax	- long    - LBA to read in
114// %es:(%bx)	- caddr_t - buffer to read data into
115// %dl		- byte    - drive to read from
116// %dh		- byte    - num sectors to read
117//
118
119xread:		push %ss			// Address
120		pop %ds				//  data
121//
122// Setup an EDD disk packet and pass it to read
123//
124xread.1:					// Starting
125		pushl $0x0			//  absolute
126		push %cx			//  block
127		push %ax			//  number
128		push %es			// Address of
129		push %bx			//  transfer buffer
130		xor %ax,%ax			// Number of
131		movb %dh,%al			//  blocks to
132		push %ax			//  transfer
133		push $0x10			// Size of packet
134		mov %sp,%bp			// Packet pointer
135		callw read			// Read from disk
136		lea 0x10(%bp),%sp		// Clear stack
137		lret				// To far caller
138//
139// Load the rest of boot2 and BTX up, copy the parts to the right locations,
140// and start it all up.
141//
142
143//
144// Setup the segment registers to flat addressing (segment 0) and setup the
145// stack to end just below the start of our code.
146//
147// XXX note - our origin (start) points to the MEM_BIOS_LADDR.  We run
148// from there but boot2 later on calls xread at BOOT1_ORIGIN.
149//
150main:		cld				// String ops inc
151		xor %cx,%cx			// Zero
152		mov %cx,%es			// Address
153		mov %cx,%ds			//  data
154		mov %cx,%ss			// Set up
155		mov $start,%sp			//  stack
156//
157// Relocate ourself to BOOT1_ORIGIN.  Since %cx == 0, the inc %ch sets
158// %cx == 0x100 (256 words == 512 bytes).
159//
160		mov %sp,%si			// Source
161		mov $BOOT1_ORIGIN,%di		// Destination
162		incb %ch			// Word count
163		rep				// Copy
164		movsw				//  code
165//
166// If we are on a hard drive, then load the MBR and look for the first
167// FreeBSD slice.
168//
169// Note, we can't use the fake partition entry (part4), as it may contain
170// garbage if this is a normal boot1 on a slice, verses a dangerously
171// dedicated disk.  Hardwire sector 0 to acquire the MBR
172//
173		xor %ax,%ax
174		xor %cx,%cx
175		cmpb $0x80,%dl			// Hard drive?
176		jb main.4			// No
177		movb $0x1,%dh			// Block count
178		callw nread_alt			// Read MBR
179		mov $0x1,%cx	 		// Two passes
180main.1: 	mov $BOOT2_LOAD_BUF+PRT_OFF,%si	// Partition table
181		movb $0x1,%dh			// Partition
182main.2: 	cmpb $PRT_BSD,0x4(%si)		// FreeBSD / old DFly
183		je main.2a
184		cmpb $PRT_DFLY,0x4(%si)		// New DFly
185		jne main.3			// No
186main.2a:	jcxz main.5			// If second pass
187		testb $0x80,(%si)		// Active?
188		jnz main.5			// Yes
189main.3: 	add $0x10,%si	 		// Next entry
190		incb %dh			// Partition
191		cmpb $0x1+PRT_NUM,%dh		// In table?
192		jb main.2			// Yes
193		dec %cx				// Do two
194		jcxz main.1			//  passes
195//
196// If we get here, we didn't find any FreeBSD slices at all, so print an
197// error message and die.
198//
199		jmp error			// Error
200//
201// Floppies use partition 0 of drive 0.
202//
203main.4: 	xor %dx,%dx			// Partition:drive
204//
205// Ok, we have a slice and drive in %dx now, so use that to locate and load
206// boot2.  %si references the start of the slice we are looking for, so go
207// ahead and load up the first N sectors (boot1 + boot2) from that.
208//
209// N is 16 for boot1 in a disklabel32 and up to 32 in a disklabel64.  The
210// disklabel64 can hold up to 64 sectors but MEM_BTX_USR+BOOT2_VORIGIN
211// will overflow the segment if we use more then 32 sectors.
212//
213// When we read it in, we conveniently use BOOT2_LOAD_BUF (0x8c00) as our
214// transfer buffer.  Thus, boot1 ends up at 0x8c00, and boot2 starts at
215// 0x8c00 + 0x200 = 0x8e00.
216//
217// The first part of boot2 is the disklabel, which is 0x200 bytes long.
218// The second part is BTX, which is thus loaded into 0x9000, which is where
219// it also runs from.  The boot2.bin binary starts right after the end of
220// BTX, so we have to figure out where the start of it is and then move the
221// binary to 0xc000.  Normally, BTX clients start at MEM_BTX_USR, or 0xa000,
222// but when we use btxld to create boot2, we use an entry point of 0x2000.
223// That entry point is relative to MEM_BTX_USR; thus boot2.bin starts
224// at 0xc000.
225//
226// MEM_BTX_USR_ARG will be overwritten by the disk read and the relocation
227// loop, so we must store the argument after completing said loops.
228//
229main.5: 	pushw %dx			// Save args
230		movb $NSECT,%dh			// Sector count
231#ifdef DISKLABEL64
232						// In disklabel64 boot2 starts
233		addl $7,0x8(%si)		// offset 0x1000.
234#endif
235		callw nread			// Read disk
236		mov $MEM_BTX_ORG,%bx		// Base of BTX header
237		mov 0xa(%bx),%si		// Get BTX text length (btx.S)
238		add %bx,%si			// %si = start of boot2.bin
239						// %di = relocation target
240		mov $MEM_BTX_USR+BOOT2_VORIGIN,%di
241		mov $MEM_BTX_ORG+(NSECT-1)*SIZ_SEC,%cx
242		sub %si,%cx			// %cx = Size of boot2 client
243		rep				// Relocate boot2
244		movsb
245		popw MEM_BTX_USR_ARG		// save (disk,slice) for boot2
246
247#if 0
248		// XXX DISABLED.  This makes incorrect assumptions about
249		// where BSS begins, potentially leaving garbage in the BSS
250		// space.  The BSS zeroing code has been moved to
251		// btx/lib/btxcsu.S (BTX client startup code) where we have
252		// more definitive knowledge about where BSS resides.
253		//
254		// %cx now contains 0.  Calculate 0x[1]0000 - %di to get a
255		// count of assumed BSS bytes from the end of boot2.bin up
256		// to 0x10000, then zero it out.
257		//
258		sub %di,%cx
259		xorb %al,%al
260		rep
261		stosb
262#endif
263		callw seta20			// Enable A20
264
265		// YYY
266		pushw $MEM_BTX_ENTRY		// Start BTX
267		retw
268
269		/*
270		 * Enable A20. Put upper limit on amount of time we wait for the
271		 * keyboard controller to get ready (65K x ISA access time). If
272		 * we wait more than that amount it's likely that the hardware
273		 * is legacy-free and simply doesn't have keyboard controller
274		 * and don't need enabling A20 at all.
275		 */
276seta20: 	cli				# Disable interrupts
277		xor %cx,%cx			# Clear
278seta20.1:	inc %cx				# Increment, overflow?
279		jz seta20.3			# Yes
280		inb $0x64,%al			# Get status
281		testb $0x2,%al			# Busy?
282		jnz seta20.1			# Yes
283		movb $0xd1,%al			# Command: Write
284		outb %al,$0x64			#  output port
285seta20.2:	inb $0x64,%al			# Get status
286		testb $0x2,%al			# Busy?
287		jnz seta20.2			# Yes
288		movb $0xdf,%al			# Enable
289		outb %al,$0x60			#  A20
290seta20.3:	sti				# Enable interrupts
291		retw				# To caller
292
293//
294// Trampoline used to call read from within boot1.
295//
296nread:
297		mov 0x8(%si),%ax		// Get
298		mov 0xa(%si),%cx		//  LBA
299nread_alt:
300		mov $BOOT2_LOAD_BUF,%bx		// Transfer buffer
301		push %cs			// Read from
302		callw xread.1	 		//  disk
303		jnc return			// If success, return
304
305// Print that an error occured (no room to determine which error
306// occured) and the prompt.  Then wait for a keypress, then reboot the
307// machine.
308//
309error:
310		mov $prompt,%si			// Display
311		callw putstr			//  prompt
312		xorb %ah,%ah			// BIOS: Get
313		int $0x16			//  keypress
314		movw $0x1234, BDA_BOOT		// Do a warm boot
315		ljmp $0xf000,$0xfff0		// reboot the machine
316//
317// Display a null-terminated string using the BIOS output.
318//
319putstr.0:	mov $0x7,%bx	 		// Page:attribute
320		movb $0xe,%ah			// BIOS: Display
321		int $0x10			//  character
322putstr: 	lodsb				// Get char
323		testb %al,%al			// End of string?
324		jne putstr.0			// No
325
326//
327// Overused return code.  ereturn is used to return an error from the
328// read function.  Since we assume putstr succeeds, we (ab)use the
329// same code when we return from putstr.
330//
331ereturn:	movb $0x1,%ah			// Invalid
332		stc				//  argument
333return: 	retw				// To caller
334//
335// Reads sectors from the disk.  If EDD is enabled, then check if it is
336// installed and use it if it is.  If it is not installed or not enabled, then
337// fall back to using CHS.  Since we use a LBA, if we are using CHS, we have to
338// fetch the drive parameters from the BIOS and divide it out ourselves.
339// Call with:
340//
341// %dl	- byte     - drive number
342// stack - 10 bytes - EDD Packet
343
344read:
345		/*
346		 * Try EDD mode first.  If not enabled or no BIOS support
347		 * exists, fall back to CHS mode.
348		 */
349		testb	$FL_PACKET,%cs:BOOT1_ORIGIN+flags-start
350		jz	read.1
351
352		/*
353		 * BIOS: check extensions present
354		 */
355		mov	$0x55aa,%bx
356		push	%dx
357		movb	$0x41,%ah
358		int	$0x13
359		pop	%dx
360		jc	read.1			/* BIOS error return */
361		cmp	$0xaa55,%bx		/* check for proper magic */
362		jne	read.1
363		testb $0x1,%cl			/* packet interface support? */
364		jz	read.1
365
366		/*
367		 * Issue packet command.
368		 * BIOS: Extended read command
369		 */
370		mov	%bp,%si
371		movb	$0x42,%ah
372		int	$0x13
373		retw
374
375		/*
376		 * Fallback to CHS mode
377		 */
378read.1:
379		push %dx			// Save
380		movb $0x8,%ah			// BIOS: Get drive
381		int $0x13			//  parameters
382		movb %dh,%ch			// Max head number
383		pop %dx				// Restore
384		jc return			// If error
385		andb $0x3f,%cl			// Sectors per track
386		jz ereturn			// If zero
387		cli				// Disable interrupts
388		mov 0x8(%bp),%eax		// Get LBA
389		push %dx			// Save
390		movzbl %cl,%ebx			// Divide by
391		xor %edx,%edx			//  sectors
392		div %ebx			//  per track
393		movb %ch,%bl			// Max head number
394		movb %dl,%ch			// Sector number
395		inc %bx				// Divide by
396		xorb %dl,%dl			//  number
397		div %ebx			//  of heads
398		movb %dl,%bh			// Head number
399		pop %dx				// Restore
400		cmpl $0x3ff,%eax		// Cylinder number supportable?
401		sti				// Enable interrupts
402		ja ereturn			// No, failed
403		xchgb %al,%ah			// Set up cylinder
404		rorb $0x2,%al			//  number
405		orb %ch,%al			// Merge
406		inc %ax				//  sector
407		xchg %ax,%cx	 		//  number
408		movb %bh,%dh			// Head number
409		subb %ah,%al			// Sectors this track
410		mov 0x2(%bp),%ah		// Blocks to read
411		cmpb %ah,%al			// To read
412		jb read.2			//  this
413#ifdef	TRACK_AT_A_TIME
414		movb %ah,%al			//  track
415#else
416		movb $1,%al			//  one sector
417#endif
418read.2: 	mov $0x5,%di	 		// Try count
419read.3: 	les 0x4(%bp),%bx		// Transfer buffer
420		push %ax			// Save
421		movb $0x2,%ah			// BIOS: Read
422		int $0x13			//  from disk
423		pop %bx				// Restore
424		jnc read.4			// If success
425		dec %di				// Retry?
426		jz read.6			// No
427		xorb %ah,%ah			// BIOS: Reset
428		int $0x13			//  disk system
429		xchg %bx,%ax	 		// Block count
430		jmp read.3			// Continue
431read.4: 	movzbw %bl,%ax	 		// Sectors read
432		add %ax,0x8(%bp)		// Adjust
433		jnc read.5			//  LBA,
434		incw 0xa(%bp)	 		//  transfer
435read.5: 	shlb %bl			//  buffer
436		add %bl,0x5(%bp)		//  pointer,
437		sub %al,0x2(%bp)		//  block count
438		ja read.1			// If not done
439read.6: 	retw				// To caller
440
441// Messages
442
443prompt: 	.asciz " error\r\n"
444
445flags:		.byte FLAGS			// Flags
446
447		.org PRT_OFF,0x90
448
449// Partition table
450//
451// THIS MAY NOT BE WRITTEN OUT TO THE BOOT1 AREA OF THE DISKLABEL.  This
452// section is only written out when the disklabel is placed on the raw
453// disk instead of in a slice, when creating a dangerously dedicated disk.
454
455		.fill 0x30,0x1,0x0
456part4:		.byte 0x80, 0x00, 0x01, 0x00
457		.byte 0xa5, 0xfe, 0xff, 0xff
458		.byte 0x00, 0x00, 0x00, 0x00
459		.byte 0x50, 0xc3, 0x00, 0x00	// 50000 sectors long, bleh
460
461		.word 0xaa55			// Magic number
462