/*
* Copyright (c) 2008 Luigi Rizzo (mostly documentation)
* Copyright (c) 2002 Bruce M. Simpson
* Copyright (c) 1998 Robert Nordier
* All rights reserved.
*
* Redistribution and use in source and binary forms are freely
* permitted provided that the above copyright notice and this
* paragraph and the following disclaimer are duplicated in all
* such forms.
*
* This software is provided "AS IS" and without any express or
* implied warranties, including, without limitation, the implied
* warranties of merchantability and fitness for a particular
* purpose.
*
* $FreeBSD$
*/
/* build options: */
#ifdef SIO /* use serial console on COM1. */
#endif
#define SAVE_MORE_MEMORY
#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 VOLUME_SERIAL /* support Volume serial number */
#define SAVE_MEMORY
#else
#endif
#ifdef TEST /* enable some test code */
#define SAVE_MEMORY
#define SAVE_MORE_MEMORY
#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
*
* 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 %si 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. Computed as a negative offset from 0x200
* MAGIC is the signature of a boot block.
*/
/*
* 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.
*/
/* ticks is at a fixed position */
/*
* 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
*/
/*
* Copy this code to the address it was linked for, 0x600 by default.
*/
/*
* 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).
*/
main:
/*
* Init the serial port. bioscom preserves the driver number in DX.
*/
#endif
/*
* 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
* The test costs 4 bytes of code so it is disabled by default.
*/
#ifndef CHECK_DRIVE /* disable drive checks */
#else
#endif
/*
* Disable updates if the drive number is forced.
*/
/*
* 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'
*/
#ifdef TEST /* test code, print internal bios drive */
#endif
/*
* 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).
*/
/*
* Loop around on the partition table, printing values until we
* pass a 256 byte boundary.
*/
/*
* 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. We use a count of TLEN+1 so if we don't find a match
* within the first TLEN entries, we hit the 'unknown' entry.
*/
/*
* 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.
*/
/*
* 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.
*/
/*
* If this is the only drive, don't display it as an option.
*/
/*
* If it was illegal or we cycled through them, go back to drive 0.
*/
/*
* 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.
*/
/*
* Menu is complete, display a prompt followed by current selection.
* 'decw %si' makes the register point to the space after 'Boot: '
* so we do not see an extra CRLF on the screen.
*/
/*
* Here we have the code waiting for user input or a timeout.
*/
/*
* Actual Start of input loop. Take note of time
*/
/*
* Busy loop, looking for keystrokes but keeping one eye on the time.
*/
#ifndef SIO
int $0x16 # for keypress
#else /* SIO */
#endif /* SIO */
/*
* Timed out or default selection
*/
/*
* 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.
*/
#ifndef SIO
#else
#endif
/*
* 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 */
#else /* console mode -- use scancodes */
#if !defined(ONLY_F_KEYS)
3:
#endif /* ONLY_F_KEYS */
#endif /* SIO */
jne 1f;
1:
#endif /* PXE */
/*
* 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.
*/
/*
* Save the info in the original tables
* for rewriting to the disk.
*/
/*
* 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.
*/
/*
* F1..F4 was pressed, so make %bx point to the currently
* selected partition, and leave the drive number unchanged.
*/
/*
* 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.
*/
/*
* If going to next drive, replace drive with selected one.
* Remember to un-ascii it. Hey 0x80 is already set, cool!
*/
/*
* Load selected bootsector to the LOAD location in RAM. If read
* fails or there is no 0x55aa marker, treat it as a bad selection.
*/
/*
* 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.
* putn: print a crlf
* putstr: print the string at %si
* putchr: print the char in al
*/
/*
* 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.
*/
#ifndef SIO
#endif
#ifndef SIO
int $0x10 # character
#else /* SIO */
#endif /* SIO */
/* One-sector disk I/O routine */
/*
* %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.
*/
#ifndef CHECK_DRIVE /* floppy support */
#endif
/*
* Various menu strings. 'item' goes after 'prompt' to save space.
*/
#ifdef PXE
.ascii "\nF6 PXE\r"
#endif
.ascii "\nBoot:"
/* Partition type tables */
/*
* These values indicate bootable types we know about.
* Corresponding descriptions are at desc_ofs:
* Entries don't need to be sorted.
*/
#ifndef SAVE_MORE_MEMORY
#endif
#ifndef SAVE_MEMORY /* other DOS partitions */
#endif
/*
* 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.
*/
#ifndef SAVE_MORE_MEMORY
#endif
#ifndef SAVE_MEMORY
#endif
/*
* And here are the strings themselves. The last byte of
* the string has bit 7 set.
*/
#ifndef SAVE_MORE_MEMORY /* 'DOS' remapped to 'WIN' if no room */
#endif
#ifndef SAVE_MORE_MEMORY
#endif
/*
* 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.
*/
#ifdef VOLUME_SERIAL
#endif
/*
* Here is the 64 byte partition table that fdisk would fiddle with.
*/