1 1.24 andvar /* $NetBSD: mbr.S,v 1.24 2023/08/01 21:26:27 andvar Exp $ */ 2 1.1 dsl 3 1.1 dsl /* 4 1.5 lukem * Copyright (c) 1999-2004 The NetBSD Foundation, Inc. 5 1.1 dsl * All rights reserved. 6 1.1 dsl * 7 1.1 dsl * This code is derived from software contributed to The NetBSD Foundation 8 1.3 dsl * by Frank van der Linden, based on an earlier work by Wolfgang Solfrank. 9 1.1 dsl * Major surgery performed by David Laight. 10 1.1 dsl * 11 1.1 dsl * Redistribution and use in source and binary forms, with or without 12 1.1 dsl * modification, are permitted provided that the following conditions 13 1.1 dsl * are met: 14 1.1 dsl * 1. Redistributions of source code must retain the above copyright 15 1.1 dsl * notice, this list of conditions and the following disclaimer. 16 1.1 dsl * 2. Redistributions in binary form must reproduce the above copyright 17 1.1 dsl * notice, this list of conditions and the following disclaimer in the 18 1.1 dsl * documentation and/or other materials provided with the distribution. 19 1.1 dsl * 20 1.3 dsl * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 1.3 dsl * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 1.3 dsl * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 1.3 dsl * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 1.3 dsl * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 1.3 dsl * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 1.3 dsl * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 1.3 dsl * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 1.3 dsl * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 1.3 dsl * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 1.3 dsl * POSSIBILITY OF SUCH DAMAGE. 31 1.1 dsl */ 32 1.1 dsl 33 1.1 dsl /* 34 1.1 dsl * i386 master boot code 35 1.1 dsl */ 36 1.1 dsl 37 1.1 dsl /* Compile options: 38 1.1 dsl * BOOTSEL - bootselector code 39 1.1 dsl * BOOT_EXTENDED - scan extended partition list (LBA reads) 40 1.16 dsl * COM_PORT - do serial io to specified port number 41 1.16 dsl * 0..3 => bios port, otherwise actual io_addr 42 1.16 dsl * COM_BAUD - initialise serial port baud rate 43 1.16 dsl * 44 1.1 dsl * TERSE_ERROR - terse error messages 45 1.1 dsl * NO_CHS - all reads are LBA 46 1.1 dsl * NO_LBA_CHECK - no check if bios supports LBA reads 47 1.16 dsl * NO_BANNER - do not output title line 'banner' 48 1.1 dsl */ 49 1.1 dsl 50 1.7 dsl #ifdef BOOT_EXTENDED 51 1.7 dsl #define NO_CHS 1 52 1.7 dsl #define BOOTSEL 1 53 1.7 dsl #endif 54 1.7 dsl 55 1.16 dsl #ifdef COM_PORT 56 1.16 dsl #if COM_PORT < 4 57 1.16 dsl /* The first 4 items in the 40:xx segment are the serial port base addresses */ 58 1.16 dsl #define COM_PORT_VAL (0x400 + (COM_PORT * 2)) 59 1.16 dsl #else 60 1.16 dsl #define COM_PORT_VAL $COM_PORT 61 1.16 dsl #endif 62 1.16 dsl 63 1.16 dsl #if !defined(COM_FREQ) 64 1.16 dsl #define COM_FREQ 1843200 65 1.16 dsl #endif 66 1.21 dsl #else 67 1.21 dsl #undef COM_BAUD 68 1.16 dsl #endif 69 1.16 dsl 70 1.11 dsl #ifdef BOOTSEL 71 1.11 dsl #define TERSE_ERROR 1 72 1.11 dsl #endif 73 1.11 dsl 74 1.1 dsl #include <machine/asm.h> 75 1.4 lukem #include <sys/bootblock.h> 76 1.1 dsl 77 1.21 dsl #define BOOTADDR 0x7c00 /* where we get loaded to */ 78 1.1 dsl 79 1.4 lukem #define TABENTRYSIZE (MBR_BS_PARTNAMESIZE + 1) 80 1.4 lukem #define NAMETABSIZE (MBR_PART_COUNT * TABENTRYSIZE) 81 1.4 lukem 82 1.16 dsl #ifdef COM_PORT 83 1.16 dsl /* ASCII values for the keys */ 84 1.18 dsl #define KEY_ACTIVE '\r' 85 1.17 dsl #define KEY_DISK1 'a' 86 1.17 dsl #define KEY_PTN1 '1' 87 1.16 dsl #else 88 1.4 lukem /* Scan values for the various keys we use, as returned by the BIOS */ 89 1.4 lukem #define SCAN_ENTER 0x1c 90 1.4 lukem #define SCAN_F1 0x3b 91 1.4 lukem #define SCAN_1 0x2 92 1.1 dsl 93 1.16 dsl #define KEY_ACTIVE SCAN_ENTER 94 1.17 dsl #define KEY_DISK1 SCAN_F1 95 1.17 dsl #define KEY_PTN1 SCAN_1 96 1.16 dsl #endif 97 1.16 dsl 98 1.1 dsl /* 99 1.1 dsl * Minimum and maximum drive number that is considered to be valid. 100 1.1 dsl */ 101 1.1 dsl #define MINDRV 0x80 102 1.8 mycroft #define MAXDRV 0x8f 103 1.1 dsl 104 1.1 dsl #ifdef TERSE_ERROR 105 1.1 dsl /* 106 1.1 dsl * Error codes. Done this way to save space. 107 1.1 dsl */ 108 1.1 dsl #define ERR_INVPART '1' /* Invalid partition table */ 109 1.1 dsl #define ERR_READ '2' /* Read error */ 110 1.1 dsl #define ERR_NOOS '3' /* Magic no. check failed for part. */ 111 1.1 dsl #define ERR_KEY '?' /* unknown key press */ 112 1.1 dsl #define ERR_NO_LBA 'L' /* sector above chs limit */ 113 1.1 dsl 114 1.1 dsl #define set_err(err) movb $err, %al 115 1.1 dsl 116 1.1 dsl #else 117 1.1 dsl #define set_err(err) mov $err, %ax 118 1.1 dsl #endif 119 1.1 dsl 120 1.1 dsl .text 121 1.1 dsl .code16 122 1.1 dsl /* 123 1.1 dsl * Move ourselves out of the way first. 124 1.23 jakllsch * (to the address we are linked at) 125 1.1 dsl * and zero our bss 126 1.1 dsl */ 127 1.1 dsl ENTRY(start) 128 1.1 dsl xor %ax, %ax 129 1.1 dsl mov %ax, %ss 130 1.1 dsl movw $BOOTADDR, %sp 131 1.1 dsl mov %ax, %es 132 1.1 dsl mov %ax, %ds 133 1.14 dsl movw $mbr, %di 134 1.21 dsl mov $BOOTADDR + (mbr - start), %si 135 1.16 dsl push %ax /* zero for %cs of lret */ 136 1.14 dsl push %di 137 1.14 dsl movw $(bss_start - mbr), %cx 138 1.1 dsl rep 139 1.14 dsl movsb /* relocate code */ 140 1.18 dsl mov $(bss_end - bss_start + 511)/512, %ch 141 1.1 dsl rep 142 1.11 dsl stosw /* zero bss */ 143 1.16 dsl lret /* Ensures %cs == 0 */ 144 1.1 dsl 145 1.1 dsl /* 146 1.1 dsl * Sanity check the drive number passed by the BIOS. Some BIOSs may not 147 1.1 dsl * do this and pass garbage. 148 1.1 dsl */ 149 1.1 dsl mbr: 150 1.1 dsl cmpb $MAXDRV, %dl /* relies on MINDRV being 0x80 */ 151 1.1 dsl jle 1f 152 1.1 dsl movb $MINDRV, %dl /* garbage in, boot disk 0 */ 153 1.1 dsl 1: 154 1.1 dsl push %dx /* save drive number */ 155 1.1 dsl push %dx /* twice - for err_msg loop */ 156 1.1 dsl 157 1.21 dsl #if defined(COM_BAUD) 158 1.16 dsl mov $com_args, %si 159 1.16 dsl mov $num_com_args, %cl /* %ch is zero from above */ 160 1.16 dsl mov COM_PORT_VAL, %dx 161 1.16 dsl 1: lodsw 162 1.16 dsl add %ah, %dl 163 1.16 dsl outb %dx 164 1.16 dsl loop 1b 165 1.16 dsl #endif 166 1.16 dsl 167 1.11 dsl #ifndef NO_BANNER 168 1.11 dsl mov $banner, %si 169 1.11 dsl call message_crlf 170 1.11 dsl #endif 171 1.11 dsl 172 1.1 dsl /* 173 1.1 dsl * Walk through the selector (name) table printing used entries. 174 1.16 dsl * 175 1.16 dsl * Register use: 176 1.16 dsl * %ax temp 177 1.24 andvar * %bx nametab[] boot selector menu 178 1.16 dsl * %ecx base of 'extended' partition 179 1.16 dsl * %edx next extended partition 180 1.16 dsl * %si message ptr (etc) 181 1.16 dsl * %edi sector number of this partition 182 1.16 dsl * %bp parttab[] mbr partition table 183 1.1 dsl */ 184 1.1 dsl bootsel_menu: 185 1.1 dsl movw $nametab, %bx 186 1.1 dsl #ifdef BOOT_EXTENDED 187 1.1 dsl xorl %ecx, %ecx /* base of extended partition */ 188 1.1 dsl next_extended: 189 1.1 dsl xorl %edx, %edx /* for next extended partition */ 190 1.1 dsl #endif 191 1.1 dsl lea parttab - nametab(%bx), %bp 192 1.1 dsl next_ptn: 193 1.1 dsl movb 4(%bp), %al /* partition type */ 194 1.7 dsl #ifdef NO_CHS 195 1.7 dsl movl 8(%bp), %edi /* partition sector number */ 196 1.1 dsl #ifdef BOOT_EXTENDED 197 1.1 dsl cmpb $MBR_PTYPE_EXT, %al /* Extended partition */ 198 1.1 dsl je 1f 199 1.1 dsl cmpb $MBR_PTYPE_EXT_LBA, %al /* Extended LBA partition */ 200 1.1 dsl je 1f 201 1.1 dsl cmpb $MBR_PTYPE_EXT_LNX, %al /* Linux extended partition */ 202 1.1 dsl jne 2f 203 1.1 dsl 1: movl %edi, %edx /* save next extended ptn */ 204 1.7 dsl jmp 4f 205 1.1 dsl 2: 206 1.1 dsl #endif 207 1.7 dsl addl lba_sector, %edi /* add in extended ptn base */ 208 1.7 dsl #endif 209 1.1 dsl test %al, %al /* undefined partition */ 210 1.7 dsl je 4f 211 1.7 dsl cmpb $0x80, (%bp) /* check for active partition */ 212 1.7 dsl jne 3f /* jump if not... */ 213 1.16 dsl #define ACTIVE (4 * ((KEY_ACTIVE - KEY_DISK1) & 0xff)) 214 1.7 dsl #ifdef NO_CHS 215 1.16 dsl movl %edi, ptn_list + ACTIVE /* save location of active ptn */ 216 1.7 dsl #else 217 1.16 dsl mov %bp, ptn_list + ACTIVE 218 1.7 dsl #endif 219 1.20 dsl #undef ACTIVE 220 1.7 dsl 3: 221 1.7 dsl #ifdef BOOTSEL 222 1.1 dsl cmpb $0, (%bx) /* check for prompt */ 223 1.7 dsl jz 4f 224 1.1 dsl /* output menu item */ 225 1.1 dsl movw $prefix, %si 226 1.1 dsl incb (%si) 227 1.1 dsl call message /* menu number */ 228 1.1 dsl mov (%si), %si /* ':' << 8 | '1' + count */ 229 1.1 dsl shl $2, %si /* const + count * 4 */ 230 1.16 dsl #define CONST (4 * ((':' << 8) + '1' - ((KEY_PTN1 - KEY_DISK1) & 0xff))) 231 1.1 dsl #ifdef NO_CHS 232 1.1 dsl movl %edi, ptn_list - CONST(%si) /* sector to read */ 233 1.1 dsl #else 234 1.1 dsl mov %bp, ptn_list - CONST(%si) /* partition info */ 235 1.1 dsl #endif 236 1.1 dsl #undef CONST 237 1.1 dsl mov %bx, %si 238 1.11 dsl call message_crlf /* prompt */ 239 1.7 dsl #endif 240 1.7 dsl 4: 241 1.1 dsl add $0x10, %bp 242 1.1 dsl add $TABENTRYSIZE, %bx 243 1.1 dsl cmpb $(nametab - start - 0x100) + 4 * TABENTRYSIZE, %bl 244 1.1 dsl jne next_ptn 245 1.1 dsl 246 1.1 dsl #ifdef BOOT_EXTENDED 247 1.1 dsl /* 248 1.1 dsl * Now check extended partition chain 249 1.1 dsl */ 250 1.1 dsl testl %edx, %edx 251 1.1 dsl je wait_key 252 1.1 dsl testl %ecx, %ecx 253 1.1 dsl jne 1f 254 1.1 dsl xchg %ecx, %edx /* save base of ext ptn chain */ 255 1.1 dsl 1: addl %ecx, %edx /* sector to read */ 256 1.1 dsl movl %edx, lba_sector 257 1.1 dsl movw $lba_info, %si 258 1.1 dsl movb $0x42, %ah 259 1.1 dsl pop %dx /* recover drive # */ 260 1.1 dsl push %dx /* save drive */ 261 1.1 dsl int $0x13 262 1.21 dsl movw $BOOTADDR + (nametab - start), %bx 263 1.15 dsl jnc next_extended /* abort menu on read fail */ 264 1.1 dsl #endif 265 1.1 dsl 266 1.1 dsl /* 267 1.10 dsl * The non-bootsel code traverses this code path, it needs the 268 1.10 dsl * correct keycode to select the active partition. 269 1.7 dsl */ 270 1.7 dsl 271 1.7 dsl #ifndef BOOTSEL 272 1.16 dsl mov $(KEY_ACTIVE - KEY_DISK1) & 0xff, %ax 273 1.7 dsl #else 274 1.7 dsl /* 275 1.1 dsl * Get the initial time value for the timeout comparison. It is returned 276 1.1 dsl * by int 1a in cx:dx. We do sums modulo 2^16 so it doesn't matter if 277 1.1 dsl * the counter wraps (which it does every hour) - so we can safely 278 1.1 dsl * ignore 'cx'. 279 1.1 dsl * 280 1.1 dsl * Loop around checking for a keypress until we have one, or timeout is 281 1.1 dsl * reached. 282 1.1 dsl */ 283 1.1 dsl wait_key: 284 1.1 dsl xorb %ah, %ah 285 1.1 dsl int $0x1a 286 1.1 dsl mov %dx, %di /* start time to di */ 287 1.1 dsl 3: 288 1.21 dsl #ifdef COM_PORT_VAL 289 1.16 dsl mov COM_PORT_VAL, %dx 290 1.16 dsl push %dx 291 1.16 dsl add $5, %dx 292 1.16 dsl inb %dx 293 1.16 dsl pop %dx 294 1.16 dsl test $1, %al 295 1.16 dsl jz 1f 296 1.16 dsl inb %dx 297 1.16 dsl jmp check_key 298 1.16 dsl #else 299 1.1 dsl movb $1, %ah /* looks to see if a */ 300 1.1 dsl int $0x16 /* key has been pressed */ 301 1.16 dsl jz 1f 302 1.16 dsl get_key: 303 1.1 dsl xorb %ah, %ah 304 1.16 dsl int $0x16 /* 'read key', code ah, ascii al */ 305 1.16 dsl shr $8, %ax /* code in %al, %ah zero */ 306 1.16 dsl jmp check_key 307 1.16 dsl #endif 308 1.16 dsl 309 1.16 dsl 1: xorb %ah, %ah 310 1.1 dsl int $0x1a /* current time to cx:dx */ 311 1.1 dsl sub %di, %dx 312 1.9 dsl cmpw timeout, %dx /* always wait for 1 tick... */ 313 1.1 dsl jbe 3b /* 0xffff means never timeout */ 314 1.1 dsl def_key: 315 1.18 dsl mov defkey, %al /* timedout - we need %ah to still be zero! */ 316 1.1 dsl 317 1.1 dsl /* 318 1.1 dsl * We have a keycode, see what it means. 319 1.1 dsl * If we don't know we generate error '?' and go ask again 320 1.1 dsl */ 321 1.1 dsl check_key: 322 1.1 dsl /* 323 1.1 dsl * F1-F10 -> boot disk 0-9. Check if the requested disk isn't above 324 1.1 dsl * the number of disks actually in the system as stored in 0:0475 by 325 1.1 dsl * the BIOS. 326 1.1 dsl * If we trust loc 475, we needn't check the upper bound on the keystroke 327 1.1 dsl * This is always sector 0, so always read using chs. 328 1.1 dsl */ 329 1.16 dsl subb $KEY_DISK1, %al 330 1.1 dsl cmpb 0x0475, %al 331 1.1 dsl jae boot_ptn 332 1.1 dsl addb $0x80, %al 333 1.1 dsl pop %dx /* dump saved drive # */ 334 1.1 dsl push %ax /* replace with new */ 335 1.1 dsl #ifdef NO_CHS 336 1.1 dsl xorl %ebp, %ebp /* read sector number 0 */ 337 1.1 dsl jmp boot_lba 338 1.1 dsl #else 339 1.1 dsl movw $chs_zero, %si /* chs read sector zero info */ 340 1.1 dsl jmp read_chs 341 1.1 dsl #endif 342 1.7 dsl #endif /* BOOTSEL */ 343 1.1 dsl 344 1.1 dsl /* 345 1.1 dsl * Boot requested partition. 346 1.1 dsl * Use keycode to index the table we generated when we scanned the mbr 347 1.1 dsl * while generating the menu. 348 1.1 dsl * 349 1.1 dsl * We very carfully saved the values in the correct part of the table. 350 1.1 dsl */ 351 1.1 dsl 352 1.1 dsl boot_ptn: 353 1.1 dsl shl $2, %ax 354 1.1 dsl movw %ax, %si 355 1.1 dsl #ifdef NO_CHS 356 1.1 dsl movl ptn_list(%si), %ebp 357 1.1 dsl testl %ebp, %ebp 358 1.1 dsl jnz boot_lba 359 1.1 dsl #else 360 1.1 dsl mov ptn_list(%si), %si 361 1.1 dsl test %si, %si 362 1.1 dsl jnz boot_si 363 1.1 dsl #endif 364 1.7 dsl #ifdef BOOTSEL 365 1.1 dsl set_err(ERR_KEY) 366 1.7 dsl #else 367 1.7 dsl set_err(ERR_INVPART) 368 1.7 dsl #endif 369 1.1 dsl /* jmp err_msg */ 370 1.1 dsl 371 1.1 dsl /* Something went wrong... 372 1.1 dsl * Output error code, 373 1.1 dsl * reset disk subsystem - needed after read failure, 374 1.1 dsl * and wait for user key 375 1.1 dsl */ 376 1.1 dsl err_msg: 377 1.1 dsl #ifdef TERSE_ERROR 378 1.1 dsl movb %al, errcod 379 1.1 dsl movw $errtxt, %si 380 1.1 dsl call message 381 1.1 dsl #else 382 1.16 dsl push %ax 383 1.1 dsl movw $errtxt, %si 384 1.1 dsl call message 385 1.16 dsl pop %si 386 1.11 dsl call message_crlf 387 1.1 dsl #endif 388 1.1 dsl pop %dx /* drive we errored on */ 389 1.1 dsl xor %ax,%ax /* only need %ah = 0 */ 390 1.1 dsl int $0x13 /* reset disk subsystem */ 391 1.1 dsl #ifdef BOOTSEL 392 1.1 dsl pop %dx /* original drive number */ 393 1.1 dsl push %dx 394 1.1 dsl push %dx 395 1.21 dsl #ifdef COM_PORT_VAL 396 1.16 dsl jmp wait_key /* Read with timeout (again) */ 397 1.16 dsl #else 398 1.16 dsl jmp get_key /* Blocking read */ 399 1.16 dsl #endif 400 1.1 dsl #else 401 1.1 dsl int $0x18 /* BIOS might ask for a key */ 402 1.1 dsl /* press and retry boot seq. */ 403 1.1 dsl 1: sti 404 1.1 dsl hlt 405 1.1 dsl jmp 1b 406 1.1 dsl #endif 407 1.1 dsl 408 1.1 dsl #ifndef NO_CHS 409 1.1 dsl /* 410 1.1 dsl * Active partition pointed to by si. 411 1.1 dsl * Read the first sector. 412 1.1 dsl * 413 1.1 dsl * We can either do a CHS (Cylinder Head Sector) or an LBA (Logical 414 1.1 dsl * Block Address) read. Always doing the LBA one 415 1.1 dsl * would be nice - unfortunately not all systems support it. 416 1.1 dsl * Also some may contain a separate (eg SCSI) bios that doesn't 417 1.1 dsl * support it even when the main bios does. 418 1.1 dsl * 419 1.1 dsl * There is also the additional problem that the CHS values may be wrong 420 1.1 dsl * (eg if fdisk was run on a different system that used different BIOS 421 1.1 dsl * geometry). We convert the CHS value to a LBA sector number using 422 1.1 dsl * the geometry from the BIOS, if the number matches we do a CHS read. 423 1.1 dsl */ 424 1.1 dsl boot_si: 425 1.1 dsl movl 8(%si), %ebp /* get sector # */ 426 1.1 dsl 427 1.4 lukem testb $MBR_BS_READ_LBA, flags 428 1.1 dsl jnz boot_lba /* fdisk forced LBA read */ 429 1.1 dsl 430 1.1 dsl pop %dx /* collect saved drive... */ 431 1.1 dsl push %dx /* ...number to dl */ 432 1.1 dsl movb $8, %ah 433 1.1 dsl int $0x13 /* chs info */ 434 1.1 dsl 435 1.1 dsl /* 436 1.1 dsl * Validate geometry, if the CHS sector number doesn't match the LBA one 437 1.1 dsl * we'll do an LBA read. 438 1.1 dsl * calc: (cylinder * number_of_heads + head) * number_of_sectors + sector 439 1.1 dsl * and compare against LBA sector number. 440 1.1 dsl * Take a slight 'flier' and assume we can just check 16bits (very likely 441 1.1 dsl * to be true because the number of sectors per track is 63). 442 1.1 dsl */ 443 1.1 dsl movw 2(%si), %ax /* cylinder + sector */ 444 1.1 dsl push %ax /* save for sector */ 445 1.1 dsl shr $6, %al 446 1.1 dsl xchgb %al, %ah /* 10 bit cylinder number */ 447 1.1 dsl shr $8, %dx /* last head */ 448 1.1 dsl inc %dx /* number of heads */ 449 1.1 dsl mul %dx 450 1.1 dsl mov 1(%si), %dl /* head we want */ 451 1.1 dsl add %dx, %ax 452 1.1 dsl and $0x3f, %cx /* number of sectors */ 453 1.1 dsl mul %cx 454 1.1 dsl pop %dx /* recover sector we want */ 455 1.1 dsl and $0x3f, %dx 456 1.1 dsl add %dx, %ax 457 1.1 dsl dec %ax 458 1.1 dsl 459 1.1 dsl cmp %bp, %ax 460 1.1 dsl je read_chs 461 1.1 dsl 462 1.1 dsl #ifndef NO_LBA_CHECK 463 1.1 dsl /* 464 1.1 dsl * Determine whether we have int13-extensions, by calling int 13, function 41. 465 1.1 dsl * Check for the magic number returned, and the disk packet capability. 466 1.1 dsl */ 467 1.1 dsl movw $0x55aa, %bx 468 1.1 dsl movb $0x41, %ah 469 1.1 dsl pop %dx 470 1.1 dsl push %dx 471 1.1 dsl int $0x13 472 1.11 dsl set_err(ERR_NO_LBA) 473 1.11 dsl jc err_msg /* no int13 extensions */ 474 1.1 dsl cmpw $0xaa55, %bx 475 1.11 dsl jnz err_msg 476 1.1 dsl testb $1, %cl 477 1.11 dsl jz err_msg 478 1.1 dsl #endif /* NO_LBA_CHECK */ 479 1.1 dsl #endif /* NO_CHS */ 480 1.1 dsl 481 1.1 dsl /* 482 1.1 dsl * Save sector number (passed in %ebp) into lba parameter block, 483 1.1 dsl * read the sector and leap into it. 484 1.1 dsl */ 485 1.1 dsl boot_lba: 486 1.1 dsl movl %ebp, lba_sector /* save sector number */ 487 1.1 dsl movw $lba_info, %si 488 1.1 dsl movb $0x42, %ah 489 1.1 dsl pop %dx /* recover drive # */ 490 1.1 dsl do_read: 491 1.1 dsl push %dx /* save drive */ 492 1.1 dsl int $0x13 493 1.1 dsl 494 1.1 dsl set_err(ERR_READ) 495 1.1 dsl jc err_msg 496 1.1 dsl 497 1.1 dsl /* 498 1.1 dsl * Check signature for valid bootcode 499 1.1 dsl */ 500 1.1 dsl movb BOOTADDR, %al /* first byte non-zero */ 501 1.1 dsl test %al, %al 502 1.1 dsl jz 1f 503 1.4 lukem movw BOOTADDR + MBR_MAGIC_OFFSET, %ax 504 1.1 dsl 1: cmp $MBR_MAGIC, %ax 505 1.1 dsl set_err(ERR_NOOS) 506 1.1 dsl jnz err_msg 507 1.1 dsl 508 1.1 dsl /* We pass the sector number through to the next stage boot. 509 1.1 dsl * It doesn't have to use it (indeed no other mbr code will generate) it, 510 1.1 dsl * but it does let us have a NetBSD pbr that can identify where it was 511 1.1 dsl * read from! This lets us use this code to select between two 512 1.1 dsl * NetBSD system on the same physical driver. 513 1.1 dsl * (If we've read the mbr of a different disk, it gets a random number 514 1.1 dsl * - but it wasn't expecting anything...) 515 1.1 dsl */ 516 1.1 dsl movl %ebp, %esi 517 1.1 dsl pop %dx /* recover drive # */ 518 1.21 dsl jmp BOOTADDR 519 1.1 dsl 520 1.1 dsl 521 1.1 dsl #ifndef NO_CHS 522 1.1 dsl /* 523 1.1 dsl * Sector below CHS limit 524 1.1 dsl * Do a cylinder-head-sector read instead. 525 1.1 dsl */ 526 1.1 dsl read_chs: 527 1.1 dsl pop %dx /* recover drive # */ 528 1.1 dsl movb 1(%si), %dh /* head */ 529 1.1 dsl movw 2(%si), %cx /* ch=cyl, cl=sect */ 530 1.1 dsl movw $BOOTADDR, %bx /* es:bx is buffer */ 531 1.1 dsl movw $0x201, %ax /* command 2, 1 sector */ 532 1.1 dsl jmp do_read 533 1.1 dsl #endif 534 1.1 dsl 535 1.1 dsl /* 536 1.1 dsl * Control block for int-13 LBA read. 537 1.1 dsl * We need a xx, 00, 01, 00 somewhere to load chs for sector zero, 538 1.1 dsl * by a complete fluke there is one here! 539 1.1 dsl */ 540 1.1 dsl chs_zero: 541 1.1 dsl lba_info: 542 1.1 dsl .word 0x10 /* control block length */ 543 1.1 dsl .word 1 /* sector count */ 544 1.1 dsl .word BOOTADDR /* offset in segment */ 545 1.1 dsl .word 0 /* segment */ 546 1.1 dsl lba_sector: 547 1.1 dsl .long 0x0000 /* sector # goes here... */ 548 1.1 dsl .long 0x0000 549 1.1 dsl 550 1.1 dsl errtxt: .ascii "Error " /* runs into crlf if errcod set */ 551 1.1 dsl errcod: .byte 0 552 1.1 dsl crlf: .asciz "\r\n" 553 1.1 dsl 554 1.11 dsl #ifndef NO_BANNER 555 1.11 dsl #ifdef BOOTSEL 556 1.21 dsl #ifdef COM_PORT_VAL 557 1.16 dsl banner: .asciz "a: disk" 558 1.16 dsl #else 559 1.11 dsl banner: .asciz "Fn: diskn" 560 1.16 dsl #endif 561 1.11 dsl #else 562 1.11 dsl banner: .asciz "NetBSD MBR boot" 563 1.11 dsl #endif 564 1.11 dsl #endif 565 1.11 dsl 566 1.1 dsl #ifdef BOOTSEL 567 1.1 dsl prefix: .asciz "0: " 568 1.1 dsl #endif 569 1.1 dsl 570 1.1 dsl #ifndef TERSE_ERROR 571 1.1 dsl ERR_INVPART: .asciz "No active partition" 572 1.1 dsl ERR_READ: .asciz "Disk read error" 573 1.1 dsl ERR_NOOS: .asciz "No operating system" 574 1.1 dsl #ifndef NO_LBA_CHECK 575 1.1 dsl ERR_NO_LBA: .asciz "Invalid CHS read" 576 1.1 dsl #endif 577 1.1 dsl #ifdef BOOTSEL 578 1.1 dsl ERR_KEY: .asciz "bad key" 579 1.1 dsl #endif 580 1.1 dsl #endif 581 1.1 dsl 582 1.21 dsl #if defined(COM_BAUD) 583 1.16 dsl #define COM_DIVISOR (((COM_FREQ / COM_BAUD) + 8) / 16) 584 1.16 dsl com_args: 585 1.16 dsl .byte 0x80 /* divisor latch enable */ 586 1.16 dsl .byte +3 /* io_port + 3 */ 587 1.16 dsl .byte COM_DIVISOR & 0xff 588 1.16 dsl .byte -3 /* io_port */ 589 1.16 dsl .byte COM_DIVISOR >> 8 /* high baud */ 590 1.16 dsl .byte +1 /* io_port + 1 */ 591 1.16 dsl .byte 0x03 /* 8 bit no parity */ 592 1.16 dsl .byte +2 /* io_port + 3 */ 593 1.16 dsl num_com_args = (. - com_args)/2 594 1.16 dsl #endif 595 1.16 dsl 596 1.1 dsl /* 597 1.1 dsl * I hate #including source files, but the stuff below has to be at 598 1.1 dsl * the correct absolute address. 599 1.1 dsl * Clearly this could be done with a linker script. 600 1.1 dsl */ 601 1.1 dsl 602 1.11 dsl message_crlf: 603 1.11 dsl call message 604 1.11 dsl movw $crlf, %si 605 1.1 dsl #include <message.S> 606 1.1 dsl #if 0 607 1.1 dsl #include <dump_eax.S> 608 1.1 dsl #endif 609 1.1 dsl 610 1.1 dsl /* 611 1.1 dsl * Stuff from here on is overwritten by fdisk - the offset must not change... 612 1.1 dsl * 613 1.1 dsl * Get amount of space to makefile can report it. 614 1.1 dsl * (Unfortunately I can't seem to get the value reported when it is -ve) 615 1.1 dsl */ 616 1.1 dsl mbr_space = defkey - . 617 1.5 lukem . = start + MBR_BS_OFFSET 618 1.1 dsl /* 619 1.1 dsl * Default action, as a keyvalue we'd normally read from the BIOS. 620 1.1 dsl */ 621 1.1 dsl defkey: 622 1.16 dsl .byte KEY_ACTIVE /* ps/2 code */ 623 1.2 dsl #ifndef BOOTSEL_FLAGS 624 1.2 dsl #define BOOTSEL_FLAGS 0 625 1.2 dsl #endif 626 1.4 lukem flags: .byte MBR_BS_NEWMBR | BOOTSEL_FLAGS 627 1.1 dsl /* 628 1.1 dsl * Timeout value. ~65536 ticks per hour, which is ~18.2 times per second. 629 1.1 dsl * 0xffff means never timeout. 630 1.1 dsl */ 631 1.1 dsl timeout: 632 1.1 dsl .word 182 /* default to 10 seconds */ 633 1.1 dsl /* 634 1.5 lukem * mbr_bootsel 635 1.1 dsl */ 636 1.1 dsl nametab: 637 1.4 lukem .fill MBR_PART_COUNT * (MBR_BS_PARTNAMESIZE + 1), 0x01, 0x00 638 1.1 dsl 639 1.5 lukem /* space for mbr_dsn */ 640 1.5 lukem . = start + MBR_DSN_OFFSET 641 1.5 lukem .long 0 642 1.5 lukem 643 1.5 lukem /* mbr_bootsel_magic */ 644 1.5 lukem . = start + MBR_BS_MAGIC_OFFSET 645 1.5 lukem .word MBR_BS_MAGIC 646 1.1 dsl 647 1.5 lukem /* 648 1.5 lukem * MBR partition table 649 1.5 lukem */ 650 1.4 lukem . = start + MBR_PART_OFFSET 651 1.1 dsl parttab: 652 1.1 dsl .fill 0x40, 0x01, 0x00 653 1.1 dsl 654 1.4 lukem . = start + MBR_MAGIC_OFFSET 655 1.1 dsl .word MBR_MAGIC 656 1.1 dsl 657 1.1 dsl /* zeroed data space */ 658 1.1 dsl bss_off = 0 659 1.1 dsl bss_start = . 660 1.1 dsl #define BSS(name, size) name = bss_start + bss_off; bss_off = bss_off + size 661 1.1 dsl BSS(ptn_list, 256 * 4) /* long[]: boot sector numbers */ 662 1.16 dsl BSS(dump_eax_buff, 16) 663 1.1 dsl BSS(bss_end, 0) 664