From owner-svn-src-all@FreeBSD.ORG Tue Dec 2 14:57:49 2008 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id E78D11065673; Tue, 2 Dec 2008 14:57:48 +0000 (UTC) (envelope-from luigi@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id CD4078FC17; Tue, 2 Dec 2008 14:57:48 +0000 (UTC) (envelope-from luigi@FreeBSD.org) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id mB2Evmq5063420; Tue, 2 Dec 2008 14:57:48 GMT (envelope-from luigi@svn.freebsd.org) Received: (from luigi@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id mB2Evmha063418; Tue, 2 Dec 2008 14:57:48 GMT (envelope-from luigi@svn.freebsd.org) Message-Id: <200812021457.mB2Evmha063418@svn.freebsd.org> From: Luigi Rizzo Date: Tue, 2 Dec 2008 14:57:48 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r185562 - head/sys/boot/i386/boot0 X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 02 Dec 2008 14:57:49 -0000 Author: luigi Date: Tue Dec 2 14:57:48 2008 New Revision: 185562 URL: http://svn.freebsd.org/changeset/base/185562 Log: 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 Modified: head/sys/boot/i386/boot0/Makefile head/sys/boot/i386/boot0/boot0.S Modified: head/sys/boot/i386/boot0/Makefile ============================================================================== --- head/sys/boot/i386/boot0/Makefile Tue Dec 2 14:19:53 2008 (r185561) +++ head/sys/boot/i386/boot0/Makefile Tue Dec 2 14:57:48 2008 (r185562) @@ -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 Modified: head/sys/boot/i386/boot0/boot0.S ============================================================================== --- head/sys/boot/i386/boot0/boot0.S Tue Dec 2 14:19:53 2008 (r185561) +++ head/sys/boot/i386/boot0/boot0.S Tue Dec 2 14:57:48 2008 (r185562) @@ -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 + .set ASCII_BEL,'#' # ASCII code for .set ASCII_CR,0x0D # ASCII code for /* - * Addresses in the sector of embedded data values. - * Accessed with negative offsets from the end of the relocated sector (%ebp). - */ - .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 + * 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 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 -/* - * Users's last try was bad, beep in displeasure. - */ -main.10: movb $ASCII_BEL,%al # Signal - callw putchr # beep! + jmp start_input # Skip beep + /* - * Start of input loop. Take note of time + * Here we have the code waiting for user input or a timeout. */ -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 /* * 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: