2 * Copyright (c) 2003,2004 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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
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.
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
34 * Copyright (c) 1998 Robert Nordier
35 * All rights reserved.
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
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
48 * $FreeBSD: src/sys/boot/i386/boot0/boot0.s,v 1.26 2003/06/01 20:41:04 obrien Exp $
51 #include "../bootasm.h"
54 /* ... using a serial console on COM1. */
58 * A 512-byte boot manager.
60 .set PRT_OFF,0x1be # Partition table
62 .set TBL0SZ,0x3 # Table 0 size
63 .set TBL1SZ,0xb # Table 1 size
65 .set MAGIC,0xaa55 # Magic: bootable
66 .set B0MAGIC,0xbb66 # Identification
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
72 .set ASCII_BEL,0x07 # ASCII code for <BEL>
73 .set ASCII_CR,0x0D # ASCII code for <CR>
76 * Addresses in the sector of embedded data values. Accessed
77 * with negative offsets from the end of the relocated sector
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.
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
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
99 .globl start # Entry point
100 .code16 # This runs in real mode
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.
107 start: cld # String ops inc
109 movw %ax,%es # Address
111 movw %ax,%ss # Set up
112 movw $MEM_BIOS_LADDR,%sp # stack
115 * Copy this code to the address it was linked for.
116 * Base address after relocation is - 0x600.
118 movw %sp,%si # Source
119 movw $start,%di # Destination
120 movw $0x100,%cx # Word count
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.
129 movw %di,%bp # Address variables
130 movb $0x8,%cl # Words to clear
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.
139 incb _SECTOR_FIELD_OFF(%di) # Sector number
140 pushw $main # Jump to relocated code
144 #if defined(SIO) && COMSPEED != 0
146 * Initialize the serial port.
147 * bioscom preserves the drive number in DX.
149 movw $COMSPEED,%ax # defined by Makefile
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.
157 testb $0x20,_FLAGS(%bp) # Set number drive?
159 testb %dl,%dl # Drive number valid?
160 js main.2 # Possibly (0x80 set)
162 main.1: movb _SETDRV(%bp),%dl # Drive number to use
165 * Whatever we decided to use, now store it into the fake
166 * partition entry that lives in the data space above us.
168 main.2: movb %dl,_FAKE(%bp) # Save drive number
169 callw putn # To new line
170 pushw %dx # Save drive number
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)
180 movw $(partbl+0x4),%bx # Partition table (+4 - type)
181 xorw %dx,%dx # Partition entry item number
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
189 main.3: movb %ch,-0x4(%bx) # Zero active flag (ch == 0)
190 btw %dx,_FLAGS(%bp) # Entry enabled?
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.
198 movb (%bx),%al # Load type
199 movw $tables,%di # Lookup tables
200 movb $TBL0SZ,%cl # Number of entries
206 * Now scan the table of known types
208 movb $TBL1SZ,%cl # Number of entries
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 "?".
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
227 * Passed a 256 byte boundary..
229 * Add one to the drive number and check it is valid,
231 popw %ax # Drive number
232 subb $0x80-0x1,%al # Does next
233 cmpb BDA_NHRDRV,%al # drive exist? (from BIOS?)
237 * If not then if there is only one drive,
238 * Don't display drive as an option.
240 decw %ax # Already drive 0?
244 * If it was illegal or we cycled through them,
245 * then go back to drive 0.
247 xorb %al,%al # Drive 0
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
256 main.6: addb $'0'|0x80,%al # Save next
257 movb %al,_NXTDRV(%bp) # drive number
258 movw $drive,%di # Display
262 * Now that we've printed the drive (if we needed to),
263 * display a prompt. Get ready for the input by noting the
266 main.7: movw $prompt,%si # Display
267 callw putstr # prompt
268 movb _OPT(%bp),%dl # Display
271 xorb %ah,%ah # BIOS: Get
272 int $0x1a # system time
273 movw %dx,%di # Ticks when
274 addw _TICKS(%bp),%di # timeout
277 * Busy loop, looking for keystrokes but
278 * keeping one eye on the time.
282 movb $0x1,%ah # BIOS: Check
283 int $0x16 # for keypress
284 jnz main.11 # Have one
286 movb $0x03,%ah # BIOS: Read COM
288 testb $0x01,%ah # Check line status
289 jnz main.11 # (bit 1 indicates input)
291 xorb %ah,%ah # BIOS: Get
292 int $0x1a # system time
293 cmpw %di,%dx # Timeout?
297 * If timed out or defaulting, come here.
299 main.9: movb _OPT(%bp),%al # Load default
300 jmp main.12 # Join common code
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
310 main.10: movb $ASCII_BEL,%al # Signal
318 xorb %ah,%ah # BIOS: Get
320 movb %ah,%al # Scan code
322 movb $0x02,%ah # BIOS: Receive
327 * If it's CR act as if timed out.
330 cmpb $KEY_ENTER,%al # Enter pressed?
332 cmpb $ASCII_CR,%al # Enter pressed?
337 * Otherwise check if legal
341 subb $KEY_F1,%al # Less F1 scan code
342 cmpb $0x4,%al # F1..F5?
344 subb $(KEY_1 - KEY_F1),%al # Less #1 scan code
346 subb $'1',%al # Less '1' ascii character
348 cmpb $0x4,%al # #1..#5?
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.
356 main.12: cbtw # Option
357 btw %ax,_MNUOPT(%bp) # enabled?
361 * Save the info in the original tables
362 * for rewriting to the disk.
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?
371 shlb $0x4,%al # Point to
372 addw $partbl,%ax # selected
373 xchgw %bx,%ax # partition
374 movb $0x80,(%bx) # Flag active
377 * If not asked to do a write-back (flags 0x40) don't do one.
379 main.13: pushw %bx # Save
380 testb $0x40,_FLAGS(%bp) # No updates?
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
389 * If going to next drive, replace drive with selected one.
390 * Remember to un-ascii it. Hey 0x80 is already set, cool!
392 jne main.15 # If not F5
393 movb _NXTDRV(%bp),%dl # Next drive
394 subb $'0',%dl # number
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?
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?
409 movw $crlf,%si # Leave some
412 jmp *%bx # Invoke bootstrap
419 movb $'F',%al # Display
422 movb $'1',%al # Prepare
424 jmp putstr.1 # Display the rest
427 * Display the option and note that it is a valid option.
428 * That last point is a bit tricky..
430 putx: btsw %dx,_MNUOPT(%bp) # Enable menu option
431 movw $item,%si # Display
433 movw %di,%si # Display the rest
435 puts: callw putstr # Display string
437 putn: movw $crlf,%si # To next line
439 putstr: lodsb # Get byte
440 testb $0x80,%al # End of string?
442 putstr.1: callw putchr # Display char
443 jmp putstr # Continue
444 putstr.2: andb $~0x80,%al # Clear MSB
449 movw $0x7,%bx # Page:attribute
450 movb $0xe,%ah # BIOS: Display
451 int $0x10 # character
456 movb $0x01,%ah # BIOS: Send
458 pusha # Save drive number
459 xorw %dx,%dx # Use COM1
460 int $0x14 # Character
461 popa # Restore drive number
467 * One-sector disk I/O routine
469 * Setup for both the packet and non-packet interfaces
470 * then select one or the other.
472 * Don't trust the BIOS to keep registers intact across
473 * the call, use pusha/popa.
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
483 pushw $1 # (pkt) Block count
484 pushw $16 # (pkt) Packet size
485 testb $0x80,_FLAGS(%bp) # Use packet interface?
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
493 lea 16(%si),%sp # cleanup the stack
494 popa # Restore registers
501 item: .ascii " "; .byte ' '|0x80
502 prompt: .ascii "\nDefault:"; .byte ' '|0x80
503 crlf: .ascii "\r"; .byte '\n'|0x80
506 * Partition type tables
511 * These entries identify invalid or NON BOOT types and
517 * These values indicate bootable types we know the names of.
519 .byte 0x1, 0x6, 0x7, 0xb, 0xc, 0xe, 0x83
520 .byte 0x9f, 0xa5, 0xa6, 0xa9
523 * These are offsets that match the known names above and
524 * point to the strings that will be printed.
526 .byte os_misc-. # Unknown
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
540 * And here are the strings themselves. 0x80 or'd into a
541 * byte indicates the end of the string. (not so great for
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
552 .word B0MAGIC # Magic number
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.
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
567 * here is the 64 byte partition table that fdisk would
570 partbl: .fill 0x40,0x1,0x0 # Partition table
571 .word MAGIC # Magic number