Support LOCALBOOT (ISOLINUX-style) in SYSLINUX/EXTLINUX
[profile/ivi/syslinux.git] / extlinux.asm
1 ; -*- fundamental -*- (asm-mode sucks)
2 ; ****************************************************************************
3 ;
4 ;  extlinux.asm
5 ;
6 ;  A program to boot Linux kernels off an ext2/ext3 filesystem.
7 ;
8 ;   Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
9 ;
10 ;  This program is free software; you can redistribute it and/or modify
11 ;  it under the terms of the GNU General Public License as published by
12 ;  the Free Software Foundation, Inc., 53 Temple Place Ste 330,
13 ;  Boston MA 02111-1307, USA; either version 2 of the License, or
14 ;  (at your option) any later version; incorporated herein by reference.
15 ;
16 ; ****************************************************************************
17
18 %define IS_EXTLINUX 1
19 %include "head.inc"
20 %include "ext2_fs.inc"
21
22 ;
23 ; Some semi-configurable constants... change on your own risk.
24 ;
25 my_id           equ extlinux_id
26 ; NASM 0.98.38 croaks if these are equ's rather than macros...
27 FILENAME_MAX_LG2 equ 8                  ; log2(Max filename size Including final null)
28 FILENAME_MAX    equ (1 << FILENAME_MAX_LG2)     ; Max mangled filename size
29 NULLFILE        equ 0                   ; Null character == empty filename
30 NULLOFFSET      equ 0                   ; Position in which to look
31 retry_count     equ 16                  ; How patient are we with the disk?
32 %assign HIGHMEM_SLOP 0                  ; Avoid this much memory near the top
33 LDLINUX_MAGIC   equ 0x3eb202fe          ; A random number to identify ourselves with
34
35 MAX_OPEN_LG2    equ 6                   ; log2(Max number of open files)
36 MAX_OPEN        equ (1 << MAX_OPEN_LG2)
37
38 SECTOR_SHIFT    equ 9
39 SECTOR_SIZE     equ (1 << SECTOR_SHIFT)
40
41 MAX_SYMLINKS    equ 64                  ; Maximum number of symlinks per lookup
42 SYMLINK_SECTORS equ 2                   ; Max number of sectors in a symlink
43                                         ; (should be >= FILENAME_MAX)
44
45 ;
46 ; This is what we need to do when idle
47 ;
48 %macro  RESET_IDLE 0
49         ; Nothing
50 %endmacro
51 %macro  DO_IDLE 0
52         ; Nothing
53 %endmacro
54
55 ;
56 ; The following structure is used for "virtual kernels"; i.e. LILO-style
57 ; option labels.  The options we permit here are `kernel' and `append
58 ; Since there is no room in the bottom 64K for all of these, we
59 ; stick them in high memory and copy them down before we need them.
60 ;
61                 struc vkernel
62 vk_vname:       resb FILENAME_MAX       ; Virtual name **MUST BE FIRST!**
63 vk_rname:       resb FILENAME_MAX       ; Real name
64 vk_appendlen:   resw 1
65 vk_type:        resb 1                  ; Type of file
66                 alignb 4
67 vk_append:      resb max_cmd_len+1      ; Command line
68                 alignb 4
69 vk_end:         equ $                   ; Should be <= vk_size
70                 endstruc
71
72 ;
73 ; Segment assignments in the bottom 640K
74 ; Stick to the low 512K in case we're using something like M-systems flash
75 ; which load a driver into low RAM (evil!!)
76 ;
77 ; 0000h - main code/data segment (and BIOS segment)
78 ;
79 real_mode_seg   equ 3000h
80 cache_seg       equ 2000h               ; 64K area for metadata cache
81 xfer_buf_seg    equ 1000h               ; Bounce buffer for I/O to high mem
82 comboot_seg     equ real_mode_seg       ; COMBOOT image loading zone
83
84 ;
85 ; File structure.  This holds the information for each currently open file.
86 ;
87                 struc open_file_t
88 file_left       resd 1                  ; Number of sectors left (0 = free)
89 file_sector     resd 1                  ; Next linear sector to read
90 file_in_sec     resd 1                  ; Sector where inode lives
91 file_in_off     resw 1
92 file_mode       resw 1
93                 endstruc
94
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
100
101 ; ---------------------------------------------------------------------------
102 ;   BEGIN CODE
103 ; ---------------------------------------------------------------------------
104
105 ;
106 ; Memory below this point is reserved for the BIOS and the MBR
107 ;
108                 section .earlybss
109 trackbufsize    equ 8192
110 trackbuf        resb trackbufsize       ; Track buffer goes here
111                 ; ends at 2800h
112
113                 section .bss
114 SuperBlock      resb 1024               ; ext2 superblock
115 SuperInfo       resq 16                 ; DOS superblock expanded
116 ClustSize       resd 1                  ; Bytes/cluster ("block")
117 SecPerClust     resd 1                  ; Sectors/cluster
118 ClustMask       resd 1                  ; Sectors/cluster - 1
119 PtrsPerBlock1   resd 1                  ; Pointers/cluster
120 PtrsPerBlock2   resd 1                  ; (Pointers/cluster)^2
121 DriveNumber     resb 1                  ; BIOS drive number
122 ClustShift      resb 1                  ; Shift count for sectors/cluster
123 ClustByteShift  resb 1                  ; Shift count for bytes/cluster
124
125                 alignb open_file_t_size
126 Files           resb MAX_OPEN*open_file_t_size
127
128                 section .text
129 ;
130 ; Some of the things that have to be saved very early are saved
131 ; "close" to the initial stack pointer offset, in order to
132 ; reduce the code size...
133 ;
134 StackBuf        equ $-44-32             ; Start the stack here (grow down - 4K)
135 PartInfo        equ StackBuf            ; Saved partition table entry
136 FloppyTable     equ PartInfo+16         ; Floppy info table (must follow PartInfo)
137 OrigFDCTabPtr   equ StackBuf-8          ; The 2nd high dword on the stack
138 OrigESDI        equ StackBuf-4          ; The high dword on the stack
139
140 ;
141 ; Primary entry point.  Tempting as though it may be, we can't put the
142 ; initial "cli" here; the jmp opcode in the first byte is part of the
143 ; "magic number" (using the term very loosely) for the DOS superblock.
144 ;
145 bootsec         equ $
146                 jmp short start         ; 2 bytes
147                 nop                     ; 1 byte
148 ;
149 ; "Superblock" follows -- it's in the boot sector, so it's already
150 ; loaded and ready for us
151 ;
152 bsOemName       db 'EXTLINUX'           ; The SYS command sets this, so...
153 ;
154 ; These are the fields we actually care about.  We end up expanding them
155 ; all to dword size early in the code, so generate labels for both
156 ; the expanded and unexpanded versions.
157 ;
158 %macro          superb 1
159 bx %+ %1        equ SuperInfo+($-superblock)*8+4
160 bs %+ %1        equ $
161                 zb 1
162 %endmacro
163 %macro          superw 1
164 bx %+ %1        equ SuperInfo+($-superblock)*8
165 bs %+ %1        equ $
166                 zw 1
167 %endmacro
168 %macro          superd 1
169 bx %+ %1        equ $                   ; no expansion for dwords
170 bs %+ %1        equ $
171                 zd 1
172 %endmacro
173 superblock      equ $
174                 superw BytesPerSec
175                 superb SecPerClust
176                 superw ResSectors
177                 superb FATs
178                 superw RootDirEnts
179                 superw Sectors
180                 superb Media
181                 superw FATsecs
182                 superw SecPerTrack
183                 superw Heads
184 superinfo_size  equ ($-superblock)-1    ; How much to expand
185                 superd Hidden
186                 superd HugeSectors
187                 ;
188                 ; This is as far as FAT12/16 and FAT32 are consistent
189                 ;
190                 zb 54                   ; FAT12/16 need 26 more bytes,
191                                         ; FAT32 need 54 more bytes
192 superblock_len  equ $-superblock
193
194 ;
195 ; Note we don't check the constraints above now; we did that at install
196 ; time (we hope!)
197 ;
198 start:
199                 cli                     ; No interrupts yet, please
200                 cld                     ; Copy upwards
201 ;
202 ; Set up the stack
203 ;
204                 xor ax,ax
205                 mov ss,ax
206                 mov sp,StackBuf         ; Just below BSS
207                 push es                 ; Save initial ES:DI -> $PnP pointer
208                 push di
209                 mov es,ax
210 ;
211 ; DS:SI may contain a partition table entry.  Preserve it for us.
212 ;
213                 mov cx,8                ; Save partition info
214                 mov di,PartInfo
215                 rep movsw
216
217                 mov ds,ax               ; Now we can initialize DS...
218
219 ;
220 ; Now sautee the BIOS floppy info block to that it will support decent-
221 ; size transfers; the floppy block is 11 bytes and is stored in the
222 ; INT 1Eh vector (brilliant waste of resources, eh?)
223 ;
224 ; Of course, if BIOSes had been properly programmed, we wouldn't have
225 ; had to waste precious space with this code.
226 ;
227                 mov bx,fdctab
228                 lfs si,[bx]             ; FS:SI -> original fdctab
229                 push fs                 ; Save on stack in case we need to bail
230                 push si
231
232                 ; Save the old fdctab even if hard disk so the stack layout
233                 ; is the same.  The instructions above do not change the flags
234                 mov [DriveNumber],dl    ; Save drive number in DL
235                 and dl,dl               ; If floppy disk (00-7F), assume no
236                                         ; partition table
237                 js harddisk
238
239 floppy:
240                 mov cl,6                ; 12 bytes (CX == 0)
241                 ; es:di -> FloppyTable already
242                 ; This should be safe to do now, interrupts are off...
243                 mov [bx],di             ; FloppyTable
244                 mov [bx+2],ax           ; Segment 0
245                 fs rep movsw            ; Faster to move words
246                 mov cl,[bsSecPerTrack]  ; Patch the sector count
247                 mov [di-8],cl
248                 ; AX == 0 here
249                 int 13h                 ; Some BIOSes need this
250
251                 jmp short not_harddisk
252 ;
253 ; The drive number and possibly partition information was passed to us
254 ; by the BIOS or previous boot loader (MBR).  Current "best practice" is to
255 ; trust that rather than what the superblock contains.
256 ;
257 ; Would it be better to zero out bsHidden if we don't have a partition table?
258 ;
259 ; Note: di points to beyond the end of PartInfo
260 ;
261 harddisk:
262                 test byte [di-16],7Fh   ; Sanity check: "active flag" should
263                 jnz no_partition        ; be 00 or 80
264                 mov eax,[di-8]          ; Partition offset (dword)
265                 mov [bsHidden],eax
266 no_partition:
267 ;
268 ; Get disk drive parameters (don't trust the superblock.)  Don't do this for
269 ; floppy drives -- INT 13:08 on floppy drives will (may?) return info about
270 ; what the *drive* supports, not about the *media*.  Fortunately floppy disks
271 ; tend to have a fixed, well-defined geometry which is stored in the superblock.
272 ;
273                 ; DL == drive # still
274                 mov ah,08h
275                 int 13h
276                 jc no_driveparm
277                 and ah,ah
278                 jnz no_driveparm
279                 shr dx,8
280                 inc dx                  ; Contains # of heads - 1
281                 mov [bsHeads],dx
282                 and cx,3fh
283                 mov [bsSecPerTrack],cx
284 no_driveparm:
285 not_harddisk:
286 ;
287 ; Ready to enable interrupts, captain
288 ;
289                 sti
290
291 ;
292 ; Do we have EBIOS (EDD)?
293 ;
294 eddcheck:
295                 mov bx,55AAh
296                 mov ah,41h              ; EDD existence query
297                 mov dl,[DriveNumber]
298                 int 13h
299                 jc .noedd
300                 cmp bx,0AA55h
301                 jne .noedd
302                 test cl,1               ; Extended disk access functionality set
303                 jz .noedd
304                 ;
305                 ; We have EDD support...
306                 ;
307                 mov byte [getlinsec.jmp+1],(getlinsec_ebios-(getlinsec.jmp+2))
308 .noedd:
309
310 ;
311 ; Load the first sector of LDLINUX.SYS; this used to be all proper
312 ; with parsing the superblock and root directory; it doesn't fit
313 ; together with EBIOS support, unfortunately.
314 ;
315                 mov eax,[FirstSector]   ; Sector start
316                 mov bx,ldlinux_sys      ; Where to load it
317                 call getonesec
318
319                 ; Some modicum of integrity checking
320                 cmp dword [ldlinux_magic+4],LDLINUX_MAGIC^HEXDATE
321                 jne kaboom
322
323                 ; Go for it...
324                 jmp ldlinux_ent
325
326 ;
327 ; getonesec: get one disk sector
328 ;
329 getonesec:
330                 mov bp,1                ; One sector
331                 ; Fall through
332
333 ;
334 ; getlinsec: load a sequence of BP floppy sector given by the linear sector
335 ;            number in EAX into the buffer at ES:BX.  We try to optimize
336 ;            by loading up to a whole track at a time, but the user
337 ;            is responsible for not crossing a 64K boundary.
338 ;            (Yes, BP is weird for a count, but it was available...)
339 ;
340 ;            On return, BX points to the first byte after the transferred
341 ;            block.
342 ;
343 ;            This routine assumes CS == DS, and trashes most registers.
344 ;
345 ; Stylistic note: use "xchg" instead of "mov" when the source is a register
346 ; that is dead from that point; this saves space.  However, please keep
347 ; the order to dst,src to keep things sane.
348 ;
349 getlinsec:
350                 add eax,[bsHidden]              ; Add partition offset
351                 xor edx,edx                     ; Zero-extend LBA (eventually allow 64 bits)
352
353 .jmp:           jmp strict short getlinsec_cbios
354
355 ;
356 ; getlinsec_ebios:
357 ;
358 ; getlinsec implementation for EBIOS (EDD)
359 ;
360 getlinsec_ebios:
361 .loop:
362                 push bp                         ; Sectors left
363 .retry2:
364                 call maxtrans                   ; Enforce maximum transfer size
365                 movzx edi,bp                    ; Sectors we are about to read
366                 mov cx,retry_count
367 .retry:
368
369                 ; Form DAPA on stack
370                 push edx
371                 push eax
372                 push es
373                 push bx
374                 push di
375                 push word 16
376                 mov si,sp
377                 pushad
378                 mov dl,[DriveNumber]
379                 push ds
380                 push ss
381                 pop ds                          ; DS <- SS
382                 mov ah,42h                      ; Extended Read
383                 int 13h
384                 pop ds
385                 popad
386                 lea sp,[si+16]                  ; Remove DAPA
387                 jc .error
388                 pop bp
389                 add eax,edi                     ; Advance sector pointer
390                 sub bp,di                       ; Sectors left
391                 shl di,SECTOR_SHIFT             ; 512-byte sectors
392                 add bx,di                       ; Advance buffer pointer
393                 and bp,bp
394                 jnz .loop
395
396                 ret
397
398 .error:
399                 ; Some systems seem to get "stuck" in an error state when
400                 ; using EBIOS.  Doesn't happen when using CBIOS, which is
401                 ; good, since some other systems get timeout failures
402                 ; waiting for the floppy disk to spin up.
403
404                 pushad                          ; Try resetting the device
405                 xor ax,ax
406                 mov dl,[DriveNumber]
407                 int 13h
408                 popad
409                 loop .retry                     ; CX-- and jump if not zero
410
411                 ;shr word [MaxTransfer],1       ; Reduce the transfer size
412                 ;jnz .retry2
413
414                 ; Total failure.  Try falling back to CBIOS.
415                 mov byte [getlinsec.jmp+1],(getlinsec_cbios-(getlinsec.jmp+2))
416                 ;mov byte [MaxTransfer],63      ; Max possibe CBIOS transfer
417
418                 pop bp
419                 ; ... fall through ...
420
421 ;
422 ; getlinsec_cbios:
423 ;
424 ; getlinsec implementation for legacy CBIOS
425 ;
426 getlinsec_cbios:
427 .loop:
428                 push edx
429                 push eax
430                 push bp
431                 push bx
432
433                 movzx esi,word [bsSecPerTrack]
434                 movzx edi,word [bsHeads]
435                 ;
436                 ; Dividing by sectors to get (track,sector): we may have
437                 ; up to 2^18 tracks, so we need to use 32-bit arithmetric.
438                 ;
439                 div esi
440                 xor cx,cx
441                 xchg cx,dx              ; CX <- sector index (0-based)
442                                         ; EDX <- 0
443                 ; eax = track #
444                 div edi                 ; Convert track to head/cyl
445
446                 ; We should test this, but it doesn't fit...
447                 ; cmp eax,1023
448                 ; ja .error
449
450                 ;
451                 ; Now we have AX = cyl, DX = head, CX = sector (0-based),
452                 ; BP = sectors to transfer, SI = bsSecPerTrack,
453                 ; ES:BX = data target
454                 ;
455
456                 call maxtrans                   ; Enforce maximum transfer size
457
458                 ; Must not cross track boundaries, so BP <= SI-CX
459                 sub si,cx
460                 cmp bp,si
461                 jna .bp_ok
462                 mov bp,si
463 .bp_ok:
464
465                 shl ah,6                ; Because IBM was STOOPID
466                                         ; and thought 8 bits were enough
467                                         ; then thought 10 bits were enough...
468                 inc cx                  ; Sector numbers are 1-based, sigh
469                 or cl,ah
470                 mov ch,al
471                 mov dh,dl
472                 mov dl,[DriveNumber]
473                 xchg ax,bp              ; Sector to transfer count
474                 mov ah,02h              ; Read sectors
475                 mov bp,retry_count
476 .retry:
477                 pushad
478                 int 13h
479                 popad
480                 jc .error
481 .resume:
482                 movzx ecx,al            ; ECX <- sectors transferred
483                 shl ax,SECTOR_SHIFT     ; Convert sectors in AL to bytes in AX
484                 pop bx
485                 add bx,ax
486                 pop bp
487                 pop eax
488                 pop edx
489                 add eax,ecx
490                 sub bp,cx
491                 jnz .loop
492                 ret
493
494 .error:
495                 dec bp
496                 jnz .retry
497
498                 xchg ax,bp              ; Sectors transferred <- 0
499                 shr word [MaxTransfer],1
500                 jnz .resume
501                 ; Fall through to disk_error
502
503 ;
504 ; kaboom: write a message and bail out.
505 ;
506 disk_error:
507 kaboom:
508                 xor si,si
509                 mov ss,si
510                 mov sp,StackBuf-4       ; Reset stack
511                 mov ds,si               ; Reset data segment
512                 pop dword [fdctab]      ; Restore FDC table
513 .patch:                                 ; When we have full code, intercept here
514                 mov si,bailmsg
515
516                 ; Write error message, this assumes screen page 0
517 .loop:          lodsb
518                 and al,al
519                 jz .done
520                 mov ah,0Eh              ; Write to screen as TTY
521                 mov bx,0007h            ; Attribute
522                 int 10h
523                 jmp short .loop
524 .done:
525                 cbw                     ; AH <- 0
526 .again:         int 16h                 ; Wait for keypress
527                                         ; NB: replaced by int 18h if
528                                         ; chosen at install time..
529                 int 19h                 ; And try once more to boot...
530 .norge:         jmp short .norge        ; If int 19h returned; this is the end
531
532 ;
533 ; Truncate BP to MaxTransfer
534 ;
535 maxtrans:
536                 cmp bp,[MaxTransfer]
537                 jna .ok
538                 mov bp,[MaxTransfer]
539 .ok:            ret
540
541 ;
542 ; Error message on failure
543 ;
544 bailmsg:        db 'Boot error', 0Dh, 0Ah, 0
545
546                 ; This fails if the boot sector overflows
547                 zb 1F8h-($-$$)
548
549 FirstSector     dd 0xDEADBEEF                   ; Location of sector 1
550 MaxTransfer     dw 0x007F                       ; Max transfer size
551
552 ; This field will be filled in 0xAA55 by the installer, but we abuse it
553 ; to house a pointer to the INT 16h instruction at
554 ; kaboom.again, which gets patched to INT 18h in RAID mode.
555 bootsignature   dw kaboom.again-bootsec
556
557 ;
558 ; ===========================================================================
559 ;  End of boot sector
560 ; ===========================================================================
561 ;  Start of LDLINUX.SYS
562 ; ===========================================================================
563
564 ldlinux_sys:
565
566 syslinux_banner db 0Dh, 0Ah
567                 db 'EXTLINUX '
568                 db version_str, ' ', date, ' ', 0
569                 db 0Dh, 0Ah, 1Ah        ; EOF if we "type" this in DOS
570
571                 align 8, db 0
572 ldlinux_magic   dd LDLINUX_MAGIC
573                 dd LDLINUX_MAGIC^HEXDATE
574
575 ;
576 ; This area is patched by the installer.  It is found by looking for
577 ; LDLINUX_MAGIC, plus 8 bytes.
578 ;
579 patch_area:
580 LDLDwords       dw 0            ; Total dwords starting at ldlinux_sys,
581                                 ; not including ADVs
582 LDLSectors      dw 0            ; Number of sectors, not including
583                                 ; bootsec & this sec, but including the two ADVs
584 CheckSum        dd 0            ; Checksum starting at ldlinux_sys
585                                 ; value = LDLINUX_MAGIC - [sum of dwords]
586 CurrentDir      dd 2            ; "Current" directory inode number
587
588 ; Space for up to 64 sectors, the theoretical maximum
589 SectorPtrs      times 64 dd 0
590
591 ldlinux_ent:
592 ;
593 ; Note that some BIOSes are buggy and run the boot sector at 07C0:0000
594 ; instead of 0000:7C00 and the like.  We don't want to add anything
595 ; more to the boot sector, so it is written to not assume a fixed
596 ; value in CS, but we don't want to deal with that anymore from now
597 ; on.
598 ;
599                 jmp 0:.next
600 .next:
601
602 ;
603 ; Tell the user we got this far
604 ;
605                 mov si,syslinux_banner
606                 call writestr
607
608 ;
609 ; Tell the user if we're using EBIOS or CBIOS
610 ;
611 print_bios:
612                 mov si,cbios_name
613                 cmp byte [getlinsec.jmp+1],(getlinsec_ebios-(getlinsec.jmp+2))
614                 jne .cbios
615                 mov si,ebios_name
616 .cbios:
617                 mov [BIOSName],si
618                 call writestr
619
620                 section .bss
621 %define HAVE_BIOSNAME 1
622 BIOSName        resw 1
623
624                 section .text
625 ;
626 ; Now we read the rest of LDLINUX.SYS.  Don't bother loading the first
627 ; sector again, though.
628 ;
629 load_rest:
630                 mov si,SectorPtrs
631                 mov bx,7C00h+2*SECTOR_SIZE      ; Where we start loading
632                 mov cx,[LDLSectors]
633
634 .get_chunk:
635                 jcxz .done
636                 xor bp,bp
637                 lodsd                           ; First sector of this chunk
638
639                 mov edx,eax
640
641 .make_chunk:
642                 inc bp
643                 dec cx
644                 jz .chunk_ready
645                 inc edx                         ; Next linear sector
646                 cmp [si],edx                    ; Does it match
647                 jnz .chunk_ready                ; If not, this is it
648                 add si,4                        ; If so, add sector to chunk
649                 jmp short .make_chunk
650
651 .chunk_ready:
652                 call getlinsecsr
653                 shl bp,SECTOR_SHIFT
654                 add bx,bp
655                 jmp .get_chunk
656
657 .done:
658
659 ;
660 ; All loaded up, verify that we got what we needed.
661 ; Note: the checksum field is embedded in the checksum region, so
662 ; by the time we get to the end it should all cancel out.
663 ;
664 verify_checksum:
665                 mov si,ldlinux_sys
666                 mov cx,[LDLDwords]
667                 mov edx,-LDLINUX_MAGIC
668 .checksum:
669                 lodsd
670                 add edx,eax
671                 loop .checksum
672
673                 and edx,edx                     ; Should be zero
674                 jz all_read                     ; We're cool, go for it!
675
676 ;
677 ; Uh-oh, something went bad...
678 ;
679                 mov si,checksumerr_msg
680                 call writestr
681                 jmp kaboom
682
683 ;
684 ; -----------------------------------------------------------------------------
685 ; Subroutines that have to be in the first sector
686 ; -----------------------------------------------------------------------------
687
688 ;
689 ;
690 ; writestr: write a null-terminated string to the console
691 ;           This assumes we're on page 0.  This is only used for early
692 ;           messages, so it should be OK.
693 ;
694 writestr:
695 .loop:          lodsb
696                 and al,al
697                 jz .return
698                 mov ah,0Eh              ; Write to screen as TTY
699                 mov bx,0007h            ; Attribute
700                 int 10h
701                 jmp short .loop
702 .return:        ret
703
704
705 ; getlinsecsr: save registers, call getlinsec, restore registers
706 ;
707 getlinsecsr:    pushad
708                 call getlinsec
709                 popad
710                 ret
711
712 ;
713 ; Checksum error message
714 ;
715 checksumerr_msg db ' Load error - ', 0  ; Boot failed appended
716
717 ;
718 ; BIOS type string
719 ;
720 cbios_name      db 'CBIOS', 0
721 ebios_name      db 'EBIOS', 0
722
723 ;
724 ; Debug routine
725 ;
726 %ifdef debug
727 safedumpregs:
728                 cmp word [Debug_Magic],0D00Dh
729                 jnz nc_return
730                 jmp dumpregs
731 %endif
732
733 rl_checkpt      equ $                           ; Must be <= 8000h
734
735 rl_checkpt_off  equ ($-$$)
736 %ifndef DEPEND
737 %if rl_checkpt_off > 400h
738 %error "Sector 1 overflow"
739 %endif
740 %endif
741
742 ; ----------------------------------------------------------------------------
743 ;  End of code and data that have to be in the first sector
744 ; ----------------------------------------------------------------------------
745
746 all_read:
747 ;
748 ; Let the user (and programmer!) know we got this far.  This used to be
749 ; in Sector 1, but makes a lot more sense here.
750 ;
751                 mov si,copyright_str
752                 call writestr
753
754 ;
755 ; Insane hack to expand the DOS superblock to dwords
756 ;
757 expand_super:
758                 xor eax,eax
759                 mov si,superblock
760                 mov di,SuperInfo
761                 mov cx,superinfo_size
762 .loop:
763                 lodsw
764                 dec si
765                 stosd                           ; Store expanded word
766                 xor ah,ah
767                 stosd                           ; Store expanded byte
768                 loop .loop
769
770 ;
771 ; Load the real (ext2) superblock; 1024 bytes long at offset 1024
772 ;
773                 mov bx,SuperBlock
774                 mov eax,1024 >> SECTOR_SHIFT
775                 mov bp,ax
776                 call getlinsec
777
778 ;
779 ; Compute some values...
780 ;
781                 xor edx,edx
782                 inc edx
783
784                 ; s_log_block_size = log2(blocksize) - 10
785                 mov cl,[SuperBlock+s_log_block_size]
786                 add cl,10
787                 mov [ClustByteShift],cl
788                 mov eax,edx
789                 shl eax,cl
790                 mov [ClustSize],eax
791
792                 sub cl,SECTOR_SHIFT
793                 mov [ClustShift],cl
794                 shr eax,SECTOR_SHIFT
795                 mov [SecPerClust],eax
796                 dec eax
797                 mov [ClustMask],eax
798
799                 add cl,SECTOR_SHIFT-2           ; 4 bytes/pointer
800                 shl edx,cl
801                 mov [PtrsPerBlock1],edx
802                 shl edx,cl
803                 mov [PtrsPerBlock2],edx
804
805 ;
806 ; Common initialization code
807 ;
808 %include "init.inc"
809 %include "cpuinit.inc"
810
811 ;
812 ; Initialize the metadata cache
813 ;
814                 call initcache
815
816 ;
817 ; Now, everything is "up and running"... patch kaboom for more
818 ; verbosity and using the full screen system
819 ;
820                 ; E9 = JMP NEAR
821                 mov dword [kaboom.patch],0e9h+((kaboom2-(kaboom.patch+3)) << 8)
822
823 ;
824 ; Now we're all set to start with our *real* business.  First load the
825 ; configuration file (if any) and parse it.
826 ;
827 ; In previous versions I avoided using 32-bit registers because of a
828 ; rumour some BIOSes clobbered the upper half of 32-bit registers at
829 ; random.  I figure, though, that if there are any of those still left
830 ; they probably won't be trying to install Linux on them...
831 ;
832 ; The code is still ripe with 16-bitisms, though.  Not worth the hassle
833 ; to take'm out.  In fact, we may want to put them back if we're going
834 ; to boot ELKS at some point.
835 ;
836
837 ;
838 ; Load configuration file
839 ;
840 load_config:
841                 mov si,config_name      ; Save config file name
842                 mov di,ConfigName
843                 call strcpy
844
845                 mov di,ConfigName
846                 call open
847                 jz no_config_file
848
849 ;
850 ; Now we have the config file open.  Parse the config file and
851 ; run the user interface.
852 ;
853 %include "ui.inc"
854
855 ;
856 ; getlinsec_ext: same as getlinsec, except load any sector from the zero
857 ;                block as all zeros; use to load any data derived
858 ;                from an ext2 block pointer, i.e. anything *except the
859 ;                superblock.*
860 ;
861 getonesec_ext:
862                 mov bp,1
863
864 getlinsec_ext:
865                 cmp eax,[SecPerClust]
866                 jae getlinsec                   ; Nothing fancy
867
868                 ; If we get here, at least part of what we want is in the
869                 ; zero block.  Zero one sector at a time and loop.
870                 push eax
871                 push cx
872                 xchg di,bx
873                 xor eax,eax
874                 mov cx,SECTOR_SIZE >> 2
875                 rep stosd
876                 xchg di,bx
877                 pop cx
878                 pop eax
879                 inc eax
880                 dec bp
881                 jnz getlinsec_ext
882                 ret
883
884 ;
885 ; allocate_file: Allocate a file structure
886 ;
887 ;               If successful:
888 ;                 ZF set
889 ;                 BX = file pointer
890 ;               In unsuccessful:
891 ;                 ZF clear
892 ;
893 allocate_file:
894                 TRACER 'a'
895                 push cx
896                 mov bx,Files
897                 mov cx,MAX_OPEN
898 .check:         cmp dword [bx], byte 0
899                 je .found
900                 add bx,open_file_t_size         ; ZF = 0
901                 loop .check
902                 ; ZF = 0 if we fell out of the loop
903 .found:         pop cx
904                 ret
905 ;
906 ; open_inode:
907 ;            Open a file indicated by an inode number in EAX
908 ;
909 ;            NOTE: This file considers finding a zero-length file an
910 ;            error.  This is so we don't have to deal with that special
911 ;            case elsewhere in the program (most loops have the test
912 ;            at the end).
913 ;
914 ;            If successful:
915 ;               ZF clear
916 ;               SI          = file pointer
917 ;               DX:AX = EAX = file length in bytes
918 ;               ThisInode   = the first 128 bytes of the inode
919 ;            If unsuccessful
920 ;               ZF set
921 ;
922 ;            Assumes CS == DS == ES.
923 ;
924 open_inode.allocate_failure:
925                 xor eax,eax
926                 pop bx
927                 pop di
928                 ret
929
930 open_inode:
931                 push di
932                 push bx
933                 call allocate_file
934                 jnz .allocate_failure
935
936                 push cx
937                 push gs
938                 ; First, get the appropriate inode group and index
939                 dec eax                         ; There is no inode 0
940                 xor edx,edx
941                 mov [bx+file_sector],edx
942                 div dword [SuperBlock+s_inodes_per_group]
943                 ; EAX = inode group; EDX = inode within group
944                 push edx
945
946                 ; Now, we need the block group descriptor.
947                 ; To get that, we first need the relevant descriptor block.
948
949                 shl eax, ext2_group_desc_lg2size ; Get byte offset in desc table
950                 xor edx,edx
951                 div dword [ClustSize]
952                 ; eax = block #, edx = offset in block
953                 add eax,dword [SuperBlock+s_first_data_block]
954                 inc eax                         ; s_first_data_block+1
955                 mov cl,[ClustShift]
956                 shl eax,cl
957                 push edx
958                 shr edx,SECTOR_SHIFT
959                 add eax,edx
960                 pop edx
961                 and dx,SECTOR_SIZE-1
962                 call getcachesector             ; Get the group descriptor
963                 add si,dx
964                 mov esi,[gs:si+bg_inode_table]  ; Get inode table block #
965                 pop eax                         ; Get inode within group
966                 movzx edx, word [SuperBlock+s_inode_size]
967                 mul edx
968                 ; edx:eax = byte offset in inode table
969                 div dword [ClustSize]
970                 ; eax = block # versus inode table, edx = offset in block
971                 add eax,esi
972                 shl eax,cl                      ; Turn into sector
973                 push dx
974                 shr edx,SECTOR_SHIFT
975                 add eax,edx
976                 mov [bx+file_in_sec],eax
977                 pop dx
978                 and dx,SECTOR_SIZE-1
979                 mov [bx+file_in_off],dx
980
981                 call getcachesector
982                 add si,dx
983                 mov cx,EXT2_GOOD_OLD_INODE_SIZE >> 2
984                 mov di,ThisInode
985                 gs rep movsd
986
987                 mov ax,[ThisInode+i_mode]
988                 mov [bx+file_mode],ax
989                 mov eax,[ThisInode+i_size]
990                 push eax
991                 add eax,SECTOR_SIZE-1
992                 shr eax,SECTOR_SHIFT
993                 mov [bx+file_left],eax
994                 pop eax
995                 mov si,bx
996                 mov edx,eax
997                 shr edx,16                      ; 16-bitism, sigh
998                 and eax,eax                     ; ZF clear unless zero-length file
999                 pop gs
1000                 pop cx
1001                 pop bx
1002                 pop di
1003                 ret
1004
1005                 section .bss
1006                 alignb 4
1007 ThisInode       resb EXT2_GOOD_OLD_INODE_SIZE   ; The most recently opened inode
1008
1009                 section .text
1010 ;
1011 ; close_file:
1012 ;            Deallocates a file structure (pointer in SI)
1013 ;            Assumes CS == DS.
1014 ;
1015 close_file:
1016                 and si,si
1017                 jz .closed
1018                 mov dword [si],0                ; First dword == file_left
1019 .closed:        ret
1020
1021 ;
1022 ; searchdir:
1023 ;            Search the root directory for a pre-mangled filename in DS:DI.
1024 ;
1025 ;            NOTE: This file considers finding a zero-length file an
1026 ;            error.  This is so we don't have to deal with that special
1027 ;            case elsewhere in the program (most loops have the test
1028 ;            at the end).
1029 ;
1030 ;            If successful:
1031 ;               ZF clear
1032 ;               SI          = file pointer
1033 ;               DX:AX = EAX = file length in bytes
1034 ;            If unsuccessful
1035 ;               ZF set
1036 ;
1037 ;            Assumes CS == DS == ES; *** IS THIS CORRECT ***?
1038 ;
1039 searchdir:
1040                 push bx
1041                 push cx
1042                 push bp
1043                 mov byte [SymlinkCtr],MAX_SYMLINKS
1044
1045                 mov eax,[CurrentDir]
1046 .begin_path:
1047 .leadingslash:
1048                 cmp byte [di],'/'       ; Absolute filename?
1049                 jne .gotdir
1050                 mov eax,EXT2_ROOT_INO
1051                 inc di                  ; Skip slash
1052                 jmp .leadingslash
1053 .gotdir:
1054
1055                 ; At this point, EAX contains the directory inode,
1056                 ; and DS:DI contains a pathname tail.
1057 .open:
1058                 push eax                ; Save directory inode
1059
1060                 call open_inode
1061                 jz .missing             ; If error, done
1062
1063                 mov cx,[si+file_mode]
1064                 shr cx,S_IFSHIFT        ; Get file type
1065
1066                 cmp cx,T_IFDIR
1067                 je .directory
1068
1069                 add sp,4                ; Drop directory inode
1070
1071                 cmp cx,T_IFREG
1072                 je .file
1073                 cmp cx,T_IFLNK
1074                 je .symlink
1075
1076                 ; Otherwise, something bad...
1077 .err:
1078                 call close_file
1079 .err_noclose:
1080                 xor eax,eax
1081                 xor si,si
1082                 cwd                     ; DX <- 0
1083
1084 .done:
1085                 and eax,eax             ; Set/clear ZF
1086                 pop bp
1087                 pop cx
1088                 pop bx
1089                 ret
1090
1091 .missing:
1092                 add sp,4                ; Drop directory inode
1093                 jmp .done
1094
1095                 ;
1096                 ; It's a file.
1097                 ;
1098 .file:
1099                 cmp byte [di],0         ; End of path?
1100                 je .done                ; If so, done
1101                 jmp .err                ; Otherwise, error
1102
1103                 ;
1104                 ; It's a directory.
1105                 ;
1106 .directory:
1107                 pop dword [ThisDir]     ; Remember what directory we're searching
1108
1109                 cmp byte [di],0         ; More path?
1110                 je .err                 ; If not, bad
1111
1112 .skipslash:                             ; Skip redundant slashes
1113                 cmp byte [di],'/'
1114                 jne .readdir
1115                 inc di
1116                 jmp .skipslash
1117
1118 .readdir:
1119                 mov cx,[SecPerClust]
1120                 push cx
1121                 shl cx,SECTOR_SHIFT
1122                 mov bx,trackbuf
1123                 add cx,bx
1124                 mov [EndBlock],cx
1125                 pop cx
1126                 push bx
1127                 call getfssec
1128                 pop bx
1129                 pushf                   ; Save EOF flag
1130                 push si                 ; Save filesystem pointer
1131 .getent:
1132                 cmp bx,[EndBlock]
1133                 jae .endblock
1134
1135                 push di
1136                 cmp dword [bx+d_inode],0        ; Zero inode = void entry
1137                 je .nope
1138
1139                 movzx cx,byte [bx+d_name_len]
1140                 lea si,[bx+d_name]
1141                 repe cmpsb
1142                 je .maybe
1143 .nope:
1144                 pop di
1145                 add bx,[bx+d_rec_len]
1146                 jmp .getent
1147
1148 .endblock:
1149                 pop si
1150                 popf
1151                 jnc .readdir            ; There is more
1152                 jmp .err                ; Otherwise badness...
1153
1154 .maybe:
1155                 mov eax,[bx+d_inode]
1156
1157                 ; Does this match the end of the requested filename?
1158                 cmp byte [di],0
1159                 je .finish
1160                 cmp byte [di],'/'
1161                 jne .nope
1162
1163                 ; We found something; now we need to open the file
1164 .finish:
1165                 pop bx                  ; Adjust stack (di)
1166                 pop si
1167                 call close_file         ; Close directory
1168                 pop bx                  ; Adjust stack (flags)
1169                 jmp .open
1170
1171                 ;
1172                 ; It's a symlink.  We have to determine if it's a fast symlink
1173                 ; (data stored in the inode) or not (data stored as a regular
1174                 ; file.)  Either which way, we start from the directory
1175                 ; which we just visited if relative, or from the root directory
1176                 ; if absolute, and append any remaining part of the path.
1177                 ;
1178 .symlink:
1179                 dec byte [SymlinkCtr]
1180                 jz .err                 ; Too many symlink references
1181
1182                 cmp eax,SYMLINK_SECTORS*SECTOR_SIZE
1183                 jae .err                ; Symlink too long
1184
1185                 ; Computation for fast symlink, as defined by ext2/3 spec
1186                 xor ecx,ecx
1187                 cmp [ThisInode+i_file_acl],ecx
1188                 setne cl                ; ECX <- i_file_acl ? 1 : 0
1189                 cmp [ThisInode+i_blocks],ecx
1190                 jne .slow_symlink
1191
1192                 ; It's a fast symlink
1193 .fast_symlink:
1194                 call close_file         ; We've got all we need
1195                 mov si,ThisInode+i_block
1196
1197                 push di
1198                 mov di,SymlinkTmpBuf
1199                 mov ecx,eax
1200                 rep movsb
1201                 pop si
1202
1203 .symlink_finish:
1204                 cmp byte [si],0
1205                 je .no_slash
1206                 mov al,'/'
1207                 stosb
1208 .no_slash:
1209                 mov bp,SymlinkTmpBufEnd
1210                 call strecpy
1211                 jc .err_noclose         ; Buffer overflow
1212
1213                 ; Now copy it to the "real" buffer; we need to have
1214                 ; two buffers so we avoid overwriting the tail on the
1215                 ; next copy
1216                 mov si,SymlinkTmpBuf
1217                 mov di,SymlinkBuf
1218                 push di
1219                 call strcpy
1220                 pop di
1221                 mov eax,[ThisDir]       ; Resume searching previous directory
1222                 jmp .begin_path
1223
1224 .slow_symlink:
1225                 mov bx,SymlinkTmpBuf
1226                 mov cx,SYMLINK_SECTORS
1227                 call getfssec
1228                 ; The EOF closed the file
1229
1230                 mov si,di               ; SI = filename tail
1231                 mov di,SymlinkTmpBuf
1232                 add di,ax               ; AX = file length
1233                 jmp .symlink_finish
1234
1235
1236                 section .bss
1237                 alignb  4
1238 SymlinkBuf      resb    SYMLINK_SECTORS*SECTOR_SIZE+64
1239 SymlinkTmpBuf    equ    trackbuf
1240 SymlinkTmpBufEnd equ    trackbuf+SYMLINK_SECTORS*SECTOR_SIZE+64
1241 ThisDir         resd    1
1242 EndBlock        resw    1
1243 SymlinkCtr      resb    1
1244
1245                 section .text
1246 ;
1247 ; mangle_name: Mangle a filename pointed to by DS:SI into a buffer pointed
1248 ;              to by ES:DI; ends on encountering any whitespace.
1249 ;              DI is preserved.
1250 ;
1251 ;              This verifies that a filename is < FILENAME_MAX characters,
1252 ;              doesn't contain whitespace, zero-pads the output buffer,
1253 ;              and removes redundant slashes,
1254 ;              so "repe cmpsb" can do a compare, and the
1255 ;              path-searching routine gets a bit of an easier job.
1256 ;
1257 ;              FIX: we may want to support \-escapes here (and this would
1258 ;              be the place.)
1259 ;
1260 mangle_name:
1261                 push di
1262                 push bx
1263                 xor ax,ax
1264                 mov cx,FILENAME_MAX-1
1265                 mov bx,di
1266
1267 .mn_loop:
1268                 lodsb
1269                 cmp al,' '                      ; If control or space, end
1270                 jna .mn_end
1271                 cmp al,ah                       ; Repeated slash?
1272                 je .mn_skip
1273                 xor ah,ah
1274                 cmp al,'/'
1275                 jne .mn_ok
1276                 mov ah,al
1277 .mn_ok          stosb
1278 .mn_skip:       loop .mn_loop
1279 .mn_end:
1280                 cmp bx,di                       ; At the beginning of the buffer?
1281                 jbe .mn_zero
1282                 cmp byte [di-1],'/'             ; Terminal slash?
1283                 jne .mn_zero
1284 .mn_kill:       dec di                          ; If so, remove it
1285                 inc cx
1286                 jmp short .mn_end
1287 .mn_zero:
1288                 inc cx                          ; At least one null byte
1289                 xor ax,ax                       ; Zero-fill name
1290                 rep stosb
1291                 pop bx
1292                 pop di
1293                 ret                             ; Done
1294
1295 ;
1296 ; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
1297 ;                filename to the conventional representation.  This is needed
1298 ;                for the BOOT_IMAGE= parameter for the kernel.
1299 ;
1300 ;                DS:SI -> input mangled file name
1301 ;                ES:DI -> output buffer
1302 ;
1303 ;                On return, DI points to the first byte after the output name,
1304 ;                which is set to a null byte.
1305 ;
1306 unmangle_name:  call strcpy
1307                 dec di                          ; Point to final null byte
1308                 ret
1309
1310 ;
1311 ;
1312 ; kaboom2: once everything is loaded, replace the part of kaboom
1313 ;          starting with "kaboom.patch" with this part
1314
1315 kaboom2:
1316                 mov si,err_bootfailed
1317                 call cwritestr
1318                 cmp byte [kaboom.again+1],18h   ; INT 18h version?
1319                 je .int18
1320                 call getchar
1321                 call vgaclearmode
1322                 int 19h                 ; And try once more to boot...
1323 .norge:         jmp short .norge        ; If int 19h returned; this is the end
1324 .int18:
1325                 call vgaclearmode
1326                 int 18h
1327 .noreg:         jmp short .noreg        ; Nynorsk
1328
1329
1330 ;
1331 ; linsector:    Convert a linear sector index in a file to a linear sector number
1332 ;       EAX     -> linear sector number
1333 ;       DS:SI   -> open_file_t
1334 ;
1335 ;               Returns next sector number in EAX; CF on EOF (not an error!)
1336 ;
1337 linsector:
1338                 push gs
1339                 push ebx
1340                 push esi
1341                 push edi
1342                 push ecx
1343                 push edx
1344                 push ebp
1345
1346                 push eax                ; Save sector index
1347                 mov cl,[ClustShift]
1348                 shr eax,cl              ; Convert to block number
1349                 push eax
1350                 mov eax,[si+file_in_sec]
1351                 mov bx,si
1352                 call getcachesector     ; Get inode
1353                 add si,[bx+file_in_off] ; Get *our* inode
1354                 pop eax
1355                 lea ebx,[i_block+4*eax]
1356                 cmp eax,EXT2_NDIR_BLOCKS
1357                 jb .direct
1358                 mov ebx,i_block+4*EXT2_IND_BLOCK
1359                 sub eax,EXT2_NDIR_BLOCKS
1360                 mov ebp,[PtrsPerBlock1]
1361                 cmp eax,ebp
1362                 jb .ind1
1363                 mov ebx,i_block+4*EXT2_DIND_BLOCK
1364                 sub eax,ebp
1365                 mov ebp,[PtrsPerBlock2]
1366                 cmp eax,ebp
1367                 jb .ind2
1368                 mov ebx,i_block+4*EXT2_TIND_BLOCK
1369                 sub eax,ebp
1370
1371 .ind3:
1372                 ; Triple indirect; eax contains the block no
1373                 ; with respect to the start of the tind area;
1374                 ; ebx contains the pointer to the tind block.
1375                 xor edx,edx
1376                 div dword [PtrsPerBlock2]
1377                 ; EAX = which dind block, EDX = pointer within dind block
1378                 push ax
1379                 shr eax,SECTOR_SHIFT-2
1380                 mov ebp,[gs:si+bx]
1381                 shl ebp,cl
1382                 add eax,ebp
1383                 call getcachesector
1384                 pop bx
1385                 and bx,(SECTOR_SIZE >> 2)-1
1386                 shl bx,2
1387                 mov eax,edx             ; The ind2 code wants the remainder...
1388
1389 .ind2:
1390                 ; Double indirect; eax contains the block no
1391                 ; with respect to the start of the dind area;
1392                 ; ebx contains the pointer to the dind block.
1393                 xor edx,edx
1394                 div dword [PtrsPerBlock1]
1395                 ; EAX = which ind block, EDX = pointer within ind block
1396                 push ax
1397                 shr eax,SECTOR_SHIFT-2
1398                 mov ebp,[gs:si+bx]
1399                 shl ebp,cl
1400                 add eax,ebp
1401                 call getcachesector
1402                 pop bx
1403                 and bx,(SECTOR_SIZE >> 2)-1
1404                 shl bx,2
1405                 mov eax,edx             ; The int1 code wants the remainder...
1406
1407 .ind1:
1408                 ; Single indirect; eax contains the block no
1409                 ; with respect to the start of the ind area;
1410                 ; ebx contains the pointer to the ind block.
1411                 push ax
1412                 shr eax,SECTOR_SHIFT-2
1413                 mov ebp,[gs:si+bx]
1414                 shl ebp,cl
1415                 add eax,ebp
1416                 call getcachesector
1417                 pop bx
1418                 and bx,(SECTOR_SIZE >> 2)-1
1419                 shl bx,2
1420
1421 .direct:
1422                 mov ebx,[gs:bx+si]      ; Get the pointer
1423
1424                 pop eax                 ; Get the sector index again
1425                 shl ebx,cl              ; Convert block number to sector
1426                 and eax,[ClustMask]     ; Add offset within block
1427                 add eax,ebx
1428
1429                 pop ebp
1430                 pop edx
1431                 pop ecx
1432                 pop edi
1433                 pop esi
1434                 pop ebx
1435                 pop gs
1436                 ret
1437
1438 ;
1439 ; getfssec: Get multiple sectors from a file
1440 ;
1441 ;       Same as above, except SI is a pointer to a open_file_t
1442 ;
1443 ;       ES:BX   -> Buffer
1444 ;       DS:SI   -> Pointer to open_file_t
1445 ;       CX      -> Sector count (0FFFFh = until end of file)
1446 ;                  Must not exceed the ES segment
1447 ;       Returns CF=1 on EOF (not necessarily error)
1448 ;       All arguments are advanced to reflect data read.
1449 ;
1450 getfssec:
1451                 push ebp
1452                 push eax
1453                 push edx
1454                 push edi
1455
1456                 movzx ecx,cx
1457                 cmp ecx,[si]                    ; Number of sectors left
1458                 jbe .lenok
1459                 mov cx,[si]
1460 .lenok:
1461 .getfragment:
1462                 mov eax,[si+file_sector]        ; Current start index
1463                 mov edi,eax
1464                 call linsector
1465                 push eax                        ; Fragment start sector
1466                 mov edx,eax
1467                 xor ebp,ebp                     ; Fragment sector count
1468 .getseccnt:
1469                 inc bp
1470                 dec cx
1471                 jz .do_read
1472                 xor eax,eax
1473                 mov ax,es
1474                 shl ax,4
1475                 add ax,bx                       ; Now DI = how far into 64K block we are
1476                 not ax                          ; Bytes left in 64K block
1477                 inc eax
1478                 shr eax,SECTOR_SHIFT            ; Sectors left in 64K block
1479                 cmp bp,ax
1480                 jnb .do_read                    ; Unless there is at least 1 more sector room...
1481                 inc edi                         ; Sector index
1482                 inc edx                         ; Linearly next sector
1483                 mov eax,edi
1484                 call linsector
1485                 ; jc .do_read
1486                 cmp edx,eax
1487                 je .getseccnt
1488 .do_read:
1489                 pop eax                         ; Linear start sector
1490                 pushad
1491                 call getlinsec_ext
1492                 popad
1493                 push bp
1494                 shl bp,9
1495                 add bx,bp                       ; Adjust buffer pointer
1496                 pop bp
1497                 add [si+file_sector],ebp        ; Next sector index
1498                 sub [si],ebp                    ; Sectors consumed
1499                 jcxz .done
1500                 jnz .getfragment
1501                 ; Fall through
1502 .done:
1503                 cmp dword [si],1                ; Did we run out of file?
1504                 ; CF set if [SI] < 1, i.e. == 0
1505                 pop edi
1506                 pop edx
1507                 pop eax
1508                 pop ebp
1509                 ret
1510
1511 ; -----------------------------------------------------------------------------
1512 ;  Common modules
1513 ; -----------------------------------------------------------------------------
1514
1515 %include "getc.inc"             ; getc et al
1516 %include "conio.inc"            ; Console I/O
1517 %include "plaincon.inc"         ; writechr
1518 %include "writestr.inc"         ; String output
1519 %include "configinit.inc"       ; Initialize configuration
1520 %include "parseconfig.inc"      ; High-level config file handling
1521 %include "parsecmd.inc"         ; Low-level config file handling
1522 %include "bcopy32.inc"          ; 32-bit bcopy
1523 %include "loadhigh.inc"         ; Load a file into high memory
1524 %include "font.inc"             ; VGA font stuff
1525 %include "graphics.inc"         ; VGA graphics
1526 %include "highmem.inc"          ; High memory sizing
1527 %include "strcpy.inc"           ; strcpy()
1528 %include "strecpy.inc"          ; strcpy with end pointer check
1529 %include "cache.inc"            ; Metadata disk cache
1530 %include "adv.inc"              ; Auxillary Data Vector
1531 %include "localboot.inc"        ; Disk-based local boot
1532
1533 ; -----------------------------------------------------------------------------
1534 ;  Begin data section
1535 ; -----------------------------------------------------------------------------
1536
1537                 section .data
1538 copyright_str   db ' Copyright (C) 1994-', year, ' H. Peter Anvin'
1539                 db CR, LF, 0
1540 err_bootfailed  db CR, LF, 'Boot failed: please change disks and press '
1541                 db 'a key to continue.', CR, LF, 0
1542 config_name     db 'extlinux.conf',0            ; Unmangled form
1543
1544 ;
1545 ; Command line options we'd like to take a look at
1546 ;
1547 ; mem= and vga= are handled as normal 32-bit integer values
1548 initrd_cmd      db 'initrd='
1549 initrd_cmd_len  equ 7
1550
1551 ;
1552 ; Config file keyword table
1553 ;
1554 %include "keywords.inc"
1555
1556 ;
1557 ; Extensions to search for (in *forward* order).
1558 ;
1559                 align 4, db 0
1560 exten_table:    db '.cbt'               ; COMBOOT (specific)
1561                 db '.img'               ; Disk image
1562                 db '.bs', 0             ; Boot sector
1563                 db '.com'               ; COMBOOT (same as DOS)
1564                 db '.c32'               ; COM32
1565 exten_table_end:
1566                 dd 0, 0                 ; Need 8 null bytes here
1567
1568 ;
1569 ; Misc initialized (data) variables
1570 ;
1571 %ifdef debug                            ; This code for debugging only
1572 debug_magic     dw 0D00Dh               ; Debug code sentinel
1573 %endif
1574
1575                 alignb 4, db 0
1576 BufSafe         dw trackbufsize/SECTOR_SIZE     ; Clusters we can load into trackbuf
1577 BufSafeBytes    dw trackbufsize         ; = how many bytes?
1578 %ifndef DEPEND
1579 %if ( trackbufsize % SECTOR_SIZE ) != 0
1580 %error trackbufsize must be a multiple of SECTOR_SIZE
1581 %endif
1582 %endif