1/* 2 * PROJECT: ReactOS Boot Sector for ISO file system (based on ISOLINUX) 3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) 4 * PURPOSE: Booting ReactOS off a CD-ROM using the El Torito boot standard in "no emulation mode" 5 * COPYRIGHT: Copyright 1994-2009 H. Peter Anvin 6 * Copyright 2002 Michael K. Ter Louw 7 * Copyright 2002 Eric Kohl 8 * Copyright 2009 Intel Corporation *author: H. Peter Anvin 9 * Copyright 2011 Timo Kreuzer (timo.kreuzer@reactos.org) 10 * Copyright 2017 Colin Finck (colin@reactos.org) 11 */ 12 13/* INCLUDES ******************************************************************/ 14#include <asm.inc> 15#include <freeldr/include/arch/pc/x86common.h> 16 17#ifndef ROS_REGTEST 18#define WAIT_FOR_KEY 19#endif 20 21 22.code16 23ASSUME CS:.text, DS:.text, ES:.text 24 25/* CONSTANTS ******************************************************************/ 26BIOS_timer = HEX(046C) // Timer ticks (1 word) 27BIOS_magic = HEX(0472) // BIOS reset magic (1 word) 28 29// Memory below this point is reserved for the BIOS and the MBR 30trackbuf = HEX(1000) // Track buffer goes here (8192 bytes) 31trackbufsize = 8192 // trackbuf ends at 3000h 32 33// struct open_file_t 34file_sector = 0 // Sector pointer (0 = structure free) 35file_bytesleft = 4 // Number of bytes left 36file_left = 8 // Number of sectors left 37// Another unused DWORD follows here in ISOLINUX 38#define open_file_t_size 16 39 40// struct dir_t 41dir_lba = 0 // Directory start (LBA) 42dir_len = 4 // Length in bytes 43dir_clust = 8 // Length in clusters 44#define dir_t_size 12 45 46MAX_OPEN_LG2 = 2 // log2(Max number of open files) 47MAX_OPEN = 4 48SECTOR_SHIFT = 11 // 2048 bytes/sector (El Torito requirement) 49SECTOR_SIZE = 2048 50retry_count = 6 // How patient are we with the BIOS? 51 52/* UNINITIALIZED VARIABLES ****************************************************/ 53absolute HEX(5000) // Here we keep our BSS stuff 54 55resb ISOFileName, 64 // ISO filename canonicalization buffer 56resb ISOFileNameEnd, 1 57resb CurrentDir, dir_t_size // Current directory 58resb RootDir, dir_t_size // Root directory 59resb DiskSys, 2 // Last INT 13h call 60resb GetlinsecPtr, 2 // The sector-read pointer 61resb DiskError, 1 // Error code for disk I/O 62resb DriveNumber, 1 // CD-ROM BIOS drive number 63resb ISOFlags, 1 // Flags for ISO directory search 64resb RetryCount, 1 // Used for disk access retries 65 66//align open_file_t_size 67absolute HEX(5070) 68resb Files, (MAX_OPEN * open_file_t_size) 69 70 71/* ENTRY POINTS ***************************************************************/ 72 73// Entry point when booted from CD (El Torito standard) 74start: 75 mov bx, offset getlinsec_cdrom 76 // Fall through 77 78start_common: 79 // Set up our stack and a flat addressing model. 80 cli 81 xor ax, ax 82 mov ss, ax 83 mov sp, offset start 84 mov ds, ax 85 mov es, ax 86 mov fs, ax 87 mov gs, ax 88 sti 89 90 // Our boot sector has been loaded to address 0x7C00. 91 // Relocate our 2048 bytes boot sector to the given base address (should be 0x7000). 92 cld 93 mov cx, 2048 / 4 94 mov si, HEX(7C00) 95 mov di, offset start 96 rep movsd 97 98 ljmp16 0, relocated // jump into relocated code 99 100.org 64 101hybrid_signature: 102 .long HEX(7078c0fb) 103 104// Entry point when booted through ISOMBR from a drive (isohybrid mode) 105start_hybrid: 106 mov bx, offset getlinsec_ebios 107 jmp start_common 108 109relocated: 110 // Save our passed variables (BX from the entry point, DL from the BIOS) before anybody clobbers the registers. 111 mov word ptr ds:[GetlinsecPtr], bx 112 mov byte ptr ds:[DriveNumber], dl 113 114 // Make sure the keyboard buffer is empty 115 call pollchar_and_empty 116 117 // If we're booting in hybrid mode and our boot drive is the first HDD (drive 80h), 118 // we have no other option than booting into FREELDR. 119 cmp word ptr ds:[GetlinsecPtr], offset getlinsec_ebios 120 jne .read_mbr 121 cmp byte ptr ds:[DriveNumber], HEX(80) 122 je .boot_freeldr 123 124.read_mbr: 125 // Read the first sector (MBR) from the first hard disk (drive 80h) to 7C00h. 126 // If we then decide to boot from HDD, we already have it at the right place. 127 // In case of an error (indicated by the Carry Flag), just boot FREELDR from our ReactOS medium. 128 mov ax, HEX(0201) 129 mov dx, HEX(0080) 130 mov cx, HEX(0001) 131 mov bx, HEX(7C00) 132 call int13 133 jc .boot_freeldr 134 135 // Verify the signature of the read MBR. 136 // If it's invalid, there is probably no OS installed and we just boot FREELDR from our ReactOS medium. 137 mov ax, word ptr ds:[HEX(7C00)+510] 138 cmp ax, HEX(AA55) 139 jne .boot_freeldr 140 141#ifdef WAIT_FOR_KEY 142 // We could either boot from the ReactOS medium or from hard disk. Let the user decide! 143 // Display the 'Press key' message. 144 call crlf_early 145 mov si, offset presskey_msg 146 call writestr_early 147 148 // Count down 5 seconds. 149 mov cx, 5 150 151.next_second: 152 // Count in seconds using the BIOS Timer, which runs roughly at 19 ticks per second. 153 // Load its value plus one second into EAX for comparison later. 154 mov eax, ds:[BIOS_timer] 155 add eax, 19 156 157.poll_again: 158 // Check for a keypress, boot FREELDR from our ReactOS medium if a key was pressed. 159 call pollchar_and_empty 160 jnz .boot_freeldr 161 162 // Check if another second has passed (in BIOS Timer ticks). 163 mov ebx, ds:[BIOS_timer] 164 cmp eax, ebx 165 jnz .poll_again 166 167 // Another second has passed, so print the dot and decrement the second counter. 168 // If the user hasn't pressed a key after the entire 5 seconds have elapsed, just boot from the first hard disk. 169 mov si, offset dot_msg 170 call writestr_early 171 dec cx 172 jz .boot_harddisk 173 jmp .next_second 174#endif 175 176.boot_harddisk: 177 // Restore a clean context for the hard disk MBR and boot the already loaded MBR. 178 call crlf_early 179 mov ax, cs 180 mov ds, ax 181 mov es, ax 182 mov fs, ax 183 mov gs, ax 184 mov dx, HEX(0080) 185 186 ljmp16 0, HEX(7C00) 187 188.boot_freeldr: 189#ifdef WAIT_FOR_KEY 190 call crlf_early 191 call crlf_early 192#endif 193 194 // The BIOS gave us a boot drive number, so in a perfect world we could just use that one now. 195 // Unfortunately, there are many broken BIOSes around, which is why ISOLINUX verifies it and applies some hacks if the number is wrong. 196 // Let's do exactly the same here to achieve maximum compatibility. 197 198 // Don't do this if we are running in hybrid mode. 199 cmp word ptr ds:[GetlinsecPtr], offset getlinsec_ebios 200 je found_drive 201 202 // Use the INT 13 function 4B01h (Get Disk Emulation Status) to fetch the El Torito Spec Packet. 203 // We can use this information to verify that our passed boot drive number really belongs to our CD. 204 mov ax, HEX(4B01) 205 mov dl, byte ptr ds:[DriveNumber] 206 mov si, offset spec_packet 207 call int13 208 209 // If this INT 13 function yields an error, we may be on a broken AWARD BIOS. 210 // Check this and patch if possible. 211 jc award_hack 212 213 // Check that our passed boot drive number and the number in the Spec Packet match. 214 // If not, try some workarounds to find our drive anyway. 215 mov dl, byte ptr ds:[DriveNumber] 216 cmp byte ptr ds:[sp_drive], dl 217 jne spec_query_failed 218 219found_drive: 220 // Clear Files structures 221 mov di, Files 222 mov cx, (MAX_OPEN*open_file_t_size)/4 223 xor eax, eax 224 rep stosd 225 226 // Read the entire 2K-sized ISO9660 Primary Volume Descriptor at sector 16 (32K). 227 // This calculation only holds for single-session ISOs, but we should never encounter anything else. 228 mov eax, 16 229 mov bx, trackbuf 230 call getonesec 231 232 // Read the LBA address (offset 2 in the Directory Record) of the root directory (offset 156 in the Primary Volume Descriptor). 233 mov eax, dword ptr ds:[trackbuf+156+2] 234 mov dword ptr ds:[RootDir+dir_lba], eax 235 mov dword ptr ds:[CurrentDir+dir_lba], eax 236 237 // Read the data length (offset 10 in the Directory Record) of the root directory (offset 156 in the Primary Volume Descriptor). 238 mov eax, dword ptr ds:[trackbuf+156+10] 239 mov dword ptr ds:[RootDir+dir_len], eax 240 mov dword ptr ds:[CurrentDir+dir_len], eax 241 242 // Calculate the number of clusters and write that to our RootDir and CurrentDir structures. 243 add eax, SECTOR_SIZE-1 244 shr eax, SECTOR_SHIFT 245 mov dword ptr ds:[RootDir+dir_clust],eax 246 mov dword ptr ds:[CurrentDir+dir_clust],eax 247 248 // Look for the "LOADER" directory (directory is indicated by AL = 2 when using searchdir_iso). 249 mov di, offset loader_dir 250 mov al, 2 251 call searchdir_iso 252 jnz .dir_found 253 254 // No directory was found, so bail out with an error message. 255 mov si, offset no_dir_msg 256 call writemsg 257 jmp kaboom 258 259.dir_found: 260 // The directory was found, so update the information in our CurrentDir structure. 261 // Free the file pointer entry at SI in the process. 262 mov dword ptr ds:[CurrentDir+dir_len], eax 263 mov eax, dword ptr ds:[si+file_left] 264 mov dword ptr ds:[CurrentDir+dir_clust], eax 265 xor eax, eax 266 xchg eax, dword ptr ds:[si+file_sector] 267 mov dword ptr ds:[CurrentDir+dir_lba], eax 268 269 // Look for the "FREELDR.SYS" file. 270 mov di, offset freeldr_sys 271 call searchdir 272 jnz .freeldr_found 273 274 // The FREELDR file was not found, so bail out with an error message. 275 mov si, offset no_freeldr_msg 276 call writemsg 277 jmp kaboom 278 279.freeldr_found: 280 // Calculate the rounded up number of 2K sectors that need to be read. 281 mov ecx, eax 282 shr ecx, SECTOR_SHIFT 283 test eax, HEX(7FF) 284 jz .load_freeldr 285 inc ecx 286 287.load_freeldr: 288 // Load the entire FREELDR.SYS (parameter CX = FFFFh) to its designated base address FREELDR_BASE. 289 // Using a high segment address with offset 0 instead of segment 0 with offset FREELDR_BASE apparently increases compatibility with some BIOSes. 290 mov bx, FREELDR_BASE / 16 291 mov es, bx 292 xor ebx, ebx 293 mov cx, HEX(FFFF) 294 call getfssec 295 296 // Pass two parameters to FREELDR: 297 // DL = BIOS Drive Number 298 // DH = Boot Partition (0 for HDD booting in hybrid mode, FFh for CD booting) 299 movzx dx, byte ptr ds:[DriveNumber] 300 cmp word ptr ds:[GetlinsecPtr], offset getlinsec_ebios 301 je .jump_to_freeldr 302 mov dh, HEX(FF) 303 304.jump_to_freeldr: 305 // Transfer execution to the bootloader. 306 ljmp16 0, FREELDR_BASE 307 308 309/* FUNCTIONS *****************************************************************/ 310 311/////////////////////////////////////////////////////////////////////////////// 312// Start of BrokenAwardHack --- 10-nov-2002 Knut_Petersen@t-online.de 313/////////////////////////////////////////////////////////////////////////////// 314// 315// There is a problem with certain versions of the AWARD BIOS ... 316// the boot sector will be loaded and executed correctly, but, because the 317// int 13 vector points to the wrong code in the BIOS, every attempt to 318// load the spec packet will fail. We scan for the equivalent of 319// 320// mov ax,0201h 321// mov bx,7c00h 322// mov cx,0006h 323// mov dx,0180h 324// pushf 325// call <direct far> 326// 327// and use <direct far> as the new vector for int 13. The code above is 328// used to load the boot code into ram, and there should be no reason 329// for anybody to change it now or in the future. There are no opcodes 330// that use encodings relativ to IP, so scanning is easy. If we find the 331// code above in the BIOS code we can be pretty sure to run on a machine 332// with an broken AWARD BIOS ... 333// 334/////////////////////////////////////////////////////////////////////////////// 335award_oldint13: 336 .long 0 337award_string: 338 .byte HEX(0b8),1,2,HEX(0bb),0,HEX(7c),HEX(0b9),6,0,HEX(0ba),HEX(80),1,HEX(09c),HEX(09a) 339 340award_hack: 341 mov si, offset spec_err_msg // Moved to this place from 342 call writemsg // spec_query_failed 343 344 mov eax, dword ptr ds:[HEX(13)*4] 345 mov dword ptr ds:[award_oldint13], eax 346 347 push es 348 mov ax, HEX(F000) // ES = BIOS Seg 349 mov es, ax 350 cld 351 xor di, di // start at ES:DI = f000:0 352award_loop: 353 push di // save DI 354 mov si, offset award_string // scan for award_string 355 mov cx, 7 // length of award_string = 7dw 356 repz cmpsw // compare 357 pop di // restore DI 358 jcxz award_found // jmp if found 359 inc di // not found, inc di 360 jno award_loop 361 362award_failed: 363 pop es // No, not this way :-(( 364award_fail2: 365 mov eax, dword ptr ds:[award_oldint13] // restore the original int 366 or eax, eax // 13 vector if there is one 367 jz spec_query_failed // and try other workarounds 368 mov dword ptr ds:[HEX(13)*4], eax 369 jmp spec_query_failed 370 371award_found: 372 mov eax, dword ptr es:[di+HEX(0e)] // load possible int 13 addr 373 pop es // restore ES 374 375 cmp eax, dword ptr ds:[award_oldint13] // give up if this is the 376 jz award_failed // active int 13 vector, 377 mov dword ptr ds:[HEX(13)*4], eax // otherwise change 0:13h*4 378 379 mov ax, HEX(4B01) // try to read the spec packet 380 mov dl, byte ptr ds:[DriveNumber] // now ... it should not fail 381 mov si, offset spec_packet // any longer 382 int HEX(13) 383 jc award_fail2 384 385 jmp found_drive // and leave error recovery code 386/////////////////////////////////////////////////////////////////////////////// 387// End of BrokenAwardHack ---- 10-nov-2002 Knut_Petersen@t-online.de 388/////////////////////////////////////////////////////////////////////////////// 389 390 391// INT 13h, AX=4B01h, DL=<passed in value> failed. 392// Try to scan the entire 80h-FFh from the end. 393spec_query_failed: 394 // some code moved to BrokenAwardHack 395 396 mov dl, HEX(FF) 397 398.test_loop: 399 pusha 400 mov ax, HEX(4B01) 401 mov si, offset spec_packet 402 mov byte ptr ds:[si], HEX(13) // Size of buffer 403 call int13 404 popa 405 jc .still_broken 406 407 mov si, offset maybe_msg 408 call writemsg 409 mov al, dl 410 call writehex2 411 call crlf_early 412 413 cmp byte ptr ds:[sp_drive], dl 414 jne .maybe_broken 415 416 // Okay, good enough... 417 mov si, offset alright_msg 418 call writemsg 419.found_drive0: 420 mov byte ptr ds:[DriveNumber], dl 421.found_drive: 422 jmp found_drive 423 424 // Award BIOS 4.51 apparently passes garbage in sp_drive, 425 // but if this was the drive number originally passed in 426 // DL then consider it "good enough" 427.maybe_broken: 428 mov al, byte ptr ds:[DriveNumber] 429 cmp al, dl 430 je .found_drive 431 432 // Intel Classic R+ computer with Adaptec 1542CP BIOS 1.02 433 // passes garbage in sp_drive, and the drive number originally 434 // passed in DL does not have 80h bit set. 435 or al, HEX(80) 436 cmp al, dl 437 je .found_drive0 438 439.still_broken: 440 dec dx 441 cmp dl, HEX(80) 442 jnb .test_loop 443 444 // No spec packet anywhere. Some particularly pathetic 445 // BIOSes apparently don't even implement function 446 // 4B01h, so we can't query a spec packet no matter 447 // what. If we got a drive number in DL, then try to 448 // use it, and if it works, then well... 449 mov dl, byte ptr ds:[DriveNumber] 450 cmp dl, HEX(81) // Should be 81-FF at least 451 jb fatal_error // If not, it's hopeless 452 453 // Write a warning to indicate we're on *very* thin ice now 454 mov si, offset nospec_msg 455 call writemsg 456 mov al, dl 457 call writehex2 458 call crlf_early 459 jmp .found_drive // Pray that this works... 460 461fatal_error: 462 mov si, offset nothing_msg 463 call writemsg 464 465.norge: 466 jmp short .norge 467 468// 469// searchdir: 470// 471// Open a file 472// 473// On entry: 474// DS:DI = filename 475// If successful: 476// ZF clear 477// SI = file pointer 478// EAX = file length in bytes 479// If unsuccessful 480// ZF set 481// 482// Assumes CS == DS == ES, and trashes BX and CX. 483// 484// searchdir_iso is a special entry point for ISOLINUX only. In addition 485// to the above, searchdir_iso passes a file flag mask in AL. This is useful 486// for searching for directories. 487// 488alloc_failure: 489 xor ax, ax // ZF <- 1 490 ret 491 492searchdir: 493 xor al, al 494searchdir_iso: 495 mov byte ptr ds:[ISOFlags], al 496 call allocate_file // Temporary file structure for directory 497 jnz alloc_failure 498 push es 499 push ds 500 pop es // ES = DS 501 mov si, offset CurrentDir 502 cmp byte ptr ds:[di], '/' // If filename begins with slash 503 jne .not_rooted 504 inc di // Skip leading slash 505 mov si, offset RootDir // Reference root directory instead 506.not_rooted: 507 mov eax, dword ptr ds:[si+dir_clust] 508 mov dword ptr ds:[bx+file_left], eax 509 shl eax, SECTOR_SHIFT 510 mov dword ptr ds:[bx+file_bytesleft], eax 511 mov eax, dword ptr ds:[si+dir_lba] 512 mov dword ptr ds:[bx+file_sector], eax 513 mov edx, dword ptr ds:[si+dir_len] 514 515.look_for_slash: 516 mov ax, di 517.scan: 518 mov cl, byte ptr ds:[di] 519 inc di 520 and cl, cl 521 jz .isfile 522 cmp cl, '/' 523 jne .scan 524 mov byte ptr ds:[di-1], 0 // Terminate at directory name 525 mov cl, 2 // Search for directory 526 xchg cl, byte ptr ds:[ISOFlags] 527 528 push di // Save these... 529 push cx 530 531 // Create recursion stack frame... 532 push offset .resume // Where to "return" to 533 push es 534.isfile: 535 xchg ax, di 536 537.getsome: 538 // Get a chunk of the directory 539 // This relies on the fact that ISOLINUX doesn't change SI 540 mov si, trackbuf 541 pushad 542 xchg bx, si 543 mov cx, word ptr ds:[BufSafe] 544 call getfssec 545 popad 546 547.compare: 548 movzx eax, byte ptr ds:[si] // Length of directory entry 549 cmp al, 33 550 jb .next_sector 551 mov cl, byte ptr ds:[si+25] 552 xor cl, byte ptr ds:[ISOFlags] 553 test cl, HEX(8E) // Unwanted file attributes! 554 jnz .not_file 555 pusha 556 movzx cx, byte ptr ds:[si+32] // File identifier length 557 add si, 33 // File identifier offset 558 call iso_compare_names 559 popa 560 je .success 561.not_file: 562 sub edx, eax // Decrease bytes left 563 jbe .failure 564 add si, ax // Advance pointer 565 566.check_overrun: 567 // Did we finish the buffer? 568 cmp si, trackbuf+trackbufsize 569 jb .compare // No, keep going 570 571 jmp short .getsome // Get some more directory 572 573.next_sector: 574 // Advance to the beginning of next sector 575 lea ax, [si+SECTOR_SIZE-1] 576 and ax, not (SECTOR_SIZE-1) 577 sub ax, si 578 jmp short .not_file // We still need to do length checks 579 580.failure: 581 xor eax, eax // ZF = 1 582 mov dword ptr ds:[bx+file_sector], eax 583 pop es 584 ret 585 586.success: 587 mov eax, dword ptr ds:[si+2] // Location of extent 588 mov dword ptr ds:[bx+file_sector], eax 589 mov eax, dword ptr ds:[si+10] // Data length 590 mov dword ptr ds:[bx+file_bytesleft], eax 591 push eax 592 add eax, SECTOR_SIZE-1 593 shr eax, SECTOR_SHIFT 594 mov dword ptr ds:[bx+file_left], eax 595 pop eax 596 jz .failure // Empty file? 597 // ZF = 0 598 mov si, bx 599 pop es 600 ret 601 602.resume: 603 // We get here if we were only doing part of a lookup 604 // This relies on the fact that .success returns bx == si 605 xchg edx, eax // Directory length in edx 606 pop cx // Old ISOFlags 607 pop di // Next filename pointer 608 mov byte ptr ds:[di-1], '/' // Restore slash 609 mov byte ptr ds:[ISOFlags], cl // Restore the flags 610 jz .failure // Did we fail? If so fail for real! 611 jmp .look_for_slash // Otherwise, next level 612 613// 614// allocate_file: Allocate a file structure 615// 616// If successful: 617// ZF set 618// BX = file pointer 619// In unsuccessful: 620// ZF clear 621// 622allocate_file: 623 push cx 624 mov bx, Files 625 mov cx, MAX_OPEN 626.check: 627 cmp dword ptr ds:[bx], 0 628 je .found 629 add bx, open_file_t_size // ZF = 0 630 loop .check 631 // ZF = 0 if we fell out of the loop 632.found: 633 pop cx 634 ret 635 636// 637// iso_compare_names: 638// Compare the names DS:SI and DS:DI and report if they are 639// equal from an ISO 9660 perspective. SI is the name from 640// the filesystem; CX indicates its length, and ';' terminates. 641// DI is expected to end with a null. 642// 643// Note: clobbers AX, CX, SI, DI; assumes DS == ES == base segment 644// 645iso_compare_names: 646 // First, terminate and canonicalize input filename 647 push di 648 mov di, offset ISOFileName 649.canon_loop: 650 jcxz .canon_end 651 lodsb 652 dec cx 653 cmp al, ';' 654 je .canon_end 655 and al, al 656 je .canon_end 657 stosb 658 cmp di, offset ISOFileNameEnd-1 // Guard against buffer overrun 659 jb .canon_loop 660.canon_end: 661 cmp di, ISOFileName 662 jbe .canon_done 663 cmp byte ptr ds:[di-1], '.' // Remove terminal dots 664 jne .canon_done 665 dec di 666 jmp short .canon_end 667.canon_done: 668 mov byte ptr ds:[di], 0 // Null-terminate string 669 pop di 670 mov si, ISOFileName 671.compare2: 672 lodsb 673 mov ah, byte ptr ds:[di] 674 inc di 675 and ax, ax 676 jz .success2 // End of string for both 677 and al, al // Is either one end of string? 678 jz .failure2 // If so, failure 679 and ah, ah 680 jz .failure2 681 or ax, HEX(2020) // Convert to lower case 682 cmp al, ah 683 je .compare2 684.failure2: 685 and ax, ax // ZF = 0 (at least one will be nonzero) 686.success2: 687 ret 688 689// 690// getfssec: Get multiple clusters from a file, given the file pointer. 691// 692// On entry: 693// ES:BX -> Buffer 694// SI -> File pointer 695// CX -> Cluster count 696// On exit: 697// SI -> File pointer (or 0 on EOF) 698// CF = 1 -> Hit EOF 699// ECX -> Bytes actually read 700// 701getfssec: 702 push ds 703 push cs 704 pop ds // DS <- CS 705 706 movzx ecx, cx 707 cmp ecx, dword ptr ds:[si+file_left] 708 jna .ok_size 709 mov ecx, dword ptr ds:[si+file_left] 710.ok_size: 711 pushad 712 mov eax, dword ptr ds:[si+file_sector] 713 mov bp, cx 714 call getlinsec 715 popad 716 717 // ECX[31:16] == 0 here... 718 add dword ptr ds:[si+file_sector], ecx 719 sub dword ptr ds:[si+file_left], ecx 720 shl ecx, SECTOR_SHIFT // Convert to bytes 721 cmp ecx, dword ptr ds:[si+file_bytesleft] 722 jb .not_all 723 mov ecx, dword ptr ds:[si+file_bytesleft] 724.not_all: 725 sub dword ptr ds:[si+file_bytesleft], ecx 726 jnz .ret // CF = 0 in this case... 727 push eax 728 xor eax, eax 729 mov dword ptr ds:[si+file_sector], eax // Unused 730 mov si, ax 731 pop eax 732 stc 733.ret: 734 pop ds 735 ret 736 737// 738// Information message (DS:SI) output 739// Prefix with "ISOBOOT: " 740// 741writemsg: 742 push ax 743 push si 744 mov si, offset isoboot_str 745 call writestr_early 746 pop si 747 call writestr_early 748 pop ax 749 ret 750 751writestr_early: 752 pushfd 753 pushad 754.top: 755 lodsb 756 and al, al 757 jz .end_writestr 758 call writechr 759 jmp short .top 760.end_writestr: 761 popad 762 popfd 763 ret 764 765crlf_early: 766 push ax 767 mov al, 13 768 call writechr 769 mov al, 10 770 call writechr 771 pop ax 772 ret 773 774// 775// writechr: Write a character to the screen. 776// 777writechr: 778 pushfd 779 pushad 780 mov ah, HEX(0E) 781 xor bx, bx 782 int HEX(10) 783 popad 784 popfd 785 ret 786 787// 788// int13: save all the segment registers and call INT 13h. 789// Some CD-ROM BIOSes have been found to corrupt segment registers 790// and/or disable interrupts. 791// 792int13: 793 pushf 794 push bp 795 push ds 796 push es 797 push fs 798 push gs 799 int HEX(13) 800 mov bp, sp 801 setc byte ptr ds:[bp+10] // Propagate CF to the caller 802 pop gs 803 pop fs 804 pop es 805 pop ds 806 pop bp 807 popf 808 ret 809 810// 811// Get one sector. Convenience entry point. 812// 813getonesec: 814 mov bp, 1 815 // Fall through to getlinsec 816 817// 818// Get linear sectors - EBIOS LBA addressing, 2048-byte sectors. 819// 820getlinsec: 821 jmp word ptr cs:[GetlinsecPtr] 822 823// 824// getlinsec_ebios: 825// 826// getlinsec implementation for floppy/HDD EBIOS (EDD) 827// 828getlinsec_ebios: 829 xor edx, edx 830 shld edx, eax, 2 831 shl eax, 2 // Convert to HDD sectors 832 shl bp, 2 833 834.loop_ebios: 835 push bp // Sectors left 836.retry2: 837 call maxtrans // Enforce maximum transfer size 838 movzx edi, bp // Sectors we are about to read 839 mov cx, retry_count 840.retry: 841 // Form DAPA on stack 842 push edx 843 push eax 844 push es 845 push bx 846 push di 847 push 16 848 mov si, sp 849 pushad 850 mov dl, byte ptr ds:[DriveNumber] 851 push ds 852 push ss 853 pop ds // DS <- SS 854 mov ah, HEX(42) // Extended Read 855 call int13 856 pop ds 857 popad 858 lea sp, [si+16] // Remove DAPA 859 jc .error_ebios 860 pop bp 861 add eax, edi // Advance sector pointer 862 adc edx, 0 863 sub bp, di // Sectors left 864 shl di, 9 // 512-byte sectors 865 add bx, di // Advance buffer pointer 866 jnc .no_overflow // Check if we have read more than 64K and need to adjust ES 867 mov di, es 868 add di, HEX(1000) // Adjust segment by 64K (1000h * 16 = 10000h = 64K + 1) 869 mov es, di 870.no_overflow: 871 and bp, bp 872 jnz .loop_ebios 873 874 ret 875 876.error_ebios: 877 pushad // Try resetting the device 878 xor ax, ax 879 mov dl, byte ptr ds:[DriveNumber] 880 call int13 881 popad 882 loop .retry // CX-- and jump if not zero 883 884 // Total failure. 885 jmp kaboom 886 887// 888// Truncate BP to MaxTransfer 889// 890maxtrans: 891 cmp bp, word ptr ds:[MaxTransfer] 892 jna .ok 893 mov bp, word ptr ds:[MaxTransfer] 894.ok: 895 ret 896 897// 898// This is the variant we use for real CD-ROMs: 899// LBA, 2K sectors, some special error handling. 900// 901getlinsec_cdrom: 902 mov si, offset dapa // Load up the DAPA 903 mov word ptr ds:[si+4], bx 904 mov word ptr ds:[si+6], es 905 mov dword ptr ds:[si+8], eax 906.loop_cdrom: 907 push bp // Sectors left 908 cmp bp, word ptr ds:[MaxTransferCD] 909 jbe .bp_ok 910 mov bp, word ptr ds:[MaxTransferCD] 911.bp_ok: 912 mov word ptr ds:[si+2], bp 913 push si 914 mov dl, byte ptr ds:[DriveNumber] 915 mov ah, HEX(42) // Extended Read 916 call xint13 917 pop si 918 pop bp 919 movzx eax, word ptr ds:[si+2] // Sectors we read 920 add dword ptr ds:[si+8], eax // Advance sector pointer 921 sub bp, ax // Sectors left 922 shl ax, SECTOR_SHIFT-4 // 2048-byte sectors -> segment 923 add word ptr ds:[si+6], ax // Advance buffer pointer 924 and bp, bp 925 jnz .loop_cdrom 926 mov eax, dword ptr ds:[si+8] // Next sector 927 ret 928 929 // INT 13h with retry 930xint13: 931 mov byte ptr ds:[RetryCount], retry_count 932.try: 933 pushad 934 call int13 935 jc .error_cdrom 936 add sp, 8*4 // Clean up stack 937 ret 938.error_cdrom: 939 mov byte ptr ds:[DiskError], ah // Save error code 940 popad 941 mov word ptr ds:[DiskSys], ax // Save system call number 942 dec byte ptr ds:[RetryCount] 943 jz .real_error 944 push ax 945 mov al, byte ptr ds:[RetryCount] 946 mov ah, byte ptr ds:[dapa+2] // Sector transfer count 947 cmp al, 2 // Only 2 attempts left 948 ja .nodanger 949 mov ah, 1 // Drop transfer size to 1 950 jmp short .setsize 951.nodanger: 952 cmp al, retry_count-2 953 ja .again // First time, just try again 954 shr ah, 1 // Otherwise, try to reduce 955 adc ah, 0 // the max transfer size, but not to 0 956.setsize: 957 mov byte ptr ds:[MaxTransferCD], ah 958 mov byte ptr ds:[dapa+2], ah 959.again: 960 pop ax 961 jmp .try 962 963.real_error: 964 mov si, offset diskerr_msg 965 call writemsg 966 mov al, byte ptr ds:[DiskError] 967 call writehex2 968 mov si, offset oncall_str 969 call writestr_early 970 mov ax, word ptr ds:[DiskSys] 971 call writehex4 972 mov si, offset ondrive_str 973 call writestr_early 974 mov al, dl 975 call writehex2 976 call crlf_early 977 // Fall through to kaboom 978 979// 980// kaboom: write a message and bail out. Wait for a user keypress, 981// then do a hard reboot. 982// 983kaboom: 984 // Restore a clean context. 985 mov ax, cs 986 mov ds, ax 987 mov es, ax 988 mov fs, ax 989 mov gs, ax 990 sti 991 992 // Display the failure message. 993 mov si, offset err_bootfailed 994 call writestr_early 995 996 // Wait for a keypress. 997 xor ax, ax 998 int HEX(16) 999 1000 // Disable interrupts and reset the system through a magic BIOS call. 1001 cli 1002 mov word ptr ds:[BIOS_magic], 0 1003 ljmp16 HEX(0F000), HEX(0FFF0) 1004 1005// 1006// writehex[248]: Write a hex number in (AL, AX, EAX) to the console 1007// 1008writehex2: 1009 pushfd 1010 pushad 1011 rol eax, 24 1012 mov cx,2 1013 jmp short writehex_common 1014writehex4: 1015 pushfd 1016 pushad 1017 rol eax, 16 1018 mov cx, 4 1019 jmp short writehex_common 1020writehex8: 1021 pushfd 1022 pushad 1023 mov cx, 8 1024writehex_common: 1025.loop_writehex: 1026 rol eax, 4 1027 push eax 1028 and al, HEX(0F) 1029 cmp al, 10 1030 jae .high 1031.low: 1032 add al, '0' 1033 jmp short .ischar 1034.high: 1035 add al, 'A'-10 1036.ischar: 1037 call writechr 1038 pop eax 1039 loop .loop_writehex 1040 popad 1041 popfd 1042 ret 1043 1044// 1045// pollchar_and_empty: Check if we have an input character pending (ZF = 0) 1046// and empty the input buffer afterwards. 1047// 1048pollchar_and_empty: 1049 pushad 1050 mov ah, 1 // Did the user press a key? 1051 int HEX(16) 1052 jz .end_pollchar // No, then we're done 1053 mov ah, 0 // Otherwise empty the buffer by reading it 1054 int HEX(16) 1055.end_pollchar: 1056 popad 1057 ret 1058 1059 1060/* INITIALIZED VARIABLES *****************************************************/ 1061presskey_msg: 1062 .ascii "Press any key to boot from the ReactOS medium", NUL 1063dot_msg: 1064 .ascii ".", NUL 1065isoboot_str: 1066 .ascii "ISOBOOT: ", NUL 1067spec_err_msg: 1068 .ascii "Loading spec packet failed, trying to wing it...", CR, LF, NUL 1069maybe_msg: 1070 .ascii "Found something at drive = ", NUL 1071alright_msg: 1072 .ascii "Looks reasonable, continuing...", CR, LF, NUL 1073nospec_msg: 1074 .ascii "Extremely broken BIOS detected, last attempt with drive = ", NUL 1075nothing_msg: 1076 .ascii "Failed to locate CD-ROM device; boot failed.", CR, LF, NUL 1077diskerr_msg: 1078 .ascii "Disk error ", NUL 1079oncall_str: 1080 .ascii ", AX = ", NUL 1081ondrive_str: 1082 .ascii ", drive ", NUL 1083err_bootfailed: 1084 .ascii CR, LF, "Boot failed: press a key to retry...", NUL 1085loader_dir: 1086 .ascii "/LOADER", NUL 1087no_dir_msg: 1088 .ascii "LOADER dir not found.", CR, LF, NUL 1089freeldr_sys: 1090 .ascii "FREELDR.SYS", NUL 1091no_freeldr_msg: 1092 .ascii "FREELDR.SYS not found.", CR, LF, NUL 1093 1094.align 4 1095BufSafe: 1096 .word trackbufsize/SECTOR_SIZE // Clusters we can load into trackbuf 1097 1098// Maximum transfer size 1099.align 4 1100MaxTransfer: 1101 .word 127 // Hard disk modes 1102MaxTransferCD: 1103 .word 32 // CD mode 1104 1105// 1106// El Torito spec packet 1107// 1108.align 8 1109spec_packet: 1110 .byte HEX(13) // Size of packet 1111sp_media: 1112 .byte 0 // Media type 1113sp_drive: 1114 .byte 0 // Drive number 1115sp_controller: 1116 .byte 0 // Controller index 1117sp_lba: 1118 .long 0 // LBA for emulated disk image 1119sp_devspec: 1120 .word 0 // IDE/SCSI information 1121sp_buffer: 1122 .word 0 // User-provided buffer 1123sp_loadseg: 1124 .word 0 // Load segment 1125sp_sectors: 1126 .word 0 // Sector count 1127sp_chs: 1128 .byte 0,0,0 // Simulated CHS geometry 1129sp_dummy: 1130 .byte 0 // Scratch, safe to overwrite 1131 1132// 1133// EBIOS disk address packet 1134// 1135.align 8 1136dapa: 1137 .word 16 // Packet size 1138.count: 1139 .word 0 // Block count 1140.off: 1141 .word 0 // Offset of buffer 1142.seg: 1143 .word 0 // Segment of buffer 1144.lba: 1145 .long 0 // LBA (LSW) 1146 .long 0 // LBA (MSW) 1147 1148 1149// Extend the size to cover one 2K-sized sector 1150.org 2046 1151 .word HEX(0aa55) // BootSector signature 1152 1153.endcode16 1154 1155END 1156