Merge commit 'origin/master' into nolen
[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_bytesleft  resd 1                  ; Number of bytes 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 ;               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                 mov [bx+file_bytesleft],eax
991                 mov si,bx
992                 and eax,eax                     ; ZF clear unless zero-length file
993                 pop gs
994                 pop cx
995                 pop bx
996                 pop di
997                 ret
998
999                 section .bss
1000                 alignb 4
1001 ThisInode       resb EXT2_GOOD_OLD_INODE_SIZE   ; The most recently opened inode
1002
1003                 section .text
1004 ;
1005 ; close_file:
1006 ;            Deallocates a file structure (pointer in SI)
1007 ;            Assumes CS == DS.
1008 ;
1009 close_file:
1010                 and si,si
1011                 jz .closed
1012                 mov dword [si],0                ; First dword == file_bytesleft
1013                 xor si,si
1014 .closed:        ret
1015
1016 ;
1017 ; searchdir:
1018 ;            Search the root directory for a pre-mangled filename in DS:DI.
1019 ;
1020 ;            NOTE: This file considers finding a zero-length file an
1021 ;            error.  This is so we don't have to deal with that special
1022 ;            case elsewhere in the program (most loops have the test
1023 ;            at the end).
1024 ;
1025 ;            If successful:
1026 ;               ZF clear
1027 ;               SI          = file pointer
1028 ;               DX:AX = EAX = file length in bytes
1029 ;            If unsuccessful
1030 ;               ZF set
1031 ;
1032 ;            Assumes CS == DS == ES; *** IS THIS CORRECT ***?
1033 ;
1034 searchdir:
1035                 push bx
1036                 push cx
1037                 push bp
1038                 mov byte [SymlinkCtr],MAX_SYMLINKS
1039
1040                 mov eax,[CurrentDir]
1041 .begin_path:
1042 .leadingslash:
1043                 cmp byte [di],'/'       ; Absolute filename?
1044                 jne .gotdir
1045                 mov eax,EXT2_ROOT_INO
1046                 inc di                  ; Skip slash
1047                 jmp .leadingslash
1048 .gotdir:
1049
1050                 ; At this point, EAX contains the directory inode,
1051                 ; and DS:DI contains a pathname tail.
1052 .open:
1053                 push eax                ; Save directory inode
1054
1055                 call open_inode
1056                 jz .missing             ; If error, done
1057
1058                 mov cx,[si+file_mode]
1059                 shr cx,S_IFSHIFT        ; Get file type
1060
1061                 cmp cx,T_IFDIR
1062                 je .directory
1063
1064                 add sp,4                ; Drop directory inode
1065
1066                 cmp cx,T_IFREG
1067                 je .file
1068                 cmp cx,T_IFLNK
1069                 je .symlink
1070
1071                 ; Otherwise, something bad...
1072 .err:
1073                 call close_file
1074 .err_noclose:
1075                 xor eax,eax
1076                 xor si,si
1077                 cwd                     ; DX <- 0
1078
1079 .done:
1080                 and eax,eax             ; Set/clear ZF
1081                 pop bp
1082                 pop cx
1083                 pop bx
1084                 ret
1085
1086 .missing:
1087                 add sp,4                ; Drop directory inode
1088                 jmp .done
1089
1090                 ;
1091                 ; It's a file.
1092                 ;
1093 .file:
1094                 cmp byte [di],0         ; End of path?
1095                 je .done                ; If so, done
1096                 jmp .err                ; Otherwise, error
1097
1098                 ;
1099                 ; It's a directory.
1100                 ;
1101 .directory:
1102                 pop dword [ThisDir]     ; Remember what directory we're searching
1103
1104                 cmp byte [di],0         ; More path?
1105                 je .err                 ; If not, bad
1106
1107 .skipslash:                             ; Skip redundant slashes
1108                 cmp byte [di],'/'
1109                 jne .readdir
1110                 inc di
1111                 jmp .skipslash
1112
1113 .readdir:
1114                 mov bx,trackbuf
1115                 push bx
1116                 mov cx,[SecPerClust]
1117                 call getfssec
1118                 pop bx
1119                 pushf                   ; Save EOF flag
1120                 push si                 ; Save filesystem pointer
1121 .getent:
1122                 cmp dword [bx+d_inode],0
1123                 je .endblock
1124
1125                 push di
1126                 movzx cx,byte [bx+d_name_len]
1127                 lea si,[bx+d_name]
1128                 repe cmpsb
1129                 je .maybe
1130 .nope:
1131                 pop di
1132
1133                 add bx,[bx+d_rec_len]
1134                 jmp .getent
1135
1136 .endblock:
1137                 pop si
1138                 popf
1139                 jnc .readdir            ; There is more
1140                 jmp .err                ; Otherwise badness...
1141
1142 .maybe:
1143                 mov eax,[bx+d_inode]
1144
1145                 ; Does this match the end of the requested filename?
1146                 cmp byte [di],0
1147                 je .finish
1148                 cmp byte [di],'/'
1149                 jne .nope
1150
1151                 ; We found something; now we need to open the file
1152 .finish:
1153                 pop bx                  ; Adjust stack (di)
1154                 pop si
1155                 call close_file         ; Close directory
1156                 pop bx                  ; Adjust stack (flags)
1157                 jmp .open
1158
1159                 ;
1160                 ; It's a symlink.  We have to determine if it's a fast symlink
1161                 ; (data stored in the inode) or not (data stored as a regular
1162                 ; file.)  Either which way, we start from the directory
1163                 ; which we just visited if relative, or from the root directory
1164                 ; if absolute, and append any remaining part of the path.
1165                 ;
1166 .symlink:
1167                 dec byte [SymlinkCtr]
1168                 jz .err                 ; Too many symlink references
1169
1170                 cmp eax,SYMLINK_SECTORS*SECTOR_SIZE
1171                 jae .err                ; Symlink too long
1172
1173                 ; Computation for fast symlink, as defined by ext2/3 spec
1174                 xor ecx,ecx
1175                 cmp [ThisInode+i_file_acl],ecx
1176                 setne cl                ; ECX <- i_file_acl ? 1 : 0
1177                 cmp [ThisInode+i_blocks],ecx
1178                 jne .slow_symlink
1179
1180                 ; It's a fast symlink
1181 .fast_symlink:
1182                 call close_file         ; We've got all we need
1183                 mov si,ThisInode+i_block
1184
1185                 push di
1186                 mov di,SymlinkTmpBuf
1187                 mov ecx,eax
1188                 rep movsb
1189                 pop si
1190
1191 .symlink_finish:
1192                 cmp byte [si],0
1193                 je .no_slash
1194                 mov al,'/'
1195                 stosb
1196 .no_slash:
1197                 mov bp,SymlinkTmpBufEnd
1198                 call strecpy
1199                 jc .err_noclose         ; Buffer overflow
1200
1201                 ; Now copy it to the "real" buffer; we need to have
1202                 ; two buffers so we avoid overwriting the tail on the
1203                 ; next copy
1204                 mov si,SymlinkTmpBuf
1205                 mov di,SymlinkBuf
1206                 push di
1207                 call strcpy
1208                 pop di
1209                 mov eax,[ThisDir]       ; Resume searching previous directory
1210                 jmp .begin_path
1211
1212 .slow_symlink:
1213                 mov bx,SymlinkTmpBuf
1214                 mov cx,SYMLINK_SECTORS
1215                 call getfssec
1216                 ; The EOF closed the file
1217
1218                 mov si,di               ; SI = filename tail
1219                 mov di,SymlinkTmpBuf
1220                 add di,ax               ; AX = file length
1221                 jmp .symlink_finish
1222
1223
1224                 section .bss
1225                 alignb  4
1226 SymlinkBuf      resb    SYMLINK_SECTORS*SECTOR_SIZE+64
1227 SymlinkTmpBuf    equ    trackbuf
1228 SymlinkTmpBufEnd equ    trackbuf+SYMLINK_SECTORS*SECTOR_SIZE+64
1229 ThisDir         resd    1
1230 SymlinkCtr      resb    1
1231
1232                 section .text
1233 ;
1234 ; mangle_name: Mangle a filename pointed to by DS:SI into a buffer pointed
1235 ;              to by ES:DI; ends on encountering any whitespace.
1236 ;              DI is preserved.
1237 ;
1238 ;              This verifies that a filename is < FILENAME_MAX characters,
1239 ;              doesn't contain whitespace, zero-pads the output buffer,
1240 ;              and removes redundant slashes,
1241 ;              so "repe cmpsb" can do a compare, and the
1242 ;              path-searching routine gets a bit of an easier job.
1243 ;
1244 ;              FIX: we may want to support \-escapes here (and this would
1245 ;              be the place.)
1246 ;
1247 mangle_name:
1248                 push di
1249                 push bx
1250                 xor ax,ax
1251                 mov cx,FILENAME_MAX-1
1252                 mov bx,di
1253
1254 .mn_loop:
1255                 lodsb
1256                 cmp al,' '                      ; If control or space, end
1257                 jna .mn_end
1258                 cmp al,ah                       ; Repeated slash?
1259                 je .mn_skip
1260                 xor ah,ah
1261                 cmp al,'/'
1262                 jne .mn_ok
1263                 mov ah,al
1264 .mn_ok          stosb
1265 .mn_skip:       loop .mn_loop
1266 .mn_end:
1267                 cmp bx,di                       ; At the beginning of the buffer?
1268                 jbe .mn_zero
1269                 cmp byte [di-1],'/'             ; Terminal slash?
1270                 jne .mn_zero
1271 .mn_kill:       dec di                          ; If so, remove it
1272                 inc cx
1273                 jmp short .mn_end
1274 .mn_zero:
1275                 inc cx                          ; At least one null byte
1276                 xor ax,ax                       ; Zero-fill name
1277                 rep stosb
1278                 pop bx
1279                 pop di
1280                 ret                             ; Done
1281
1282 ;
1283 ; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
1284 ;                filename to the conventional representation.  This is needed
1285 ;                for the BOOT_IMAGE= parameter for the kernel.
1286 ;
1287 ;                DS:SI -> input mangled file name
1288 ;                ES:DI -> output buffer
1289 ;
1290 ;                On return, DI points to the first byte after the output name,
1291 ;                which is set to a null byte.
1292 ;
1293 unmangle_name:  call strcpy
1294                 dec di                          ; Point to final null byte
1295                 ret
1296
1297 ;
1298 ;
1299 ; kaboom2: once everything is loaded, replace the part of kaboom
1300 ;          starting with "kaboom.patch" with this part
1301
1302 kaboom2:
1303                 mov si,err_bootfailed
1304                 call cwritestr
1305                 cmp byte [kaboom.again+1],18h   ; INT 18h version?
1306                 je .int18
1307                 call getchar
1308                 call vgaclearmode
1309                 int 19h                 ; And try once more to boot...
1310 .norge:         jmp short .norge        ; If int 19h returned; this is the end
1311 .int18:
1312                 call vgaclearmode
1313                 int 18h
1314 .noreg:         jmp short .noreg        ; Nynorsk
1315
1316
1317 ;
1318 ; linsector:    Convert a linear sector index in a file to a linear sector number
1319 ;       EAX     -> linear sector number
1320 ;       DS:SI   -> open_file_t
1321 ;
1322 ;               Returns next sector number in EAX; CF on EOF (not an error!)
1323 ;
1324 linsector:
1325                 push gs
1326                 push ebx
1327                 push esi
1328                 push edi
1329                 push ecx
1330                 push edx
1331                 push ebp
1332
1333                 push eax                ; Save sector index
1334                 mov cl,[ClustShift]
1335                 shr eax,cl              ; Convert to block number
1336                 push eax
1337                 mov eax,[si+file_in_sec]
1338                 mov bx,si
1339                 call getcachesector     ; Get inode
1340                 add si,[bx+file_in_off] ; Get *our* inode
1341                 pop eax
1342                 lea ebx,[i_block+4*eax]
1343                 cmp eax,EXT2_NDIR_BLOCKS
1344                 jb .direct
1345                 mov ebx,i_block+4*EXT2_IND_BLOCK
1346                 sub eax,EXT2_NDIR_BLOCKS
1347                 mov ebp,[PtrsPerBlock1]
1348                 cmp eax,ebp
1349                 jb .ind1
1350                 mov ebx,i_block+4*EXT2_DIND_BLOCK
1351                 sub eax,ebp
1352                 mov ebp,[PtrsPerBlock2]
1353                 cmp eax,ebp
1354                 jb .ind2
1355                 mov ebx,i_block+4*EXT2_TIND_BLOCK
1356                 sub eax,ebp
1357
1358 .ind3:
1359                 ; Triple indirect; eax contains the block no
1360                 ; with respect to the start of the tind area;
1361                 ; ebx contains the pointer to the tind block.
1362                 xor edx,edx
1363                 div dword [PtrsPerBlock2]
1364                 ; EAX = which dind block, EDX = pointer within dind block
1365                 push ax
1366                 shr eax,SECTOR_SHIFT-2
1367                 mov ebp,[gs:si+bx]
1368                 shl ebp,cl
1369                 add eax,ebp
1370                 call getcachesector
1371                 pop bx
1372                 and bx,(SECTOR_SIZE >> 2)-1
1373                 shl bx,2
1374                 mov eax,edx             ; The ind2 code wants the remainder...
1375
1376 .ind2:
1377                 ; Double indirect; eax contains the block no
1378                 ; with respect to the start of the dind area;
1379                 ; ebx contains the pointer to the dind block.
1380                 xor edx,edx
1381                 div dword [PtrsPerBlock1]
1382                 ; EAX = which ind block, EDX = pointer within ind block
1383                 push ax
1384                 shr eax,SECTOR_SHIFT-2
1385                 mov ebp,[gs:si+bx]
1386                 shl ebp,cl
1387                 add eax,ebp
1388                 call getcachesector
1389                 pop bx
1390                 and bx,(SECTOR_SIZE >> 2)-1
1391                 shl bx,2
1392                 mov eax,edx             ; The int1 code wants the remainder...
1393
1394 .ind1:
1395                 ; Single indirect; eax contains the block no
1396                 ; with respect to the start of the ind area;
1397                 ; ebx contains the pointer to the ind block.
1398                 push ax
1399                 shr eax,SECTOR_SHIFT-2
1400                 mov ebp,[gs:si+bx]
1401                 shl ebp,cl
1402                 add eax,ebp
1403                 call getcachesector
1404                 pop bx
1405                 and bx,(SECTOR_SIZE >> 2)-1
1406                 shl bx,2
1407
1408 .direct:
1409                 mov ebx,[gs:bx+si]      ; Get the pointer
1410
1411                 pop eax                 ; Get the sector index again
1412                 shl ebx,cl              ; Convert block number to sector
1413                 and eax,[ClustMask]     ; Add offset within block
1414                 add eax,ebx
1415
1416                 pop ebp
1417                 pop edx
1418                 pop ecx
1419                 pop edi
1420                 pop esi
1421                 pop ebx
1422                 pop gs
1423                 ret
1424
1425 ;
1426 ; getfssec: Get multiple sectors from a file
1427 ;
1428 ;       Same as above, except SI is a pointer to a open_file_t
1429 ;
1430 ;       ES:BX   -> Buffer
1431 ;       DS:SI   -> Pointer to open_file_t
1432 ;       CX      -> Sector count (0FFFFh = until end of file)
1433 ;                  Must not exceed the ES segment
1434 ;       Returns CF=1 on EOF (not necessarily error)
1435 ;       On return ECX = number of bytes read
1436 ;       All arguments are advanced to reflect data read.
1437 ;
1438 getfssec:
1439                 push ebp
1440                 push eax
1441                 push edx
1442                 push edi
1443
1444                 movzx ecx,cx
1445                 push ecx                        ; Sectors requested read
1446                 mov eax,[si+file_bytesleft]
1447                 add eax,SECTOR_SIZE-1
1448                 shr eax,SECTOR_SHIFT
1449                 cmp ecx,eax                     ; Number of sectors left
1450                 jbe .lenok
1451                 mov cx,ax
1452 .lenok:
1453 .getfragment:
1454                 mov eax,[si+file_sector]        ; Current start index
1455                 mov edi,eax
1456                 call linsector
1457                 push eax                        ; Fragment start sector
1458                 mov edx,eax
1459                 xor ebp,ebp                     ; Fragment sector count
1460 .getseccnt:
1461                 inc bp
1462                 dec cx
1463                 jz .do_read
1464                 xor eax,eax
1465                 mov ax,es
1466                 shl ax,4
1467                 add ax,bx                       ; Now DI = how far into 64K block we are
1468                 not ax                          ; Bytes left in 64K block
1469                 inc eax
1470                 shr eax,SECTOR_SHIFT            ; Sectors left in 64K block
1471                 cmp bp,ax
1472                 jnb .do_read                    ; Unless there is at least 1 more sector room...
1473                 inc edi                         ; Sector index
1474                 inc edx                         ; Linearly next sector
1475                 mov eax,edi
1476                 call linsector
1477                 ; jc .do_read
1478                 cmp edx,eax
1479                 je .getseccnt
1480 .do_read:
1481                 pop eax                         ; Linear start sector
1482                 pushad
1483                 call getlinsec_ext
1484                 popad
1485                 push bp
1486                 shl bp,9
1487                 add bx,bp                       ; Adjust buffer pointer
1488                 pop bp
1489                 add [si+file_sector],ebp        ; Next sector index
1490                 jcxz .done
1491                 jnz .getfragment
1492                 ; Fall through
1493 .done:
1494                 pop ecx                         ; Sectors requested read
1495                 shl ecx,SECTOR_SHIFT
1496                 cmp ecx,[si+file_bytesleft]
1497                 jb .noteof
1498                 mov ecx,[si+file_bytesleft]
1499 .noteof:        sub [si+file_bytesleft],ecx
1500                 ; Did we run out of file?
1501                 cmp dword [si+file_bytesleft],1
1502                 ; CF set if [SI] < 1, i.e. == 0
1503                 pop edi
1504                 pop edx
1505                 pop eax
1506                 pop ebp
1507                 ret
1508
1509 ; -----------------------------------------------------------------------------
1510 ;  Common modules
1511 ; -----------------------------------------------------------------------------
1512
1513 %include "getc.inc"             ; getc et al
1514 %include "conio.inc"            ; Console I/O
1515 %include "plaincon.inc"         ; writechr
1516 %include "writestr.inc"         ; String output
1517 %include "configinit.inc"       ; Initialize configuration
1518 %include "parseconfig.inc"      ; High-level config file handling
1519 %include "parsecmd.inc"         ; Low-level config file handling
1520 %include "bcopy32.inc"          ; 32-bit bcopy
1521 %include "loadhigh.inc"         ; Load a file into high memory
1522 %include "font.inc"             ; VGA font stuff
1523 %include "graphics.inc"         ; VGA graphics
1524 %include "highmem.inc"          ; High memory sizing
1525 %include "strcpy.inc"           ; strcpy()
1526 %include "strecpy.inc"          ; strcpy with end pointer check
1527 %include "cache.inc"            ; Metadata disk cache
1528 %include "adv.inc"              ; Auxillary Data Vector
1529
1530 ; -----------------------------------------------------------------------------
1531 ;  Begin data section
1532 ; -----------------------------------------------------------------------------
1533
1534                 section .data
1535 copyright_str   db ' Copyright (C) 1994-', year, ' H. Peter Anvin'
1536                 db CR, LF, 0
1537 err_bootfailed  db CR, LF, 'Boot failed: please change disks and press '
1538                 db 'a key to continue.', CR, LF, 0
1539 config_name     db 'extlinux.conf',0            ; Unmangled form
1540
1541 ;
1542 ; Command line options we'd like to take a look at
1543 ;
1544 ; mem= and vga= are handled as normal 32-bit integer values
1545 initrd_cmd      db 'initrd='
1546 initrd_cmd_len  equ 7
1547
1548 ;
1549 ; Config file keyword table
1550 ;
1551 %include "keywords.inc"
1552
1553 ;
1554 ; Extensions to search for (in *forward* order).
1555 ;
1556                 align 4, db 0
1557 exten_table:    db '.cbt'               ; COMBOOT (specific)
1558                 db '.img'               ; Disk image
1559                 db '.bs', 0             ; Boot sector
1560                 db '.com'               ; COMBOOT (same as DOS)
1561                 db '.c32'               ; COM32
1562 exten_table_end:
1563                 dd 0, 0                 ; Need 8 null bytes here
1564
1565 ;
1566 ; Misc initialized (data) variables
1567 ;
1568 %ifdef debug                            ; This code for debugging only
1569 debug_magic     dw 0D00Dh               ; Debug code sentinel
1570 %endif
1571
1572                 alignb 4, db 0
1573 BufSafe         dw trackbufsize/SECTOR_SIZE     ; Clusters we can load into trackbuf
1574 BufSafeBytes    dw trackbufsize         ; = how many bytes?
1575 %ifndef DEPEND
1576 %if ( trackbufsize % SECTOR_SIZE ) != 0
1577 %error trackbufsize must be a multiple of SECTOR_SIZE
1578 %endif
1579 %endif