import of upstream 2.4.34.4 from kernel.org
[linux-2.4.git] / arch / x86_64 / boot / bootsect.S
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  */
31
32 #include <asm/boot.h>
33
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" */
43
44 #ifndef SVGA_MODE
45 #define SVGA_MODE ASK_VGA
46 #endif
47
48 #ifndef RAMDISK
49 #define RAMDISK 0
50 #endif
51
52 #ifndef ROOT_RDONLY
53 #define ROOT_RDONLY 1
54 #endif
55
56 .code16
57 .text
58
59 .global _start
60 _start:
61
62 # First things first. Move ourself from 0x7C00 -> 0x90000 and jump there.
63
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
75
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.
81
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.
89
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.
94 #
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.  
99 #
100 # High doesn't hurt.  Low does.
101 #
102 # Segments are as follows: %cs = %ds = %es = %ss = INITSEG, %fs = 0,
103 # and %gs is unused.
104
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)
118
119 # Get disk drive parameters, specifically number of sectors/track.
120
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.
125
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
133
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
140
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..
152
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).
156
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)
160
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
180
181         pushw   $SYSSEG
182         popw    %es                     # %es = SYSSEG
183         call    read_it
184         call    kill_motor
185         call    print_nl
186
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.
191
192 # Segments are as follows: %cs = %ds = %ss = INITSEG,
193 #       %es = SYSSEG, %fs = 0, %gs is unused.
194
195         movw    root_dev, %ax
196         orw     %ax, %ax
197         jne     root_defined
198
199         movw    sectors, %bx
200         movw    $0x0208, %ax            # /dev/ps0 - 1.2Mb
201         cmpw    $15, %bx
202         je      root_defined
203
204         movb    $0x1c, %al              # /dev/PS0 - 1.44Mb
205         cmpw    $18, %bx
206         je      root_defined
207
208         movb    $0x20, %al              # /dev/fd0H2880 - 2.88Mb
209         cmpw    $36, %bx
210         je      root_defined
211
212         movb    $0, %al                 # /dev/fd0 - autodetect
213 root_defined:
214         movw    %ax, root_dev
215
216 # After that (everything loaded), we jump to the setup-routine
217 # loaded directly after the bootblock:
218
219         ljmp    $SETUPSEG, $0
220
221 # These variables are addressed via %si register as it gives shorter code.
222
223 sread:  .word 0                         # sectors read of current track
224 head:   .word 0                         # current head
225 track:  .word 0                         # current track
226
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.
230
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
250
251         ret
252
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
260
261         je      ok2_read
262
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
270
271 read_track:
272         pusha
273         pusha   
274         movw    $0xe2e, %ax             # loading... message 2e = .
275         movw    $7, %bx
276         int     $0x10
277         popa            
278
279 # Accessing head, track, sread via %si gives shorter code.
280
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
295
296         addw    $8, %sp
297         popa
298         ret
299
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
322
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
332
333 # print_all is for debugging purposes.  
334 #
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
337 #
338 #       %dx 
339 #       %cx
340 #       %bx
341 #       %ax
342 #       (error)
343 #       ret <- %sp
344  
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
367
368 print_nl:
369         movw    $0xe0d, %ax             # CR
370         int     $0x10
371         movb    $0xa, %al               # LF
372         int     $0x10
373         ret
374
375 # print_hex is for debugging purposes, and prints the word
376 # pointed to by %ss:%bp in hexadecimal.
377
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
392
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.
397
398 kill_motor:
399         xorw    %ax, %ax                # reset FDC
400         xorb    %dl, %dl
401         int     $0x13
402         ret
403
404 sectors:        .word 0
405 disksizes:      .byte 36, 18, 15, 9
406 msg1:           .byte 13, 10
407                 .ascii "Loading"
408
409 # XXX: This is a fairly snug fit.
410
411 .org 497
412 setup_sects:    .byte SETUPSECTS
413 root_flags:     .word ROOT_RDONLY
414 syssize:        .word SYSSIZE
415 swap_dev:       .word SWAP_DEV
416 ram_size:       .word RAMDISK
417 vid_mode:       .word SVGA_MODE
418 root_dev:       .word ROOT_DEV
419 boot_flag:      .word 0xAA55