; -*- fundamental -*- (asm-mode sucks) ; $Id$ ; ----------------------------------------------------------------------- ; ; Copyright 1998-2001 H. Peter Anvin - All Rights Reserved ; ; 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., 675 Mass Ave, Cambridge MA 02139, ; USA; either version 2 of the License, or (at your option) any later ; version; incorporated herein by reference. ; ; ----------------------------------------------------------------------- ; ; syslinux.asm ; ; DOS installer for SYSLINUX ; absolute 0 pspInt20: resw 1 pspNextParagraph: resw 1 resb 1 ; reserved pspDispatcher: resb 5 pspTerminateVector: resd 1 pspControlCVector: resd 1 pspCritErrorVector: resd 1 resw 11 ; reserved pspEnvironment: resw 1 resw 23 ; reserved pspFCB_1: resb 16 pspFCB_2: resb 16 resd 1 ; reserved pspCommandLen: resb 1 pspCommandArg: resb 127 section .text org 0100h _start: mov ax,3000h ; Get DOS version int 21h xchg al,ah mov [DOSVersion],ax cmp ax,0314h ; DOS 3.20 minimum jae dosver_ok mov dx,msg_ancient_err jmp die section .bss alignb 2 DOSVersion: resw 1 section .text ; ; Scan command line for a drive letter followed by a colon ; dosver_ok: xor cx,cx mov si,pspCommandArg mov cl,[pspCommandLen] cmdscan1: jcxz bad_usage ; End of command line? lodsb ; Load character dec cx cmp al,' ' ; White space jbe cmdscan1 cmp al,'-' je scan_option or al,020h ; -> lower case cmp al,'a' ; Check for letter jb bad_usage cmp al,'z' ja bad_usage sub al,'a' ; Convert to zero-based index mov [DriveNo],al ; Save away drive index section .bss DriveNo: resb 1 section .text ; ; Got the leading letter, now the next character must be a colon ; got_letter: jcxz bad_usage lodsb dec cx cmp al,':' jne bad_usage ; ; Got the colon; the rest better be whitespace ; got_colon: jcxz got_cmdline lodsb dec cx cmp al,' ' jbe got_colon ; ; We end up here if the command line doesn't parse ; bad_usage: mov dx,msg_unfair jmp die section .data msg_unfair: db 'Usage: syslinux [-s] :', 0Dh, 0Ah, '$' section .text ; ; Scan for options after a - sign. The only recognized option right now ; is -s. ; scan_option: jcxz bad_usage lodsb dec cx cmp al,' ' jbe cmdscan1 or al,20h cmp al,'s' jne bad_usage push si ; make_stupid doesn't save these push cx call make_stupid ; Enable stupid boot sector pop cx pop si jmp short scan_option ; ; Parsed the command line OK. Check that the drive parameters are acceptable ; struc DPB dpbDrive: resb 1 dpbUnit: resb 1 dpbSectorSize: resw 1 dpbClusterMask: resb 1 dpbClusterShift: resb 1 dpbFirstFAT: resw 1 dpbFATCount: resb 1 dpbRootEntries: resw 1 dpbFirstSector: resw 1 dpbMaxCluster: resw 1 dpbFATSize: resw 1 dpbDirSector: resw 1 dpbDriverAddr: resd 1 dpbMedia: resb 1 dpbFirstAccess: resb 1 dpbNextDPB: resd 1 dpbNextFree: resw 1 dpbFreeCnt: resw 1 endstruc got_cmdline: mov dl,[DriveNo] inc dl ; 1-based mov ah,32h int 21h ; Get Drive Parameter Block and al,al jnz filesystem_error cmp word [bx+dpbSectorSize],512 ; Sector size = 512 required jne sectorsize_error cmp byte [bx+dpbClusterShift],5 ; Max size = 16K = 2^5 sectors jna drive_ok hugeclust_error: mov dx,msg_hugeclust_err jmp die filesystem_error: mov dx,msg_filesystem_err jmp doserr sectorsize_error: mov dx,msg_sectorsize_err jmp die drive_ok: push cs pop ds ; ; Writing LDLINUX.SYS ; section .data ldlinux_sys_str: db 'A:\LDLINUX.SYS', 0 section .text write_file: ; 0. Set the correct filename mov al,[DriveNo] add byte [ldlinux_sys_str],al ; 1. If the file exists, strip its attributes and delete xor cx,cx ; Clear attributes mov dx,ldlinux_sys_str mov ax,4301h ; Set file attributes int 21h mov dx,ldlinux_sys_str mov ah,41h ; Delete file int 21h ; 2. Create LDLINUX.SYS and write data to it mov dx,ldlinux_sys_str xor cx,cx ; Normal file mov ah,3Ch ; Create file int 21h jc .file_write_error mov [FileHandle],ax mov bx,ax mov cx,ldlinux_size mov dx,LDLinuxSYS mov ah,40h ; Write data int 21h jc .file_write_error cmp ax,ldlinux_size je .no_file_write_error .file_write_error: mov dx, msg_fwrite_err jmp doserr .no_file_write_error: mov bx,[FileHandle] mov ah,3Eh ; Close file int 21h section .bss FileHandle: resw 1 section .text ; 3. Set the readonly flag on LDLINUX.SYS mov dx,ldlinux_sys_str mov cx,1 ; Read only mov ax,4301h ; Set attributes int 21h ; ; Now, if we're on a recent Windows system we need to lock the device. ; This call should have no effect on plain DOS. ; lock_drive: cmp word [DOSVersion], 0700h ; Win9x/NT? jb .plain_dos ; Plain DOS -> no locking mov ax,440Dh ; Generic IOCTL mov bl,[DriveNo] inc bl ; 1-based mov bh,1 ; Lock level 1 mov cx,084Ah ; Lock logical volume mov dx,01h ; Allow write mappings/allow new mappings pusha int 21h jc .disk_lock_error_nocleanup popa xor dx,dx inc bh ; Lock level 2 pusha int 21h jc .disk_lock_error popa inc bh ; Lock level 3 pusha int 21h jnc .done .disk_lock_error: xor cx,cx mov cl,bh dec cx .lock_cleanup: push cx mov ax, 440Dh mov bl,[DriveNo] inc bl mov cx,086Ah int 21h pop cx loop .lock_cleanup .disk_lock_error_nocleanup: popa mov dx, msg_lock_err jmp doserr .done: popa .plain_dos: ; Plain DOS -> no locking ; ; Now read the old boot sector and copy the superblock. ; section .data align 4, db 0 DISKIO equ $ diStartSector: dd 0 ; Absolute sector 0 diSectors: dw 1 ; One sector diBuffer: dw SectorBuffer ; Buffer offset dw 0 ; Buffer segment section .text read_bootsect: mov ax,cs ; Set DS <- CS mov ds,ax cmp word [DOSVersion],0400h ; DOS 4.00 has a new interface jae .new .old: mov bx,SectorBuffer mov cx,1 ; One sector jmp short .common .new: mov bx,DISKIO mov [bx+8],ax ; Buffer segment mov cx,-1 .common: xor dx,dx ; Absolute sector 0 mov al,[DriveNo] int 25h ; DOS absolute disk read pop ax ; Remove flags from stack jc disk_read_error mov si,SectorBuffer+11 ; Offset of superblock mov di,BootSector+11 mov cx,51 ; Superblock = 51 bytes rep movsb ; Copy the superblock jmp short write_bootsect disk_read_error: mov dx,msg_read_err jmp doserr ; ; Writing boot sector ; write_bootsect: cmp word [DOSVersion],0400h ; DOS 4.00 has a new interface jae .new .old: mov bx,BootSector mov cx,1 ; One sector jmp short .common .new: mov bx,DISKIO mov word [bx+6],BootSector mov cx,-1 .common: xor dx,dx ; Absolute sector 0 mov al,[DriveNo] int 26h ; DOS absolute disk write pop ax ; Remove flags from stack jc disk_write_error ; ; Unlock the disk if we had to lock it ; unlock_disk: cmp word [DOSVersion], 0700h jb .plain_dos mov cx, 3 ; Need to release lock 3 times .loop: push cx mov ax,440Dh ; Generic IOCTL mov bl,[DriveNo] inc bl ; 1-based drive number mov cx,086Ah ; Unlock logical drive int 21h pop cx loop .loop .plain_dos: ; Plain DOS -> no locking all_done: mov ax,4C00h ; Exit good status int 21h ; ; Error routine jump ; disk_write_error: mov dx,msg_write_err doserr: push cs pop ds push dx ; Error message push ax ; Error code mov dx, msg_error_sp mov ah,09h int 21h pop ax mov cx,4 mov bx,hexdigits mov si,ax .digit: rol si,1 rol si,1 rol si,1 rol si,1 mov ax,si and al,0Fh xlatb mov ah,02h ; Display character mov dl,al int 21h loop .digit mov dx,msg_colon mov ah,09h int 21h jmp short die_common section .data hexdigits: db '0123456789ABCDEF' section .text die: push cs pop ds push dx mov dx, msg_error mov ah,09h int 21h die_common: pop dx ; Error message mov ah,09h ; Write string int 21h mov ax,4C01h ; Exit error status int 21h ; ; Patch the code to make it "stupid" ; make_stupid: ; Only access one sector at a time mov word [LDLinuxSYS+PATCH_OFFSET],1 ret section .data msg_error_sp: db 'ERROR $' msg_colon: db ': $' msg_error: db 'ERROR: $' msg_ancient_err: db 'DOS version 3.20 or later required', 0Dh, 0Ah, '$' msg_filesystem_err: db 'Filesystem not found on disk', 0Dh, 0Ah, '$' msg_sectorsize_err: db 'Sector sizes other than 512 bytes not supported', 0Dh, 0Ah, '$' msg_hugeclust_err: db 'Clusters larger than 16K not supported', 0Dh, 0Ah, '$' msg_read_err: db 'Boot sector read failed', 0Dh, 0Ah, '$' msg_write_err: db 'Boot sector write failed', 0Dh, 0Ah, '$' msg_fwrite_err: db 'LDLINUX.SYS write failed', 0Dh, 0Ah, '$' msg_lock_err: db 'Unable to lock drive for exclusive access', 0Dh, 0Ah, '$' section .data align 16, db 0 BootSector: incbin "ldlinux.bss" LDLinuxSYS: incbin "ldlinux.sys" ldlinux_size: equ $-LDLinuxSYS section .bss alignb 16 SectorBuffer: resb 512