cdboot.S revision 1.9 1 /* $NetBSD: cdboot.S,v 1.9 2009/10/24 10:28:30 dsl 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 * TODO:
38 * - Support for loading secondary boot loader > 64kB
39 */
40
41 #include <machine/asm.h>
42 #include <sys/bootblock.h>
43
44 #define BOOT_ADDR 0x7c00
45 #define BLOCK_SIZE 2048 /* Default for ISO 9660 */
46 #define VD_LBA 16 /* LBA of Volume Descriptor (VD) */
47 #define PVD_ADDR 0x1000 /* Where Primary VD is loaded */
48 #define ROOTDIR_ADDR 0x1800 /* Where Root Directory is loaded */
49 #define LOADER_ADDR SECONDARY_LOAD_ADDRESS
50
51 #ifdef BOOT_FROM_FAT
52 #define MBR_AFTERBPB 90 /* BPB size in FAT32 partition BR */
53 #else
54 #define MBR_AFTERBPB 62 /* BPB size in floppy master BR */
55 #endif
56
57 /*
58 * See src/sys/sys/bootblock.h for details.
59 */
60 #define MBR_PART_COUNT 4
61 #define MBR_PART_OFFSET 446
62 #define MBR_PART_SIZE 16 /* sizeof(struct mbr_partition) */
63
64 /*
65 * Disk error codes
66 */
67 #define ERROR_TIMEOUT 0x80
68
69 /*
70 * Volume Descriptor types.
71 */
72 #define VD_PRIMARY 1
73 #define VD_SUPPLEMENTARY 2
74 #define VD_TERMINATOR 255
75
76 /* Only actually used entries are listed below */
77
78 /*
79 * Format of Primary Volume Descriptor (8.4)
80 */
81 #define PVD_ROOT_DR 156 /* Offset of Root Directory Record */
82
83 /*
84 * Format of Directory Record (9.1)
85 */
86 #define DR_LEN 0
87 #define DR_EXTENT 2
88 #define DR_DATA_LEN 10
89 #define DR_NAME_LEN 32
90 #define DR_NAME 33
91
92 .text
93 .code16
94 ENTRY(start)
95 jmp start1
96
97 . = start + MBR_AFTERBPB /* skip BPB */
98 . = start + MBR_DSN_OFFSET
99 .long 0
100
101 /* mbr_bootsel_magic (not used here) */
102 . = start + MBR_BS_MAGIC_OFFSET
103 .word 0
104
105 . = start + MBR_PART_OFFSET
106 . = start + MBR_MAGIC_OFFSET
107 pbr_magic:
108 .word MBR_MAGIC
109 .fill 512 /* reserve space for disklabel */
110 start1:
111 jmp 1f
112 .balign 4
113 .long X86_BOOT_MAGIC_1 /* checked by installboot & pbr code */
114 boot_params: /* space for patchable variables */
115 .long 1f - boot_params /* length of this data area */
116 #include <boot_params.S>
117 . = start1 + 0x80 /* Space for patching unknown params */
118
119 1: xorw %ax, %ax
120 movw %ax, %ds
121 movw %ax, %es
122 movw %ax, %ss
123 movw $BOOT_ADDR, %sp
124 movw %sp, %si
125 movw $start, %di
126 movw $BLOCK_SIZE/2, %cx
127 rep
128 movsw
129 ljmp $0, $real_start
130
131 real_start:
132 movb %dl, boot_drive /* Save boot drive number */
133
134 #ifndef DISABLE_KEYPRESS
135 /*
136 * We can skip boot wait when:
137 * - there's no hard disk present.
138 * - there's no active partition in the MBR of the 1st hard disk.
139 */
140
141 /*
142 * Check presence of hard disks.
143 */
144 movw $0x475, %si
145 movb (%si), %al
146 testb %al, %al
147 jz boot_cdrom
148
149 /*
150 * Find the active partition from the MBR.
151 */
152 movw $0x0201, %ax /* %al = number of sectors to read */
153 movw $BOOT_ADDR, %bx /* %es:%bx = data buffer */
154 movw $0x0001, %cx /* %ch = low 8 bits of cylinder no */
155 /* %cl = high 2 bits of cyl no & */
156 /* sector number */
157 movw $0x0080, %dx /* %dh = head number */
158 /* %dl = disk number */
159 int $0x13 /* Read MBR into memory */
160 jc boot_cdrom /* CF set on error */
161
162 movb $1, mbr_loaded
163 movb $MBR_PART_COUNT, %cl
164 movw $BOOT_ADDR+MBR_PART_OFFSET, %si
165 1:
166 movb (%si), %al
167 testb $0x80, %al
168 jnz found_active
169 addw $MBR_PART_SIZE, %si
170 decb %cl
171 testb %cl, %cl
172 jnz 1b /* If 0, no active partition found */
173 jmp boot_cdrom
174
175 found_active:
176 movw $str_press_key, %si
177 call message
178 next_second:
179 movw $str_dot, %si
180 call message
181 decb wait_count
182 jz boot_hard_disk
183 xorb %ah, %ah /* Get system time */
184 int $0x1a
185 movw %dx, %di /* %cx:%dx = number of clock ticks */
186 addw $19, %di /* 19 ~= 18.2 Hz */
187 wait_key:
188 movb $1, %ah /* Check for keystroke */
189 int $0x16
190 jz not_avail /* ZF clear if keystroke available */
191 xorb %ah, %ah /* Read key to flush keyboard buf */
192 int $0x16
193 jmp boot_cdrom
194 not_avail:
195 xorb %ah, %ah /* Get system time */
196 int $0x1a
197 cmpw %dx, %di /* Compare with saved time */
198 jnz wait_key
199 jmp next_second
200
201 boot_hard_disk:
202 movw $str_crlf, %si
203 call message
204 cmpb $1, mbr_loaded
205 jz 1f
206 movw $0x0201, %ax /* %al = number of sectors to read */
207 movw $BOOT_ADDR, %bx /* %es:%bx = data buffer */
208 movw $0x0001, %cx /* %ch = low 8 bits of cylinder no */
209 /* %cl = high 2 bits of cyl no & */
210 /* sector number */
211 movw $0x0080, %dx /* %dh = head number */
212 /* %dl = disk number */
213 int $0x13 /* Read MBR into memory */
214 jc panic /* CF set on error */
215 1:
216 movw %cs, %ax /* Restore initial state */
217 movw %ax, %ds
218 movw %ax, %es
219 movw $0x0080, %dx /* %dl = boot drive number */
220 jmp $0, $BOOT_ADDR /* Jump to MBR! */
221 jmp panic /* This should be never executed */
222 #endif /* !DISABLE_KEYPRESS */
223
224 boot_cdrom:
225 movw $str_banner, %si
226 call message
227
228 /* Read volume descriptor sectors until Primary decriptor found */
229 movl $VD_LBA, %eax
230 next_block:
231 movb $1, %dh /* Number of sectors to read */
232 movl $PVD_ADDR, %ebx
233 call read_sectors
234 cmpb $VD_PRIMARY, (%bx) /* Is it Primary Volume Descriptor? */
235 jz pvd_found
236 incl %eax
237 cmpb $VD_TERMINATOR, (%bx)
238 jnz next_block
239 movw $str_no_pvd, %si
240 call message
241 jmp panic
242
243 /* Read all of root directory */
244 pvd_found:
245 movw $PVD_ADDR+PVD_ROOT_DR, %bx
246 movl DR_EXTENT(%bx), %eax /* LBA of the root directory */
247 movl DR_DATA_LEN(%bx), %edx
248 shrl $11, %edx /* Convert to number of sectors */
249 movb %dl, %dh /* ... and load it to %dh */
250 movl $ROOTDIR_ADDR, %ebx
251 call read_sectors
252
253 /* Scan directory entries searching for /boot */
254 next_entry:
255 cmpb $0, DR_LEN(%bx)
256 jz last_entry
257 movw %bx, %si
258 addw $DR_NAME, %si
259 movb DR_NAME_LEN(%bx), %cl
260 movw $str_loader, %di
261 1:
262 movb (%si), %al
263 cmpb %al, (%di)
264 jnz fail
265 incw %si
266 incw %di
267 decb %cl
268 jnz 1b
269 jmp load_loader
270 fail:
271 addw DR_LEN(%bx), %bx
272 jmp next_entry
273 last_entry:
274 movw $str_no_loader, %si
275 call message
276 jmp panic
277
278 /* Found /boot, read contents to 0x1000:0 */
279 load_loader:
280 movl DR_EXTENT(%bx), %eax
281 movl DR_DATA_LEN(%bx), %edx
282 addl $(BLOCK_SIZE-1), %edx /* Convert file length to */
283 shrl $11, %edx /* ... number of sectors */
284 movb %dl, %dh
285 movl $LOADER_ADDR, %ebx
286 call read_sectors
287
288 /* Finally call into code of /boot */
289 movl $boot_params, %esi /* Provide boot_params */
290 xorl %edx, %edx
291 movb boot_drive, %dl
292 xorl %ebx, %ebx /* Zero sector number */
293 lcall $LOADER_ADDR/16, $0
294 /* fall through on load failure */
295 panic:
296 hlt
297 jmp panic
298
299 /*
300 * Read disk sector(s) into memory
301 *
302 * %eax = LBA of starting sector
303 * %ebx = buffer to store sectors
304 * %dh = number of sectors to read
305 */
306 read_sectors:
307 pusha
308 movl %eax, edd_lba
309 shrl $4, %ebx /* Convert buffer addr to seg:0 */
310 movw %bx, edd_segment
311 movb %dh, edd_nsecs
312 movb boot_drive, %dl
313 movw $edd_packet, %si
314 read_again:
315 movb $0x42, %ah
316 int $0x13
317 jc read_fail
318 popa
319 ret
320 read_fail:
321 cmpb $ERROR_TIMEOUT, %ah
322 jz read_again
323 movw $str_read_error, %si
324 call message
325 jmp panic
326
327 /*
328 * For debugging purpose
329 */
330 put_char:
331 pusha
332 movb $0x0e, %ah
333 movw $0x0001, %bx
334 int $0x10
335 popa
336 ret
337
338 #include <message.S>
339
340 edd_packet:
341 edd_len: .word 16
342 edd_nsecs: .word 0 /* Number of sectors to transfer */
343 edd_offset: .word 0
344 edd_segment: .word 0
345 edd_lba: .quad 0
346
347 wait_count: .byte 6
348 boot_drive: .byte 0
349 mbr_loaded: .byte 0
350
351 str_banner: .ascii "\r\nNetBSD/x86 cd9660 Primary Bootstrap"
352 str_crlf: .asciz "\r\n"
353 str_press_key: .asciz "\r\nPress any key to boot from CD"
354 str_dot: .asciz "."
355 str_read_error: .asciz "Can't read CD"
356 str_no_pvd: .asciz "Can't find Primary Volume Descriptor"
357 str_no_loader: .asciz "Can't find /boot"
358 str_loader: .asciz "BOOT.;1"
359
360 /* Used to calculate free bytes */
361 free_space = end - .
362
363 . = start + BLOCK_SIZE
364 end:
365