xref: /netbsd-src/sys/arch/i386/stand/cdboot/cdboot.S (revision 86811edb37e43f44504b192591c863c5d48f5e08)
1/*	$NetBSD: cdboot.S,v 1.6 2005/12/11 12:17:48 christos Exp $	*/
2
3/*-
4 * Copyright (c) 2005 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Bang Jun-Young.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 *    must display the following acknowledgement:
20 *	This product includes software developed by the NetBSD
21 *	Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 *    contributors may be used to endorse or promote products derived
24 *    from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39/*
40 * This is a primary boot loader that loads a secondary boot loader
41 * directly from CD without performing floppy/hard disk emulation as
42 * described by the El Torito specification.
43 *
44 * TODO:
45 *  - Support for loading secondary boot loader > 64kB
46 */
47
48#include <machine/asm.h>
49#include <sys/bootblock.h>
50
51#define BOOT_ADDR	0x7c00
52#define BLOCK_SIZE	2048		/* Default for ISO 9660 */
53#define VD_LBA		16		/* LBA of Volume Descriptor (VD) */
54#define PVD_ADDR	0x1000		/* Where Primary VD is loaded */
55#define ROOTDIR_ADDR	0x1800		/* Where Root Directory is loaded */
56#define LOADER_ADDR	SECONDARY_LOAD_ADDRESS
57
58#ifdef BOOT_FROM_FAT
59#define MBR_AFTERBPB	90		/* BPB size in FAT32 partition BR */
60#else
61#define MBR_AFTERBPB	62		/* BPB size in floppy master BR */
62#endif
63
64/*
65 * See src/sys/sys/bootblock.h for details.
66 */
67#define MBR_PART_COUNT	4
68#define MBR_PART_OFFSET	446
69#define MBR_PART_SIZE	16		/* sizeof(struct mbr_partition) */
70
71/*
72 * Disk error codes
73 */
74#define ERROR_TIMEOUT	0x80
75
76/*
77 * Volume Descriptor types.
78 */
79#define VD_PRIMARY		1
80#define VD_SUPPLEMENTARY	2
81#define VD_TERMINATOR		255
82
83/* Only actually used entries are listed below */
84
85/*
86 * Format of Primary Volume Descriptor (8.4)
87 */
88#define PVD_ROOT_DR	156	/* Offset of Root Directory Record */
89
90/*
91 * Format of Directory Record (9.1)
92 */
93#define DR_LEN		0
94#define DR_EXTENT	2
95#define DR_DATA_LEN	10
96#define DR_NAME_LEN	32
97#define DR_NAME		33
98
99	.text
100	.code16
101ENTRY(start)
102	jmp	start1
103
104	. = start + MBR_AFTERBPB	/* skip BPB */
105	. = start + MBR_DSN_OFFSET
106	.long	0
107
108/* mbr_bootsel_magic (not used here) */
109	. = start + MBR_BS_MAGIC_OFFSET
110	.word	0
111
112	. = start + MBR_PART_OFFSET
113	. = start + MBR_MAGIC_OFFSET
114pbr_magic:
115	.word	MBR_MAGIC
116	.fill	512			/* reserve space for disklabel */
117start1:
118	jmp	1f
119	.balign	4
120	.long	X86_BOOT_MAGIC_1	/* checked by installboot & pbr code */
121boot_params:				/* space for patchable variables */
122	.long	1f - boot_params	/* length of this data area */
123#include <boot_params.S>
124	. = start1 + 0x80		/* Space for patching unknown params */
125
1261:	xorw	%ax, %ax
127	movw	%ax, %ds
128	movw	%ax, %es
129	movw	%ax, %ss
130	movw	$BOOT_ADDR, %sp
131	movw	%sp, %si
132	movw	$start, %di
133	movw	$BLOCK_SIZE/2, %cx
134	rep
135	movsw
136	ljmp	$0, $real_start
137
138real_start:
139	movb	%dl, boot_drive		/* Save boot drive number */
140
141#ifndef DISABLE_KEYPRESS
142	/*
143	 * We can skip boot wait when:
144	 *  - there's no hard disk present.
145	 *  - there's no active partition in the MBR of the 1st hard disk.
146	 */
147
148	/*
149	 * Check presence of hard disks.
150	 */
151	movw	$0x475, %si
152	movb	(%si), %al
153	testb	%al, %al
154	jz	boot_cdrom
155
156	/*
157	 * Find the active partition from the MBR.
158	 */
159	movw	$0x0201, %ax		/* %al = number of sectors to read */
160	movw	$BOOT_ADDR, %bx		/* %es:%bx = data buffer */
161	movw	$0x0001, %cx		/* %ch = low 8 bits of cylinder no */
162					/* %cl = high 2 bits of cyl no & */
163					/*       sector number */
164	movw	$0x0080, %dx		/* %dh = head number */
165					/* %dl = disk number */
166	int	$0x13			/* Read MBR into memory */
167	jc	boot_cdrom		/* CF set on error */
168
169	movb	$1, mbr_loaded
170	movb	$MBR_PART_COUNT, %cl
171	movw	$BOOT_ADDR+MBR_PART_OFFSET, %si
1721:
173	movb	(%si), %al
174	testb	$0x80, %al
175	jnz	found_active
176	addw	$MBR_PART_SIZE, %si
177	decb	%cl
178	testb	%cl, %cl
179	jnz	1b			/* If 0, no active partition found */
180	jmp	boot_cdrom
181
182found_active:
183	movw	$str_press_key, %si
184	call	message
185next_second:
186	movw	$str_dot, %si
187	call	message
188	decb	wait_count
189	jz	boot_hard_disk
190	xorb	%ah, %ah		/* Get system time */
191	int	$0x1a
192	movw	%dx, %di		/* %cx:%dx = number of clock ticks */
193	addw	$19, %di		/* 19 ~= 18.2 Hz */
194wait_key:
195	movb	$1, %ah			/* Check for keystroke */
196	int	$0x16
197	jz	not_avail		/* ZF clear if keystroke available */
198	xorb	%ah, %ah		/* Read key to flush keyboard buf */
199	int	$0x16
200	jmp	boot_cdrom
201not_avail:
202	xorb	%ah, %ah		/* Get system time */
203	int	$0x1a
204	cmpw	%dx, %di		/* Compare with saved time */
205	jnz	wait_key
206	jmp	next_second
207
208boot_hard_disk:
209	movw	$str_crlf, %si
210	call	message
211	cmpb	$1, mbr_loaded
212	jz	1f
213	movw	$0x0201, %ax		/* %al = number of sectors to read */
214	movw	$BOOT_ADDR, %bx		/* %es:%bx = data buffer */
215	movw	$0x0001, %cx		/* %ch = low 8 bits of cylinder no */
216					/* %cl = high 2 bits of cyl no & */
217					/*       sector number */
218	movw	$0x0080, %dx		/* %dh = head number */
219					/* %dl = disk number */
220	int	$0x13			/* Read MBR into memory */
221	jc	panic			/* CF set on error */
2221:
223	movw	%cs, %ax		/* Restore initial state */
224	movw	%ax, %ds
225	movw	%ax, %es
226	movw	$0x0080, %dx		/* %dl = boot drive number */
227	jmp	$0, $BOOT_ADDR		/* Jump to MBR! */
228	jmp	panic			/* This should be never executed */
229#endif /* !DISABLE_KEYPRESS */
230
231boot_cdrom:
232	movw	$str_banner, %si
233	call	message
234	movl	$VD_LBA, %eax
235next_block:
236	movb	$1, %dh			/* Number of sectors to read */
237	movl	$PVD_ADDR, %ebx
238	call	read_sectors
239	cmpb	$VD_PRIMARY, (%bx)	/* Is it Primary Volume Descriptor? */
240	jz	pvd_found
241	incl	%eax
242	cmpb	$VD_TERMINATOR, (%bx)
243	jnz	next_block
244	movw	$str_no_pvd, %si
245	call	message
246	jmp	panic
247
248pvd_found:
249	movw	$PVD_ADDR+PVD_ROOT_DR, %bx
250	movl	DR_EXTENT(%bx), %eax	/* LBA of the root directory */
251	movl	DR_DATA_LEN(%bx), %edx
252	shrl	$11, %edx		/* Convert to number of sectors */
253	movb	%dl, %dh		/*  ... and load it to %dh */
254	movl	$ROOTDIR_ADDR, %ebx
255	call	read_sectors
256next_entry:
257	cmpb	$0, DR_LEN(%bx)
258	jz	last_entry
259	movw	%bx, %si
260	addw	$DR_NAME, %si
261	movb	DR_NAME_LEN(%bx), %cl
262	movw	$str_loader, %di
2631:
264	movb	(%si), %al
265	cmpb	%al, (%di)
266	jnz	fail
267	incw	%si
268	incw	%di
269	decb	%cl
270	jnz	1b
271	jmp	load_loader
272fail:
273	addw	DR_LEN(%bx), %bx
274	jmp	next_entry
275last_entry:
276	movw	$str_no_loader, %si
277	call	message
278	jmp	panic
279
280load_loader:
281	movl	DR_EXTENT(%bx), %eax
282	movl	DR_DATA_LEN(%bx), %edx
283	addl	$(BLOCK_SIZE-1), %edx	/* Convert file length to */
284	shrl	$11, %edx		/*  ... number of sectors */
285	movb	%dl, %dh
286	movl	$LOADER_ADDR, %ebx
287	call	read_sectors
288	movl	$boot_params, %esi	/* Provide boot_params */
289	xorl	%edx, %edx
290	movb	boot_drive, %dl
291	xorl	%ebx, %ebx		/* Zero sector number */
292	lcall	$LOADER_ADDR/16, $0
293	/* fall through on load failure */
294panic:
295	hlt
296	jmp	panic
297
298/*
299 * Read disk sector(s) into memory
300 *
301 * %eax = LBA of starting sector
302 * %ebx = buffer to store sectors
303 * %dh = number of sectors to read
304 */
305read_sectors:
306	pusha
307	movl	%eax, edd_lba		/* Convert LBA to segment */
308	shrl	$4, %ebx
309	movw	%bx, edd_segment
310	movb	%dh, edd_nsecs
311	movb	boot_drive, %dl
312	movw	$edd_packet, %si
313read_again:
314	movb	$0x42, %ah
315	int	$0x13
316	jc	read_fail
317	popa
318	ret
319read_fail:
320	cmpb	$ERROR_TIMEOUT, %ah
321	jz	read_again
322	movw	$str_read_error, %si
323	call	message
324	jmp	panic
325
326/*
327 * For debugging purpose
328 */
329put_char:
330	pusha
331	movb	$0x0e, %ah
332	movw	$0x0001, %bx
333	int	$0x10
334	popa
335	ret
336
337#include <message.S>
338
339edd_packet:
340edd_len:	.word	16
341edd_nsecs:	.word	0		/* Number of sectors to transfer */
342edd_offset:	.word	0
343edd_segment:	.word	0
344edd_lba:	.quad	0
345
346wait_count:	.byte	6
347boot_drive:	.byte	0
348mbr_loaded:	.byte	0
349
350str_banner:	.ascii	"\r\nNetBSD/i386 cd9660 Primary Bootstrap"
351str_crlf:	.asciz	"\r\n"
352str_press_key:	.asciz	"\r\nPress any key to boot from CD"
353str_dot:	.asciz	"."
354str_read_error:	.asciz	"Can't read CD"
355str_no_pvd:	.asciz	"Can't find Primary Volume Descriptor"
356str_no_loader:	.asciz	"Can't find /boot"
357str_loader:	.asciz	"BOOT.;1"
358
359/* Used to calculate free bytes */
360free_space = end - .
361
362	. = start + BLOCK_SIZE
363end:
364