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