Imported Upstream version 24.0
[rrq/maintain_lilo.git] / src / chain.S
1 ;  chain.S  -  LILO boot chainer 
2 ;
3 ;  Copyright 1992-1998 Werner Almesberger
4 ;  Copyright 1999-2004 John Coffman
5 ;  Copyright 2009-2011 Joachim Wiedorn
6 ;  All rights reserved.
7 ;
8 ;  Licensed under the terms contained in the file 'COPYING'
9 ;  in the source directory.
10 ;
11
12 #define LILO_ASM
13 #include "lilo.h"
14 get common.s            /* as86 "include" will bypass the CPP */
15
16 ; for debugging, set the EBDA size in Kilobytes; e.g., 64
17 #define EBDA 0
18
19 #define REVERSE_DL 1
20
21 #if defined(LCF_SOLO_CHAIN) && !defined(DOS_D)
22
23 #ifndef DOS_D
24 #define DOS_D
25 #endif
26
27 #ifndef CHECK
28 #define CHECK
29 #endif
30
31 #endif          /* LCF_SOLO_CHAIN */
32
33
34 #if VERSION_MINOR >= 50
35 #define DEBUG_NEW
36 #endif
37
38         .text
39
40         .globl  _main
41         .org    0
42
43 _main:  cld                     ! make sure !!!
44         jmp     start
45
46         
47         .org    6
48
49         .ascii  "LILO"          ! signature
50
51         .word   STAGE_CHAIN
52         .word   VERSION
53
54 offset: .word   0
55
56 ! the byte "drive" is filled in by the installer & updated by the second stage 
57
58 drive:  .byte   0                       ! drive, 0x80, 0x81
59         .byte   0                       ! head, always zero
60
61 hint:   .word   drvmap                  ! pointer to drive map
62
63 ptable: .blkw   0x20                    ! partition table to preload
64
65 devmap: .word   0,0                     ! device map filled in by second.S
66
67 cmd:    .word   0,0                     ! command line to pass on
68
69 ; ES:DI contains a pointer to the command line passed from second-stage
70
71 start:
72         xor     bx,bx                   ! set SS:SP to 0:7C00
73         mov     ss,bx
74         mov     sp,#BOOTSEG*16          ! #0x7C00
75         mov     bp,sp                   ! address from BP
76 #if EBDA
77         push    #0x40
78         pop     ds
79         mov     word ptr [0x13],#640-EBDA       ; simulate EBDA in Kilobytes
80 #endif
81         push    cs
82         pop     ds
83
84 #ifdef DEBUG_NEW
85         push    bp
86
87         push    es
88         pop     ds
89         mov     si,di
90         call    say
91
92         push    cs
93         pop     ds
94         mov     si,#crlf
95         call    say
96
97         call    bd1
98         .ascii  "Boot drive 0x"
99         .byte   0
100 bd1:    pop     si
101         call    say
102         mov     al,drive
103         call    bout
104         mov     si,#crlf
105         call    say
106
107         pop     bp
108 #endif
109
110         mov     al,#0x3D                ! '=' sign
111         mov     cx,#-1
112         repne
113           scasb                         ! scan for =
114 srch:   seg es
115           mov   al,(di)
116         inc     di
117         cmp     al,#0x20                ! test for space
118         ja      srch
119 ; real command line if AL==space, no command line if NUL
120         jb      nocmd
121         mov     [cmd],di
122         mov     [cmd+2],es
123 nocmd:
124
125 ;
126 ; Account for any drive mappings being used by the Second Stage
127 ;
128         les     di,[parC_devmap]        ! second stage translate table
129                                         ! this was set by the second stage loader
130 #if defined DEBUG_NEW
131         mov     ax,es
132         call    wout
133         mov     al,#0x20
134         call    cout
135         mov     ax,di
136         call    wout
137         mov     si,#crlf
138         call    say
139
140         mov     bx,di
141         call    dump_drvmap
142 #endif
143
144         call    install_map             ! install it
145
146
147 ; but first, process the two 0xFFFF drive-map records for "master-boot"
148 ;
149         mov     si,#drvmap              ! our created drive map
150         cmp     WORD (si),#-1           ! test for "master-boot"
151         jne     noswap
152         mov     ah,[drive]              ! boot drive
153         mov     al,(si+3)               ! possible "boot-as="
154         cmp     al,#-1                  ! test for master
155         jne     boot_as
156 ! make AL the master drive (0 or 80)
157         mov     al,ah
158         and     al,#0x80                ! AL is master drive 0 or 80
159 boot_as:
160         mov     (si),ax         ! 80 -> boot
161         xchg    ah,al
162         mov     (si+2),ax               ! boot -> 80
163
164         cmp     ah,al                   ! are they the same
165         jne     domerge
166         add     si,#4                   ! skip a "no-translation"
167         mov     byte (si-1),#-1         ! clear any boot-as
168 noswap:
169 domerge:
170
171         mov     [devmap],si     ! save updated value
172
173
174 ;**************************************
175         push    ss
176         pop     es
177
178         mov     cx,#SECTOR_SIZE/2
179 mtmp = SETUPSECS-1                      ! broken math ...
180         mov     si,#mtmp*SECTOR_SIZE
181 #ifdef DEBUG_NEW
182         mov     di,#boot_sector
183         cmp     si,di
184         ja      use_setupsecs_m_1
185         mov     si,di
186 use_setupsecs_m_1:
187 #endif
188         mov     di,bp                   ! #0x7C00
189         rep
190           movsw
191 #ifdef DOS_D
192 #ifdef CHECK
193         mov     si,#BOOTSEG*16+0x24     ; address of first byte to test
194
195         cmp     byte (bp+0x15),#0xf8            ; check media descriptor
196         jne     ck_failed
197
198         seg     es
199           lodsb
200         cmp     al,#0x80                ; check range of device codes
201         jb      ck_failed
202         cmp     al,#0x8f
203         ja      ck_failed
204
205         seg     es
206           lodsb
207         or      al,al                   ; check hi-byte is empty
208         jnz     ck_failed
209
210         seg     es
211           lodsb
212         cmp     al,#0x29                ; I do not know what this byte means
213         je      ck_okay
214         cmp     al,#0x28                ; HPFS marker
215         jne     ck_failed
216
217 ck_okay:
218         lea     si,(si+4)               ; address of vol label & fs type
219         mov     cx,#11                  ; volume label (11)
220 ck_next:
221         seg     es
222           lodsb
223         or      al,al
224         js      ck_failed               ; not alphabetic if >= 0x80
225         jz      ck_loop                 ; NUL allowed for HPFS
226         cmp     al,#0x20
227         jb      ck_failed               ; not alphabetic if < SPACE
228 ck_loop:
229         loop    ck_next
230
231         mov     cx,#8                   ; check Filesystem type
232 ck_fstype:
233         seg     es
234           lodsb
235         or      al,al                   ; not alphabetic if >= 0x80
236         js      ck_failed
237         cmp     al,#0x20                ; not alphabetic if < SPACE
238         jb      ck_failed
239         loop    ck_fstype
240
241 #endif
242 dos4:
243
244         call    revmap1
245
246         mov     (bp+0x24),dx            ! fill in 0x24 and 0x25
247         mov     si,offset
248         mov     edx,ptable+8(si)
249         mov     (bp+0x1C),edx
250
251 #ifdef DEBUG_NEW
252         mov     si,#update
253         jmp     ck_say   
254 ck_failed:
255         mov     si,#no_update
256 ck_say:
257         call    say
258 #else
259 ck_failed:
260 #endif
261
262 #endif
263         mov     cx,#0x20                ! move partition table
264         mov     si,#ptable
265         mov     di,#PART_TABLE
266         rep
267         movsw
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
272         or      al,al
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
278         pop     ax
279         push    ax
280         mov     cache,al                ! remember drive in cache
281 #if 0
282         cmp     al,drive                ! boot drive ?
283 #else
284         call    revmap1
285         cmp     al,dl
286 #endif
287         jne     noc                     ! no -> load into scratch area
288         xor     ax,ax                   ! load at 0000:0600
289         mov     bx,#PARTS_LOAD
290         jmp     loadit
291
292 pmend:  call    flush                   ! flush table
293         br      nopp                    ! and proceed
294
295 noc:    mov     ax,ds
296         mov     bx,#PARTS_SCR           ! scratch area  0000:0800
297 loadit: mov     es,ax                   ! set up pointers and remember them
298         mov     ces,ax
299         mov     cbx,bx
300         mov     ax,#0x201               ! load partition table, one sector
301         mov     dx,cache                ! drive from cache (DH = 0)
302         mov     cx,#1
303 #ifdef DEBUG_NEW
304         pusha
305         mov     al,dl                   ! dump device code
306         call    bout
307         mov     si,#msg_load            ! say loading
308         call    say
309         popa
310 #endif
311         int     0x13                    ! load it
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
318         seg     es                      ! match ?
319         cmp     byte ptr (bx),al
320         jne     nocng                   ! no -> do not change
321         seg     es                      ! change
322         mov     byte ptr (bx),ah
323         mov     byte ptr dirty,#1       ! mark as dirty
324 nocng:  br      prtclp                  ! next one
325
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
333 #ifdef DEBUG_NEW
334         pusha
335         mov     al,dl                   ! dump device code
336         call    bout
337         mov     si,#msg_write           ! say writing
338         call    say
339         popa
340 #endif
341         int     0x13                    ! write ...
342         jc      wrfail                  ! argl
343 noflush:ret
344 wrfail: mov     si,#failmsg             ! complain
345         call    say
346 #if 0
347         mov     ax,#FIRSTSEG            ! try to restart LILO
348         jmpi    #GO,FIRSTSEG
349 #else
350         xor     dx,dx                   ! zap the device code
351         jmpi    FIRSTSEG*16,0           ! try to restart at 0000:7c00
352 #endif
353 cache:  .byte   0                       ! drive, 0 means not cached
354         .byte   0                       ! head, always 0
355 cbx:    .blkw   1
356 ces:    .blkw   1
357 dirty:  .byte   0
358
359 #endif
360
361 ; reverse drive mapping
362 ;       uses AX
363 ;       updates DL
364 ;
365 revmap1:
366         push    si
367         mov     dx,drive        ; get drive/head pair
368         mov     si,#drvmap
369 rev0:   lodsw                   ; get to, from pair
370         or      ax,ax           ; test for end
371         jz      rev9            ; done
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
376         ret
377
378 nopp:
379 #if 0
380         mov     ax,drvmap               ! need to install mapper ?
381         or      ax,ax
382         jz      noimap                  ! no -> go on
383         call    swap13
384 noimap:
385 #else
386         mov     di,[devmap]     ! get drive map pointer
387         cmp     word (di),#0
388         je      noimap
389         push    cs
390         pop     es              ; ES:DI points at the current map
391         call    install_map
392 noimap:
393 #endif
394
395
396 #if REVERSE_DL
397         call    revmap1
398 #else
399         mov     dx,drive                ! initialize DX (drive and head)
400 #endif
401         mov     si,offset               ! DS:SI and ES:SI point to the partition
402         add     si,#PART_TABLE
403 #ifdef DEBUG_NEW
404         pusha
405
406         mov     cx,# 4<<4               ; delay 4 seconds
407         xor     dx,dx
408         mov     ah,# 0x86
409         int     0x15                    ! Delay 6 seconds
410 #ifdef DEBUG_CONTINUE
411         jnc     delayed
412
413         mov     si,#msg_cont            ! Hit any key ...
414         call    say
415         xor     ax,ax
416         int     0x16    ! AH==0, get key
417 delayed:
418 #endif
419
420         popa
421 #endif
422
423         xor     ax,ax                   ! set DS and ES to zero
424         mov     ds,ax
425         mov     es,ax
426         mov     bx,#BOOTSEG*16
427         
428 ;;;;    mov     ss,ax                   ! on all processors since the 186
429         mov     sp,bx                   ! these instructions are locked
430         
431 #ifdef LCF_COHERENT
432         mov     (si),dl                 ! yes, put it in the partition table
433 #endif
434         mov     bp,si                   ! BP==SI flags hard disk boot
435         push    ax
436         push    bx
437         seg ss
438         cmp     byte ptr (bx+par1_cli),#0xFA    ! first.S starts with CLI
439         je      try_sig
440         cmp     byte ptr (bx+par1_cli),#0xEB    ! short jump?
441         jne     gotoit
442         push    ax
443         mov     al,(bx+par1_cli+1)              ! get offset
444         cbw
445         inc     ax
446         inc     ax
447         add     bx,ax                   ! test relocated record
448         pop     ax
449         cmp     byte ptr (bx+par1_cli),#0xFA    ! first.S starts with CLI
450         jne     gotoit                  ! not LILO if no CLI
451 try_sig:
452         seg ss
453           cmp   dword ptr (bx+par1_signature),#EX_MAG_HL
454         jne     gotoit          ! LILO signature required for command line
455         seg cs
456           cmp   dword ptr [cmd],#0
457         je      gotoit
458
459 ;  pass on a command line
460         seg cs
461           les   bx,[cmd]
462         lea     si,(bx-4)
463         seg es
464           mov   dword ptr (si),#EX_MAG_HL
465         mov     dh,dl
466         mov     dl,#EX_DL_MAG
467 gotoit:
468         retf
469
470
471 #if defined(LCF_REWRITE_TABLE) || defined(DEBUG_NEW)
472
473 ! Display a NUL-terminated string on the console
474 !       DS:SI points at the string
475 !
476 say:    push    ax
477         push    bx              ! save BX
478 say_2:  lodsb                   ! get byte
479         or      al,al           ! NUL ?
480         jz      aret            ! yes -> done
481         mov     ah,#14          ! display, tty-style
482         mov     bx,#0007
483         int     0x10
484         jmp     say_2           ! next one
485 aret:   pop     bx              ! restore
486         pop     ax
487         ret                     ! done
488
489 failmsg:.ascii  "Rewrite error."
490         .byte   13,10,0
491
492 #endif
493
494 ;**************************************
495
496
497 ; Merge the contents of two drive maps
498 ;
499 ;       First drive map encountered:    DS:SI
500 ;       Second drive map encountered:   ES:DI
501 ;       Output drive map:               DS:BX
502 ;
503 ; The output drive coincides with the First drive map
504 ;
505 ;       Enter with  DS != CS
506 ;
507 ;
508 ;       
509 drive_map_merge:
510         pusha                           ! save all the registers
511
512 ;
513 ; this is the guts of the loop to merge the records
514 ;
515 process:
516         lodsw                           ! get drive mapping
517         or      ax,ax
518         jz      copy
519
520         push    di
521         jmp     nextone1
522 nextone:
523         inc     di
524         inc     di
525 nextone1:
526         seg es
527           cmp   word (di),#0
528         je      atend
529
530         seg es
531           cmp   (di),ah
532         jne     nextone
533
534         seg es
535           mov   ah,(di+1)               ! do the translation
536         seg es
537           mov   word (di),#-1           ! wipe out entry
538 atend:
539         cmp     ah,al                   ! remove null translation
540         je      nostore
541         mov     (bx),ax
542         inc     bx
543         inc     bx
544 nostore:
545         pop     di
546         jmp     process
547
548 ; finished merge, copy the rest from the source
549 copy:
550         seg es
551           mov   ax,(di)                 !
552         inc     di
553         inc     di
554
555         inc     ax
556         jz      copy                    ! it was -1, skip it
557
558         dec     ax
559         mov     (bx),ax                 ! store value or end marker
560
561         jz      alldone                 ! it was 0, end marker
562
563         inc     bx
564         inc     bx
565         jmp     copy
566
567
568 alldone:
569         popa                    ! restore all that we saved
570         ret
571
572 ; end of drive_map_merge
573
574 ;**************************************
575
576 ; Install a drive swapper with a null drive map
577 ;
578 ;       Enter with:
579 ;               DS == CS, SS == 0000
580 ;
581 ;       Exit with:
582 ;               ES:DI points at the null device map
583 ;
584 ;               EAX is trashed
585 ;               All other registers preserved
586 ;
587 ;
588 swap13_null:
589         push    cx
590         push    si
591 #ifdef DEBUG_NEW
592         call    sn11
593         .asciz  "Installing Drive Swapper\r\n"
594 sn11:   pop     si
595         call    say
596 #endif
597         seg ss
598           dec   word [0x413]    ; allocate 1k
599         int     0x12
600 #if EBDA_EXTRA
601         sub     ax,#EBDA_EXTRA  ! allocate extra EBDA
602 #endif
603         shl     ax,#10-4        ; convert to paragraphs
604         mov     es,ax           ;
605         xor     di,di           ; DI = 0
606         shl     eax,#16         ; EAX is ES:DI
607         seg ss
608           xchg  eax,[4*0x13]    ; set new int 0x13 vector; get old
609         mov     [old13of],eax
610         mov     cx,#NEW13B/2    ; count of words to move
611         mov     si,#new13       ; source is DS:SI
612         rep
613           movsw                 ; move in the new drive mapper
614         seg es
615           mov   (di),cx         ; CX is zero from the move
616
617         pop     si
618         pop     cx
619         ret
620
621 ; Install drive mapper map
622 ;
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.
626 ;
627 ;       Enter with  ES:DI  set to point at the map to install
628 ;               DS == CS
629 ;
630 ;       Exit with  DS=CX
631 ;               All registers are preserved
632 ;
633
634 install_map:
635         push    es
636         pusha           ; save all the regs
637         mov     bp,sp   ; save stack location
638
639 #ifdef DEBUG_NEW
640         call    im111
641         .asciz  "Install Map\r\n"
642 im111:  pop     si
643         call    say
644 #endif
645
646         seg es
647           cmp   word (di),#0
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
654
655         push    ds
656
657         push    es
658         pop     ds
659         push    ss
660         pop     es
661 install_move1:
662         lodsw                   ; get part of a map
663         stosw                   ; store it
664         or      ax,ax           ; test for null
665         jz      install_done1
666         loop    install_move1
667         jmp     fatal
668 install_done1:                  ; the map is at SS:SP
669         pop     ds
670
671 #ifdef DEBUG_NEW
672         mov     bx,sp           ; ES==SS
673         call    dump_drvmap
674 #endif
675
676         call    is_prev_mapper  ; is there a previous drive swapper
677                                 ; sets ES:DI
678         jnz     install_skip
679
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
683 install_skip:
684         mov     si,sp           ; SS:SI is place to do the map merge
685         push    ss
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
689         call    drive_map_merge
690
691 #ifdef DEBUG_NEW
692         mov     bx,sp
693         push    es
694
695         push    ss
696         pop     es
697         call    dump_drvmap
698
699         pop     es
700 #endif
701         mov     si,sp           ; DS:SI is the source
702         mov     cx,#COUNT
703         rep
704           movsw
705
706         push    cs
707         pop     ds              ; restore the DS
708
709 install_ret:
710         mov     sp,bp   ; get ready for pop
711         popa
712         pop     es
713         ret
714
715 fatal:  hlt
716         jmp     fatal
717
718 #ifdef DEBUG_NEW
719 wout:   push    ax
720         xchg    ah,al
721         call    bout            ! write hi-byte
722         pop     ax
723 bout:   push    ax              ! save byte
724         shr     al,#4           ! display upper nibble
725         call    nout
726         pop     ax
727 nout:   and     al,#0x0F        ! lower nible only
728         daa                     ! smaller conversion routine
729         add     al,#0xF0
730         adc     al,#0x40        ! AL is hex char [0..9A..F]
731 cout:
732         push    bx
733         mov     ah,#14          ! display, tty-style
734         mov     bx,#0007
735         int     0x10
736         pop     bx
737         ret
738
739 msg_new:
740         .ascii  "Found v.22 drive swapper"
741         .byte   13,10,0
742 msg_old:
743         .ascii  "Found v.21 drive swapper"
744         .byte   13,10,0
745 msg_swap13:
746         .ascii  "Drive Mapping"
747         .byte   13,10,0
748 msg_load:
749         .ascii  " - PT loaded"
750         .byte   13,10,0
751 msg_write:
752         .ascii  " - PT written"
753         .byte   13,10,0
754 no_update:
755         .ascii  "NO "
756 update:
757         .ascii  "24-25 update has occurred"
758 crlf:   .byte   13,10,0
759
760 #ifdef DEBUG_CONTINUE
761 msg_cont: .ascii  "\r\nHit any key to continue ..."
762         .byte   0
763 #endif
764
765 #endif  /* DEBUG_NEW */
766
767 #if 0
768 /* LILO version 21 (and maybe earlier) drive map header signature code */
769 new13_old:
770         push    ax              ! save AX (contains function code in AH)
771         push    bp              ! need BP to mess with stack
772         mov     bp,sp
773         pushf                   ! push flags (to act like interrupt)
774         push    si
775         mov     si,#drvmap-new13
776
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
781 #endif
782
783 #ifdef DEBUG_NEW
784 ; dump the drive map pointed to by  ES:BX
785 ;       Beware: DS != CS on some calls
786 ;
787 dump_drvmap:
788         pusha
789         push    ds
790
791         push    cs
792         pop     ds
793
794 sw13b:  seg es
795           mov   ax,(bx)         ; get drvmap entry
796         or      ax,ax
797         jz      sw13z
798         call    bout
799         mov     si,#sw13p
800         inc     bx
801         call    say
802         seg es
803           mov   al,(bx)
804         call    bout
805         inc     bx
806         mov     si,#crlf
807         call    say
808         jmp     sw13b
809 sw13z:
810         mov     si,#msg_swap13
811         call    say
812
813         pop     ds
814         popa
815         ret
816 sw13p:  .asciz  " -> "
817 #endif
818
819
820 #define CHAIN_LOADER
821 #include "mapper.S"
822
823 NEW13B  =   drvmap-new13
824
825 #if defined(LCF_REWRITE_TABLE)
826 prtmap: .blkw   PRTMAP_SIZE*2+1 ! only first word of last entry is read
827 #endif
828
829
830 #ifdef CHAIN
831         .org    *+4
832 #endif
833 theend:
834
835 #ifdef CHAIN
836 the_end1        = theend+511
837 theends =       the_end1/512
838         .org    theends*512-4
839         .long   CHAIN           ! boot signature check
840 #endif
841         .align  512
842 boot_sector: