diff options
Diffstat (limited to 'contrib/syslinux-4.02/core/diskstart.inc')
-rw-r--r-- | contrib/syslinux-4.02/core/diskstart.inc | 874 |
1 files changed, 874 insertions, 0 deletions
diff --git a/contrib/syslinux-4.02/core/diskstart.inc b/contrib/syslinux-4.02/core/diskstart.inc new file mode 100644 index 0000000..c0ba52a --- /dev/null +++ b/contrib/syslinux-4.02/core/diskstart.inc @@ -0,0 +1,874 @@ +; ----------------------------------------------------------------------- +; +; Copyright 1994-2009 H. Peter Anvin - All Rights Reserved +; Copyright 2009-2010 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., 51 Franklin St, Fifth Floor, +; Boston MA 02110-1301, USA; either version 2 of the License, or +; (at your option) any later version; incorporated herein by reference. +; +; ----------------------------------------------------------------------- + +; +; diskstart.inc +; +; Common early-bootstrap code for harddisk-based Syslinux derivatives. +; + + section .init +; +; Some of the things that have to be saved very early are saved +; "close" to the initial stack pointer offset, in order to +; reduce the code size... +; + +StackBuf equ STACK_TOP-44-92 ; Start the stack here (grow down - 4K) +PartInfo equ StackBuf +.mbr equ PartInfo +.gptlen equ PartInfo+16 +.gpt equ PartInfo+20 +FloppyTable equ PartInfo+76 +; Total size of PartInfo + FloppyTable == 76+16 = 92 bytes +Hidden equ StackBuf-20 ; Partition offset +OrigFDCTabPtr equ StackBuf-12 ; The 2nd high dword on the stack +OrigESDI equ StackBuf-8 ; The high dword on the stack +DriveNumber equ StackBuf-4 ; Drive number +StackHome equ Hidden ; The start of the canonical stack + +; +; Primary entry point. Tempting as though it may be, we can't put the +; initial "cli" here; the jmp opcode in the first byte is part of the +; "magic number" (using the term very loosely) for the DOS superblock. +; +bootsec equ $ +_start: jmp short start ; 2 bytes + nop ; 1 byte +; +; "Superblock" follows -- it's in the boot sector, so it's already +; loaded and ready for us +; +bsOemName db MY_NAME ; The SYS command sets this, so... + zb 8-($-bsOemName) + +; +; These are the fields we actually care about. We end up expanding them +; all to dword size early in the code, so generate labels for both +; the expanded and unexpanded versions. +; +%macro superb 1 +bx %+ %1 equ SuperInfo+($-superblock)*8+4 +bs %+ %1 equ $ + zb 1 +%endmacro +%macro superw 1 +bx %+ %1 equ SuperInfo+($-superblock)*8 +bs %+ %1 equ $ + zw 1 +%endmacro +%macro superd 1 +bx %+ %1 equ $ ; no expansion for dwords +bs %+ %1 equ $ + zd 1 +%endmacro +superblock equ $ + superw BytesPerSec + superb SecPerClust + superw ResSectors + superb FATs + superw RootDirEnts + superw Sectors + superb Media + superw FATsecs + superw SecPerTrack + superw Heads +superinfo_size equ ($-superblock)-1 ; How much to expand + superd Hidden + superd HugeSectors + ; + ; This is as far as FAT12/16 and FAT32 are consistent + ; + ; FAT12/16 need 26 more bytes, + ; FAT32 need 54 more bytes + ; +superblock_len_fat16 equ $-superblock+26 +superblock_len_fat32 equ $-superblock+54 + zb 54 ; Maximum needed size +superblock_max equ $-superblock + + global SecPerClust +SecPerClust equ bxSecPerClust + +; +; Note we don't check the constraints above now; we did that at install +; time (we hope!) +; +start: + cli ; No interrupts yet, please + cld ; Copy upwards +; +; Set up the stack +; + xor cx,cx + mov ss,cx + mov sp,StackBuf-2 ; Just below BSS (-2 for alignment) + push dx ; Save drive number (in DL) + push es ; Save initial ES:DI -> $PnP pointer + push di + mov es,cx + +; +; DS:SI may contain a partition table entry and possibly a GPT entry. +; Preserve it for us. This saves 56 bytes of the GPT entry, which is +; currently the maximum we care about. Total is 76 bytes. +; + mov cl,(16+4+56)/2 ; Save partition info + mov di,PartInfo + rep movsw ; This puts CX back to zero + + mov ds,cx ; Now we can initialize DS... + +; +; Now sautee the BIOS floppy info block to that it will support decent- +; size transfers; the floppy block is 11 bytes and is stored in the +; INT 1Eh vector (brilliant waste of resources, eh?) +; +; Of course, if BIOSes had been properly programmed, we wouldn't have +; had to waste precious space with this code. +; + mov bx,fdctab + lfs si,[bx] ; FS:SI -> original fdctab + push fs ; Save on stack in case we need to bail + push si + + ; Save the old fdctab even if hard disk so the stack layout + ; is the same. The instructions above do not change the flags + and dl,dl ; If floppy disk (00-7F), assume no + ; partition table + js harddisk + +floppy: + xor ax,ax + mov cl,6 ; 12 bytes (CX == 0) + ; es:di -> FloppyTable already + ; This should be safe to do now, interrupts are off... + mov [bx],di ; FloppyTable + mov [bx+2],ax ; Segment 0 + fs rep movsw ; Faster to move words + mov cl,[bsSecPerTrack] ; Patch the sector count + mov [di-76+8],cl + + push ax ; Partition offset == 0 + push ax + push ax + push ax + + int 13h ; Some BIOSes need this + jmp short not_harddisk +; +; The drive number and possibly partition information was passed to us +; by the BIOS or previous boot loader (MBR). Current "best practice" is to +; trust that rather than what the superblock contains. +; +; Note: di points to beyond the end of PartInfo +; +harddisk: + test byte [di-76],7Fh ; Sanity check: "active flag" should + jnz .no_partition ; be 00 or 80 + cmp [di-76+4],cl ; Sanity check: partition type != 0 + je .no_partition + cmp eax,'!GPT' ; !GPT signature? + jne .mbr + cmp byte [di-76+4],0EDh ; Synthetic GPT partition entry? + jne .mbr +.gpt: ; GPT-style partition info + push dword [di-76+20+36] + push dword [di-76+20+32] + jmp .gotoffs +.mbr: ; MBR-style partition info + push cx ; Upper half partition offset == 0 + push cx + push dword [di-76+8] ; Partition offset (dword) + jmp .gotoffs +.no_partition: +; +; No partition table given... assume that the Hidden field in the boot sector +; tells the truth (in particular, is zero if this is an unpartitioned disk.) +; + push cx + push cx + push dword [bsHidden] +.gotoffs: +; +; Get disk drive parameters (don't trust the superblock.) Don't do this for +; floppy drives -- INT 13:08 on floppy drives will (may?) return info about +; what the *drive* supports, not about the *media*. Fortunately floppy disks +; tend to have a fixed, well-defined geometry which is stored in the superblock. +; + ; DL == drive # still + mov ah,08h + int 13h + jc no_driveparm + and ah,ah + jnz no_driveparm + shr dx,8 + inc dx ; Contains # of heads - 1 + mov [bsHeads],dx + and cx,3fh + mov [bsSecPerTrack],cx +no_driveparm: +not_harddisk: +; +; Ready to enable interrupts, captain +; + sti + +; +; Do we have EBIOS (EDD)? +; +eddcheck: + mov bx,55AAh + mov ah,41h ; EDD existence query + mov dl,[DriveNumber] + int 13h + jc .noedd + cmp bx,0AA55h + jne .noedd + test cl,1 ; Extended disk access functionality set + jz .noedd + ; + ; We have EDD support... + ; + mov byte [getonesec.jmp+1],(getonesec_ebios-(getonesec.jmp+2)) +.noedd: + +; +; Load the first sector of LDLINUX.SYS; this used to be all proper +; with parsing the superblock and root directory; it doesn't fit +; together with EBIOS support, unfortunately. +; + mov eax,strict dword 0xdeadbeef +Sect1Ptr0 equ $-4 + mov edx,strict dword 0xfeedface +Sect1Ptr1 equ $-4 + mov bx,ldlinux_sys ; Where to load it + call getonesec + + ; Some modicum of integrity checking + cmp dword [ldlinux_magic+4],LDLINUX_MAGIC^HEXDATE + jne kaboom + + ; Go for it... + jmp 0:ldlinux_ent + + +; +; getonesec: load a single disk linear sector EDX:EAX into the buffer +; at ES:BX. +; +; This routine assumes CS == DS == SS, and trashes most registers. +; +; Stylistic note: use "xchg" instead of "mov" when the source is a register +; that is dead from that point; this saves space. However, please keep +; the order to dst,src to keep things sane. +; +getonesec: + add eax,[Hidden] ; Add partition offset + adc edx,[Hidden+4] + mov cx,retry_count +.jmp: jmp strict short getonesec_cbios + +; +; getonesec_ebios: +; +; getonesec implementation for EBIOS (EDD) +; +getonesec_ebios: +.retry: + ; Form DAPA on stack + push edx + push eax + push es + push bx + push word 1 + push word 16 + mov si,sp + pushad + mov ah,42h ; Extended Read + call xint13 + popad + lea sp,[si+16] ; Remove DAPA + jc .error + 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 + call xint13 + popad + loop .retry ; CX-- and jump if not zero + + ; Total failure. Try falling back to CBIOS. + mov byte [getonesec.jmp+1],(getonesec_cbios-(getonesec.jmp+2)) + +; +; getonesec_cbios: +; +; getlinsec implementation for legacy CBIOS +; +getonesec_cbios: +.retry: + pushad + + 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 + + cmp eax,1023 ; Outside the CHS range? + ja kaboom + + ; + ; Now we have AX = cyl, DX = head, CX = sector (0-based), + ; SI = bsSecPerTrack, ES:BX = data target + ; + 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 ax,0201h ; Read one sector + call xint13 + popad + jc .error + ret + +.error: + loop .retry + ; Fall through to disk_error + +; +; kaboom: write a message and bail out. +; + global kaboom +disk_error: +kaboom: + xor si,si + mov ss,si + mov sp,OrigFDCTabPtr ; Reset stack + mov ds,si ; Reset data segment + pop dword [fdctab] ; Restore FDC table +.patch: ; When we have full code, intercept here + mov si,bailmsg + call writestr_early + + xor ax,ax +.again: int 16h ; Wait for keypress + ; NB: replaced by int 18h if + ; chosen at install time.. + int 19h ; And try once more to boot... +.norge: hlt ; If int 19h returned; this is the end + jmp short .norge + +; +; +; writestr_early: write a null-terminated string to the console +; This assumes we're on page 0. This is only used for early +; messages, so it should be OK. +; +writestr_early: + pushad +.loop: lodsb + and al,al + jz .return + mov ah,0Eh ; Write to screen as TTY + mov bx,0007h ; Attribute + int 10h + jmp short .loop +.return: popad + ret + +; +; INT 13h wrapper function +; +xint13: + mov dl,[DriveNumber] + int 13h + ret + +; +; Error message on failure +; +bailmsg: db 'Boot error', 0Dh, 0Ah, 0 + + ; This fails if the boot sector overflowsg + zb 1FEh-($-$$) + +bootsignature dw 0xAA55 + +; +; =========================================================================== +; End of boot sector +; =========================================================================== +; Start of LDLINUX.SYS +; =========================================================================== + +LDLINUX_SYS equ ($-$$)+TEXT_START +ldlinux_sys: + +syslinux_banner db CR, LF, MY_NAME, ' ', VERSION_STR, ' ', DATE_STR, ' ', 0 + db CR, LF, 1Ah ; EOF if we "type" this in DOS + + alignz 8 +ldlinux_magic dd LDLINUX_MAGIC + dd LDLINUX_MAGIC^HEXDATE + +; +; This area is patched by the installer. It is found by looking for +; LDLINUX_MAGIC, plus 8 bytes. +; +SUBVOL_MAX equ 256 +CURRENTDIR_MAX equ FILENAME_MAX + +patch_area: +DataSectors dw 0 ; Number of sectors (not including bootsec) +ADVSectors dw 0 ; Additional sectors for ADVs +LDLDwords dd 0 ; Total dwords starting at ldlinux_sys, +CheckSum dd 0 ; Checksum starting at ldlinux_sys + ; value = LDLINUX_MAGIC - [sum of dwords] +MaxTransfer dw 127 ; Max sectors to transfer +EPAPtr dw EPA - LDLINUX_SYS ; Pointer to the extended patch area + +; +; Extended patch area -- this is in .data16 so it doesn't occupy space in +; the first sector. Use this structure for anything that isn't used by +; the first sector itself. +; + section .data16 + alignz 2 +EPA: +ADVSecPtr dw ADVSec0 - LDLINUX_SYS +CurrentDirPtr dw CurrentDirName-LDLINUX_SYS ; Current directory name string +CurrentDirLen dw CURRENTDIR_MAX +SubvolPtr dw SubvolName-LDLINUX_SYS +SubvolLen dw SUBVOL_MAX +SecPtrOffset dw SectorPtrs-LDLINUX_SYS +SecPtrCnt dw (SectorPtrsEnd - SectorPtrs)/10 + +; +; Boot sector patch pointers +; +Sect1Ptr0Ptr dw Sect1Ptr0 - bootsec ; Pointers to Sector 1 location +Sect1Ptr1Ptr dw Sect1Ptr1 - bootsec +RAIDPatchPtr dw kaboom.again - bootsec ; Patch to INT 18h in RAID mode + +; +; Base directory name and subvolume, if applicable. +; +%define HAVE_CURRENTDIRNAME + global CurrentDirName, SubvolName +CurrentDirName times CURRENTDIR_MAX db 0 +SubvolName times SUBVOL_MAX db 0 + + section .init +ldlinux_ent: +; +; Note that some BIOSes are buggy and run the boot sector at 07C0:0000 +; instead of 0000:7C00 and the like. We don't want to add anything +; more to the boot sector, so it is written to not assume a fixed +; value in CS, but we don't want to deal with that anymore from now +; on. +; + sti ; In case of broken INT 13h BIOSes + +; +; Tell the user we got this far +; + mov si,syslinux_banner + call writestr_early + +; +; Checksum data thus far +; + mov si,ldlinux_sys + mov cx,SECTOR_SIZE >> 2 + mov edx,-LDLINUX_MAGIC +.checksum: + lodsd + add edx,eax + loop .checksum + mov [CheckSum],edx ; Save intermediate result + +; +; Tell the user if we're using EBIOS or CBIOS +; +print_bios: + mov si,cbios_name + cmp byte [getonesec.jmp+1],(getonesec_ebios-(getonesec.jmp+2)) + jne .cbios + mov si,ebios_name + mov byte [getlinsec.jmp+1],(getlinsec_ebios-(getlinsec.jmp+2)) +.cbios: + mov [BIOSName],si + call writestr_early + + section .earlybss +%define HAVE_BIOSNAME 1 +BIOSName resw 1 + + section .init +; +; Now we read the rest of LDLINUX.SYS. +; +load_rest: + lea esi,[SectorPtrs] + mov ebx,TEXT_START+2*SECTOR_SIZE ; Where we start loading + mov cx,[DataSectors] + dec cx ; Minus this sector + +.get_chunk: + jcxz .done + mov eax,[si] + mov edx,[si+4] + movzx ebp,word [si+8] + sub cx,bp + push ebx + shr ebx,4 ; Convert to a segment + mov es,bx + xor bx,bx + call getlinsec + pop ebx + shl ebp,SECTOR_SHIFT + add ebx,ebp + add si,10 + jmp .get_chunk + +.done: + +; +; All loaded up, verify that we got what we needed. +; Note: the checksum field is embedded in the checksum region, so +; by the time we get to the end it should all cancel out. +; +verify_checksum: + mov si,ldlinux_sys + SECTOR_SIZE + mov ecx,[LDLDwords] + sub ecx,SECTOR_SIZE >> 2 + mov eax,[CheckSum] +.checksum: + add eax,[si] + add si,4 + jnz .nowrap + ; Handle segment wrap + mov dx,ds + add dx,1000h + mov ds,dx +.nowrap: + dec ecx + jnz .checksum + + and eax,eax ; Should be zero + jz all_read ; We're cool, go for it! + +; +; Uh-oh, something went bad... +; + mov si,checksumerr_msg + call writestr_early + jmp kaboom + +; +; ----------------------------------------------------------------------------- +; Subroutines that have to be in the first sector +; ----------------------------------------------------------------------------- + + + +; +; getlinsec: load a sequence of BP floppy sector given by the linear sector +; number in EAX into the buffer at ES:BX. We try to optimize +; by loading up to a whole track at a time, but the user +; is responsible for not crossing a 64K boundary. +; (Yes, BP is weird for a count, but it was available...) +; +; On return, BX points to the first byte after the transferred +; block. +; +; This routine assumes CS == DS. +; + global getlinsec +getlinsec: + pushad + add eax,[Hidden] ; Add partition offset + adc edx,[Hidden+4] +.jmp: jmp strict short getlinsec_cbios + +; +; getlinsec_ebios: +; +; getlinsec implementation for EBIOS (EDD) +; +getlinsec_ebios: +.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 ah,42h ; Extended Read + push ds + push ss + pop ds + call xint13 + 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,SECTOR_SHIFT ; 512-byte sectors + add bx,di ; Advance buffer pointer + and bp,bp + jnz .loop + + popad + 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] + int 13h + 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 byte [getlinsec.jmp+1],(getlinsec_cbios-(getlinsec.jmp+2)) + ;mov byte [MaxTransfer],63 ; Max possibe CBIOS transfer + + pop bp + ; ... fall through ... + +; +; getlinsec_cbios: +; +; getlinsec implementation for legacy CBIOS +; +getlinsec_cbios: +.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 + + cmp eax,1023 ; Outside the CHS range? + ja kaboom + + ; + ; 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 + xchg ax,bp ; Sector to transfer count + mov ah,02h ; Read sectors + mov bp,retry_count +.retry: + pushad + call xint13 + popad + jc .error +.resume: + movzx ecx,al ; ECX <- sectors transferred + shl ax,SECTOR_SHIFT ; 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 + popad + ret + +.error: + dec bp + jnz .retry + + xchg ax,bp ; Sectors transferred <- 0 + shr word [MaxTransfer],1 + jnz .resume + jmp kaboom + +maxtrans: + cmp bp,[MaxTransfer] + jna .ok + mov bp,[MaxTransfer] +.ok: ret + +; +; Checksum error message +; +checksumerr_msg db ' Load error - ', 0 ; Boot failed appended + +; +; BIOS type string +; +cbios_name db 'CHS', 0 ; CHS/CBIOS +ebios_name db 'EDD', 0 ; EDD/EBIOS + +; +; Debug routine +; +%ifdef debug +safedumpregs: + cmp word [Debug_Magic],0D00Dh + jnz nc_return + jmp dumpregs +%endif + +rl_checkpt equ $ ; Must be <= 8000h + +rl_checkpt_off equ ($-$$) +%ifndef DEPEND + %if rl_checkpt_off > 3F6h ; Need one extent + %assign rl_checkpt_overflow rl_checkpt_off - 3F6h + %error Sector 1 overflow by rl_checkpt_overflow bytes + %endif +%endif + +; +; Extent pointers... each extent contains an 8-byte LBA and an 2-byte +; sector count. In most cases, we will only ever need a handful of +; extents, but we have to assume a maximally fragmented system where each +; extent contains only one sector. +; + alignz 2 +MaxInitDataSize equ 96 << 10 +MaxLMA equ TEXT_START+SECTOR_SIZE+MaxInitDataSize +SectorPtrs zb 10*(MaxInitDataSize >> SECTOR_SHIFT) +SectorPtrsEnd equ $ + +; ---------------------------------------------------------------------------- +; End of code and data that have to be in the first sector +; ---------------------------------------------------------------------------- + + section .text16 +all_read: + ; We enter here with both DS and ES scrambled... + xor ax,ax + mov ds,ax + mov es,ax +; +; Let the user (and programmer!) know we got this far. This used to be +; in Sector 1, but makes a lot more sense here. +; + mov si,copyright_str + call writestr_early + + +; +; Insane hack to expand the DOS superblock to dwords +; +expand_super: + xor eax,eax + mov si,superblock + mov di,SuperInfo + mov cx,superinfo_size +.loop: + lodsw + dec si + stosd ; Store expanded word + xor ah,ah + stosd ; Store expanded byte + loop .loop + + +; +; Common initialization code +; +%include "init.inc" + + pushad + mov eax,ROOT_FS_OPS + movzx dx,byte [DriveNumber] + ; DH = 0: we are boot from disk not CDROM + mov ecx,[Hidden] + mov ebx,[Hidden+4] + mov si,[bsHeads] + mov di,[bsSecPerTrack] + movzx ebp,word [MaxTransfer] + pm_call fs_init + popad + + section .bss16 +SuperInfo resq 16 ; The first 16 bytes expanded 8 times + + section .text16 |