1 ; first.S - LILO first stage boot loader with LBA32 support
3 ; Copyright 1992-1998 Werner Almesberger
4 ; Copyright 1999-2005 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 */
22 # define VIDEO_ENABLE 3
24 # define VALIDATE !DEBUG /* adds 0Dh bytes */
25 # define SECOND_CHECK !DEBUG /* adds 5h bytes */
26 # define CYL1023 DEBUG /* subs 8h bytes */
27 # define GEOMETRIC !DEBUG /* adds 1h byte */
34 # define VALIDATE 1 /* adds 0Dh bytes */
35 # define SECOND_CHECK 1 /* adds 5h bytes */
36 # define CYL1023 0 /* subs 8h bytes */
37 # define GEOMETRIC 1 /* adds 1h byte */
41 ! VIDEO_ENABLE for those systems that disable the video on boot
42 ! = 0 first stage does not enable video
43 ! = 1 use get vid mode/set vid mode to enable
44 ! = 2 use VGA enable call to enable video
45 ! (cannot use, as code gets too big)
46 ! = 3 use direct mode set (mode 3, CGA, EGA, VGA)
47 ! = 7 use direct mode set (mode 7, MDA)
51 # define VIDEO_ENABLE 2
53 # define VIDEO_ENABLE 2
57 ! do not change the following -- it must correspond to the code in bsect.c
58 #define RELOCATABLE -1
68 _main: cli ! NT 4 blows up if this is missing
71 stage: .byte STAGE_FIRST
75 .word theend-zero ! size of the code & params
77 .word 0 ! no size indication
81 ! Boot device parameters. They are set by the installer.
85 mapstamp: .long 0 ! map timestamp
87 length = *-sig ! for the stage 1 vs stage 2 comparison
89 raid: .long 0 ! raid sector offset
90 tstamp: .long 0 ! timestamp
91 map_serial_no: .long 0 ! volume S/N containing map file
92 prompt: .word 0 ! indicates whether to always enter prompt
93 ! contains many other flags, too
95 d_dev: .byte 0x80 ! map file device code
96 d_flag: .byte 0 ! disk addressing flags
97 d_addr: .long 0 ! disk addr of second stage index sector
101 ;;; .word 16 ! size of packet
102 ;;; .word 1 ! count of bytes to read
105 ;;; .word map2 ! where to read
106 ;;; .word *-* ! segment where to read
109 ;;; .long 1 ! low address or CX,DX (geometric)
110 ! start at sector 1 for search in geo mode
112 ;;; .long 0 ! hi address
115 ! These locations are referenced as EX_OFF
116 ! (they used to be at CODE_START_1)
117 ext_si: .word 0 ! external interface
118 ext_es: .word 0 ! these locations are referenced in second.S
119 ext_bx: .word 0 ! do not disturb the ordering
120 ext_dl: .byte 0 ! second.S will check this magic number
121 ext_dh: .byte 0 ! not referenced, but must align stack
125 /***************************************************/
126 ! The following instruction MUST be
127 ! first instruction after the CLI/JMP short
128 ! at the start of the file; otherwise
129 ! the boot sector relocation fails.
132 mov ax,#BOOTSEG ! use DS,ES,SS = 0x07C0
133 /***************************************************/
136 mov sp,#SETUP_STACKSIZE ! set the stack for First Stage
139 push dx ! set ext_dl (and ext_dh, which is not used)
140 push bx ! WATCH the order of pushes
146 push es ! just not enough space with debug turned on
151 cld ! do not forget to do this !!!
152 mov ds,ax ! address data area
153 xor bp,bp ! shorted addressing
156 ! a BIOS has been found where the video interrupt (0x10) trashes DX
157 ! so, we had better be very paranoid about DX
162 # if VIDEO_ENABLE > 2
163 mov ax,#VIDEO_ENABLE ! set video mode 3 or 7
164 # elif VIDEO_ENABLE==1
165 mov ah,#15 ! get video mode
168 # else /* VIDEO_ENABLE==2 */
169 mov ax,#0x1200 ! enable video (VGA)
170 mov bl,#0x36 ! (probably a nop on EGA or MDA)
172 int 0x10 ! video call
178 #if (VIDEO_ENABLE&1) == 0
179 mov al,#0x0d ! gimme a CR ...
181 ; the suspect call for trashing DX on one BIOS:
182 mov al,#0x0a ! ... an LF ...
186 #if defined(DEBUG_NEW)
188 call bout ! code in AH
190 mov al,#0x4c ! ... an 'L' ...
194 pusha ! preserve all the registers for restart
197 pop es ! use buffer at end of boot sector
199 cmp dl,#EX_DL_MAG ! possible boot command line (chain.S)
201 mov dl,dh ! code passed in DH instead
204 mov bx,#map2 ! buffer for volume search
205 mov dh,[d_dev](bp) ! map device to DH
208 mov ax,dx ! copy device code to AL
209 and ah,#0x80 ! AH = 00 or 80
210 xor al,ah ! hi-bits must be the same
212 cmp al,#MAX_BIOS_DEVICES ! limit the device code
213 jae use_installed ! jump if DL is not valid
216 ! map is on boot device for RAID1, and if so marked; viz.,
218 test byte ptr [prompt](bp),#FLAG_MAP_ON_BOOT
219 jnz use_boot ! as passed in from BIOS or MBR loader
222 mov dl,dh ! device code to DL
223 mov esi,[map_serial_no](bp) ! to search for
229 mov ah,#8 ! get number of hard disks
236 movzx cx,dl ! extend to word in CX
239 mov dx,#0x80-1 ! device 80, flags=0
241 mov dx,#LBA32_FLAG*256+0x80-1 ! device 80, flags=LBA32
248 inc ax ! geometric addressing
250 call disk_read ! read
252 cmp esi,[PART_TABLE_OFFSET-6](bx)
256 pop dx ! restore specified BIOS code
257 ! AX and DX are identical at this point
260 ! uses value in DX, stack may have extra value
264 push bx ! save map2 for later
266 mov dh,[d_flag](bp) ! get device flags to DH
268 call pread ! increments BX
270 mov ah,#0x99 ! possible error code
271 cmp dword (bx-4),#EX_MAG_HL ! "LILO"
274 pop si ! point at #map2
277 push #SETUP_STACKSIZE/16 + BOOTSEG + SECTOR_SIZE/16*2
280 mov ax,ds ! get segment
281 add ax,#SETUP_STACKSIZE/16 ! + SECTOR_SIZE/16*2
287 call pread ! read using map at DS:SI
288 jnz sload ! into memory at ES:BX (auto increment)
290 ! Verify second stage loader signature
292 mov si,#sig ! pointer to signature area
294 mov cx,#length ! number of bytes to compare
295 mov ah,#0x9A ! possible error code
297 cmpsb ! check Signature 1 & 2
298 jne error ! check Signature 2
301 /* it would be nice to re-incorporate this check */
302 mov al,#STAGE_SECOND ! do not touch AH (error code)
307 ! Start the second stage loader DS=location of Params
309 push es ! segment of second stage
312 mov al,#0x49 ! display an 'I'
321 mov ah,#0x40 ; signal seek error
323 ! no return from error
326 #ifndef LCF_NO1STDIAG
327 mov al,#32 ! display a space
334 dec byte [zero](bp) ! CLI == 0xFA == 250
338 mov sp,#SETUP_STACKSIZE-4*2-8*2 ! set the stack for First Stage
340 mov sp,#SETUP_STACKSIZE-4*2-2*2-8*2 ! set the stack for First Stage
342 popa ! restore registers for restart
343 jmp near lagain ! redo from start
351 jmp zzz ! spin; wait for Ctrl-Alt-Del
356 ! packet read routine
372 push eax ! low order disk address
377 call dout ! print out disk address
379 push es ! memory segment ES
380 push bx ! memory offset BX
381 push #1 ! sector count
382 push #16 ! size of packet = 16 bytes
383 mov si,sp ! address of packet DS:SI
387 test dh,#LINEAR_FLAG|LBA32_FLAG
391 jz disk_convert ; it must be LINEAR
393 mov bx,#0x55AA ;magic number
397 cmp bx,#0xAA55 ;changed?
399 test cl,#EDD_PACKET ;EDD packet calls supported
404 push es ! protect on floppies
405 mov ah,#8 ! get geometry
408 disk_error3: ! transfer through on CF=1
409 jc error ! disk_error12
414 xchg cl,ch ;CX is max cylinder number
415 mov di,cx ;DI saves it
420 inc ax ;AX is number of heads (256 allowed)
422 ; compensate for Davide BIOS bug
423 dec cx ; 1..63 -> 0..62; 0->63
424 and cx,#0x003f ;CX is number of sectors
425 inc cx ; allow Sectors==0 to mean 64
427 mul cx ; kills DX also
428 xchg ax,bx ;save in BX
430 mov ax,[edd_d_addr](si) ;low part of address
431 mov dx,[edd_d_addr+2](si) ;hi part of address
434 jae disk_error2 ;prevent division error
436 div bx ;AX is cyl, DX is head/sect
442 ja disk_error2 ;cyl is too big
444 shl ah,#6 ; save hi 2 bits
447 div cl ;AH = sec-1, AL = head
448 or dl,ah ;form Cyl/Sec
450 inc cx ; sector is 1 based
452 pop dx ! restore device code
453 mov dh,al ! set head#
473 dec bp ! does not alter CF, already 0
474 jz disk_error3 ! go to "jc" with CF=1 & ZF=1
476 xor ax,ax ! reset the disk controller
478 popa ! reset AX,BX,CX,DX,SI
479 dec bp ! fix up BP count
490 mov ax,#0x201 ;read, count of 1
495 ; the pusha block is implicitly removed below
496 ;;; mov (si+2*16-1),ah ! set error code
497 ; the error code is never checked
498 lea sp,(si+16) ! do not touch carry;
507 ! Pointer Read -- read using pointer in DS:SI
513 add eax,[raid](bp) ! reloc is 0 on non-raid
516 add bh,#SECTOR_SIZE/256 ! next sector
523 #if !defined(LCF_NO1STDIAG) || defined(DEBUG_NEW)
524 bout: rol ax,#4 ! bring hi-nibble to position
526 rol ax,#4 ! bring lo-nibble to position
527 nout: and al,#0x0F ! display one nibble
528 daa ! shorter conversion routine
530 adc al,#0x40 ! is now a hex char 0..9A..F
532 ; display - write byte in AL to console
533 ; preserves all register contents
539 pusha ! make sure no register is changed
540 mov bx,#7 ! BH=0, BL=07
543 popa ! restore all the registers
562 call bout ! put out AH
566 call bout ! put out AL (now in AH)
574 ! If 'first' loads as the MBR, then there must be space for the partition
575 ! table. If 'first' loads as the boot record of some partition, then
576 ! the space reserved below is not used. But we must reserve the area
577 ! as a hedge against the first case.
581 .word 0,0,0,0 ! space for NT, DRDOS, and LiLO volume S/N
583 ! .org 0x1be ! spot for the partition table
591 .long FIRST ! boot block check
593 .word 0xAA55 ! boot block signature
596 ! Better be exactly 0x200
598 map2 equ * ! addressed as ES:[map2]