1; -*- fundamental -*- (asm-mode sucks) 2; **************************************************************************** 3; 4; isolinux.asm 5; 6; A program to boot Linux kernels off a CD-ROM using the El Torito 7; boot standard in "no emulation" mode, making the entire filesystem 8; available. It is based on the SYSLINUX boot loader for MS-DOS 9; floppies. 10; 11; Copyright 1994-2009 H. Peter Anvin - All Rights Reserved 12; Copyright 2009 Intel Corporation; author: H. Peter Anvin 13; 14; This program is free software; you can redistribute it and/or modify 15; it under the terms of the GNU General Public License as published by 16; the Free Software Foundation, Inc., 53 Temple Place Ste 330, 17; Boston MA 02111-1307, USA; either version 2 of the License, or 18; (at your option) any later version; incorporated herein by reference. 19; 20; **************************************************************************** 21 22%define IS_ISOLINUX 1 23%include "head.inc" 24 25; 26; Some semi-configurable constants... change on your own risk. 27; 28my_id equ isolinux_id 29NULLFILE equ 0 ; Zero byte == null file name 30NULLOFFSET equ 0 ; Position in which to look 31retry_count equ 6 ; How patient are we with the BIOS? 32%assign HIGHMEM_SLOP 128*1024 ; Avoid this much memory near the top 33SECTOR_SHIFT equ 11 ; 2048 bytes/sector (El Torito requirement) 34SECTOR_SIZE equ (1 << SECTOR_SHIFT) 35 36ROOT_DIR_WORD equ 0x002F 37 38; --------------------------------------------------------------------------- 39; BEGIN CODE 40; --------------------------------------------------------------------------- 41 42; 43; Memory below this point is reserved for the BIOS and the MBR 44; 45 section .earlybss 46 global trackbuf 47trackbufsize equ 8192 48trackbuf resb trackbufsize ; Track buffer goes here 49; ends at 2800h 50 51 ; Some of these are touched before the whole image 52 ; is loaded. DO NOT move this to .bss16/.uibss. 53 section .earlybss 54 global BIOSName 55 alignb 4 56FirstSecSum resd 1 ; Checksum of bytes 64-2048 57ImageDwords resd 1 ; isolinux.bin size, dwords 58InitStack resd 1 ; Initial stack pointer (SS:SP) 59DiskSys resw 1 ; Last INT 13h call 60ImageSectors resw 1 ; isolinux.bin size, sectors 61; These following two are accessed as a single dword... 62GetlinsecPtr resw 1 ; The sector-read pointer 63BIOSName resw 1 ; Display string for BIOS type 64%define HAVE_BIOSNAME 1 65 global BIOSType 66BIOSType resw 1 67DiskError resb 1 ; Error code for disk I/O 68 global DriveNumber 69DriveNumber resb 1 ; CD-ROM BIOS drive number 70ISOFlags resb 1 ; Flags for ISO directory search 71RetryCount resb 1 ; Used for disk access retries 72 73 alignb 8 74 global Hidden 75Hidden resq 1 ; Used in hybrid mode 76bsSecPerTrack resw 1 ; Used in hybrid mode 77bsHeads resw 1 ; Used in hybrid mode 78 79 80; 81; El Torito spec packet 82; 83 84 alignb 8 85_spec_start equ $ 86 global spec_packet 87spec_packet: resb 1 ; Size of packet 88sp_media: resb 1 ; Media type 89sp_drive: resb 1 ; Drive number 90sp_controller: resb 1 ; Controller index 91sp_lba: resd 1 ; LBA for emulated disk image 92sp_devspec: resw 1 ; IDE/SCSI information 93sp_buffer: resw 1 ; User-provided buffer 94sp_loadseg: resw 1 ; Load segment 95sp_sectors: resw 1 ; Sector count 96sp_chs: resb 3 ; Simulated CHS geometry 97sp_dummy: resb 1 ; Scratch, safe to overwrite 98 99; 100; EBIOS drive parameter packet 101; 102 alignb 8 103drive_params: resw 1 ; Buffer size 104dp_flags: resw 1 ; Information flags 105dp_cyl: resd 1 ; Physical cylinders 106dp_head: resd 1 ; Physical heads 107dp_sec: resd 1 ; Physical sectors/track 108dp_totalsec: resd 2 ; Total sectors 109dp_secsize: resw 1 ; Bytes per sector 110dp_dpte: resd 1 ; Device Parameter Table 111dp_dpi_key: resw 1 ; 0BEDDh if rest valid 112dp_dpi_len: resb 1 ; DPI len 113 resb 1 114 resw 1 115dp_bus: resb 4 ; Host bus type 116dp_interface: resb 8 ; Interface type 117db_i_path: resd 2 ; Interface path 118db_d_path: resd 2 ; Device path 119 resb 1 120db_dpi_csum: resb 1 ; Checksum for DPI info 121 122; 123; EBIOS disk address packet 124; 125 alignb 8 126dapa: resw 1 ; Packet size 127.count: resw 1 ; Block count 128.off: resw 1 ; Offset of buffer 129.seg: resw 1 ; Segment of buffer 130.lba: resd 2 ; LBA (LSW, MSW) 131 132; 133; Spec packet for disk image emulation 134; 135 alignb 8 136dspec_packet: resb 1 ; Size of packet 137dsp_media: resb 1 ; Media type 138dsp_drive: resb 1 ; Drive number 139dsp_controller: resb 1 ; Controller index 140dsp_lba: resd 1 ; LBA for emulated disk image 141dsp_devspec: resw 1 ; IDE/SCSI information 142dsp_buffer: resw 1 ; User-provided buffer 143dsp_loadseg: resw 1 ; Load segment 144dsp_sectors: resw 1 ; Sector count 145dsp_chs: resb 3 ; Simulated CHS geometry 146dsp_dummy: resb 1 ; Scratch, safe to overwrite 147 148 alignb 4 149_spec_end equ $ 150_spec_len equ _spec_end - _spec_start 151 152 section .init 153;; 154;; Primary entry point. Because BIOSes are buggy, we only load the first 155;; CD-ROM sector (2K) of the file, so the number one priority is actually 156;; loading the rest. 157;; 158 global StackBuf 159StackBuf equ STACK_TOP-44 ; 44 bytes needed for 160 ; the bootsector chainloading 161 ; code! 162 global OrigESDI 163OrigESDI equ StackBuf-4 ; The high dword on the stack 164StackHome equ OrigESDI 165 166bootsec equ $ 167 168_start: ; Far jump makes sure we canonicalize the address 169 cli 170 jmp 0:_start1 171 times 8-($-$$) nop ; Pad to file offset 8 172 173 ; This table hopefully gets filled in by mkisofs using the 174 ; -boot-info-table option. If not, the values in this 175 ; table are default values that we can use to get us what 176 ; we need, at least under a certain set of assumptions. 177 global iso_boot_info 178iso_boot_info: 179bi_pvd: dd 16 ; LBA of primary volume descriptor 180bi_file: dd 0 ; LBA of boot file 181bi_length: dd 0xdeadbeef ; Length of boot file 182bi_csum: dd 0xdeadbeef ; Checksum of boot file 183bi_reserved: times 10 dd 0xdeadbeef ; Reserved 184bi_end: 185 186 ; Custom entry point for the hybrid-mode disk. 187 ; The following values will have been pushed onto the 188 ; entry stack: 189 ; - partition offset (qword) 190 ; - ES 191 ; - DI 192 ; - DX (including drive number) 193 ; - CBIOS Heads 194 ; - CBIOS Sectors 195 ; - EBIOS flag 196 ; (top of stack) 197 ; 198 ; If we had an old isohybrid, the partition offset will 199 ; be missing; we can check for that with sp >= 0x7c00. 200 ; Serious hack alert. 201%ifndef DEBUG_MESSAGES 202_hybrid_signature: 203 dd 0x7078c0fb ; An arbitrary number... 204 205_start_hybrid: 206 pop cx ; EBIOS flag 207 pop word [cs:bsSecPerTrack] 208 pop word [cs:bsHeads] 209 pop dx 210 pop di 211 pop es 212 xor eax,eax 213 xor ebx,ebx 214 cmp sp,7C00h 215 jae .nooffset 216 pop eax 217 pop ebx 218.nooffset: 219 mov si,bios_cbios 220 jcxz _start_common 221 mov si,bios_ebios 222 jmp _start_common 223%endif 224 225_start1: 226 mov si,bios_cdrom 227 xor eax,eax 228 xor ebx,ebx 229_start_common: 230 mov [cs:InitStack],sp ; Save initial stack pointer 231 mov [cs:InitStack+2],ss 232 xor cx,cx 233 mov ss,cx 234 mov sp,StackBuf ; Set up stack 235 push es ; Save initial ES:DI -> $PnP pointer 236 push di 237 mov ds,cx 238 mov es,cx 239 mov fs,cx 240 mov gs,cx 241 sti 242 cld 243 244 mov [Hidden],eax 245 mov [Hidden+4],ebx 246 247 mov [BIOSType],si 248 mov eax,[si] 249 mov [GetlinsecPtr],eax 250 251 ; Show signs of life 252 mov si,syslinux_banner 253 call writestr_early 254%ifdef DEBUG_MESSAGES 255 mov si,copyright_str 256%else 257 mov si,[BIOSName] 258%endif 259 call writestr_early 260 261 ; 262 ; Before modifying any memory, get the checksum of bytes 263 ; 64-2048 264 ; 265initial_csum: xor edi,edi 266 mov si,bi_end 267 mov cx,(SECTOR_SIZE-64) >> 2 268.loop: lodsd 269 add edi,eax 270 loop .loop 271 mov [FirstSecSum],edi 272 273 mov [DriveNumber],dl 274%ifdef DEBUG_MESSAGES 275 mov si,startup_msg 276 call writemsg 277 mov al,dl 278 call writehex2 279 call crlf_early 280%endif 281 ; 282 ; Initialize spec packet buffers 283 ; 284 mov di,_spec_start 285 mov cx,_spec_len >> 2 286 xor eax,eax 287 rep stosd 288 289 ; Initialize length field of the various packets 290 mov byte [spec_packet],13h 291 mov byte [drive_params],30 292 mov byte [dapa],16 293 mov byte [dspec_packet],13h 294 295 ; Other nonzero fields 296 inc word [dsp_sectors] 297 298 ; Are we just pretending to be a CD-ROM? 299 cmp word [BIOSType],bios_cdrom 300 jne found_drive ; If so, no spec packet... 301 302 ; Now figure out what we're actually doing 303 ; Note: use passed-in DL value rather than 7Fh because 304 ; at least some BIOSes will get the wrong value otherwise 305 mov ax,4B01h ; Get disk emulation status 306 mov dl,[DriveNumber] 307 mov si,spec_packet 308 call int13 309 jc award_hack ; changed for BrokenAwardHack 310 mov dl,[DriveNumber] 311 cmp [sp_drive],dl ; Should contain the drive number 312 jne spec_query_failed 313 314%ifdef DEBUG_MESSAGES 315 mov si,spec_ok_msg 316 call writemsg 317 mov al,byte [sp_drive] 318 call writehex2 319 call crlf_early 320%endif 321 322found_drive: 323 ; Alright, we have found the drive. Now, try to find the 324 ; boot file itself. If we have a boot info table, life is 325 ; good; if not, we have to make some assumptions, and try 326 ; to figure things out ourselves. In particular, the 327 ; assumptions we have to make are: 328 ; - single session only 329 ; - only one boot entry (no menu or other alternatives) 330 331 cmp dword [bi_file],0 ; Address of code to load 332 jne found_file ; Boot info table present :) 333 334%ifdef DEBUG_MESSAGES 335 mov si,noinfotable_msg 336 call writemsg 337%endif 338 339 ; No such luck. See if the spec packet contained one. 340 mov eax,[sp_lba] 341 and eax,eax 342 jz set_file ; Good enough 343 344%ifdef DEBUG_MESSAGES 345 mov si,noinfoinspec_msg 346 call writemsg 347%endif 348 349 ; No such luck. Get the Boot Record Volume, assuming single 350 ; session disk, and that we're the first entry in the chain. 351 mov eax,17 ; Assumed address of BRV 352 mov bx,trackbuf 353 call getonesec 354 355 mov eax,[trackbuf+47h] ; Get boot catalog address 356 mov bx,trackbuf 357 call getonesec ; Get boot catalog 358 359 mov eax,[trackbuf+28h] ; First boot entry 360 ; And hope and pray this is us... 361 362 ; Some BIOSes apparently have limitations on the size 363 ; that may be loaded (despite the El Torito spec being very 364 ; clear on the fact that it must all be loaded.) Therefore, 365 ; we load it ourselves, and *bleep* the BIOS. 366 367set_file: 368 mov [bi_file],eax 369 370found_file: 371 ; Set up boot file sizes 372 mov eax,[bi_length] 373 sub eax,SECTOR_SIZE-3 ; ... minus sector loaded 374 shr eax,2 ; bytes->dwords 375 mov [ImageDwords],eax ; boot file dwords 376 add eax,((SECTOR_SIZE-1) >> 2) 377 shr eax,SECTOR_SHIFT-2 ; dwords->sectors 378 mov [ImageSectors],ax ; boot file sectors 379 380 mov eax,[bi_file] ; Address of code to load 381 inc eax ; Don't reload bootstrap code 382%ifdef DEBUG_MESSAGES 383 mov si,offset_msg 384 call writemsg 385 call writehex8 386 call crlf_early 387%endif 388 389 ; Load the rest of the file. However, just in case there 390 ; are still BIOSes with 64K wraparound problems, we have to 391 ; take some extra precautions. Since the normal load 392 ; address (TEXT_START) is *not* 2K-sector-aligned, we round 393 ; the target address upward to a sector boundary, 394 ; and then move the entire thing down as a unit. 395MaxLMA equ 384*1024 ; Reasonable limit (384K) 396 397 mov bx,((TEXT_START+2*SECTOR_SIZE-1) & ~(SECTOR_SIZE-1)) >> 4 398 mov bp,[ImageSectors] 399 push bx ; Load segment address 400 401.more: 402 push bx ; Segment address 403 push bp ; Sector count 404 mov es,bx 405 mov cx,0xfff 406 and bx,cx 407 inc cx 408 sub cx,bx 409 shr cx,SECTOR_SHIFT - 4 410 jnz .notaligned 411 mov cx,0x10000 >> SECTOR_SHIFT ; Full 64K segment possible 412.notaligned: 413 cmp bp,cx 414 jbe .ok 415 mov bp,cx 416.ok: 417 xor bx,bx 418 push bp 419 push eax 420 call getlinsec 421 pop eax 422 pop cx 423 movzx edx,cx 424 pop bp 425 pop bx 426 427 shl cx,SECTOR_SHIFT - 4 428 add bx,cx 429 add eax,edx 430 sub bp,dx 431 jnz .more 432 433 ; Move the image into place, and also verify the 434 ; checksum 435 pop ax ; Load segment address 436 mov bx,(TEXT_START + SECTOR_SIZE) >> 4 437 mov ecx,[ImageDwords] 438 mov edi,[FirstSecSum] ; First sector checksum 439 xor si,si 440 441move_verify_image: 442.setseg: 443 mov ds,ax 444 mov es,bx 445.loop: 446 mov edx,[si] 447 add edi,edx 448 dec ecx 449 mov [es:si],edx 450 jz .done 451 add si,4 452 jnz .loop 453 add ax,1000h 454 add bx,1000h 455 jmp .setseg 456.done: 457 mov ax,cs 458 mov ds,ax 459 mov es,ax 460 461 ; Verify the checksum on the loaded image. 462 cmp [bi_csum],edi 463 je integrity_ok 464 465 mov si,checkerr_msg 466 call writemsg 467 jmp kaboom 468 469integrity_ok: 470%ifdef DEBUG_MESSAGES 471 mov si,allread_msg 472 call writemsg 473%endif 474 jmp all_read ; Jump to main code 475 476;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 477;; Start of BrokenAwardHack --- 10-nov-2002 Knut_Petersen@t-online.de 478;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 479;; 480;; There is a problem with certain versions of the AWARD BIOS ... 481;; the boot sector will be loaded and executed correctly, but, because the 482;; int 13 vector points to the wrong code in the BIOS, every attempt to 483;; load the spec packet will fail. We scan for the equivalent of 484;; 485;; mov ax,0201h 486;; mov bx,7c00h 487;; mov cx,0006h 488;; mov dx,0180h 489;; pushf 490;; call <direct far> 491;; 492;; and use <direct far> as the new vector for int 13. The code above is 493;; used to load the boot code into ram, and there should be no reason 494;; for anybody to change it now or in the future. There are no opcodes 495;; that use encodings relativ to IP, so scanning is easy. If we find the 496;; code above in the BIOS code we can be pretty sure to run on a machine 497;; with an broken AWARD BIOS ... 498;; 499;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 500 ;; 501%ifdef DEBUG_MESSAGES ;; 502 ;; 503award_notice db "Trying BrokenAwardHack first ...",CR,LF,0 ;; 504award_not_orig db "BAH: Original Int 13 vector : ",0 ;; 505award_not_new db "BAH: Int 13 vector changed to : ",0 ;; 506award_not_succ db "BAH: SUCCESS",CR,LF,0 ;; 507award_not_fail db "BAH: FAILURE" ;; 508award_not_crlf db CR,LF,0 ;; 509 ;; 510%endif ;; 511 ;; 512award_oldint13 dd 0 ;; 513award_string db 0b8h,1,2,0bbh,0,7ch,0b9h,6,0,0bah,80h,1,09ch,09ah ;; 514 ;; 515 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 516award_hack: mov si,spec_err_msg ; Moved to this place from 517 call writemsg ; spec_query_failed 518 ; 519%ifdef DEBUG_MESSAGES ; 520 ; 521 mov si,award_notice ; display our plan 522 call writemsg ; 523 mov si,award_not_orig ; display original int 13 524 call writemsg ; vector 525%endif ; 526 mov eax,[13h*4] ; 527 mov [award_oldint13],eax ; 528 ; 529%ifdef DEBUG_MESSAGES ; 530 ; 531 call writehex8 ; 532 mov si,award_not_crlf ; 533 call writestr_early ; 534%endif ; 535 push es ; save ES 536 mov ax,0f000h ; ES = BIOS Seg 537 mov es,ax ; 538 cld ; 539 xor di,di ; start at ES:DI = f000:0 540award_loop: push di ; save DI 541 mov si,award_string ; scan for award_string 542 mov cx,7 ; length of award_string = 7dw 543 repz cmpsw ; compare 544 pop di ; restore DI 545 jcxz award_found ; jmp if found 546 inc di ; not found, inc di 547 jno award_loop ; 548 ; 549award_failed: pop es ; No, not this way :-(( 550award_fail2: ; 551 ; 552%ifdef DEBUG_MESSAGES ; 553 ; 554 mov si,award_not_fail ; display failure ... 555 call writemsg ; 556%endif ; 557 mov eax,[award_oldint13] ; restore the original int 558 or eax,eax ; 13 vector if there is one 559 jz spec_query_failed ; and try other workarounds 560 mov [13h*4],eax ; 561 jmp spec_query_failed ; 562 ; 563award_found: mov eax,[es:di+0eh] ; load possible int 13 addr 564 pop es ; restore ES 565 ; 566 cmp eax,[award_oldint13] ; give up if this is the 567 jz award_failed ; active int 13 vector, 568 mov [13h*4],eax ; otherwise change 0:13h*4 569 ; 570 ; 571%ifdef DEBUG_MESSAGES ; 572 ; 573 push eax ; display message and 574 mov si,award_not_new ; new vector address 575 call writemsg ; 576 pop eax ; 577 call writehex8 ; 578 mov si,award_not_crlf ; 579 call writestr_early ; 580%endif ; 581 mov ax,4B01h ; try to read the spec packet 582 mov dl,[DriveNumber] ; now ... it should not fail 583 mov si,spec_packet ; any longer 584 int 13h ; 585 jc award_fail2 ; 586 ; 587%ifdef DEBUG_MESSAGES ; 588 ; 589 mov si,award_not_succ ; display our SUCCESS 590 call writemsg ; 591%endif ; 592 jmp found_drive ; and leave error recovery code 593 ; 594;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 595;; End of BrokenAwardHack ---- 10-nov-2002 Knut_Petersen@t-online.de 596;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 597 598 599 ; INT 13h, AX=4B01h, DL=<passed in value> failed. 600 ; Try to scan the entire 80h-FFh from the end. 601 602spec_query_failed: 603 604 ; some code moved to BrokenAwardHack 605 606 mov dl,0FFh 607.test_loop: pusha 608 mov ax,4B01h 609 mov si,spec_packet 610 mov byte [si],13h ; Size of buffer 611 call int13 612 popa 613 jc .still_broken 614 615 mov si,maybe_msg 616 call writemsg 617 mov al,dl 618 call writehex2 619 call crlf_early 620 621 cmp byte [sp_drive],dl 622 jne .maybe_broken 623 624 ; Okay, good enough... 625 mov si,alright_msg 626 call writemsg 627.found_drive0: mov [DriveNumber],dl 628.found_drive: jmp found_drive 629 630 ; Award BIOS 4.51 apparently passes garbage in sp_drive, 631 ; but if this was the drive number originally passed in 632 ; DL then consider it "good enough" 633.maybe_broken: 634 mov al,[DriveNumber] 635 cmp al,dl 636 je .found_drive 637 638 ; Intel Classic R+ computer with Adaptec 1542CP BIOS 1.02 639 ; passes garbage in sp_drive, and the drive number originally 640 ; passed in DL does not have 80h bit set. 641 or al,80h 642 cmp al,dl 643 je .found_drive0 644 645.still_broken: dec dx 646 cmp dl, 80h 647 jnb .test_loop 648 649 ; No spec packet anywhere. Some particularly pathetic 650 ; BIOSes apparently don't even implement function 651 ; 4B01h, so we can't query a spec packet no matter 652 ; what. If we got a drive number in DL, then try to 653 ; use it, and if it works, then well... 654 mov dl,[DriveNumber] 655 cmp dl,81h ; Should be 81-FF at least 656 jb fatal_error ; If not, it's hopeless 657 658 ; Write a warning to indicate we're on *very* thin ice now 659 mov si,nospec_msg 660 call writemsg 661 mov al,dl 662 call writehex2 663 call crlf_early 664 mov si,trysbm_msg 665 call writemsg 666 jmp .found_drive ; Pray that this works... 667 668fatal_error: 669 mov si,nothing_msg 670 call writemsg 671 672.norge: jmp short .norge 673 674 ; Information message (DS:SI) output 675 ; Prefix with "isolinux: " 676 ; 677writemsg: push ax 678 push si 679 mov si,isolinux_str 680 call writestr_early 681 pop si 682 call writestr_early 683 pop ax 684 ret 685 686writestr_early: 687 pushfd 688 pushad 689.top: lodsb 690 and al,al 691 jz .end 692 call writechr 693 jmp short .top 694.end: popad 695 popfd 696 ret 697 698crlf_early: push ax 699 mov al,CR 700 call writechr 701 mov al,LF 702 call writechr 703 pop ax 704 ret 705 706; 707; Write a character to the screen. There is a more "sophisticated" 708; version of this in the subsequent code, so we patch the pointer 709; when appropriate. 710; 711 712writechr: 713.simple: 714 pushfd 715 pushad 716 mov ah,0Eh 717 xor bx,bx 718 int 10h 719 popad 720 popfd 721 ret 722 723; 724; int13: save all the segment registers and call INT 13h. 725; Some CD-ROM BIOSes have been found to corrupt segment registers 726; and/or disable interrupts. 727; 728int13: 729 pushf 730 push bp 731 push ds 732 push es 733 push fs 734 push gs 735 int 13h 736 mov bp,sp 737 setc [bp+10] ; Propagate CF to the caller 738 pop gs 739 pop fs 740 pop es 741 pop ds 742 pop bp 743 popf 744 ret 745 746; 747; Get one sector. Convenience entry point. 748; 749getonesec: 750 mov bp,1 751 ; Fall through to getlinsec 752 753; 754; Get linear sectors - EBIOS LBA addressing, 2048-byte sectors. 755; 756; Input: 757; EAX - Linear sector number 758; ES:BX - Target buffer 759; BP - Sector count 760; 761 global getlinsec 762getlinsec: jmp word [cs:GetlinsecPtr] 763 764%ifndef DEBUG_MESSAGES 765 766; 767; First, the variants that we use when actually loading off a disk 768; (hybrid mode.) These are adapted versions of the equivalent routines 769; in ldlinux.asm. 770; 771 772; 773; getlinsec_ebios: 774; 775; getlinsec implementation for floppy/HDD EBIOS (EDD) 776; 777getlinsec_ebios: 778 xor edx,edx 779 shld edx,eax,2 780 shl eax,2 ; Convert to HDD sectors 781 add eax,[Hidden] 782 adc edx,[Hidden+4] 783 shl bp,2 784 785.loop: 786 push bp ; Sectors left 787.retry2: 788 call maxtrans ; Enforce maximum transfer size 789 movzx edi,bp ; Sectors we are about to read 790 mov cx,retry_count 791.retry: 792 793 ; Form DAPA on stack 794 push edx 795 push eax 796 push es 797 push bx 798 push di 799 push word 16 800 mov si,sp 801 pushad 802 mov dl,[DriveNumber] 803 push ds 804 push ss 805 pop ds ; DS <- SS 806 mov ah,42h ; Extended Read 807 call int13 808 pop ds 809 popad 810 lea sp,[si+16] ; Remove DAPA 811 jc .error 812 pop bp 813 add eax,edi ; Advance sector pointer 814 adc edx,0 815 sub bp,di ; Sectors left 816 shl di,9 ; 512-byte sectors 817 add bx,di ; Advance buffer pointer 818 and bp,bp 819 jnz .loop 820 821 ret 822 823.error: 824 ; Some systems seem to get "stuck" in an error state when 825 ; using EBIOS. Doesn't happen when using CBIOS, which is 826 ; good, since some other systems get timeout failures 827 ; waiting for the floppy disk to spin up. 828 829 pushad ; Try resetting the device 830 xor ax,ax 831 mov dl,[DriveNumber] 832 call int13 833 popad 834 loop .retry ; CX-- and jump if not zero 835 836 ;shr word [MaxTransfer],1 ; Reduce the transfer size 837 ;jnz .retry2 838 839 ; Total failure. Try falling back to CBIOS. 840 mov word [GetlinsecPtr], getlinsec_cbios 841 ;mov byte [MaxTransfer],63 ; Max possibe CBIOS transfer 842 843 pop bp 844 jmp getlinsec_cbios.loop 845 846; 847; getlinsec_cbios: 848; 849; getlinsec implementation for legacy CBIOS 850; 851getlinsec_cbios: 852 xor edx,edx 853 shl eax,2 ; Convert to HDD sectors 854 add eax,[Hidden] 855 shl bp,2 856 857.loop: 858 push edx 859 push eax 860 push bp 861 push bx 862 863 movzx esi,word [bsSecPerTrack] 864 movzx edi,word [bsHeads] 865 ; 866 ; Dividing by sectors to get (track,sector): we may have 867 ; up to 2^18 tracks, so we need to use 32-bit arithmetric. 868 ; 869 div esi 870 xor cx,cx 871 xchg cx,dx ; CX <- sector index (0-based) 872 ; EDX <- 0 873 ; eax = track # 874 div edi ; Convert track to head/cyl 875 876 ; We should test this, but it doesn't fit... 877 ; cmp eax,1023 878 ; ja .error 879 880 ; 881 ; Now we have AX = cyl, DX = head, CX = sector (0-based), 882 ; BP = sectors to transfer, SI = bsSecPerTrack, 883 ; ES:BX = data target 884 ; 885 886 call maxtrans ; Enforce maximum transfer size 887 888 ; Must not cross track boundaries, so BP <= SI-CX 889 sub si,cx 890 cmp bp,si 891 jna .bp_ok 892 mov bp,si 893.bp_ok: 894 895 shl ah,6 ; Because IBM was STOOPID 896 ; and thought 8 bits were enough 897 ; then thought 10 bits were enough... 898 inc cx ; Sector numbers are 1-based, sigh 899 or cl,ah 900 mov ch,al 901 mov dh,dl 902 mov dl,[DriveNumber] 903 xchg ax,bp ; Sector to transfer count 904 mov ah,02h ; Read sectors 905 mov bp,retry_count 906.retry: 907 pushad 908 call int13 909 popad 910 jc .error 911.resume: 912 movzx ecx,al ; ECX <- sectors transferred 913 shl ax,9 ; Convert sectors in AL to bytes in AX 914 pop bx 915 add bx,ax 916 pop bp 917 pop eax 918 pop edx 919 add eax,ecx 920 sub bp,cx 921 jnz .loop 922 ret 923 924.error: 925 dec bp 926 jnz .retry 927 928 xchg ax,bp ; Sectors transferred <- 0 929 shr word [MaxTransfer],1 930 jnz .resume 931 jmp disk_error 932 933; 934; Truncate BP to MaxTransfer 935; 936maxtrans: 937 cmp bp,[MaxTransfer] 938 jna .ok 939 mov bp,[MaxTransfer] 940.ok: ret 941 942%endif 943 944; 945; This is the variant we use for real CD-ROMs: 946; LBA, 2K sectors, some special error handling. 947; 948getlinsec_cdrom: 949 mov si,dapa ; Load up the DAPA 950 mov [si+4],bx 951 mov [si+6],es 952 mov [si+8],eax 953.loop: 954 push bp ; Sectors left 955 cmp bp,[MaxTransferCD] 956 jbe .bp_ok 957 mov bp,[MaxTransferCD] 958.bp_ok: 959 mov [si+2],bp 960 push si 961 mov dl,[DriveNumber] 962 mov ah,42h ; Extended Read 963 call xint13 964 pop si 965 pop bp 966 movzx eax,word [si+2] ; Sectors we read 967 add [si+8],eax ; Advance sector pointer 968 sub bp,ax ; Sectors left 969 shl ax,SECTOR_SHIFT-4 ; 2048-byte sectors -> segment 970 add [si+6],ax ; Advance buffer pointer 971 and bp,bp 972 jnz .loop 973 mov eax,[si+8] ; Next sector 974 ret 975 976 ; INT 13h with retry 977xint13: mov byte [RetryCount],retry_count 978.try: pushad 979 call int13 980 jc .error 981 add sp,byte 8*4 ; Clean up stack 982 ret 983.error: 984 mov [DiskError],ah ; Save error code 985 popad 986 mov [DiskSys],ax ; Save system call number 987 dec byte [RetryCount] 988 jz .real_error 989 push ax 990 mov al,[RetryCount] 991 mov ah,[dapa+2] ; Sector transfer count 992 cmp al,2 ; Only 2 attempts left 993 ja .nodanger 994 mov ah,1 ; Drop transfer size to 1 995 jmp short .setsize 996.nodanger: 997 cmp al,retry_count-2 998 ja .again ; First time, just try again 999 shr ah,1 ; Otherwise, try to reduce 1000 adc ah,0 ; the max transfer size, but not to 0 1001.setsize: 1002 mov [MaxTransferCD],ah 1003 mov [dapa+2],ah 1004.again: 1005 pop ax 1006 jmp .try 1007 1008.real_error: mov si,diskerr_msg 1009 call writemsg 1010 mov al,[DiskError] 1011 call writehex2 1012 mov si,oncall_str 1013 call writestr_early 1014 mov ax,[DiskSys] 1015 call writehex4 1016 mov si,ondrive_str 1017 call writestr_early 1018 mov al,dl 1019 call writehex2 1020 call crlf_early 1021 ; Fall through to kaboom 1022 1023; 1024; kaboom: write a message and bail out. Wait for a user keypress, 1025; then do a hard reboot. 1026; 1027 global kaboom 1028disk_error: 1029kaboom: 1030 RESET_STACK_AND_SEGS AX 1031 mov si,bailmsg 1032 pm_call pm_writestr 1033 pm_call pm_getchar 1034 cli 1035 mov word [BIOS_magic],0 ; Cold reboot 1036 jmp 0F000h:0FFF0h ; Reset vector address 1037 1038; ----------------------------------------------------------------------------- 1039; Common modules needed in the first sector 1040; ----------------------------------------------------------------------------- 1041 1042%include "writehex.inc" ; Hexadecimal output 1043 1044; ----------------------------------------------------------------------------- 1045; Data that needs to be in the first sector 1046; ----------------------------------------------------------------------------- 1047 1048 global syslinux_banner, copyright_str 1049syslinux_banner db CR, LF, MY_NAME, ' ', VERSION_STR, ' ', DATE_STR, ' ', 0 1050copyright_str db ' Copyright (C) 1994-' 1051 asciidec YEAR 1052 db ' H. Peter Anvin et al', CR, LF, 0 1053isolinux_str db 'isolinux: ', 0 1054%ifdef DEBUG_MESSAGES 1055startup_msg: db 'Starting up, DL = ', 0 1056spec_ok_msg: db 'Loaded spec packet OK, drive = ', 0 1057secsize_msg: db 'Sector size ', 0 1058offset_msg: db 'Main image LBA = ', 0 1059verify_msg: db 'Image csum verified.', CR, LF, 0 1060allread_msg db 'Image read, jumping to main code...', CR, LF, 0 1061%endif 1062noinfotable_msg db 'No boot info table, assuming single session disk...', CR, LF, 0 1063noinfoinspec_msg db 'Spec packet missing LBA information, trying to wing it...', CR, LF, 0 1064spec_err_msg: db 'Loading spec packet failed, trying to wing it...', CR, LF, 0 1065maybe_msg: db 'Found something at drive = ', 0 1066alright_msg: db 'Looks reasonable, continuing...', CR, LF, 0 1067nospec_msg db 'Extremely broken BIOS detected, last attempt with drive = ', 0 1068nothing_msg: db 'Failed to locate CD-ROM device; boot failed.', CR, LF 1069trysbm_msg db 'See http://syslinux.zytor.com/sbm for more information.', CR, LF, 0 1070diskerr_msg: db 'Disk error ', 0 1071oncall_str: db ', AX = ',0 1072ondrive_str: db ', drive ', 0 1073checkerr_msg: db 'Image checksum error, sorry...', CR, LF, 0 1074 1075err_bootfailed db CR, LF, 'Boot failed: press a key to retry...' 1076bailmsg equ err_bootfailed 1077crlf_msg db CR, LF 1078null_msg db 0 1079 1080bios_cdrom_str db 'ETCD', 0 1081%ifndef DEBUG_MESSAGES 1082bios_cbios_str db 'CHDD', 0 1083bios_ebios_str db 'EHDD' ,0 1084%endif 1085 1086 alignz 4 1087 global bios_cdrom 1088bios_cdrom: dw getlinsec_cdrom, bios_cdrom_str 1089%ifndef DEBUG_MESSAGES 1090bios_cbios: dw getlinsec_cbios, bios_cbios_str 1091bios_ebios: dw getlinsec_ebios, bios_ebios_str 1092%endif 1093 1094; Maximum transfer size 1095MaxTransfer dw 127 ; Hard disk modes 1096MaxTransferCD dw 32 ; CD mode 1097 1098rl_checkpt equ $ ; Must be <= 800h 1099 1100 ; This pads to the end of sector 0 and errors out on 1101 ; overflow. 1102 times 2048-($-$$) db 0 1103 1104; ---------------------------------------------------------------------------- 1105; End of code and data that have to be in the first sector 1106; ---------------------------------------------------------------------------- 1107 1108 section .text16 1109 1110all_read: 1111 1112; Test tracers 1113 TRACER 'T' 1114 TRACER '>' 1115 1116; 1117; Common initialization code 1118; 1119%include "init.inc" 1120 1121; Tell the user we got this far... 1122%ifndef DEBUG_MESSAGES ; Gets messy with debugging on 1123 mov si,copyright_str 1124 pm_call pm_writestr 1125%endif 1126 1127; 1128; Now we're all set to start with our *real* business. First load the 1129; configuration file (if any) and parse it. 1130; 1131; In previous versions I avoided using 32-bit registers because of a 1132; rumour some BIOSes clobbered the upper half of 32-bit registers at 1133; random. I figure, though, that if there are any of those still left 1134; they probably won't be trying to install Linux on them... 1135; 1136; The code is still ripe with 16-bitisms, though. Not worth the hassle 1137; to take'm out. In fact, we may want to put them back if we're going 1138; to boot ELKS at some point. 1139; 1140 1141; 1142; Now, we need to sniff out the actual filesystem data structures. 1143; mkisofs gave us a pointer to the primary volume descriptor 1144; (which will be at 16 only for a single-session disk!); from the PVD 1145; we should be able to find the rest of what we need to know. 1146; 1147init_fs: 1148 pushad 1149 mov eax,ROOT_FS_OPS 1150 mov dl,[DriveNumber] 1151 cmp word [BIOSType],bios_cdrom 1152 sete dh ; 1 for cdrom, 0 for hybrid mode 1153 jne .hybrid 1154 movzx ebp,word [MaxTransferCD] 1155 jmp .common 1156.hybrid: 1157 movzx ebp,word [MaxTransfer] 1158.common: 1159 mov ecx,[Hidden] 1160 mov ebx,[Hidden+4] 1161 mov si,[bsHeads] 1162 mov di,[bsSecPerTrack] 1163 pm_call pm_fs_init 1164 pm_call load_env32 1165enter_command: 1166auto_boot: 1167 jmp kaboom ; load_env32() should never return. If 1168 ; it does, then kaboom! 1169 popad 1170 1171 section .rodata 1172 alignz 4 1173ROOT_FS_OPS: 1174 extern iso_fs_ops 1175 dd iso_fs_ops 1176 dd 0 1177 1178 section .text16 1179 1180%ifdef DEBUG_TRACERS 1181; 1182; debug hack to print a character with minimal code impact 1183; 1184debug_tracer: pushad 1185 pushfd 1186 mov bp,sp 1187 mov bx,[bp+9*4] ; Get return address 1188 mov al,[cs:bx] ; Get data byte 1189 inc word [bp+9*4] ; Return to after data byte 1190 call writechr 1191 popfd 1192 popad 1193 ret 1194%endif ; DEBUG_TRACERS 1195 1196 section .bss16 1197 alignb 4 1198ThisKbdTo resd 1 ; Temporary holder for KbdTimeout 1199ThisTotalTo resd 1 ; Temporary holder for TotalTimeout 1200KernelExtPtr resw 1 ; During search, final null pointer 1201FuncFlag resb 1 ; Escape sequences received from keyboard 1202KernelType resb 1 ; Kernel type, from vkernel, if known 1203 global KernelName 1204KernelName resb FILENAME_MAX ; Mangled name for kernel 1205 1206 section .text16 1207; 1208; COM32 vestigial data structure 1209; 1210%include "com32.inc" 1211 1212; 1213; Common local boot code 1214; 1215%include "localboot.inc" 1216 1217; ----------------------------------------------------------------------------- 1218; Common modules 1219; ----------------------------------------------------------------------------- 1220 1221%include "common.inc" ; Universal modules 1222 1223; ----------------------------------------------------------------------------- 1224; Begin data section 1225; ----------------------------------------------------------------------------- 1226 1227 section .data16 1228err_disk_image db 'Cannot load disk image (invalid file)?', CR, LF, 0 1229 1230 section .bss16 1231 global OrigFDCTabPtr 1232OrigFDCTabPtr resd 1 ; Keep bios_cleanup_hardware() honest 1233