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