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