1 ; chain.S - LILO boot chainer
3 ; Copyright 1992-1998 Werner Almesberger
4 ; Copyright 1999-2004 John Coffman
5 ; Copyright 2009-2011 Joachim Wiedorn
8 ; Licensed under the terms contained in the file 'COPYING'
9 ; in the source directory.
14 get common.s /* as86 "include" will bypass the CPP */
16 ; for debugging, set the EBDA size in Kilobytes; e.g., 64
21 #if defined(LCF_SOLO_CHAIN) && !defined(DOS_D)
31 #endif /* LCF_SOLO_CHAIN */
34 #if VERSION_MINOR >= 50
43 _main: cld ! make sure !!!
49 .ascii "LILO" ! signature
56 ! the byte "drive" is filled in by the installer & updated by the second stage
58 drive: .byte 0 ! drive, 0x80, 0x81
59 .byte 0 ! head, always zero
61 hint: .word drvmap ! pointer to drive map
63 ptable: .blkw 0x20 ! partition table to preload
65 devmap: .word 0,0 ! device map filled in by second.S
67 cmd: .word 0,0 ! command line to pass on
69 ; ES:DI contains a pointer to the command line passed from second-stage
72 xor bx,bx ! set SS:SP to 0:7C00
74 mov sp,#BOOTSEG*16 ! #0x7C00
75 mov bp,sp ! address from BP
79 mov word ptr [0x13],#640-EBDA ; simulate EBDA in Kilobytes
98 .ascii "Boot drive 0x"
110 mov al,#0x3D ! '=' sign
117 cmp al,#0x20 ! test for space
119 ; real command line if AL==space, no command line if NUL
126 ; Account for any drive mappings being used by the Second Stage
128 les di,[parC_devmap] ! second stage translate table
129 ! this was set by the second stage loader
130 #if defined DEBUG_NEW
144 call install_map ! install it
147 ; but first, process the two 0xFFFF drive-map records for "master-boot"
149 mov si,#drvmap ! our created drive map
150 cmp WORD (si),#-1 ! test for "master-boot"
152 mov ah,[drive] ! boot drive
153 mov al,(si+3) ! possible "boot-as="
154 cmp al,#-1 ! test for master
156 ! make AL the master drive (0 or 80)
158 and al,#0x80 ! AL is master drive 0 or 80
160 mov (si),ax ! 80 -> boot
162 mov (si+2),ax ! boot -> 80
164 cmp ah,al ! are they the same
166 add si,#4 ! skip a "no-translation"
167 mov byte (si-1),#-1 ! clear any boot-as
171 mov [devmap],si ! save updated value
174 ;**************************************
178 mov cx,#SECTOR_SIZE/2
179 mtmp = SETUPSECS-1 ! broken math ...
180 mov si,#mtmp*SECTOR_SIZE
193 mov si,#BOOTSEG*16+0x24 ; address of first byte to test
195 cmp byte (bp+0x15),#0xf8 ; check media descriptor
200 cmp al,#0x80 ; check range of device codes
207 or al,al ; check hi-byte is empty
212 cmp al,#0x29 ; I do not know what this byte means
214 cmp al,#0x28 ; HPFS marker
218 lea si,(si+4) ; address of vol label & fs type
219 mov cx,#11 ; volume label (11)
224 js ck_failed ; not alphabetic if >= 0x80
225 jz ck_loop ; NUL allowed for HPFS
227 jb ck_failed ; not alphabetic if < SPACE
231 mov cx,#8 ; check Filesystem type
235 or al,al ; not alphabetic if >= 0x80
237 cmp al,#0x20 ; not alphabetic if < SPACE
246 mov (bp+0x24),dx ! fill in 0x24 and 0x25
263 mov cx,#0x20 ! move partition table
268 ! mess with the partition table
269 #if defined(LCF_REWRITE_TABLE) && !defined(LCF_READONLY)
270 mov si,#prtmap ! get partition table change rules
271 prtclp: lodsw ! bios == 0 indicates end
273 jz pmend ! at end -> quit
274 cmp al,cache ! already in cache ?
275 je incache ! yes -> no loading required
276 push ax ! save table data
277 call flush ! flush the cache
280 mov cache,al ! remember drive in cache
282 cmp al,drive ! boot drive ?
287 jne noc ! no -> load into scratch area
288 xor ax,ax ! load at 0000:0600
292 pmend: call flush ! flush table
293 br nopp ! and proceed
296 mov bx,#PARTS_SCR ! scratch area 0000:0800
297 loadit: mov es,ax ! set up pointers and remember them
300 mov ax,#0x201 ! load partition table, one sector
301 mov dx,cache ! drive from cache (DH = 0)
305 mov al,dl ! dump device code
307 mov si,#msg_load ! say loading
312 jc wrfail ! error -> abort
313 pop ax ! get BIOS and offset
314 incache:les bx,cbx ! load pointer
315 add bx,#PART_TABLE_OFFSET ! move to partition table
316 add bl,ah ! offset is always in [0x1be,0x1fd]
317 lodsw ! see what we need to do
320 jne nocng ! no -> do not change
323 mov byte ptr dirty,#1 ! mark as dirty
324 nocng: br prtclp ! next one
326 flush: test byte ptr dirty,#1 ! dirty ?
327 jz noflush ! no -> do not write
328 mov ax,#0x301 ! write one sector
329 mov dx,cache ! get the drive
330 or dl,dl ! nothing cached ?
331 jz noflush ! no -> do not flush
332 les bx,cbx ! reload pointer
335 mov al,dl ! dump device code
337 mov si,#msg_write ! say writing
344 wrfail: mov si,#failmsg ! complain
347 mov ax,#FIRSTSEG ! try to restart LILO
350 xor dx,dx ! zap the device code
351 jmpi FIRSTSEG*16,0 ! try to restart at 0000:7c00
353 cache: .byte 0 ! drive, 0 means not cached
354 .byte 0 ! head, always 0
361 ; reverse drive mapping
367 mov dx,drive ; get drive/head pair
369 rev0: lodsw ; get to, from pair
370 or ax,ax ; test for end
372 cmp ah,dl ; booting from "to"
373 jne rev0 ; loop if not
374 mov dl,al ; substitute the "from"
375 rev9: pop si ; restore SI
380 mov ax,drvmap ! need to install mapper ?
382 jz noimap ! no -> go on
386 mov di,[devmap] ! get drive map pointer
390 pop es ; ES:DI points at the current map
399 mov dx,drive ! initialize DX (drive and head)
401 mov si,offset ! DS:SI and ES:SI point to the partition
406 mov cx,# 4<<4 ; delay 4 seconds
409 int 0x15 ! Delay 6 seconds
410 #ifdef DEBUG_CONTINUE
413 mov si,#msg_cont ! Hit any key ...
416 int 0x16 ! AH==0, get key
423 xor ax,ax ! set DS and ES to zero
428 ;;;; mov ss,ax ! on all processors since the 186
429 mov sp,bx ! these instructions are locked
432 mov (si),dl ! yes, put it in the partition table
434 mov bp,si ! BP==SI flags hard disk boot
438 cmp byte ptr (bx+par1_cli),#0xFA ! first.S starts with CLI
440 cmp byte ptr (bx+par1_cli),#0xEB ! short jump?
443 mov al,(bx+par1_cli+1) ! get offset
447 add bx,ax ! test relocated record
449 cmp byte ptr (bx+par1_cli),#0xFA ! first.S starts with CLI
450 jne gotoit ! not LILO if no CLI
453 cmp dword ptr (bx+par1_signature),#EX_MAG_HL
454 jne gotoit ! LILO signature required for command line
456 cmp dword ptr [cmd],#0
459 ; pass on a command line
464 mov dword ptr (si),#EX_MAG_HL
471 #if defined(LCF_REWRITE_TABLE) || defined(DEBUG_NEW)
473 ! Display a NUL-terminated string on the console
474 ! DS:SI points at the string
478 say_2: lodsb ! get byte
480 jz aret ! yes -> done
481 mov ah,#14 ! display, tty-style
485 aret: pop bx ! restore
489 failmsg:.ascii "Rewrite error."
494 ;**************************************
497 ; Merge the contents of two drive maps
499 ; First drive map encountered: DS:SI
500 ; Second drive map encountered: ES:DI
501 ; Output drive map: DS:BX
503 ; The output drive coincides with the First drive map
505 ; Enter with DS != CS
510 pusha ! save all the registers
513 ; this is the guts of the loop to merge the records
516 lodsw ! get drive mapping
535 mov ah,(di+1) ! do the translation
537 mov word (di),#-1 ! wipe out entry
539 cmp ah,al ! remove null translation
548 ; finished merge, copy the rest from the source
556 jz copy ! it was -1, skip it
559 mov (bx),ax ! store value or end marker
561 jz alldone ! it was 0, end marker
569 popa ! restore all that we saved
572 ; end of drive_map_merge
574 ;**************************************
576 ; Install a drive swapper with a null drive map
579 ; DS == CS, SS == 0000
582 ; ES:DI points at the null device map
585 ; All other registers preserved
593 .asciz "Installing Drive Swapper\r\n"
598 dec word [0x413] ; allocate 1k
601 sub ax,#EBDA_EXTRA ! allocate extra EBDA
603 shl ax,#10-4 ; convert to paragraphs
606 shl eax,#16 ; EAX is ES:DI
608 xchg eax,[4*0x13] ; set new int 0x13 vector; get old
610 mov cx,#NEW13B/2 ; count of words to move
611 mov si,#new13 ; source is DS:SI
613 movsw ; move in the new drive mapper
615 mov (di),cx ; CX is zero from the move
621 ; Install drive mapper map
623 ; The map to use is at ES:DI
624 ; If there is an existing drive mapper, the two are merged.
625 ; If there is no drive mapper, then one is installed.
627 ; Enter with ES:DI set to point at the map to install
631 ; All registers are preserved
636 pusha ; save all the regs
637 mov bp,sp ; save stack location
641 .asciz "Install Map\r\n"
648 je install_ret ; nothing to do
649 COUNT = DRVMAP_SIZE*2
650 mov cx,#COUNT ; count of words
651 sub sp,#COUNT*2 ; now allocate words
652 mov si,di ; ES:SI is now source
653 mov di,sp ; SS:DI is now destination
662 lodsw ; get part of a map
664 or ax,ax ; test for null
668 install_done1: ; the map is at SS:SP
676 call is_prev_mapper ; is there a previous drive swapper
680 call swap13_null ; install a new, null drive mapper
681 ; sets ES:DI to point at drvmap in swapper
682 ; which must be NULL terminated
684 mov si,sp ; SS:SI is place to do the map merge
686 pop ds ; DS:SI is primary map
687 mov bx,si ; DS:BX receives the new map
688 ; and ES:DI points at the existing map
701 mov si,sp ; DS:SI is the source
707 pop ds ; restore the DS
710 mov sp,bp ; get ready for pop
721 call bout ! write hi-byte
723 bout: push ax ! save byte
724 shr al,#4 ! display upper nibble
727 nout: and al,#0x0F ! lower nible only
728 daa ! smaller conversion routine
730 adc al,#0x40 ! AL is hex char [0..9A..F]
733 mov ah,#14 ! display, tty-style
740 .ascii "Found v.22 drive swapper"
743 .ascii "Found v.21 drive swapper"
746 .ascii "Drive Mapping"
749 .ascii " - PT loaded"
752 .ascii " - PT written"
757 .ascii "24-25 update has occurred"
760 #ifdef DEBUG_CONTINUE
761 msg_cont: .ascii "\r\nHit any key to continue ..."
765 #endif /* DEBUG_NEW */
768 /* LILO version 21 (and maybe earlier) drive map header signature code */
770 push ax ! save AX (contains function code in AH)
771 push bp ! need BP to mess with stack
773 pushf ! push flags (to act like interrupt)
777 new13_old_drvmap_offs = * - new13_old - 2
778 new13_old_length = new13_old_drvmap_offs
779 new13_old_min_offs = 0x46 ; min seen in old code is 0x49
780 new13_old_max_offs = 0x50 ; maxed out at 21.7.5 at 0x4d
784 ; dump the drive map pointed to by ES:BX
785 ; Beware: DS != CS on some calls
795 mov ax,(bx) ; get drvmap entry
823 NEW13B = drvmap-new13
825 #if defined(LCF_REWRITE_TABLE)
826 prtmap: .blkw PRTMAP_SIZE*2+1 ! only first word of last entry is read
836 the_end1 = theend+511
837 theends = the_end1/512
839 .long CHAIN ! boot signature check