Adding upstream version 3.53.
[syslinux-debian/hramrach.git] / ldlinux.asm
blobff9004359207b5b97001a418cc02ee2b40c66aff
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-8 ; The 2nd high dword on the stack
158 OrigESDI equ StackBuf-4 ; The high dword on the stack
161 ; Primary entry point. Tempting as though it may be, we can't put the
162 ; initial "cli" here; the jmp opcode in the first byte is part of the
163 ; "magic number" (using the term very loosely) for the DOS superblock.
165 bootsec equ $
166 jmp short start ; 2 bytes
167 nop ; 1 byte
169 ; "Superblock" follows -- it's in the boot sector, so it's already
170 ; loaded and ready for us
172 bsOemName db 'SYSLINUX' ; The SYS command sets this, so...
174 ; These are the fields we actually care about. We end up expanding them
175 ; all to dword size early in the code, so generate labels for both
176 ; the expanded and unexpanded versions.
178 %macro superb 1
179 bx %+ %1 equ SuperInfo+($-superblock)*8+4
180 bs %+ %1 equ $
181 zb 1
182 %endmacro
183 %macro superw 1
184 bx %+ %1 equ SuperInfo+($-superblock)*8
185 bs %+ %1 equ $
186 zw 1
187 %endmacro
188 %macro superd 1
189 bx %+ %1 equ $ ; no expansion for dwords
190 bs %+ %1 equ $
191 zd 1
192 %endmacro
193 superblock equ $
194 superw BytesPerSec
195 superb SecPerClust
196 superw ResSectors
197 superb FATs
198 superw RootDirEnts
199 superw Sectors
200 superb Media
201 superw FATsecs
202 superw SecPerTrack
203 superw Heads
204 superinfo_size equ ($-superblock)-1 ; How much to expand
205 superd Hidden
206 superd HugeSectors
208 ; This is as far as FAT12/16 and FAT32 are consistent
210 zb 54 ; FAT12/16 need 26 more bytes,
211 ; FAT32 need 54 more bytes
212 superblock_len equ $-superblock
214 SecPerClust equ bxSecPerClust
216 ; Note we don't check the constraints above now; we did that at install
217 ; time (we hope!)
219 start:
220 cli ; No interrupts yet, please
221 cld ; Copy upwards
223 ; Set up the stack
225 xor ax,ax
226 mov ss,ax
227 mov sp,StackBuf ; Just below BSS
228 push es ; Save initial ES:DI -> $PnP pointer
229 push di
230 mov es,ax
232 ; DS:SI may contain a partition table entry. Preserve it for us.
234 mov cx,8 ; Save partition info
235 mov di,PartInfo
236 rep movsw
238 mov ds,ax ; Now we can initialize DS...
241 ; Now sautee the BIOS floppy info block to that it will support decent-
242 ; size transfers; the floppy block is 11 bytes and is stored in the
243 ; INT 1Eh vector (brilliant waste of resources, eh?)
245 ; Of course, if BIOSes had been properly programmed, we wouldn't have
246 ; had to waste precious space with this code.
248 mov bx,fdctab
249 lfs si,[bx] ; FS:SI -> original fdctab
250 push fs ; Save on stack in case we need to bail
251 push si
253 ; Save the old fdctab even if hard disk so the stack layout
254 ; is the same. The instructions above do not change the flags
255 mov [DriveNumber],dl ; Save drive number in DL
256 and dl,dl ; If floppy disk (00-7F), assume no
257 ; partition table
258 js harddisk
260 floppy:
261 mov cl,6 ; 12 bytes (CX == 0)
262 ; es:di -> FloppyTable already
263 ; This should be safe to do now, interrupts are off...
264 mov [bx],di ; FloppyTable
265 mov [bx+2],ax ; Segment 0
266 fs rep movsw ; Faster to move words
267 mov cl,[bsSecPerTrack] ; Patch the sector count
268 mov [di-8],cl
269 ; AX == 0 here
270 int 13h ; Some BIOSes need this
272 jmp short not_harddisk
274 ; The drive number and possibly partition information was passed to us
275 ; by the BIOS or previous boot loader (MBR). Current "best practice" is to
276 ; trust that rather than what the superblock contains.
278 ; Would it be better to zero out bsHidden if we don't have a partition table?
280 ; Note: di points to beyond the end of PartInfo
282 harddisk:
283 test byte [di-16],7Fh ; Sanity check: "active flag" should
284 jnz no_partition ; be 00 or 80
285 mov eax,[di-8] ; Partition offset (dword)
286 mov [bsHidden],eax
287 no_partition:
289 ; Get disk drive parameters (don't trust the superblock.) Don't do this for
290 ; floppy drives -- INT 13:08 on floppy drives will (may?) return info about
291 ; what the *drive* supports, not about the *media*. Fortunately floppy disks
292 ; tend to have a fixed, well-defined geometry which is stored in the superblock.
294 ; DL == drive # still
295 mov ah,08h
296 int 13h
297 jc no_driveparm
298 and ah,ah
299 jnz no_driveparm
300 shr dx,8
301 inc dx ; Contains # of heads - 1
302 mov [bsHeads],dx
303 and cx,3fh
304 mov [bsSecPerTrack],cx
305 no_driveparm:
306 not_harddisk:
308 ; Ready to enable interrupts, captain
313 ; Do we have EBIOS (EDD)?
315 eddcheck:
316 mov bx,55AAh
317 mov ah,41h ; EDD existence query
318 mov dl,[DriveNumber]
319 int 13h
320 jc .noedd
321 cmp bx,0AA55h
322 jne .noedd
323 test cl,1 ; Extended disk access functionality set
324 jz .noedd
326 ; We have EDD support...
328 mov byte [getlinsec.jmp+1],(getlinsec_ebios-(getlinsec.jmp+2))
329 .noedd:
332 ; Load the first sector of LDLINUX.SYS; this used to be all proper
333 ; with parsing the superblock and root directory; it doesn't fit
334 ; together with EBIOS support, unfortunately.
336 mov eax,[FirstSector] ; Sector start
337 mov bx,ldlinux_sys ; Where to load it
338 call getonesec
340 ; Some modicum of integrity checking
341 cmp dword [ldlinux_magic+4],LDLINUX_MAGIC^HEXDATE
342 jne kaboom
344 ; Go for it...
345 jmp ldlinux_ent
348 ; getonesec: get one disk sector
350 getonesec:
351 mov bp,1 ; One sector
352 ; Fall through
355 ; getlinsec: load a sequence of BP floppy sector given by the linear sector
356 ; number in EAX into the buffer at ES:BX. We try to optimize
357 ; by loading up to a whole track at a time, but the user
358 ; is responsible for not crossing a 64K boundary.
359 ; (Yes, BP is weird for a count, but it was available...)
361 ; On return, BX points to the first byte after the transferred
362 ; block.
364 ; This routine assumes CS == DS, and trashes most registers.
366 ; Stylistic note: use "xchg" instead of "mov" when the source is a register
367 ; that is dead from that point; this saves space. However, please keep
368 ; the order to dst,src to keep things sane.
370 getlinsec:
371 add eax,[bsHidden] ; Add partition offset
372 xor edx,edx ; Zero-extend LBA (eventually allow 64 bits)
374 .jmp: jmp strict short getlinsec_cbios
377 ; getlinsec_ebios:
379 ; getlinsec implementation for EBIOS (EDD)
381 getlinsec_ebios:
382 .loop:
383 push bp ; Sectors left
384 .retry2:
385 call maxtrans ; Enforce maximum transfer size
386 movzx edi,bp ; Sectors we are about to read
387 mov cx,retry_count
388 .retry:
390 ; Form DAPA on stack
391 push edx
392 push eax
393 push es
394 push bx
395 push di
396 push word 16
397 mov si,sp
398 pushad
399 mov dl,[DriveNumber]
400 push ds
401 push ss
402 pop ds ; DS <- SS
403 mov ah,42h ; Extended Read
404 int 13h
405 pop ds
406 popad
407 lea sp,[si+16] ; Remove DAPA
408 jc .error
409 pop bp
410 add eax,edi ; Advance sector pointer
411 sub bp,di ; Sectors left
412 shl di,SECTOR_SHIFT ; 512-byte sectors
413 add bx,di ; Advance buffer pointer
414 and bp,bp
415 jnz .loop
419 .error:
420 ; Some systems seem to get "stuck" in an error state when
421 ; using EBIOS. Doesn't happen when using CBIOS, which is
422 ; good, since some other systems get timeout failures
423 ; waiting for the floppy disk to spin up.
425 pushad ; Try resetting the device
426 xor ax,ax
427 mov dl,[DriveNumber]
428 int 13h
429 popad
430 loop .retry ; CX-- and jump if not zero
432 ;shr word [MaxTransfer],1 ; Reduce the transfer size
433 ;jnz .retry2
435 ; Total failure. Try falling back to CBIOS.
436 mov byte [getlinsec.jmp+1],(getlinsec_cbios-(getlinsec.jmp+2))
437 ;mov byte [MaxTransfer],63 ; Max possibe CBIOS transfer
439 pop bp
440 ; ... fall through ...
443 ; getlinsec_cbios:
445 ; getlinsec implementation for legacy CBIOS
447 getlinsec_cbios:
448 .loop:
449 push edx
450 push eax
451 push bp
452 push bx
454 movzx esi,word [bsSecPerTrack]
455 movzx edi,word [bsHeads]
457 ; Dividing by sectors to get (track,sector): we may have
458 ; up to 2^18 tracks, so we need to use 32-bit arithmetric.
460 div esi
461 xor cx,cx
462 xchg cx,dx ; CX <- sector index (0-based)
463 ; EDX <- 0
464 ; eax = track #
465 div edi ; Convert track to head/cyl
467 ; We should test this, but it doesn't fit...
468 ; cmp eax,1023
469 ; ja .error
472 ; Now we have AX = cyl, DX = head, CX = sector (0-based),
473 ; BP = sectors to transfer, SI = bsSecPerTrack,
474 ; ES:BX = data target
477 call maxtrans ; Enforce maximum transfer size
479 ; Must not cross track boundaries, so BP <= SI-CX
480 sub si,cx
481 cmp bp,si
482 jna .bp_ok
483 mov bp,si
484 .bp_ok:
486 shl ah,6 ; Because IBM was STOOPID
487 ; and thought 8 bits were enough
488 ; then thought 10 bits were enough...
489 inc cx ; Sector numbers are 1-based, sigh
490 or cl,ah
491 mov ch,al
492 mov dh,dl
493 mov dl,[DriveNumber]
494 xchg ax,bp ; Sector to transfer count
495 mov ah,02h ; Read sectors
496 mov bp,retry_count
497 .retry:
498 pushad
499 int 13h
500 popad
501 jc .error
502 .resume:
503 movzx ecx,al ; ECX <- sectors transferred
504 shl ax,SECTOR_SHIFT ; Convert sectors in AL to bytes in AX
505 pop bx
506 add bx,ax
507 pop bp
508 pop eax
509 pop edx
510 add eax,ecx
511 sub bp,cx
512 jnz .loop
515 .error:
516 dec bp
517 jnz .retry
519 xchg ax,bp ; Sectors transferred <- 0
520 shr word [MaxTransfer],1
521 jnz .resume
522 ; Fall through to disk_error
525 ; kaboom: write a message and bail out.
527 disk_error:
528 kaboom:
529 xor si,si
530 mov ss,si
531 mov sp,StackBuf-4 ; Reset stack
532 mov ds,si ; Reset data segment
533 pop dword [fdctab] ; Restore FDC table
534 .patch: ; When we have full code, intercept here
535 mov si,bailmsg
537 ; Write error message, this assumes screen page 0
538 .loop: lodsb
539 and al,al
540 jz .done
541 mov ah,0Eh ; Write to screen as TTY
542 mov bx,0007h ; Attribute
543 int 10h
544 jmp short .loop
545 .done:
546 cbw ; AH <- 0
547 .again: int 16h ; Wait for keypress
548 ; NB: replaced by int 18h if
549 ; chosen at install time..
550 int 19h ; And try once more to boot...
551 .norge: jmp short .norge ; If int 19h returned; this is the end
554 ; Truncate BP to MaxTransfer
556 maxtrans:
557 cmp bp,[MaxTransfer]
558 jna .ok
559 mov bp,[MaxTransfer]
560 .ok: ret
563 ; Error message on failure
565 bailmsg: db 'Boot error', 0Dh, 0Ah, 0
567 ; This fails if the boot sector overflows
568 zb 1F8h-($-$$)
570 FirstSector dd 0xDEADBEEF ; Location of sector 1
571 MaxTransfer dw 0x007F ; Max transfer size
573 ; This field will be filled in 0xAA55 by the installer, but we abuse it
574 ; to house a pointer to the INT 16h instruction at
575 ; kaboom.again, which gets patched to INT 18h in RAID mode.
576 bootsignature dw kaboom.again-bootsec
579 ; ===========================================================================
580 ; End of boot sector
581 ; ===========================================================================
582 ; Start of LDLINUX.SYS
583 ; ===========================================================================
585 ldlinux_sys:
587 syslinux_banner db 0Dh, 0Ah
588 %if IS_MDSLINUX
589 db 'MDSLINUX '
590 %else
591 db 'SYSLINUX '
592 %endif
593 db version_str, ' ', date, ' ', 0
594 db 0Dh, 0Ah, 1Ah ; EOF if we "type" this in DOS
596 align 8, db 0
597 ldlinux_magic dd LDLINUX_MAGIC
598 dd LDLINUX_MAGIC^HEXDATE
601 ; This area is patched by the installer. It is found by looking for
602 ; LDLINUX_MAGIC, plus 8 bytes.
604 patch_area:
605 LDLDwords dw 0 ; Total dwords starting at ldlinux_sys
606 LDLSectors dw 0 ; Number of sectors - (bootsec+this sec)
607 CheckSum dd 0 ; Checksum starting at ldlinux_sys
608 ; value = LDLINUX_MAGIC - [sum of dwords]
610 ; Space for up to 64 sectors, the theoretical maximum
611 SectorPtrs times 64 dd 0
613 ldlinux_ent:
615 ; Note that some BIOSes are buggy and run the boot sector at 07C0:0000
616 ; instead of 0000:7C00 and the like. We don't want to add anything
617 ; more to the boot sector, so it is written to not assume a fixed
618 ; value in CS, but we don't want to deal with that anymore from now
619 ; on.
621 jmp 0:.next
622 .next:
625 ; Tell the user we got this far
627 mov si,syslinux_banner
628 call writestr
631 ; Tell the user if we're using EBIOS or CBIOS
633 print_bios:
634 mov si,cbios_name
635 cmp byte [getlinsec.jmp+1],(getlinsec_ebios-(getlinsec.jmp+2))
636 jne .cbios
637 mov si,ebios_name
638 .cbios:
639 mov [BIOSName],si
640 call writestr
642 section .bss
643 %define HAVE_BIOSNAME 1
644 BIOSName resw 1
646 section .text
648 ; Now we read the rest of LDLINUX.SYS. Don't bother loading the first
649 ; sector again, though.
651 load_rest:
652 mov si,SectorPtrs
653 mov bx,7C00h+2*SECTOR_SIZE ; Where we start loading
654 mov cx,[LDLSectors]
656 .get_chunk:
657 jcxz .done
658 xor bp,bp
659 lodsd ; First sector of this chunk
661 mov edx,eax
663 .make_chunk:
664 inc bp
665 dec cx
666 jz .chunk_ready
667 inc edx ; Next linear sector
668 cmp [si],edx ; Does it match
669 jnz .chunk_ready ; If not, this is it
670 add si,4 ; If so, add sector to chunk
671 jmp short .make_chunk
673 .chunk_ready:
674 call getlinsecsr
675 shl bp,SECTOR_SHIFT
676 add bx,bp
677 jmp .get_chunk
679 .done:
682 ; All loaded up, verify that we got what we needed.
683 ; Note: the checksum field is embedded in the checksum region, so
684 ; by the time we get to the end it should all cancel out.
686 verify_checksum:
687 mov si,ldlinux_sys
688 mov cx,[LDLDwords]
689 mov edx,-LDLINUX_MAGIC
690 .checksum:
691 lodsd
692 add edx,eax
693 loop .checksum
695 and edx,edx ; Should be zero
696 jz all_read ; We're cool, go for it!
699 ; Uh-oh, something went bad...
701 mov si,checksumerr_msg
702 call writestr
703 jmp kaboom
706 ; -----------------------------------------------------------------------------
707 ; Subroutines that have to be in the first sector
708 ; -----------------------------------------------------------------------------
712 ; writestr: write a null-terminated string to the console
713 ; This assumes we're on page 0. This is only used for early
714 ; messages, so it should be OK.
716 writestr:
717 .loop: lodsb
718 and al,al
719 jz .return
720 mov ah,0Eh ; Write to screen as TTY
721 mov bx,0007h ; Attribute
722 int 10h
723 jmp short .loop
724 .return: ret
727 ; getlinsecsr: save registers, call getlinsec, restore registers
729 getlinsecsr: pushad
730 call getlinsec
731 popad
735 ; Checksum error message
737 checksumerr_msg db ' Load error - ', 0 ; Boot failed appended
740 ; BIOS type string
742 cbios_name db 'CBIOS', 0
743 ebios_name db 'EBIOS', 0
746 ; Debug routine
748 %ifdef debug
749 safedumpregs:
750 cmp word [Debug_Magic],0D00Dh
751 jnz nc_return
752 jmp dumpregs
753 %endif
755 rl_checkpt equ $ ; Must be <= 8000h
757 rl_checkpt_off equ ($-$$)
758 %ifndef DEPEND
759 %if rl_checkpt_off > 400h
760 %error "Sector 1 overflow"
761 %endif
762 %endif
764 ; ----------------------------------------------------------------------------
765 ; End of code and data that have to be in the first sector
766 ; ----------------------------------------------------------------------------
768 all_read:
770 ; Let the user (and programmer!) know we got this far. This used to be
771 ; in Sector 1, but makes a lot more sense here.
773 mov si,copyright_str
774 call writestr
778 ; Insane hack to expand the superblock to dwords
780 expand_super:
781 xor eax,eax
782 mov si,superblock
783 mov di,SuperInfo
784 mov cx,superinfo_size
785 .loop:
786 lodsw
787 dec si
788 stosd ; Store expanded word
789 xor ah,ah
790 stosd ; Store expanded byte
791 loop .loop
794 ; Compute some information about this filesystem.
797 ; First, generate the map of regions
798 genfatinfo:
799 mov edx,[bxSectors]
800 and dx,dx
801 jnz .have_secs
802 mov edx,[bsHugeSectors]
803 .have_secs:
804 mov [TotalSectors],edx
806 mov eax,[bxResSectors]
807 mov [FAT],eax ; Beginning of FAT
808 mov edx,[bxFATsecs]
809 and dx,dx
810 jnz .have_fatsecs
811 mov edx,[bootsec+36] ; FAT32 BPB_FATsz32
812 .have_fatsecs:
813 imul edx,[bxFATs]
814 add eax,edx
815 mov [RootDirArea],eax ; Beginning of root directory
816 mov [RootDir],eax ; For FAT12/16 == root dir location
818 mov edx,[bxRootDirEnts]
819 add dx,SECTOR_SIZE/32-1
820 shr dx,SECTOR_SHIFT-5
821 mov [RootDirSize],edx
822 add eax,edx
823 mov [DataArea],eax ; Beginning of data area
825 ; Next, generate a cluster size shift count and mask
826 mov eax,[bxSecPerClust]
827 bsr cx,ax
828 mov [ClustShift],cl
829 push cx
830 add cl,9
831 mov [ClustByteShift],cl
832 pop cx
833 dec ax
834 mov [ClustMask],eax
835 inc ax
836 shl eax,9
837 mov [ClustSize],eax
840 ; FAT12, FAT16 or FAT28^H^H32? This computation is fscking ridiculous.
842 getfattype:
843 mov eax,[TotalSectors]
844 sub eax,[DataArea]
845 shr eax,cl ; cl == ClustShift
846 mov cl,nextcluster_fat12-(nextcluster+2)
847 cmp eax,4085 ; FAT12 limit
848 jb .setsize
849 mov cl,nextcluster_fat16-(nextcluster+2)
850 cmp eax,65525 ; FAT16 limit
851 jb .setsize
853 ; FAT32, root directory is a cluster chain
855 mov cl,[ClustShift]
856 mov eax,[bootsec+44] ; Root directory cluster
857 sub eax,2
858 shl eax,cl
859 add eax,[DataArea]
860 mov [RootDir],eax
861 mov cl,nextcluster_fat28-(nextcluster+2)
862 .setsize:
863 mov byte [nextcluster+1],cl
866 ; Common initialization code
868 %include "cpuinit.inc"
869 %include "init.inc"
872 ; Clear Files structures
874 mov di,Files
875 mov cx,(MAX_OPEN*open_file_t_size)/4
876 xor eax,eax
877 rep stosd
880 ; Initialize the metadata cache
882 call initcache
885 ; Now, everything is "up and running"... patch kaboom for more
886 ; verbosity and using the full screen system
888 ; E9 = JMP NEAR
889 mov dword [kaboom.patch],0e9h+((kaboom2-(kaboom.patch+3)) << 8)
892 ; Now we're all set to start with our *real* business. First load the
893 ; configuration file (if any) and parse it.
895 ; In previous versions I avoided using 32-bit registers because of a
896 ; rumour some BIOSes clobbered the upper half of 32-bit registers at
897 ; random. I figure, though, that if there are any of those still left
898 ; they probably won't be trying to install Linux on them...
900 ; The code is still ripe with 16-bitisms, though. Not worth the hassle
901 ; to take'm out. In fact, we may want to put them back if we're going
902 ; to boot ELKS at some point.
906 ; Load configuration file
908 mov si,config_name ; Save configuration file name
909 mov di,ConfigName
910 call strcpy
912 mov di,syslinux_cfg1
913 call open
914 jnz .config_open
915 mov di,syslinux_cfg2
916 call open
917 jnz .config_open
918 mov di,syslinux_cfg3
919 call open
920 jz no_config_file
921 .config_open:
922 mov eax,[PrevDir] ; Make the directory with syslinux.cfg ...
923 mov [CurrentDir],eax ; ... the current directory
926 ; Now we have the config file open. Parse the config file and
927 ; run the user interface.
929 %include "ui.inc"
932 ; allocate_file: Allocate a file structure
934 ; If successful:
935 ; ZF set
936 ; BX = file pointer
937 ; In unsuccessful:
938 ; ZF clear
940 allocate_file:
941 TRACER 'a'
942 push cx
943 mov bx,Files
944 mov cx,MAX_OPEN
945 .check: cmp dword [bx], byte 0
946 je .found
947 add bx,open_file_t_size ; ZF = 0
948 loop .check
949 ; ZF = 0 if we fell out of the loop
950 .found: pop cx
954 ; search_dos_dir:
955 ; Search a specific directory for a pre-mangled filename in
956 ; MangledBuf, in the directory starting in sector EAX.
958 ; NOTE: This file considers finding a zero-length file an
959 ; error. This is so we don't have to deal with that special
960 ; case elsewhere in the program (most loops have the test
961 ; at the end).
963 ; Assumes DS == ES == CS.
965 ; If successful:
966 ; ZF clear
967 ; SI = file pointer
968 ; EAX = file length (MAY BE ZERO!)
969 ; DL = file attributes
970 ; If unsuccessful
971 ; ZF set
974 search_dos_dir:
975 push bx
976 call allocate_file
977 jnz .alloc_failure
979 push cx
980 push gs
981 push es
982 push ds
983 pop es ; ES = DS
985 .scansector:
986 ; EAX <- directory sector to scan
987 call getcachesector
988 ; GS:SI now points to this sector
990 mov cx,SECTOR_SIZE/32 ; 32 == directory entry size
991 .scanentry:
992 cmp byte [gs:si],0
993 jz .failure ; Hit directory high water mark
994 test byte [gs:si+11],8 ; Ignore volume labels and
995 ; VFAT long filename entries
996 jnz .nomatch
997 push cx
998 push si
999 push di
1000 mov di,MangledBuf
1001 mov cx,11
1002 gs repe cmpsb
1003 pop di
1004 pop si
1005 pop cx
1006 jz .found
1007 .nomatch:
1008 add si,32
1009 loop .scanentry
1011 call nextsector
1012 jnc .scansector ; CF is set if we're at end
1014 ; If we get here, we failed
1015 .failure:
1016 pop es
1017 pop gs
1018 pop cx
1019 .alloc_failure:
1020 pop bx
1021 xor eax,eax ; ZF <- 1
1023 .found:
1024 mov eax,[gs:si+28] ; File size
1025 add eax,SECTOR_SIZE-1
1026 shr eax,SECTOR_SHIFT
1027 mov [bx+4],eax ; Sector count
1029 mov cl,[ClustShift]
1030 mov dx,[gs:si+20] ; High cluster word
1031 shl edx,16
1032 mov dx,[gs:si+26] ; Low cluster word
1033 sub edx,2
1034 shl edx,cl
1035 add edx,[DataArea]
1036 mov [bx],edx ; Starting sector
1038 mov eax,[gs:si+28] ; File length again
1039 mov dl,[gs:si+11] ; File attribute
1040 mov si,bx ; File pointer...
1041 and si,si ; ZF <- 0
1043 pop es
1044 pop gs
1045 pop cx
1046 pop bx
1050 ; close_file:
1051 ; Deallocates a file structure (pointer in SI)
1052 ; Assumes CS == DS.
1054 close_file:
1055 and si,si
1056 jz .closed
1057 mov dword [si],0 ; First dword == file_left
1058 .closed: ret
1061 ; searchdir:
1063 ; Open a file
1065 ; On entry:
1066 ; DS:DI = filename
1067 ; If successful:
1068 ; ZF clear
1069 ; SI = file pointer
1070 ; DX:AX or EAX = file length in bytes
1071 ; If unsuccessful
1072 ; ZF set
1074 ; Assumes CS == DS == ES, and trashes BX and CX.
1076 searchdir:
1077 mov eax,[CurrentDir]
1078 cmp byte [di],'/' ; Root directory?
1079 jne .notroot
1080 mov eax,[RootDir]
1081 inc di
1082 .notroot:
1084 .pathwalk:
1085 push eax ; <A> Current directory sector
1086 mov si,di
1087 .findend:
1088 lodsb
1089 cmp al,' '
1090 jbe .endpath
1091 cmp al,'/'
1092 jne .findend
1093 .endpath:
1094 xchg si,di
1095 pop eax ; <A> Current directory sector
1097 mov [PrevDir],eax ; Remember last directory searched
1099 push di
1100 call mangle_dos_name ; MangledBuf <- component
1101 call search_dos_dir
1102 pop di
1103 jz .notfound ; Pathname component missing
1105 cmp byte [di-1],'/' ; Do we expect a directory
1106 je .isdir
1108 ; Otherwise, it should be a file
1109 .isfile:
1110 test dl,18h ; Subdirectory|Volume Label
1111 jnz .badfile ; If not a file, it's a bad thing
1113 ; SI and EAX are already set
1114 mov edx,eax
1115 shr edx,16 ; Old 16-bit remnant...
1116 and eax,eax ; EAX != 0
1117 jz .badfile
1118 ret ; Done!
1120 ; If we expected a directory, it better be one...
1121 .isdir:
1122 test dl,10h ; Subdirectory
1123 jz .badfile
1125 xor eax,eax
1126 xchg eax,[si+file_sector] ; Get sector number and free file structure
1127 jmp .pathwalk ; Walk the next bit of the path
1129 .badfile:
1130 xor eax,eax
1131 mov [si],eax ; Free file structure
1133 .notfound:
1134 xor eax,eax
1135 xor dx,dx
1138 section .bss
1139 alignb 4
1140 CurrentDir resd 1 ; Current directory
1141 PrevDir resd 1 ; Last scanned directory
1143 section .text
1147 ; kaboom2: once everything is loaded, replace the part of kaboom
1148 ; starting with "kaboom.patch" with this part
1150 kaboom2:
1151 mov si,err_bootfailed
1152 call cwritestr
1153 cmp byte [kaboom.again+1],18h ; INT 18h version?
1154 je .int18
1155 call getchar
1156 call vgaclearmode
1157 int 19h ; And try once more to boot...
1158 .norge: jmp short .norge ; If int 19h returned; this is the end
1159 .int18:
1160 call vgaclearmode
1161 int 18h
1162 .noreg: jmp short .noreg ; Nynorsk
1165 ; mangle_name: Mangle a filename pointed to by DS:SI into a buffer pointed
1166 ; to by ES:DI; ends on encountering any whitespace.
1167 ; DI is preserved.
1169 ; This verifies that a filename is < FILENAME_MAX characters,
1170 ; doesn't contain whitespace, zero-pads the output buffer,
1171 ; and removes trailing dots and redundant slashes, plus changes
1172 ; backslashes to forward slashes,
1173 ; so "repe cmpsb" can do a compare, and the path-searching routine
1174 ; gets a bit of an easier job.
1177 mangle_name:
1178 push di
1179 push bx
1180 xor ax,ax
1181 mov cx,FILENAME_MAX-1
1182 mov bx,di
1184 .mn_loop:
1185 lodsb
1186 cmp al,' ' ; If control or space, end
1187 jna .mn_end
1188 cmp al,'\' ; Backslash?
1189 jne .mn_not_bs
1190 mov al,'/' ; Change to forward slash
1191 .mn_not_bs:
1192 cmp al,ah ; Repeated slash?
1193 je .mn_skip
1194 xor ah,ah
1195 cmp al,'/'
1196 jne .mn_ok
1197 mov ah,al
1198 .mn_ok stosb
1199 .mn_skip: loop .mn_loop
1200 .mn_end:
1201 cmp bx,di ; At the beginning of the buffer?
1202 jbe .mn_zero
1203 cmp byte [es:di-1],'.' ; Terminal dot?
1204 je .mn_kill
1205 cmp byte [es:di-1],'/' ; Terminal slash?
1206 jne .mn_zero
1207 .mn_kill: dec di ; If so, remove it
1208 inc cx
1209 jmp short .mn_end
1210 .mn_zero:
1211 inc cx ; At least one null byte
1212 xor ax,ax ; Zero-fill name
1213 rep stosb
1214 pop bx
1215 pop di
1216 ret ; Done
1219 ; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
1220 ; filename to the conventional representation. This is needed
1221 ; for the BOOT_IMAGE= parameter for the kernel.
1222 ; NOTE: A 13-byte buffer is mandatory, even if the string is
1223 ; known to be shorter.
1225 ; DS:SI -> input mangled file name
1226 ; ES:DI -> output buffer
1228 ; On return, DI points to the first byte after the output name,
1229 ; which is set to a null byte.
1231 unmangle_name: call strcpy
1232 dec di ; Point to final null byte
1236 ; mangle_dos_name:
1237 ; Mangle a DOS filename component pointed to by DS:SI
1238 ; into [MangledBuf]; ends on encountering any whitespace or slash.
1239 ; Assumes CS == DS == ES.
1242 mangle_dos_name:
1243 pusha
1244 mov di,MangledBuf
1246 mov cx,11 ; # of bytes to write
1247 .loop:
1248 lodsb
1249 cmp al,' ' ; If control or space, end
1250 jna .end
1251 cmp al,'/' ; Slash, too
1252 je .end
1253 cmp al,'.' ; Period -> space-fill
1254 je .is_period
1255 cmp al,'a'
1256 jb .not_lower
1257 cmp al,'z'
1258 ja .not_uslower
1259 sub al,020h
1260 jmp short .not_lower
1261 .is_period: mov al,' ' ; We need to space-fill
1262 .period_loop: cmp cx,3 ; If <= 3 characters left
1263 jbe .loop ; Just ignore it
1264 stosb ; Otherwise, write a period
1265 loop .period_loop ; Dec CX and (always) jump
1266 .not_uslower: cmp al,ucase_low
1267 jb .not_lower
1268 cmp al,ucase_high
1269 ja .not_lower
1270 mov bx,ucase_tab-ucase_low
1271 xlatb
1272 .not_lower: stosb
1273 loop .loop ; Don't continue if too long
1274 .end:
1275 mov al,' ' ; Space-fill name
1276 rep stosb ; Doesn't do anything if CX=0
1277 popa
1278 ret ; Done
1280 section .bss
1281 MangledBuf resb 11
1283 section .text
1285 ; Case tables for extended characters; this is technically code page 865,
1286 ; but code page 437 users will probably not miss not being able to use the
1287 ; cent sign in kernel images too much :-)
1289 ; The table only covers the range 129 to 164; the rest we can deal with.
1291 section .data
1293 ucase_low equ 129
1294 ucase_high equ 164
1295 ucase_tab db 154, 144, 'A', 142, 'A', 143, 128, 'EEEIII'
1296 db 142, 143, 144, 146, 146, 'O', 153, 'OUUY', 153, 154
1297 db 157, 156, 157, 158, 159, 'AIOU', 165
1299 section .text
1301 ; getfssec_edx: Get multiple sectors from a file
1303 ; This routine makes sure the subtransfers do not cross a 64K boundary,
1304 ; and will correct the situation if it does, UNLESS *sectors* cross
1305 ; 64K boundaries.
1307 ; ES:BX -> Buffer
1308 ; EDX -> Current sector number
1309 ; CX -> Sector count (0FFFFh = until end of file)
1310 ; Must not exceed the ES segment
1311 ; Returns EDX=0, CF=1 on EOF (not necessarily error)
1312 ; All arguments are advanced to reflect data read.
1314 getfssec_edx:
1315 push ebp
1316 push eax
1317 .getfragment:
1318 xor ebp,ebp ; Fragment sector count
1319 push edx ; Starting sector pointer
1320 .getseccnt:
1321 inc bp
1322 dec cx
1323 jz .do_read
1324 xor eax,eax
1325 mov ax,es
1326 shl ax,4
1327 add ax,bx ; Now AX = how far into 64K block we are
1328 not ax ; Bytes left in 64K block
1329 inc eax
1330 shr eax,SECTOR_SHIFT ; Sectors left in 64K block
1331 cmp bp,ax
1332 jnb .do_read ; Unless there is at least 1 more sector room...
1333 mov eax,edx ; Current sector
1334 inc edx ; Predict it's the linearly next sector
1335 call nextsector
1336 jc .do_read
1337 cmp edx,eax ; Did it match?
1338 jz .getseccnt
1339 .do_read:
1340 pop eax ; Starting sector pointer
1341 call getlinsecsr
1342 lea eax,[eax+ebp-1] ; This is the last sector actually read
1343 shl bp,9
1344 add bx,bp ; Adjust buffer pointer
1345 call nextsector
1346 jc .eof
1347 mov edx,eax
1348 and cx,cx
1349 jnz .getfragment
1350 .done:
1351 pop eax
1352 pop ebp
1354 .eof:
1355 xor edx,edx
1357 jmp .done
1360 ; getfssec: Get multiple sectors from a file
1362 ; Same as above, except SI is a pointer to a open_file_t
1364 ; ES:BX -> Buffer
1365 ; DS:SI -> Pointer to open_file_t
1366 ; CX -> Sector count (0FFFFh = until end of file)
1367 ; Must not exceed the ES segment
1368 ; Returns CF=1 on EOF (not necessarily error)
1369 ; All arguments are advanced to reflect data read.
1371 getfssec:
1372 push edx
1373 movzx edx,cx
1374 cmp edx,[si+4]
1375 jbe .sizeok
1376 mov edx,[si+4]
1377 mov cx,dx
1378 .sizeok:
1379 sub [si+4],edx
1380 mov edx,[si]
1381 call getfssec_edx
1382 mov [si],edx
1383 pop edx
1387 ; nextcluster: Advance a cluster pointer in EDI to the next cluster
1388 ; pointed at in the FAT tables. CF=0 on return if end of file.
1390 nextcluster:
1391 jmp strict short nextcluster_fat28 ; This gets patched
1393 nextcluster_fat12:
1394 push eax
1395 push edx
1396 push bx
1397 push cx
1398 push si
1399 mov edx,edi
1400 shr edi,1
1401 pushf ; Save the shifted-out LSB (=CF)
1402 add edx,edi
1403 mov eax,edx
1404 shr eax,9
1405 call getfatsector
1406 mov bx,dx
1407 and bx,1FFh
1408 mov cl,[gs:si+bx]
1409 inc edx
1410 mov eax,edx
1411 shr eax,9
1412 call getfatsector
1413 mov bx,dx
1414 and bx,1FFh
1415 mov ch,[gs:si+bx]
1416 popf
1417 jnc .even
1418 shr cx,4
1419 .even: and cx,0FFFh
1420 movzx edi,cx
1421 cmp di,0FF0h
1422 pop si
1423 pop cx
1424 pop bx
1425 pop edx
1426 pop eax
1430 ; FAT16 decoding routine.
1432 nextcluster_fat16:
1433 push eax
1434 push si
1435 push bx
1436 mov eax,edi
1437 shr eax,SECTOR_SHIFT-1
1438 call getfatsector
1439 mov bx,di
1440 add bx,bx
1441 and bx,1FEh
1442 movzx edi,word [gs:si+bx]
1443 cmp di,0FFF0h
1444 pop bx
1445 pop si
1446 pop eax
1449 ; FAT28 ("FAT32") decoding routine.
1451 nextcluster_fat28:
1452 push eax
1453 push si
1454 push bx
1455 mov eax,edi
1456 shr eax,SECTOR_SHIFT-2
1457 call getfatsector
1458 mov bx,di
1459 add bx,bx
1460 add bx,bx
1461 and bx,1FCh
1462 mov edi,dword [gs:si+bx]
1463 and edi,0FFFFFFFh ; 28 bits only
1464 cmp edi,0FFFFFF0h
1465 pop bx
1466 pop si
1467 pop eax
1471 ; nextsector: Given a sector in EAX on input, return the next sector
1472 ; of the same filesystem object, which may be the root
1473 ; directory or a cluster chain. Returns EOF.
1475 ; Assumes CS == DS.
1477 nextsector:
1478 push edi
1479 push edx
1480 mov edx,[DataArea]
1481 mov edi,eax
1482 sub edi,edx
1483 jae .isdata
1485 ; Root directory
1486 inc eax
1487 cmp eax,edx
1489 jmp .done
1491 .isdata:
1492 not edi
1493 test edi,[ClustMask]
1494 jz .endcluster
1496 ; It's not the final sector in a cluster
1497 inc eax
1498 jmp .done
1500 .endcluster:
1501 push gs ; nextcluster trashes gs
1502 push cx
1503 not edi
1504 mov cl,[ClustShift]
1505 shr edi,cl
1506 add edi,2
1508 ; Now EDI contains the cluster number
1509 call nextcluster
1511 jc .exit ; There isn't anything else...
1513 ; New cluster number now in EDI
1514 sub edi,2
1515 shl edi,cl ; CF <- 0, unless something is very wrong
1516 lea eax,[edi+edx]
1517 .exit:
1518 pop cx
1519 pop gs
1520 .done:
1521 pop edx
1522 pop edi
1526 ; getfatsector: Check for a particular sector (in EAX) in the FAT cache,
1527 ; and return a pointer in GS:SI, loading it if needed.
1529 ; Assumes CS == DS.
1531 getfatsector:
1532 add eax,[FAT] ; FAT starting address
1533 jmp getcachesector
1535 ; -----------------------------------------------------------------------------
1536 ; Common modules
1537 ; -----------------------------------------------------------------------------
1539 %include "getc.inc" ; getc et al
1540 %include "conio.inc" ; Console I/O
1541 %include "plaincon.inc" ; writechr
1542 %include "writestr.inc" ; String output
1543 %include "configinit.inc" ; Initialize configuration
1544 %include "parseconfig.inc" ; High-level config file handling
1545 %include "parsecmd.inc" ; Low-level config file handling
1546 %include "bcopy32.inc" ; 32-bit bcopy
1547 %include "loadhigh.inc" ; Load a file into high memory
1548 %include "font.inc" ; VGA font stuff
1549 %include "graphics.inc" ; VGA graphics
1550 %include "highmem.inc" ; High memory sizing
1551 %include "strcpy.inc" ; strcpy()
1552 %include "cache.inc" ; Metadata disk cache
1554 ; -----------------------------------------------------------------------------
1555 ; Begin data section
1556 ; -----------------------------------------------------------------------------
1558 section .data
1559 copyright_str db ' Copyright (C) 1994-', year, ' H. Peter Anvin'
1560 db CR, LF, 0
1561 err_bootfailed db CR, LF, 'Boot failed: please change disks and press '
1562 db 'a key to continue.', CR, LF, 0
1563 syslinux_cfg1 db '/boot' ; /boot/syslinux/syslinux.cfg
1564 syslinux_cfg2 db '/syslinux' ; /syslinux/syslinux.cfg
1565 syslinux_cfg3 db '/' ; /syslinux.cfg
1566 config_name db 'syslinux.cfg', 0 ; syslinux.cfg
1569 ; Command line options we'd like to take a look at
1571 ; mem= and vga= are handled as normal 32-bit integer values
1572 initrd_cmd db 'initrd='
1573 initrd_cmd_len equ 7
1576 ; Config file keyword table
1578 %include "keywords.inc"
1581 ; Extensions to search for (in *forward* order).
1583 exten_table: db '.cbt' ; COMBOOT (specific)
1584 db '.bss' ; Boot Sector (add superblock)
1585 db '.bs', 0 ; Boot Sector
1586 db '.com' ; COMBOOT (same as DOS)
1587 db '.c32' ; COM32
1588 exten_table_end:
1589 dd 0, 0 ; Need 8 null bytes here
1592 ; Misc initialized (data) variables
1594 %ifdef debug ; This code for debugging only
1595 debug_magic dw 0D00Dh ; Debug code sentinel
1596 %endif
1598 alignb 4, db 0
1599 BufSafe dw trackbufsize/SECTOR_SIZE ; Clusters we can load into trackbuf
1600 BufSafeBytes dw trackbufsize ; = how many bytes?
1601 %ifndef DEPEND
1602 %if ( trackbufsize % SECTOR_SIZE ) != 0
1603 %error trackbufsize must be a multiple of SECTOR_SIZE
1604 %endif
1605 %endif