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