xref: /titanic_52/usr/src/psm/stand/bootblks/ufs/i386/mboot.S (revision bdfc6d18da790deeec2e0eb09c625902defe2498)
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 *
26 * ident	"%Z%%M%	%I%	%E% SMI"
27 */
28
29/*
30 * SOLARIS MASTER BOOT:
31 *
32 * PURPOSE: loads the primary boot from the active fdisk partition.
33 *          in effect, this routine mimics the functionality of INT 0x19.
34 *
35 * resides on the first physical sector of the hard drive media.
36 * loaded by INT 0x19 (ROM bootstrap loader) at address 0x7C00
37 * limited to 512 bytes total, including embedded fdisk table.
38 *
39 * for compatibility with the ROM BIOS, we contain standard DOS structures:
40 *
41 *	the fdisk partition table (at offset 0x1BE-0x1FE)
42 *	boot signature bytes (0x55, 0xAA at 0x1FE, 0x1FF)
43 *
44 * the above two entities are required in order to be compatible with
45 * the manner in which the DOS BIOS has always performed its boot operation.
46 * In the event that our master boot record is inadvertently replaced by
47 * a standard DOS boot sector, the booting operation will still succeed!
48 *
49 * This master boot record uses the relsect/numsect fields of the partition
50 * table entry, to compute the start of the active partition; therefore,
51 * it is geometry independent.  This means that the drive could be "built"
52 * on a system with a disk controller that uses a given disk geometry, but
53 * would run on any other controller.
54 *
55 * SYNOPSIS:
56 *     begins execution at 0:0x7C00
57 *     relocates to 0:0x600 (to get out of the way!)
58 *     reads fdisk table to locate bootable partition
59 *     load boot record from the active fdisk partition at 0x7C00
60 *     verify boot record signature bytes
61 *     jump to/execute the SOLARIS PARTITION PRIMARY BOOT
62 *     error handler - can either reboot, or invoke INT 0x18.
63 *
64 * interface from DOS INT 0x19:  BootDev in DL
65 * (this fails sometimes, so we look for a signature to determine whether
66 *  to rely on DL from the floppy boot, or if we should assume 0x80 from
67 *  the BIOS)
68 *
69 * interface to partition boot: BootDev in DL
70 *
71 *=============================================================================
72 * Master boot record: resides on first physical sector of device
73 */
74
75/*
76 * This file is written in GNU as syntax using Intel assembler syntax.  The
77 * startup label _start will be executed at address PBOOT_ADDR (0x7C00), but
78 * the text section must be set at address RELOC_ADDR (0x600).  With GNU ld
79 * this can be done using the "-Ttext 600" option.
80 */
81
82
83#define	PBOOT_ADDR	0x7C00
84#define	RELOC_ADDR	0x600
85
86#define	FDISK_START	0x1BE
87#define	BOOT_SIG	0xAA55
88#define	N_RETRIES	5
89
90#define	FD_NUMPART	4
91#define	FD_PTESIZE	0x10
92#define	ACTIVE		0x80
93
94/*
95 * A convenience macro for declaring a message string (using .ascii directive--
96 * NOT nul-terminated) surrounded by two labels, which can then be used with
97 * the SIZEOF() macro to get its length.
98 */
99#define	MSG(label, string)	label: .ascii string; label##_end:
100
101/*
102 * Returns the length of some consecutive bytes.  These bytes must be placed
103 * between two labels.  The ending label must be the same as the starting label
104 * but with a suffix "_end".
105 */
106#define	SIZEOF(label)	(offset label##_end - offset label)
107
108
109	.title	"Solaris_Master_Boot"
110
111	.intel_syntax noprefix		/* use Intel syntax */
112	.code16				/* 16-bit mode (real mode) */
113
114	.text				/* code segment begins here */
115
116	.global	BootDev
117	.global _start
118
119_start:					/* _start is loaded at PBOOT_ADDR */
120	jmp	bootrun
121
122Version:
123	.ascii	"M3.0"			/* ident string */
124
125bootrun:
126	cli				/* don't bother me now! */
127
128	/* prepare to relocate ourselves */
129	cld				/* prepare for relocation */
130	mov	si, PBOOT_ADDR
131	mov	di, RELOC_ADDR
132
133	/* set up segment registers */
134	mov	ax, cs			/* initialize segment registers */
135	mov	ss, ax
136	mov	sp, si			/* stack starts down from 7C00 */
137	mov	es, ax
138	mov	ds, ax
139
140	push	cx 			/* save possible signature on stack */
141	mov	cx, 0x100
142	rep	movsw
143	pop	cx			/* restore saved cx */
144
145	/* running at PBOOT_ADDR, jump to RELOC_ADDR-rel addr */
146	jmp	(new_home - PBOOT_ADDR + RELOC_ADDR)
147
148new_home:
149	sti				/* re-enable interrupts */
150
151	/*
152	 * assuming boot device number is in dl has caused problems in the past
153	 * since we still don't absolutely have to rely on it, I've just
154	 * removed the now-pointless code to check for the FACE-CAFE signature
155	 * from mdexec, which doesn't do anything anymore, but left the
156	 * assumption that BootDev is 0x80 and nothing but.  If we ever need to
157	 * have BIOS load us from a drive not numbered 0x80, we'll need to
158	 * uncomment the following line; otherwise, the initialized value of
159	 * BootDev, namely 0x80, will be used for disk accesses.
160	 */
161	/* mov BootDev, dl */
162
163	/* set debug flag based on seeing "both shift down" */
164	mov	ah, 2		/* get shift state */
165	int	0x16
166	and	al, 3		/* isolate shift-key bits */
167	cmp	al, 3
168	jne	nodbg
169	mov	byte ptr [debugmode], 1		/* set to 1 */
170
171nodbg:
172	/*
173	 * Search the fdisk table sequentially to find a physical partition
174	 * that is marked as "active" (bootable).
175	 */
176	mov	bx, RELOC_ADDR + FDISK_START
177	mov	cx, FD_NUMPART
178
179nxtpart:
180	cmp	byte ptr [bx], ACTIVE
181	je	got_active_part
182	add	bx, FD_PTESIZE
183	loop	nxtpart
184
185noparts:
186	mov	bp, offset NoActiveErrMsg
187	mov	cx, SIZEOF(NoActiveErrMsg)
188	jmp	fatal_err
189
190got_active_part:
191	mov	ah, 0		/* reset disk */
192	int	0x13
193
194	push	bx		/* save partition pointer */
195
196	/* Check for LBA BIOS */
197	mov	ah, 0x41	/* chkext function */
198	mov	bx, 0x55AA	/* signature to change */
199	mov	cx, 0
200	int	0x13
201	jc	noLBA		/* carry == failure */
202	cmp	bx, 0xAA55
203	jne	noLBA		/* bad signature in BX == failure */
204	test	cx, 1		/* cx & 1 must be true, or... */
205	jz	noLBA		/* ...no LBA */
206
207	mov	bp, offset lbastring
208	mov	cx, SIZEOF(lbastring)
209	call	debugout
210
211	/*
212	 * LBA case: form a packet on the stack and call fn 0x42 to read
213	 * packet, backwards (from hi to lo addresses):
214	 * 8-byte LBA
215	 * seg:ofs buffer address
216	 * byte reserved
217	 * byte nblocks
218	 * byte reserved
219	 * packet size in bytes (>= 0x10)
220	 */
221
222	pop	bx		/* restore partition pointer */
223	push	bx		/* and save again */
224	mov	cx, N_RETRIES	/* retry count */
225retryLBA:
226	pushd	0		/* hi 32 bits of 64-bit sector number */
227	push	dword ptr [bx+8]	/* relsect (lo 32 of 64-bit number) */
228	push	dword ptr [solaris_priboot]	/* seg:ofs of buffer */
229	push	1		/* reserved, one block */
230	push	0x10		/* reserved, size (0x10) */
231	mov	ah, 0x42	/* "read LBA" */
232	mov	si, sp		/* (ds already == ss) */
233	int	0x13
234	lahf			/* save flags */
235	add	sp, 16		/* restore stack */
236	sahf			/* restore flags */
237	jnc	readok		/* got it */
238	mov	ah, 0		/* reset disk */
239	int	0x13
240	loop	retryLBA	/* try again */
241	jmp	readerr		/* exhausted retries; give up */
242
243noLBA:
244	mov	bp, offset chsstring
245	mov	cx, SIZEOF(chsstring)
246	call	debugout
247
248	pop	bx		/* restore partition pointer */
249	push	bx		/* and save again */
250
251	/* get BIOS disk parameters */
252	mov	dl, byte ptr [BootDev]
253	mov	ah, 0x8
254	int	0x13
255
256	jnc	geomok
257
258	/* error reading geom; die */
259	mov	bp, offset GeomErrMsg
260	mov	cx, SIZEOF(GeomErrMsg)
261	jmp	fatal_err
262
263geomok:
264	/* calculate sectors per track */
265	mov	al, cl		/* ah doesn't matter; mul dh will set it */
266	and	al, 0x3F
267	mov	byte ptr [secPerTrk], al
268
269	/* calculate sectors per cylinder */
270	inc	dh
271	mul	dh
272	mov	word ptr [secPerCyl], ax
273
274	/* calculate cylinder # */
275	mov	ax, [bx+8]	/* ax = loword(relsect) */
276	mov	dx, [bx+10]	/* dx:ax = relsect */
277	div	word ptr [secPerCyl]	/* ax = cyl, */
278					/* dx = sect in cyl (0 - cylsize-1) */
279	mov	bx, ax		/* bx = cyl */
280
281	/* calculate head/sector # */
282	mov	ax, dx		/* ax = sect in cyl (0 - cylsize-1) */
283	div	byte ptr [secPerTrk]	/* al = head, */
284					/* ah = 0-rel sect in track */
285	inc	ah		/* ah = 1-rel sector */
286
287	xor	cl,cl		/* cl = 0 */
288	mov	ch, bh		/* ch = hi bits of cyl (if any) */
289	shr	cx, 2		/* cl{7:6} = cyl{9:8} (if any) */
290	and	cl, 0xC0	/* cl = cyl{9:8} to merge with sect (if any) */
291
292	or	cl, ah		/* cl{7:6} = cyl bits, cl{5:0} = sect */
293	mov	ch, bl		/* ch = lo cyl bits */
294	mov	dh, al		/* dh = head */
295	mov	dl, byte ptr [BootDev]		/* dl = drivenum */
296	les	bx, solaris_priboot		/* es:bx points to buffer */
297
298	mov	si, N_RETRIES
299retry_noLBA:
300	mov	ax, 0x201	/* 02=read, sector count = 1 */
301
302	int	0x13
303	jnc	readok
304	mov	ah, 0		/* reset disk */
305	int	0x13
306	dec	si
307	cmp	si, 0
308	jne	retry_noLBA	/* retry, or fall through to read error */
309
310readerr:
311	mov	bp, offset ReadErrMsg
312	mov	cx, SIZEOF(ReadErrMsg)
313	jmp	fatal_err
314
315readok:
316	/* verify boot record signature */
317	mov	bx, PBOOT_ADDR
318	cmp	word ptr [bx+0x1FE], BOOT_SIG
319	je	sigok
320
321	mov	bp, offset SigErrMsg
322	mov	cx, SIZEOF(SigErrMsg)
323	jmp	fatal_err
324
325sigok:
326	mov	dl, byte ptr [BootDev]	/* pass BootDev to next boot phase */
327	pop	si			/* and pass partition pointer ds:si */
328	call	dword ptr [solaris_priboot]	/* call doesn't return! */
329
330	mov	bp, offset ReturnErrMsg
331	mov	cx, SIZEOF(ReturnErrMsg)
332
333fatal_err:			/* land of no return....... */
334	/*
335	 * bp contains pointer to error message string,
336	 * cx contains string length
337	 */
338	mov	bx, 0x4F	/* video page, attribute */
339	call	msgout
340	int	0x18
341
342debugout:
343	/* call with string pointer in es:bp, len in cx */
344	cmp	byte ptr [debugmode], 0
345	je	debugout_ret	/* skip if not in debug mode */
346
347	mov	bx, 0x1F	/* page, attr (white on blue) */
348
349	/* alternate entry for fatal_err */
350msgout:
351	pusha
352	mov	ax, 0x1301
353	mov	dx, 0x1700	/* row, col */
354	int	0x10
355
356	mov	al, 7		/* BEL */
357	mov	cx, 1
358	int	0x10
359
360	mov	ah, 0		/* get key */
361	int	0x16
362	popa
363
364debugout_ret:
365	ret
366
367secPerTrk:
368	.byte	0
369secPerCyl:
370	.word	0
371solaris_priboot:
372	.long	PBOOT_ADDR
373BootDev:
374	.byte	0x80		/* assumes drive 80 (see comment above) */
375debugmode:
376	.byte	0
377
378MSG(GeomErrMsg,		"Can't read geometry")
379MSG(NoActiveErrMsg,	"No active partition")
380MSG(ReadErrMsg,		"Can't read PBR")
381MSG(SigErrMsg,		"Bad PBR sig")
382MSG(ReturnErrMsg,	"!!!")
383MSG(lbastring,		"LBA")
384MSG(chsstring,		"CHS")
385
386/*
387 * For debugging:  Here's a representative FDISK table entry
388 *
389 * .org   0x1BE
390 * .byte  0x80,1,1,0,0x82,0xfe,0x7f,4,0x3f,0,0,0,0x86,0xfa,0x3f,0
391 */
392	.org 	0x1FE
393
394	.word	BOOT_SIG
395