xref: /minix/sys/arch/i386/stand/cdboot/cdboot.S (revision 0a6a1f1d)
1/*	$NetBSD: cdboot.S,v 1.12 2011/01/04 16:53:05 jakllsch 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 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32/*
33 * This is a primary boot loader that loads a secondary boot loader
34 * directly from CD without performing floppy/hard disk emulation as
35 * described by the El Torito specification.
36 */
37
38#include <machine/asm.h>
39#include <sys/bootblock.h>
40
41#define BOOT_ADDR	0x7c00
42#define BLOCK_SIZE	2048		/* Default for ISO 9660 */
43#define VD_LBA		16		/* LBA of Volume Descriptor (VD) */
44#define PVD_ADDR	end		/* Where Primary VD is loaded */
45#define ROOTDIR_ADDR	end+BLOCK_SIZE	/* Where Root Directory is loaded */
46#define LOADER_ADDR	SECONDARY_LOAD_ADDRESS
47
48#ifdef BOOT_FROM_FAT
49#define MBR_AFTERBPB	90		/* BPB size in FAT32 partition BR */
50#else
51#define MBR_AFTERBPB	62		/* BPB size in floppy master BR */
52#endif
53
54/*
55 * See src/sys/sys/bootblock.h for details.
56 */
57#define MBR_PART_COUNT	4
58#define MBR_PART_OFFSET	446
59#define MBR_PART_SIZE	16		/* sizeof(struct mbr_partition) */
60
61/*
62 * Disk error codes
63 */
64#define ERROR_TIMEOUT	0x80
65
66/*
67 * Volume Descriptor types.
68 */
69#define VD_PRIMARY		1
70#define VD_SUPPLEMENTARY	2
71#define VD_TERMINATOR		255
72
73/* Only actually used entries are listed below */
74
75/*
76 * Format of Primary Volume Descriptor (8.4)
77 */
78#define PVD_ROOT_DR	156	/* Offset of Root Directory Record */
79
80/*
81 * Format of Directory Record (9.1)
82 */
83#define DR_LEN		0
84#define DR_EXTENT	2
85#define DR_DATA_LEN	10
86#define DR_NAME_LEN	32
87#define DR_NAME		33
88
89	.text
90	.code16
91ENTRY(start)
92	jmp	start1
93
94	. = start + MBR_AFTERBPB	/* skip BPB */
95	. = start + MBR_DSN_OFFSET
96	.long	0
97
98/* mbr_bootsel_magic (not used here) */
99	. = start + MBR_BS_MAGIC_OFFSET
100	.word	0
101
102	. = start + MBR_PART_OFFSET
103	. = start + MBR_MAGIC_OFFSET
104pbr_magic:
105	.word	MBR_MAGIC
106	.fill	512			/* reserve space for disklabel */
107start1:
108	jmp	1f
109	.balign	4
110	.long	X86_BOOT_MAGIC_1	/* checked by installboot & pbr code */
111boot_params:				/* space for patchable variables */
112	.long	1f - boot_params	/* length of this data area */
113#include <boot_params.S>
114	. = start1 + 0x80		/* Space for patching unknown params */
115
1161:	xorw	%ax, %ax
117	movw	%ax, %ds
118	movw	%ax, %es
119	movw	%ax, %ss
120	movw	$BOOT_ADDR, %sp
121	movw	%sp, %si
122	movw	$start, %di
123	movw	$BLOCK_SIZE/2, %cx
124	rep
125	movsw
126	ljmp	$0, $real_start
127
128real_start:
129	movb	%dl, boot_drive		/* Save boot drive number */
130
131#ifndef DISABLE_KEYPRESS
132	/*
133	 * We can skip boot wait when:
134	 *  - there's no hard disk present.
135	 *  - there's no active partition in the MBR of the 1st hard disk.
136	 */
137
138	/*
139	 * Check presence of hard disks.
140	 */
141	movw	$0x475, %si
142	movb	(%si), %al
143	testb	%al, %al
144	jz	boot_cdrom
145
146	/*
147	 * Find the active partition from the MBR.
148	 */
149	movw	$0x0201, %ax		/* %al = number of sectors to read */
150	movw	$BOOT_ADDR, %bx		/* %es:%bx = data buffer */
151	movw	$0x0001, %cx		/* %ch = low 8 bits of cylinder no */
152					/* %cl = high 2 bits of cyl no & */
153					/*       sector number */
154	movw	$0x0080, %dx		/* %dh = head number */
155					/* %dl = disk number */
156	int	$0x13			/* Read MBR into memory */
157	jc	boot_cdrom		/* CF set on error */
158
159	movb	$1, mbr_loaded
160	movb	$MBR_PART_COUNT, %cl
161	movw	$BOOT_ADDR+MBR_PART_OFFSET, %si
1621:
163	movb	(%si), %al
164	testb	$0x80, %al
165	jnz	found_active
166	addw	$MBR_PART_SIZE, %si
167	decb	%cl
168	testb	%cl, %cl
169	jnz	1b			/* If 0, no active partition found */
170	jmp	boot_cdrom
171
172found_active:
173	movw	$str_press_key, %si
174	call	message
175next_second:
176	movw	$str_dot, %si
177	call	message
178	decb	wait_count
179	jz	boot_hard_disk
180	xorb	%ah, %ah		/* Get system time */
181	int	$0x1a
182	movw	%dx, %di		/* %cx:%dx = number of clock ticks */
183	addw	$19, %di		/* 19 ~= 18.2 Hz */
184wait_key:
185	movb	$1, %ah			/* Check for keystroke */
186	int	$0x16
187	jz	not_avail		/* ZF clear if keystroke available */
188	xorb	%ah, %ah		/* Read key to flush keyboard buf */
189	int	$0x16
190	jmp	boot_cdrom
191not_avail:
192	xorb	%ah, %ah		/* Get system time */
193	int	$0x1a
194	cmpw	%dx, %di		/* Compare with saved time */
195	jnz	wait_key
196	jmp	next_second
197
198boot_hard_disk:
199	movw	$str_crlf, %si
200	call	message
201	cmpb	$1, mbr_loaded
202	jz	1f
203	movw	$0x0201, %ax		/* %al = number of sectors to read */
204	movw	$BOOT_ADDR, %bx		/* %es:%bx = data buffer */
205	movw	$0x0001, %cx		/* %ch = low 8 bits of cylinder no */
206					/* %cl = high 2 bits of cyl no & */
207					/*       sector number */
208	movw	$0x0080, %dx		/* %dh = head number */
209					/* %dl = disk number */
210	int	$0x13			/* Read MBR into memory */
211	jc	panic			/* CF set on error */
2121:
213	movw	%cs, %ax		/* Restore initial state */
214	movw	%ax, %ds
215	movw	%ax, %es
216	movw	$0x0080, %dx		/* %dl = boot drive number */
217	jmp	$0, $BOOT_ADDR		/* Jump to MBR! */
218	jmp	panic			/* This should be never executed */
219#endif /* !DISABLE_KEYPRESS */
220
221boot_cdrom:
222	movw	$str_banner, %si
223	call	message
224
225/* Read volume descriptor sectors until Primary decriptor found */
226	movl	$VD_LBA, %eax
227next_block:
228	movb	$1, %dh			/* Number of sectors to read */
229	movl	$PVD_ADDR, %ebx
230	call	read_sectors
231	cmpb	$VD_PRIMARY, (%bx)	/* Is it Primary Volume Descriptor? */
232	jz	pvd_found
233	incl	%eax
234	cmpb	$VD_TERMINATOR, (%bx)
235	jnz	next_block
236	movw	$str_no_pvd, %si
237	call	message
238	jmp	panic
239
240/* Read all of root directory */
241pvd_found:
242	movw	$PVD_ADDR+PVD_ROOT_DR, %bx
243	movl	DR_EXTENT(%bx), %eax	/* LBA of the root directory */
244	movl	DR_DATA_LEN(%bx), %edx
245	shrl	$11, %edx		/* Convert to number of sectors */
246	movb	%dl, %dh		/*  ... and load it to %dh */
247	movl	$ROOTDIR_ADDR, %ebx
248	call	read_sectors
249
250/* Scan directory entries searching for /boot */
251next_entry:
252	cmpb	$0, DR_LEN(%bx)
253	jz	last_entry
254	movw	%bx, %si
255	addw	$DR_NAME, %si
256	movb	DR_NAME_LEN(%bx), %cl
257	movw	$str_loader, %di
2581:
259	movb	(%si), %al
260	cmpb	%al, (%di)
261	jnz	fail
262	incw	%si
263	incw	%di
264	decb	%cl
265	jnz	1b
266	jmp	load_loader
267fail:
268	addw	DR_LEN(%bx), %bx
269	jmp	next_entry
270last_entry:
271	movw	$str_no_loader, %si
272	call	message
273	jmp	panic
274
275/* Found /boot, read contents to 0x1000:0 */
276load_loader:
277	movl	DR_EXTENT(%bx), %eax
278	movl	DR_DATA_LEN(%bx), %edx
279	addl	$(BLOCK_SIZE-1), %edx	/* Convert file length to */
280	shrl	$11, %edx		/*  ... number of sectors */
281	movb	%dl, %dh
282	movl	$LOADER_ADDR, %ebx
283	call	read_sectors
284
285/* Finally call into code of /boot */
286	movl	$boot_params, %esi	/* Provide boot_params */
287	xorl	%edx, %edx
288	movb	boot_drive, %dl
289	xorl	%ebx, %ebx		/* Zero sector number */
290	lcall	$LOADER_ADDR/16, $0
291	/* fall through on load failure */
292panic:
293	hlt
294	jmp	panic
295
296/*
297 * Read disk sector(s) into memory
298 *
299 * %eax = LBA of starting sector
300 * %ebx = buffer to store sectors
301 * %dh = number of sectors to read
302 *
303 * Long transfers are split onto multiple 64k reads
304 */
305#define MAX_SECTORS (0x10000/BLOCK_SIZE)
306read_sectors:
307	pushal
308	movl	%eax, edd_lba
309	shrl	$4, %ebx		/* Convert buffer addr to seg:0 */
310	movw	%bx, edd_segment
3111:	movb	%dh, edd_nsecs
312	cmpb	$MAX_SECTORS, %dh
313	jle	2f			/* j if less than 64k */
314	movb	$MAX_SECTORS, edd_nsecs	/* Read 32 sectors - 64k bytes */
3152:	movb	boot_drive, %dl
316	movw	$edd_packet, %si
317read_again:
318	movb	$0x42, %ah
319	push	%dx			/* bios shouldn't kill %dh, but ... */
320	int	$0x13
321	pop	%dx			/* ... better safe than sorry! */
322	jc	read_fail
323	addw	$0x1000, edd_segment	/* Advance segment addr by 64k bytes */
324	addl	$MAX_SECTORS, edd_lba	/* And sector number to match */
325	sub	edd_nsecs, %dh		/* Number of sectors remaining */
326	jnz	1b
327	popal
328	ret
329
330read_fail:
331	cmpb	$ERROR_TIMEOUT, %ah
332	jz	read_again
333	movw	$str_read_error, %si
334	call	message
335	jmp	panic
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/x86 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 /minixboot"
357str_loader:	.asciz	"MINIXBOOT.;1"
358
359/* Used to calculate free bytes */
360free_space = end - .
361
362	. = start + BLOCK_SIZE
363end:
364