Initial import from FreeBSD RELENG_4:
[dragonfly.git] / sys / platform / pc32 / boot / biosboot / start.S
1 /*
2  * Mach Operating System
3  * Copyright (c) 1992, 1991 Carnegie Mellon University
4  * All Rights Reserved.
5  * 
6  * Permission to use, copy, modify and distribute this software and its
7  * documentation is hereby granted, provided that both the copyright
8  * notice and this permission notice appear in all copies of the
9  * software, derivative works or modified versions, and any portions
10  * thereof, and that both notices appear in supporting documentation.
11  * 
12  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
13  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
14  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
15  * 
16  * Carnegie Mellon requests users of this software to return to
17  * 
18  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
19  *  School of Computer Science
20  *  Carnegie Mellon University
21  *  Pittsburgh PA 15213-3890
22  * 
23  * any improvements or extensions that they make and grant Carnegie Mellon
24  * the rights to redistribute these changes.
25  *
26  *      from: Mach, Revision 2.2  92/04/04  11:36:29  rpd
27  * $FreeBSD: src/sys/i386/boot/biosboot/start.S,v 1.13 1999/08/28 00:43:14 peter Exp $
28  */
29
30 /*
31   Copyright 1988, 1989, 1990, 1991, 1992 
32    by Intel Corporation, Santa Clara, California.
33
34                 All Rights Reserved
35
36 Permission to use, copy, modify, and distribute this software and
37 its documentation for any purpose and without fee is hereby
38 granted, provided that the above copyright notice appears in all
39 copies and that both the copyright notice and this permission notice
40 appear in supporting documentation, and that the name of Intel
41 not be used in advertising or publicity pertaining to distribution
42 of the software without specific, written prior permission.
43
44 INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
45 INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
46 IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
47 CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
48 LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
49 NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
50 WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
51 */
52 #include        "asm.h"
53
54         .file   "start.S"
55
56 SIGNATURE=      0xaa55
57 LOADSZ=         15      /* size of unix boot */
58 PARTSTART=      0x1be   /* starting address of partition table */
59 NUMPART=        4       /* number of partitions in partition table */
60 PARTSZ=         16      /* each partition table entry is 16 bytes */
61 BSDPART=        0xA5    /* value of boot_ind, means bootable partition */
62 BOOTABLE=       0x80    /* value of boot_ind, means bootable partition */
63 NAMEBLOCKMAGIC= 0xfadefeed /* value of magicnumebr for block2   */
64
65 /*
66  * This DEBUGMSG(msg) macro may be useful for debugging.  Its use is
67  * restricted to this file since it only works in real mode.
68  */
69 #define DEBUGMSG(msg)           \
70         data32                  ; \
71         mov     $msg, %esi      ; \
72         data32                  ; \
73         call    message
74
75         .text   
76
77 ENTRY(boot1)
78
79         /*
80          * XXX I have encountered at least one machine (a no-name laptop
81          * with an AMI WinBIOS) that will refuse to run the bootblock
82          * unless this short jump and nop are here. I'm not certain, but
83          * this may be a case of the BIOS performing some kind of simple
84          * virus detection.
85          */
86         jmp pacify_braindead_bios
87         nop
88 pacify_braindead_bios:
89
90         /*
91          * start (aka boot1) is loaded at 0x0:0x7c00 but we want 0x7c0:0
92          * ljmp to the next instruction to adjust %cs
93          */
94         data32
95         ljmp $0x7c0, $start
96
97 start:
98         /* set up %ds */
99         mov     %cs, %ax
100         mov     %ax, %ds
101
102         /* set up %ss and %esp */
103         data32
104         mov     $BOOTSEG, %eax
105         mov     %ax, %ss
106         /*
107          * make a little room on the stack for
108          * us to save the default bootstring we might find..
109          * effectively, we push the bootstring.
110          */
111         data32
112         mov     $BOOTSTACK-64, %esp
113
114         /* set up %es, (where we will load boot2 to) */
115         mov     %ax, %es
116
117
118         /* bootstrap passes us drive number in %dl */
119         cmpb    $0x80, %dl
120         data32
121         jae     hd
122
123 fd:
124         /*
125          * XXX some bootstraps don't pass the drive number in %dl.
126          * This is a problem mainly when we are block 0 on a floppy.
127          * Force drive 0 for floppies.
128          * XXX %dl was assumed valid in the test that led here.
129          */
130         mov     $0x0, %dl
131
132         /* reset the disk system */
133         movb    $0x0, %ah
134         int     $0x13
135         data32
136         mov     $0x0001, %ecx   /* cyl 0, sector 1 */
137         movb    $0, %dh         /* head */
138         data32
139         jmp     load
140
141 hd:     /**** load sector 0 into the BOOTSEG ****/
142         data32
143         mov     $0x0201, %eax
144         xor     %ebx, %ebx      /* %bx = 0 */
145         data32
146         mov     $0x0001, %ecx
147         data32
148         andl    $0xff, %edx
149         /*mov   $0x0080, %edx*/
150         int     $0x13
151         data32
152         jb      read_error
153
154         /* find the first 386BSD partition */
155         data32
156         mov     $PARTSTART, %ebx
157         data32
158         mov     $NUMPART, %ecx
159 again:
160         addr32
161         movb    %es:4(%ebx), %al
162         cmpb    $BSDPART, %al
163         data32
164         je      found
165         data32
166         add     $PARTSZ, %ebx
167         data32
168         loop    again
169         data32
170         mov     $enoboot, %esi
171         data32
172         jmp     err_stop
173
174
175 /*
176  * BIOS call "INT 0x13 Function 0x2" to read sectors from disk into memory
177  *      Call with       %ah = 0x2
178  *                      %al = number of sectors
179  *                      %ch = cylinder
180  *                      %cl = sector
181  *                      %dh = head
182  *                      %dl = drive (0x80 for hard disk, 0x0 for floppy disk)
183  *                      %es:%bx = segment:offset of buffer
184  *      Return:
185  *                      %al = 0x0 on success; err code on failure
186  */
187
188 found:
189         addr32
190         movb    %es:1(%ebx), %dh /* head */
191         addr32
192         movl    %es:2(%ebx), %ecx /*sect, cyl (+ 2 bytes junk in top word) */
193
194 load:
195 #ifdef NAMEBLOCK
196 /*
197  * Load the second sector and see if it is a boot instruction block.
198  * If it is then scan the contents for the first valid string and copy it to 
199  * the location of the default boot string.. then zero it out.
200  * Finally write the block back to disk with the zero'd out entry..
201  * I hate writing at this stage but we need this to be persistant.
202  * If the boot fails, then the next boot will get the next string.
203  * /etc/rc will regenerate a complete block2 iff the boot succeeds.
204  *
205  * Format of block 2 is:
206  * [NAMEBLOCKMAGIC] <--0xdeafc0de
207  * [nulls]
208  * [bootstring]NULL  <---e.g. 0:wd(0,a)/kernel.experimental
209  * [bootstring]NULL  <---e.g. 0:wd(0,a)/kernel.old
210  * ....
211  * [bootstring]NULL  <---e.g. 0:wd(0,f)/kernel
212  * FF FF FF
213  */
214 where:
215         /*
216          * save things we might smash
217          * (that are not smashed immedatly after us anyway.)
218          */
219         data32
220         push    %ecx    /* preserve 'cyl,sector ' */
221         data32
222         push    %edx
223 /* 
224  * Load the second sector
225  * BIOS call "INT 0x13 Function 0x2" to read sectors from disk into memory
226  *      Call with       %ah = 0x2
227  *                      %al = number of sectors
228  *                      %ch = cylinder
229  *                      %cl = sector
230  *                      %dh = head
231  *                      %dl = drive (0x80 for hard disk, 0x0 for floppy disk)
232  *                      %es:%bx = segment:offset of buffer
233  *      Return:
234  *                      %al = 0x0 on success; err code on failure
235  */
236         data32
237         movl    $0x0201, %eax   /function 2 (read) 1 sector */
238         xor     %ebx, %ebx      /* %bx = 0 */ /* buffer address (ES:0) */
239         data32
240         movl    $0x0002, %ecx   /* sector 2, cylinder 0 */
241         data32
242         andl    $0x00ff, %edx   /* head 0, drive N */
243         int     $0x13
244         data32
245         jb      read_error
246         /*
247          * confirm that it is one for us
248          */
249         data32
250         xorl    %ebx, %ebx      /* magic number at start of buffer */
251         data32
252         addr32
253         movl    %es:(%ebx), %eax
254         data32
255         cmpl    $NAMEBLOCKMAGIC, %eax
256         data32
257         jne     notours         /* not ours so return to caller */
258         /*
259          * scan for a bootstring
260          * Skip the magic number, and scan till we find a non-null,
261          * or a -1
262          */
263         incl    %ebx    /* quicker and smaller */
264         incl    %ebx
265         incl    %ebx
266 scan:
267         incl    %ebx
268         addr32
269         movb    %es:(%ebx), %al /* load the next byte */
270         testb   %al, %al        /* and if it is null */
271         data32                  /* keep scanning (past deleted entries) */
272         jz scan
273         incb    %al             /* now look for -1 */
274         data32
275         jz      notours         /* if we reach the 0xFF then we have finished */
276
277         /*
278          * save our settings.. we need them twice..
279          */
280         data32
281         push    %ebx
282         /*
283          * copy it to the default string location
284          * which is just above the stack for 64 bytes.
285          */
286         data32
287         movl    $BOOTSTACK-64, %ecx     /* 64 bytes at the top of the stack */
288 nxtbyte:
289         addr32
290         movb    %es:(%ebx), %al /* get the next byte in */
291         addr32
292         movb    %al, %es:(%ecx) /* and transfer it to the name buffer */
293         incl    %ebx            /* get on with the next byte */
294         incl    %ecx            /* get on with the next byte */
295         testb   %al, %al        /* if it was 0 then quit this */
296         data32
297         jnz nxtbyte             /* and looop if more to do */ 
298         
299         /*
300          * restore the saved settings and
301          * zero it out so next time we don't try it again
302          */
303         data32
304         pop     %ebx            /* get back our starting location */
305 #ifdef  NAMEBLOCK_WRITEBACK
306 nxtbyte2:
307         addr32
308         movb    %es:(%ebx), %al /* get the byte */
309         addr32
310         movb    $0,  %es:(%ebx) /* zero it out */
311         data32
312         incl    %ebx            /* point to the next byte */
313         testb   %al, %al        /* check if we have finished.. */
314         data32
315         jne nxtbyte2
316 /* 
317  * Write the second sector back
318  * Load the second sector
319  * BIOS call "INT 0x13 Function 0x3" to write sectors from memory to disk
320  *      Call with       %ah = 0x3
321  *                      %al = number of sectors
322  *                      %ch = cylinder
323  *                      %cl = sector
324  *                      %dh = head
325  *                      %dl = drive (0x80 for hard disk, 0x0 for floppy disk)
326  *                      %es:%bx = segment:offset of buffer
327  *      Return:
328  *                      %al = 0x0 on success; err code on failure
329  */
330         data32
331         movl    $0x0301, %eax   /* write 1 sector */
332         xor     %ebx, %ebx      /* buffer is at offset 0 */ 
333         data32
334         movl    $0x0002, %ecx   /* block 2 */
335         data32
336         andl    $0xff, %edx     /* head 0 */
337         int     $0x13
338         data32
339         jnb     notours
340         data32
341         mov     $eread, %esi
342         jmp     err_stop
343 #endif  /* NAMEBLOCK_WRITEBACK */
344         /*
345          * return to the main-line
346          */
347 notours:
348         data32
349         pop     %edx
350         data32
351         pop     %ecx
352 #endif
353         movb    $0x2, %ah       /* function 2 */
354         movb    $LOADSZ, %al    /* number of blocks */
355         xor     %ebx, %ebx      /* %bx = 0, put it at 0 in the BOOTSEG */
356         int     $0x13
357         data32
358         jb      read_error
359
360         /*
361          * ljmp to the second stage boot loader (boot2).
362          * After ljmp, %cs is BOOTSEG and boot1 (512 bytes) will be used
363          * as an internal buffer "intbuf".
364          */
365
366         data32
367         ljmp    $BOOTSEG, $ EXT(boot2)
368
369 /*
370  * read_error
371  */
372 read_error:
373         data32
374         mov     $eread, %esi
375 err_stop:
376         data32
377         call    message
378         data32
379         jmp     stop
380
381 /*
382  * message: write the error message in %ds:%esi to console
383  */
384 message:
385         /*
386          * Use BIOS "int 10H Function 0Eh" to write character in teletype mode
387          *      %ah = 0xe       %al = character
388          *      %bh = page      %bl = foreground color (graphics modes)
389          */
390
391         data32
392         push    %eax
393         data32
394         push    %ebx
395         data32
396         mov     $0x0001, %ebx
397         cld
398
399 nextb:
400         lodsb                   /* load a byte into %al */
401         cmpb    $0x0, %al
402         data32
403         je      done
404         movb    $0xe, %ah
405         int     $0x10           /* display a byte */
406         data32
407         jmp     nextb
408 done:
409         data32
410         pop     %ebx
411         data32
412         pop     %eax
413         data32
414         ret
415
416 stop:   hlt
417         data32
418         jmp     stop            /* halt doesnt actually halt forever */
419
420 /* error messages */
421
422
423 #ifdef  DEBUG
424 one:    String          "1-\0"
425 two:    String          "2-\0"
426 three:  String          "3-\0"
427 four:   String          "4-\0"
428 #endif  DEBUG
429 #ifdef  NAMEBLOCK_WRITEBACK
430 ewrite: String          "Write error\r\n\0"
431 #endif  /* NAMEBLOCK_WRITEBACK */
432 eread:  String          "Read error\r\n\0"
433 enoboot: String         "No bootable partition\r\n\0"
434 endofcode:
435 /*
436  * Dummy partition table in case we are block 0.  The ending c/h/s values
437  * of the non-null partition are almost arbitary.  The length of this
438  * partition is bogus for backwards compatibility and as a signature.
439  * A real partition table shouldn't be as weird and broken as this one,
440  * and the isa slice initialization routine interprets this table as
441  * saying that the whole disk is used for FreeBSD.
442  */
443 /* flag, head, sec, cyl, typ, ehead, esect, ecyl, start, len */
444         . = EXT(boot1) + PARTSTART
445 strttbl:
446         .byte 0x0,0,0,0,0,0,0,0
447         .long 0,0
448         .byte 0x0,0,0,0,0,0,0,0
449         .long 0,0
450         .byte 0x0,0,0,0,0,0,0,0
451         .long 0,0
452         .byte BOOTABLE,0,1,0,BSDPART,255,255,255
453         .long 0,50000
454 /* the last 2 bytes in the sector 0 contain the signature */
455         . = EXT(boot1) + 0x1fe
456         .value  SIGNATURE
457 ENTRY(disklabel)
458         . = EXT(boot1) + 0x400