Merge from vendor branch NTPD:
[dragonfly.git] / sys / boot / pc32 / boot0 / boot0.S
1 /*
2  * Copyright (c) 2003,2004 The DragonFly Project.  All rights reserved.
3  * 
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  * 
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  * 
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  * 
34  * Copyright (c) 1998 Robert Nordier
35  * All rights reserved.
36  *
37  * Redistribution and use in source and binary forms are freely
38  * permitted provided that the above copyright notice and this
39  * paragraph and the following disclaimer are duplicated in all
40  * such forms.
41  *
42  * This software is provided "AS IS" and without any express or
43  * implied warranties, including, without limitation, the implied
44  * warranties of merchantability and fitness for a particular
45  * purpose.
46  *
47  *
48  * $FreeBSD: src/sys/boot/i386/boot0/boot0.s,v 1.26 2003/06/01 20:41:04 obrien Exp $
49  * $DragonFly: src/sys/boot/pc32/boot0/boot0.S,v 1.6 2004/07/19 23:30:31 dillon Exp $
50  */
51
52 #include "../bootasm.h"
53
54                 /*
55                  * A 512-byte boot manager.
56                  */
57                 .set PRT_OFF,0x1be              # Partition table
58
59                 .set TBL0SZ,0x3                 # Table 0 size
60                 .set TBL1SZ,0xc                 # Table 1 size
61
62                 .set MAGIC,0xaa55               # Magic: bootable
63                 .set B0MAGIC,0xbb66             # Identification
64
65                 .set KEY_ENTER,0x1c             # Enter key scan code
66                 .set KEY_F1,0x3b                # F1 key scan code
67                 .set KEY_1,0x02                 # #1 key scan code
68
69                 /*
70                  * Addresses in the sector of embedded data values.  Accessed
71                  * with negative offsets from the end of the relocated sector
72                  * (%ebp).
73                  *
74                  * Note that %ebp is the base of our variable space and 
75                  * points at the end of the sector (base + 0x200).  The
76                  * fake partition and menu option is thus stored in the
77                  * memory just after the boot0 sector.
78                  */
79                 .set _NXTDRV,-0x48              # Next drive
80                 .set _OPT,-0x47                 # Default option
81                 .set _SETDRV,-0x46              # Drive to force
82                 .set _FLAGS,-0x45               # Flags
83                 .set _TICKS,-0x44               # Timeout ticks
84                 .set _FAKE,0x0                  # Fake partition entry
85                 .set _MNUOPT,0xc                # Menu options
86
87                 .globl start                    # Entry point
88                 .code16                         # This runs in real mode
89
90                 /*
91                  * Initialise segments and registers to known values.  
92                  * segments start at 0.  The stack is immediately below the
93                  * address we were loaded to.
94                  */
95 start:          cld                             # String ops inc
96                 xorw %ax,%ax                    # Zero
97                 movw %ax,%es                    # Address
98                 movw %ax,%ds                    #  data
99                 movw %ax,%ss                    # Set up
100                 movw $MEM_BIOS_LADDR,%sp        #  stack
101         
102                 /*
103                  * Copy this code to the address it was linked for
104                  */
105                 movw %sp,%si                    # Source
106                 movw $start,%di                 # Destination
107                 movw $0x100,%cx                 # Word count
108                 rep                             # Relocate
109                 movsw                           #  code
110
111                 /*
112                  * Set address for variable space beyond code, and clear it.
113                  * Notice that this is also used to point to the values 
114                  * embedded in the block, by using negative offsets.
115                  */
116                 movw %di,%bp                    # Address variables
117                 movb $0x8,%cl                   # Words to clear
118                 rep                             # Zero
119                 stosw                           #  them
120
121                 /*
122                  * Relocate to the new copy of the code.  Do not make
123                  * assumptions with regard to a relative-PC near jump
124                  * capability.
125                  */
126                 incb -0xe(%di)                  # Sector number
127                 pushw $main                     # Jump to relocated code
128                 retw
129
130                 /*
131                  * Check what flags were loaded with us, specifically, Use a
132                  * predefined Drive.  If what the bios gives us is bad, use
133                  * the '0' in the block instead, as well.
134                  */
135 main:           testb $0x20,_FLAGS(%bp)         # Set number drive?
136                 jnz main.1                      # Yes
137                 testb %dl,%dl                   # Drive number valid?
138                 js main.2                       # Possibly (0x80 set)
139 main.1:         movb _SETDRV(%bp),%dl           # Drive number to use
140
141                 /*
142                  * Whatever we decided to use, now store it into the fake
143                  * partition entry that lives in the data space above us.
144                  */
145 main.2:         movb %dl,_FAKE(%bp)             # Save drive number
146                 callw putn                      # To new line
147                 pushw %dx                       # Save drive number
148
149                 /*
150                  * Start out with a pointer to the 4th byte of the first
151                  * table entry so that after 4 iterations it's beyond the
152                  * end of the sector.  and beyond a 256 byte boundary and
153                  * has overflowed 8 bits (see next comment).  (remember
154                  * that the table starts 2 bytes earlier than you would
155                  * expect as the bootable flag is after it in the block)
156                  */
157                 movw $(partbl+0x4),%bx          # Partition table (+4)
158                 xorw %dx,%dx                    # Item number
159
160                 /*
161                  * Loop around on the partition table, printing values until
162                  * we pass a 256 byte boundary. The end of loop test is at
163                  * main.5.
164                  */
165 main.3:         movb %ch,-0x4(%bx)              # Zero active flag (ch == 0)
166                 btw %dx,_FLAGS(%bp)             # Entry enabled?
167                 jnc main.5                      # No
168
169                 /*
170                  * If any of the entries in the table are the same as the
171                  * 'type' in the slice table entry, then this is an empty
172                  * or non bootable partition. Skip it.
173                  */
174                 movb (%bx),%al                  # Load type
175                 movw $tables,%di                # Lookup tables
176                 movb $TBL0SZ,%cl                # Number of entries
177                 repne                           # Exclude
178                 scasb                           #  partition?
179                 je main.5                       # Yes
180
181                 /*
182                  * Now scan the table of known types
183                  */
184                 movb $TBL1SZ,%cl                # Number of entries
185                 repne                           # Known
186                 scasb                           #  type?
187                 jne main.4                      # No
188
189                 /*
190                  * If it matches get the matching element in the
191                  * next array. if it doesn't, we are already
192                  * pointing at its first element which points to a "?".
193                  */
194                 addw $TBL1SZ,%di                # Adjust
195 main.4:         movb (%di),%cl                  # Partition
196                 addw %cx,%di                    #  description
197                 callw putx                      # Display it
198 main.5:         incw %dx                        # Next item 
199                 addb $0x10,%bl                  # Next entry
200                 jnc main.3                      # Till done
201
202                 /*
203                  * Passed a 256 byte boundary..
204                  * table is finished.
205                  * Add one to the drive number and check it is valid, 
206                  */
207                 popw %ax                        # Drive number
208                 subb $0x80-0x1,%al              # Does next
209                 cmpb BDA_NHRDRV,%al             #  drive exist? (from BIOS?)
210                 jb main.6                       # Yes
211
212                 /*
213                  * If not then if there is only one drive,
214                  * Don't display drive as an option.
215                  */
216                 decw %ax                        # Already drive 0?
217                 jz main.7                       # Yes
218
219                 /*
220                  * If it was illegal or we cycled through them,
221                  * then go back to drive 0.
222                  */
223                 xorb %al,%al                    # Drive 0
224
225                 /*
226                  * Whatever drive we selected, make it an ascii digit and
227                  * save it back to the "next drive" location in the loaded
228                  * block in case we want to save it for next time.  This also
229                  * is part of the printed drive string so add 0x80 to indicate
230                  * end of string.
231                  */
232 main.6:         addb $'0'|0x80,%al              # Save next
233                 movb %al,_NXTDRV(%bp)           #  drive number
234                 movw $drive,%di                 # Display
235                 callw putx                      #  item
236
237                 /*
238                  * Now that we've printed the drive (if we needed to), 
239                  * display a prompt.  Get ready for the input by noting the
240                  * time.
241                  */
242 main.7:         movw $prompt,%si                # Display
243                 callw putstr                    #  prompt
244                 movb _OPT(%bp),%dl              # Display
245                 decw %si                        #  default
246                 callw putkey                    #  key
247                 xorb %ah,%ah                    # BIOS: Get
248                 int $0x1a                       #  system time
249                 movw %dx,%di                    # Ticks when
250                 addw _TICKS(%bp),%di            #  timeout
251
252                 /* 
253                  * Busy loop, looking for keystrokes but
254                  * keeping one eye on the time.
255                  */
256 main.8:         movb $0x1,%ah                   # BIOS: Check
257                 int $0x16                       #  for keypress
258                 jnz main.11                     # Have one
259                 xorb %ah,%ah                    # BIOS: Get
260                 int $0x1a                       #  system time
261                 cmpw %di,%dx                    # Timeout?
262                 jb main.8                       # No
263
264                 /*
265                  * If timed out or defaulting, come here.
266                  */
267 main.9:         movb _OPT(%bp),%al              # Load default
268                 jmp main.12                     # Join common code
269
270                 /*
271                  * User's last try was bad, beep in displeasure.
272                  * Since nothing was printed, just continue on as if the
273                  * user hadn't done anything. This gives the effect of the
274                  * user getting a beep for all bad keystrokes but no action
275                  * until either the timeout occurs or the user hits a good
276                  * key.
277                  */
278 main.10:        movb $0x7,%al                   # Signal
279                 callw putchr                    #  error
280
281                 /*
282                  * Get the keystroke.
283                  */
284 main.11:        xorb %ah,%ah                    # BIOS: Get
285                 int $0x16                       #  keypress
286                 movb %ah,%al                    # Scan code
287
288                 /*
289                  * If it's CR act as if timed out.
290                  */
291                 cmpb $KEY_ENTER,%al             # Enter pressed?
292                 je main.9                       # Yes
293
294                 /*
295                  * Otherwise check if legal
296                  * If not ask again.
297                  */
298                 subb $KEY_F1,%al                # Less F1 scan code
299                 cmpb $0x4,%al                   # F1..F5?
300                 jna main.12                     # Yes
301                 subb $(KEY_1 - KEY_F1),%al      # Less #1 scan code
302                 cmpb $0x4,%al                   # #1..#5?
303                 ja main.10                      # No
304
305                 /*
306                  * We have a selection.  But if it's a bad selection go back
307                  * to complain.  The bits in MNUOPT were set when the options
308                  * were printed.  Anything not printed is not an option.
309                  */
310 main.12:        cbtw                            # Option
311                 btw %ax,_MNUOPT(%bp)            #  enabled?
312                 jnc main.10                     # No
313
314                 /*
315                  * Save the info in the original tables
316                  * for rewriting to the disk.
317                  */
318                 movb %al,_OPT(%bp)              # Save option
319                 lea _FAKE(%bp),%si              # Partition for write
320                 movb (%si),%dl                  # Drive number
321                 movw %si,%bx                    # Partition for read
322                 cmpb $0x4,%al                   # F5 pressed?
323                 pushf                           # Save
324                 je main.13                      # Yes
325                 shlb $0x4,%al                   # Point to
326                 addw $partbl,%ax                #  selected
327                 xchgw %bx,%ax                   #  partition
328                 movb $0x80,(%bx)                # Flag active
329
330                 /*
331                  * If not asked to do a write-back (flags 0x40) don't do one.
332                  */
333 main.13:        pushw %bx                       # Save
334                 testb $0x40,_FLAGS(%bp)         # No updates?
335                 jnz main.14                     # Yes
336                 movw $start,%bx                 # Data to write
337                 movb $0x3,%ah                   # Write sector
338                 callw intx13                    #  to disk
339 main.14:        popw %si                        # Restore
340                 popf                            # Restore
341
342                 /*
343                  * If going to next drive, replace drive with selected one.
344                  * Remember to un-ascii it. Hey 0x80 is already set, cool!
345                  */
346                 jne main.15                     # If not F5
347                 movb _NXTDRV(%bp),%dl           # Next drive
348                 subb $'0',%dl                   #  number
349
350                 /* 
351                  * load  selected bootsector to the MEM_BIOS_LADDR location
352                  * in RAM.  If it fails to read or isn't marked bootable,
353                  * treat it as a bad selection.
354                  * XXX what does %si carry?
355                  */
356 main.15:        movw $MEM_BIOS_LADDR,%bx        # Address for read
357                 movb $0x2,%ah                   # Read sector
358                 callw intx13                    #  from disk
359                 jc main.10                      # If error
360                 cmpw $MAGIC,0x1fe(%bx)          # Bootable?
361                 jne main.10                     # No
362                 pushw %si                       # Save
363                 movw $crlf,%si                  # Leave some
364                 callw puts                      #  space
365                 popw %si                        # Restore
366                 jmp *%bx                        # Invoke bootstrap
367
368                 /*
369                  * Display routines
370                  */
371 putkey:         movb $'F',%al                   # Display
372                 callw putchr                    #  'F'
373                 movb $'1',%al                   # Prepare
374                 addb %dl,%al                    #  digit
375                 jmp putstr.1                    # Display the rest
376
377                 /*
378                  * Display the option and note that it is a valid option.
379                  * That last point is a bit tricky..
380                  */
381 putx:           btsw %dx,_MNUOPT(%bp)           # Enable menu option
382                 movw $item,%si                  # Display
383                 callw putkey                    #  key
384                 movw %di,%si                    # Display the rest
385
386 puts:           callw putstr                    # Display string
387
388 putn:           movw $crlf,%si                  # To next line
389
390 putstr:         lodsb                           # Get byte
391                 testb $0x80,%al                 # End of string?
392                 jnz putstr.2                    # Yes
393 putstr.1:       callw putchr                    # Display char
394                 jmp putstr                      # Continue
395 putstr.2:       andb $~0x80,%al                 # Clear MSB
396
397 putchr:         pusha                           # Save
398                 movw $0x7,%bx                   # Page:attribute
399                 movb $0xe,%ah                   # BIOS: Display
400                 int $0x10                       #  character
401                 popa                            # Restore
402                 retw                            # To caller
403
404                 /*
405                  * One-sector disk I/O routine
406                  *
407                  * Setup for both the packet and non-packet interfaces 
408                  * then select one or the other.
409                  *
410                  * Don't trust the BIOS to keep registers intact across 
411                  * the call, use pusha/popa.
412                  */
413 intx13:         movb 0x1(%si),%dh               # (nonpkt) head
414                 movw 0x2(%si),%cx               # (nonpkt) cylinder:sector
415                 movb $0x1,%al                   # (nonpkt) Sector count
416                 pusha                           # save: do not trust the bios
417                 pushl $0x0                      # (pkt) LBA address
418                 pushl 0x8(%si)                  # (pkt)
419                 pushw %es                       # (pkt) xfer buffer address
420                 pushw %bx                       # (pkt)
421                 pushw $1                        # (pkt) Block count
422                 pushw $16                       # (pkt) Packet size
423                 testb $0x80,_FLAGS(%bp)         # Use packet interface?
424                 jz intx13.1
425                 movw %sp,%si                    # Yes, set packet pointer
426                 decw %ax                        # Verify off
427                 orb $0x40,%ah                   # Set pkt mode in command
428 intx13.1:       int $0x13                       # BIOS: Disk I/O
429                 # WARNING: RETAIN CARRY AFTER BIOS CALL
430                 movw %sp,%si
431                 lea 16(%si),%sp                 # cleanup the stack
432                 popa                            # Restore registers
433                 retw
434
435                 /*
436                  * Menu strings
437                  */
438
439 item:           .ascii "  ";         .byte ' '|0x80
440 prompt:         .ascii "\nDefault:"; .byte ' '|0x80
441 crlf:           .ascii "\r";         .byte '\n'|0x80
442
443                 /*
444                  * Partition type tables
445                  */
446
447 tables:
448                 /*
449                  * These entries identify invalid or NON BOOT types and
450                  * partitions.
451                  */
452                 .byte 0x0, 0x5, 0xf
453
454                 /*
455                  * These values indicate bootable types we know the names of.
456                  */
457                 .byte 0x1, 0x4, 0x6, 0xb, 0xc, 0xe, 0x83
458                 .byte 0x9f, 0xa5, 0xa6, 0xa9
459
460                 /*
461                  * These are offsets that match the known names above and
462                  * point to the strings that will be printed.
463                  */
464                 .byte os_misc-.                 # Unknown
465                 .byte os_dos-.                  # DOS
466                 .byte os_dos-.                  # DOS
467                 .byte os_dos-.                  # DOS
468                 .byte os_dos-.                  # Windows
469                 .byte os_dos-.                  # Windows
470                 .byte os_dos-.                  # Windows
471                 .byte os_linux-.                # Linux
472                 .byte os_bsd-.                  # BSD/OS
473                 .byte os_freebsd-.              # FreeBSD
474                 .byte os_bsd-.                  # OpenBSD
475                 .byte os_bsd-.                  # NetBSD
476
477                 /*
478                  * And here are the strings themselves. 0x80 or'd into a
479                  * byte indicates the end of the string. (not so great for
480                  * Russians but...)
481                  */
482 os_misc:        .ascii "?";    .byte '?'|0x80
483 os_dos:         .ascii "DO";   .byte 'S'|0x80
484 os_linux:       .ascii "Linu"; .byte 'x'|0x80
485 os_freebsd:     .ascii "Free"
486 os_bsd:         .ascii "BS";   .byte 'D'|0x80
487
488                 .org PRT_OFF-0xe,0x90
489
490                 .word B0MAGIC                   # Magic number
491
492                 /*
493                  * These values are sometimes changed before writing back 
494                  * to the drive.  Be especially careful that nxtdrv: must
495                  * come after drive:, as it is part of the same string.
496                  */
497 drive:          .ascii "Drive "
498 nxtdrv:         .byte 0x0                       # Next drive number
499 opt:            .byte 0x0                       # Option
500 setdrv:         .byte 0x80                      # Drive to force
501 flags:          .byte FLAGS                     # Flags
502 ticks:          .word TICKS                     # Delay
503
504                 /*
505                  * here is the 64 byte partition table that fdisk would 
506                  * fiddle with.
507                  */
508 partbl:         .fill 0x40,0x1,0x0              # Partition table
509                 .word MAGIC                     # Magic number