This commits brings in a lot of documentation and some enhancement

of the boot0.S code, with a number of compile-time selectable options,
the most interesting one being the ability to select PXE booting.

The code is completely compatible with the previous one, and with
the boot0cfg program. Even the actual code is largely unmodified,
with only minor rearrangements or fixes to make room for the new
features.

The behaviour of the standard build differs from the previous
version in the following, minor things:

+ 'noupdate' is the default, which means the code does not
  write back the selection to disk. You can enable the feature
  at runtime with boot0cfg, or changing the flags in the Makefile.

+ a drive number of 0x00 (floppy, or USB in floppy emulation) is
  now accepted as valid. Previously, it was overridden with 0x80,
  meaning that the partition table coming from the media was
  used to access sectors on a possibly different media.
  You can revert to the previous mode building with -DCHECK_DRIVE,
  and you can always use the 'setdrv' option in boot0cfg

+ certain FAT or NTFS partitions are listed as WIN instead of DOS.

+ the 'bel' character on a bad selection is replaced by a '#' to
  make it clear that the system is not hang even if the machine
  does not have a speaker. This can be reverted back at compile
  time, or at runtime with an upcoming boot0cfg option.

Additional features are available as compile time options,
and may be become the default if deemed useful. In particular:

+ INT18/PXE boot (make -DPXE)
  This option enables booting through INT 18h (which on certain
  BIOSes can be hooked to PXE) by pressing F6. There is unfortunately
  no room to print the additional menu option.
  Also, to make room for the code, the 'Default: ' string is
  changed to 'Boot: '

+ print current drive number (make -DTEST)
  Prints a line indicating the current drive number.
  This is useful to figure out what is going on for machines/bioses
  which remap drives in sometimes surprising ways.

+ disable numeric keys in console mode (make -DONLY_F_KEYS)
  Not really a significant option, but it is needed to make
  room for the -DTEST mode.

+ disable floppy support (make -DCHECK_DRIVE)
  Revert to the old behaviour of only accepting 0x80 and above
  as valid drive numbers.

MFC after:	6 weeks
This commit is contained in:
luigi 2008-12-02 14:57:48 +00:00
parent a6ba0e1fed
commit e0b90fff45
2 changed files with 475 additions and 247 deletions

View File

@ -6,11 +6,29 @@ BINMODE=${NOBINMODE}
NO_MAN=
SRCS= ${PROG}.S
# The default set of flags compiled into boot0. This enables update (writing
# the modified boot0 back to disk after running so that the selection made is
# saved), packet mode (detect and use the BIOS EDD extensions if we try to
# boot past the 1024 cylinder liimt), and booting from all valid slices.
BOOT_BOOT0_FLAGS?= 0x8f
# Additional options that you can specify with make OPTS="..."
# (these only apply to boot0.S)
#
# -DSIO do I/O using COM1:
# -DPXE fallback to INT18/PXE with F6
# -DCHECK_DRIVE enable checking drive number
# -DONLY_F_KEYS accept only Fx keys in console
# -DTEST print drive number on entry
#
CFLAGS += ${OPTS}
# Flags used in the boot0.S code:
# 0x0f all valid partitions enabled.
# 0x80 'packet', use BIOS EDD (LBA) extensions instead of CHS
# to read from disk. boot0.S does not check that the extensions
# are supported, but all modern BIOSes should have them.
# 0x40 'noupdate', disable writing boot0 back to disk so that
# the current selection is not preserved across reboots.
# 0x20 'setdrv', override the drive number supplied by the bios
# with the one in the boot sector.
# Default boot flags:
BOOT_BOOT0_FLAGS?= 0xcf
# The number of timer ticks to wait for a keypress before assuming the default
# selection. Since there are 18.2 ticks per second, the default value of

View File

@ -1,4 +1,5 @@
/*
* Copyright (c) 2008 Luigi Rizzo (mostly documentation)
* Copyright (c) 2002 Bruce M. Simpson
* Copyright (c) 1998 Robert Nordier
* All rights reserved.
@ -16,50 +17,158 @@
* $FreeBSD$
*/
/* A 512-byte boot manager. */
#ifdef SIO
/* ... using a serial console on COM1. */
/* build options: */
#ifdef SIO /* use serial console on COM1. */
#endif
#ifdef PXE /* enable PXE/INT18 booting with F6 */
#endif
#ifdef CHECK_DRIVE /* make sure we boot from a HD. */
#endif
#ifdef ONLY_F_KEYS /* Only F1..F6, no digits on console */
#endif
#ifdef TEST /* enable some test code */
#ifndef ONLY_F_KEYS
#define ONLY_F_KEYS /* make room for the test code */
#endif
#endif
/*
* Note - this code uses many tricks to save space and fit in one sector.
* This includes using side effects of certain instructions, reusing
* register values from previous operations, etc.
* Be extremely careful when changing the code, even for simple things.
*/
/*
* BOOT BLOCK STRUCTURE
*
* This code implements a Master Boot Record (MBR) for an Intel/PC disk.
* It is 512 bytes long and it is normally loaded by the BIOS (or another
* bootloader) at 0:0x7c00. This code depends on %cs:%ip being 0:0x7c00
*
* The initial chunk of instructions is used as a signature by external
* tools (e.g. boot0cfg) which can manipulate the block itself.
*
* The area at offset 0x1b2 contains a magic string ('Drive '), also
* used as a signature to detect the block, and some variables that can
* be updated by boot0cfg (and optionally written back to the disk).
* These variables control the operation of the bootloader itself,
* e.g. which partitions to enable, the timeout, the use of LBA
* (called 'packet') or CHS mode, whether to force a drive number,
* and whether to write back the user's selection back to disk.
*
* As in every Master Boot Record, the partition table is at 0x1be,
* made of four 16-byte entries each containing:
*
* OFF SIZE DESCRIPTION
* 0 1 status (0x80: bootable, 0: non bootable)
* 1 3 start sector CHS
* 8:head, 6:sector, 2:cyl bit 9..8, 8:cyl bit 7..0
* 4 1 partition type
* 5 3 end sector CHS
* 8 4 LBA of first sector
* 12 4 partition size in sectors
*
* and followed by the two bytes 0x55, 0xAA (MBR signature).
*/
/*
* BOOT BLOCK OPERATION
*
* On entry, the registers contain the following values:
*
* %cs:%ip 0:0x7c00
* %dl drive number (0x80, 0x81, ... )
* %si pointer to the partition table from which we were loaded.
* Some boot code (e.g. syslinux) use this info to relocate
* themselves, so we want to pass a valid one to the next stage.
* NOTE: the use of %is is not a standard.
*
* This boot block first relocates itself at a different address (0:0x600),
* to free the space at 0:0x7c00 for the next stage boot block.
*
* It then initializes some memory at 0:0x800 and above (pointed by %bp)
* to store the original drive number (%dl) passed to us, and to construct a
* fake partition entry. The latter is used by the disk I/O routine and,
* in some cases, passed in %si to the next stage boot code.
*
* The variables at 0x1b2 are accessed as negative offsets from %bp.
*
* After the relocation, the code scans the partition table printing
* out enabled partition or disks, and waits for user input.
*
* When a partition is selected, or a timeout expires, the currently
* selected partition is used to load the next stage boot code,
* %dl and %si are set appropriately as when we were called, and
* control is transferred to the newly loaded code at 0:0x7c00.
*/
/*
* CONSTANTS
*
* NHRDRV is the address in segment 0 where the BIOS writes the
* total number of hard disks in the system.
* LOAD is the original load address and cannot be changed.
* ORIGIN is the relocation address. If you change it, you also need
* to change the value passed to the linker in the Makefile
* PRT_OFF is the location of the partition table (from the MBR standard).
* B0_OFF is the location of the data area, known to boot0cfg so
* it cannot be changed.
* MAGIC is the signature of a boot block.
*/
.set NHRDRV,0x475 # Number of hard drives
.set ORIGIN,0x600 # Execution address
.set FAKE,0x800 # Partition entry
.set LOAD,0x7c00 # Load address
.set PRT_OFF,0x1be # Partition table
.set TBL0SZ,0x3 # Table 0 size
.set TBL1SZ,0xa # Table 1 size
.set B0_OFF,0x1b2 # Offset of boot0 data
.set MAGIC,0xaa55 # Magic: bootable
.set B0MAGIC,0xbb66 # Identification
.set KEY_ENTER,0x1c # Enter key scan code
.set KEY_F1,0x3b # F1 key scan code
.set KEY_1,0x02 # #1 key scan code
.set ASCII_BEL,0x07 # ASCII code for <BEL>
.set ASCII_BEL,'#' # ASCII code for <BEL>
.set ASCII_CR,0x0D # ASCII code for <CR>
/*
* Addresses in the sector of embedded data values.
* Accessed with negative offsets from the end of the relocated sector (%ebp).
* Offsets of variables in the block at B0_OFF, and in the volatile
* data area, computed as displacement from %bp.
* We need to define them as constant as the assembler cannot
* compute them in its single pass.
*/
.set _NXTDRV,-0x48 # Next drive
.set _OPT,-0x47 # Default option
.set _SETDRV,-0x46 # Drive to force
.set _FLAGS,-0x45 # Flags
.set _TICKS,-0x44 # Timeout ticks
.set _FAKE,0x0 # Fake partition entry
.set _MNUOPT,0xc # Menu options
.set _NXTDRV, -0x48 # Next drive
.set _OPT, -0x47 # Default option
.set _SETDRV, -0x46 # Drive to force
.set _FLAGS, -0x45 # Flags
.set SETDRV, 0x20 # the 'setdrv' flag
.set NOUPDATE, 0x40 # the 'noupdate' flag
.set USEPACKET, 0x80 # the 'packet' flag
.set _TICKS, -0x44 # Timeout ticks
.set _FAKE,0x0 # Fake partition table
.set _MNUOPT, 0x10 # Saved menu entries
.set TLEN, (desc_ofs - bootable_ids) # size of bootable ids
.globl start # Entry point
.code16 # This runs in real mode
/*
* MAIN ENTRY POINT
* Initialise segments and registers to known values.
* segments start at 0.
* The stack is immediately below the address we were loaded to.
* NOTE: the initial section of the code (up to movw $LOAD,%sp)
* is used by boot0cfg, together with the 'Drive ' string and
* the 0x55, 0xaa at the end, as an identifier for version 1.0
* of the boot code. Do not change it.
* In version 1.0 the parameter table (_NEXTDRV etc) is at 0x1b9
*/
start: cld # String ops inc
xorw %ax,%ax # Zero
@ -68,257 +177,318 @@ start: cld # String ops inc
movw %ax,%ss # Set up
movw $LOAD,%sp # stack
/*
* Copy this code to the address it was linked for
*/
/*
* Copy this code to the address it was linked for, 0x600 by default.
*/
movw %sp,%si # Source
movw $start,%di # Destination
movw $0x100,%cx # Word count
rep # Relocate
movsw # code
/*
* Set address for variable space beyond code, and clear it.
* Notice that this is also used to point to the values embedded in the block,
* by using negative offsets.
*/
/*
* After the code, (i.e. at %di+0, 0x800) create a partition entry,
* initialized to LBA 0 / CHS 0:0:1.
* Set %bp to point to the partition and also, with negative offsets,
* to the variables embedded in the bootblock (nextdrv and so on).
*/
movw %di,%bp # Address variables
movb $0x8,%cl # Words to clear
rep # Zero
stosw # them
/*
* Relocate to the new copy of the code.
*/
incb -0xe(%di) # Sector number
jmp main-LOAD+ORIGIN # To relocated code
incb -0xe(%di) # Set the S field to 1
jmp main-LOAD+ORIGIN # Jump to relocated code
main:
#if defined(SIO) && COMSPEED != 0
/*
* Initialize the serial port. bioscom preserves the driver number in DX.
*/
/*
* Init the serial port. bioscom preserves the driver number in DX.
*/
movw $COMSPEED,%ax # defined by Makefile
callw bioscom
#endif
/*
* Check what flags were loaded with us, specifically if a predefined drive
* number should be used. If what the bios gives us is bad, use the '0' in
* the block instead.
*/
testb $0x20,_FLAGS(%bp) # Set drive number?
jnz main.1 # Yes
/*
* If the 'setdrv' flag is set in the boot sector, use the drive
* number from the boot sector at 'setdrv_num'.
* Optionally, do the same if the BIOS gives us an invalid number
* (note though that the override prevents booting from a floppy
* or a ZIP/flash drive in floppy emulation).
* The test costs 4 bytes of code so it is disabled by default.
*/
testb $SETDRV,_FLAGS(%bp) # Set drive number?
#ifndef CHECK_DRIVE /* disable drive checks */
jz save_curdrive # no, use the default
#else
jnz disable_update # Yes
testb %dl,%dl # Drive number valid?
js main.2 # Possibly (0x80 set)
/*
* Only update the boot-sector when there is a valid drive number or
* the drive number is set manually.
*/
orb $0x40,_FLAGS(%bp) # Disable updates
main.1: movb _SETDRV(%bp),%dl # Drive number to use
/*
* Whatever we decided to use, now store it into the fake
* partition entry that lives in the data space above us.
*/
main.2: movb %dl,_FAKE(%bp) # Save drive number
callw putn # To new line
pushw %dx # Save drive number
/*
* Start out with a pointer to the 4th byte of the first table entry
* so that after 4 iterations it's beyond the end of the sector
* and beyond a 256 byte boundary and has overflowed 8 bits (see next comment).
* Remember that the table starts 2 bytes earlier than you would expect
* as the bootable flag is after it in the block.
*/
js save_curdrive # Possibly (0x80 set)
#endif
/*
* Disable updates if the drive number is forced.
*/
disable_update: orb $NOUPDATE,_FLAGS(%bp) # Disable updates
movb _SETDRV(%bp),%dl # Use stored drive number
/*
* Whatever drive we decided to use, store it at (%bp). The byte
* is normally used for the state of the partition (0x80 or 0x00),
* but we abuse it as it is very convenient to access at offset 0.
* The value is read back after 'check_selection'
*/
save_curdrive: movb %dl, (%bp) # Save drive number
pushw %dx # Also in the stack
#ifdef TEST /* test code, print internal bios drive */
rolb $1, %dl
movw $drive, %si
call putkey
#endif
callw putn # Print a newline
/*
* Start out with a pointer to the 4th byte of the first table entry
* so that after 4 iterations it's beyond the end of the sector
* and beyond a 256 byte boundary. We use the latter trick to check for
* end of the loop without using an extra register (see start.5).
*/
movw $(partbl+0x4),%bx # Partition table (+4)
xorw %dx,%dx # Item number
/*
* Loop around on the partition table, printing values until we
* pass a 256 byte boundary. The end of loop test is at main.5.
*/
main.3: movb %ch,-0x4(%bx) # Zero active flag (ch == 0)
/*
* Loop around on the partition table, printing values until we
* pass a 256 byte boundary.
*/
read_entry: movb %ch,-0x4(%bx) # Zero active flag (ch == 0)
btw %dx,_FLAGS(%bp) # Entry enabled?
jnc main.5 # No
/*
* If any of the entries in the table are the same as the 'type' in the slice
* table entry, then this is an empty or non bootable partition. Skip it.
*/
jnc next_entry # No
/*
* Lookup type in the 'non_bootable_ids' table, skip matching entries.
* This is implemented is by setting %di to the start of the
* exclude table, and %cl to the length of the table itself. After the
* 'repne scasb' the zero flag is set if we found a match.
* If not, %di points to the beginning of the 'valid' types,
* which is what we need for the next check.
*/
movb (%bx),%al # Load type
movw $tables,%di # Lookup tables
movb $TBL0SZ,%cl # Number of entries
movw $non_bootable_ids,%di # Lookup tables
movb $(bootable_ids-non_bootable_ids),%cl # length
repne # Exclude
scasb # partition?
je main.5 # Yes
/*
* Now scan the table of known types
*/
movb $TBL1SZ+1,%cl # Number of entries
je next_entry # Yes, ignore it
/*
* Now scan the table of bootable ids, which starts at %di and has
* length TLEN. On a match, %di points to the element following the
* match; the corresponding offset to the description is $(TLEN-1)
* bytes ahead. If we don't find a match, we hit the 'unknown' entry.
*/
movb $(TLEN),%cl # Number of entries
repne # Locate
scasb # type
/*
* Get the matching element in the next array.
*/
addw $TBL1SZ-1, %di # Adjust
/*
* Get the matching element in the next array.
* The byte at $(TLEN-1)(%di) contains the offset of the description
* string from %di, so we add the number and print the string.
*/
addw $(TLEN-1), %di # Adjust
movb (%di),%cl # Partition
addw %cx,%di # description
callw putx # Display it
main.5: incw %dx # Next item
next_entry: incw %dx # Next item
addb $0x10,%bl # Next entry
jnc main.3 # Till done
/*
* Passed a 256 byte boundary; the table is finished.
* Add one to the drive number and check it is valid.
*/
jnc read_entry # Till done
/*
* We are past a 256 byte boundary: the partition table is finished.
* Add one to the drive number and check it is valid.
* Note that if we started from a floppy, %dl was 0 so we still
* get an entry for the next drive, which is the first Hard Disk.
*/
popw %ax # Drive number
subb $0x80-0x1,%al # Does next
cmpb NHRDRV,%al # drive exist? (from BIOS?)
jb main.6 # Yes
/*
* If this is the only drive, don't display it as an option.
*/
jb print_drive # Yes
/*
* If this is the only drive, don't display it as an option.
*/
decw %ax # Already drive 0?
jz main.7 # Yes
/*
* If it was illegal or we cycled through them, go back to drive 0.
*/
jz print_prompt # Yes
/*
* If it was illegal or we cycled through them, go back to drive 0.
*/
xorb %al,%al # Drive 0
/*
* Whatever drive we selected, make it an ascii digit and save it back to the
* "next drive" location in the loaded block in case we want to save it later
* for next time. This also is part of the printed drive string so add 0x80
* to indicate end of string.
*/
main.6: addb $'0'|0x80,%al # Save next
/*
* Whatever drive we selected, make it an ascii digit and save it
* back to the "nxtdrv" location in case we want to save it to disk.
* This digit is also part of the printed drive string, so add 0x80
* to indicate end of string.
*/
print_drive: addb $'0'|0x80,%al # Save next
movb %al,_NXTDRV(%bp) # drive number
movw $drive,%di # Display
callw putx # item
/*
* Now that we've printed the drive (if we needed to), display a prompt.
*/
main.7: movw $prompt,%si # Display
/*
* Menu is complete, display a prompt followed by current selection.
* 'decw %si' makes the register point to the space after 'Default: '
* so we do not see an extra CRLF on the screen.
*/
print_prompt: movw $prompt,%si # Display
callw putstr # prompt
movb _OPT(%bp),%dl # Display
decw %si # default
callw putkey # key
jmp main.7_1 # Skip beep
jmp start_input # Skip beep
/*
* Users's last try was bad, beep in displeasure.
* Here we have the code waiting for user input or a timeout.
*/
main.10: movb $ASCII_BEL,%al # Signal
callw putchr # beep!
/*
* Start of input loop. Take note of time
*/
main.7_1: xorb %ah,%ah # BIOS: Get
beep: movb $ASCII_BEL,%al # Input error, print or beep
callw putchr
start_input:
/*
* Actual Start of input loop. Take note of time
*/
xorb %ah,%ah # BIOS: Get
int $0x1a # system time
movw %dx,%di # Ticks when
addw _TICKS(%bp),%di # timeout
/*
* Busy loop, looking for keystrokes but keeping one eye on the time.
*/
main.8:
read_key:
/*
* Busy loop, looking for keystrokes but keeping one eye on the time.
*/
#ifndef SIO
movb $0x1,%ah # BIOS: Check
int $0x16 # for keypress
jnz main.11 # Have one
#else /* SIO */
movb $0x03,%ah # BIOS: Read COM
call bioscom
testb $0x01,%ah # Check line status
jnz main.11 # (bit 1 indicates input)
# (bit 1 indicates input)
#endif /* SIO */
xorb %ah,%ah # BIOS: Get
int $0x1a # system time
jnz got_key # Have input
xorb %ah,%ah # BIOS: int 0x1a, 00
int $0x1a # get system time
cmpw %di,%dx # Timeout?
jb main.8 # No
/*
* If timed out or defaulting, come here.
*/
main.9: movb _OPT(%bp),%al # Load default
jmp main.12 # Join common code
/*
* Get the keystroke.
*/
main.11:
jb read_key # No
/*
* Timed out or default selection
*/
use_default: movb _OPT(%bp),%al # Load default
jmp check_selection # Join common code
/*
* Get the keystroke.
* ENTER or CR confirm the current selection (same as a timeout).
* Otherwise convert F1..F6 (or '1'..'6') to 0..5 and check if the
* selection is valid.
* The SIO code uses ascii chars, the console code uses scancodes.
*/
got_key:
#ifndef SIO
xorb %ah,%ah # BIOS: Get
int $0x16 # keypress
movb %ah,%al # Scan code
xorb %ah,%ah # BIOS: int 0x16, 00
int $0x16 # get keypress
movb %ah,%al # move scan code to %al
cmpb $KEY_ENTER,%al
#else
movb $0x02,%ah # BIOS: Receive
call bioscom
cmpb $ASCII_CR,%al
#endif
/*
* If it's CR act as if timed out.
*/
#ifndef SIO
cmpb $KEY_ENTER,%al # Enter pressed?
#else
cmpb $ASCII_CR,%al # Enter pressed?
#endif
je main.9 # Yes
/*
* Otherwise check if legal. If not ask again.
*/
#ifndef SIO
subb $KEY_F1,%al # Less F1 scan code
cmpb $0x4,%al # F1..F5?
jna main.12 # Yes
je use_default # enter -> default
/*
* Check if the key is acceptable, and loop back if not.
* The console (non-SIO) code looks at scancodes and accepts
* both F1..F6 and 1..6 (the latter costs 6 bytes of code),
* relying on the fact that F1..F6 have higher scancodes than 1..6
* The SIO code only takes 1..6
*/
#ifdef SIO /* SIO mode, use ascii values */
subb $'1',%al # Subtract '1' ascii code
#else /* console mode -- use scancodes */
subb $KEY_F1,%al /* Subtract F1 scan code */
#if !defined(ONLY_F_KEYS)
cmpb $0x5,%al # F1..F6
jna 3f # Yes
subb $(KEY_1 - KEY_F1),%al # Less #1 scan code
#else
subb $'1',%al # Less '1' ascii character
#endif
cmpb $0x4,%al # #1..#5?
ja main.10 # No
/*
* We have a selection. If it's a bad selection go back to complain.
* The bits in MNUOPT were set when the options were printed.
* Anything not printed is not an option.
*/
main.12: cbtw # Option
btw %ax,_MNUOPT(%bp) # enabled?
jnc main.10 # No
/*
* Save the info in the original tables
* for rewriting to the disk.
*/
3:
#endif /* ONLY_F_KEYS */
#endif /* SIO */
cmpb $0x5,%al # F1..F6 or 1..6 ?
#ifdef PXE /* enable PXE/INT18 using F6 */
jne 1f;
int $0x18 # found F6, try INT18
1:
#endif /* PXE */
jae beep # Not in F1..F5, beep
check_selection:
/*
* We have a selection. If it's a bad selection go back to complain.
* The bits in MNUOPT were set when the options were printed.
* Anything not printed is not an option.
*/
cbtw # Extend (%ah=0 used later)
btw %ax,_MNUOPT(%bp) # Option enabled?
jnc beep # No
/*
* Save the info in the original tables
* for rewriting to the disk.
*/
movb %al,_OPT(%bp) # Save option
movw $FAKE,%si # Partition for write
movb (%si),%dl # Drive number
/*
* Make %si and %bx point to the fake partition at LBA 0 (CHS 0:0:1).
* Because the correct address is already in %bp, just use it.
* Set %dl with the drive number saved in byte 0.
* If we have pressed F5 or 5, then this is a good, fake value
* to present to the next stage boot code.
*/
movw %bp,%si # Partition for write
movb (%si),%dl # Drive number, saved above
movw %si,%bx # Partition for read
cmpb $0x4,%al # F5/#5 pressed?
pushf # Save
je main.13 # Yes
pushf # Save results for later
je 1f # Yes, F5
/*
* F1..F4 was pressed, so make %bx point to the currently
* selected partition, and leave the drive number unchanged.
*/
shlb $0x4,%al # Point to
addw $partbl,%ax # selected
xchgw %bx,%ax # partition
movb $0x80,(%bx) # Flag active
/*
* If not asked to do a write-back (flags 0x40) don't do one.
*/
main.13: pushw %bx # Save
testb $0x40,_FLAGS(%bp) # No updates?
jnz main.14 # Yes
/*
* If not asked to do a write-back (flags 0x40) don't do one.
* Around the call, save the partition pointer to %bx and
* restore to %si which is where the next stage expects it.
*/
1: pushw %bx # Save
testb $NOUPDATE,_FLAGS(%bp) # No updates?
jnz 2f # skip update
movw $start,%bx # Data to write
movb $0x3,%ah # Write sector
callw intx13 # to disk
main.14: popw %si # Restore
popf # Restore
/*
* If going to next drive, replace drive with selected one.
* Remember to un-ascii it. Hey 0x80 is already set, cool!
*/
jne main.15 # If not F5/#5
2: popw %si # Restore
/*
* If going to next drive, replace drive with selected one.
* Remember to un-ascii it. Hey 0x80 is already set, cool!
*/
popf # Restore %al test results
jne 3f # If not F5/#5
movb _NXTDRV(%bp),%dl # Next drive
subb $'0',%dl # number
/*
* Load selected bootsector to the LOAD location in RAM.
* If it fails to read or isn't marked bootable, treat it as a bad selection.
*/
main.15: movw $LOAD,%bx # Address for read
/*
* Load selected bootsector to the LOAD location in RAM. If read
* fails or there is no 0x55aa marker, treat it as a bad selection.
*/
3: movw $LOAD,%bx # Address for read
movb $0x2,%ah # Read sector
callw intx13 # from disk
jc main.10 # If error
jc beep # If error
cmpw $MAGIC,0x1fe(%bx) # Bootable?
jne main.10 # No
jne beep # No
pushw %si # Save ptr to selected part.
callw putn # Leave some space
popw %si # Restore, next stage uses it
@ -326,6 +496,14 @@ main.15: movw $LOAD,%bx # Address for read
/*
* Display routines
* putkey prints the option selected in %dl (F1..F5 or 1..5) followed by
* the string at %si
* putx: print the option in %dl followed by the string at %di
* also record the drive as valid.
* puts: print the string at %si followed by a crlf
* putn: print a crlf
* putstr: print the string at %si
* putchr: print the char in al
*/
putkey:
#ifndef SIO
@ -337,8 +515,9 @@ putkey:
jmp putstr.1 # Display the rest
/*
* Display the option and note that it is a valid option.
* That last point is a bit tricky..
* Display the option and record the drive as valid in the options.
* That last point is done using the btsw instruction which does
* a test and set. We don't care for the test part.
*/
putx: btsw %dx,_MNUOPT(%bp) # Enable menu option
movw $item,%si # Display
@ -356,34 +535,43 @@ putstr.1: callw putchr # Display char
jmp putstr # Continue
putstr.2: andb $~0x80,%al # Clear MSB
#ifndef SIO
putchr:
#ifndef SIO
pushw %bx # Save
movw $0x7,%bx # Page:attribute
movb $0xe,%ah # BIOS: Display
int $0x10 # character
popw %bx # Restore
retw # To caller
#else /* SIO */
putchr:
movb $0x01,%ah # BIOS: Send
bioscom:
pushw %dx # Save
xorw %dx,%dx # Use COM1
int $0x14 # Character
popw %dx # Restore
retw # To caller
#endif /* SIO */
retw # To caller
/* One-sector disk I/O routine */
intx13: movb 0x1(%si),%dh # Load head
/*
* %dl: drive, %si partition entry, %es:%bx transfer buffer.
* Load the CHS values and possibly the LBA address from the block
* at %si, and use the appropriate method to load the sector.
* Don't use packet mode for a floppy.
*/
intx13: # Prepare CHS parameters
movb 0x1(%si),%dh # Load head
movw 0x2(%si),%cx # Load cylinder:sector
movb $0x1,%al # Sector count
pushw %si # Save
movw %sp,%di # Save
testb $0x80,_FLAGS(%bp) # Use packet interface?
jz intx13.1 # No
#ifndef CHECK_DRIVE /* floppy support */
testb %dl, %dl # is this a floppy ?
jz 1f # Yes, use CHS mode
#endif
testb $USEPACKET,_FLAGS(%bp) # Use packet interface?
jz 1f # No
pushl $0x0 # Set the
pushl 0x8(%si) # LBA address
pushw %es # Set the transfer
@ -393,73 +581,95 @@ intx13: movb 0x1(%si),%dh # Load head
movw %sp,%si # Packet pointer
decw %ax # Verify off
orb $0x40,%ah # Use disk packet
intx13.1: int $0x13 # BIOS: Disk I/O
1: int $0x13 # BIOS: Disk I/O
movw %di,%sp # Restore
popw %si # Restore
retw # To caller
/* Menu strings */
/*
* Various menu strings. 'item' goes after 'prompt' to save space.
* Also use shorter versions to make room for the PXE/INT18 code.
*/
#ifdef PXE
prompt: .ascii "\nBoot:"
item: .ascii " "; .byte ' '|0x80
#else
prompt: .ascii "\nDefault:"
item: .ascii " "; .byte ' '|0x80
prompt: .ascii "\nDefault:"; .byte ' '|0x80
#endif
crlf: .ascii "\r"; .byte '\n'|0x80
/* Partition type tables */
tables:
/*
* These entries identify invalid or NON BOOT types and partitions.
*/
non_bootable_ids:
/*
* These entries identify invalid or NON BOOT types and partitions.
* 0: empty, 5: DOS3 ext. partition, 0xf: WIN95 ext partition
*/
.byte 0x0, 0x5, 0xf
/*
* These values indicate bootable types we know the names of.
*/
bootable_ids:
/*
* These values indicate bootable types we know the names of.
* 1: FAT12, 4: FAT16 <32M, 6: FAT16 > 32M, 7: NTFS
* 0xb: FAT32, 0xc: FAT32-LBA, 0xe: FAT16-LBA,
* 0x83: linux, 0xa5: FreeBSD, 0xa6: netbsd, 0xa9:openbsd
*/
.byte 0x1, 0x6, 0x7, 0xb, 0xc, 0xe, 0x83
.byte 0xa5, 0xa6, 0xa9
/*
* These are offsets that match the known names above and point to the strings
* that will be printed. os_misc will be used if the search of the above table
* runs over.
*/
.byte os_dos-. # DOS
.byte os_dos-. # DOS
.byte os_dos-. # Windows
.byte os_dos-. # Windows
.byte os_dos-. # Windows
.byte os_dos-. # Windows
.byte os_linux-. # Linux
.byte os_freebsd-. # FreeBSD
.byte os_bsd-. # OpenBSD
.byte os_bsd-. # NetBSD
.byte 0xa5, 0xa6, 0xa9, 0x4
desc_ofs:
/*
* Offsets that match the known types above, used to point to the
* actual partition name. The last entry must point to os_misc,
* which is used for non-matching names.
*/
.byte os_dos-. # 1, DOS
.byte os_dos-. # 6, DOS/WIN
.byte os_win-. # 7, Windows
.byte os_win-. # 11, Windows
.byte os_win-. # 12, Windows
.byte os_win-. # 14, Windows
.byte os_linux-. # 131, Linux
.byte os_freebsd-. # 165, FreeBSD
.byte os_bsd-. # 166, OpenBSD
.byte os_bsd-. # 169, NetBSD
.byte os_dos-. # 4, DOS
.byte os_misc-. # Unknown
/*
* And here are the strings themselves. 0x80 or'd into a byte indicates
* the end of the string. (not so great for Russians but...)
*/
os_misc: .ascii "?"; .byte '?'|0x80
os_dos: .ascii "DO"; .byte 'S'|0x80
/*
* And here are the strings themselves. The last byte of
* the string has bit 7 set.
*/
os_misc: .byte '?'|0x80
os_dos:
#if !defined(TEST) /* DOS string only if room */
.ascii "DO"; .byte 'S'|0x80
#endif
os_win: .ascii "WI"; .byte 'N'|0x80
os_linux: .ascii "Linu"; .byte 'x'|0x80
os_freebsd: .ascii "Free"
os_bsd: .ascii "BS"; .byte 'D'|0x80
.org PRT_OFF-0xe,0x90
.word B0MAGIC # Magic number
.org B0_OFF,0x90
/*
* These values are sometimes changed before writing back to the drive
* The boot0 version 1.0 parameter table.
* Do not move it nor change the "Drive " string, boot0cfg
* uses its offset and content to identify the boot sector.
* The other fields are sometimes changed before writing back to the drive
* Be especially careful that nxtdrv: must come after drive:, as it
* is part of the same string.
*/
drive: .ascii "Drive "
nxtdrv: .byte 0x0 # Next drive number
opt: .byte 0x0 # Option
setdrv: .byte 0x80 # Drive to force
setdrv_num: .byte 0x80 # Drive to force
flags: .byte FLAGS # Flags
ticks: .word TICKS # Delay
.org PRT_OFF
/*
* Here is the 64 byte partition table that fdisk would fiddle with.
*/
partbl: .fill 0x40,0x1,0x0 # Partition table
.word MAGIC # Magic number
.org 0x200 # again, safety check
endblock: