ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / arch / i386 / boot98 / bootsect.S
1 /*      
2  *      bootsect.S - boot sector for NEC PC-9800 series
3  *
4  *      Linux/98 project at Kyoto University Microcomputer Club (KMC)
5  *                  FUJITA Norimasa, TAKAI Kousuke  1997-1998
6  *      rewritten by TAKAI Kousuke (as86 -> gas), Nov 1999
7  *
8  * Based on:
9  *      bootsect.S              Copyright (C) 1991, 1992 Linus Torvalds
10  *      modified by Drew Eckhardt
11  *      modified by Bruce Evans (bde)
12  *
13  * bootsect.S is loaded at 0x1FC00 or 0x1FE00 by the bios-startup routines,
14  * and moves itself out of the way to address 0x90000, and jumps there.
15  *
16  * It then loads 'setup' directly after itself (0x90200), and the system
17  * at 0x10000, using BIOS interrupts. 
18  *
19  * NOTE! currently system is at most (8*65536-4096) bytes long. This should 
20  * be no problem, even in the future. I want to keep it simple. This 508 kB
21  * kernel size should be enough, especially as this doesn't contain the
22  * buffer cache as in minix (and especially now that the kernel is 
23  * compressed :-)
24  *
25  * The loader has been made as simple as possible, and continuous
26  * read errors will result in a unbreakable loop. Reboot by hand. It
27  * loads pretty fast by getting whole tracks at a time whenever possible.
28  */
29
30 #include <linux/config.h>               /* for CONFIG_ROOT_RDONLY */
31 #include <asm/boot.h>
32
33 SETUPSECTS      = 4                     /* default nr of setup-sectors */
34 BOOTSEG         = 0x1FC0                /* original address of boot-sector */
35 INITSEG         = DEF_INITSEG           /* we move boot here - out of the way */
36 SETUPSEG        = DEF_SETUPSEG          /* setup starts here */
37 SYSSEG          = DEF_SYSSEG            /* system loaded at 0x10000 (65536) */
38 SYSSIZE         = DEF_SYSSIZE           /* system size: # of 16-byte clicks */
39                                         /* to be loaded */
40 ROOT_DEV        = 0                     /* ROOT_DEV is now written by "build" */
41 SWAP_DEV        = 0                     /* SWAP_DEV is now written by "build" */
42
43 #ifndef SVGA_MODE
44 #define SVGA_MODE ASK_VGA
45 #endif
46
47 #ifndef RAMDISK
48 #define RAMDISK 0
49 #endif 
50
51 #ifndef ROOT_RDONLY
52 #define ROOT_RDONLY 1
53 #endif
54
55 /* normal/hireso text VRAM segments */
56 #define NORMAL_TEXT     0xa000
57 #define HIRESO_TEXT     0xe000
58
59 /* bios work area addresses */
60 #define EXPMMSZ         0x0401
61 #define BIOS_FLAG       0x0501
62 #define DISK_BOOT       0x0584
63
64 .code16
65 .text
66
67 .global _start
68 _start:
69
70 #if 0 /* hook for debugger, harmless unless BIOS is fussy (old HP) */
71         int     $0x3
72 #endif
73         jmp     real_start
74         .ascii  "Linux 98"
75         .word   0
76 real_start:
77         xorw    %di, %di                /* %di = 0 */
78         movw    %di, %ss                /* %ss = 0 */
79         movw    $0x03F0, %sp
80         pushw   %cx                     /* for hint */
81
82         movw    $0x0A00, %ax            /* normal mode defaults (80x25) */
83
84         testb   $0x08, %ss:BIOS_FLAG    /* check hi-reso bit */
85         jnz     set_crt_mode
86 /*
87  * Hi-Reso (high-resolution) machine.
88  *
89  * Some hi-reso machines have no RAMs on bank 8/A (0x080000 - 0x0BFFFF).
90  * On such machines we get two RAM banks from top of protect menory and
91  * map them on bank 8/A.
92  * These work-around must be done before moving myself on INITSEG (0x090000-).
93  */
94         movw    $(HIRESO_TEXT >> 8), %cs:(vram + 1)     /* text VRAM segment */
95
96         /* set memory window */
97         movb    $0x08, %al
98         outb    %al, $0x91              /* map native RAM (if any) */
99         movb    $0x0A, %al
100         outb    %al, $0x93
101
102         /* check bank ram A */
103         pushw   $0xA500
104         popw    %ds
105         movw    (%di), %cx              /* %si == 0 from entry */
106         notw    %cx
107         movw    %cx, (%di)
108
109         movw    $0x43F, %dx             /* cache flush for 486 and up. */
110         movb    $0xA0, %al
111         outb    %al, %dx
112         
113         cmpw    %cx, (%di)
114         je      hireso_done
115
116         /* 
117          * Write test failed; we have no native RAM on 080000h - 0BFFFFh.
118          * Take 256KB of RAM from top of protected memory.
119          */
120         movb    %ss:EXPMMSZ, %al
121         subb    $2, %al                 /* reduce 2 x 128KB */
122         movb    %al, %ss:EXPMMSZ
123         addb    %al, %al
124         addb    $0x10, %al
125         outb    %al, $0x91
126         addb    $2, %al
127         outb    %al, $0x93
128
129 hireso_done:
130         movb    $0x10, %al              /* CRT mode 80x31, %ah still 0Ah */
131
132 set_crt_mode:
133         int     $0x18                   /* set CRT mode */
134
135         movb    $0x0C, %ah              /* turn on text displaying */
136         int     $0x18
137
138         xorw    %dx, %dx                /* position cursor to home */
139         movb    $0x13, %ah
140         int     $0x18
141
142         movb    $0x11, %ah              /* turn cursor displaying on */
143         int     $0x18
144
145         /* move 1 kilobytes from [BOOTSEG:0000h] to [INITSEG:0000h] */
146         cld
147         xorw    %si, %si
148         pushw   $INITSEG
149         popw    %es
150         movw    $512, %cx               /* %di == 0 from entry */
151         rep
152         cs
153         movsw
154
155         ljmp    $INITSEG, $go
156
157 go:
158         pushw   %cs
159         popw    %ds             /* %ds = %cs */
160
161         popw    %dx             /* %dh = saved %ch passed from BIOS */
162         movb    %ss:DISK_BOOT, %al
163         andb    $0xf0, %al      /* %al = Device Address */
164         movb    $18, %ch        /* 18 secs/track,  512 b/sec (1440 KB) */
165         cmpb    $0x30, %al
166         je      try512
167         cmpb    $0x90, %al      /* 1 MB I/F, 1 MB floppy */
168         je      try1.2M
169         cmpb    $0xf0, %al      /* 640 KB I/F, 1 MB floppy */
170         je      try1.2M
171         movb    $9, %ch         /*  9 secs/track,  512 b/sec ( 720 KB) */
172         cmpb    $0x10, %al      /* 1 MB I/F, 640 KB floppy */
173         je      try512
174         cmpb    $0x70, %al      /* 640 KB I/F, 640 KB floppy */
175         jne     error           /* unknown device? */
176
177         /* XXX: Does it make sense to support 8 secs/track, 512 b/sec 
178                 (640 KB) floppy? */
179
180 try512: movb    $2, %cl         /* 512 b/sec */
181 lasttry:call    tryload
182 /*
183  * Display error message and halt
184  */
185 error:  movw    $error_msg, %si
186         call    print
187 wait_reboot:
188         movb    $0x0, %ah
189         int     $0x18                   /* wait keyboard input */
190 1:      movb    $0, %al
191         outb    %al, $0xF0              /* reset CPU */
192         jmp     1b                      /* just in case... */
193
194 try1.2M:cmpb    $2, %dh
195         je      try2HC
196         movw    $0x0803, %cx    /*  8 secs/track, 1024 b/sec (1232 KB) */
197         call    tryload
198         movb    $15, %ch        /* 15 secs/track,  512 b/sec (1200 KB) */
199         jmp     try512
200 try2HC: movw    $0x0F02, %cx    /* 15 secs/track,  512 b/sec (1200 KB) */
201         call    tryload
202         movw    $0x0803, %cx    /*  8 secs/track, 1024 b/sec (1232 KB) */
203         jmp     lasttry
204
205 /*
206  * Try to load SETUP and SYSTEM provided geometry information in %cx.
207  * This routine *will not* return on successful load...
208  */
209 tryload:
210         movw    %cx, sectlen
211         movb    %ss:DISK_BOOT, %al
212         movb    $0x7, %ah               /* recalibrate the drive */
213         int     $0x1b
214         jc      error                   /* recalibration should succeed */
215
216         /*
217          * Load SETUP into memory. It is assumed that SETUP fits into
218          * first cylinder (2 tracks, 9KB on 2DD, 15-18KB on 2HD).
219          */
220         movb    $0, %bl
221         movb    setup_sects, %bh
222         incb    %bh
223         shlw    %bx                     /* %bx = (setup_sects + 1) * 512 */
224         movw    $128, %bp
225         shlw    %cl, %bp                /* %bp = <sector size> */
226         subw    %bp, %bx                /* length to load */
227         movw    $0x0002, %dx            /* head 0, sector 2 */
228         movb    %cl, %ch                /* `N' for sector address */
229         movb    $0, %cl                 /* cylinder 0 */
230         pushw   %cs
231         popw    %es                     /* %es = %cs (= INITSEG) */
232         movb    $0xd6, %ah              /* read, multi-track, MFM */
233         int     $0x1b                   /* load it! */
234         jc      read_error
235
236         movw    $loading_msg, %si
237         call    print
238
239         movw    $SYSSEG, %ax
240         movw    %ax, %es                /* %es = SYSSEG */
241
242 /*
243  * This routine loads the system at address 0x10000, making sure
244  * no 64kB boundaries are crossed. We try to load it as fast as
245  * possible, loading whole tracks whenever we can.
246  *
247  * in:  es - starting address segment (normally 0x1000)
248  */
249         movb    %ch, %cl
250         addb    $7, %cl                 /* %cl = log2 <sector_size> */
251         shrw    %cl, %bx                /* %bx = # of phys. sectors in SETUP */
252         addb    %bl, %dl                /* %dl = start sector # of SYSTEM */
253         decb    %dl                     /* %dl is 0-based in below loop */
254
255 rp_read_newseg:
256         xorw    %bp, %bp                /* = starting address within segment */
257 #ifdef __BIG_KERNEL__
258         bootsect_kludge = 0x220         /* 0x200 (size of bootsector) + 0x20 (offset */
259         lcall   *bootsect_kludge        /* of bootsect_kludge in setup.S */
260 #else
261         movw    %es, %ax
262         subw    $SYSSEG, %ax
263 #endif
264         cmpw    syssize, %ax
265         ja      boot                    /* done! */
266
267 rp_read:
268         movb    sectors, %al
269         addb    %al, %al
270         movb    %al, %ch                /* # of sectors on both surface */
271         subb    %dl, %al                /* # of sectors left on this track */
272         movb    $0, %ah
273         shlw    %cl, %ax                /* # of bytes left on this track */
274         movw    %ax, %bx                /* transfer length */
275         addw    %bp, %ax                /* cross 64K boundary? */
276         jnc     1f                      /* ok. */
277         jz      1f                      /* also ok. */
278         /*
279          * Oops, we are crossing 64K boundary...
280          * Adjust transfer length to make transfer fit in the boundary.
281          *
282          * Note: sector size is assumed to be a measure of 65536.
283          */
284         xorw    %bx, %bx
285         subw    %bp, %bx
286 1:      pushw   %dx
287         movw    $dot_msg, %si           /* give progress message */
288         call    print
289         xchgw   %ax, %dx
290         movb    $0, %ah
291         divb    sectors
292         xchgb   %al, %ah
293         xchgw   %ax, %dx                /* %dh = head # / %dl = sector # */
294         incb    %dl                     /* fix %dl to 1-based */
295         pushw   %cx
296         movw    cylinder, %cx
297         movb    $0xd6, %ah              /* read, multi-track, seek, MFM */
298         movb    %ss:DISK_BOOT, %al
299         int     $0x1b
300         popw    %cx
301         popw    %dx
302         jc      read_error
303         movw    %bx, %ax                /* # of bytes just read */
304         shrw    %cl, %ax                /* %ax = # of sectors just read */
305         addb    %al, %dl                /* advance sector # */
306         cmpb    %ch, %dl                /* %ch = # of sectors/cylinder */
307         jb      2f
308         incb    cylinder                /* next cylinder */
309         xorb    %dl, %dl                /* sector 0 */
310 2:      addw    %bx, %bp                /* advance offset pointer */
311         jnc     rp_read
312         /* offset pointer wrapped; advance segment pointer. */
313         movw    %es, %ax
314         addw    $0x1000, %ax
315         movw    %ax, %es
316         jmp     rp_read_newseg
317
318 read_error:
319         ret
320
321 boot:   movw    %cs, %ax                /* = INITSEG */
322         /* movw %ax, %ds */
323         movw    %ax, %ss
324         movw    $0x4000, %sp            /* 0x4000 is arbitrary value >=
325                                          * length of bootsect + length of
326                                          * setup + room for stack;
327                                          * PC-9800 never have BIOS workareas
328                                          * on high memory.
329                                          */
330 /*
331  * After that we check which root-device to use. If the device is
332  * not defined, /dev/fd0 (2, 0) will be used.
333  */
334         cmpw    $0, root_dev
335         jne     3f
336         movb    $2, root_dev+1
337 3:
338
339 /*
340  * After that (everything loaded), we jump to the setup-routine
341  * loaded directly after the bootblock:
342  */
343         ljmp    $SETUPSEG, $0
344
345 /*
346  * Subroutine for print string on console.
347  *      %cs:%si - pointer to message
348  */
349 print:
350         pushaw
351         pushw   %ds
352         pushw   %es
353         pushw   %cs
354         popw    %ds
355         lesw    curpos, %di             /* %es:%di = current text VRAM addr. */
356 1:      xorw    %ax, %ax
357         lodsb
358         testb   %al, %al
359         jz      2f                      /* end of string */
360         stosw                                   /* character code */
361         movb    $0xE1, %es:0x2000-2(%di)        /* character attribute */
362         jmp     1b
363 2:      movw    %di, %dx
364         movb    $0x13, %ah
365         int     $0x18                   /* move cursor to current point */
366         popw    %es
367         popw    %ds
368         popaw
369         ret
370
371 loading_msg:
372         .string "Loading"
373 dot_msg:
374         .string "."
375 error_msg:
376         .string "Read Error!"
377
378         .org    490
379
380 curpos: .word   160             /* current cursor position */
381 vram:   .word   NORMAL_TEXT     /* text VRAM segment */
382
383 cylinder:       .byte   0       /* current cylinder (lower byte)        */
384 sectlen:        .byte   0       /* (log2 of <sector size>) - 7          */
385 sectors:        .byte   0x0F    /* default is 2HD (15 sector/track)     */
386
387 # XXX: This is a fairly snug fit.
388
389 .org 497
390 setup_sects:    .byte SETUPSECTS
391 root_flags:     .word ROOT_RDONLY
392 syssize:        .word SYSSIZE
393 swap_dev:       .word SWAP_DEV
394 ram_size:       .word RAMDISK
395 vid_mode:       .word SVGA_MODE
396 root_dev:       .word ROOT_DEV
397 boot_flag:      .word 0xAA55