cdboot.S revision 1.13 1 /* $NetBSD: cdboot.S,v 1.13 2021/12/05 02:47:01 msaitoh 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
91 ENTRY(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
104 pbr_magic:
105 .word MBR_MAGIC
106 .fill 512 /* reserve space for disklabel */
107 start1:
108 jmp 1f
109 .balign 4
110 .long X86_BOOT_MAGIC_1 /* checked by installboot & pbr code */
111 boot_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
116 1: 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
128 real_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
162 1:
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
172 found_active:
173 movw $str_press_key, %si
174 call message
175 next_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 */
184 wait_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
191 not_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
198 boot_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 */
212 1:
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
221 boot_cdrom:
222 movw $str_banner, %si
223 call message
224
225 /* Read volume descriptor sectors until Primary descriptor found */
226 movl $VD_LBA, %eax
227 next_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 */
241 pvd_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 */
251 next_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
258 1:
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
267 fail:
268 addw DR_LEN(%bx), %bx
269 jmp next_entry
270 last_entry:
271 movw $str_no_loader, %si
272 call message
273 jmp panic
274
275 /* Found /boot, read contents to 0x1000:0 */
276 load_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 */
292 panic:
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)
306 read_sectors:
307 pushal
308 movl %eax, edd_lba
309 shrl $4, %ebx /* Convert buffer addr to seg:0 */
310 movw %bx, edd_segment
311 1: 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 */
315 2: movb boot_drive, %dl
316 movw $edd_packet, %si
317 read_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
330 read_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
339 edd_packet:
340 edd_len: .word 16
341 edd_nsecs: .word 0 /* Number of sectors to transfer */
342 edd_offset: .word 0
343 edd_segment: .word 0
344 edd_lba: .quad 0
345
346 wait_count: .byte 6
347 boot_drive: .byte 0
348 mbr_loaded: .byte 0
349
350 str_banner: .ascii "\r\nNetBSD/x86 cd9660 Primary Bootstrap"
351 str_crlf: .asciz "\r\n"
352 str_press_key: .asciz "\r\nPress any key to boot from CD"
353 str_dot: .asciz "."
354 str_read_error: .asciz "Can't read CD"
355 str_no_pvd: .asciz "Can't find Primary Volume Descriptor"
356 str_no_loader: .asciz "Can't find /boot"
357 str_loader: .asciz "BOOT.;1"
358
359 /* Used to calculate free bytes */
360 free_space = end - .
361
362 . = start + BLOCK_SIZE
363 end:
364