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