[PATCH] x86_64 merge: arch + asm
[opensuse:kernel.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         call    kill_motor              # reset FDC
162         movw    $0x0200, %bx            # address = 512, in INITSEG
163 next_step:
164         movb    setup_sects, %al
165         movw    sectors, %cx
166         subw    (%si), %cx              # (%si) = sread
167         cmpb    %cl, %al
168         jbe     no_cyl_crossing
169         movw    sectors, %ax
170         subw    (%si), %ax              # (%si) = sread
171 no_cyl_crossing:
172         call    read_track
173         pushw   %ax                     # save it
174         call    set_next                # set %bx properly; it uses %ax,%cx,%dx
175         popw    %ax                     # restore
176         subb    %al, setup_sects        # rest - for next step
177         jnz     next_step
178
179         pushw   $SYSSEG
180         popw    %es                     # %es = SYSSEG
181         call    read_it
182         call    kill_motor
183         call    print_nl
184
185 # After that we check which root-device to use. If the device is
186 # defined (!= 0), nothing is done and the given device is used.
187 # Otherwise, one of /dev/fd0H2880 (2,32) or /dev/PS0 (2,28) or /dev/at0 (2,8)
188 # depending on the number of sectors we pretend to know we have.
189
190 # Segments are as follows: %cs = %ds = %ss = INITSEG,
191 #       %es = SYSSEG, %fs = 0, %gs is unused.
192
193         movw    root_dev, %ax
194         orw     %ax, %ax
195         jne     root_defined
196
197         movw    sectors, %bx
198         movw    $0x0208, %ax            # /dev/ps0 - 1.2Mb
199         cmpw    $15, %bx
200         je      root_defined
201
202         movb    $0x1c, %al              # /dev/PS0 - 1.44Mb
203         cmpw    $18, %bx
204         je      root_defined
205
206         movb    $0x20, %al              # /dev/fd0H2880 - 2.88Mb
207         cmpw    $36, %bx
208         je      root_defined
209
210         movb    $0, %al                 # /dev/fd0 - autodetect
211 root_defined:
212         movw    %ax, root_dev
213
214 # After that (everything loaded), we jump to the setup-routine
215 # loaded directly after the bootblock:
216
217         ljmp    $SETUPSEG, $0
218
219 # These variables are addressed via %si register as it gives shorter code.
220
221 sread:  .word 0                         # sectors read of current track
222 head:   .word 0                         # current head
223 track:  .word 0                         # current track
224
225 # This routine loads the system at address SYSSEG, making sure
226 # no 64kB boundaries are crossed. We try to load it as fast as
227 # possible, loading whole tracks whenever we can.
228
229 read_it:
230         movw    %es, %ax                # %es = SYSSEG when called
231         testw   $0x0fff, %ax
232 die:    jne     die                     # %es must be at 64kB boundary
233         xorw    %bx, %bx                # %bx is starting address within segment
234 rp_read:
235 #ifdef __BIG_KERNEL__                   # look in setup.S for bootsect_kludge
236         bootsect_kludge = 0x220         # 0x200 + 0x20 which is the size of the
237         lcall   *bootsect_kludge        # bootsector + bootsect_kludge offset
238 #else
239         movw    %es, %ax
240         subw    $SYSSEG, %ax
241         movw    %bx, %cx
242         shr     $4, %cx
243         add     %cx, %ax                # check offset
244 #endif
245         cmpw    syssize, %ax            # have we loaded everything yet?
246         jbe     ok1_read
247
248         ret
249
250 ok1_read:
251         movw    sectors, %ax
252         subw    (%si), %ax              # (%si) = sread
253         movw    %ax, %cx
254         shlw    $9, %cx
255         addw    %bx, %cx
256         jnc     ok2_read
257
258         je      ok2_read
259
260         xorw    %ax, %ax
261         subw    %bx, %ax
262         shrw    $9, %ax
263 ok2_read:
264         call    read_track
265         call    set_next
266         jmp     rp_read
267
268 read_track:
269         pusha
270         pusha   
271         movw    $0xe2e, %ax             # loading... message 2e = .
272         movw    $7, %bx
273         int     $0x10
274         popa            
275
276 # Accessing head, track, sread via %si gives shorter code.
277
278         movw    4(%si), %dx             # 4(%si) = track
279         movw    (%si), %cx              # (%si)  = sread
280         incw    %cx
281         movb    %dl, %ch
282         movw    2(%si), %dx             # 2(%si) = head
283         movb    %dl, %dh
284         andw    $0x0100, %dx
285         movb    $2, %ah
286         pushw   %dx                     # save for error dump
287         pushw   %cx
288         pushw   %bx
289         pushw   %ax
290         int     $0x13
291         jc      bad_rt
292
293         addw    $8, %sp
294         popa
295         ret
296
297 set_next:
298         movw    %ax, %cx
299         addw    (%si), %ax              # (%si) = sread
300         cmp     sectors, %ax
301         jne     ok3_set
302         movw    $0x0001, %ax
303         xorw    %ax, 2(%si)             # change head
304         jne     ok4_set
305         incw    4(%si)                  # next track
306 ok4_set:
307         xorw    %ax, %ax
308 ok3_set:
309         movw    %ax, (%si)              # set sread
310         shlw    $9, %cx
311         addw    %cx, %bx
312         jnc     set_next_fin
313         movw    %es, %ax
314         addb    $0x10, %ah
315         movw    %ax, %es
316         xorw    %bx, %bx
317 set_next_fin:
318         ret
319
320 bad_rt:
321         pushw   %ax                     # save error code
322         call    print_all               # %ah = error, %al = read
323         xorb    %ah, %ah
324         xorb    %dl, %dl
325         int     $0x13
326         addw    $10, %sp
327         popa
328         jmp read_track
329
330 # print_all is for debugging purposes.  
331 #
332 # it will print out all of the registers.  The assumption is that this is
333 # called from a routine, with a stack frame like
334 #
335 #       %dx 
336 #       %cx
337 #       %bx
338 #       %ax
339 #       (error)
340 #       ret <- %sp
341  
342 print_all:
343         movw    $5, %cx                 # error code + 4 registers
344         movw    %sp, %bp
345 print_loop:
346         pushw   %cx                     # save count remaining
347         call    print_nl                # <-- for readability
348         cmpb    $5, %cl
349         jae     no_reg                  # see if register name is needed
350         
351         movw    $0xe05 + 'A' - 1, %ax
352         subb    %cl, %al
353         int     $0x10
354         movb    $'X', %al
355         int     $0x10
356         movb    $':', %al
357         int     $0x10
358 no_reg:
359         addw    $2, %bp                 # next register
360         call    print_hex               # print it
361         popw    %cx
362         loop    print_loop
363         ret
364
365 print_nl:
366         movw    $0xe0d, %ax             # CR
367         int     $0x10
368         movb    $0xa, %al               # LF
369         int     $0x10
370         ret
371
372 # print_hex is for debugging purposes, and prints the word
373 # pointed to by %ss:%bp in hexadecimal.
374
375 print_hex:
376         movw    $4, %cx                 # 4 hex digits
377         movw    (%bp), %dx              # load word into %dx
378 print_digit:
379         rolw    $4, %dx                 # rotate to use low 4 bits
380         movw    $0xe0f, %ax             # %ah = request
381         andb    %dl, %al                # %al = mask for nybble
382         addb    $0x90, %al              # convert %al to ascii hex
383         daa                             # in only four instructions!
384         adc     $0x40, %al
385         daa
386         int     $0x10
387         loop    print_digit
388         ret
389
390 # This procedure turns off the floppy drive motor, so
391 # that we enter the kernel in a known state, and
392 # don't have to worry about it later.
393 # NOTE: Doesn't save %ax or %dx; do it yourself if you need to.
394
395 kill_motor:
396         movw    $0x3f2, %dx
397         xorb    %al, %al
398         outb    %al, %dx
399         ret
400
401 sectors:        .word 0
402 disksizes:      .byte 36, 18, 15, 9
403 msg1:           .byte 13, 10
404                 .ascii "Loading"
405
406 # XXX: This is a fairly snug fit.
407
408 .org 497
409 setup_sects:    .byte SETUPSECTS
410 root_flags:     .word ROOT_RDONLY
411 syssize:        .word SYSSIZE
412 swap_dev:       .word SWAP_DEV
413 ram_size:       .word RAMDISK
414 vid_mode:       .word SVGA_MODE
415 root_dev:       .word ROOT_DEV
416 boot_flag:      .word 0xAA55