1 ; chain.S - LILO boot chainer
3 ; Copyright 1992-1998 Werner Almesberger.
4 ; Copyright 1999-2004 John Coffman.
7 ; Licensed under the terms contained in the file 'COPYING' in the
13 get common.s /* as86 "include" will bypass the CPP */
15 ; for debugging, set the EBDA size in Kilobytes; e.g., 64
20 #if defined(LCF_SOLO_CHAIN) && !defined(DOS_D)
30 #endif /* LCF_SOLO_CHAIN */
33 #if VERSION_MINOR >= 50
42 _main: cld ! make sure !!!
48 .ascii "LILO" ! signature
55 ! the byte "drive" is filled in by the installer & updated by the second stage
57 drive: .byte 0 ! drive, 0x80, 0x81
58 .byte 0 ! head, always zero
60 hint: .word drvmap ! pointer to drive map
62 ptable: .blkw 0x20 ! partition table to preload
64 devmap: .word 0,0 ! device map filled in by second.S
66 cmd: .word 0,0 ! command line to pass on
68 ; ES:DI contains a pointer to the command line passed from second-stage
71 xor bx,bx ! set SS:SP to 0:7C00
73 mov sp,#BOOTSEG*16 ! #0x7C00
74 mov bp,sp ! address from BP
78 mov word ptr [0x13],#640-EBDA ; simulate EBDA in Kilobytes
97 .ascii "Boot drive 0x"
109 mov al,#0x3D ! '=' sign
116 cmp al,#0x20 ! test for space
118 ; real command line if AL==space, no command line if NUL
125 ; Account for any drive mappings being used by the Second Stage
127 les di,[parC_devmap] ! second stage translate table
128 ! this was set by the second stage loader
129 #if defined DEBUG_NEW
143 call install_map ! install it
146 ; but first, process the two 0xFFFF drive-map records for "master-boot"
148 mov si,#drvmap ! our created drive map
149 cmp WORD (si),#-1 ! test for "master-boot"
151 mov ah,[drive] ! boot drive
152 mov al,(si+3) ! possible "boot-as="
153 cmp al,#-1 ! test for master
155 ! make AL the master drive (0 or 80)
157 and al,#0x80 ! AL is master drive 0 or 80
159 mov (si),ax ! 80 -> boot
161 mov (si+2),ax ! boot -> 80
163 cmp ah,al ! are they the same
165 add si,#4 ! skip a "no-translation"
166 mov byte (si-1),#-1 ! clear any boot-as
170 mov [devmap],si ! save updated value
173 ;**************************************
177 mov cx,#SECTOR_SIZE/2
178 mtmp = SETUPSECS-1 ! broken math ...
179 mov si,#mtmp*SECTOR_SIZE
192 mov si,#BOOTSEG*16+0x24 ; address of first byte to test
194 cmp byte (bp+0x15),#0xf8 ; check media descriptor
199 cmp al,#0x80 ; check range of device codes
206 or al,al ; check hi-byte is empty
211 cmp al,#0x29 ; I do not know what this byte means
213 cmp al,#0x28 ; HPFS marker
217 lea si,(si+4) ; address of vol label & fs type
218 mov cx,#11 ; volume label (11)
223 js ck_failed ; not alphabetic if >= 0x80
224 jz ck_loop ; NUL allowed for HPFS
226 jb ck_failed ; not alphabetic if < SPACE
230 mov cx,#8 ; check Filesystem type
234 or al,al ; not alphabetic if >= 0x80
236 cmp al,#0x20 ; not alphabetic if < SPACE
245 mov (bp+0x24),dx ! fill in 0x24 and 0x25
262 mov cx,#0x20 ! move partition table
267 ! mess with the partition table
268 #if defined(LCF_REWRITE_TABLE) && !defined(LCF_READONLY)
269 mov si,#prtmap ! get partition table change rules
270 prtclp: lodsw ! bios == 0 indicates end
272 jz pmend ! at end -> quit
273 cmp al,cache ! already in cache ?
274 je incache ! yes -> no loading required
275 push ax ! save table data
276 call flush ! flush the cache
279 mov cache,al ! remember drive in cache
281 cmp al,drive ! boot drive ?
286 jne noc ! no -> load into scratch area
287 xor ax,ax ! load at 0000:0600
291 pmend: call flush ! flush table
292 br nopp ! and proceed
295 mov bx,#PARTS_SCR ! scratch area 0000:0800
296 loadit: mov es,ax ! set up pointers and remember them
299 mov ax,#0x201 ! load partition table, one sector
300 mov dx,cache ! drive from cache (DH = 0)
304 mov al,dl ! dump device code
306 mov si,#msg_load ! say loading
311 jc wrfail ! error -> abort
312 pop ax ! get BIOS and offset
313 incache:les bx,cbx ! load pointer
314 add bx,#PART_TABLE_OFFSET ! move to partition table
315 add bl,ah ! offset is always in [0x1be,0x1fd]
316 lodsw ! see what we need to do
319 jne nocng ! no -> do not change
322 mov byte ptr dirty,#1 ! mark as dirty
323 nocng: br prtclp ! next one
325 flush: test byte ptr dirty,#1 ! dirty ?
326 jz noflush ! no -> do not write
327 mov ax,#0x301 ! write one sector
328 mov dx,cache ! get the drive
329 or dl,dl ! nothing cached ?
330 jz noflush ! no -> do not flush
331 les bx,cbx ! reload pointer
334 mov al,dl ! dump device code
336 mov si,#msg_write ! say writing
343 wrfail: mov si,#failmsg ! complain
346 mov ax,#FIRSTSEG ! try to restart LILO
349 xor dx,dx ! zap the device code
350 jmpi FIRSTSEG*16,0 ! try to restart at 0000:7c00
352 cache: .byte 0 ! drive, 0 means not cached
353 .byte 0 ! head, always 0
360 ; reverse drive mapping
366 mov dx,drive ; get drive/head pair
368 rev0: lodsw ; get to, from pair
369 or ax,ax ; test for end
371 cmp ah,dl ; booting from "to"
372 jne rev0 ; loop if not
373 mov dl,al ; substitute the "from"
374 rev9: pop si ; restore SI
379 mov ax,drvmap ! need to install mapper ?
381 jz noimap ! no -> go on
385 mov di,[devmap] ! get drive map pointer
389 pop es ; ES:DI points at the current map
398 mov dx,drive ! initialize DX (drive and head)
400 mov si,offset ! DS:SI and ES:SI point to the partition
405 mov cx,# 4<<4 ; delay 4 seconds
408 int 0x15 ! Delay 6 seconds
409 #ifdef DEBUG_CONTINUE
412 mov si,#msg_cont ! Hit any key ...
415 int 0x16 ! AH==0, get key
422 xor ax,ax ! set DS and ES to zero
427 ;;;; mov ss,ax ! on all processors since the 186
428 mov sp,bx ! these instructions are locked
431 mov (si),dl ! yes, put it in the partition table
433 mov bp,si ! BP==SI flags hard disk boot
437 cmp byte ptr (bx+par1_cli),#0xFA ! first.S starts with CLI
439 cmp byte ptr (bx+par1_cli),#0xEB ! short jump?
442 mov al,(bx+par1_cli+1) ! get offset
446 add bx,ax ! test relocated record
448 cmp byte ptr (bx+par1_cli),#0xFA ! first.S starts with CLI
449 jne gotoit ! not LILO if no CLI
452 cmp dword ptr (bx+par1_signature),#EX_MAG_HL
453 jne gotoit ! LILO signature required for command line
455 cmp dword ptr [cmd],#0
458 ; pass on a command line
463 mov dword ptr (si),#EX_MAG_HL
470 #if defined(LCF_REWRITE_TABLE) || defined(DEBUG_NEW)
472 ! Display a NUL-terminated string on the console
473 ! DS:SI points at the string
477 say_2: lodsb ! get byte
479 jz aret ! yes -> done
480 mov ah,#14 ! display, tty-style
484 aret: pop bx ! restore
488 failmsg:.ascii "Rewrite error."
493 ;**************************************
496 ; Merge the contents of two drive maps
498 ; First drive map encountered: DS:SI
499 ; Second drive map encountered: ES:DI
500 ; Output drive map: DS:BX
502 ; The output drive coincides with the First drive map
504 ; Enter with DS != CS
509 pusha ! save all the registers
512 ; this is the guts of the loop to merge the records
515 lodsw ! get drive mapping
534 mov ah,(di+1) ! do the translation
536 mov word (di),#-1 ! wipe out entry
538 cmp ah,al ! remove null translation
547 ; finished merge, copy the rest from the source
555 jz copy ! it was -1, skip it
558 mov (bx),ax ! store value or end marker
560 jz alldone ! it was 0, end marker
568 popa ! restore all that we saved
571 ; end of drive_map_merge
573 ;**************************************
575 ; Install a drive swapper with a null drive map
578 ; DS == CS, SS == 0000
581 ; ES:DI points at the null device map
584 ; All other registers preserved
592 .asciz "Installing Drive Swapper\r\n"
597 dec word [0x413] ; allocate 1k
600 sub ax,#EBDA_EXTRA ! allocate extra EBDA
602 shl ax,#10-4 ; convert to paragraphs
605 shl eax,#16 ; EAX is ES:DI
607 xchg eax,[4*0x13] ; set new int 0x13 vector; get old
609 mov cx,#NEW13B/2 ; count of words to move
610 mov si,#new13 ; source is DS:SI
612 movsw ; move in the new drive mapper
614 mov (di),cx ; CX is zero from the move
620 ; Install drive mapper map
622 ; The map to use is at ES:DI
623 ; If there is an existing drive mapper, the two are merged.
624 ; If there is no drive mapper, then one is installed.
626 ; Enter with ES:DI set to point at the map to install
630 ; All registers are preserved
635 pusha ; save all the regs
636 mov bp,sp ; save stack location
640 .asciz "Install Map\r\n"
647 je install_ret ; nothing to do
648 COUNT = DRVMAP_SIZE*2
649 mov cx,#COUNT ; count of words
650 sub sp,#COUNT*2 ; now allocate words
651 mov si,di ; ES:SI is now source
652 mov di,sp ; SS:DI is now destination
661 lodsw ; get part of a map
663 or ax,ax ; test for null
667 install_done1: ; the map is at SS:SP
675 call is_prev_mapper ; is there a previous drive swapper
679 call swap13_null ; install a new, null drive mapper
680 ; sets ES:DI to point at drvmap in swapper
681 ; which must be NULL terminated
683 mov si,sp ; SS:SI is place to do the map merge
685 pop ds ; DS:SI is primary map
686 mov bx,si ; DS:BX receives the new map
687 ; and ES:DI points at the existing map
700 mov si,sp ; DS:SI is the source
706 pop ds ; restore the DS
709 mov sp,bp ; get ready for pop
720 call bout ! write hi-byte
722 bout: push ax ! save byte
723 shr al,#4 ! display upper nibble
726 nout: and al,#0x0F ! lower nible only
727 daa ! smaller conversion routine
729 adc al,#0x40 ! AL is hex char [0..9A..F]
732 mov ah,#14 ! display, tty-style
739 .ascii "Found v.22 drive swapper"
742 .ascii "Found v.21 drive swapper"
745 .ascii "Drive Mapping"
748 .ascii " - PT loaded"
751 .ascii " - PT written"
756 .ascii "24-25 update has occurred"
759 #ifdef DEBUG_CONTINUE
760 msg_cont: .ascii "\r\nHit any key to continue ..."
764 #endif /* DEBUG_NEW */
767 /* LILO version 21 (and maybe earlier) drive map header signature code */
769 push ax ! save AX (contains function code in AH)
770 push bp ! need BP to mess with stack
772 pushf ! push flags (to act like interrupt)
776 new13_old_drvmap_offs = * - new13_old - 2
777 new13_old_length = new13_old_drvmap_offs
778 new13_old_min_offs = 0x46 ; min seen in old code is 0x49
779 new13_old_max_offs = 0x50 ; maxed out at 21.7.5 at 0x4d
783 ; dump the drive map pointed to by ES:BX
784 ; Beware: DS != CS on some calls
794 mov ax,(bx) ; get drvmap entry
822 NEW13B = drvmap-new13
824 #if defined(LCF_REWRITE_TABLE)
825 prtmap: .blkw PRTMAP_SIZE*2+1 ! only first word of last entry is read
835 the_end1 = theend+511
836 theends = the_end1/512
838 .long CHAIN ! boot signature check