Adding debian version 3.70~pre8+dfsg-1.
[syslinux-debian/hramrach.git] / ldlinux.asm
blobb12403eb9a63c8389be0dd4bb564a56aef89b648
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 1994-2008 H. Peter Anvin - All Rights Reserved
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 in high memory 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 3000h
82 cache_seg equ 2000h ; 64K area for metadata cache
83 xfer_buf_seg equ 1000h ; Bounce buffer for I/O to high mem
84 comboot_seg equ real_mode_seg ; COMBOOT image loading zone
87 ; File structure. This holds the information for each currently open file.
89 struc open_file_t
90 file_sector resd 1 ; Sector pointer (0 = structure free)
91 file_bytesleft resd 1 ; Number of bytes left
92 file_left resd 1 ; Number of sectors left
93 resd 1 ; Unused
94 endstruc
96 %ifndef DEPEND
97 %if (open_file_t_size & (open_file_t_size-1))
98 %error "open_file_t is not a power of 2"
99 %endif
100 %endif
102 ; ---------------------------------------------------------------------------
103 ; BEGIN CODE
104 ; ---------------------------------------------------------------------------
107 ; Memory below this point is reserved for the BIOS and the MBR
109 section .earlybss
110 trackbufsize equ 8192
111 trackbuf resb trackbufsize ; Track buffer goes here
112 ; ends at 2800h
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
136 section .text
138 ; Some of the things that have to be saved very early are saved
139 ; "close" to the initial stack pointer offset, in order to
140 ; reduce the code size...
142 StackBuf equ $-44-32 ; Start the stack here (grow down - 4K)
143 PartInfo equ StackBuf ; Saved partition table entry
144 FloppyTable equ PartInfo+16 ; Floppy info table (must follow PartInfo)
145 OrigFDCTabPtr equ StackBuf-8 ; The 2nd high dword on the stack
146 OrigESDI equ StackBuf-4 ; The high dword on the stack
149 ; Primary entry point. Tempting as though it may be, we can't put the
150 ; initial "cli" here; the jmp opcode in the first byte is part of the
151 ; "magic number" (using the term very loosely) for the DOS superblock.
153 bootsec equ $
154 jmp short start ; 2 bytes
155 nop ; 1 byte
157 ; "Superblock" follows -- it's in the boot sector, so it's already
158 ; loaded and ready for us
160 bsOemName db 'SYSLINUX' ; The SYS command sets this, so...
162 ; These are the fields we actually care about. We end up expanding them
163 ; all to dword size early in the code, so generate labels for both
164 ; the expanded and unexpanded versions.
166 %macro superb 1
167 bx %+ %1 equ SuperInfo+($-superblock)*8+4
168 bs %+ %1 equ $
169 zb 1
170 %endmacro
171 %macro superw 1
172 bx %+ %1 equ SuperInfo+($-superblock)*8
173 bs %+ %1 equ $
174 zw 1
175 %endmacro
176 %macro superd 1
177 bx %+ %1 equ $ ; no expansion for dwords
178 bs %+ %1 equ $
179 zd 1
180 %endmacro
181 superblock equ $
182 superw BytesPerSec
183 superb SecPerClust
184 superw ResSectors
185 superb FATs
186 superw RootDirEnts
187 superw Sectors
188 superb Media
189 superw FATsecs
190 superw SecPerTrack
191 superw Heads
192 superinfo_size equ ($-superblock)-1 ; How much to expand
193 superd Hidden
194 superd HugeSectors
196 ; This is as far as FAT12/16 and FAT32 are consistent
198 zb 54 ; FAT12/16 need 26 more bytes,
199 ; FAT32 need 54 more bytes
200 superblock_len equ $-superblock
202 SecPerClust equ bxSecPerClust
204 ; Note we don't check the constraints above now; we did that at install
205 ; time (we hope!)
207 start:
208 cli ; No interrupts yet, please
209 cld ; Copy upwards
211 ; Set up the stack
213 xor ax,ax
214 mov ss,ax
215 mov sp,StackBuf ; Just below BSS
216 push es ; Save initial ES:DI -> $PnP pointer
217 push di
218 mov es,ax
220 ; DS:SI may contain a partition table entry. Preserve it for us.
222 mov cx,8 ; Save partition info
223 mov di,PartInfo
224 rep movsw
226 mov ds,ax ; Now we can initialize DS...
229 ; Now sautee the BIOS floppy info block to that it will support decent-
230 ; size transfers; the floppy block is 11 bytes and is stored in the
231 ; INT 1Eh vector (brilliant waste of resources, eh?)
233 ; Of course, if BIOSes had been properly programmed, we wouldn't have
234 ; had to waste precious space with this code.
236 mov bx,fdctab
237 lfs si,[bx] ; FS:SI -> original fdctab
238 push fs ; Save on stack in case we need to bail
239 push si
241 ; Save the old fdctab even if hard disk so the stack layout
242 ; is the same. The instructions above do not change the flags
243 mov [DriveNumber],dl ; Save drive number in DL
244 and dl,dl ; If floppy disk (00-7F), assume no
245 ; partition table
246 js harddisk
248 floppy:
249 mov cl,6 ; 12 bytes (CX == 0)
250 ; es:di -> FloppyTable already
251 ; This should be safe to do now, interrupts are off...
252 mov [bx],di ; FloppyTable
253 mov [bx+2],ax ; Segment 0
254 fs rep movsw ; Faster to move words
255 mov cl,[bsSecPerTrack] ; Patch the sector count
256 mov [di-8],cl
257 ; AX == 0 here
258 int 13h ; Some BIOSes need this
260 jmp short not_harddisk
262 ; The drive number and possibly partition information was passed to us
263 ; by the BIOS or previous boot loader (MBR). Current "best practice" is to
264 ; trust that rather than what the superblock contains.
266 ; Would it be better to zero out bsHidden if we don't have a partition table?
268 ; Note: di points to beyond the end of PartInfo
270 harddisk:
271 test byte [di-16],7Fh ; Sanity check: "active flag" should
272 jnz no_partition ; be 00 or 80
273 mov eax,[di-8] ; Partition offset (dword)
274 mov [bsHidden],eax
275 no_partition:
277 ; Get disk drive parameters (don't trust the superblock.) Don't do this for
278 ; floppy drives -- INT 13:08 on floppy drives will (may?) return info about
279 ; what the *drive* supports, not about the *media*. Fortunately floppy disks
280 ; tend to have a fixed, well-defined geometry which is stored in the superblock.
282 ; DL == drive # still
283 mov ah,08h
284 int 13h
285 jc no_driveparm
286 and ah,ah
287 jnz no_driveparm
288 shr dx,8
289 inc dx ; Contains # of heads - 1
290 mov [bsHeads],dx
291 and cx,3fh
292 mov [bsSecPerTrack],cx
293 no_driveparm:
294 not_harddisk:
296 ; Ready to enable interrupts, captain
301 ; Do we have EBIOS (EDD)?
303 eddcheck:
304 mov bx,55AAh
305 mov ah,41h ; EDD existence query
306 mov dl,[DriveNumber]
307 int 13h
308 jc .noedd
309 cmp bx,0AA55h
310 jne .noedd
311 test cl,1 ; Extended disk access functionality set
312 jz .noedd
314 ; We have EDD support...
316 mov byte [getlinsec.jmp+1],(getlinsec_ebios-(getlinsec.jmp+2))
317 .noedd:
320 ; Load the first sector of LDLINUX.SYS; this used to be all proper
321 ; with parsing the superblock and root directory; it doesn't fit
322 ; together with EBIOS support, unfortunately.
324 mov eax,[FirstSector] ; Sector start
325 mov bx,ldlinux_sys ; Where to load it
326 call getonesec
328 ; Some modicum of integrity checking
329 cmp dword [ldlinux_magic+4],LDLINUX_MAGIC^HEXDATE
330 jne kaboom
332 ; Go for it...
333 jmp ldlinux_ent
336 ; getonesec: get one disk sector
338 getonesec:
339 mov bp,1 ; One sector
340 ; Fall through
343 ; getlinsec: load a sequence of BP floppy sector given by the linear sector
344 ; number in EAX into the buffer at ES:BX. We try to optimize
345 ; by loading up to a whole track at a time, but the user
346 ; is responsible for not crossing a 64K boundary.
347 ; (Yes, BP is weird for a count, but it was available...)
349 ; On return, BX points to the first byte after the transferred
350 ; block.
352 ; This routine assumes CS == DS, and trashes most registers.
354 ; Stylistic note: use "xchg" instead of "mov" when the source is a register
355 ; that is dead from that point; this saves space. However, please keep
356 ; the order to dst,src to keep things sane.
358 getlinsec:
359 add eax,[bsHidden] ; Add partition offset
360 xor edx,edx ; Zero-extend LBA (eventually allow 64 bits)
362 .jmp: jmp strict short getlinsec_cbios
365 ; getlinsec_ebios:
367 ; getlinsec implementation for EBIOS (EDD)
369 getlinsec_ebios:
370 .loop:
371 push bp ; Sectors left
372 .retry2:
373 call maxtrans ; Enforce maximum transfer size
374 movzx edi,bp ; Sectors we are about to read
375 mov cx,retry_count
376 .retry:
378 ; Form DAPA on stack
379 push edx
380 push eax
381 push es
382 push bx
383 push di
384 push word 16
385 mov si,sp
386 pushad
387 mov dl,[DriveNumber]
388 push ds
389 push ss
390 pop ds ; DS <- SS
391 mov ah,42h ; Extended Read
392 int 13h
393 pop ds
394 popad
395 lea sp,[si+16] ; Remove DAPA
396 jc .error
397 pop bp
398 add eax,edi ; Advance sector pointer
399 sub bp,di ; Sectors left
400 shl di,SECTOR_SHIFT ; 512-byte sectors
401 add bx,di ; Advance buffer pointer
402 and bp,bp
403 jnz .loop
407 .error:
408 ; Some systems seem to get "stuck" in an error state when
409 ; using EBIOS. Doesn't happen when using CBIOS, which is
410 ; good, since some other systems get timeout failures
411 ; waiting for the floppy disk to spin up.
413 pushad ; Try resetting the device
414 xor ax,ax
415 mov dl,[DriveNumber]
416 int 13h
417 popad
418 loop .retry ; CX-- and jump if not zero
420 ;shr word [MaxTransfer],1 ; Reduce the transfer size
421 ;jnz .retry2
423 ; Total failure. Try falling back to CBIOS.
424 mov byte [getlinsec.jmp+1],(getlinsec_cbios-(getlinsec.jmp+2))
425 ;mov byte [MaxTransfer],63 ; Max possibe CBIOS transfer
427 pop bp
428 ; ... fall through ...
431 ; getlinsec_cbios:
433 ; getlinsec implementation for legacy CBIOS
435 getlinsec_cbios:
436 .loop:
437 push edx
438 push eax
439 push bp
440 push bx
442 movzx esi,word [bsSecPerTrack]
443 movzx edi,word [bsHeads]
445 ; Dividing by sectors to get (track,sector): we may have
446 ; up to 2^18 tracks, so we need to use 32-bit arithmetric.
448 div esi
449 xor cx,cx
450 xchg cx,dx ; CX <- sector index (0-based)
451 ; EDX <- 0
452 ; eax = track #
453 div edi ; Convert track to head/cyl
455 ; We should test this, but it doesn't fit...
456 ; cmp eax,1023
457 ; ja .error
460 ; Now we have AX = cyl, DX = head, CX = sector (0-based),
461 ; BP = sectors to transfer, SI = bsSecPerTrack,
462 ; ES:BX = data target
465 call maxtrans ; Enforce maximum transfer size
467 ; Must not cross track boundaries, so BP <= SI-CX
468 sub si,cx
469 cmp bp,si
470 jna .bp_ok
471 mov bp,si
472 .bp_ok:
474 shl ah,6 ; Because IBM was STOOPID
475 ; and thought 8 bits were enough
476 ; then thought 10 bits were enough...
477 inc cx ; Sector numbers are 1-based, sigh
478 or cl,ah
479 mov ch,al
480 mov dh,dl
481 mov dl,[DriveNumber]
482 xchg ax,bp ; Sector to transfer count
483 mov ah,02h ; Read sectors
484 mov bp,retry_count
485 .retry:
486 pushad
487 int 13h
488 popad
489 jc .error
490 .resume:
491 movzx ecx,al ; ECX <- sectors transferred
492 shl ax,SECTOR_SHIFT ; Convert sectors in AL to bytes in AX
493 pop bx
494 add bx,ax
495 pop bp
496 pop eax
497 pop edx
498 add eax,ecx
499 sub bp,cx
500 jnz .loop
503 .error:
504 dec bp
505 jnz .retry
507 xchg ax,bp ; Sectors transferred <- 0
508 shr word [MaxTransfer],1
509 jnz .resume
510 ; Fall through to disk_error
513 ; kaboom: write a message and bail out.
515 disk_error:
516 kaboom:
517 xor si,si
518 mov ss,si
519 mov sp,StackBuf-4 ; Reset stack
520 mov ds,si ; Reset data segment
521 pop dword [fdctab] ; Restore FDC table
522 .patch: ; When we have full code, intercept here
523 mov si,bailmsg
525 ; Write error message, this assumes screen page 0
526 .loop: lodsb
527 and al,al
528 jz .done
529 mov ah,0Eh ; Write to screen as TTY
530 mov bx,0007h ; Attribute
531 int 10h
532 jmp short .loop
533 .done:
534 cbw ; AH <- 0
535 .again: int 16h ; Wait for keypress
536 ; NB: replaced by int 18h if
537 ; chosen at install time..
538 int 19h ; And try once more to boot...
539 .norge: jmp short .norge ; If int 19h returned; this is the end
542 ; Truncate BP to MaxTransfer
544 maxtrans:
545 cmp bp,[MaxTransfer]
546 jna .ok
547 mov bp,[MaxTransfer]
548 .ok: ret
551 ; Error message on failure
553 bailmsg: db 'Boot error', 0Dh, 0Ah, 0
555 ; This fails if the boot sector overflows
556 zb 1F8h-($-$$)
558 FirstSector dd 0xDEADBEEF ; Location of sector 1
559 MaxTransfer dw 0x007F ; Max transfer size
561 ; This field will be filled in 0xAA55 by the installer, but we abuse it
562 ; to house a pointer to the INT 16h instruction at
563 ; kaboom.again, which gets patched to INT 18h in RAID mode.
564 bootsignature dw kaboom.again-bootsec
567 ; ===========================================================================
568 ; End of boot sector
569 ; ===========================================================================
570 ; Start of LDLINUX.SYS
571 ; ===========================================================================
573 ldlinux_sys:
575 syslinux_banner db 0Dh, 0Ah
576 %if IS_MDSLINUX
577 db 'MDSLINUX '
578 %else
579 db 'SYSLINUX '
580 %endif
581 db version_str, ' ', date, ' ', 0
582 db 0Dh, 0Ah, 1Ah ; EOF if we "type" this in DOS
584 align 8, db 0
585 ldlinux_magic dd LDLINUX_MAGIC
586 dd LDLINUX_MAGIC^HEXDATE
589 ; This area is patched by the installer. It is found by looking for
590 ; LDLINUX_MAGIC, plus 8 bytes.
592 patch_area:
593 LDLDwords dw 0 ; Total dwords starting at ldlinux_sys
594 LDLSectors dw 0 ; Number of sectors - (bootsec+this sec)
595 CheckSum dd 0 ; Checksum starting at ldlinux_sys
596 ; value = LDLINUX_MAGIC - [sum of dwords]
598 ; Space for up to 64 sectors, the theoretical maximum
599 SectorPtrs times 64 dd 0
601 ldlinux_ent:
603 ; Note that some BIOSes are buggy and run the boot sector at 07C0:0000
604 ; instead of 0000:7C00 and the like. We don't want to add anything
605 ; more to the boot sector, so it is written to not assume a fixed
606 ; value in CS, but we don't want to deal with that anymore from now
607 ; on.
609 jmp 0:.next
610 .next:
613 ; Tell the user we got this far
615 mov si,syslinux_banner
616 call writestr
619 ; Tell the user if we're using EBIOS or CBIOS
621 print_bios:
622 mov si,cbios_name
623 cmp byte [getlinsec.jmp+1],(getlinsec_ebios-(getlinsec.jmp+2))
624 jne .cbios
625 mov si,ebios_name
626 .cbios:
627 mov [BIOSName],si
628 call writestr
630 section .bss
631 %define HAVE_BIOSNAME 1
632 BIOSName resw 1
634 section .text
636 ; Now we read the rest of LDLINUX.SYS. Don't bother loading the first
637 ; sector again, though.
639 load_rest:
640 mov si,SectorPtrs
641 mov bx,7C00h+2*SECTOR_SIZE ; Where we start loading
642 mov cx,[LDLSectors]
644 .get_chunk:
645 jcxz .done
646 xor bp,bp
647 lodsd ; First sector of this chunk
649 mov edx,eax
651 .make_chunk:
652 inc bp
653 dec cx
654 jz .chunk_ready
655 inc edx ; Next linear sector
656 cmp [si],edx ; Does it match
657 jnz .chunk_ready ; If not, this is it
658 add si,4 ; If so, add sector to chunk
659 jmp short .make_chunk
661 .chunk_ready:
662 call getlinsecsr
663 shl bp,SECTOR_SHIFT
664 add bx,bp
665 jmp .get_chunk
667 .done:
670 ; All loaded up, verify that we got what we needed.
671 ; Note: the checksum field is embedded in the checksum region, so
672 ; by the time we get to the end it should all cancel out.
674 verify_checksum:
675 mov si,ldlinux_sys
676 mov cx,[LDLDwords]
677 mov edx,-LDLINUX_MAGIC
678 .checksum:
679 lodsd
680 add edx,eax
681 loop .checksum
683 and edx,edx ; Should be zero
684 jz all_read ; We're cool, go for it!
687 ; Uh-oh, something went bad...
689 mov si,checksumerr_msg
690 call writestr
691 jmp kaboom
694 ; -----------------------------------------------------------------------------
695 ; Subroutines that have to be in the first sector
696 ; -----------------------------------------------------------------------------
700 ; writestr: write a null-terminated string to the console
701 ; This assumes we're on page 0. This is only used for early
702 ; messages, so it should be OK.
704 writestr:
705 .loop: lodsb
706 and al,al
707 jz .return
708 mov ah,0Eh ; Write to screen as TTY
709 mov bx,0007h ; Attribute
710 int 10h
711 jmp short .loop
712 .return: ret
715 ; getlinsecsr: save registers, call getlinsec, restore registers
717 getlinsecsr: pushad
718 call getlinsec
719 popad
723 ; Checksum error message
725 checksumerr_msg db ' Load error - ', 0 ; Boot failed appended
728 ; BIOS type string
730 cbios_name db 'CBIOS', 0
731 ebios_name db 'EBIOS', 0
734 ; Debug routine
736 %ifdef debug
737 safedumpregs:
738 cmp word [Debug_Magic],0D00Dh
739 jnz nc_return
740 jmp dumpregs
741 %endif
743 rl_checkpt equ $ ; Must be <= 8000h
745 rl_checkpt_off equ ($-$$)
746 %ifndef DEPEND
747 %if rl_checkpt_off > 400h
748 %error "Sector 1 overflow"
749 %endif
750 %endif
752 ; ----------------------------------------------------------------------------
753 ; End of code and data that have to be in the first sector
754 ; ----------------------------------------------------------------------------
756 all_read:
758 ; Let the user (and programmer!) know we got this far. This used to be
759 ; in Sector 1, but makes a lot more sense here.
761 mov si,copyright_str
762 call writestr
766 ; Insane hack to expand the superblock to dwords
768 expand_super:
769 xor eax,eax
770 mov si,superblock
771 mov di,SuperInfo
772 mov cx,superinfo_size
773 .loop:
774 lodsw
775 dec si
776 stosd ; Store expanded word
777 xor ah,ah
778 stosd ; Store expanded byte
779 loop .loop
782 ; Compute some information about this filesystem.
785 ; First, generate the map of regions
786 genfatinfo:
787 mov edx,[bxSectors]
788 and dx,dx
789 jnz .have_secs
790 mov edx,[bsHugeSectors]
791 .have_secs:
792 mov [TotalSectors],edx
794 mov eax,[bxResSectors]
795 mov [FAT],eax ; Beginning of FAT
796 mov edx,[bxFATsecs]
797 and dx,dx
798 jnz .have_fatsecs
799 mov edx,[bootsec+36] ; FAT32 BPB_FATsz32
800 .have_fatsecs:
801 imul edx,[bxFATs]
802 add eax,edx
803 mov [RootDirArea],eax ; Beginning of root directory
804 mov [RootDir],eax ; For FAT12/16 == root dir location
806 mov edx,[bxRootDirEnts]
807 add dx,SECTOR_SIZE/32-1
808 shr dx,SECTOR_SHIFT-5
809 mov [RootDirSize],edx
810 add eax,edx
811 mov [DataArea],eax ; Beginning of data area
813 ; Next, generate a cluster size shift count and mask
814 mov eax,[bxSecPerClust]
815 bsr cx,ax
816 mov [ClustShift],cl
817 push cx
818 add cl,SECTOR_SHIFT
819 mov [ClustByteShift],cl
820 pop cx
821 dec ax
822 mov [ClustMask],eax
823 inc ax
824 shl eax,SECTOR_SHIFT
825 mov [ClustSize],eax
828 ; FAT12, FAT16 or FAT28^H^H32? This computation is fscking ridiculous.
830 getfattype:
831 mov eax,[TotalSectors]
832 sub eax,[DataArea]
833 shr eax,cl ; cl == ClustShift
834 mov cl,nextcluster_fat12-(nextcluster+2)
835 cmp eax,4085 ; FAT12 limit
836 jb .setsize
837 mov cl,nextcluster_fat16-(nextcluster+2)
838 cmp eax,65525 ; FAT16 limit
839 jb .setsize
841 ; FAT32, root directory is a cluster chain
843 mov cl,[ClustShift]
844 mov eax,[bootsec+44] ; Root directory cluster
845 sub eax,2
846 shl eax,cl
847 add eax,[DataArea]
848 mov [RootDir],eax
849 mov cl,nextcluster_fat28-(nextcluster+2)
850 .setsize:
851 mov byte [nextcluster+1],cl
854 ; Common initialization code
856 %include "cpuinit.inc"
857 %include "init.inc"
860 ; Initialize the metadata cache
862 call initcache
865 ; Now, everything is "up and running"... patch kaboom for more
866 ; verbosity and using the full screen system
868 ; E9 = JMP NEAR
869 mov dword [kaboom.patch],0e9h+((kaboom2-(kaboom.patch+3)) << 8)
872 ; Now we're all set to start with our *real* business. First load the
873 ; configuration file (if any) and parse it.
875 ; In previous versions I avoided using 32-bit registers because of a
876 ; rumour some BIOSes clobbered the upper half of 32-bit registers at
877 ; random. I figure, though, that if there are any of those still left
878 ; they probably won't be trying to install Linux on them...
880 ; The code is still ripe with 16-bitisms, though. Not worth the hassle
881 ; to take'm out. In fact, we may want to put them back if we're going
882 ; to boot ELKS at some point.
886 ; Load configuration file
888 mov si,config_name ; Save configuration file name
889 mov di,ConfigName
890 call strcpy
892 mov di,syslinux_cfg1
893 call open
894 jnz .config_open
895 mov di,syslinux_cfg2
896 call open
897 jnz .config_open
898 mov di,syslinux_cfg3
899 call open
900 jz no_config_file
901 .config_open:
902 mov eax,[PrevDir] ; Make the directory with syslinux.cfg ...
903 mov [CurrentDir],eax ; ... the current directory
906 ; Now we have the config file open. Parse the config file and
907 ; run the user interface.
909 %include "ui.inc"
912 ; allocate_file: Allocate a file structure
914 ; If successful:
915 ; ZF set
916 ; BX = file pointer
917 ; In unsuccessful:
918 ; ZF clear
920 allocate_file:
921 TRACER 'a'
922 push cx
923 mov bx,Files
924 mov cx,MAX_OPEN
925 .check: cmp dword [bx], byte 0
926 je .found
927 add bx,open_file_t_size ; ZF = 0
928 loop .check
929 ; ZF = 0 if we fell out of the loop
930 .found: pop cx
934 ; search_dos_dir:
935 ; Search a specific directory for a pre-mangled filename in
936 ; MangledBuf, in the directory starting in sector EAX.
938 ; NOTE: This file considers finding a zero-length file an
939 ; error. This is so we don't have to deal with that special
940 ; case elsewhere in the program (most loops have the test
941 ; at the end).
943 ; Assumes DS == ES == CS.
945 ; If successful:
946 ; ZF clear
947 ; SI = file pointer
948 ; EAX = file length (MAY BE ZERO!)
949 ; DL = file attributes
950 ; If unsuccessful
951 ; ZF set
954 search_dos_dir:
955 push bx
956 call allocate_file
957 jnz .alloc_failure
959 push cx
960 push gs
961 push es
962 push ds
963 pop es ; ES = DS
965 .scansector:
966 ; EAX <- directory sector to scan
967 call getcachesector
968 ; GS:SI now points to this sector
970 mov cx,SECTOR_SIZE/32 ; 32 == directory entry size
971 .scanentry:
972 cmp byte [gs:si],0
973 jz .failure ; Hit directory high water mark
974 test byte [gs:si+11],8 ; Ignore volume labels and
975 ; VFAT long filename entries
976 jnz .nomatch
977 push cx
978 push si
979 push di
980 mov di,MangledBuf
981 mov cx,11
982 gs repe cmpsb
983 pop di
984 pop si
985 pop cx
986 jz .found
987 .nomatch:
988 add si,32
989 loop .scanentry
991 call nextsector
992 jnc .scansector ; CF is set if we're at end
994 ; If we get here, we failed
995 .failure:
996 pop es
997 pop gs
998 pop cx
999 .alloc_failure:
1000 pop bx
1001 xor eax,eax ; ZF <- 1
1003 .found:
1004 mov eax,[gs:si+28] ; File size
1005 add eax,SECTOR_SIZE-1
1006 shr eax,SECTOR_SHIFT
1007 mov [bx+4],eax ; Sector count
1009 mov cl,[ClustShift]
1010 mov dx,[gs:si+20] ; High cluster word
1011 shl edx,16
1012 mov dx,[gs:si+26] ; Low cluster word
1013 sub edx,2
1014 shl edx,cl
1015 add edx,[DataArea]
1016 mov [bx],edx ; Starting sector
1018 mov eax,[gs:si+28] ; File length again
1019 mov dl,[gs:si+11] ; File attribute
1020 mov si,bx ; File pointer...
1021 and si,si ; ZF <- 0
1023 pop es
1024 pop gs
1025 pop cx
1026 pop bx
1030 ; close_file:
1031 ; Deallocates a file structure (pointer in SI)
1032 ; Assumes CS == DS.
1034 close_file:
1035 and si,si
1036 jz .closed
1037 mov dword [si],0 ; First dword == file_sector
1038 xor si,si
1039 .closed: ret
1042 ; searchdir:
1044 ; Open a file
1046 ; On entry:
1047 ; DS:DI = filename
1048 ; If successful:
1049 ; ZF clear
1050 ; SI = file pointer
1051 ; EAX = file length in bytes
1052 ; If unsuccessful
1053 ; ZF set
1055 ; Assumes CS == DS == ES, and trashes BX and CX.
1057 searchdir:
1058 mov eax,[CurrentDir]
1059 cmp byte [di],'/' ; Root directory?
1060 jne .notroot
1061 mov eax,[RootDir]
1062 inc di
1063 .notroot:
1065 .pathwalk:
1066 push eax ; <A> Current directory sector
1067 mov si,di
1068 .findend:
1069 lodsb
1070 cmp al,' '
1071 jbe .endpath
1072 cmp al,'/'
1073 jne .findend
1074 .endpath:
1075 xchg si,di
1076 pop eax ; <A> Current directory sector
1078 mov [PrevDir],eax ; Remember last directory searched
1080 push di
1081 call mangle_dos_name ; MangledBuf <- component
1082 call search_dos_dir
1083 pop di
1084 jz .notfound ; Pathname component missing
1086 cmp byte [di-1],'/' ; Do we expect a directory
1087 je .isdir
1089 ; Otherwise, it should be a file
1090 .isfile:
1091 test dl,18h ; Subdirectory|Volume Label
1092 jnz .badfile ; If not a file, it's a bad thing
1094 ; SI and EAX are already set
1095 mov [si+file_bytesleft],eax
1096 push eax
1097 add eax,SECTOR_SIZE-1
1098 shr eax,SECTOR_SHIFT
1099 mov [si+file_left],eax ; Sectors left
1100 pop eax
1101 and eax,eax ; EAX != 0
1102 jz .badfile
1103 ret ; Done!
1105 ; If we expected a directory, it better be one...
1106 .isdir:
1107 test dl,10h ; Subdirectory
1108 jz .badfile
1110 xor eax,eax
1111 xchg eax,[si+file_sector] ; Get sector number and free file structure
1112 jmp .pathwalk ; Walk the next bit of the path
1114 .badfile:
1115 xor eax,eax
1116 mov [si],eax ; Free file structure
1118 .notfound:
1119 xor eax,eax
1122 section .bss
1123 alignb 4
1124 CurrentDir resd 1 ; Current directory
1125 PrevDir resd 1 ; Last scanned directory
1127 section .text
1131 ; kaboom2: once everything is loaded, replace the part of kaboom
1132 ; starting with "kaboom.patch" with this part
1134 kaboom2:
1135 mov si,err_bootfailed
1136 call cwritestr
1137 cmp byte [kaboom.again+1],18h ; INT 18h version?
1138 je .int18
1139 call getchar
1140 call vgaclearmode
1141 int 19h ; And try once more to boot...
1142 .norge: jmp short .norge ; If int 19h returned; this is the end
1143 .int18:
1144 call vgaclearmode
1145 int 18h
1146 .noreg: jmp short .noreg ; Nynorsk
1149 ; mangle_name: Mangle a filename pointed to by DS:SI into a buffer pointed
1150 ; to by ES:DI; ends on encountering any whitespace.
1151 ; DI is preserved.
1153 ; This verifies that a filename is < FILENAME_MAX characters,
1154 ; doesn't contain whitespace, zero-pads the output buffer,
1155 ; and removes trailing dots and redundant slashes, plus changes
1156 ; backslashes to forward slashes,
1157 ; so "repe cmpsb" can do a compare, and the path-searching routine
1158 ; gets a bit of an easier job.
1161 mangle_name:
1162 push di
1163 push bx
1164 xor ax,ax
1165 mov cx,FILENAME_MAX-1
1166 mov bx,di
1168 .mn_loop:
1169 lodsb
1170 cmp al,' ' ; If control or space, end
1171 jna .mn_end
1172 cmp al,'\' ; Backslash?
1173 jne .mn_not_bs
1174 mov al,'/' ; Change to forward slash
1175 .mn_not_bs:
1176 cmp al,ah ; Repeated slash?
1177 je .mn_skip
1178 xor ah,ah
1179 cmp al,'/'
1180 jne .mn_ok
1181 mov ah,al
1182 .mn_ok stosb
1183 .mn_skip: loop .mn_loop
1184 .mn_end:
1185 cmp bx,di ; At the beginning of the buffer?
1186 jbe .mn_zero
1187 cmp byte [es:di-1],'.' ; Terminal dot?
1188 je .mn_kill
1189 cmp byte [es:di-1],'/' ; Terminal slash?
1190 jne .mn_zero
1191 .mn_kill: dec di ; If so, remove it
1192 inc cx
1193 jmp short .mn_end
1194 .mn_zero:
1195 inc cx ; At least one null byte
1196 xor ax,ax ; Zero-fill name
1197 rep stosb
1198 pop bx
1199 pop di
1200 ret ; Done
1203 ; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
1204 ; filename to the conventional representation. This is needed
1205 ; for the BOOT_IMAGE= parameter for the kernel.
1206 ; NOTE: A 13-byte buffer is mandatory, even if the string is
1207 ; known to be shorter.
1209 ; DS:SI -> input mangled file name
1210 ; ES:DI -> output buffer
1212 ; On return, DI points to the first byte after the output name,
1213 ; which is set to a null byte.
1215 unmangle_name: call strcpy
1216 dec di ; Point to final null byte
1220 ; mangle_dos_name:
1221 ; Mangle a DOS filename component pointed to by DS:SI
1222 ; into [MangledBuf]; ends on encountering any whitespace or slash.
1223 ; Assumes CS == DS == ES.
1226 mangle_dos_name:
1227 pusha
1228 mov di,MangledBuf
1230 mov cx,11 ; # of bytes to write
1231 .loop:
1232 lodsb
1233 cmp al,' ' ; If control or space, end
1234 jna .end
1235 cmp al,'/' ; Slash, too
1236 je .end
1237 cmp al,'.' ; Period -> space-fill
1238 je .is_period
1239 cmp al,'a'
1240 jb .not_lower
1241 cmp al,'z'
1242 ja .not_uslower
1243 sub al,020h
1244 jmp short .not_lower
1245 .is_period: mov al,' ' ; We need to space-fill
1246 .period_loop: cmp cx,3 ; If <= 3 characters left
1247 jbe .loop ; Just ignore it
1248 stosb ; Otherwise, write a period
1249 loop .period_loop ; Dec CX and (always) jump
1250 .not_uslower: cmp al,ucase_low
1251 jb .not_lower
1252 cmp al,ucase_high
1253 ja .not_lower
1254 mov bx,ucase_tab-ucase_low
1255 xlatb
1256 .not_lower: stosb
1257 loop .loop ; Don't continue if too long
1258 .end:
1259 mov al,' ' ; Space-fill name
1260 rep stosb ; Doesn't do anything if CX=0
1261 popa
1262 ret ; Done
1264 section .bss
1265 MangledBuf resb 11
1267 section .text
1269 ; Case tables for extended characters; this is technically code page 865,
1270 ; but code page 437 users will probably not miss not being able to use the
1271 ; cent sign in kernel images too much :-)
1273 ; The table only covers the range 129 to 164; the rest we can deal with.
1275 section .data
1277 ucase_low equ 129
1278 ucase_high equ 164
1279 ucase_tab db 154, 144, 'A', 142, 'A', 143, 128, 'EEEIII'
1280 db 142, 143, 144, 146, 146, 'O', 153, 'OUUY', 153, 154
1281 db 157, 156, 157, 158, 159, 'AIOU', 165
1283 section .text
1285 ; getfssec_edx: Get multiple sectors from a file
1287 ; This routine makes sure the subtransfers do not cross a 64K boundary,
1288 ; and will correct the situation if it does, UNLESS *sectors* cross
1289 ; 64K boundaries.
1291 ; ES:BX -> Buffer
1292 ; EDX -> Current sector number
1293 ; CX -> Sector count (0FFFFh = until end of file)
1294 ; Must not exceed the ES segment
1295 ; Returns EDX=0, CF=1 on EOF (not necessarily error)
1296 ; All arguments are advanced to reflect data read.
1298 getfssec_edx:
1299 push ebp
1300 push eax
1301 .getfragment:
1302 xor ebp,ebp ; Fragment sector count
1303 push edx ; Starting sector pointer
1304 .getseccnt:
1305 inc bp
1306 dec cx
1307 jz .do_read
1308 xor eax,eax
1309 mov ax,es
1310 shl ax,4
1311 add ax,bx ; Now AX = how far into 64K block we are
1312 not ax ; Bytes left in 64K block
1313 inc eax
1314 shr eax,SECTOR_SHIFT ; Sectors left in 64K block
1315 cmp bp,ax
1316 jnb .do_read ; Unless there is at least 1 more sector room...
1317 mov eax,edx ; Current sector
1318 inc edx ; Predict it's the linearly next sector
1319 call nextsector
1320 jc .do_read
1321 cmp edx,eax ; Did it match?
1322 jz .getseccnt
1323 .do_read:
1324 pop eax ; Starting sector pointer
1325 call getlinsecsr
1326 lea eax,[eax+ebp-1] ; This is the last sector actually read
1327 shl bp,9
1328 add bx,bp ; Adjust buffer pointer
1329 call nextsector
1330 jc .eof
1331 mov edx,eax
1332 and cx,cx
1333 jnz .getfragment
1334 .done:
1335 pop eax
1336 pop ebp
1338 .eof:
1339 xor edx,edx
1341 jmp .done
1344 ; getfssec: Get multiple sectors from a file
1346 ; Same as above, except SI is a pointer to a open_file_t
1348 ; ES:BX -> Buffer
1349 ; DS:SI -> Pointer to open_file_t
1350 ; CX -> Sector count (0FFFFh = until end of file)
1351 ; Must not exceed the ES segment
1352 ; Returns CF=1 on EOF (not necessarily error)
1353 ; ECX returns number of bytes read.
1354 ; All arguments are advanced to reflect data read.
1356 getfssec:
1357 push edx
1358 movzx edx,cx
1359 push edx ; Zero-extended CX
1360 cmp edx,[si+file_left]
1361 jbe .sizeok
1362 mov edx,[si+file_left]
1363 mov cx,dx
1364 .sizeok:
1365 sub [si+file_left],edx
1366 mov edx,[si+file_sector]
1367 call getfssec_edx
1368 mov [si+file_sector],edx
1369 pop ecx ; Sectors requested read
1370 shl ecx,SECTOR_SHIFT
1371 cmp ecx,[si+file_bytesleft]
1372 ja .eof
1373 .noteof:
1374 sub [si+file_bytesleft],ecx ; CF <- 0
1375 pop edx
1377 .eof:
1378 mov ecx,[si+file_bytesleft]
1379 call close_file
1380 pop edx
1385 ; nextcluster: Advance a cluster pointer in EDI to the next cluster
1386 ; pointed at in the FAT tables. CF=0 on return if end of file.
1388 nextcluster:
1389 jmp strict short nextcluster_fat28 ; This gets patched
1391 nextcluster_fat12:
1392 push eax
1393 push edx
1394 push bx
1395 push cx
1396 push si
1397 mov edx,edi
1398 shr edi,1
1399 pushf ; Save the shifted-out LSB (=CF)
1400 add edx,edi
1401 mov eax,edx
1402 shr eax,9
1403 call getfatsector
1404 mov bx,dx
1405 and bx,1FFh
1406 mov cl,[gs:si+bx]
1407 inc edx
1408 mov eax,edx
1409 shr eax,9
1410 call getfatsector
1411 mov bx,dx
1412 and bx,1FFh
1413 mov ch,[gs:si+bx]
1414 popf
1415 jnc .even
1416 shr cx,4
1417 .even: and cx,0FFFh
1418 movzx edi,cx
1419 cmp di,0FF0h
1420 pop si
1421 pop cx
1422 pop bx
1423 pop edx
1424 pop eax
1428 ; FAT16 decoding routine.
1430 nextcluster_fat16:
1431 push eax
1432 push si
1433 push bx
1434 mov eax,edi
1435 shr eax,SECTOR_SHIFT-1
1436 call getfatsector
1437 mov bx,di
1438 add bx,bx
1439 and bx,1FEh
1440 movzx edi,word [gs:si+bx]
1441 cmp di,0FFF0h
1442 pop bx
1443 pop si
1444 pop eax
1447 ; FAT28 ("FAT32") decoding routine.
1449 nextcluster_fat28:
1450 push eax
1451 push si
1452 push bx
1453 mov eax,edi
1454 shr eax,SECTOR_SHIFT-2
1455 call getfatsector
1456 mov bx,di
1457 add bx,bx
1458 add bx,bx
1459 and bx,1FCh
1460 mov edi,dword [gs:si+bx]
1461 and edi,0FFFFFFFh ; 28 bits only
1462 cmp edi,0FFFFFF0h
1463 pop bx
1464 pop si
1465 pop eax
1469 ; nextsector: Given a sector in EAX on input, return the next sector
1470 ; of the same filesystem object, which may be the root
1471 ; directory or a cluster chain. Returns EOF.
1473 ; Assumes CS == DS.
1475 nextsector:
1476 push edi
1477 push edx
1478 mov edx,[DataArea]
1479 mov edi,eax
1480 sub edi,edx
1481 jae .isdata
1483 ; Root directory
1484 inc eax
1485 cmp eax,edx
1487 jmp .done
1489 .isdata:
1490 not edi
1491 test edi,[ClustMask]
1492 jz .endcluster
1494 ; It's not the final sector in a cluster
1495 inc eax
1496 jmp .done
1498 .endcluster:
1499 push gs ; nextcluster trashes gs
1500 push cx
1501 not edi
1502 mov cl,[ClustShift]
1503 shr edi,cl
1504 add edi,2
1506 ; Now EDI contains the cluster number
1507 call nextcluster
1509 jc .exit ; There isn't anything else...
1511 ; New cluster number now in EDI
1512 sub edi,2
1513 shl edi,cl ; CF <- 0, unless something is very wrong
1514 lea eax,[edi+edx]
1515 .exit:
1516 pop cx
1517 pop gs
1518 .done:
1519 pop edx
1520 pop edi
1524 ; getfatsector: Check for a particular sector (in EAX) in the FAT cache,
1525 ; and return a pointer in GS:SI, loading it if needed.
1527 ; Assumes CS == DS.
1529 getfatsector:
1530 add eax,[FAT] ; FAT starting address
1531 jmp getcachesector
1533 ; -----------------------------------------------------------------------------
1534 ; Common modules
1535 ; -----------------------------------------------------------------------------
1537 %include "getc.inc" ; getc et al
1538 %include "conio.inc" ; Console I/O
1539 %include "plaincon.inc" ; writechr
1540 %include "writestr.inc" ; String output
1541 %include "configinit.inc" ; Initialize configuration
1542 %include "parseconfig.inc" ; High-level config file handling
1543 %include "parsecmd.inc" ; Low-level config file handling
1544 %include "bcopy32.inc" ; 32-bit bcopy
1545 %include "loadhigh.inc" ; Load a file into high memory
1546 %include "font.inc" ; VGA font stuff
1547 %include "graphics.inc" ; VGA graphics
1548 %include "highmem.inc" ; High memory sizing
1549 %include "strcpy.inc" ; strcpy()
1550 %include "cache.inc" ; Metadata disk cache
1551 %include "adv.inc" ; Auxillary Data Vector
1553 ; -----------------------------------------------------------------------------
1554 ; Begin data section
1555 ; -----------------------------------------------------------------------------
1557 section .data
1558 copyright_str db ' Copyright (C) 1994-', year, ' H. Peter Anvin'
1559 db CR, LF, 0
1560 err_bootfailed db CR, LF, 'Boot failed: please change disks and press '
1561 db 'a key to continue.', CR, LF, 0
1562 syslinux_cfg1 db '/boot' ; /boot/syslinux/syslinux.cfg
1563 syslinux_cfg2 db '/syslinux' ; /syslinux/syslinux.cfg
1564 syslinux_cfg3 db '/' ; /syslinux.cfg
1565 config_name db 'syslinux.cfg', 0 ; syslinux.cfg
1568 ; Command line options we'd like to take a look at
1570 ; mem= and vga= are handled as normal 32-bit integer values
1571 initrd_cmd db 'initrd='
1572 initrd_cmd_len equ 7
1575 ; Config file keyword table
1577 %include "keywords.inc"
1580 ; Extensions to search for (in *forward* order).
1582 exten_table: db '.cbt' ; COMBOOT (specific)
1583 db '.bss' ; Boot Sector (add superblock)
1584 db '.bs', 0 ; Boot Sector
1585 db '.com' ; COMBOOT (same as DOS)
1586 db '.c32' ; COM32
1587 exten_table_end:
1588 dd 0, 0 ; Need 8 null bytes here
1591 ; Misc initialized (data) variables
1593 %ifdef debug ; This code for debugging only
1594 debug_magic dw 0D00Dh ; Debug code sentinel
1595 %endif
1597 alignb 4, db 0
1598 BufSafe dw trackbufsize/SECTOR_SIZE ; Clusters we can load into trackbuf
1599 BufSafeBytes dw trackbufsize ; = how many bytes?
1600 %ifndef DEPEND
1601 %if ( trackbufsize % SECTOR_SIZE ) != 0
1602 %error trackbufsize must be a multiple of SECTOR_SIZE
1603 %endif
1604 %endif