| ; -*- fundamental -*- (asm-mode sucks) |
| ; **************************************************************************** |
| ; |
| ; isolinux.asm |
| ; |
| ; A program to boot Linux kernels off a CD-ROM using the El Torito |
| ; boot standard in "no emulation" mode, making the entire filesystem |
| ; available. It is based on the SYSLINUX boot loader for MS-DOS |
| ; floppies. |
| ; |
| ; Copyright 1994-2009 H. Peter Anvin - All Rights Reserved |
| ; Copyright 2009 Intel Corporation; author: H. Peter Anvin |
| ; |
| ; This program is free software; you can redistribute it and/or modify |
| ; it under the terms of the GNU General Public License as published by |
| ; the Free Software Foundation, Inc., 53 Temple Place Ste 330, |
| ; Boston MA 02111-1307, USA; either version 2 of the License, or |
| ; (at your option) any later version; incorporated herein by reference. |
| ; |
| ; **************************************************************************** |
| |
| %define IS_ISOLINUX 1 |
| %include "head.inc" |
| |
| ; |
| ; Some semi-configurable constants... change on your own risk. |
| ; |
| my_id equ isolinux_id |
| NULLFILE equ 0 ; Zero byte == null file name |
| NULLOFFSET equ 0 ; Position in which to look |
| retry_count equ 6 ; How patient are we with the BIOS? |
| %assign HIGHMEM_SLOP 128*1024 ; Avoid this much memory near the top |
| SECTOR_SHIFT equ 11 ; 2048 bytes/sector (El Torito requirement) |
| SECTOR_SIZE equ (1 << SECTOR_SHIFT) |
| |
| ROOT_DIR_WORD equ 0x002F |
| |
| ; --------------------------------------------------------------------------- |
| ; BEGIN CODE |
| ; --------------------------------------------------------------------------- |
| |
| ; |
| ; Memory below this point is reserved for the BIOS and the MBR |
| ; |
| section .earlybss |
| global trackbuf |
| trackbufsize equ 8192 |
| trackbuf resb trackbufsize ; Track buffer goes here |
| ; ends at 2800h |
| |
| ; Some of these are touched before the whole image |
| ; is loaded. DO NOT move this to .bss16/.uibss. |
| section .earlybss |
| global BIOSName |
| alignb 4 |
| FirstSecSum resd 1 ; Checksum of bytes 64-2048 |
| ImageDwords resd 1 ; isolinux.bin size, dwords |
| InitStack resd 1 ; Initial stack pointer (SS:SP) |
| DiskSys resw 1 ; Last INT 13h call |
| ImageSectors resw 1 ; isolinux.bin size, sectors |
| ; These following two are accessed as a single dword... |
| GetlinsecPtr resw 1 ; The sector-read pointer |
| BIOSName resw 1 ; Display string for BIOS type |
| %define HAVE_BIOSNAME 1 |
| global BIOSType |
| BIOSType resw 1 |
| DiskError resb 1 ; Error code for disk I/O |
| global DriveNumber |
| DriveNumber resb 1 ; CD-ROM BIOS drive number |
| ISOFlags resb 1 ; Flags for ISO directory search |
| RetryCount resb 1 ; Used for disk access retries |
| |
| alignb 8 |
| global Hidden |
| Hidden resq 1 ; Used in hybrid mode |
| bsSecPerTrack resw 1 ; Used in hybrid mode |
| bsHeads resw 1 ; Used in hybrid mode |
| |
| |
| ; |
| ; El Torito spec packet |
| ; |
| |
| alignb 8 |
| _spec_start equ $ |
| global spec_packet |
| spec_packet: resb 1 ; Size of packet |
| sp_media: resb 1 ; Media type |
| sp_drive: resb 1 ; Drive number |
| sp_controller: resb 1 ; Controller index |
| sp_lba: resd 1 ; LBA for emulated disk image |
| sp_devspec: resw 1 ; IDE/SCSI information |
| sp_buffer: resw 1 ; User-provided buffer |
| sp_loadseg: resw 1 ; Load segment |
| sp_sectors: resw 1 ; Sector count |
| sp_chs: resb 3 ; Simulated CHS geometry |
| sp_dummy: resb 1 ; Scratch, safe to overwrite |
| |
| ; |
| ; EBIOS drive parameter packet |
| ; |
| alignb 8 |
| drive_params: resw 1 ; Buffer size |
| dp_flags: resw 1 ; Information flags |
| dp_cyl: resd 1 ; Physical cylinders |
| dp_head: resd 1 ; Physical heads |
| dp_sec: resd 1 ; Physical sectors/track |
| dp_totalsec: resd 2 ; Total sectors |
| dp_secsize: resw 1 ; Bytes per sector |
| dp_dpte: resd 1 ; Device Parameter Table |
| dp_dpi_key: resw 1 ; 0BEDDh if rest valid |
| dp_dpi_len: resb 1 ; DPI len |
| resb 1 |
| resw 1 |
| dp_bus: resb 4 ; Host bus type |
| dp_interface: resb 8 ; Interface type |
| db_i_path: resd 2 ; Interface path |
| db_d_path: resd 2 ; Device path |
| resb 1 |
| db_dpi_csum: resb 1 ; Checksum for DPI info |
| |
| ; |
| ; EBIOS disk address packet |
| ; |
| alignb 8 |
| dapa: resw 1 ; Packet size |
| .count: resw 1 ; Block count |
| .off: resw 1 ; Offset of buffer |
| .seg: resw 1 ; Segment of buffer |
| .lba: resd 2 ; LBA (LSW, MSW) |
| |
| ; |
| ; Spec packet for disk image emulation |
| ; |
| alignb 8 |
| dspec_packet: resb 1 ; Size of packet |
| dsp_media: resb 1 ; Media type |
| dsp_drive: resb 1 ; Drive number |
| dsp_controller: resb 1 ; Controller index |
| dsp_lba: resd 1 ; LBA for emulated disk image |
| dsp_devspec: resw 1 ; IDE/SCSI information |
| dsp_buffer: resw 1 ; User-provided buffer |
| dsp_loadseg: resw 1 ; Load segment |
| dsp_sectors: resw 1 ; Sector count |
| dsp_chs: resb 3 ; Simulated CHS geometry |
| dsp_dummy: resb 1 ; Scratch, safe to overwrite |
| |
| alignb 4 |
| _spec_end equ $ |
| _spec_len equ _spec_end - _spec_start |
| |
| section .init |
| ;; |
| ;; Primary entry point. Because BIOSes are buggy, we only load the first |
| ;; CD-ROM sector (2K) of the file, so the number one priority is actually |
| ;; loading the rest. |
| ;; |
| global StackBuf |
| StackBuf equ STACK_TOP-44 ; 44 bytes needed for |
| ; the bootsector chainloading |
| ; code! |
| global OrigESDI |
| OrigESDI equ StackBuf-4 ; The high dword on the stack |
| StackHome equ OrigESDI |
| |
| bootsec equ $ |
| |
| _start: ; Far jump makes sure we canonicalize the address |
| cli |
| jmp 0:_start1 |
| times 8-($-$$) nop ; Pad to file offset 8 |
| |
| ; This table hopefully gets filled in by mkisofs using the |
| ; -boot-info-table option. If not, the values in this |
| ; table are default values that we can use to get us what |
| ; we need, at least under a certain set of assumptions. |
| global iso_boot_info |
| iso_boot_info: |
| bi_pvd: dd 16 ; LBA of primary volume descriptor |
| bi_file: dd 0 ; LBA of boot file |
| bi_length: dd 0xdeadbeef ; Length of boot file |
| bi_csum: dd 0xdeadbeef ; Checksum of boot file |
| bi_reserved: times 10 dd 0xdeadbeef ; Reserved |
| bi_end: |
| |
| ; Custom entry point for the hybrid-mode disk. |
| ; The following values will have been pushed onto the |
| ; entry stack: |
| ; - partition offset (qword) |
| ; - ES |
| ; - DI |
| ; - DX (including drive number) |
| ; - CBIOS Heads |
| ; - CBIOS Sectors |
| ; - EBIOS flag |
| ; (top of stack) |
| ; |
| ; If we had an old isohybrid, the partition offset will |
| ; be missing; we can check for that with sp >= 0x7c00. |
| ; Serious hack alert. |
| %ifndef DEBUG_MESSAGES |
| _hybrid_signature: |
| dd 0x7078c0fb ; An arbitrary number... |
| |
| _start_hybrid: |
| pop cx ; EBIOS flag |
| pop word [cs:bsSecPerTrack] |
| pop word [cs:bsHeads] |
| pop dx |
| pop di |
| pop es |
| xor eax,eax |
| xor ebx,ebx |
| cmp sp,7C00h |
| jae .nooffset |
| pop eax |
| pop ebx |
| .nooffset: |
| mov si,bios_cbios |
| jcxz _start_common |
| mov si,bios_ebios |
| jmp _start_common |
| %endif |
| |
| _start1: |
| mov si,bios_cdrom |
| xor eax,eax |
| xor ebx,ebx |
| _start_common: |
| mov [cs:InitStack],sp ; Save initial stack pointer |
| mov [cs:InitStack+2],ss |
| xor cx,cx |
| mov ss,cx |
| mov sp,StackBuf ; Set up stack |
| push es ; Save initial ES:DI -> $PnP pointer |
| push di |
| mov ds,cx |
| mov es,cx |
| mov fs,cx |
| mov gs,cx |
| sti |
| cld |
| |
| mov [Hidden],eax |
| mov [Hidden+4],ebx |
| |
| mov [BIOSType],si |
| mov eax,[si] |
| mov [GetlinsecPtr],eax |
| |
| ; Show signs of life |
| mov si,syslinux_banner |
| call writestr_early |
| %ifdef DEBUG_MESSAGES |
| mov si,copyright_str |
| %else |
| mov si,[BIOSName] |
| %endif |
| call writestr_early |
| |
| ; |
| ; Before modifying any memory, get the checksum of bytes |
| ; 64-2048 |
| ; |
| initial_csum: xor edi,edi |
| mov si,bi_end |
| mov cx,(SECTOR_SIZE-64) >> 2 |
| .loop: lodsd |
| add edi,eax |
| loop .loop |
| mov [FirstSecSum],edi |
| |
| mov [DriveNumber],dl |
| %ifdef DEBUG_MESSAGES |
| mov si,startup_msg |
| call writemsg |
| mov al,dl |
| call writehex2 |
| call crlf_early |
| %endif |
| ; |
| ; Initialize spec packet buffers |
| ; |
| mov di,_spec_start |
| mov cx,_spec_len >> 2 |
| xor eax,eax |
| rep stosd |
| |
| ; Initialize length field of the various packets |
| mov byte [spec_packet],13h |
| mov byte [drive_params],30 |
| mov byte [dapa],16 |
| mov byte [dspec_packet],13h |
| |
| ; Other nonzero fields |
| inc word [dsp_sectors] |
| |
| ; Are we just pretending to be a CD-ROM? |
| cmp word [BIOSType],bios_cdrom |
| jne found_drive ; If so, no spec packet... |
| |
| ; Now figure out what we're actually doing |
| ; Note: use passed-in DL value rather than 7Fh because |
| ; at least some BIOSes will get the wrong value otherwise |
| mov ax,4B01h ; Get disk emulation status |
| mov dl,[DriveNumber] |
| mov si,spec_packet |
| call int13 |
| jc award_hack ; changed for BrokenAwardHack |
| mov dl,[DriveNumber] |
| cmp [sp_drive],dl ; Should contain the drive number |
| jne spec_query_failed |
| |
| %ifdef DEBUG_MESSAGES |
| mov si,spec_ok_msg |
| call writemsg |
| mov al,byte [sp_drive] |
| call writehex2 |
| call crlf_early |
| %endif |
| |
| found_drive: |
| ; Alright, we have found the drive. Now, try to find the |
| ; boot file itself. If we have a boot info table, life is |
| ; good; if not, we have to make some assumptions, and try |
| ; to figure things out ourselves. In particular, the |
| ; assumptions we have to make are: |
| ; - single session only |
| ; - only one boot entry (no menu or other alternatives) |
| |
| cmp dword [bi_file],0 ; Address of code to load |
| jne found_file ; Boot info table present :) |
| |
| %ifdef DEBUG_MESSAGES |
| mov si,noinfotable_msg |
| call writemsg |
| %endif |
| |
| ; No such luck. See if the spec packet contained one. |
| mov eax,[sp_lba] |
| and eax,eax |
| jz set_file ; Good enough |
| |
| %ifdef DEBUG_MESSAGES |
| mov si,noinfoinspec_msg |
| call writemsg |
| %endif |
| |
| ; No such luck. Get the Boot Record Volume, assuming single |
| ; session disk, and that we're the first entry in the chain. |
| mov eax,17 ; Assumed address of BRV |
| mov bx,trackbuf |
| call getonesec |
| |
| mov eax,[trackbuf+47h] ; Get boot catalog address |
| mov bx,trackbuf |
| call getonesec ; Get boot catalog |
| |
| mov eax,[trackbuf+28h] ; First boot entry |
| ; And hope and pray this is us... |
| |
| ; Some BIOSes apparently have limitations on the size |
| ; that may be loaded (despite the El Torito spec being very |
| ; clear on the fact that it must all be loaded.) Therefore, |
| ; we load it ourselves, and *bleep* the BIOS. |
| |
| set_file: |
| mov [bi_file],eax |
| |
| found_file: |
| ; Set up boot file sizes |
| mov eax,[bi_length] |
| sub eax,SECTOR_SIZE-3 ; ... minus sector loaded |
| shr eax,2 ; bytes->dwords |
| mov [ImageDwords],eax ; boot file dwords |
| add eax,((SECTOR_SIZE-1) >> 2) |
| shr eax,SECTOR_SHIFT-2 ; dwords->sectors |
| mov [ImageSectors],ax ; boot file sectors |
| |
| mov eax,[bi_file] ; Address of code to load |
| inc eax ; Don't reload bootstrap code |
| %ifdef DEBUG_MESSAGES |
| mov si,offset_msg |
| call writemsg |
| call writehex8 |
| call crlf_early |
| %endif |
| |
| ; Load the rest of the file. However, just in case there |
| ; are still BIOSes with 64K wraparound problems, we have to |
| ; take some extra precautions. Since the normal load |
| ; address (TEXT_START) is *not* 2K-sector-aligned, we round |
| ; the target address upward to a sector boundary, |
| ; and then move the entire thing down as a unit. |
| MaxLMA equ 384*1024 ; Reasonable limit (384K) |
| |
| mov bx,((TEXT_START+2*SECTOR_SIZE-1) & ~(SECTOR_SIZE-1)) >> 4 |
| mov bp,[ImageSectors] |
| push bx ; Load segment address |
| |
| .more: |
| push bx ; Segment address |
| push bp ; Sector count |
| mov es,bx |
| mov cx,0xfff |
| and bx,cx |
| inc cx |
| sub cx,bx |
| shr cx,SECTOR_SHIFT - 4 |
| jnz .notaligned |
| mov cx,0x10000 >> SECTOR_SHIFT ; Full 64K segment possible |
| .notaligned: |
| cmp bp,cx |
| jbe .ok |
| mov bp,cx |
| .ok: |
| xor bx,bx |
| push bp |
| push eax |
| call getlinsec |
| pop eax |
| pop cx |
| movzx edx,cx |
| pop bp |
| pop bx |
| |
| shl cx,SECTOR_SHIFT - 4 |
| add bx,cx |
| add eax,edx |
| sub bp,dx |
| jnz .more |
| |
| ; Move the image into place, and also verify the |
| ; checksum |
| pop ax ; Load segment address |
| mov bx,(TEXT_START + SECTOR_SIZE) >> 4 |
| mov ecx,[ImageDwords] |
| mov edi,[FirstSecSum] ; First sector checksum |
| xor si,si |
| |
| move_verify_image: |
| .setseg: |
| mov ds,ax |
| mov es,bx |
| .loop: |
| mov edx,[si] |
| add edi,edx |
| dec ecx |
| mov [es:si],edx |
| jz .done |
| add si,4 |
| jnz .loop |
| add ax,1000h |
| add bx,1000h |
| jmp .setseg |
| .done: |
| mov ax,cs |
| mov ds,ax |
| mov es,ax |
| |
| ; Verify the checksum on the loaded image. |
| cmp [bi_csum],edi |
| je integrity_ok |
| |
| mov si,checkerr_msg |
| call writemsg |
| jmp kaboom |
| |
| integrity_ok: |
| %ifdef DEBUG_MESSAGES |
| mov si,allread_msg |
| call writemsg |
| %endif |
| jmp all_read ; Jump to main code |
| |
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| ;; Start of BrokenAwardHack --- 10-nov-2002 [email protected] |
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| ;; |
| ;; There is a problem with certain versions of the AWARD BIOS ... |
| ;; the boot sector will be loaded and executed correctly, but, because the |
| ;; int 13 vector points to the wrong code in the BIOS, every attempt to |
| ;; load the spec packet will fail. We scan for the equivalent of |
| ;; |
| ;; mov ax,0201h |
| ;; mov bx,7c00h |
| ;; mov cx,0006h |
| ;; mov dx,0180h |
| ;; pushf |
| ;; call <direct far> |
| ;; |
| ;; and use <direct far> as the new vector for int 13. The code above is |
| ;; used to load the boot code into ram, and there should be no reason |
| ;; for anybody to change it now or in the future. There are no opcodes |
| ;; that use encodings relativ to IP, so scanning is easy. If we find the |
| ;; code above in the BIOS code we can be pretty sure to run on a machine |
| ;; with an broken AWARD BIOS ... |
| ;; |
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| ;; |
| %ifdef DEBUG_MESSAGES ;; |
| ;; |
| award_notice db "Trying BrokenAwardHack first ...",CR,LF,0 ;; |
| award_not_orig db "BAH: Original Int 13 vector : ",0 ;; |
| award_not_new db "BAH: Int 13 vector changed to : ",0 ;; |
| award_not_succ db "BAH: SUCCESS",CR,LF,0 ;; |
| award_not_fail db "BAH: FAILURE" ;; |
| award_not_crlf db CR,LF,0 ;; |
| ;; |
| %endif ;; |
| ;; |
| award_oldint13 dd 0 ;; |
| award_string db 0b8h,1,2,0bbh,0,7ch,0b9h,6,0,0bah,80h,1,09ch,09ah ;; |
| ;; |
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| award_hack: mov si,spec_err_msg ; Moved to this place from |
| call writemsg ; spec_query_failed |
| ; |
| %ifdef DEBUG_MESSAGES ; |
| ; |
| mov si,award_notice ; display our plan |
| call writemsg ; |
| mov si,award_not_orig ; display original int 13 |
| call writemsg ; vector |
| %endif ; |
| mov eax,[13h*4] ; |
| mov [award_oldint13],eax ; |
| ; |
| %ifdef DEBUG_MESSAGES ; |
| ; |
| call writehex8 ; |
| mov si,award_not_crlf ; |
| call writestr_early ; |
| %endif ; |
| push es ; save ES |
| mov ax,0f000h ; ES = BIOS Seg |
| mov es,ax ; |
| cld ; |
| xor di,di ; start at ES:DI = f000:0 |
| award_loop: push di ; save DI |
| mov si,award_string ; scan for award_string |
| mov cx,7 ; length of award_string = 7dw |
| repz cmpsw ; compare |
| pop di ; restore DI |
| jcxz award_found ; jmp if found |
| inc di ; not found, inc di |
| jno award_loop ; |
| ; |
| award_failed: pop es ; No, not this way :-(( |
| award_fail2: ; |
| ; |
| %ifdef DEBUG_MESSAGES ; |
| ; |
| mov si,award_not_fail ; display failure ... |
| call writemsg ; |
| %endif ; |
| mov eax,[award_oldint13] ; restore the original int |
| or eax,eax ; 13 vector if there is one |
| jz spec_query_failed ; and try other workarounds |
| mov [13h*4],eax ; |
| jmp spec_query_failed ; |
| ; |
| award_found: mov eax,[es:di+0eh] ; load possible int 13 addr |
| pop es ; restore ES |
| ; |
| cmp eax,[award_oldint13] ; give up if this is the |
| jz award_failed ; active int 13 vector, |
| mov [13h*4],eax ; otherwise change 0:13h*4 |
| ; |
| ; |
| %ifdef DEBUG_MESSAGES ; |
| ; |
| push eax ; display message and |
| mov si,award_not_new ; new vector address |
| call writemsg ; |
| pop eax ; |
| call writehex8 ; |
| mov si,award_not_crlf ; |
| call writestr_early ; |
| %endif ; |
| mov ax,4B01h ; try to read the spec packet |
| mov dl,[DriveNumber] ; now ... it should not fail |
| mov si,spec_packet ; any longer |
| int 13h ; |
| jc award_fail2 ; |
| ; |
| %ifdef DEBUG_MESSAGES ; |
| ; |
| mov si,award_not_succ ; display our SUCCESS |
| call writemsg ; |
| %endif ; |
| jmp found_drive ; and leave error recovery code |
| ; |
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| ;; End of BrokenAwardHack ---- 10-nov-2002 [email protected] |
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| |
| |
| ; INT 13h, AX=4B01h, DL=<passed in value> failed. |
| ; Try to scan the entire 80h-FFh from the end. |
| |
| spec_query_failed: |
| |
| ; some code moved to BrokenAwardHack |
| |
| mov dl,0FFh |
| .test_loop: pusha |
| mov ax,4B01h |
| mov si,spec_packet |
| mov byte [si],13h ; Size of buffer |
| call int13 |
| popa |
| jc .still_broken |
| |
| mov si,maybe_msg |
| call writemsg |
| mov al,dl |
| call writehex2 |
| call crlf_early |
| |
| cmp byte [sp_drive],dl |
| jne .maybe_broken |
| |
| ; Okay, good enough... |
| mov si,alright_msg |
| call writemsg |
| .found_drive0: mov [DriveNumber],dl |
| .found_drive: jmp found_drive |
| |
| ; Award BIOS 4.51 apparently passes garbage in sp_drive, |
| ; but if this was the drive number originally passed in |
| ; DL then consider it "good enough" |
| .maybe_broken: |
| mov al,[DriveNumber] |
| cmp al,dl |
| je .found_drive |
| |
| ; Intel Classic R+ computer with Adaptec 1542CP BIOS 1.02 |
| ; passes garbage in sp_drive, and the drive number originally |
| ; passed in DL does not have 80h bit set. |
| or al,80h |
| cmp al,dl |
| je .found_drive0 |
| |
| .still_broken: dec dx |
| cmp dl, 80h |
| jnb .test_loop |
| |
| ; No spec packet anywhere. Some particularly pathetic |
| ; BIOSes apparently don't even implement function |
| ; 4B01h, so we can't query a spec packet no matter |
| ; what. If we got a drive number in DL, then try to |
| ; use it, and if it works, then well... |
| mov dl,[DriveNumber] |
| cmp dl,81h ; Should be 81-FF at least |
| jb fatal_error ; If not, it's hopeless |
| |
| ; Write a warning to indicate we're on *very* thin ice now |
| mov si,nospec_msg |
| call writemsg |
| mov al,dl |
| call writehex2 |
| call crlf_early |
| mov si,trysbm_msg |
| call writemsg |
| jmp .found_drive ; Pray that this works... |
| |
| fatal_error: |
| mov si,nothing_msg |
| call writemsg |
| |
| .norge: jmp short .norge |
| |
| ; Information message (DS:SI) output |
| ; Prefix with "isolinux: " |
| ; |
| writemsg: push ax |
| push si |
| mov si,isolinux_str |
| call writestr_early |
| pop si |
| call writestr_early |
| pop ax |
| ret |
| |
| writestr_early: |
| pushfd |
| pushad |
| .top: lodsb |
| and al,al |
| jz .end |
| call writechr |
| jmp short .top |
| .end: popad |
| popfd |
| ret |
| |
| crlf_early: push ax |
| mov al,CR |
| call writechr |
| mov al,LF |
| call writechr |
| pop ax |
| ret |
| |
| ; |
| ; Write a character to the screen. There is a more "sophisticated" |
| ; version of this in the subsequent code, so we patch the pointer |
| ; when appropriate. |
| ; |
| |
| writechr: |
| .simple: |
| pushfd |
| pushad |
| mov ah,0Eh |
| xor bx,bx |
| int 10h |
| popad |
| popfd |
| ret |
| |
| ; |
| ; int13: save all the segment registers and call INT 13h. |
| ; Some CD-ROM BIOSes have been found to corrupt segment registers |
| ; and/or disable interrupts. |
| ; |
| int13: |
| pushf |
| push bp |
| push ds |
| push es |
| push fs |
| push gs |
| int 13h |
| mov bp,sp |
| setc [bp+10] ; Propagate CF to the caller |
| pop gs |
| pop fs |
| pop es |
| pop ds |
| pop bp |
| popf |
| ret |
| |
| ; |
| ; Get one sector. Convenience entry point. |
| ; |
| getonesec: |
| mov bp,1 |
| ; Fall through to getlinsec |
| |
| ; |
| ; Get linear sectors - EBIOS LBA addressing, 2048-byte sectors. |
| ; |
| ; Input: |
| ; EAX - Linear sector number |
| ; ES:BX - Target buffer |
| ; BP - Sector count |
| ; |
| global getlinsec |
| getlinsec: jmp word [cs:GetlinsecPtr] |
| |
| %ifndef DEBUG_MESSAGES |
| |
| ; |
| ; First, the variants that we use when actually loading off a disk |
| ; (hybrid mode.) These are adapted versions of the equivalent routines |
| ; in ldlinux.asm. |
| ; |
| |
| ; |
| ; getlinsec_ebios: |
| ; |
| ; getlinsec implementation for floppy/HDD EBIOS (EDD) |
| ; |
| getlinsec_ebios: |
| xor edx,edx |
| shld edx,eax,2 |
| shl eax,2 ; Convert to HDD sectors |
| add eax,[Hidden] |
| adc edx,[Hidden+4] |
| shl bp,2 |
| |
| .loop: |
| push bp ; Sectors left |
| .retry2: |
| call maxtrans ; Enforce maximum transfer size |
| movzx edi,bp ; Sectors we are about to read |
| mov cx,retry_count |
| .retry: |
| |
| ; Form DAPA on stack |
| push edx |
| push eax |
| push es |
| push bx |
| push di |
| push word 16 |
| mov si,sp |
| pushad |
| mov dl,[DriveNumber] |
| push ds |
| push ss |
| pop ds ; DS <- SS |
| mov ah,42h ; Extended Read |
| call int13 |
| pop ds |
| popad |
| lea sp,[si+16] ; Remove DAPA |
| jc .error |
| pop bp |
| add eax,edi ; Advance sector pointer |
| adc edx,0 |
| sub bp,di ; Sectors left |
| shl di,9 ; 512-byte sectors |
| add bx,di ; Advance buffer pointer |
| and bp,bp |
| jnz .loop |
| |
| ret |
| |
| .error: |
| ; Some systems seem to get "stuck" in an error state when |
| ; using EBIOS. Doesn't happen when using CBIOS, which is |
| ; good, since some other systems get timeout failures |
| ; waiting for the floppy disk to spin up. |
| |
| pushad ; Try resetting the device |
| xor ax,ax |
| mov dl,[DriveNumber] |
| call int13 |
| popad |
| loop .retry ; CX-- and jump if not zero |
| |
| ;shr word [MaxTransfer],1 ; Reduce the transfer size |
| ;jnz .retry2 |
| |
| ; Total failure. Try falling back to CBIOS. |
| mov word [GetlinsecPtr], getlinsec_cbios |
| ;mov byte [MaxTransfer],63 ; Max possibe CBIOS transfer |
| |
| pop bp |
| jmp getlinsec_cbios.loop |
| |
| ; |
| ; getlinsec_cbios: |
| ; |
| ; getlinsec implementation for legacy CBIOS |
| ; |
| getlinsec_cbios: |
| xor edx,edx |
| shl eax,2 ; Convert to HDD sectors |
| add eax,[Hidden] |
| shl bp,2 |
| |
| .loop: |
| push edx |
| push eax |
| push bp |
| push bx |
| |
| movzx esi,word [bsSecPerTrack] |
| movzx edi,word [bsHeads] |
| ; |
| ; Dividing by sectors to get (track,sector): we may have |
| ; up to 2^18 tracks, so we need to use 32-bit arithmetric. |
| ; |
| div esi |
| xor cx,cx |
| xchg cx,dx ; CX <- sector index (0-based) |
| ; EDX <- 0 |
| ; eax = track # |
| div edi ; Convert track to head/cyl |
| |
| ; We should test this, but it doesn't fit... |
| ; cmp eax,1023 |
| ; ja .error |
| |
| ; |
| ; Now we have AX = cyl, DX = head, CX = sector (0-based), |
| ; BP = sectors to transfer, SI = bsSecPerTrack, |
| ; ES:BX = data target |
| ; |
| |
| call maxtrans ; Enforce maximum transfer size |
| |
| ; Must not cross track boundaries, so BP <= SI-CX |
| sub si,cx |
| cmp bp,si |
| jna .bp_ok |
| mov bp,si |
| .bp_ok: |
| |
| shl ah,6 ; Because IBM was STOOPID |
| ; and thought 8 bits were enough |
| ; then thought 10 bits were enough... |
| inc cx ; Sector numbers are 1-based, sigh |
| or cl,ah |
| mov ch,al |
| mov dh,dl |
| mov dl,[DriveNumber] |
| xchg ax,bp ; Sector to transfer count |
| mov ah,02h ; Read sectors |
| mov bp,retry_count |
| .retry: |
| pushad |
| call int13 |
| popad |
| jc .error |
| .resume: |
| movzx ecx,al ; ECX <- sectors transferred |
| shl ax,9 ; Convert sectors in AL to bytes in AX |
| pop bx |
| add bx,ax |
| pop bp |
| pop eax |
| pop edx |
| add eax,ecx |
| sub bp,cx |
| jnz .loop |
| ret |
| |
| .error: |
| dec bp |
| jnz .retry |
| |
| xchg ax,bp ; Sectors transferred <- 0 |
| shr word [MaxTransfer],1 |
| jnz .resume |
| jmp disk_error |
| |
| ; |
| ; Truncate BP to MaxTransfer |
| ; |
| maxtrans: |
| cmp bp,[MaxTransfer] |
| jna .ok |
| mov bp,[MaxTransfer] |
| .ok: ret |
| |
| %endif |
| |
| ; |
| ; This is the variant we use for real CD-ROMs: |
| ; LBA, 2K sectors, some special error handling. |
| ; |
| getlinsec_cdrom: |
| mov si,dapa ; Load up the DAPA |
| mov [si+4],bx |
| mov [si+6],es |
| mov [si+8],eax |
| .loop: |
| push bp ; Sectors left |
| cmp bp,[MaxTransferCD] |
| jbe .bp_ok |
| mov bp,[MaxTransferCD] |
| .bp_ok: |
| mov [si+2],bp |
| push si |
| mov dl,[DriveNumber] |
| mov ah,42h ; Extended Read |
| call xint13 |
| pop si |
| pop bp |
| movzx eax,word [si+2] ; Sectors we read |
| add [si+8],eax ; Advance sector pointer |
| sub bp,ax ; Sectors left |
| shl ax,SECTOR_SHIFT-4 ; 2048-byte sectors -> segment |
| add [si+6],ax ; Advance buffer pointer |
| and bp,bp |
| jnz .loop |
| mov eax,[si+8] ; Next sector |
| ret |
| |
| ; INT 13h with retry |
| xint13: mov byte [RetryCount],retry_count |
| .try: pushad |
| call int13 |
| jc .error |
| add sp,byte 8*4 ; Clean up stack |
| ret |
| .error: |
| mov [DiskError],ah ; Save error code |
| popad |
| mov [DiskSys],ax ; Save system call number |
| dec byte [RetryCount] |
| jz .real_error |
| push ax |
| mov al,[RetryCount] |
| mov ah,[dapa+2] ; Sector transfer count |
| cmp al,2 ; Only 2 attempts left |
| ja .nodanger |
| mov ah,1 ; Drop transfer size to 1 |
| jmp short .setsize |
| .nodanger: |
| cmp al,retry_count-2 |
| ja .again ; First time, just try again |
| shr ah,1 ; Otherwise, try to reduce |
| adc ah,0 ; the max transfer size, but not to 0 |
| .setsize: |
| mov [MaxTransferCD],ah |
| mov [dapa+2],ah |
| .again: |
| pop ax |
| jmp .try |
| |
| .real_error: mov si,diskerr_msg |
| call writemsg |
| mov al,[DiskError] |
| call writehex2 |
| mov si,oncall_str |
| call writestr_early |
| mov ax,[DiskSys] |
| call writehex4 |
| mov si,ondrive_str |
| call writestr_early |
| mov al,dl |
| call writehex2 |
| call crlf_early |
| ; Fall through to kaboom |
| |
| ; |
| ; kaboom: write a message and bail out. Wait for a user keypress, |
| ; then do a hard reboot. |
| ; |
| global kaboom |
| disk_error: |
| kaboom: |
| RESET_STACK_AND_SEGS AX |
| mov si,bailmsg |
| pm_call pm_writestr |
| pm_call pm_getchar |
| cli |
| mov word [BIOS_magic],0 ; Cold reboot |
| jmp 0F000h:0FFF0h ; Reset vector address |
| |
| ; ----------------------------------------------------------------------------- |
| ; Common modules needed in the first sector |
| ; ----------------------------------------------------------------------------- |
| |
| %include "writehex.inc" ; Hexadecimal output |
| |
| ; ----------------------------------------------------------------------------- |
| ; Data that needs to be in the first sector |
| ; ----------------------------------------------------------------------------- |
| |
| global syslinux_banner, copyright_str |
| syslinux_banner db CR, LF, MY_NAME, ' ', VERSION_STR, ' ', DATE_STR, ' ', 0 |
| copyright_str db ' Copyright (C) 1994-' |
| asciidec YEAR |
| db ' H. Peter Anvin et al', CR, LF, 0 |
| isolinux_str db 'isolinux: ', 0 |
| %ifdef DEBUG_MESSAGES |
| startup_msg: db 'Starting up, DL = ', 0 |
| spec_ok_msg: db 'Loaded spec packet OK, drive = ', 0 |
| secsize_msg: db 'Sector size ', 0 |
| offset_msg: db 'Main image LBA = ', 0 |
| verify_msg: db 'Image csum verified.', CR, LF, 0 |
| allread_msg db 'Image read, jumping to main code...', CR, LF, 0 |
| %endif |
| noinfotable_msg db 'No boot info table, assuming single session disk...', CR, LF, 0 |
| noinfoinspec_msg db 'Spec packet missing LBA information, trying to wing it...', CR, LF, 0 |
| spec_err_msg: db 'Loading spec packet failed, trying to wing it...', CR, LF, 0 |
| maybe_msg: db 'Found something at drive = ', 0 |
| alright_msg: db 'Looks reasonable, continuing...', CR, LF, 0 |
| nospec_msg db 'Extremely broken BIOS detected, last attempt with drive = ', 0 |
| nothing_msg: db 'Failed to locate CD-ROM device; boot failed.', CR, LF |
| trysbm_msg db 'See http://syslinux.zytor.com/sbm for more information.', CR, LF, 0 |
| diskerr_msg: db 'Disk error ', 0 |
| oncall_str: db ', AX = ',0 |
| ondrive_str: db ', drive ', 0 |
| checkerr_msg: db 'Image checksum error, sorry...', CR, LF, 0 |
| |
| err_bootfailed db CR, LF, 'Boot failed: press a key to retry...' |
| bailmsg equ err_bootfailed |
| crlf_msg db CR, LF |
| null_msg db 0 |
| |
| bios_cdrom_str db 'ETCD', 0 |
| %ifndef DEBUG_MESSAGES |
| bios_cbios_str db 'CHDD', 0 |
| bios_ebios_str db 'EHDD' ,0 |
| %endif |
| |
| alignz 4 |
| global bios_cdrom |
| bios_cdrom: dw getlinsec_cdrom, bios_cdrom_str |
| %ifndef DEBUG_MESSAGES |
| bios_cbios: dw getlinsec_cbios, bios_cbios_str |
| bios_ebios: dw getlinsec_ebios, bios_ebios_str |
| %endif |
| |
| ; Maximum transfer size |
| MaxTransfer dw 127 ; Hard disk modes |
| MaxTransferCD dw 32 ; CD mode |
| |
| rl_checkpt equ $ ; Must be <= 800h |
| |
| ; This pads to the end of sector 0 and errors out on |
| ; overflow. |
| times 2048-($-$$) db 0 |
| |
| ; ---------------------------------------------------------------------------- |
| ; End of code and data that have to be in the first sector |
| ; ---------------------------------------------------------------------------- |
| |
| section .text16 |
| |
| all_read: |
| |
| ; Test tracers |
| TRACER 'T' |
| TRACER '>' |
| |
| ; |
| ; Common initialization code |
| ; |
| %include "init.inc" |
| |
| ; Tell the user we got this far... |
| %ifndef DEBUG_MESSAGES ; Gets messy with debugging on |
| mov si,copyright_str |
| pm_call pm_writestr |
| %endif |
| |
| ; |
| ; Now we're all set to start with our *real* business. First load the |
| ; configuration file (if any) and parse it. |
| ; |
| ; In previous versions I avoided using 32-bit registers because of a |
| ; rumour some BIOSes clobbered the upper half of 32-bit registers at |
| ; random. I figure, though, that if there are any of those still left |
| ; they probably won't be trying to install Linux on them... |
| ; |
| ; The code is still ripe with 16-bitisms, though. Not worth the hassle |
| ; to take'm out. In fact, we may want to put them back if we're going |
| ; to boot ELKS at some point. |
| ; |
| |
| ; |
| ; Now, we need to sniff out the actual filesystem data structures. |
| ; mkisofs gave us a pointer to the primary volume descriptor |
| ; (which will be at 16 only for a single-session disk!); from the PVD |
| ; we should be able to find the rest of what we need to know. |
| ; |
| init_fs: |
| pushad |
| mov eax,ROOT_FS_OPS |
| mov dl,[DriveNumber] |
| cmp word [BIOSType],bios_cdrom |
| sete dh ; 1 for cdrom, 0 for hybrid mode |
| jne .hybrid |
| movzx ebp,word [MaxTransferCD] |
| jmp .common |
| .hybrid: |
| movzx ebp,word [MaxTransfer] |
| .common: |
| mov ecx,[Hidden] |
| mov ebx,[Hidden+4] |
| mov si,[bsHeads] |
| mov di,[bsSecPerTrack] |
| pm_call pm_fs_init |
| pm_call load_env32 |
| enter_command: |
| auto_boot: |
| jmp kaboom ; load_env32() should never return. If |
| ; it does, then kaboom! |
| popad |
| |
| section .rodata |
| alignz 4 |
| ROOT_FS_OPS: |
| extern iso_fs_ops |
| dd iso_fs_ops |
| dd 0 |
| |
| section .text16 |
| |
| %ifdef DEBUG_TRACERS |
| ; |
| ; debug hack to print a character with minimal code impact |
| ; |
| debug_tracer: pushad |
| pushfd |
| mov bp,sp |
| mov bx,[bp+9*4] ; Get return address |
| mov al,[cs:bx] ; Get data byte |
| inc word [bp+9*4] ; Return to after data byte |
| call writechr |
| popfd |
| popad |
| ret |
| %endif ; DEBUG_TRACERS |
| |
| section .bss16 |
| alignb 4 |
| ThisKbdTo resd 1 ; Temporary holder for KbdTimeout |
| ThisTotalTo resd 1 ; Temporary holder for TotalTimeout |
| KernelExtPtr resw 1 ; During search, final null pointer |
| FuncFlag resb 1 ; Escape sequences received from keyboard |
| KernelType resb 1 ; Kernel type, from vkernel, if known |
| global KernelName |
| KernelName resb FILENAME_MAX ; Mangled name for kernel |
| |
| section .text16 |
| ; |
| ; COM32 vestigial data structure |
| ; |
| %include "com32.inc" |
| |
| ; |
| ; Common local boot code |
| ; |
| %include "localboot.inc" |
| |
| ; ----------------------------------------------------------------------------- |
| ; Common modules |
| ; ----------------------------------------------------------------------------- |
| |
| %include "common.inc" ; Universal modules |
| |
| ; ----------------------------------------------------------------------------- |
| ; Begin data section |
| ; ----------------------------------------------------------------------------- |
| |
| section .data16 |
| err_disk_image db 'Cannot load disk image (invalid file)?', CR, LF, 0 |
| |
| section .bss16 |
| global OrigFDCTabPtr |
| OrigFDCTabPtr resd 1 ; Keep bios_cleanup_hardware() honest |