| ; ----------------------------------------------------------------------- |
| ; |
| ; Copyright 1994-2009 H. Peter Anvin - All Rights Reserved |
| ; Copyright 2009-2011 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. |
| ; |
| |
| Sect1Ptr0_VAL equ 0xdeadbeef |
| Sect1Ptr1_VAL equ 0xfeedface |
| |
| %include "diskboot.inc" |
| |
| ; =========================================================================== |
| ; Padding after the (minimum) 512-byte boot sector so that the rest of |
| ; the file has aligned sectors, even if they are larger than 512 bytes. |
| ; =========================================================================== |
| |
| section .init |
| align_pad zb 512 |
| |
| ; =========================================================================== |
| ; Start of LDLINUX.SYS |
| ; =========================================================================== |
| |
| LDLINUX_SYS equ ($-$$)+TEXT_START |
| ldlinux_sys: |
| |
| early_banner db CR, LF, MY_NAME, ' ', VERSION_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 |
| |
| ; |
| ; Pointer to the Syslinux banner |
| ; |
| BannerPtr dw syslinux_banner - LDLINUX_SYS |
| |
| ; |
| ; Base directory name and subvolume, if applicable. |
| ; |
| %define HAVE_CURRENTDIRNAME |
| global CurrentDirName:data hidden, SubvolName:data hidden |
| 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. |
| ; |
| jmp 0:.next ; Normalize CS:IP |
| .next: sti ; In case of broken INT 13h BIOSes |
| |
| ; |
| ; Tell the user we got this far |
| ; |
| mov si,early_banner |
| call writestr_early |
| |
| ; |
| ; Checksum data thus far |
| ; |
| mov si,ldlinux_sys |
| mov cx,[bsBytesPerSec] |
| shr cx,2 |
| mov edx,-LDLINUX_MAGIC |
| .checksum: |
| lodsd |
| add edx,eax |
| loop .checksum |
| mov [CheckSum],edx ; Save intermediate result |
| movzx ebx,si ; Start of the next sector |
| |
| ; |
| ; 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 |
| global BIOSName |
| alignb 2 |
| %define HAVE_BIOSNAME 1 |
| BIOSName resw 1 |
| |
| section .init |
| ; |
| ; Now we read the rest of LDLINUX.SYS. |
| ; |
| load_rest: |
| push bx ; LSW of load address |
| |
| lea esi,[SectorPtrs] |
| 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 |
| imul bp,[bsBytesPerSec] ; Will be < 64K |
| 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: |
| pop si ; LSW of load address |
| movzx eax,word [bsBytesPerSec] |
| shr ax,2 |
| mov ecx,[LDLDwords] ; Total dwords |
| sub ecx,eax ; ... minus one sector |
| 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 |
| |
| mov ds,cx |
| |
| 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:function hidden |
| 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 |
| imul di,[bsBytesPerSec] |
| 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 |
| call xint13 |
| 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 |
| imul ax,[bsBytesPerSec] ; 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 |
| |
| ; |
| ; |
| ; 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 |
| |
| ; |
| ; 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 $-ldlinux_sys |
| %ifndef DEPEND |
| %if rl_checkpt_off > 512-10 ; Need minimum one extent |
| %assign rl_checkpt_overflow rl_checkpt_off - (512-10) |
| %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 LDLINUX_SYS+MaxInitDataSize |
| SectorPtrs zb 10*(MaxInitDataSize >> MIN_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 ES scrambled... |
| xor ax,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,late_banner |
| call writestr_early |
| |
| 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 pm_fs_init |
| pm_call load_env32 |
| popad |
| |
| section .bss16 |
| SuperInfo resq 16 ; The first 16 bytes expanded 8 times |
| |
| ; |
| ; Banner information not needed in sector 1 |
| ; |
| section .data16 |
| global syslinux_banner |
| syslinux_banner db CR, LF, MY_NAME, ' ', VERSION_STR |
| late_banner db ' ', DATE_STR, 0 |
| |
| section .text16 |