523 lines
12 KiB
PHP
523 lines
12 KiB
PHP
; -----------------------------------------------------------------------
|
|
;
|
|
; 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
|