Code cleanup: get rid of hard-coded descriptor numbers
[syslinux.git] / ldlinux.asm
blobb882d732d154f9d4d3c9d76c8023e4e4dbf56cde
1 ; -*- fundamental -*- (asm-mode sucks)
2 ; ****************************************************************************
4 ; ldlinux.asm
6 ; A program to boot Linux kernels off an MS-DOS formatted floppy disk. This
7 ; functionality is good to have for installation floppies, where it may
8 ; be hard to find a functional Linux system to run LILO off.
10 ; This program allows manipulation of the disk to take place entirely
11 ; from MS-LOSS, and can be especially useful in conjunction with the
12 ; umsdos filesystem.
14 ; Copyright (C) 1994-2007 H. Peter Anvin
16 ; This program is free software; you can redistribute it and/or modify
17 ; it under the terms of the GNU General Public License as published by
18 ; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
19 ; Boston MA 02111-1307, USA; either version 2 of the License, or
20 ; (at your option) any later version; incorporated herein by reference.
22 ; ****************************************************************************
24 %ifndef IS_MDSLINUX
25 %define IS_SYSLINUX 1
26 %endif
27 %include "head.inc"
30 ; Some semi-configurable constants... change on your own risk.
32 my_id equ syslinux_id
33 FILENAME_MAX_LG2 equ 6 ; log2(Max filename size Including final null)
34 FILENAME_MAX equ (1<<FILENAME_MAX_LG2) ; Max mangled filename size
35 NULLFILE equ 0 ; First char space == null filename
36 NULLOFFSET equ 0 ; Position in which to look
37 retry_count equ 16 ; How patient are we with the disk?
38 %assign HIGHMEM_SLOP 0 ; Avoid this much memory near the top
39 LDLINUX_MAGIC equ 0x3eb202fe ; A random number to identify ourselves with
41 MAX_OPEN_LG2 equ 6 ; log2(Max number of open files)
42 MAX_OPEN equ (1 << MAX_OPEN_LG2)
44 SECTOR_SHIFT equ 9
45 SECTOR_SIZE equ (1 << SECTOR_SHIFT)
48 ; This is what we need to do when idle
50 %macro RESET_IDLE 0
51 ; Nothing
52 %endmacro
53 %macro DO_IDLE 0
54 ; Nothing
55 %endmacro
58 ; The following structure is used for "virtual kernels"; i.e. LILO-style
59 ; option labels. The options we permit here are `kernel' and `append
60 ; Since there is no room in the bottom 64K for all of these, we
61 ; stick them at vk_seg:0000 and copy them down before we need them.
63 struc vkernel
64 vk_vname: resb FILENAME_MAX ; Virtual name **MUST BE FIRST!**
65 vk_rname: resb FILENAME_MAX ; Real name
66 vk_appendlen: resw 1
67 vk_type: resb 1 ; Type of file
68 alignb 4
69 vk_append: resb max_cmd_len+1 ; Command line
70 alignb 4
71 vk_end: equ $ ; Should be <= vk_size
72 endstruc
75 ; Segment assignments in the bottom 640K
76 ; Stick to the low 512K in case we're using something like M-systems flash
77 ; which load a driver into low RAM (evil!!)
79 ; 0000h - main code/data segment (and BIOS segment)
81 real_mode_seg equ 4000h
82 cache_seg equ 3000h ; 64K area for metadata cache
83 vk_seg equ 2000h ; Virtual kernels
84 xfer_buf_seg equ 1000h ; Bounce buffer for I/O to high mem
85 comboot_seg equ real_mode_seg ; COMBOOT image loading zone
88 ; File structure. This holds the information for each currently open file.
90 struc open_file_t
91 file_sector resd 1 ; Sector pointer (0 = structure free)
92 file_left resd 1 ; Number of sectors left
93 endstruc
95 %ifndef DEPEND
96 %if (open_file_t_size & (open_file_t_size-1))
97 %error "open_file_t is not a power of 2"
98 %endif
99 %endif
101 ; ---------------------------------------------------------------------------
102 ; BEGIN CODE
103 ; ---------------------------------------------------------------------------
106 ; Memory below this point is reserved for the BIOS and the MBR
108 section .earlybss
109 trackbufsize equ 8192
110 trackbuf resb trackbufsize ; Track buffer goes here
111 getcbuf resb trackbufsize
112 ; ends at 4800h
114 section .bss
115 alignb 8
117 ; Expanded superblock
118 SuperInfo equ $
119 resq 16 ; The first 16 bytes expanded 8 times
120 FAT resd 1 ; Location of (first) FAT
121 RootDirArea resd 1 ; Location of root directory area
122 RootDir resd 1 ; Location of root directory proper
123 DataArea resd 1 ; Location of data area
124 RootDirSize resd 1 ; Root dir size in sectors
125 TotalSectors resd 1 ; Total number of sectors
126 ClustSize resd 1 ; Bytes/cluster
127 ClustMask resd 1 ; Sectors/cluster - 1
128 CopySuper resb 1 ; Distinguish .bs versus .bss
129 DriveNumber resb 1 ; BIOS drive number
130 ClustShift resb 1 ; Shift count for sectors/cluster
131 ClustByteShift resb 1 ; Shift count for bytes/cluster
133 alignb open_file_t_size
134 Files resb MAX_OPEN*open_file_t_size
137 ; Constants for the xfer_buf_seg
139 ; The xfer_buf_seg is also used to store message file buffers. We
140 ; need two trackbuffers (text and graphics), plus a work buffer
141 ; for the graphics decompressor.
143 xbs_textbuf equ 0 ; Also hard-coded, do not change
144 xbs_vgabuf equ trackbufsize
145 xbs_vgatmpbuf equ 2*trackbufsize
148 section .text
150 ; Some of the things that have to be saved very early are saved
151 ; "close" to the initial stack pointer offset, in order to
152 ; reduce the code size...
154 StackBuf equ $-44-32 ; Start the stack here (grow down - 4K)
155 PartInfo equ StackBuf ; Saved partition table entry
156 FloppyTable equ PartInfo+16 ; Floppy info table (must follow PartInfo)
157 OrigFDCTabPtr equ StackBuf-4 ; The high dword on the stack
160 ; Primary entry point. Tempting as though it may be, we can't put the
161 ; initial "cli" here; the jmp opcode in the first byte is part of the
162 ; "magic number" (using the term very loosely) for the DOS superblock.
164 bootsec equ $
165 jmp short start ; 2 bytes
166 nop ; 1 byte
168 ; "Superblock" follows -- it's in the boot sector, so it's already
169 ; loaded and ready for us
171 bsOemName db 'SYSLINUX' ; The SYS command sets this, so...
173 ; These are the fields we actually care about. We end up expanding them
174 ; all to dword size early in the code, so generate labels for both
175 ; the expanded and unexpanded versions.
177 %macro superb 1
178 bx %+ %1 equ SuperInfo+($-superblock)*8+4
179 bs %+ %1 equ $
180 zb 1
181 %endmacro
182 %macro superw 1
183 bx %+ %1 equ SuperInfo+($-superblock)*8
184 bs %+ %1 equ $
185 zw 1
186 %endmacro
187 %macro superd 1
188 bx %+ %1 equ $ ; no expansion for dwords
189 bs %+ %1 equ $
190 zd 1
191 %endmacro
192 superblock equ $
193 superw BytesPerSec
194 superb SecPerClust
195 superw ResSectors
196 superb FATs
197 superw RootDirEnts
198 superw Sectors
199 superb Media
200 superw FATsecs
201 superw SecPerTrack
202 superw Heads
203 superinfo_size equ ($-superblock)-1 ; How much to expand
204 superd Hidden
205 superd HugeSectors
207 ; This is as far as FAT12/16 and FAT32 are consistent
209 zb 54 ; FAT12/16 need 26 more bytes,
210 ; FAT32 need 54 more bytes
211 superblock_len equ $-superblock
213 SecPerClust equ bxSecPerClust
215 ; Note we don't check the constraints above now; we did that at install
216 ; time (we hope!)
218 start:
219 cli ; No interrupts yet, please
220 cld ; Copy upwards
222 ; Set up the stack
224 xor ax,ax
225 mov ss,ax
226 mov sp,StackBuf ; Just below BSS
227 mov es,ax
229 ; DS:SI may contain a partition table entry. Preserve it for us.
231 mov cx,8 ; Save partition info
232 mov di,sp
233 rep movsw
235 mov ds,ax ; Now we can initialize DS...
238 ; Now sautee the BIOS floppy info block to that it will support decent-
239 ; size transfers; the floppy block is 11 bytes and is stored in the
240 ; INT 1Eh vector (brilliant waste of resources, eh?)
242 ; Of course, if BIOSes had been properly programmed, we wouldn't have
243 ; had to waste precious space with this code.
245 mov bx,fdctab
246 lfs si,[bx] ; FS:SI -> original fdctab
247 push fs ; Save on stack in case we need to bail
248 push si
250 ; Save the old fdctab even if hard disk so the stack layout
251 ; is the same. The instructions above do not change the flags
252 mov [DriveNumber],dl ; Save drive number in DL
253 and dl,dl ; If floppy disk (00-7F), assume no
254 ; partition table
255 js harddisk
257 floppy:
258 mov cl,6 ; 12 bytes (CX == 0)
259 ; es:di -> FloppyTable already
260 ; This should be safe to do now, interrupts are off...
261 mov [bx],di ; FloppyTable
262 mov [bx+2],ax ; Segment 0
263 fs rep movsw ; Faster to move words
264 mov cl,[bsSecPerTrack] ; Patch the sector count
265 mov [di-8],cl
266 ; AX == 0 here
267 int 13h ; Some BIOSes need this
269 jmp short not_harddisk
271 ; The drive number and possibly partition information was passed to us
272 ; by the BIOS or previous boot loader (MBR). Current "best practice" is to
273 ; trust that rather than what the superblock contains.
275 ; Would it be better to zero out bsHidden if we don't have a partition table?
277 ; Note: di points to beyond the end of PartInfo
279 harddisk:
280 test byte [di-16],7Fh ; Sanity check: "active flag" should
281 jnz no_partition ; be 00 or 80
282 mov eax,[di-8] ; Partition offset (dword)
283 mov [bsHidden],eax
284 no_partition:
286 ; Get disk drive parameters (don't trust the superblock.) Don't do this for
287 ; floppy drives -- INT 13:08 on floppy drives will (may?) return info about
288 ; what the *drive* supports, not about the *media*. Fortunately floppy disks
289 ; tend to have a fixed, well-defined geometry which is stored in the superblock.
291 ; DL == drive # still
292 mov ah,08h
293 int 13h
294 jc no_driveparm
295 and ah,ah
296 jnz no_driveparm
297 shr dx,8
298 inc dx ; Contains # of heads - 1
299 mov [bsHeads],dx
300 and cx,3fh
301 mov [bsSecPerTrack],cx
302 no_driveparm:
303 not_harddisk:
305 ; Ready to enable interrupts, captain
310 ; Do we have EBIOS (EDD)?
312 eddcheck:
313 mov bx,55AAh
314 mov ah,41h ; EDD existence query
315 mov dl,[DriveNumber]
316 int 13h
317 jc .noedd
318 cmp bx,0AA55h
319 jne .noedd
320 test cl,1 ; Extended disk access functionality set
321 jz .noedd
323 ; We have EDD support...
325 mov byte [getlinsec.jmp+1],(getlinsec_ebios-(getlinsec.jmp+2))
326 .noedd:
329 ; Load the first sector of LDLINUX.SYS; this used to be all proper
330 ; with parsing the superblock and root directory; it doesn't fit
331 ; together with EBIOS support, unfortunately.
333 mov eax,[FirstSector] ; Sector start
334 mov bx,ldlinux_sys ; Where to load it
335 call getonesec
337 ; Some modicum of integrity checking
338 cmp dword [ldlinux_magic+4],LDLINUX_MAGIC^HEXDATE
339 jne kaboom
341 ; Go for it...
342 jmp ldlinux_ent
345 ; getonesec: get one disk sector
347 getonesec:
348 mov bp,1 ; One sector
349 ; Fall through
352 ; getlinsec: load a sequence of BP floppy sector given by the linear sector
353 ; number in EAX into the buffer at ES:BX. We try to optimize
354 ; by loading up to a whole track at a time, but the user
355 ; is responsible for not crossing a 64K boundary.
356 ; (Yes, BP is weird for a count, but it was available...)
358 ; On return, BX points to the first byte after the transferred
359 ; block.
361 ; This routine assumes CS == DS, and trashes most registers.
363 ; Stylistic note: use "xchg" instead of "mov" when the source is a register
364 ; that is dead from that point; this saves space. However, please keep
365 ; the order to dst,src to keep things sane.
367 getlinsec:
368 add eax,[bsHidden] ; Add partition offset
369 xor edx,edx ; Zero-extend LBA (eventually allow 64 bits)
371 .jmp: jmp strict short getlinsec_cbios
374 ; getlinsec_ebios:
376 ; getlinsec implementation for EBIOS (EDD)
378 getlinsec_ebios:
379 .loop:
380 push bp ; Sectors left
381 .retry2:
382 call maxtrans ; Enforce maximum transfer size
383 movzx edi,bp ; Sectors we are about to read
384 mov cx,retry_count
385 .retry:
387 ; Form DAPA on stack
388 push edx
389 push eax
390 push es
391 push bx
392 push di
393 push word 16
394 mov si,sp
395 pushad
396 mov dl,[DriveNumber]
397 push ds
398 push ss
399 pop ds ; DS <- SS
400 mov ah,42h ; Extended Read
401 int 13h
402 pop ds
403 popad
404 lea sp,[si+16] ; Remove DAPA
405 jc .error
406 pop bp
407 add eax,edi ; Advance sector pointer
408 sub bp,di ; Sectors left
409 shl di,SECTOR_SHIFT ; 512-byte sectors
410 add bx,di ; Advance buffer pointer
411 and bp,bp
412 jnz .loop
416 .error:
417 ; Some systems seem to get "stuck" in an error state when
418 ; using EBIOS. Doesn't happen when using CBIOS, which is
419 ; good, since some other systems get timeout failures
420 ; waiting for the floppy disk to spin up.
422 pushad ; Try resetting the device
423 xor ax,ax
424 mov dl,[DriveNumber]
425 int 13h
426 popad
427 loop .retry ; CX-- and jump if not zero
429 ;shr word [MaxTransfer],1 ; Reduce the transfer size
430 ;jnz .retry2
432 ; Total failure. Try falling back to CBIOS.
433 mov byte [getlinsec.jmp+1],(getlinsec_cbios-(getlinsec.jmp+2))
434 ;mov byte [MaxTransfer],63 ; Max possibe CBIOS transfer
436 pop bp
437 ; ... fall through ...
440 ; getlinsec_cbios:
442 ; getlinsec implementation for legacy CBIOS
444 getlinsec_cbios:
445 .loop:
446 push edx
447 push eax
448 push bp
449 push bx
451 movzx esi,word [bsSecPerTrack]
452 movzx edi,word [bsHeads]
454 ; Dividing by sectors to get (track,sector): we may have
455 ; up to 2^18 tracks, so we need to use 32-bit arithmetric.
457 div esi
458 xor cx,cx
459 xchg cx,dx ; CX <- sector index (0-based)
460 ; EDX <- 0
461 ; eax = track #
462 div edi ; Convert track to head/cyl
464 ; We should test this, but it doesn't fit...
465 ; cmp eax,1023
466 ; ja .error
469 ; Now we have AX = cyl, DX = head, CX = sector (0-based),
470 ; BP = sectors to transfer, SI = bsSecPerTrack,
471 ; ES:BX = data target
474 call maxtrans ; Enforce maximum transfer size
476 ; Must not cross track boundaries, so BP <= SI-CX
477 sub si,cx
478 cmp bp,si
479 jna .bp_ok
480 mov bp,si
481 .bp_ok:
483 shl ah,6 ; Because IBM was STOOPID
484 ; and thought 8 bits were enough
485 ; then thought 10 bits were enough...
486 inc cx ; Sector numbers are 1-based, sigh
487 or cl,ah
488 mov ch,al
489 mov dh,dl
490 mov dl,[DriveNumber]
491 xchg ax,bp ; Sector to transfer count
492 mov ah,02h ; Read sectors
493 mov bp,retry_count
494 .retry:
495 pushad
496 int 13h
497 popad
498 jc .error
499 .resume:
500 movzx ecx,al ; ECX <- sectors transferred
501 shl ax,SECTOR_SHIFT ; Convert sectors in AL to bytes in AX
502 pop bx
503 add bx,ax
504 pop bp
505 pop eax
506 pop edx
507 add eax,ecx
508 sub bp,cx
509 jnz .loop
512 .error:
513 dec bp
514 jnz .retry
516 xchg ax,bp ; Sectors transferred <- 0
517 shr word [MaxTransfer],1
518 jnz .resume
519 ; Fall through to disk_error
522 ; kaboom: write a message and bail out.
524 disk_error:
525 kaboom:
526 xor si,si
527 mov ss,si
528 mov sp,StackBuf-4 ; Reset stack
529 mov ds,si ; Reset data segment
530 pop dword [fdctab] ; Restore FDC table
531 .patch: ; When we have full code, intercept here
532 mov si,bailmsg
534 ; Write error message, this assumes screen page 0
535 .loop: lodsb
536 and al,al
537 jz .done
538 mov ah,0Eh ; Write to screen as TTY
539 mov bx,0007h ; Attribute
540 int 10h
541 jmp short .loop
542 .done:
543 cbw ; AH <- 0
544 .again: int 16h ; Wait for keypress
545 ; NB: replaced by int 18h if
546 ; chosen at install time..
547 int 19h ; And try once more to boot...
548 .norge: jmp short .norge ; If int 19h returned; this is the end
551 ; Truncate BP to MaxTransfer
553 maxtrans:
554 cmp bp,[MaxTransfer]
555 jna .ok
556 mov bp,[MaxTransfer]
557 .ok: ret
560 ; Error message on failure
562 bailmsg: db 'Boot error', 0Dh, 0Ah, 0
564 ; This fails if the boot sector overflows
565 zb 1F8h-($-$$)
567 FirstSector dd 0xDEADBEEF ; Location of sector 1
568 MaxTransfer dw 0x007F ; Max transfer size
570 ; This field will be filled in 0xAA55 by the installer, but we abuse it
571 ; to house a pointer to the INT 16h instruction at
572 ; kaboom.again, which gets patched to INT 18h in RAID mode.
573 bootsignature dw kaboom.again-bootsec
576 ; ===========================================================================
577 ; End of boot sector
578 ; ===========================================================================
579 ; Start of LDLINUX.SYS
580 ; ===========================================================================
582 ldlinux_sys:
584 syslinux_banner db 0Dh, 0Ah
585 %if IS_MDSLINUX
586 db 'MDSLINUX '
587 %else
588 db 'SYSLINUX '
589 %endif
590 db version_str, ' ', date, ' ', 0
591 db 0Dh, 0Ah, 1Ah ; EOF if we "type" this in DOS
593 align 8, db 0
594 ldlinux_magic dd LDLINUX_MAGIC
595 dd LDLINUX_MAGIC^HEXDATE
598 ; This area is patched by the installer. It is found by looking for
599 ; LDLINUX_MAGIC, plus 8 bytes.
601 patch_area:
602 LDLDwords dw 0 ; Total dwords starting at ldlinux_sys
603 LDLSectors dw 0 ; Number of sectors - (bootsec+this sec)
604 CheckSum dd 0 ; Checksum starting at ldlinux_sys
605 ; value = LDLINUX_MAGIC - [sum of dwords]
607 ; Space for up to 64 sectors, the theoretical maximum
608 SectorPtrs times 64 dd 0
610 ldlinux_ent:
612 ; Note that some BIOSes are buggy and run the boot sector at 07C0:0000
613 ; instead of 0000:7C00 and the like. We don't want to add anything
614 ; more to the boot sector, so it is written to not assume a fixed
615 ; value in CS, but we don't want to deal with that anymore from now
616 ; on.
618 jmp 0:.next
619 .next:
622 ; Tell the user we got this far
624 mov si,syslinux_banner
625 call writestr
628 ; Tell the user if we're using EBIOS or CBIOS
630 print_bios:
631 mov si,cbios_name
632 cmp byte [getlinsec.jmp+1],(getlinsec_ebios-(getlinsec.jmp+2))
633 jne .cbios
634 mov si,ebios_name
635 .cbios:
636 mov [BIOSName],si
637 call writestr
639 section .bss
640 %define HAVE_BIOSNAME 1
641 BIOSName resw 1
643 section .text
645 ; Now we read the rest of LDLINUX.SYS. Don't bother loading the first
646 ; sector again, though.
648 load_rest:
649 mov si,SectorPtrs
650 mov bx,7C00h+2*SECTOR_SIZE ; Where we start loading
651 mov cx,[LDLSectors]
653 .get_chunk:
654 jcxz .done
655 xor bp,bp
656 lodsd ; First sector of this chunk
658 mov edx,eax
660 .make_chunk:
661 inc bp
662 dec cx
663 jz .chunk_ready
664 inc edx ; Next linear sector
665 cmp [si],edx ; Does it match
666 jnz .chunk_ready ; If not, this is it
667 add si,4 ; If so, add sector to chunk
668 jmp short .make_chunk
670 .chunk_ready:
671 call getlinsecsr
672 shl bp,SECTOR_SHIFT
673 add bx,bp
674 jmp .get_chunk
676 .done:
679 ; All loaded up, verify that we got what we needed.
680 ; Note: the checksum field is embedded in the checksum region, so
681 ; by the time we get to the end it should all cancel out.
683 verify_checksum:
684 mov si,ldlinux_sys
685 mov cx,[LDLDwords]
686 mov edx,-LDLINUX_MAGIC
687 .checksum:
688 lodsd
689 add edx,eax
690 loop .checksum
692 and edx,edx ; Should be zero
693 jz all_read ; We're cool, go for it!
696 ; Uh-oh, something went bad...
698 mov si,checksumerr_msg
699 call writestr
700 jmp kaboom
703 ; -----------------------------------------------------------------------------
704 ; Subroutines that have to be in the first sector
705 ; -----------------------------------------------------------------------------
709 ; writestr: write a null-terminated string to the console
710 ; This assumes we're on page 0. This is only used for early
711 ; messages, so it should be OK.
713 writestr:
714 .loop: lodsb
715 and al,al
716 jz .return
717 mov ah,0Eh ; Write to screen as TTY
718 mov bx,0007h ; Attribute
719 int 10h
720 jmp short .loop
721 .return: ret
724 ; getlinsecsr: save registers, call getlinsec, restore registers
726 getlinsecsr: pushad
727 call getlinsec
728 popad
732 ; Checksum error message
734 checksumerr_msg db ' Load error - ', 0 ; Boot failed appended
737 ; BIOS type string
739 cbios_name db 'CBIOS', 0
740 ebios_name db 'EBIOS', 0
743 ; Debug routine
745 %ifdef debug
746 safedumpregs:
747 cmp word [Debug_Magic],0D00Dh
748 jnz nc_return
749 jmp dumpregs
750 %endif
752 rl_checkpt equ $ ; Must be <= 8000h
754 rl_checkpt_off equ ($-$$)
755 %ifndef DEPEND
756 %if rl_checkpt_off > 400h
757 %error "Sector 1 overflow"
758 %endif
759 %endif
761 ; ----------------------------------------------------------------------------
762 ; End of code and data that have to be in the first sector
763 ; ----------------------------------------------------------------------------
765 all_read:
767 ; Let the user (and programmer!) know we got this far. This used to be
768 ; in Sector 1, but makes a lot more sense here.
770 mov si,copyright_str
771 call writestr
775 ; Insane hack to expand the superblock to dwords
777 expand_super:
778 xor eax,eax
779 mov si,superblock
780 mov di,SuperInfo
781 mov cx,superinfo_size
782 .loop:
783 lodsw
784 dec si
785 stosd ; Store expanded word
786 xor ah,ah
787 stosd ; Store expanded byte
788 loop .loop
791 ; Compute some information about this filesystem.
794 ; First, generate the map of regions
795 genfatinfo:
796 mov edx,[bxSectors]
797 and dx,dx
798 jnz .have_secs
799 mov edx,[bsHugeSectors]
800 .have_secs:
801 mov [TotalSectors],edx
803 mov eax,[bxResSectors]
804 mov [FAT],eax ; Beginning of FAT
805 mov edx,[bxFATsecs]
806 and dx,dx
807 jnz .have_fatsecs
808 mov edx,[bootsec+36] ; FAT32 BPB_FATsz32
809 .have_fatsecs:
810 imul edx,[bxFATs]
811 add eax,edx
812 mov [RootDirArea],eax ; Beginning of root directory
813 mov [RootDir],eax ; For FAT12/16 == root dir location
815 mov edx,[bxRootDirEnts]
816 add dx,SECTOR_SIZE/32-1
817 shr dx,SECTOR_SHIFT-5
818 mov [RootDirSize],edx
819 add eax,edx
820 mov [DataArea],eax ; Beginning of data area
822 ; Next, generate a cluster size shift count and mask
823 mov eax,[bxSecPerClust]
824 bsr cx,ax
825 mov [ClustShift],cl
826 push cx
827 add cl,9
828 mov [ClustByteShift],cl
829 pop cx
830 dec ax
831 mov [ClustMask],eax
832 inc ax
833 shl eax,9
834 mov [ClustSize],eax
837 ; FAT12, FAT16 or FAT28^H^H32? This computation is fscking ridiculous.
839 getfattype:
840 mov eax,[TotalSectors]
841 sub eax,[DataArea]
842 shr eax,cl ; cl == ClustShift
843 mov cl,nextcluster_fat12-(nextcluster+2)
844 cmp eax,4085 ; FAT12 limit
845 jb .setsize
846 mov cl,nextcluster_fat16-(nextcluster+2)
847 cmp eax,65525 ; FAT16 limit
848 jb .setsize
850 ; FAT32, root directory is a cluster chain
852 mov cl,[ClustShift]
853 mov eax,[bootsec+44] ; Root directory cluster
854 sub eax,2
855 shl eax,cl
856 add eax,[DataArea]
857 mov [RootDir],eax
858 mov cl,nextcluster_fat28-(nextcluster+2)
859 .setsize:
860 mov byte [nextcluster+1],cl
863 ; Common initialization code
865 %include "cpuinit.inc"
866 %include "init.inc"
869 ; Clear Files structures
871 mov di,Files
872 mov cx,(MAX_OPEN*open_file_t_size)/4
873 xor eax,eax
874 rep stosd
877 ; Initialize the metadata cache
879 call initcache
882 ; Now, everything is "up and running"... patch kaboom for more
883 ; verbosity and using the full screen system
885 ; E9 = JMP NEAR
886 mov dword [kaboom.patch],0e9h+((kaboom2-(kaboom.patch+3)) << 8)
889 ; Now we're all set to start with our *real* business. First load the
890 ; configuration file (if any) and parse it.
892 ; In previous versions I avoided using 32-bit registers because of a
893 ; rumour some BIOSes clobbered the upper half of 32-bit registers at
894 ; random. I figure, though, that if there are any of those still left
895 ; they probably won't be trying to install Linux on them...
897 ; The code is still ripe with 16-bitisms, though. Not worth the hassle
898 ; to take'm out. In fact, we may want to put them back if we're going
899 ; to boot ELKS at some point.
903 ; Load configuration file
905 mov si,config_name ; Save configuration file name
906 mov di,ConfigName
907 call strcpy
909 mov di,syslinux_cfg1
910 call open
911 jnz .config_open
912 mov di,syslinux_cfg2
913 call open
914 jnz .config_open
915 mov di,syslinux_cfg3
916 call open
917 jz no_config_file
918 .config_open:
919 mov eax,[PrevDir] ; Make the directory with syslinux.cfg ...
920 mov [CurrentDir],eax ; ... the current directory
923 ; Now we have the config file open. Parse the config file and
924 ; run the user interface.
926 %include "ui.inc"
929 ; allocate_file: Allocate a file structure
931 ; If successful:
932 ; ZF set
933 ; BX = file pointer
934 ; In unsuccessful:
935 ; ZF clear
937 allocate_file:
938 TRACER 'a'
939 push cx
940 mov bx,Files
941 mov cx,MAX_OPEN
942 .check: cmp dword [bx], byte 0
943 je .found
944 add bx,open_file_t_size ; ZF = 0
945 loop .check
946 ; ZF = 0 if we fell out of the loop
947 .found: pop cx
951 ; search_dos_dir:
952 ; Search a specific directory for a pre-mangled filename in
953 ; MangledBuf, in the directory starting in sector EAX.
955 ; NOTE: This file considers finding a zero-length file an
956 ; error. This is so we don't have to deal with that special
957 ; case elsewhere in the program (most loops have the test
958 ; at the end).
960 ; Assumes DS == ES == CS.
962 ; If successful:
963 ; ZF clear
964 ; SI = file pointer
965 ; EAX = file length (MAY BE ZERO!)
966 ; DL = file attributes
967 ; If unsuccessful
968 ; ZF set
971 search_dos_dir:
972 push bx
973 call allocate_file
974 jnz .alloc_failure
976 push cx
977 push gs
978 push es
979 push ds
980 pop es ; ES = DS
982 .scansector:
983 ; EAX <- directory sector to scan
984 call getcachesector
985 ; GS:SI now points to this sector
987 mov cx,SECTOR_SIZE/32 ; 32 == directory entry size
988 .scanentry:
989 cmp byte [gs:si],0
990 jz .failure ; Hit directory high water mark
991 test byte [gs:si+11],8 ; Ignore volume labels and
992 ; VFAT long filename entries
993 jnz .nomatch
994 push cx
995 push si
996 push di
997 mov di,MangledBuf
998 mov cx,11
999 gs repe cmpsb
1000 pop di
1001 pop si
1002 pop cx
1003 jz .found
1004 .nomatch:
1005 add si,32
1006 loop .scanentry
1008 call nextsector
1009 jnc .scansector ; CF is set if we're at end
1011 ; If we get here, we failed
1012 .failure:
1013 pop es
1014 pop gs
1015 pop cx
1016 .alloc_failure:
1017 pop bx
1018 xor eax,eax ; ZF <- 1
1020 .found:
1021 mov eax,[gs:si+28] ; File size
1022 add eax,SECTOR_SIZE-1
1023 shr eax,SECTOR_SHIFT
1024 mov [bx+4],eax ; Sector count
1026 mov cl,[ClustShift]
1027 mov dx,[gs:si+20] ; High cluster word
1028 shl edx,16
1029 mov dx,[gs:si+26] ; Low cluster word
1030 sub edx,2
1031 shl edx,cl
1032 add edx,[DataArea]
1033 mov [bx],edx ; Starting sector
1035 mov eax,[gs:si+28] ; File length again
1036 mov dl,[gs:si+11] ; File attribute
1037 mov si,bx ; File pointer...
1038 and si,si ; ZF <- 0
1040 pop es
1041 pop gs
1042 pop cx
1043 pop bx
1047 ; close_file:
1048 ; Deallocates a file structure (pointer in SI)
1049 ; Assumes CS == DS.
1051 close_file:
1052 and si,si
1053 jz .closed
1054 mov dword [si],0 ; First dword == file_left
1055 .closed: ret
1058 ; searchdir:
1060 ; Open a file
1062 ; On entry:
1063 ; DS:DI = filename
1064 ; If successful:
1065 ; ZF clear
1066 ; SI = file pointer
1067 ; DX:AX or EAX = file length in bytes
1068 ; If unsuccessful
1069 ; ZF set
1071 ; Assumes CS == DS == ES, and trashes BX and CX.
1073 searchdir:
1074 mov eax,[CurrentDir]
1075 cmp byte [di],'/' ; Root directory?
1076 jne .notroot
1077 mov eax,[RootDir]
1078 inc di
1079 .notroot:
1081 .pathwalk:
1082 push eax ; <A> Current directory sector
1083 mov si,di
1084 .findend:
1085 lodsb
1086 cmp al,' '
1087 jbe .endpath
1088 cmp al,'/'
1089 jne .findend
1090 .endpath:
1091 xchg si,di
1092 pop eax ; <A> Current directory sector
1094 mov [PrevDir],eax ; Remember last directory searched
1096 push di
1097 call mangle_dos_name ; MangledBuf <- component
1098 call search_dos_dir
1099 pop di
1100 jz .notfound ; Pathname component missing
1102 cmp byte [di-1],'/' ; Do we expect a directory
1103 je .isdir
1105 ; Otherwise, it should be a file
1106 .isfile:
1107 test dl,18h ; Subdirectory|Volume Label
1108 jnz .badfile ; If not a file, it's a bad thing
1110 ; SI and EAX are already set
1111 mov edx,eax
1112 shr edx,16 ; Old 16-bit remnant...
1113 and eax,eax ; EAX != 0
1114 jz .badfile
1115 ret ; Done!
1117 ; If we expected a directory, it better be one...
1118 .isdir:
1119 test dl,10h ; Subdirectory
1120 jz .badfile
1122 xor eax,eax
1123 xchg eax,[si+file_sector] ; Get sector number and free file structure
1124 jmp .pathwalk ; Walk the next bit of the path
1126 .badfile:
1127 xor eax,eax
1128 mov [si],eax ; Free file structure
1130 .notfound:
1131 xor eax,eax
1132 xor dx,dx
1135 section .bss
1136 alignb 4
1137 CurrentDir resd 1 ; Current directory
1138 PrevDir resd 1 ; Last scanned directory
1140 section .text
1144 ; kaboom2: once everything is loaded, replace the part of kaboom
1145 ; starting with "kaboom.patch" with this part
1147 kaboom2:
1148 mov si,err_bootfailed
1149 call cwritestr
1150 cmp byte [kaboom.again+1],18h ; INT 18h version?
1151 je .int18
1152 call getchar
1153 call vgaclearmode
1154 int 19h ; And try once more to boot...
1155 .norge: jmp short .norge ; If int 19h returned; this is the end
1156 .int18:
1157 call vgaclearmode
1158 int 18h
1159 .noreg: jmp short .noreg ; Nynorsk
1162 ; mangle_name: Mangle a filename pointed to by DS:SI into a buffer pointed
1163 ; to by ES:DI; ends on encountering any whitespace.
1164 ; DI is preserved.
1166 ; This verifies that a filename is < FILENAME_MAX characters,
1167 ; doesn't contain whitespace, zero-pads the output buffer,
1168 ; and removes trailing dots and redundant slashes, plus changes
1169 ; backslashes to forward slashes,
1170 ; so "repe cmpsb" can do a compare, and the path-searching routine
1171 ; gets a bit of an easier job.
1174 mangle_name:
1175 push di
1176 push bx
1177 xor ax,ax
1178 mov cx,FILENAME_MAX-1
1179 mov bx,di
1181 .mn_loop:
1182 lodsb
1183 cmp al,' ' ; If control or space, end
1184 jna .mn_end
1185 cmp al,'\' ; Backslash?
1186 jne .mn_not_bs
1187 mov al,'/' ; Change to forward slash
1188 .mn_not_bs:
1189 cmp al,ah ; Repeated slash?
1190 je .mn_skip
1191 xor ah,ah
1192 cmp al,'/'
1193 jne .mn_ok
1194 mov ah,al
1195 .mn_ok stosb
1196 .mn_skip: loop .mn_loop
1197 .mn_end:
1198 cmp bx,di ; At the beginning of the buffer?
1199 jbe .mn_zero
1200 cmp byte [es:di-1],'.' ; Terminal dot?
1201 je .mn_kill
1202 cmp byte [es:di-1],'/' ; Terminal slash?
1203 jne .mn_zero
1204 .mn_kill: dec di ; If so, remove it
1205 inc cx
1206 jmp short .mn_end
1207 .mn_zero:
1208 inc cx ; At least one null byte
1209 xor ax,ax ; Zero-fill name
1210 rep stosb
1211 pop bx
1212 pop di
1213 ret ; Done
1216 ; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
1217 ; filename to the conventional representation. This is needed
1218 ; for the BOOT_IMAGE= parameter for the kernel.
1219 ; NOTE: A 13-byte buffer is mandatory, even if the string is
1220 ; known to be shorter.
1222 ; DS:SI -> input mangled file name
1223 ; ES:DI -> output buffer
1225 ; On return, DI points to the first byte after the output name,
1226 ; which is set to a null byte.
1228 unmangle_name: call strcpy
1229 dec di ; Point to final null byte
1233 ; mangle_dos_name:
1234 ; Mangle a DOS filename component pointed to by DS:SI
1235 ; into [MangledBuf]; ends on encountering any whitespace or slash.
1236 ; Assumes CS == DS == ES.
1239 mangle_dos_name:
1240 pusha
1241 mov di,MangledBuf
1243 mov cx,11 ; # of bytes to write
1244 .loop:
1245 lodsb
1246 cmp al,' ' ; If control or space, end
1247 jna .end
1248 cmp al,'/' ; Slash, too
1249 je .end
1250 cmp al,'.' ; Period -> space-fill
1251 je .is_period
1252 cmp al,'a'
1253 jb .not_lower
1254 cmp al,'z'
1255 ja .not_uslower
1256 sub al,020h
1257 jmp short .not_lower
1258 .is_period: mov al,' ' ; We need to space-fill
1259 .period_loop: cmp cx,3 ; If <= 3 characters left
1260 jbe .loop ; Just ignore it
1261 stosb ; Otherwise, write a period
1262 loop .period_loop ; Dec CX and (always) jump
1263 .not_uslower: cmp al,ucase_low
1264 jb .not_lower
1265 cmp al,ucase_high
1266 ja .not_lower
1267 mov bx,ucase_tab-ucase_low
1268 xlatb
1269 .not_lower: stosb
1270 loop .loop ; Don't continue if too long
1271 .end:
1272 mov al,' ' ; Space-fill name
1273 rep stosb ; Doesn't do anything if CX=0
1274 popa
1275 ret ; Done
1277 section .bss
1278 MangledBuf resb 11
1280 section .text
1282 ; Case tables for extended characters; this is technically code page 865,
1283 ; but code page 437 users will probably not miss not being able to use the
1284 ; cent sign in kernel images too much :-)
1286 ; The table only covers the range 129 to 164; the rest we can deal with.
1288 section .data
1290 ucase_low equ 129
1291 ucase_high equ 164
1292 ucase_tab db 154, 144, 'A', 142, 'A', 143, 128, 'EEEIII'
1293 db 142, 143, 144, 146, 146, 'O', 153, 'OUUY', 153, 154
1294 db 157, 156, 157, 158, 159, 'AIOU', 165
1296 section .text
1298 ; getfssec_edx: Get multiple sectors from a file
1300 ; This routine makes sure the subtransfers do not cross a 64K boundary,
1301 ; and will correct the situation if it does, UNLESS *sectors* cross
1302 ; 64K boundaries.
1304 ; ES:BX -> Buffer
1305 ; EDX -> Current sector number
1306 ; CX -> Sector count (0FFFFh = until end of file)
1307 ; Must not exceed the ES segment
1308 ; Returns EDX=0, CF=1 on EOF (not necessarily error)
1309 ; All arguments are advanced to reflect data read.
1311 getfssec_edx:
1312 push ebp
1313 push eax
1314 .getfragment:
1315 xor ebp,ebp ; Fragment sector count
1316 push edx ; Starting sector pointer
1317 .getseccnt:
1318 inc bp
1319 dec cx
1320 jz .do_read
1321 xor eax,eax
1322 mov ax,es
1323 shl ax,4
1324 add ax,bx ; Now AX = how far into 64K block we are
1325 not ax ; Bytes left in 64K block
1326 inc eax
1327 shr eax,SECTOR_SHIFT ; Sectors left in 64K block
1328 cmp bp,ax
1329 jnb .do_read ; Unless there is at least 1 more sector room...
1330 mov eax,edx ; Current sector
1331 inc edx ; Predict it's the linearly next sector
1332 call nextsector
1333 jc .do_read
1334 cmp edx,eax ; Did it match?
1335 jz .getseccnt
1336 .do_read:
1337 pop eax ; Starting sector pointer
1338 call getlinsecsr
1339 lea eax,[eax+ebp-1] ; This is the last sector actually read
1340 shl bp,9
1341 add bx,bp ; Adjust buffer pointer
1342 call nextsector
1343 jc .eof
1344 mov edx,eax
1345 and cx,cx
1346 jnz .getfragment
1347 .done:
1348 pop eax
1349 pop ebp
1351 .eof:
1352 xor edx,edx
1354 jmp .done
1357 ; getfssec: Get multiple sectors from a file
1359 ; Same as above, except SI is a pointer to a open_file_t
1361 ; ES:BX -> Buffer
1362 ; DS:SI -> Pointer to open_file_t
1363 ; CX -> Sector count (0FFFFh = until end of file)
1364 ; Must not exceed the ES segment
1365 ; Returns CF=1 on EOF (not necessarily error)
1366 ; All arguments are advanced to reflect data read.
1368 getfssec:
1369 push edx
1370 movzx edx,cx
1371 cmp edx,[si+4]
1372 jbe .sizeok
1373 mov edx,[si+4]
1374 mov cx,dx
1375 .sizeok:
1376 sub [si+4],edx
1377 mov edx,[si]
1378 call getfssec_edx
1379 mov [si],edx
1380 pop edx
1384 ; nextcluster: Advance a cluster pointer in EDI to the next cluster
1385 ; pointed at in the FAT tables. CF=0 on return if end of file.
1387 nextcluster:
1388 jmp strict short nextcluster_fat28 ; This gets patched
1390 nextcluster_fat12:
1391 push eax
1392 push edx
1393 push bx
1394 push cx
1395 push si
1396 mov edx,edi
1397 shr edi,1
1398 pushf ; Save the shifted-out LSB (=CF)
1399 add edx,edi
1400 mov eax,edx
1401 shr eax,9
1402 call getfatsector
1403 mov bx,dx
1404 and bx,1FFh
1405 mov cl,[gs:si+bx]
1406 inc edx
1407 mov eax,edx
1408 shr eax,9
1409 call getfatsector
1410 mov bx,dx
1411 and bx,1FFh
1412 mov ch,[gs:si+bx]
1413 popf
1414 jnc .even
1415 shr cx,4
1416 .even: and cx,0FFFh
1417 movzx edi,cx
1418 cmp di,0FF0h
1419 pop si
1420 pop cx
1421 pop bx
1422 pop edx
1423 pop eax
1427 ; FAT16 decoding routine.
1429 nextcluster_fat16:
1430 push eax
1431 push si
1432 push bx
1433 mov eax,edi
1434 shr eax,SECTOR_SHIFT-1
1435 call getfatsector
1436 mov bx,di
1437 add bx,bx
1438 and bx,1FEh
1439 movzx edi,word [gs:si+bx]
1440 cmp di,0FFF0h
1441 pop bx
1442 pop si
1443 pop eax
1446 ; FAT28 ("FAT32") decoding routine.
1448 nextcluster_fat28:
1449 push eax
1450 push si
1451 push bx
1452 mov eax,edi
1453 shr eax,SECTOR_SHIFT-2
1454 call getfatsector
1455 mov bx,di
1456 add bx,bx
1457 add bx,bx
1458 and bx,1FCh
1459 mov edi,dword [gs:si+bx]
1460 and edi,0FFFFFFFh ; 28 bits only
1461 cmp edi,0FFFFFF0h
1462 pop bx
1463 pop si
1464 pop eax
1468 ; nextsector: Given a sector in EAX on input, return the next sector
1469 ; of the same filesystem object, which may be the root
1470 ; directory or a cluster chain. Returns EOF.
1472 ; Assumes CS == DS.
1474 nextsector:
1475 push edi
1476 push edx
1477 mov edx,[DataArea]
1478 mov edi,eax
1479 sub edi,edx
1480 jae .isdata
1482 ; Root directory
1483 inc eax
1484 cmp eax,edx
1486 jmp .done
1488 .isdata:
1489 not edi
1490 test edi,[ClustMask]
1491 jz .endcluster
1493 ; It's not the final sector in a cluster
1494 inc eax
1495 jmp .done
1497 .endcluster:
1498 push gs ; nextcluster trashes gs
1499 push cx
1500 not edi
1501 mov cl,[ClustShift]
1502 shr edi,cl
1503 add edi,2
1505 ; Now EDI contains the cluster number
1506 call nextcluster
1508 jc .exit ; There isn't anything else...
1510 ; New cluster number now in EDI
1511 sub edi,2
1512 shl edi,cl ; CF <- 0, unless something is very wrong
1513 lea eax,[edi+edx]
1514 .exit:
1515 pop cx
1516 pop gs
1517 .done:
1518 pop edx
1519 pop edi
1523 ; getfatsector: Check for a particular sector (in EAX) in the FAT cache,
1524 ; and return a pointer in GS:SI, loading it if needed.
1526 ; Assumes CS == DS.
1528 getfatsector:
1529 add eax,[FAT] ; FAT starting address
1530 jmp getcachesector
1532 ; -----------------------------------------------------------------------------
1533 ; Common modules
1534 ; -----------------------------------------------------------------------------
1536 %include "getc.inc" ; getc et al
1537 %include "conio.inc" ; Console I/O
1538 %include "plaincon.inc" ; writechr
1539 %include "writestr.inc" ; String output
1540 %include "configinit.inc" ; Initialize configuration
1541 %include "parseconfig.inc" ; High-level config file handling
1542 %include "parsecmd.inc" ; Low-level config file handling
1543 %include "bcopy32.inc" ; 32-bit bcopy
1544 %include "loadhigh.inc" ; Load a file into high memory
1545 %include "font.inc" ; VGA font stuff
1546 %include "graphics.inc" ; VGA graphics
1547 %include "highmem.inc" ; High memory sizing
1548 %include "strcpy.inc" ; strcpy()
1549 %include "cache.inc" ; Metadata disk cache
1551 ; -----------------------------------------------------------------------------
1552 ; Begin data section
1553 ; -----------------------------------------------------------------------------
1555 section .data
1556 copyright_str db ' Copyright (C) 1994-', year, ' H. Peter Anvin'
1557 db CR, LF, 0
1558 err_bootfailed db CR, LF, 'Boot failed: please change disks and press '
1559 db 'a key to continue.', CR, LF, 0
1560 syslinux_cfg1 db '/boot' ; /boot/syslinux/syslinux.cfg
1561 syslinux_cfg2 db '/syslinux' ; /syslinux/syslinux.cfg
1562 syslinux_cfg3 db '/' ; /syslinux.cfg
1563 config_name db 'syslinux.cfg', 0 ; syslinux.cfg
1566 ; Command line options we'd like to take a look at
1568 ; mem= and vga= are handled as normal 32-bit integer values
1569 initrd_cmd db 'initrd='
1570 initrd_cmd_len equ 7
1573 ; Config file keyword table
1575 %include "keywords.inc"
1578 ; Extensions to search for (in *forward* order).
1580 exten_table: db '.cbt' ; COMBOOT (specific)
1581 db '.bss' ; Boot Sector (add superblock)
1582 db '.bs', 0 ; Boot Sector
1583 db '.com' ; COMBOOT (same as DOS)
1584 db '.c32' ; COM32
1585 exten_table_end:
1586 dd 0, 0 ; Need 8 null bytes here
1589 ; Misc initialized (data) variables
1591 %ifdef debug ; This code for debugging only
1592 debug_magic dw 0D00Dh ; Debug code sentinel
1593 %endif
1595 alignb 4, db 0
1596 BufSafe dw trackbufsize/SECTOR_SIZE ; Clusters we can load into trackbuf
1597 BufSafeSec dw trackbufsize/SECTOR_SIZE ; = how many sectors?
1598 BufSafeBytes dw trackbufsize ; = how many bytes?
1599 EndOfGetCBuf dw getcbuf+trackbufsize ; = getcbuf+BufSafeBytes
1600 %ifndef DEPEND
1601 %if ( trackbufsize % SECTOR_SIZE ) != 0
1602 %error trackbufsize must be a multiple of SECTOR_SIZE
1603 %endif
1604 %endif