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