boot0.S revision 128723
1/* 2 * Copyright (c) 2002 Bruce M. Simpson 3 * Copyright (c) 1998 Robert Nordier 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms are freely 7 * permitted provided that the above copyright notice and this 8 * paragraph and the following disclaimer are duplicated in all 9 * such forms. 10 * 11 * This software is provided "AS IS" and without any express or 12 * implied warranties, including, without limitation, the implied 13 * warranties of merchantability and fitness for a particular 14 * purpose. 15 * 16 * $FreeBSD: head/sys/boot/i386/boot0/boot0.S 128723 2004-04-28 20:55:01Z ru $ 17 */ 18 19/* A 512-byte boot manager. */ 20#ifdef SIO 21/* ... using a serial console on COM1. */ 22#endif /* SIO */ 23 24 .set NHRDRV,0x475 // Number of hard drives 25 .set ORIGIN,0x600 // Execution address 26 .set FAKE,0x800 // Partition entry 27 .set LOAD,0x7c00 // Load address 28 29 .set PRT_OFF,0x1be // Partition table 30 31 .set TBL0SZ,0x3 // Table 0 size 32 .set TBL1SZ,0xb // Table 1 size 33 34 .set MAGIC,0xaa55 // Magic: bootable 35 .set B0MAGIC,0xbb66 // Identification 36 37 .set KEY_ENTER,0x1c // Enter key scan code 38 .set KEY_F1,0x3b // F1 key scan code 39 .set KEY_1,0x02 // #1 key scan code 40 41 .set ASCII_BEL,0x07 // ASCII code for <BEL> 42 .set ASCII_CR,0x0D // ASCII code for <CR> 43 44/* 45 * Addresses in the sector of embedded data values. 46 * Accessed with negative offsets from the end of the relocated sector (%ebp). 47 */ 48 .set _NXTDRV,-0x48 // Next drive 49 .set _OPT,-0x47 // Default option 50 .set _SETDRV,-0x46 // Drive to force 51 .set _FLAGS,-0x45 // Flags 52 .set _TICKS,-0x44 // Timeout ticks 53 .set _FAKE,0x0 // Fake partition entry 54 .set _MNUOPT,0xc // Menu options 55 56 .globl start // Entry point 57 .code16 // This runs in real mode 58 59/* 60 * Initialise segments and registers to known values. 61 * segments start at 0. 62 * The stack is immediately below the address we were loaded to. 63 */ 64start: cld // String ops inc 65 xorw %ax,%ax // Zero 66 movw %ax,%es // Address 67 movw %ax,%ds // data 68 movw %ax,%ss // Set up 69 movw $LOAD,%sp // stack 70 71/* 72 * Copy this code to the address it was linked for 73 */ 74 movw %sp,%si // Source 75 movw $start,%di // Destination 76 movw $0x100,%cx // Word count 77 rep // Relocate 78 movsw // code 79/* 80 * Set address for variable space beyond code, and clear it. 81 * Notice that this is also used to point to the values embedded in the block, 82 * by using negative offsets. 83 */ 84 movw %di,%bp // Address variables 85 movb $0x8,%cl // Words to clear 86 rep // Zero 87 stosw // them 88/* 89 * Relocate to the new copy of the code. 90 */ 91 incb -0xe(%di) // Sector number 92 jmp main-LOAD+ORIGIN // To relocated code 93 94main: 95#ifdef SIO 96/* 97 * Initialize the serial port. 98 * Must save DX (contains drive number) 99 */ 100 pushw %dx // Save 101 xorw %dx,%dx // Port: COM1 102 movb COMSPEED,%al // defined by Makefile 103 movb $0x00,%ah // BIOS: Set COM Port 104 int $0x14 // Parameters 105 popw %dx // Restore 106#endif /* SIO */ 107/* 108 * Check what flags were loaded with us, specifically, Use a predefined Drive. 109 * If what the bios gives us is bad, use the '0' in the block instead, as well. 110 */ 111 testb $0x20,_FLAGS(%bp) // Set number drive? 112 jnz main.1 // Yes 113 testb %dl,%dl // Drive number valid? 114 js main.2 // Possibly (0x80 set) 115main.1: movb _SETDRV(%bp),%dl // Drive number to use 116/* 117 * Whatever we decided to use, now store it into the fake 118 * partition entry that lives in the data space above us. 119 */ 120main.2: movb %dl,_FAKE(%bp) // Save drive number 121 callw putn // To new line 122 pushw %dx // Save drive number 123/* 124 * Start out with a pointer to the 4th byte of the first table entry 125 * so that after 4 iterations it's beyond the end of the sector. 126 * and beyond a 256 byte boundary and has overflowed 8 bits (see next comment). 127 * (remember that the table starts 2 bytes earlier than you would expect 128 * as the bootable flag is after it in the block) 129 */ 130 movw $(partbl+0x4),%bx // Partition table (+4) 131 xorw %dx,%dx // Item number 132/* 133 * Loop around on the partition table, printing values until we 134 * pass a 256 byte boundary. The end of loop test is at main.5. 135 */ 136main.3: movb %ch,-0x4(%bx) // Zero active flag (ch == 0) 137 btw %dx,_FLAGS(%bp) // Entry enabled? 138 jnc main.5 // No 139/* 140 * If any of the entries in the table are 141 * the same as the 'type' in the slice table entry, 142 * then this is an empty or non bootable partition. Skip it. 143 */ 144 movb (%bx),%al // Load type 145 movw $tables,%di // Lookup tables 146 movb $TBL0SZ,%cl // Number of entries 147 repne // Exclude 148 scasb // partition? 149 je main.5 // Yes 150/* 151 * Now scan the table of known types 152 */ 153 movb $TBL1SZ,%cl // Number of entries 154 repne // Known 155 scasb // type? 156 jne main.4 // No 157/* 158 * If it matches get the matching element in the 159 * next array. if it doesn't, we are already 160 * pointing at its first element which points to a "?". 161 */ 162 addw $TBL1SZ,%di // Adjust 163main.4: movb (%di),%cl // Partition 164 addw %cx,%di // description 165 callw putx // Display it 166main.5: incw %dx // Next item 167 addb $0x10,%bl // Next entry 168 jnc main.3 // Till done 169/* 170 * Passed a 256 byte boundary.. 171 * table is finished. 172 * Add one to the drive number and check it is valid, 173 */ 174 popw %ax // Drive number 175 subb $0x80-0x1,%al // Does next 176 cmpb NHRDRV,%al // drive exist? (from BIOS?) 177 jb main.6 // Yes 178/* 179 * If not then if there is only one drive, 180 * Don't display drive as an option. 181 */ 182 decw %ax // Already drive 0? 183 jz main.7 // Yes 184/* 185 * If it was illegal or we cycled through them, 186 * then go back to drive 0. 187 */ 188 xorb %al,%al // Drive 0 189/* 190 * Whatever drive we selected, make it an ascii digit and save it back 191 * to the "next drive" location in the loaded block in case we 192 * want to save it for next time. 193 * This also is part of the printed drive string so add 0x80 to indicate 194 * end of string. 195 */ 196main.6: addb $'0'|0x80,%al // Save next 197 movb %al,_NXTDRV(%bp) // drive number 198 movw $drive,%di // Display 199 callw putx // item 200/* 201 * Now that we've printed the drive (if we needed to), display a prompt. 202 * Get ready for the input by noting the time. 203 */ 204main.7: movw $prompt,%si // Display 205 callw putstr // prompt 206 movb _OPT(%bp),%dl // Display 207 decw %si // default 208 callw putkey // key 209main.7_1: 210 xorb %ah,%ah // BIOS: Get 211 int $0x1a // system time 212#ifndef SIO 213 movw %dx,%di // Ticks when 214 addw _TICKS(%bp),%di // timeout 215#else /* SIO */ 216 movw %dx,%si // Ticks when 217 addw _TICKS(%bp),%si // timeout 218#endif /* SIO */ 219/* 220 * Busy loop, looking for keystrokes but 221 * keeping one eye on the time. 222 */ 223main.8: 224#ifndef SIO 225 movb $0x1,%ah // BIOS: Check 226 int $0x16 // for keypress 227 jnz main.11 // Have one 228#else /* SIO */ 229 xorw %dx,%dx // Use COM1 230 movb $0x03,%ah // BIOS: Read COM 231 int $0x14 // Status 232 testb $0x01,%ah // Check line status 233 jnz main.11 // (bit 1 indicates input) 234#endif /* SIO */ 235 xorb %ah,%ah // BIOS: Get 236 int $0x1a // system time 237#ifndef SIO 238 cmpw %di,%dx // Timeout? 239#else /* SIO */ 240 cmpw %si,%dx // Timeout? 241#endif /* SIO */ 242 jb main.8 // No 243/* 244 * If timed out or defaulting, come here. 245 */ 246main.9: movb _OPT(%bp),%al // Load default 247 jmp main.12 // Join common code 248/* 249 * User's last try was bad, beep in displeasure. 250 * Since nothing was printed, just continue on as if the user 251 * hadn't done anything. This gives the effect of the user getting a beep 252 * for all bad keystrokes but no action until either the timeout 253 * occurs or the user hits a good key. 254 */ 255main.10: movb $ASCII_BEL,%al // Signal 256 callw putchr // error 257#ifdef SIO 258 jmp main.7_1 // Go back 259#endif /* SIO */ 260/* 261 * Get the keystroke. 262 */ 263main.11: 264#ifndef SIO 265 xorb %ah,%ah // BIOS: Get 266 int $0x16 // keypress 267 movb %ah,%al // Scan code 268#else /* SIO */ 269 movb $0x02,%ah // BIOS: Receive 270 int $0x14 // COM Byte 271#endif /* SIO */ 272/* 273 * If it's CR act as if timed out. 274 */ 275#ifndef SIO 276 cmpb $KEY_ENTER,%al // Enter pressed? 277#else /* SIO */ 278 cmpb $ASCII_CR,%al // Enter pressed? 279#endif /* SIO */ 280 je main.9 // Yes 281/* 282 * Otherwise check if legal 283 * If not ask again. 284 */ 285#ifndef SIO 286 subb $KEY_F1,%al // Less F1 scan code 287 cmpb $0x4,%al // F1..F5? 288 jna main.12 // Yes 289 subb $(KEY_1 - KEY_F1),%al // Less #1 scan code 290#else /* SIO */ 291 subb $'1',%al // Less '1' ascii character 292#endif /* SIO */ 293 cmpb $0x4,%al // #1..#5? 294 ja main.10 // No 295/* 296 * We have a selection. 297 * but if it's a bad selection go back to complain. 298 * The bits in MNUOPT were set when the options were printed. 299 * Anything not printed is not an option. 300 */ 301main.12: cbtw // Option 302 btw %ax,_MNUOPT(%bp) // enabled? 303 jnc main.10 // No 304/* 305 * Save the info in the original tables 306 * for rewriting to the disk. 307 */ 308 movb %al,_OPT(%bp) // Save option 309 movw $FAKE,%si // Partition for write 310 movb (%si),%dl // Drive number 311 movw %si,%bx // Partition for read 312 cmpb $0x4,%al // F5/#5 pressed? 313 pushf // Save 314 je main.13 // Yes 315 shlb $0x4,%al // Point to 316 addw $partbl,%ax // selected 317 xchgw %bx,%ax // partition 318 movb $0x80,(%bx) // Flag active 319/* 320 * If not asked to do a write-back (flags 0x40) don't do one. 321 */ 322main.13: pushw %bx // Save 323 testb $0x40,_FLAGS(%bp) // No updates? 324 jnz main.14 // Yes 325 movw $start,%bx // Data to write 326 movb $0x3,%ah // Write sector 327 callw intx13 // to disk 328main.14: popw %si // Restore 329 popf // Restore 330/* 331 * If going to next drive, replace drive with selected one. 332 * Remember to un-ascii it. Hey 0x80 is already set, cool! 333 */ 334 jne main.15 // If not F5/#5 335 movb _NXTDRV(%bp),%dl // Next drive 336 subb $'0',%dl // number 337/* 338 * load selected bootsector to the LOAD location in RAM. 339 * If it fails to read or isn't marked bootable, treat it 340 * as a bad selection. 341 * XXX what does %si carry? 342 */ 343main.15: movw $LOAD,%bx // Address for read 344 movb $0x2,%ah // Read sector 345 callw intx13 // from disk 346 jc main.10 // If error 347 cmpw $MAGIC,0x1fe(%bx) // Bootable? 348 jne main.10 // No 349 pushw %si // Save 350 movw $crlf,%si // Leave some 351 callw puts // space 352 popw %si // Restore 353 jmp *%bx // Invoke bootstrap 354 355/* 356 * Display routines 357 */ 358putkey: 359#ifndef SIO 360 movb $'F',%al // Display 361 callw putchr // 'F' 362#endif /* SIO */ 363 movb $'1',%al // Prepare 364 addb %dl,%al // digit 365 jmp putstr.1 // Display the rest 366 367/* 368 * Display the option and note that it is a valid option. 369 * That last point is a bit tricky.. 370 */ 371putx: btsw %dx,_MNUOPT(%bp) // Enable menu option 372 movw $item,%si // Display 373 callw putkey // key 374 movw %di,%si // Display the rest 375 376puts: callw putstr // Display string 377 378putn: movw $crlf,%si // To next line 379 380putstr: lodsb // Get byte 381 testb $0x80,%al // End of string? 382 jnz putstr.2 // Yes 383putstr.1: callw putchr // Display char 384 jmp putstr // Continue 385putstr.2: andb $~0x80,%al // Clear MSB 386 387putchr: 388#ifndef SIO 389 pushw %bx // Save 390 movw $0x7,%bx // Page:attribute 391 movb $0xe,%ah // BIOS: Display 392 int $0x10 // character 393 popw %bx // Restore 394#else /* SIO */ 395 pushw %dx // Save 396 xorw %dx,%dx // Use COM1 397 xorw %cx,%cx // No timeout 398 movb $0x01,%ah // BIOS: Send 399 int $0x14 // Character 400 popw %dx // Restore 401#endif /* SIO */ 402 retw // To caller 403 404/* One-sector disk I/O routine */ 405 406intx13: movb 0x1(%si),%dh // Load head 407 movw 0x2(%si),%cx // Load cylinder:sector 408 movb $0x1,%al // Sector count 409 pushw %si // Save 410 movw %sp,%di // Save 411 testb $0x80,_FLAGS(%bp) // Use packet interface? 412 jz intx13.1 // No 413 pushl $0x0 // Set the 414 pushl 0x8(%si) // LBA address 415 pushw %es // Set the transfer 416 pushw %bx // buffer address 417 push $0x1 // Block count 418 push $0x10 // Packet size 419 movw %sp,%si // Packet pointer 420 decw %ax // Verify off 421 orb $0x40,%ah // Use disk packet 422intx13.1: int $0x13 // BIOS: Disk I/O 423 movw %di,%sp // Restore 424 popw %si // Restore 425 retw // To caller 426 427/* Menu strings */ 428 429#ifndef SIO 430item: .ascii " "; .byte ' '|0x80 431prompt: .ascii "\nDefault:"; .byte ' '|0x80 432#else /* SIO */ 433item: .ascii " "; .byte ' '|0x80 434prompt: .ascii "\nDef:"; .byte ' '|0x80 435#endif /* SIO */ 436crlf: .ascii "\r"; .byte '\n'|0x80 437 438/* Partition type tables */ 439 440tables: 441/* 442 * These entries identify invalid or NON BOOT types and partitions. 443 */ 444 .byte 0x0, 0x5, 0xf 445/* 446 * These values indicate bootable types we know the names of 447 */ 448 .byte 0x1, 0x4, 0x6, 0xb, 0xc, 0xe, 0x83 449 .byte 0x9f, 0xa5, 0xa6, 0xa9 450/* 451 * These are offsets that match the known names above and point to the strings 452 * that will be printed. 453 */ 454 .byte os_misc-. // Unknown 455 .byte os_dos-. // DOS 456 .byte os_dos-. // DOS 457 .byte os_dos-. // DOS 458 .byte os_dos-. // Windows 459 .byte os_dos-. // Windows 460 .byte os_dos-. // Windows 461 .byte os_linux-. // Linux 462 .byte os_bsd-. // BSD/OS 463 .byte os_freebsd-. // FreeBSD 464 .byte os_bsd-. // OpenBSD 465 .byte os_bsd-. // NetBSD 466/* 467 * And here are the strings themselves. 0x80 or'd into a byte indicates 468 * the end of the string. (not so great for Russians but...) 469 */ 470os_misc: .ascii "?"; .byte '?'|0x80 471os_dos: .ascii "DO"; .byte 'S'|0x80 472os_linux: .ascii "Linu"; .byte 'x'|0x80 473os_freebsd: .ascii "Free" 474os_bsd: .ascii "BS"; .byte 'D'|0x80 475 476 .org PRT_OFF-0xe,0x90 477 478 .word B0MAGIC // Magic number 479 480/* 481 * These values are sometimes changed before writing back to the drive 482 * Be especially careful that nxtdrv: must come after drive:, as it 483 * is part of the same string. 484 */ 485drive: .ascii "Drive " 486nxtdrv: .byte 0x0 // Next drive number 487opt: .byte 0x0 // Option 488setdrv: .byte 0x80 // Drive to force 489flags: .byte FLAGS // Flags 490ticks: .word TICKS // Delay 491 492/* 493 * here is the 64 byte partition table that fdisk would fiddle with. 494 */ 495partbl: .fill 0x40,0x1,0x0 // Partition table 496 .word MAGIC // Magic number 497