MOXA linux-2.6.x / linux-2.6.19-uc1 from UC-7110-LX-BOOTLOADER-1.9_VERSION-4.2.tgz
[linux-2.6.19-moxart.git] / arch / i386 / boot / bootsect.S
blob8a084e67515f564073cad5865ae5ba7916ad6f35
1 /*
2  *      bootsect.S              Copyright (C) 1991, 1992 Linus Torvalds
3  *
4  *      modified by Drew Eckhardt
5  *      modified by Bruce Evans (bde)
6  *      modified by Chris Noe (May 1999) (as86 -> gas)
7  *
8  * 360k/720k disk support: Andrzej Krzysztofowicz <ankry@green.mif.pg.gda.pl>
9  *
10  * BIG FAT NOTE: We're in real mode using 64k segments.  Therefore segment
11  * addresses must be multiplied by 16 to obtain their respective linear
12  * addresses. To avoid confusion, linear addresses are written using leading
13  * hex while segment addresses are written as segment:offset.
14  *
15  * bde - should not jump blindly, there may be systems with only 512K low
16  * memory.  Use int 0x12 to get the top of memory, etc.
17  *
18  * It then loads 'setup' directly after itself (0x90200), and the system
19  * at 0x10000, using BIOS interrupts. 
20  *
21  * NOTE! currently system is at most (8*65536-4096) bytes long. This should 
22  * be no problem, even in the future. I want to keep it simple. This 508 kB
23  * kernel size should be enough, especially as this doesn't contain the
24  * buffer cache as in minix (and especially now that the kernel is 
25  * compressed :-)
26  *
27  * The loader has been made as simple as possible, and continuous
28  * read errors will result in a unbreakable loop. Reboot by hand. It
29  * loads pretty fast by getting whole tracks at a time whenever possible.
30  */
32 #include <asm/boot.h>
34 SETUPSECTS      = 4                     /* default nr of setup-sectors */
35 BOOTSEG         = 0x07C0                /* original address of boot-sector */
36 INITSEG         = DEF_INITSEG           /* we move boot here - out of the way */
37 SETUPSEG        = DEF_SETUPSEG          /* setup starts here */
38 SYSSEG          = DEF_SYSSEG            /* system loaded at 0x10000 (65536) */
39 SYSSIZE         = DEF_SYSSIZE           /* system size: # of 16-byte clicks */
40                                         /* to be loaded */
41 ROOT_DEV        = 0                     /* ROOT_DEV is now written by "build" */
42 SWAP_DEV        = 0                     /* SWAP_DEV is now written by "build" */
44 #ifndef SVGA_MODE
45 #define SVGA_MODE ASK_VGA
46 #endif
48 #ifndef RAMDISK
49 #define RAMDISK 0
50 #endif
52 #ifndef ROOT_RDONLY
53 #define ROOT_RDONLY 1
54 #endif
56 .code16
57 .text
59 .global _start
60 _start:
62 # First things first. Move ourself from 0x7C00 -> 0x90000 and jump there.
64         movw    $BOOTSEG, %ax
65         movw    %ax, %ds                # %ds = BOOTSEG
66         movw    $INITSEG, %ax
67         movw    %ax, %es                # %ax = %es = INITSEG
68         movw    $256, %cx
69         subw    %si, %si
70         subw    %di, %di
71         cld
72         rep
73         movsw
74         ljmp    $INITSEG, $go
76 # bde - changed 0xff00 to 0x4000 to use debugger at 0x6400 up (bde).  We
77 # wouldn't have to worry about this if we checked the top of memory.  Also
78 # my BIOS can be configured to put the wini drive tables in high memory
79 # instead of in the vector table.  The old stack might have clobbered the
80 # drive table.
82 go:     movw    $0x4000-12, %di         # 0x4000 is an arbitrary value >=
83                                         # length of bootsect + length of
84                                         # setup + room for stack;
85                                         # 12 is disk parm size.
86         movw    %ax, %ds                # %ax and %es already contain INITSEG
87         movw    %ax, %ss
88         movw    %di, %sp                # put stack at INITSEG:0x4000-12.
90 # Many BIOS's default disk parameter tables will not recognize
91 # multi-sector reads beyond the maximum sector number specified
92 # in the default diskette parameter tables - this may mean 7
93 # sectors in some cases.
95 # Since single sector reads are slow and out of the question,
96 # we must take care of this by creating new parameter tables
97 # (for the first disk) in RAM.  We will set the maximum sector
98 # count to 36 - the most we will encounter on an ED 2.88.  
100 # High doesn't hurt.  Low does.
102 # Segments are as follows: %cs = %ds = %es = %ss = INITSEG, %fs = 0,
103 # and %gs is unused.
105         movw    %cx, %fs                # %fs = 0
106         movw    $0x78, %bx              # %fs:%bx is parameter table address
107         pushw   %ds
108         ldsw    %fs:(%bx), %si          # %ds:%si is source
109         movb    $6, %cl                 # copy 12 bytes
110         pushw   %di                     # %di = 0x4000-12.
111         rep                             # don't worry about cld
112         movsw                           # already done above
113         popw    %di
114         popw    %ds
115         movb    $36, 0x4(%di)           # patch sector count
116         movw    %di, %fs:(%bx)
117         movw    %es, %fs:2(%bx)
119 # Get disk drive parameters, specifically number of sectors/track.
121 # It seems that there is no BIOS call to get the number of sectors.
122 # Guess 36 sectors if sector 36 can be read, 18 sectors if sector 18
123 # can be read, 15 if sector 15 can be read.  Otherwise guess 9.
124 # Note that %cx = 0 from rep movsw above.
126         movw    $disksizes, %si         # table of sizes to try
127 probe_loop:
128         lodsb
129         cbtw                            # extend to word
130         movw    %ax, sectors
131         cmpw    $disksizes+4, %si
132         jae     got_sectors             # If all else fails, try 9
134         xchgw   %cx, %ax                # %cx = track and sector
135         xorw    %dx, %dx                # drive 0, head 0
136         movw    $0x0200, %bx            # address = 512, in INITSEG (%es = %cs)
137         movw    $0x0201, %ax            # service 2, 1 sector
138         int     $0x13
139         jc      probe_loop              # try next value
141 got_sectors:
142         movb    $0x03, %ah              # read cursor pos
143         xorb    %bh, %bh
144         int     $0x10
145         movw    $9, %cx
146         movb    $0x07, %bl              # page 0, attribute 7 (normal)
147                                         # %bh is set above; int10 doesn't
148                                         # modify it
149         movw    $msg1, %bp
150         movw    $0x1301, %ax            # write string, move cursor
151         int     $0x10                   # tell the user we're loading..
153 # Load the setup-sectors directly after the moved bootblock (at 0x90200).
154 # We should know the drive geometry to do it, as setup may exceed first
155 # cylinder (for 9-sector 360K and 720K floppies).
157         movw    $0x0001, %ax            # set sread (sector-to-read) to 1 as
158         movw    $sread, %si             # the boot sector has already been read
159         movw    %ax, (%si)
161         xorw    %ax, %ax                # reset FDC
162         xorb    %dl, %dl
163         int     $0x13
164         movw    $0x0200, %bx            # address = 512, in INITSEG
165 next_step:
166         movb    setup_sects, %al
167         movw    sectors, %cx
168         subw    (%si), %cx              # (%si) = sread
169         cmpb    %cl, %al
170         jbe     no_cyl_crossing
171         movw    sectors, %ax
172         subw    (%si), %ax              # (%si) = sread
173 no_cyl_crossing:
174         call    read_track
175         pushw   %ax                     # save it
176         call    set_next                # set %bx properly; it uses %ax,%cx,%dx
177         popw    %ax                     # restore
178         subb    %al, setup_sects        # rest - for next step
179         jnz     next_step
181         pushw   $SYSSEG
182         popw    %es                     # %es = SYSSEG
183         call    read_it
184         call    kill_motor
185         call    print_nl
187 # After that we check which root-device to use. If the device is
188 # defined (!= 0), nothing is done and the given device is used.
189 # Otherwise, one of /dev/fd0H2880 (2,32) or /dev/PS0 (2,28) or /dev/at0 (2,8)
190 # depending on the number of sectors we pretend to know we have.
192 # Segments are as follows: %cs = %ds = %ss = INITSEG,
193 #       %es = SYSSEG, %fs = 0, %gs is unused.
195         movw    root_dev, %ax
196         orw     %ax, %ax
197         jne     root_defined
199         movw    sectors, %bx
200         movw    $0x0208, %ax            # /dev/ps0 - 1.2Mb
201         cmpw    $15, %bx
202         je      root_defined
204         movb    $0x1c, %al              # /dev/PS0 - 1.44Mb
205         cmpw    $18, %bx
206         je      root_defined
208         movb    $0x20, %al              # /dev/fd0H2880 - 2.88Mb
209         cmpw    $36, %bx
210         je      root_defined
212         movb    $0, %al                 # /dev/fd0 - autodetect
213 root_defined:
214         movw    %ax, root_dev
216 # After that (everything loaded), we jump to the setup-routine
217 # loaded directly after the bootblock:
219         ljmp    $SETUPSEG, $0
221 # These variables are addressed via %si register as it gives shorter code.
223 sread:  .word 0                         # sectors read of current track
224 head:   .word 0                         # current head
225 track:  .word 0                         # current track
227 # This routine loads the system at address SYSSEG, making sure
228 # no 64kB boundaries are crossed. We try to load it as fast as
229 # possible, loading whole tracks whenever we can.
231 read_it:
232         movw    %es, %ax                # %es = SYSSEG when called
233         testw   $0x0fff, %ax
234 die:    jne     die                     # %es must be at 64kB boundary
235         xorw    %bx, %bx                # %bx is starting address within segment
236 rp_read:
237 #ifdef __BIG_KERNEL__
238                                         # look in setup.S for bootsect_kludge
239         bootsect_kludge = 0x220         # 0x200 + 0x20 which is the size of the
240         lcall   bootsect_kludge         # bootsector + bootsect_kludge offset
241 #else
242         movw    %es, %ax
243         subw    $SYSSEG, %ax
244         movw    %bx, %cx
245         shr     $4, %cx
246         add     %cx, %ax                # check offset
247 #endif
248         cmpw    syssize, %ax            # have we loaded everything yet?
249         jbe     ok1_read
251         ret
253 ok1_read:
254         movw    sectors, %ax
255         subw    (%si), %ax              # (%si) = sread
256         movw    %ax, %cx
257         shlw    $9, %cx
258         addw    %bx, %cx
259         jnc     ok2_read
261         je      ok2_read
263         xorw    %ax, %ax
264         subw    %bx, %ax
265         shrw    $9, %ax
266 ok2_read:
267         call    read_track
268         call    set_next
269         jmp     rp_read
271 read_track:
272         pusha
273         pusha   
274         movw    $0xe2e, %ax             # loading... message 2e = .
275         movw    $7, %bx
276         int     $0x10
277         popa            
279 # Accessing head, track, sread via %si gives shorter code.
281         movw    4(%si), %dx             # 4(%si) = track
282         movw    (%si), %cx              # (%si)  = sread
283         incw    %cx
284         movb    %dl, %ch
285         movw    2(%si), %dx             # 2(%si) = head
286         movb    %dl, %dh
287         andw    $0x0100, %dx
288         movb    $2, %ah
289         pushw   %dx                     # save for error dump
290         pushw   %cx
291         pushw   %bx
292         pushw   %ax
293         int     $0x13
294         jc      bad_rt
296         addw    $8, %sp
297         popa
298         ret
300 set_next:
301         movw    %ax, %cx
302         addw    (%si), %ax              # (%si) = sread
303         cmp     sectors, %ax
304         jne     ok3_set
305         movw    $0x0001, %ax
306         xorw    %ax, 2(%si)             # change head
307         jne     ok4_set
308         incw    4(%si)                  # next track
309 ok4_set:
310         xorw    %ax, %ax
311 ok3_set:
312         movw    %ax, (%si)              # set sread
313         shlw    $9, %cx
314         addw    %cx, %bx
315         jnc     set_next_fin
316         movw    %es, %ax
317         addb    $0x10, %ah
318         movw    %ax, %es
319         xorw    %bx, %bx
320 set_next_fin:
321         ret
323 bad_rt:
324         pushw   %ax                     # save error code
325         call    print_all               # %ah = error, %al = read
326         xorb    %ah, %ah
327         xorb    %dl, %dl
328         int     $0x13
329         addw    $10, %sp
330         popa
331         jmp read_track
333 # print_all is for debugging purposes.  
335 # it will print out all of the registers.  The assumption is that this is
336 # called from a routine, with a stack frame like
338 #       %dx 
339 #       %cx
340 #       %bx
341 #       %ax
342 #       (error)
343 #       ret <- %sp
345 print_all:
346         movw    $5, %cx                 # error code + 4 registers
347         movw    %sp, %bp
348 print_loop:
349         pushw   %cx                     # save count remaining
350         call    print_nl                # <-- for readability
351         cmpb    $5, %cl
352         jae     no_reg                  # see if register name is needed
353         
354         movw    $0xe05 + 'A' - 1, %ax
355         subb    %cl, %al
356         int     $0x10
357         movb    $'X', %al
358         int     $0x10
359         movb    $':', %al
360         int     $0x10
361 no_reg:
362         addw    $2, %bp                 # next register
363         call    print_hex               # print it
364         popw    %cx
365         loop    print_loop
366         ret
368 print_nl:
369         movw    $0xe0d, %ax             # CR
370         int     $0x10
371         movb    $0xa, %al               # LF
372         int     $0x10
373         ret
375 # print_hex is for debugging purposes, and prints the word
376 # pointed to by %ss:%bp in hexadecimal.
378 print_hex:
379         movw    $4, %cx                 # 4 hex digits
380         movw    (%bp), %dx              # load word into %dx
381 print_digit:
382         rolw    $4, %dx                 # rotate to use low 4 bits
383         movw    $0xe0f, %ax             # %ah = request
384         andb    %dl, %al                # %al = mask for nybble
385         addb    $0x90, %al              # convert %al to ascii hex
386         daa                             # in only four instructions!
387         adc     $0x40, %al
388         daa
389         int     $0x10
390         loop    print_digit
391         ret
393 # This procedure turns off the floppy drive motor, so
394 # that we enter the kernel in a known state, and
395 # don't have to worry about it later.
396 # NOTE: Doesn't save %ax or %dx; do it yourself if you need to.
398 kill_motor:
399 #if 1
400         xorw    %ax, %ax                # reset FDC
401         xorb    %dl, %dl
402         int     $0x13
403 #else
404         movw    $0x3f2, %dx
405         xorb    %al, %al
406         outb    %al, %dx
407 #endif
408         ret
410 sectors:        .word 0
411 disksizes:      .byte 36, 18, 15, 9
412 msg1:           .byte 13, 10
413                 .ascii "Loading"
415 # XXX: This is a fairly snug fit.
417 .org 497
418 setup_sects:    .byte SETUPSECTS
419 root_flags:     .word ROOT_RDONLY
420 syssize:        .word SYSSIZE
421 swap_dev:       .word SWAP_DEV
422 ram_size:       .word RAMDISK
423 vid_mode:       .word SVGA_MODE
424 root_dev:       .word ROOT_DEV
425 boot_flag:      .word 0xAA55