code:
/*	$OpenBSD: biosboot.S,v 1.41 2011/07/05 17:38:54 krw Exp $	*/

/*
 * Copyright (c) 2003 Tobias Weingartner
 * Copyright (c) 2003 Tom Cosgrove <tom.cosgrove@arches-consulting.com>
 * Copyright (c) 1997 Michael Shalayeff, Tobias Weingartner
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */
	.file	"biosboot.S"

#include <machine/asm.h>
#include <assym.h>

/* Error indicators */
#define PBR_READ_ERROR			'R'
#define PBR_CANT_BOOT			'X'
#define PBR_BAD_MAGIC			'M'
#define PBR_TOO_MANY_INDIRECTS		'I'

#define CHAR_BLOCK_READ		'.'
#define CHAR_CHS_READ		';'

/*
 * Memory layout:
 *
 * 0x00000 -> 0x07BFF	our stack		(to  31k)
 * 0x07A00 -> 0x07BFF	typical MBR loc		(at  30k5)
 * 0x07C00 -> 0x07DFF	our code		(at  31k)
 * 0x07E00 -> ...    	/boot inode block	(at  31k5)
 * 0x07E00 -> ...    	(indirect block if nec)
 * 0x40000 -> ...	/boot			(at 256k)
 *
 * The BIOS loads the MBR at physical address 0x07C00.  It then relocates
 * itself to (typically) 0x07A00.
 *
 * The MBR then loads us at physical address 0x07C00.
 *
 * We use a long jmp to normalise our address to seg:offset 07C0:0000.
 * (In real mode on x86, segment registers contain a base address in
 * paragraphs (16 bytes).  0000:00010 is the same as 0001:0000.)
 *
 * We set the stack to start at 0000:7BFC (grows down on i386)
 *
 * We then read the inode for /boot into memory just above us at
 * 07E0:0000, and run through the direct block table (and the first
 * indirect block table, if necessary).
 *
 * We load /boot at seg:offset 4000:0000.
 *
 * Previous versions limited the size of /boot to 64k (loaded in a single
 * segment).  This version does not have this limitation.
 */
#define INODESEG	0x07e0	/* where we put /boot's inode's block */
#define INDIRECTSEG	0x07e0	/* where we put indirect table, if nec */
#define BOOTSEG		0x07c0	/* biosboot loaded here */
#define BOOTSTACKOFF  ((BOOTSEG << 4) - 4)  /* stack starts here, grows down */
#define LFMAGIC		0x464c  /* LFMAGIC (last two bytes of \7fELF) */
#define ELFMAGIC    0x464c457f  /* ELFMAGIC ("\7fELF") */

#define INODEOFF  ((INODESEG-BOOTSEG) << 4)

/*
 * The data passed by installboot is:
 *
 * inodeblk	uint32	the filesystem block that holds /boot's inode
 * inodedbl	uint32	the memory offset to the beginning of the
 *			direct block list (di_db[]).  (This is the
 *			offset within the block + $INODEOFF, which is
 *			where we load the block to.)
 * fs_bsize_p	uint16	the filesystem block size _in paragraphs_
 *			(i.e. fs_bsize / 16)
 * fs_bsize_s	uint16	the number of disk sectors in a filesystem
 *			block (i.e. fs_bsize / d_secsize). Directly written
 *			into the LBA command block, at lba_count.
 *			XXX LIMITED TO 127 BY PHOENIX EDD SPEC.
 * fsbtodb	uint8	shift count to convert filesystem blocks to
 *			disk blocks (sectors).  Note that this is NOT
 *			log2 fs_bsize, since fragmentation allows
 *			the trailing part of a file to use part of a
 *			filesystem block.  In other words, filesystem
 *			block numbers can point into the middle of
 *			filesystem blocks.
 * p_offset	uint32	the starting disk block (sector) of the
 *			filesystem
 * nblocks	uint16	the number of filesystem blocks to read.
 *			While this can be calculated as
 *			howmany(di_size, fs_bsize) it takes us too
 *			many code bytes to do it.
 *
 * All of these are patched directly into the code where they are used
 * (once only, each), to save space.
 *
 * One more symbol is exported, in anticipation of a "-c" flag in
 * installboot to force CHS reads:
 *
 * force_chs	uint8	set to the value 1 to force biosboot to use CHS
 *			reads (this will of course cause the boot sequence
 *			to fail if /boot is above 8 GB).
 */

	.globl	inodeblk, inodedbl, fs_bsize_p, fsbtodb, p_offset, nblocks
	.globl	fs_bsize_s, force_chs
	.type	inodeblk, @function
	.type	inodedbl, @function
	.type	fs_bsize_p, @function
	.type	fs_bsize_s, @function
	.type	fsbtodb, @function
	.type	p_offset, @function
	.type	nblocks, @function
	.type	force_chs, @function

/* Clobbers %ax, maybe more */
#define	putc(c)		movb	$c, %al;	call	Lchr

/* Clobbers %ax, %si, maybe more */
#define	puts(s)		movw	$s, %si;	call	Lmessage

	.text
	.code16
	.globl	_start
_start:
	jmp	begin
	nop

	/*
	 * BIOS Parameter Block.  Read by many disk utilities.
	 *
	 * We would have liked biosboot to go from the superblock to
	 * the root directory to the inode for /boot, thence to read
	 * its blocks into memory.
	 *
	 * As code and data space is quite tight in the 512-byte
	 * partition boot sector, we instead get installboot to pass
	 * us some pre-processed fields.
	 *
	 * We would have liked to put these in the BIOS parameter block,
	 * as that seems to be the right place to put them (it's really
	 * the equivalent of the superblock for FAT filesystems), but
	 * caution prevents us.
	 *
	 * For now, these fields are either directly in the code (when they
	 * are used once only) or at the end of this sector.
	 */

	. = _start + 3

	.asciz	"OpenBSD"

	/* BPB */
	. = _start + 0x0b
bpb:	.word	DEV_BSIZE			/* sector size */
	.byte	2				/* sectors/cluster */
	.word	0				/* reserved sectors */
	.byte	0				/* # of FAT */
	.word	0				/* root entries */
	.word	0				/* small sectors */
	.byte	0xf8				/* media type (hd) */
	.word	0				/* sectors/fat */
	.word	0				/* sectors per track */
	.word	0				/* # of heads */

	/* EBPB */
	. = _start + 0x1c
ebpb:	.long	16			/* hidden sectors */
	.long	0			/* large sectors */
	.word	0			/* physical disk */
	.byte	0x29			/* signature, needed by NT */
	.space	4, 0			/* volume serial number */
	.asciz	"UNIX LABEL"
	.asciz	"UFS 4.4"

	/* boot code */
	. = _start + 0x3e

begin:
	/* Fix up %cs just in case */
	ljmp	$BOOTSEG, $main

	/*
	 * Come here if we have to do a CHS boot, but we get an error from
	 * BIOS get drive parameters, or it returns nsectors == 0 (in which
	 * case we can't do the division we need to convert LBA sector
	 * number to CHS).
	 */
cant_boot:
	movb	$PBR_CANT_BOOT, %al
	jmp	err_print_crlf

main:
	/* Set up stack */
	xorw	%ax, %ax
	movw	%ax, %ss
	movw	$BOOTSTACKOFF, %sp

	/* Set up needed data segment reg */
	pushw	%cs
	popw	%ds			/* Now %cs == %ds, != %ss (%ss == 0) */

#ifdef SERIAL
	/* Initialize the serial port to 9600 baud, 8N1 */
	push	%dx
	movw	$0x00e3, %ax
	movw	SERIAL, %dx
	int	$0x14
	pop	%dx
#endif

#ifdef BDEBUG
	putc('R')
#endif

	/*
	 * We're going to print our sign-on message.
	 *
	 * We're now LBA-aware, and will use LBA to load /boot if the
	 * BIOS says it's available.  However, we have seen machines
	 * where CHS is required even when LBA is available.  Therefore
	 * we provide a way to force CHS use:
	 *
	 * If the SHIFT key is held down on entry, force CHS reads.
	 */
	movw	$load_msg+1, %si	/* "Loading" */
	movb	%dl, %dh

	/*
	 * BIOS call "INT 0x16 Get Keyboard Shift Flags
	 *	Call with	%ah = 0x02
	 *	Return:
	 *			%al = shift flags
	 *			%ah - undefined by many BIOSes
	 */
	movb	$0x02, %ah
	int	$0x16

	/*
	 * We provide the ability to force CHS use without having to hold
	 * down the SHIFT key each boot.  Just set the byte at force_chs
	 * to 1 (more accurately any value with either of the bottom two
	 * bits set, but the use of 1 is recommended).
	 */
force_chs = .+1
	orb	$0, %al

	testb	$0x3, %al		/* Either shift key down? */
	jz	no_force_chs

	decw	%si			/* "!Loading" indicates forced CHS */
	xorb	%dh, %dh		/* Pretend a floppy, so no LBA use */

no_force_chs:
	/* Print pretty message */
	call	Lmessage

	/*
	 * We will use LBA reads if we have LBA support, so find out.
	 */

	/*
	 * But don't even try on floppies, OR if forcing to CHS.
	 *
	 * (We're really testing %dl, but use %dh so we can force the
	 * top bit to zero to force CHS boot.)
	 */
	testb	$0x80, %dh
	jz	no_lba

	/*
	 * BIOS call "INT 0x13 Extensions Installation Check"
	 *	Call with	%ah = 0x41
	 *			%bx = 0x55AA
	 *			%dl = drive (0x80 for 1st hd, 0x81 for 2nd, etc)
	 *	Return:
	 *			carry set: failure
	 *				%ah = error code (0x01, invalid func)
	 *			carry clear: success
	 *				%bx = 0xAA55 (must verify)
	 *				%ah = major version of extensions
	 *				%al   (internal use)
	 *				%cx = capabilities bitmap
	 *					0x0001 - extnd disk access funcs
	 *					0x0002 - rem. drive ctrl funcs
	 *					0x0004 - EDD functions with EBP
	 *				%dx   (extension version?)
	 */

	pushw	%dx			/* Save the drive number (%dl) */
	movw	$0x55AA, %bx
	movb	$0x41, %ah
	int	$0x13
	popw	%dx			/* Retrieve drive number */

	jc	no_lba			/* Did the command work? Jump if not */
	cmpw	$0xAA55, %bx		/* Check that bl, bh exchanged */
	jne	no_lba			/* If not, don't have EDD extensions */
	testb	$0x01, %cl		/* And do we have "read" available? */
	jz	no_lba			/* Again, use CHS if not */

	/* We have LBA support, so that's the vector to use */

	movw	$load_lba, load_fsblock
	jmp	get_going

no_lba:
	pushw	%dx

	/*
	 * BIOS call "INT 0x13 Function 0x08" to get drive parameters
	 *	Call with        %ah = 0x08
	 *                       %dl = drive (0x80 for 1st hd, 0x81 for 2nd...)
	 *       Return:
	 *                       carry set: failure
	 *                           %ah = err code
	 *                       carry clear: success
	 *                           %ah = 0x00
	 *                           %al = 0x00 (some BIOSes)
	 *                           %ch = 0x00 (some BIOSes)
	 *                           %ch = max-cylinder & 0xFF
	 *                           %cl = max sector | rest of max-cyl bits
	 *                           %dh = max head number
	 *                           %dl = number of drives
	 *                                 (according to Ralph Brown Int List)
	 */
	movb	$0x08, %ah
	int	$0x13			/* We need to know heads & sectors */

	jc	cant_boot		/* If error, can't boot */

	movb	%dh, maxheads		/* Remember this */

	andb	$0x3F, %cl
	jz	cant_boot
	movb	%cl, nsectors

	putc(CHAR_CHS_READ)		/* Indicate (subtly) CHS reads */

	popw	%dx			/* Retrieve the drive number */

get_going:
	/*
	 * Older versions of biosboot used to set up the destination
	 * segment, and increase the target offset every time a number
	 * of blocks was read.  That limits /boot to 64k.
	 *
	 * In order to support /boots > 64k, we always read to offset
	 * 0000 in the target segment, and just increase the target segment
	 * each time.
	 */

	/*
	 * We would do movl inodeblk, %eax  here, but that instruction
	 * is 4 bytes long; add 4 bytes for data takes 8 bytes.  Using
	 * a load immediate takes 6 bytes, and we just get installboot
	 * to patch here, rather than data anywhere else.
	 */
inodeblk = .+2
	movl	$0x90909090, %eax	/* mov $inodeblk, %eax */

	movw	$INODESEG, %bx		/* Where to put /boot's inode */

	/*
	 * %eax - filesystem block to read
	 * %bx  - target segment (target offset is 0000)
	 * %dl  - BIOS drive number
	 */
	call	*load_fsblock		/* This will crash'n'burn on errs */

	/*
	 * We now have /boot's inode in memory.
	 *
	 * /usr/include/ufs/ufs/dinode.h for the details:
	 *
	 * Offset  8 (decimal): 64-bit file size (only use low 32 bits)
	 * Offset 40 (decimal): list of NDADDR (12) direct disk blocks
	 * Offset 88 (decimal): list of NIADDR (3) indirect disk blocks
	 *
	 * NOTE: list of indirect blocks immediately follows list of
	 * direct blocks.  We use this fact in the code.
	 *
	 * We only support loading from direct blocks plus the first
	 * indirect block.  This is the same as the previous biosboot/
	 * installboot limit.  Note that, with default 16,384-bytes
	 * filesystem blocks, the direct block list supports files up
	 * to 192 KB.  /boot is currently around 60 KB.
	 *
	 * The on-disk format can't change (filesystems with this format
	 * already exist) so okay to hardcode offsets here.
	 *
	 * The nice thing about doing things with filesystem blocks
	 * rather than sectors is that filesystem blocks numbers have
	 * 32 bits, so fit into a single register (even if "e"d).
	 *
	 * Note that this code does need updating if booting from a new
	 * filesystem is required.
	 */
#define NDADDR	12
#define di_db	40			/* Not used; addr put in by instboot */
#define di_ib	88			/* Not used; run on from direct blks */

	/*
	 * Register usage:
	 *
	 * %eax - block number for load_fsblock
	 * %bx  - target segment (target offset is 0000) for load_fsblock
	 * %dl  - BIOS drive number for load_fsblock
	 * %esi - points to block table in inode/indirect block
	 * %cx  - number of blocks to load within loop (i.e. from current
	 *	  block list, which is either the direct block list di_db[]
	 *	  or the indirect block list)
	 * %di  - total number of blocks to load
	 */

	/*
	 * We would do movl inodedbl, %esi  here, but that instruction
	 * is 4 bytes long; add 4 bytes for data takes 8 bytes.  Using
	 * a load immediate takes 6 bytes, and we just get installboot
	 * to patch here, rather than in data anywhere else.
	 */
inodedbl = .+2
	movl	$0x90909090, %esi	/* mov $inodedbl, %esi */
					/* Now esi -> di_db[] */

nblocks = .+1
	movw	$0x9090, %di		/* mov nblocks, %di */
	movw	%di, %cx
	cmpw	$NDADDR, %cx
	jc	1f
	movw	$NDADDR, %cx
1:					/* %cx = min(nblocks, $NADDR) */

	movw	$(LOADADDR >> 4), %bx	/* Target segment for /boot */

load_blocks:
	putc(CHAR_BLOCK_READ)		/* Show progress indicator */

	cld

	/* Get the next filesystem block number into %eax */
	lodsl			/* %eax = *(%si++), make sure 0x66 0xad */

	pushal				/* Save all 32-bit registers */

	/*
	 * Read a single filesystem block (will almost certainly be multiple
	 * disk sectors)
	 *
	 * %eax - filesystem block to read
	 * %bx  - target segment (target offset is 0000)
	 * %dl  - BIOS drive number
	 */
	call	*load_fsblock		/* This will crash'n'burn on errs */

	popal				/* Restore 32-bit registers */

	/*
	 * We want to put addw fs_bsize_p, %bx, which takes 4 bytes
	 * of code and two bytes of data.
	 *
	 * Instead, use an immediate load, and have installboot patch
	 * here directly.
	 */
	/* Move on one filesystem block */
fs_bsize_p = .+2
	addw	$0x9090, %bx		/* addw $fs_bsize_p, %bx */

	decw	%di
	loop	load_blocks

	/* %cx == 0 ... important it stays this way (used later) */

	/*
	 * Finished reading a set of blocks.
	 *
	 * This was either the direct blocks, and there may or may not
	 * be indirect blocks to read, or it was the indirect blocks,
	 * and we may or may not have read in all of /boot.  (Ideally
	 * will have read in all of /boot.)
	 */
	orw	%di, %di
	jz	done_load		/* No more sectors to read */

	/* We have more blocks to load */

	/* We only support a single indirect block (the same as previous
	 * versions of installboot.  This is required for the boot floppies.
	 *
	 * We use a bit of the code to store a flag that indicates
	 * whether we have read the first indirect block or not.
	 *
	 * If we've already read the indirect list, we can't load this /boot.
	 *
	 * indirect	uint8	0 => running through load_blocks loop reading
	 *			direct blocks.  If != 0, we're reading the
	 *			indirect blocks.  Must use a field that is
	 *			initialised to 0.
	 */
indirect = .+2
	movw	$PBR_TOO_MANY_INDIRECTS, %ax	/* movb $PRB_TOO..., %al */
						/* movb indirect, %ah */
	orb	%ah, %ah
	jnz	err_print_crlf

	incb	indirect		/* No need to worry about wrap */
					/* around, as this will only be done */
					/* once before we fail */

	/* Okay, let's read in the indirect block */

	lodsl				/* Get blk num of 1st indirect blk */

	pushw	%bx			/* Remember where we got to */
	movw	$INODESEG, %bx
	call	*load_fsblock		/* This will crash'n'burn on errs */
	popw	%bx			/* Indirect blocks get added on to */
					/* just after where we got to */
	movl	$INODEOFF, %esi
	movw	%di, %cx		/* How many blocks left to read */

	jmp	load_blocks

done_load:
	puts(crlf)

	/* %cx == 0 from loop above... keep it that way */

	/*
	 * Check the magic signature at the beginning of /boot.
	 * Since /boot is now ELF, this should be 0x7F E L F.
	 */
	movw	$(LOADADDR >> 4), %ax	/* Target segment */
	movw	%ax, %es

	/*
	 * We cheat a little here, and only check the L and F.
	 *
	 * (Saves 3 bytes of code... the two signature bytes we
	 * don't check, and the operand size prefix that's not
	 * needed.)
	 */
	cmpw	$LFMAGIC, %es:2(,1)
	je	exec_boot

	movb	$PBR_BAD_MAGIC, %al

err_print:
	movw	$err_txt, %si
err_print2:
	movb	%al, err_id
err_stop:
	call	Lmessage
stay_stopped:
	sti				/* Ensure Ctl-Alt-Del will work */
	hlt				/* (don't require power cycle) */
	jmp	stay_stopped		/* Just to make sure :-) */

exec_boot:
	/* At this point we could try to use the entry point in
	 * the image we just loaded.  But if we do that, we also
	 * have to potentially support loading that image where it
	 * is supposed to go.  Screw it, just assume that the image
	 * is sane.
	 */
#ifdef BDEBUG
	putc('P')
#endif

	/* %cx == 0 from loop above... keep it that way */

	/*
	 * We want to do movzbl %dl, %eax ; pushl %eax to zero-extend the
	 * drive number to 32 bits and pass it to /boot.  However, this
	 * takes 6 bytes.
	 *
	 * Doing it this way saves 2 bytes.
	 */
	pushw	%cx
	movb	%dl, %cl
	pushw	%cx

	pushl	$BOOTMAGIC	/* use some magic */

	/* jmp	/boot */
	ljmp $(LINKADDR >> 4), $0
	/* not reached */

/*
 * Load a single filesystem block into memory using CHS calls.
 *
 * Input:	%eax - 32-bit filesystem block number
 * 		%bx  - target segment (target offset is 0000)
 * 		%dl  - BIOS drive number
 *
 * Output:	block successfully read in (panics if not)
 *		all general purpose registers may have been trashed
 */
load_chs:
	/*
	 * BIOS call "INT 0x13 Function 0x2" to read sectors from disk into
	 * memory.
	 *	Call with        %ah = 0x42
	 *                       %ah = 0x2
	 *                       %al = number of sectors
	 *                       %ch = cylinder & 0xFF
	 *                       %cl = sector (0-63) | rest of cylinder bits
	 *                       %dh = head
	 *                       %dl = drive (0x80 for 1st hd, 0x81 for 2nd...)
	 *                       %es:%bx = segment:offset of buffer
	 *       Return:
	 *                       carry set: failure
	 *                           %ah = err code
	 *                           %al = number of sectors transferred
	 *                       carry clear: success
	 *                           %al = 0x0 OR number of sectors transferred
	 *                                 (depends on BIOS!)
	 *                                 (according to Ralph Brown Int List)
	 */

	/* Convert the filesystem block into a sector value */
	call	fsbtosector
	movl	lba_sector, %eax	/* we can only use 24 bits, really */

	movw	fs_bsize_s, %cx	/* sectors per filesystem block */

	/*
	 * Some BIOSes require that reads don't cross track boundaries.
	 * Therefore we do all CHS reads single-sector.
	 */
calc_chs:
	pushal
	movw	%bx, %es	/* Set up target segment */

	pushw	%dx		/* Save drive number (in %dl) */
	xorl	%edx, %edx
	movl	%edx, %ecx

nsectors = .+1
	movb	$0x90, %cl	/* movb $nsectors, %cl */
				/* Doing it this way saves 4-2 = 2 bytes code */
				/* bytes (no data, since we would overload) */

	divl	%ecx, %eax
				/* Now have sector number in %dl */
	pushw	%dx		/* Remember for later */

	xorl	%edx, %edx

maxheads = .+1
	movb	$0x90, %cl	/* movb $maxheads, %cl; 0 <= maxheads <= 255 */
				/* Doing it this way saves 4-2 = 2 code */
				/* bytes (no data, since we would overload */

	incw	%cx		/* Number of heads is 1..256, no "/0" worries */

	divl	%ecx, %eax
				/* Have head number in %dl */
				/* Cylinder number in %ax */
	movb	%al, %ch	/* Bottom 8 bits of cyl number */
	shlb	$6, %ah		/* Move up top 2 bits of cyl number */
	movb	%ah, %cl	/* Top 2 bits of cyl number in here */

	popw	%bx		/* (pushed %dx, but need %dl for now */
	incb	%bl		/* Sector numbers run from 1, not 0 */
	orb	%bl, %cl	/* Or the sector number into top bits cyl */

				/* Remember, %dl has head number */
	popw	%ax
				/* %al has BIOS drive number -> %dl */

	movb	%dl, %dh	/* Now %dh has head number (from 0) */
	movb	%al, %dl	/* Now %dl has BIOS drive number */

	xorw	%bx, %bx	/* Set up target offset */

	movw	$0x0201, %ax	/* %al = 1 - read one sector at a time */
				/* %ah = 2 - int 0x13 function for CHS read */

	call	do_int_13	/* saves us 1 byte :-) */

	/* Get the next sector */

	popal
	incl	%eax
	addw	$32, %bx	/* Number of segments/paras in a sector */
	loop	calc_chs

	ret

	/* read error */
read_error:
	movb	$PBR_READ_ERROR, %al
err_print_crlf:
	movw	$err_txt_crlf, %si
	jmp	err_print2

/*
 * Load a single filesystem block into memory using LBA calls.
 *
 * Input:	%eax - 32-bit filesystem block number
 * 		%bx  - target segment (target offset is 0000)
 * 		%dl  - BIOS drive number
 *
 * Output:	block successfully read in (panics if not)
 *		all general purpose registers may have been trashed
 */
load_lba:
	/*
	 * BIOS call "INT 0x13 Extensions Extended Read"
	 *	Call with	%ah = 0x42
	 *			%dl = drive (0x80 for 1st hd, 0x81 for 2nd, etc)
	 *			%ds:%si = segment:offset of command packet
	 *	Return:
	 *			carry set: failure
	 *				%ah = error code (0x01, invalid func)
	 *				command packet's sector count field set
	 *				to the number of sectors successfully
	 *				transferred
	 *			carry clear: success
	 *				%ah = 0 (success)
	 *	Command Packet:
	 *			0x0000	BYTE	packet size (0x10 or 0x18)
	 *			0x0001	BYTE	reserved (should be 0)
	 *			0x0002	WORD	sectors to transfer (max 127)
	 *			0x0004	DWORD	seg:offset of transfer buffer
	 *			0x0008	QWORD	starting sector number
	 */
	call	fsbtosector		/* Set up lba_sector & lba_sector+4 */

	/* movb	%dh, lba_count		<- XXX done by installboot */
	movw	%bx, lba_seg
	movw	$lba_command, %si
	movb	$0x42, %ah
do_int_13:
	int	$0x13
	jc	read_error

	ret

/*
 * Converts a given filesystem block number into a disk sector
 * at lba_sector and lba_sector+4.
 *
 * Input:	%eax - 32-bit filesystem block number
 *
 * Output:	lba_sector and lba_sector+4 set up
 *		XXX
 */
fsbtosector:
	/*
	 * We want to do
	 *
	 * movb	fsbtodb, %ch		/# Shift counts we'll need #/
	 * movb	$32, %cl
	 *
	 * which is 6 bytes of code + 1 byte of data.
	 *
	 * We'll actually code it with an immediate 16-bit load into %cx,
	 * which is just 3 bytes of data (saves 4 bytes).
	 */
fsbtodb = .+2
	movw	$0x9020, %cx		/* %ch = fsbtodb, %cl = 0x20 */

	pushl	%eax
	subb	%ch, %cl
	shrl	%cl, %eax
	movl	%eax, lba_sector+4
	popl	%eax

	movb	%ch, %cl
	shll	%cl, %eax

	/*
	 * And add p_offset, which is the block offset to the start
	 * of the filesystem.
	 *
	 * We would do addl p_offset, %eax, which is 5 bytes of code
	 * and 4 bytes of data, but it's more efficient to have
	 * installboot patch directly in the code (this variable is
	 * only used here) for 6 bytes of code (but no data).
	 */
p_offset = .+2
	addl	$0x90909090, %eax	/* addl $p_offset, %eax */

	movl	%eax, lba_sector
	jnc	1f

	incl	lba_sector+4
1:
	ret

/*
 * Display string
 */
Lmessage:
	cld
1:
	lodsb			/* load a byte into %al */
	orb	%al, %al
	jz	1f
	call	Lchr
	jmp	1b

/*
 *	Lchr: write the character in %al to console
 */
Lchr:
#ifdef SERIAL
	pushw	%dx
	movb	$0x01, %ah
	xorw	%dx, %dx
	movb	SERIAL, %dl
	int	$0x14
	popw	%dx
#else
	pushw	%bx
	movb	$0x0e, %ah
	xorw	%bx, %bx
	incw	%bx		/* movw $0x01, %bx */
	int	$0x10
	popw	%bx
#endif
1:
	ret

	/* .data */

/* vector to the routine to read a particular filesystem block for us */
load_fsblock:
	.word	load_chs

/* This next block is used for the EDD command packet used to read /boot
 * sectors.
 *
 * lba_count is set up for us by installboot.  It is the number of sectors
 * in a filesystem block.  (Max value 127.)
 *
 * XXX The EDD limit of 127 sectors in one read means that we currently
 *     restrict filesystem blocks to 127 sectors, or < 64 KB.  That is
 *     effectively a 32 KB block limit, as filesystem block sizes are
 *     powers of two.  The default filesystem block size is 16 KB.
 *
 *     I say we run with this limitation and see where it bites us...
 */

lba_command:
	.byte	0x10			/* size of command packet */
	.byte	0x00			/* reserved */
fs_bsize_s:
lba_count:
	.word	0			/* sectors to transfer, max 127 */
	.word	0			/* target buffer, offset */
lba_seg:
	.word	0			/* target buffer, segment */
lba_sector:
	.long	0, 0			/* sector number */

load_msg:
	.asciz	"!Loading"
err_txt_crlf:
	.ascii	"\r\n"
err_txt:
	.ascii	"ERR "
err_id:
	.ascii	"?"
crlf:	.asciz	"\r\n"

	. = 0x200 - 2
	/* a little signature */
	.word	DOSMBR_SIGNATURE


code:
#	$OpenBSD: Makefile,v 1.56 2014/07/13 09:26:08 jasper Exp $

COPTS?=
MAN?=	boot.8
MLINKS?=boot.8 boot.conf.5

.if !empty(COPTS:M-DFDBOOT)
SOFTRAID?=no
.else
SOFTRAID?=yes
.endif

.if ${MACHINE} == "i386"
S=	${.CURDIR}/../../../..
SADIR=	${.CURDIR}/..

PROG?=	boot
LD?=	ld
SIZE?=	size
LDFLAGS+=-nostdlib -Bstatic -nopie

.if defined(DEBUGFLAGS) && !empty(DEBUGFLAGS:M-D_TEST)
CLEANFILES+=	srt0.o
SRCS=	crt0.c conf.c
.else
CLEANFILES+=	crt0.o
SRCS=	srt0.S conf.c
LDFLAGS+=-Ttext $(LINKADDR) -N -x
.endif

.PATH:	${S}/stand/boot
SRCS+=	boot.c bootarg.c cmd.c vars.c

.PATH:	${SADIR}/libsa
SRCS+=	debug_i386.S
SRCS+=	alloca.S gidt.S random_i386.S
SRCS+=	apmprobe.c debug.c pciprobe.c ps2probe.c
SRCS+=	cmd_i386.c dev_i386.c exec_i386.c gateA20.c machdep.c
SRCS+=	bioscons.c biosdev.c diskprobe.c memprobe.c time.c
.if ${SOFTRAID:L} == "yes"
SRCS+=	softraid.c
.endif

.PATH:	${S}/lib/libsa
SRCS+=	alloc.c ctime.c exit.c getchar.c memcmp.c memcpy.c memset.c printf.c \
	putchar.c snprintf.c strcmp.c strerror.c strlen.c strncmp.c strncpy.c \
	strtol.c strtoll.c
SRCS+=	close.c closeall.c cons.c cread.c dev.c disklabel.c dkcksum.c fstat.c \
	lseek.c open.c read.c readdir.c stat.c
SRCS+=	elf32.c elf64.c loadfile.c
SRCS+=	ufs.c
.if ${SOFTRAID:L} == "yes"
SRCS+=	aes_xts.c explicit_bzero.c hmac_sha1.c pbkdf2.c rijndael.c sha1.c
.endif

.PATH:	${S}/lib/libkern
SRCS+=	divdi3.c moddi3.c qdivrem.c
SRCS+=	strlcpy.c

.PATH:	${S}/lib/libz
SRCS+=	adler32.c crc32.c inflate.c inftrees.c

boot.bin: boot
	objcopy -v -O binary ${PROG} boot.bin

CLEANFILES+= ${PROG}.new

.depend srt0.o gidt.o: assym.h

${PROG}: $(OBJS)
	$(LD) $(LDFLAGS) -o ${PROG}.new $(OBJS)
	@perl ${SADIR}/boot/check-boot.pl ${PROG}.new
	@mv ${PROG}.new ${PROG}
	@$(SIZE) ${PROG}

.else
NOPROG=
.endif

.include <bsd.prog.mk>

.if ${SOFTRAID:L} == "yes"
CPPFLAGS+=-DSOFTRAID
.endif
CPPFLAGS+=-DBOOTMAGIC=$(BOOTMAGIC) ${DEBUGFLAGS} -DLINKADDR=${LINKADDR}
CPPFLAGS+=-DSLOW -DSMALL -DNOBYFOUR -DNO_GZIP -DDYNAMIC_CRC_TABLE
CPPFLAGS+=-DHEAP_LIMIT=${HEAP_LIMIT} -I${S}/stand/boot #-DCOMPAT_UFS
CFLAGS+=-m32 $(SACFLAGS) -D__INTERNAL_LIBSA_CREAD -fno-pie
AFLAGS+=-m32 # -Wa,-R
# AFLAGS+=-Wa,-a
AFLAGS+=-fno-pie

license: unknown


code:
#!/usr/bin/perl -w

#
# Copyright (c) 2012 Joel Sing <jsing@openbsd.org>
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#

#
# Ensure that the file offset for .text and .data aligns with the LMA. If they
# do not match boot(8) will fail since biosboot(8) does not perform relocation.
#

$LINKBASE = 0x40000;

if (scalar(@ARGV) != 1) {
	print "Usage: check-boot.pl boot\n";
	exit(2);
}

$boot = shift @ARGV;
if (! -f $boot) {
	print "No such file - $boot\n";
	exit(2);
}

$valid = 1;
$sections = 0;

open(OBJDUMP, "/usr/bin/objdump -h $boot |") || die "Failed to run objdump";
while (<OBJDUMP>) {
	@section = split / +/, $_;
	next if scalar(@section) < 7;
	if ($section[2] =~ /(\.(text|data))/) {
		$name = $1;
		$lma = hex($section[5]);
		$fileoff = hex($section[6]);
		$offset = $lma - $LINKBASE;
		$sections++;

		if ($fileoff != $offset) {
			printf "$name has incorrect file offset " .
			    "0x%x (should be 0x%x)\n", $fileoff, $offset;
			$valid = 0;
		}
	}
}
close(OBJDUMP);

if ($sections != 2) {
	print "Failed to find .text or .data section!\n";
	exit(1);
}

exit(1) if ! $valid;


code:
/*	$OpenBSD: conf.c,v 1.55 2014/02/18 13:56:02 jsing Exp $	*/

/*
 * Copyright (c) 1996 Michael Shalayeff
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <sys/param.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <libsa.h>
#include <lib/libsa/ufs.h>
#ifdef notdef
#include <lib/libsa/cd9660.h>
#include <lib/libsa/fat.h>
#include <lib/libsa/nfs.h>
#include <lib/libsa/tftp.h>
#include <lib/libsa/netif.h>
#endif
#include <lib/libsa/unixdev.h>
#include <biosdev.h>
#include <dev/cons.h>
#include "debug.h"

const char version[] = "3.26";
int	debug = 1;

void (*sa_cleanup)(void) = NULL;

void (*i386_probe1[])(void) = {
	ps2probe, gateA20on, debug_init, cninit,
	apmprobe, pciprobe, /* smpprobe, */ memprobe
};
void (*i386_probe2[])(void) = {
 	diskprobe
};

struct i386_boot_probes probe_list[] = {
	{ "probing", i386_probe1, nitems(i386_probe1) },
	{ "disk",    i386_probe2, nitems(i386_probe2) }
};
int nibprobes = nitems(probe_list);

struct fs_ops file_system[] = {
	{ ufs_open,    ufs_close,    ufs_read,    ufs_write,    ufs_seek,
	  ufs_stat,    ufs_readdir    },
#ifdef notdef
	{ fat_open,    fat_close,    fat_read,    fat_write,    fat_seek,
	  fat_stat,    fat_readdir    },
	{ nfs_open,    nfs_close,    nfs_read,    nfs_write,    nfs_seek,
	  nfs_stat,    nfs_readdir    },
	{ cd9660_open, cd9660_close, cd9660_read, cd9660_write, cd9660_seek,
	  cd9660_stat, cd9660_readdir },
#endif
#ifdef _TEST
	{ null_open,   null_close,   null_read,   null_write,   null_seek,
	  null_stat,   null_readdir   }
#endif
};
int nfsys = nitems(file_system);

struct devsw	devsw[] = {
#ifdef _TEST
	{ "UNIX", unixstrategy, unixopen, unixclose, unixioctl },
#else
	{ "BIOS", biosstrategy, biosopen, biosclose, biosioctl },
#endif
#if 0
	{ "TFTP", tftpstrategy, tftpopen, tftpclose, tftpioctl },
#endif
};
int ndevs = nitems(devsw);

#ifdef notdef
struct netif_driver	*netif_drivers[] = {
	NULL
};
int n_netif_drivers = nitems(netif_drivers);
#endif

struct consdev constab[] = {
#ifdef _TEST
	{ unix_probe, unix_init, unix_getc, unix_putc },
#else
	{ pc_probe, pc_init, pc_getc, pc_putc },
	{ com_probe, com_init, com_getc, com_putc },
#endif
	{ NULL }
};
struct consdev *cn_tab = constab;


code:
/*	$OpenBSD: crt0.c,v 1.6 2013/03/21 21:51:00 deraadt Exp $	*/

/*
 * Copyright (c) 1997-1998 Michael Shalayeff
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */

#include <sys/param.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <termios.h>
#include "libsa.h"
#include <lib/libsa/unixdev.h>

void start(void) asm("start");
void _rtt(void);
extern int  boot(dev_t);
static void domap(void);
static void seterm(void);

void
start()
{
	domap();
	seterm();
	uexit(boot(0));
}

void
_rtt()
{
	uexit(1);
}

#define ummap(a,l,p,f,fd,o) (caddr_t)syscall((quad_t)SYS_mmap,a,l,p,f,fd,0,o)

static void
domap()
{
	extern char end[];
	register caddr_t p = (caddr_t)(((u_long)end + PAGE_MASK) & ~PAGE_MASK);

	/* map heap */
	if ( (p = ummap(p, 32*PAGE_SIZE, PROT_READ|PROT_WRITE,
		   MAP_FIXED|MAP_ANON, -1, 0)) == (caddr_t)-1) {
		printf("mmap failed: %d\n", errno);
		uexit(1);
	}
#ifdef DEBUG
	else
		printf("mmap==%p\n", p);
#endif

	/* map kernel */
	if ( (p = ummap(0x100000, 0xf00000, PROT_READ|PROT_WRITE,
		   MAP_FIXED|MAP_ANON, -1, 0)) == (caddr_t)-1) {
		printf("mmap failed: %d\n", errno);
		uexit(1);
	}
#ifdef DEBUG
	else
		printf("mmap==%p\n", p);
#endif

}

void
seterm()
{
	struct termios tc;

	if (uioctl(0, TIOCGETA, (char *)&tc) < 0) {
		printf("cannot get tty\n");
		uexit(1);
	}

        tc.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
        tc.c_oflag &= ~OPOST;
        tc.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
        tc.c_cflag &= ~(CSIZE|PARENB);
        tc.c_cflag |= CS8;

	if (uioctl(0, TIOCSETA, (char *)&tc) < 0) {
		printf("cannot set tty\n");
		uexit(1);
	}
}


code:
/*	$OpenBSD: srt0.S,v 1.16 2012/10/12 15:00:33 jsing Exp $	*/

/*
 * Copyright (c) 1997 Michael Shalayeff
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */
#include <machine/asm.h>
#include <assym.h>

#define BOOTSTACK 0xfffc

	.globl	_C_LABEL(end)
	.globl	_C_LABEL(edata)
	.globl	_C_LABEL(boot)
	.globl	_C_LABEL(_rtt)
	.globl	_C_LABEL(bios_bootdev)
	.globl	_ASM_LABEL(pmm_init)
	.globl	Gdtr

	.text
	.align	16
	.code16
	.globl	_start
_start:
	popl %eax
	cmpl $BOOTMAGIC, %eax
	je	1f
#ifdef DEBUG
	movl	$0xb80a0, %ebx
	addr32 movl $0x07420742, (%ebx)
#endif
1:
	popl %edx
	cli
	pushl	%cs
	popl	%ds
	addr32 data32 lgdt	(Gdtr - LINKADDR)
	movl	%cr0, %eax
	orl $CR0_PE, %eax
	data32 movl %eax, %cr0
	data32 ljmp $8, $1f
1:
	.code32
	movl	$0x10,%eax
	mov	%ax,%ds
	mov	%ax,%ss
	mov	%ax,%es
	mov	%ax,%fs
	mov	%ax,%gs
	movl	$BOOTSTACK,%esp
	pushl	%edx
	movl	%edx, _C_LABEL(bios_bootdev)

	/* Now do it all */
#ifdef DEBUG
	movl	$0xb80a4, %ebx
	movl	$0x07520752, (%ebx)
#endif
	/* zero .bss */
	xorl	%eax, %eax
	movl	$_C_LABEL(end), %ecx
	subl	$_C_LABEL(edata),%ecx
	movl	$_C_LABEL(edata), %edi
	cld
	rep;	stosb

	call	_ASM_LABEL(pmm_init)
	call	_C_LABEL(boot)

	jmp	_C_LABEL(_rtt)


code:
#	$OpenBSD: Makefile,v 1.23 2014/07/15 22:14:37 deraadt Exp $

MAN=	cdboot.8

.if ${MACHINE} == "i386"
S=	${.CURDIR}/../../../..
SADIR=	${.CURDIR}/..

PROG=	cdboot
SRCS=	srt0.S boot.c conf.c
LD?=	ld
SIZE?=	size
LDFLAGS+=-nostdlib -Bstatic -Ttext $(LINKADDR) -N -x -nopie
INSTALL_STRIP=
BINMODE=644

CLEANFILES+=	crt0.o ${PROG}.whole

.PATH:	${SADIR}/libsa
SRCS+=	alloca.S debug_i386.S gidt.S random_i386.S
SRCS+=	cmd_i386.c dev_i386.c exec_i386.c gateA20.c machdep.c
SRCS+=	apmprobe.c bioscons.c biosdev.c debug.c diskprobe.c memprobe.c \
	pciprobe.c ps2probe.c time.c
SRCS+=	softraid.c

.PATH:	${S}/stand/boot
SRCS+=	bootarg.c cmd.c vars.c

.PATH:	${S}/lib/libsa
SRCS+=	alloc.c ctime.c exit.c getchar.c memcmp.c memcpy.c memset.c printf.c \
	putchar.c snprintf.c strcmp.c strerror.c strlen.c strncmp.c strncpy.c \
	strtol.c strtoll.c
SRCS+=	close.c closeall.c cons.c cread.c dev.c disklabel.c dkcksum.c fstat.c \
	lseek.c open.c read.c readdir.c stat.c
SRCS+=	elf32.c elf64.c loadfile.c
SRCS+=	cd9660.c ufs.c
SRCS+=	aes_xts.c explicit_bzero.c hmac_sha1.c pbkdf2.c rijndael.c sha1.c

.PATH:	${S}/lib/libkern/arch/i386
SRCS+=	moddi3.c divdi3.c qdivrem.c

.PATH:	${S}/lib/libkern
SRCS+=	strlcpy.c

.PATH:	${S}/lib/libz
SRCS+=	adler32.c crc32.c inflate.c inftrees.c

.depend srt0.o gidt.o: assym.h

${PROG}: $(OBJS)
	$(LD) $(LDFLAGS) -o ${PROG} $(OBJS)
	@$(SIZE) ${PROG}
	cp ${PROG} ${PROG}.whole
	@if [ -x ${.OBJDIR}/${PROG} ]; then \
		objcopy -O binary ${PROG} ${.OBJDIR}/.tmp;\
		mv -f ${.OBJDIR}/.tmp ${.OBJDIR}/${PROG}; \
		ls -l ${.OBJDIR}/${PROG}; \
	fi

.else
NOPROG=
.endif

.include <bsd.prog.mk>

CPPFLAGS+=-DSOFTRAID
CPPFLAGS+=-DBOOTMAGIC=$(BOOTMAGIC) ${DEBUGFLAGS}
CPPFLAGS+=-DLINKADDR=${LINKADDR}
CPPFLAGS+=-DSLOW -DSMALL -DNOBYFOUR -DNO_GZIP -DDYNAMIC_CRC_TABLE
CPPFLAGS+=-I${S}/stand/boot
CFLAGS+=${SACFLAGS} -D__INTERNAL_LIBSA_CREAD
CFLAGS+=-DOSREV=\"${OSREV}\" -DMACHINE=\"${MACHINE}\"
CFLAGS+=-DKERNEL=\"/${OSREV}/${MACHINE}/bsd.rd\"
CFLAGS+=-fno-pie
#AFLAGS+=-Wa,-R
# AFLAGS+=-Wa,-a
AFLAGS+=-fno-pie


code:
/*	$OpenBSD: conf.c,v 1.24 2014/02/18 13:56:02 jsing Exp $	*/

/*
 * Copyright (c) 2004 Tom Cosgrove
 * Copyright (c) 1996 Michael Shalayeff
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <sys/param.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <libsa.h>
#include <lib/libsa/ufs.h>
#include <lib/libsa/cd9660.h>
#ifdef notdef
#include <lib/libsa/fat.h>
#include <lib/libsa/nfs.h>
#include <lib/libsa/tftp.h>
#include <lib/libsa/netif.h>
#endif
#include <lib/libsa/unixdev.h>
#include <biosdev.h>
#include <dev/cons.h>
#include "debug.h"

const char version[] = "3.23";
int	debug = 1;

#undef _TEST

void (*sa_cleanup)(void) = NULL;

void (*i386_probe1[])(void) = {
	ps2probe, gateA20on, debug_init, cninit, apmprobe,
	pciprobe, /* smpprobe, */ memprobe
};
void (*i386_probe2[])(void) = {
	diskprobe, cdprobe
};

struct i386_boot_probes probe_list[] = {
	{ "probing", i386_probe1, nitems(i386_probe1) },
	{ "disk",    i386_probe2, nitems(i386_probe2) },
};
int nibprobes = nitems(probe_list);

struct fs_ops file_system[] = {
	{ ufs_open,    ufs_close,    ufs_read,    ufs_write,    ufs_seek,
	  ufs_stat,    ufs_readdir    },
	{ cd9660_open, cd9660_close, cd9660_read, cd9660_write, cd9660_seek,
	  cd9660_stat, cd9660_readdir },
#ifdef notdef
	{ tftp_open,   tftp_close,   tftp_read,   tftp_write,   tftp_seek,
	  tftp_stat,   tftp_readdir   },
	{ nfs_open,    nfs_close,    nfs_read,    nfs_write,    nfs_seek,
	  nfs_stat,    nfs_readdir    },
	{ fat_open,    fat_close,    fat_read,    fat_write,    fat_seek,
	  fat_stat,    fat_readdir    },
#endif
#ifdef _TEST
	{ null_open,   null_close,   null_read,   null_write,   null_seek,
	  null_stat,   null_readdir   }
#endif
};
int nfsys = nitems(file_system);

struct devsw	devsw[] = {
#ifdef _TEST
	{ "UNIX", unixstrategy, unixopen, unixclose, unixioctl },
#else
	{ "BIOS", biosstrategy, biosopen, biosclose, biosioctl },
#endif
};
int ndevs = nitems(devsw);

struct consdev constab[] = {
#ifdef _TEST
	{ unix_probe, unix_init, unix_getc, unix_putc },
#else
	{ pc_probe, pc_init, pc_getc, pc_putc },
	{ com_probe, com_init, com_getc, com_putc },
#endif
	{ NULL }
};
struct consdev *cn_tab = constab;


code:
/*	$OpenBSD: srt0.S,v 1.3 2012/10/31 14:31:30 jsing Exp $	*/

/*
 * Copyright (c) 1997 Michael Shalayeff
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */
#include <machine/asm.h>
#include <assym.h>

#define BOOTSTACK 0xfffc

	.globl	_C_LABEL(end)
	.globl	_C_LABEL(edata)
	.globl	_C_LABEL(boot)
	.globl	_C_LABEL(_rtt)
	.globl	_C_LABEL(bios_bootdev)
	.globl	_ASM_LABEL(pmm_init)
	.globl	Gdtr

	.text
	.code16
	.globl	_start
_start:
#ifdef DEBUG
	movl	$0xb80a0, %ebx
	addr32 movl $0x07420742, (%ebx)
#endif

/* Clobbers %ax, maybe more */
#define	putc(c)		movb	$c, %al;	call	Lchr

	/*
	 * We operate as a no emulation boot image, as defined by the
	 * El Torito Bootable CD-ROM Format Specification v1.0.  We use
	 * a load segment of 0x07C0 (physical load address of 0x7C00).
	 * Like the standard /boot, we are linked to run at 0x40120
	 * (load address 0x40000), so we relocate to there.
	 *
	 * From 0x7C00 to 0x40000 is 0x38400 (230400) bytes, so we don't
	 * have to worry about an overlapping copy until cdboot is
	 * over 225 KB.
	 *
	 * Our cdbr CD-ROM boot sector passes us the drive number to use
	 * in %dl.
	 */
#define CDBOOTADDR	0x7c00		/* Address where BIOS loads up */
	xorw	%ax, %ax
	movw	%ax, %ss		/* CPU disables interrupts till... */
	movl	$CDBOOTADDR-4, %esp	/* after this instruction */

	pushl	%edx			/* Preserve the drive number. */

	movw	$(CDBOOTADDR >> 4), %ax /* Reloc from %ds = 0x7c0. */
	movw	$(LINKADDR >> 4), %bx	/* Reloc to %es = 0x4012. */

	movl	$_C_LABEL(end), %edx
	subl	$_C_LABEL(_start), %edx	/* How big are we? */

	/*
	 * Relocate in blocks that are a maximum of 32KB in size, incrementing
	 * the segment registers after each block. The 'rep; movsb' instruction
	 * uses %cx, which limits us to a maximum block size of 0xfff0, even
	 * though we can address the full 64KB within a single segment.
	 */
#define RELOC_BLOCK_SIZE 0x8000
reloc_loop:
	movl	%edx, %ecx
	jcxz	reloc_done
	cmpl	$RELOC_BLOCK_SIZE, %ecx
	jbe	reloc_notrunc
	movl	$RELOC_BLOCK_SIZE, %ecx
reloc_notrunc:
	subl	%ecx, %edx

	movw	%ax, %ds		/* Where we're coming from */
	xorw	%si, %si

	movw	%bx, %es		/* Where we're going to */
	xorw	%di, %di

	cld
	rep; movsb			/* Copy into place */

	addw	$(RELOC_BLOCK_SIZE >> 4), %ax
	addw	$(RELOC_BLOCK_SIZE >> 4), %bx

	jmp	reloc_loop

reloc_done:
	popl	%edx
	jmpl	$(LINKADDR >> 4), $(relocated-_start)	/* Now relocate */

relocated:
	/*
	 * In 16-bit mode, we have segment registers == 0x4012, and
	 * offsets work from here, with offset(_start) == 0.
	 *
	 * In 32-bit mode, we have a flat memory model, where
	 * offset(_start) == 0x40120.  This is how we're linked.
	 *
	 * Now transition to protected mode.
	 *
	 * First, initialise the global descriptor table.
	 */
	cli
	push	%cs
	pop	%ds
	addr32 data32 lgdt (Gdtr - LINKADDR)

	movl	%cr0, %eax
	orl	$CR0_PE, %eax
	data32 movl %eax, %cr0
	data32 ljmp $8, $1f		/* Seg sel 0x08 is flat 32-bit code */
1:
	.code32
	movl	$0x10, %eax		/* Seg sel 0x10 is flat 32-bit data */
	mov	%ax, %ds
	mov	%ax, %es
	mov	%ax, %fs
	mov	%ax, %gs
	mov	%ax, %ss
	movl	$BOOTSTACK, %esp
#ifdef DEBUG
	movl	$0xb8000, %ebx
	movl	$0x07420742, (%ebx)
#endif

	movzbl	%dl, %eax
	orl	$0x100, %eax		/* Indicate that it's a cd device */
	pushl	%eax			/* boot() takes this as a parameter */

#ifdef DEBUG
	movl	$0xb80a4, %ebx
	movl	$0x07520752, (%ebx)
#endif

	/* Zero .bss */
	xorl	%eax, %eax
	movl	$_C_LABEL(end), %ecx
	subl	$_C_LABEL(edata), %ecx
	movl	$_C_LABEL(edata), %edi
	cld
	rep;	stosb

	/* Set up an interrupt descriptor table for protected mode. */
	call	_ASM_LABEL(pmm_init)

	/* Set our program name ("CDBOOT", not "BOOT"). */
	movl	$cd_progname, %eax
	movl	%eax, progname

	/* Put the boot device number into the globals that need it */
	popl	%eax			/* Get this back from the stack */
	pushl	%eax			/* boot() takes this as a parameter */
	movl	%eax, _C_LABEL(bios_bootdev)
	movl	%eax, _C_LABEL(bios_cddev)

	/*
	 * Now call "main()".
	 *
	 * We run in flat 32-bit protected mode, with no address mapping.
	 */
#ifdef DEBUG
	movl	$0xb8004, %ebx
	movl	$0x07410741, (%ebx)
#endif
	call	_C_LABEL(boot)

	/* boot() should not return.  If it does, reset computer. */
	jmp	_C_LABEL(_rtt)

ENTRY(debugchar)
	pushl	%ebx
	movl	8(%esp), %ebx
	addl	%ebx, %ebx
	addl	$0xb8000, %ebx

	xorl	%eax, %eax
	movb	12(%esp), %al

	andl	$0xfffffffe, %ebx
	movb	%al, (%ebx)
	popl	%ebx
	ret

	.code16

/*
 * Display ASCIZ string at %si.  Trashes %si.
 */
Lstr:
	pushw	%ax
	cld
1:
	lodsb			/* %al = *%si++ */
	testb	%al, %al
	jz	1f
	call    Lchr
	jmp	1b
1:	popw	%ax
	ret

/*
 * Write out value in %ax in hex
 */
hex_word:
	pushw	%ax
	mov	%ah, %al
	call	hex_byte
	popw	%ax
	/* fall thru */
/*
 * Write out value in %al in hex
 */
hex_byte:
	pushw	%ax
	shrb	$4, %al
	call	hex_nibble
	popw	%ax
	/* fall thru */

/* Write out nibble in %al */
hex_nibble:
	and	$0x0F, %al
	add	$'0', %al
	cmpb	$'9', %al
	jbe	Lchr
	addb	$'A'-'9'-1, %al
	/* fall thru to Lchr */
/*
 *	Lchr: write the character in %al to console
 */
Lchr:
	pushw	%bx
	movb	$0x0e, %ah
	xorw	%bx, %bx
	incw	%bx		/* movw $0x01, %bx */
	int	$0x10
	popw	%bx
	ret

cd_progname:
	.asciz	"CDBOOT"

	.end


code:
#	$OpenBSD: Makefile,v 1.6 2014/07/15 22:14:37 deraadt Exp $
#

S=	${.CURDIR}/../../../..
SADIR=	${.CURDIR}/..

PROG=	cdbr
SRCS=	cdbr.S
AFLAGS+=-I${.CURDIR} -I${.CURDIR}/../../.. #-Wa,-a
AFLAGS+=-DOSREV=\"${OSREV}\"
AFLAGS+=-DMACH=\"${MACHINE}\" -DMACH_U=\"${MACHINE:U}\"
AFLAGS+=-fno-pie
LD=ld
ORG=	0x0000
LDFLAGS=-nostdlib -Ttext ${ORG} -x -N -s -Bstatic -e start -nopie

NOMAN=
#MAN+=	cdbr.8

INSTALL_STRIP=
BINMODE=644

${PROG}: $(OBJS)
	$(LD) $(LDFLAGS) -o $(PROG) $(OBJS)
	@size $(PROG)
	@if [ -x ${.OBJDIR}/${PROG} ]; then \
		objcopy -O binary ${PROG} ${.OBJDIR}/.tmp;\
		mv -f ${.OBJDIR}/.tmp ${.OBJDIR}/${PROG}; \
		ls -l ${.OBJDIR}/${PROG}; \
	fi

.include <bsd.prog.mk>


code:
/*	$OpenBSD: cdbr.S,v 1.3 2012/10/31 14:29:58 jsing Exp $	*/

/*
 * Copyright (c) 2004 Tom Cosgrove <tom.cosgrove@arches-consulting.com>
 * Copyright (c) 2001 John Baldwin <jhb@FreeBSD.org>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the author nor the names of any co-contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

	.file	"cdbr.S"

/* #include <machine/asm.h> */
/* #include <assym.h> */

/*
 * This program is a CD boot sector, similar to the partition boot record
 * (pbr, also called biosboot) used by hard disks.  It is implemented as a
 * "no-emulation" boot sector, as described in the "El Torito" Bootable
 * CD-ROM Format Specification.
 *
 * The function of this boot sector is to load and start the next-stage
 * cdboot program, which will load the kernel.
 *
 * The El Torito standard allows us to specify where we want to be loaded,
 * but for maximum compatibility we choose the default load address of
 * 0x07C00.
 *
 * Memory layout:
 *
 * 0x00000 -> 0x003FF	real mode interrupt vector table
 * 0x00400 -> 0x00500	BIOS data segment
 *
 * 0x00000 -> 0x073FF	our stack (grows down)		(from 29k)
 * 0x07400 -> 0x07BFF	we relocate to here		(at 29k)
 * 0x07C00 -> 0x08400	BIOS loads us here		(at 31k, for 2k)
 * 0x07C00 -> ...	/cdboot
 *
 * The BIOS loads us at physical address 0x07C00.  We then relocate to
 * 0x07400, seg:offset 0740:0000.  We then load /cdboot at seg:offset
 * 07C0:0000.
 */
#define BOOTSEG		0x7c0			/* segment we're loaded to */
#define BOOTSECTSIZE	0x800			/* our size in bytes */
#define BOOTRELOCSEG	0x740			/* segment we relocate to */
#define BOOTSTACKOFF  ((BOOTRELOCSEG << 4) - 4)	/* starts here, grows down */

/* Constants for reading from the CD */
#define ERROR_TIMEOUT		0x80		/* BIOS timeout on read */
#define NUM_RETRIES		3		/* Num times to retry */
#define SECTOR_SIZE		0x800		/* size of a sector */
#define SECTOR_SHIFT		11		/* number of place to shift */
#define BUFFER_LEN		0x100		/* number of sectors in buffr */
#define MAX_READ		0x10000		/* max we can read at a time */
#define MAX_READ_PARAS		MAX_READ >> 4
#define MAX_READ_SEC		MAX_READ >> SECTOR_SHIFT
#define MEM_READ_BUFFER		0x9000		/* buffer to read from CD */
#define MEM_VOLDESC		MEM_READ_BUFFER /* volume descriptor */
#define MEM_DIR			MEM_VOLDESC+SECTOR_SIZE /* Lookup buffer */
#define VOLDESC_LBA		0x10		/* LBA of vol descriptor */
#define VD_PRIMARY		1		/* Primary VD */
#define VD_END			255		/* VD Terminator */
#define VD_ROOTDIR		156		/* Offset of Root Dir Record */
#define DIR_LEN			0		/* Offset of Dir Rec length */
#define DIR_EA_LEN		1		/* Offset of EA length */
#define DIR_EXTENT		2		/* Offset of 64-bit LBA */
#define DIR_SIZE		10		/* Offset of 64-bit length */
#define DIR_NAMELEN		32		/* Offset of 8-bit name len */
#define DIR_NAME		33		/* Offset of dir name */

	.text
	.code16

	.globl	start
start:
	/* Set up stack */
	xorw	%ax, %ax
	movw	%ax, %ss
	movw	$BOOTSTACKOFF, %sp

	/* Relocate so we can load cdboot where we were */
	movw	$BOOTSEG, %ax
	movw	%ax, %ds
	movw	$BOOTRELOCSEG, %ax
	movw	%ax, %es
	xorw	%si, %si
	xorw	%di, %di
	movw	$BOOTSECTSIZE, %cx	/* Bytes in cdbr, relocate it all */
	cld
	rep
	movsb

	/* Jump to relocated self */
	ljmp $BOOTRELOCSEG, $reloc
reloc:

	/*
	 * Set up %ds and %es: %ds is our data segment (= %cs), %es is
	 * used to specify the segment address of the destination buffer
	 * for cd reads.  We initially have %es = %ds.
	 */
	movw	%cs, %ax
	movw	%ax, %ds
	movw	%ax, %es

	movb	%dl, drive		/* Store the boot drive number */

	movw	$signon, %si		/* Say "hi", and give boot drive */
	call	display_string
	movb	drive, %al
	call	hex_byte
	movw	$crlf, %si
	call	display_string

/*
 * Load Volume Descriptor
 */
	movl	$VOLDESC_LBA, %eax	/* Get the sector # for vol desc */
load_vd:
	pushl	%eax
	movb	$1, %dh			/* One sector */
	movw	$MEM_VOLDESC, %bx	/* Destination */
	call	read			/* Read it in */
	popl	%eax
	cmpb	$VD_PRIMARY, (%bx)	/* Primary vol descriptor? */
	je	have_vd			/* Yes */
	inc	%eax			/* Try the next one */
	cmpb	$VD_END, (%bx)		/* Is it the last one? */
	jne	load_vd			/* No, so go try the next one */
	movw	$msg_novd, %si		/* No pri vol descriptor */
	jmp	err_stop		/* Panic */
have_vd:				/* Have Primary VD */

/*
 * Look for the next-stage loader binary at pre-defined paths (loader_paths)
 */
	movw	$loader_paths, %si	/* Point to start of array */
lookup_path:
	movw	%si, loader		/* remember the one we're looking for */
	pushw	%si			/* Save file name pointer */
	call	lookup			/* Try to find file */
	popw	%di			/* Restore file name pointer */
	jnc	lookup_found		/* Found this file */
	xorb	%al, %al		/* Look for next */
	movw	$0xffff, %cx		/*  path name by */
	repnz				/*  scanning for */
	scasb				/*  nul char */
	movw	%di, %si		/* Point %si at next path */
	movb	(%si), %al		/* Get first char of next path */
	orb	%al, %al		/* Is it double nul? */
	jnz	lookup_path		/* No, try it */
	movw	$msg_failed, %si	/* Failed message */
	jmp	err_stop		/* Print it and halt */

lookup_found:				/* Found a loader file */

/*
 * Load the binary into the buffer.  Due to real mode addressing limitations
 * we have to read it in in 64k chunks.
 */
	movl	DIR_SIZE(%bx), %eax	/* Read file length */
	add	$SECTOR_SIZE-1, %eax	/* Convert length to sectors */
	shr	$SECTOR_SHIFT, %eax
	cmp	$BUFFER_LEN, %eax
	jbe	load_sizeok
	movw	$msg_load2big, %si	/* Error message */
	jmp	err_stop
load_sizeok:
	movzbw	%al, %cx		/* Num sectors to read */
	movl	DIR_EXTENT(%bx), %eax	/* Load extent */
	xorl	%edx, %edx
	movb	DIR_EA_LEN(%bx), %dl
	addl	%edx, %eax		/* Skip extended */

	/* Use %bx to hold the segment (para) number */
	movw	$BOOTSEG, %bx		/* We put cdboot here too */
load_loop:
	movb	%cl, %dh
	cmpb	$MAX_READ_SEC, %cl	/* Truncate to max read size */
	jbe	load_notrunc
	movb	$MAX_READ_SEC, %dh
load_notrunc:
	subb	%dh, %cl		/* Update count */
	pushl	%eax			/* Save */
	pushl	%ebx			/* Save */
	movw	%bx, %es		/* %bx has the segment (para) number */
	xorw	%bx, %bx		/* %es:0000 for destination */
	call	read			/* Read it in */
	popl	%ebx			/* Restore */
	popl	%eax			/* Restore */
	addl	$MAX_READ_SEC, %eax	/* Update LBA */
	addw	$MAX_READ_PARAS, %bx	/* Update dest addr */
	jcxz	load_done		/* Done? */
	jmp	load_loop		/* Keep going */
load_done:

	/* Now we can start the loaded program */

	movw	loader, %cx		/* Tell cdboot where it is */
					/* (Older versions of cdbr have */
					/*  %cx == 0 from the jcxz load_done) */
	movb	drive, %dl		/* Get the boot drive number */
	ljmp	$BOOTSEG, $0		/* Go run cdboot */

/*
 * Lookup the file in the path at [SI] from the root directory.
 *
 * Trashes: All but BX
 * Returns: CF = 0 (success), BX = pointer to record
 *          CF = 1 (not found)
 */
lookup:
	movw	$VD_ROOTDIR + MEM_VOLDESC, %bx	/* Root directory record */

lookup_dir:
	lodsb				/* Get first char of path */
	cmpb	$0, %al			/* Are we done? */
	je	lookup_done		/* Yes */
	cmpb	$'/', %al		/* Skip path separator */
	je	lookup_dir
	decw	%si			/* Undo lodsb side effect */
	call	find_file		/* Lookup first path item */
	jnc	lookup_dir		/* Try next component */
	ret
lookup_done:
	movw	$msg_loading, %si	/* Success message - say which file */
	call	display_string
	mov	loader, %si
	call	display_string
	mov	$crlf, %si
	call	display_string
	clc				/* Clear carry */
	ret

/*
 * Lookup file at [SI] in directory whose record is at [BX].
 *
 * Trashes: All but returns
 * Returns: CF = 0 (success), BX = pointer to record, SI = next path item
 *          CF = 1 (not found), SI = preserved
 */
find_file:
	mov	DIR_EXTENT(%bx), %eax	/* Load extent */
	xor	%edx, %edx
	mov	DIR_EA_LEN(%bx), %dl
	add	%edx, %eax		/* Skip extended attributes */
	mov	%eax, rec_lba		/* Save LBA */
	mov	DIR_SIZE(%bx), %eax	/* Save size */
	mov	%eax, rec_size
	xor	%cl, %cl		/* Zero length */
	push	%si			/* Save */
ff_namelen:
	inc	%cl			/* Update length */
	lodsb				/* Read char */
	cmp	$0, %al			/* Nul? */
	je	ff_namedone		/* Yes */
	cmp	$'/', %al		/* Path separator? */
	jnz	ff_namelen		/* No, keep going */
ff_namedone:
	dec	%cl			/* Adjust length and save */
	mov	%cl, name_len
	pop	%si			/* Restore */
ff_load:
	mov	rec_lba, %eax		/* Load LBA */
	mov	$MEM_DIR, %ebx		/* Address buffer */
	mov	$1, %dh			/* One sector */
	call	read			/* Read directory block */
	incl	rec_lba			/* Update LBA to next block */
ff_scan:
	mov	%ebx, %edx		/* Check for EOF */
	sub	$MEM_DIR, %edx
	cmp	%edx, rec_size
	ja	ff_scan_1
	stc				/* EOF reached */
	ret
ff_scan_1:
	cmpb	$0, DIR_LEN(%bx)	/* Last record in block? */
	je	ff_nextblock
	push	%si			/* Save */
	movzbw	DIR_NAMELEN(%bx), %si	/* Find end of string */
ff_checkver:
	cmpb	$'0', DIR_NAME-1(%bx,%si)	/* Less than '0'? */
	jb	ff_checkver_1
	cmpb	$'9', DIR_NAME-1(%bx,%si)	/* Greater than '9'? */
	ja	ff_checkver_1
	dec	%si			/* Next char */
	jnz	ff_checkver
	jmp	ff_checklen		/* All numbers in name, so */
					/*  no version */
ff_checkver_1:
	movzbw	DIR_NAMELEN(%bx), %cx
	cmp	%cx, %si		/* Did we find any digits? */
	je	ff_checkdot		/* No */
	cmpb	$';', DIR_NAME-1(%bx,%si)	/* Check for semicolon */
	jne	ff_checkver_2
	dec	%si			/* Skip semicolon */
	mov	%si, %cx
	mov	%cl, DIR_NAMELEN(%bx)	/* Adjust length */
	jmp	ff_checkdot
ff_checkver_2:
	mov	%cx, %si		/* Restore %si to end of string */
ff_checkdot:
	cmpb	$'.', DIR_NAME-1(%bx,%si)	/* Trailing dot? */
	jne	ff_checklen			/* No */
	decb	DIR_NAMELEN(%bx)	/* Adjust length */
ff_checklen:
	pop	%si			/* Restore */
	movzbw	name_len, %cx		/* Load length of name */
	cmp	%cl, DIR_NAMELEN(%bx)	/* Does length match? */
	je	ff_checkname		/* Yes, check name */
ff_nextrec:
	add	DIR_LEN(%bx), %bl	/* Next record */
	adc	$0, %bh
	jmp	ff_scan
ff_nextblock:
	subl	$SECTOR_SIZE, rec_size	/* Adjust size */
	jnc	ff_load			/* If subtract ok, keep going */
	ret				/* End of file, so not found */
ff_checkname:
	lea	DIR_NAME(%bx), %di	/* Address name in record */
	push	%si			/* Save */
	repe	cmpsb			/* Compare name */
	jcxz	ff_match		/* We have a winner! */
	pop	%si			/* Restore */
	jmp	ff_nextrec		/* Keep looking */
ff_match:
	add	$2, %sp			/* Discard saved %si */
	clc				/* Clear carry */
	ret

/*
 * Load DH sectors starting at LBA %eax into address %es:%bx.
 *
 * Preserves %bx, %cx, %dx, %si, %es
 * Trashes %eax
 */
read:
	pushw	%si			/* Save */
	pushw	%cx			/* Save since some BIOSs trash */
	movl	%eax, edd_lba		/* LBA to read from */
	movw	%es, %ax		/* Get the segment */
	movw	%ax, edd_addr + 2	/*  and store */
	movw	%bx, edd_addr		/* Store offset too */
read_retry:
	call	twiddle			/* Entertain the user */
	pushw	%dx			/* Save */
	movw	$edd_packet, %si	/* Address Packet */
	movb	%dh, edd_len		/* Set length */
	movb	drive, %dl		/* BIOS Device */
	movb	$0x42, %ah		/* BIOS: Extended Read */
	int	$0x13			/* Call BIOS */
	popw	%dx			/* Restore */
	jc	read_fail		/* Worked? */
	popw	%cx			/* Restore */
	popw	%si
	ret				/* Return */
read_fail:
	cmpb	$ERROR_TIMEOUT, %ah	/* Timeout? */
	je	read_retry		/* Yes, Retry */
read_error:
	pushw	%ax			/* Save error */
	movw	$msg_badread, %si	/* "Read error: 0x" */
	call	display_string
	popw	%ax			/* Retrieve error code */
	movb	%ah, %al		/* Into %al */
	call	hex_byte		/* Display error code */
	jmp	stay_stopped		/* ... then hang */

/*
 * Display the ASCIZ error message in %esi then halt
 */
err_stop:
	call	display_string

stay_stopped:
	sti				/* Ensure Ctl-Alt-Del will work */
	hlt				/* (don't require power cycle) */
	jmp	stay_stopped		/* (Just to make sure) */

/*
 * Output the "twiddle"
 */
twiddle:
	push	%ax			/* Save */
	push	%bx			/* Save */
	mov	twiddle_index, %al	/* Load index */
	mov	twiddle_chars, %bx	/* Address table */
	inc	%al			/* Next */
	and	$3, %al			/*  char */
	mov	%al, twiddle_index	/* Save index for next call */
	xlat				/* Get char */
	call	display_char		/* Output it */
	mov	$8, %al			/* Backspace */
	call	display_char		/* Output it */
	pop	%bx			/* Restore */
	pop	%ax			/* Restore */
	ret

/*
 * Display the ASCIZ string pointed to by %si.
 *
 * Destroys %si, possibly others.
 */
display_string:
	pushw	%ax
	cld
1:
	lodsb			/* %al = *%si++ */
	testb	%al, %al
	jz	1f
	call    display_char
	jmp	1b

/*
 * Write out value in %eax in hex
 */
hex_long:
	pushl	%eax
	shrl	$16, %eax
	call	hex_word
	popl	%eax
	/* fall thru */

/*
 * Write out value in %ax in hex
 */
hex_word:
	pushw	%ax
	mov	%ah, %al
	call	hex_byte
	popw	%ax
	/* fall thru */
/*
 * Write out value in %al in hex
 */
hex_byte:
	pushw	%ax
	shrb	$4, %al
	call	hex_nibble
	popw	%ax
	/* fall thru */

/* Write out nibble in %al */
hex_nibble:
	and	$0x0F, %al
	add	$'0', %al
	cmpb	$'9', %al
	jbe	display_char
	addb	$'A'-'9'-1, %al
	/* fall thru to display_char */

/*
 * Display the character in %al
 */
display_char:
	pushw	%ax

	pushw	%bx
	movb	$0x0e, %ah
	movw	$1, %bx
	int	$0x10
	popw	%bx
1:	popw	%ax
	ret

/*
 * Data
 */
drive:		.byte	0			/* Given to us by the BIOS */
signon:		.asciz	"CD-ROM: "
crlf:		.asciz	"\r\n"
msg_load2big:	.asciz  "File too big"
msg_badread:	.asciz  "Read error: 0x"
msg_novd:	.asciz  "No Primary Volume Descriptor"
msg_loading:	.asciz  "Loading "

/* State for searching dir */
rec_lba:	.long	0x0			/* LBA (adjusted for EA) */
rec_size:	.long	0x0			/* File size */
name_len:	.byte	0x0			/* Length of current name */

twiddle_index:	.byte	0x0
twiddle_chars:	.ascii	"|/-\\"

/* Packet for LBA (CD) read */
edd_packet:	.byte	0x10			/* Length */
		.byte	0			/* Reserved */
edd_len:	.byte	0x0			/* Num to read */
		.byte	0			/* Reserved */
edd_addr:	.word	0x0, 0x0		/* Seg:Off */
edd_lba:	.quad	0x0			/* LBA */

/* The data from here must be last in the file, only followed by 0x00 bytes */

loader:		.word	0			/* The path we end up using */

msg_failed:	.ascii	"Can't find "		/* This string runs into... */

/* loader_paths is a list of ASCIZ strings followed by a term NUL byte */
loader_paths:	.asciz  "/cdboot"
		.asciz	"/CDBOOT"
		.ascii	"/", OSREV, "/", MACH, "/cdboot"
		.byte	0			/* NUL-term line above */
		.ascii	"/", OSREV, "/", MACH_U, "/CDBOOT"
		.byte	0			/* NUL-term line above */
		.byte	0			/* Terminate the list */

	. = BOOTSECTSIZE

	.end


code:
/genassym.cf/1.4/Sun Jun 17 00:28:56 2007//TOPENBSD_5_8_BASE
D


code:
#	$OpenBSD: genassym.cf,v 1.4 2007/06/17 00:28:56 deraadt Exp $

include <sys/param.h>
include <sys/disklabel.h>

include <machine/specialreg.h>
include <machine/segments.h>
include <machine/biosvar.h>

export	DOSPARTOFF
export	DOSACTIVE
export	DOSPTYP_OPENBSD
export	NDOSPART
export	DOSMBR_SIGNATURE
export	DEV_BSIZE
export	CR0_PE
export	SDT_MEMERAC
export	SDT_MEMERA
export	SDT_MEMRWA
export	SDT_SYS386TGT
struct	BIOS_regs
member	biosr_ax
member	biosr_cx
member	biosr_dx
member	biosr_bx
member	biosr_bp
member	biosr_si
member	biosr_di
member	biosr_ds
member	biosr_es


code:
/Makefile/1.2/Sat Oct 13 14:10:35 2012//TOPENBSD_5_8_BASE
D


code:
#	$OpenBSD: Makefile,v 1.2 2012/10/13 14:10:35 jsing Exp $

MAN=
MLINKS=

.if ${MACHINE} == "i386"
PROG=	fdboot
COPTS=	-DFDBOOT
.endif

.PATH:	${.CURDIR}/../boot

.include "${.CURDIR}/../boot/Makefile"


code:
/*-
 * Copyright (c) 1990 The Regents of the University of California.
 * All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * William Jolitz.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <machine/asm.h>

/* like alloc, but automatic free in return */

ENTRY(alloca)
	popl	%edx		/*  pop return addr */
	popl	%eax		/*  pop amount to allocate */
	movl	%esp,%ecx
	addl	$3,%eax		/*  round up to next word */
	andl	$-4,%eax
	subl	%eax,%esp
	movl	%esp,%eax	/* base of newly allocated space */
	pushl	8(%ecx)		/* copy possible saved registers */
	pushl	4(%ecx)
	pushl	0(%ecx)
	pushl	%eax		/* dummy to pop at callsite */
	jmp	*%edx		/* "return" */


code:
/*	$OpenBSD: apmprobe.c,v 1.18 2014/07/08 13:31:30 deraadt Exp $	*/

/*
 * Copyright (c) 1997-2000 Michael Shalayeff
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */
/*
 * APM derived from: apm_init.S, LP (Laptop Package)
 * wich contained this:
 * Copyright (C) 1994 by HOSOKAWA, Tatsumi <hosokawa@mt.cs.keio.ac.jp>
 *
 */
/*
 * If you want to know the specification of APM BIOS, see the following
 * documentations,
 *
 * [1] Intel Corporation and Microsoft Corporation, "Advanced Power
 *     Management, The Next Generation, Version 1.0", Feb.,1992.
 *
 * [2] Intel Corporation and Microsoft Corporation, "Advanced Power
 *     Management (APM) BIOS Interface Specification Revision 1.1",
 *     Sep.,1993, Intel Order Number: 241704-001, Microsoft Part
 *     Number: 781-110-X01
 *
 * or contact
 *
 * APM Support Desk (Intel Corporation, US)
 *   TEL: (800)628-8686
 *   FAX: (916)356-6100.
 */

#include <sys/param.h>
#include "libsa.h"
#include <stand/boot/bootarg.h>

#include <dev/isa/isareg.h>

#include <machine/apmvar.h>
#include <machine/biosvar.h>

#include "debug.h"

extern int debug;

static __inline u_int
apm_check(void)
{
	register u_int detail;
	register u_int8_t f;

	__asm volatile(DOINT(0x15) "\n\t"
	    "setc %b1\n\t"
	    "movzwl %%ax, %0\n\t"
	    "shll $16, %%ecx\n\t"
	    "orl %%ecx, %0"
	    : "=a" (detail), "=b" (f)
	    : "0" (APM_INSTCHECK), "1" (APM_DEV_APM_BIOS)
	    : "%ecx", "cc");

	if (f || BIOS_regs.biosr_bx != 0x504d /* "PM" */ ) {
#ifdef DEBUG
		if (debug)
			printf("apm_check: %x, %x, %x\n",
			    f, BIOS_regs.biosr_bx, detail);
#endif
		return 0;
	} else
		return detail;
}

static __inline int
apm_disconnect(void)
{
	register u_int16_t rv;

	__asm volatile(DOINT(0x15) "\n\t"
	    "setc %b0"
	    : "=a" (rv)
	    : "0" (APM_DISCONNECT), "b" (APM_DEV_APM_BIOS)
	    : "%ecx", "%edx", "cc");

	return ((rv & 0xff)? rv >> 8 : 0);
}

static __inline int
apm_connect(bios_apminfo_t *ai)
{
	register u_int16_t f;

	__asm volatile (DOINT(0x15) "\n\t"
	    "setc %b1\n\t"
	    "movb %%ah, %h1\n\t"
	    "movzwl %%ax, %%eax\n\tshll $4, %0\n\t"
	    "movzwl %%cx, %%ecx\n\tshll $4, %2\n\t"
	    "movzwl %%dx, %%edx\n\tshll $4, %3\n\t"
	    : "=a" (ai->apm_code32_base),
	      "=b" (f),
	      "=c" (ai->apm_code16_base),
	      "=d" (ai->apm_data_base)
	    : "0" (APM_PROT32_CONNECT), "1" (APM_DEV_APM_BIOS)
	    : "cc");

	if (f & 0xff)
		return (f >> 8);

	ai->apm_entry      = BIOS_regs.biosr_bx;
#if 0
	ai->apm_code_len   = BIOS_regs.biosr_si & 0xffff;
	ai->apm_code16_len = BIOS_regs.biosr_si & 0xffff;
	ai->apm_data_len   = BIOS_regs.biosr_di & 0xffff;
#else
	ai->apm_code_len   = 0xffff - (ai->apm_code32_base & 0xffff);
	ai->apm_code16_len = 0xffff - (ai->apm_code16_base & 0xffff);
	ai->apm_data_len   = 0xffff - (ai->apm_data_base & 0xffff);
#endif
	if (ai->apm_data_base < BOOTARG_OFF)
		ai->apm_data_len = PAGE_SIZE - (ai->apm_data_base & PAGE_MASK) - 1;

#ifdef DEBUG
	if (debug)
		printf("cs=%x:%x/%x:%x, ds=%x:%x\n",
		    ai->apm_code32_base, ai->apm_code_len,
		    ai->apm_code16_base, ai->apm_code16_len,
		    ai->apm_data_base,   ai->apm_data_len);
#endif
	/* inform apm bios about our driver version */
	__asm volatile (DOINT(0x15) "\n\t"
	    "setc %b1\n\t"
	    "movb %%ah, %h1"
	    : "=b" (f)
	    : "a" (APM_DRIVER_VERSION),
	      "0" (APM_DEV_APM_BIOS),
	      "c" (APM_VERSION)
	    : "cc");

	return 0;
}

static bios_apminfo_t ai;

void
apmprobe(void)
{
	if ((ai.apm_detail = apm_check())) {

		apm_disconnect();

		if (apm_connect(&ai) != 0) {
#ifdef DEBUG
			printf("\napm: connect error\n");
#endif
			return;
		}
#ifdef DEBUG
		if (debug)
			printf("apm[%x cs=%x[%x]/%x[%x] ds=%x[%x] @ %x]",
			    ai.apm_detail,
			    ai.apm_code32_base, ai.apm_code_len,
			    ai.apm_code16_base, ai.apm_code16_len,
			    ai.apm_data_base, ai.apm_data_len,
			    ai.apm_entry);
		else
			printf(" apm");
#else
		printf(" apm");
#endif
		addbootarg(BOOTARG_APMINFO, sizeof(ai), &ai);
	}
}

#define round_page(x)   (((x) + PAGE_MASK) & ~PAGE_MASK)
#define trunc_page(x)   ((x) & ~PAGE_MASK)

void
apmfixmem(void)
{
#ifdef DEBUG
	printf("apmremove (%d)", ai.apm_detail);
#endif
	if (ai.apm_detail)
		mem_delete(trunc_page(ai.apm_data_base),
		    round_page(ai.apm_data_base + ai.apm_data_len));
}


code:
/*	$OpenBSD: bioscons.c,v 1.35 2014/03/29 18:09:29 guenther Exp $	*/

/*
 * Copyright (c) 1997-1999 Michael Shalayeff
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/types.h>
#include <machine/biosvar.h>
#include <machine/pio.h>
#include <dev/isa/isareg.h>
#include <dev/ic/mc146818reg.h>
#include <dev/ic/comreg.h>
#include <dev/ic/ns16450reg.h>
/* #include <i386/isa/nvram.h> */
#include <dev/cons.h>
#include <lib/libsa/stand.h>
#include "debug.h"
#include "biosdev.h"

/* XXX cannot trust NVRAM on this.  Maybe later we make a real probe.  */
#if 0
#define PRESENT_MASK (NVRAM_EQUIPMENT_KBD|NVRAM_EQUIPMENT_DISPLAY)
#else
#define PRESENT_MASK 0
#endif

void
pc_probe(struct consdev *cn)
{
	cn->cn_pri = CN_MIDPRI;
	cn->cn_dev = makedev(12, 0);
	printf(" pc%d", minor(cn->cn_dev));

#if 0
	outb(IO_RTC, NVRAM_EQUIPMENT);
	if ((inb(IO_RTC+1) & PRESENT_MASK) == PRESENT_MASK) {
		cn->cn_pri = CN_MIDPRI;
		/* XXX from i386/conf.c */
		cn->cn_dev = makedev(12, 0);
		printf(" pc%d", minor(cn->cn_dev));
	}
#endif
}

void
pc_init(struct consdev *cn)
{
}

int
pc_getc(dev_t dev)
{
	register int rv;

	if (dev & 0x80) {
		__asm volatile(DOINT(0x16) "; setnz %b0" : "=a" (rv) :
		    "0" (0x100) : "%ecx", "%edx", "cc" );
		return (rv & 0xff);
	}

	/*
	 * Wait for a character to actually become available.  Appears to
	 * be necessary on (at least) the Intel Mac Mini.
	 */
	do {
		__asm volatile(DOINT(0x16) "; setnz %b0" : "=a" (rv) :
		    "0" (0x100) : "%ecx", "%edx", "cc" );
	} while ((rv & 0xff) == 0);

	__asm volatile(DOINT(0x16) : "=a" (rv) : "0" (0x000) :
	    "%ecx", "%edx", "cc" );

	return (rv & 0xff);
}

int
pc_getshifts(dev_t dev)
{
	register int rv;

	__asm volatile(DOINT(0x16) : "=a" (rv) : "0" (0x200) :
	    "%ecx", "%edx", "cc" );

	return (rv & 0xff);
}

void
pc_putc(dev_t dev, int c)
{
	__asm volatile(DOINT(0x10) : : "a" (c | 0xe00), "b" (1) :
	    "%ecx", "%edx", "cc" );
}

const int comports[4] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };

void
com_probe(struct consdev *cn)
{
	register int i, n;

	/* get equip. (9-11 # of coms) */
	__asm volatile(DOINT(0x11) : "=a" (n) : : "%ecx", "%edx", "cc");
	n >>= 9;
	n &= 7;
	for (i = 0; i < n; i++)
		printf(" com%d", i);

	cn->cn_pri = CN_LOWPRI;
	/* XXX from i386/conf.c */
	cn->cn_dev = makedev(8, 0);
}

int com_speed = -1;
int com_addr = -1;

void
com_init(struct consdev *cn)
{
	int port = (com_addr == -1) ? comports[minor(cn->cn_dev)] : com_addr;

	outb(port + com_ier, 0);
	if (com_speed == -1)
		comspeed(cn->cn_dev, 9600); /* default speed is 9600 baud */
	outb(port + com_mcr, MCR_DTR | MCR_RTS);
	outb(port + com_fifo, FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST |
	    FIFO_TRIGGER_1);

	/* drain the input buffer */
	while (inb(port + com_lsr) & LSR_RXRDY)
		(void)inb(port + com_data);
}

int
com_getc(dev_t dev)
{
	int port = (com_addr == -1) ? comports[minor(dev & 0x7f)] : com_addr;

	if (dev & 0x80)
		return (inb(port + com_lsr) & LSR_RXRDY);

	while ((inb(port + com_lsr) & LSR_RXRDY) == 0)
		;

	return (inb(port + com_data) & 0xff);
}

/* call with sp == 0 to query the current speed */
int
comspeed(dev_t dev, int sp)
{
	int port = (com_addr == -1) ? comports[minor(dev)] : com_addr;
	int i, newsp;
	int err;

	if (sp <= 0)
		return com_speed;
	/* valid baud rate? */
	if (115200 < sp || sp < 75)
		return -1;

	/*
	 * Accepted speeds:
	 *   75 150 300 600 1200 2400 4800 9600 19200 38400 76800 and
	 *   14400 28800 57600 115200
	 */
	for (i = sp; i != 75 && i != 14400; i >>= 1)
		if (i & 1)
			return -1;

/* ripped screaming from dev/ic/com.c */
#define divrnd(n, q)    (((n)*2/(q)+1)/2)       /* divide and round off */
	newsp = divrnd((COM_FREQ / 16), sp);
	if (newsp <= 0)
		return -1;
	err = divrnd((COM_FREQ / 16) * 1000, sp * newsp) - 1000;
	if (err < 0)
		err = -err;
	if (err > COM_TOLERANCE)
		return -1;
#undef  divrnd

	if (com_speed != -1 && cn_tab && cn_tab->cn_dev == dev &&
	    com_speed != sp) {
		printf("com%d: changing speed to %d baud in 5 seconds, "
		    "change your terminal to match!\n\a",
		    minor(dev), sp);
		sleep(5);
	}

	outb(port + com_cfcr, LCR_DLAB);
	outb(port + com_dlbl, newsp);
	outb(port + com_dlbh, newsp>>8);
	outb(port + com_cfcr, LCR_8BITS);
	if (com_speed != -1)
		printf("\ncom%d: %d baud\n", minor(dev), sp);

	newsp = com_speed;
	com_speed = sp;
	return newsp;
}

void
com_putc(dev_t dev, int c)
{
	int port = (com_addr == -1) ? comports[minor(dev)] : com_addr;

	while ((inb(port + com_lsr) & LSR_TXRDY) == 0)
		;

	outb(port + com_data, c);
}


code:
/*	$OpenBSD: biosdev.c,v 1.89 2014/03/29 18:09:29 guenther Exp $	*/

/*
 * Copyright (c) 1996 Michael Shalayeff
 * Copyright (c) 2003 Tobias Weingartner
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */

#include <sys/param.h>
#include <sys/reboot.h>
#include <sys/disklabel.h>
#include <isofs/cd9660/iso.h>
#include <lib/libsa/saerrno.h>
#include <machine/biosvar.h>
#include <machine/tss.h>

#include "biosdev.h"
#include "debug.h"
#include "disk.h"
#include "libsa.h"

#ifdef SOFTRAID
#include <dev/softraidvar.h>
#include "softraid.h"
#endif

static const char *biosdisk_err(u_int);
static int biosdisk_errno(u_int);

int CHS_rw (int, int, int, int, int, int, void *);
static int EDD_rw (int, int, u_int32_t, u_int32_t, void *);

static u_int findopenbsd(bios_diskinfo_t *, const char **);

extern int debug;
int bios_bootdev;
int bios_cddev = -1;		/* Set by srt0 if coming from CD */

struct EDD_CB {
	u_int8_t  edd_len;	/* size of packet */
	u_int8_t  edd_res1;	/* reserved */
	u_int8_t  edd_nblk;	/* # of blocks to transfer */
	u_int8_t  edd_res2;	/* reserved */
	u_int16_t edd_off;	/* address of buffer (offset) */
	u_int16_t edd_seg;	/* address of buffer (segment) */
	u_int64_t edd_daddr;	/* starting block */
};

/*
 * reset disk system
 */
static int
biosdreset(int dev)
{
	int rv;

	__asm volatile (DOINT(0x13) "; setc %b0" : "=a" (rv)
	    : "0" (0), "d" (dev) : "%ecx", "cc");

	return ((rv & 0xff)? rv >> 8 : 0);
}

/*
 * Fill out a bios_diskinfo_t for this device.
 * Return 0 if all ok.
 * Return 1 if not ok.
 */
int
bios_getdiskinfo(int dev, bios_diskinfo_t *pdi)
{
	u_int rv;

	/* Just reset, don't check return code */
	rv = biosdreset(dev);

#ifdef BIOS_DEBUG
	if (debug)
		printf("getinfo: try #8, 0x%x, %p\n", dev, pdi);
#endif
	__asm volatile (DOINT(0x13) "\n\t"
	    "setc %b0; movzbl %h1, %1\n\t"
	    "movzbl %%cl, %3; andb $0x3f, %b3\n\t"
	    "xchgb %%cl, %%ch; rolb $2, %%ch"
	    : "=a" (rv), "=d" (pdi->bios_heads),
	      "=c" (pdi->bios_cylinders),
	      "=b" (pdi->bios_sectors)
	    : "0" (0x0800), "1" (dev) : "cc");

#ifdef BIOS_DEBUG
	if (debug) {
		printf("getinfo: got #8\n");
		printf("disk 0x%x: %d,%d,%d\n", dev, pdi->bios_cylinders,
		    pdi->bios_heads, pdi->bios_sectors);
	}
#endif
	if (rv & 0xff)
		return 1;

	/* Fix up info */
	pdi->bios_number = dev;
	pdi->bios_heads++;
	pdi->bios_cylinders &= 0x3ff;
	pdi->bios_cylinders++;

	/* NOTE:
	 * This currently hangs/reboots some machines
	 * The IBM ThinkPad 750ED for one.
	 *
	 * Funny that an IBM/MS extension would not be
	 * implemented by an IBM system...
	 *
	 * Future hangs (when reported) can be "fixed"
	 * with getSYSCONFaddr() and an exceptions list.
	 */
	if (dev & 0x80 && (dev == 0x80 || dev == 0x81 || dev == bios_bootdev)) {
		int bm;

#ifdef BIOS_DEBUG
		if (debug)
			printf("getinfo: try #41, 0x%x\n", dev);
#endif
		/* EDD support check */
		__asm volatile(DOINT(0x13) "; setc %b0"
			 : "=a" (rv), "=c" (bm)
			 : "0" (0x4100), "b" (0x55aa), "d" (dev) : "cc");
		if (!(rv & 0xff) && (BIOS_regs.biosr_bx & 0xffff) == 0xaa55)
			pdi->bios_edd = (bm & 0xffff) | ((rv & 0xff) << 16);
		else
			pdi->bios_edd = -1;

#ifdef BIOS_DEBUG
		if (debug) {
			printf("getinfo: got #41\n");
			printf("disk 0x%x: 0x%x\n", dev, bm);
		}
#endif
		/*
		 * If extended disk access functions are not supported
		 * there is not much point on doing EDD.
		 */
		if (!(pdi->bios_edd & EXT_BM_EDA))
			pdi->bios_edd = -1;
	} else
		pdi->bios_edd = -1;

	/* Skip sanity check for CHS options in EDD mode. */
	if (pdi->bios_edd != -1)
		return 0;

	/* Sanity check */
	if (!pdi->bios_cylinders || !pdi->bios_heads || !pdi->bios_sectors)
		return 1;

	/* CD-ROMs sometimes return heads == 1 */
	if (pdi->bios_heads < 2)
		return 1;

	return 0;
}

/*
 * Read/Write a block from given place using the BIOS.
 */
int
CHS_rw(int rw, int dev, int cyl, int head, int sect, int nsect, void *buf)
{
	int rv;

	rw = rw == F_READ ? 2 : 3;
	BIOS_regs.biosr_es = (u_int32_t)buf >> 4;
	__asm volatile ("movb %b7, %h1\n\t"
	    "movb %b6, %%dh\n\t"
	    "andl $0xf, %4\n\t"
	    /* cylinder; the highest 2 bits of cyl is in %cl */
	    "xchgb %%ch, %%cl\n\t"
	    "rorb  $2, %%cl\n\t"
	    "orb %b5, %%cl\n\t"
	    "inc %%cx\n\t"
	    DOINT(0x13) "\n\t"
	    "setc %b0"
	    : "=a" (rv)
	    : "0" (nsect), "d" (dev), "c" (cyl),
	      "b" (buf), "m" (sect), "m" (head),
	      "m" (rw)
	    : "cc", "memory");

	return ((rv & 0xff)? rv >> 8 : 0);
}

static __inline int
EDD_rw(int rw, int dev, u_int32_t daddr, u_int32_t nblk, void *buf)
{
	int rv;
	volatile static struct EDD_CB cb;

	/* Zero out reserved stuff */
	cb.edd_res1 = 0;
	cb.edd_res2 = 0;

	/* Fill in parameters */
	cb.edd_len = sizeof(cb);
	cb.edd_nblk = nblk;
	cb.edd_seg = ((u_int32_t)buf >> 4) & 0xffff;
	cb.edd_off = (u_int32_t)buf & 0xf;
	cb.edd_daddr = daddr;

	/* if offset/segment are zero, punt */
	if (!cb.edd_seg && !cb.edd_off)
		return 1;

	/* Call extended read/write (with disk packet) */
	BIOS_regs.biosr_ds = (u_int32_t)&cb >> 4;
	__asm volatile (DOINT(0x13) "; setc %b0" : "=a" (rv)
	    : "0" ((rw == F_READ)? 0x4200: 0x4300),
	      "d" (dev), "S" ((int) (&cb) & 0xf) : "%ecx", "cc");
	return ((rv & 0xff)? rv >> 8 : 0);
}

/*
 * Read given sector, handling retry/errors/etc.
 */
int
biosd_io(int rw, bios_diskinfo_t *bd, u_int off, int nsect, void *buf)
{
	int dev = bd->bios_number;
	int j, error;
	void *bb;
	int bbsize = nsect * DEV_BSIZE;

	if (bd->flags & BDI_EL_TORITO) {	/* It's a CD device */
		dev &= 0xff;			/* Mask out this flag bit */

		/*
		 * sys/lib/libsa/cd9600.c converts 2,048-byte CD sectors
		 * to DEV_BSIZE blocks before calling the device strategy
		 * routine.  However, the El Torito spec says that the
		 * BIOS will work in 2,048-byte sectors.  So shift back.
		 */
		off /= (ISO_DEFAULT_BLOCK_SIZE / DEV_BSIZE);
		nsect /= (ISO_DEFAULT_BLOCK_SIZE / DEV_BSIZE);
	}

	/*
	 * Use a bounce buffer to not cross 64k DMA boundary, and to
	 * not access 1 MB or above.
	 */
	if (((((u_int32_t)buf) & ~0xffff) !=
	    (((u_int32_t)buf + bbsize) & ~0xffff)) ||
	    (((u_int32_t)buf) >= 0x100000)) {
		/*
		 * XXX we believe that all the io is buffered
		 * by fs routines, so no big reads anyway
		 */
		bb = alloca(bbsize);
		if (rw != F_READ)
			bcopy(buf, bb, bbsize);
	} else
		bb = buf;

	/* Try to do operation up to 5 times */
	for (error = 1, j = 5; j-- && error; ) {
		/* CHS or LBA access? */
		if (bd->bios_edd != -1) {
			error = EDD_rw(rw, dev, off, nsect, bb);
		} else {
			int cyl, head, sect;
			size_t i, n;
			char *p = bb;

			/* Handle track boundaries */
			for (error = i = 0; error == 0 && i < nsect;
			    i += n, off += n, p += n * DEV_BSIZE) {

				btochs(off, cyl, head, sect, bd->bios_heads,
				    bd->bios_sectors);

				if ((sect + (nsect - i)) >= bd->bios_sectors)
					n = bd->bios_sectors - sect;
				else
					n = nsect - i;

				error = CHS_rw(rw, dev, cyl, head, sect, n, p);

				/* ECC corrected */
				if (error == 0x11)
					error = 0;
			}
		}
		switch (error) {
		case 0x00:	/* No errors */
		case 0x11:	/* ECC corrected */
			error = 0;
			break;

		default:	/* All other errors */
#ifdef BIOS_DEBUG
			if (debug)
				printf("\nBIOS error 0x%x (%s)\n",
				    error, biosdisk_err(error));
#endif
			biosdreset(dev);
			break;
		}
	}

	if (bb != buf && rw == F_READ)
		bcopy(bb, buf, bbsize);

#ifdef BIOS_DEBUG
	if (debug) {
		if (error != 0)
			printf("=0x%x(%s)", error, biosdisk_err(error));
		putchar('\n');
	}
#endif

	return error;
}

/*
 * Try to read the bsd label on the given BIOS device.
 */
static u_int
findopenbsd(bios_diskinfo_t *bd, const char **err)
{
	struct dos_mbr mbr;
	struct dos_partition *dp;
	u_int mbroff = DOSBBSECTOR;
	u_int mbr_eoff = DOSBBSECTOR;	/* Offset of MBR extended partition. */
	int error, i, maxebr = DOS_MAXEBR, nextebr;

again:
	if (!maxebr--) {
		*err = "too many extended partitions";
		return (-1);
	}

	/* Read MBR */
	bzero(&mbr, sizeof(mbr));
	error = biosd_io(F_READ, bd, mbroff, 1, &mbr);
	if (error) {
		*err = biosdisk_err(error);
		return (-1);
	}

	/* check mbr signature */
	if (mbr.dmbr_sign != DOSMBR_SIGNATURE) {
		*err = "bad MBR signature\n";
		return (-1);
	}

	/* Search for OpenBSD partition */
	nextebr = 0;
	for (i = 0; i < NDOSPART; i++) {
		dp = &mbr.dmbr_parts[i];
		if (!dp->dp_size)
			continue;
#ifdef BIOS_DEBUG
		if (debug)
			printf("found partition %u: "
			    "type %u (0x%x) offset %u (0x%x)\n",
			    (int)(dp - mbr.dmbr_parts),
			    dp->dp_typ, dp->dp_typ,
			    dp->dp_start, dp->dp_start);
#endif
		if (dp->dp_typ == DOSPTYP_OPENBSD) {
			if (dp->dp_start > (dp->dp_start + mbroff))
				continue;
			return (dp->dp_start + mbroff);
		}

		/*
		 * Record location of next ebr if and only if this is the first
		 * extended partition in this boot record!
		 */
		if (!nextebr && (dp->dp_typ == DOSPTYP_EXTEND ||
		    dp->dp_typ == DOSPTYP_EXTENDL)) {
			nextebr = dp->dp_start + mbr_eoff;
			if (nextebr < dp->dp_start)
				nextebr = (u_int)-1;
			if (mbr_eoff == DOSBBSECTOR)
				mbr_eoff = dp->dp_start;
		}
	}

	if (nextebr && nextebr != (u_int)-1) {
		mbroff = nextebr;
		goto again;
	}

	return (-1);
}

const char *
bios_getdisklabel(bios_diskinfo_t *bd, struct disklabel *label)
{
	u_int start = 0;
	char *buf;
	const char *err = NULL;
	int error;

	/* Sanity check */
	if (bd->bios_edd == -1 &&
	    (bd->bios_heads == 0 || bd->bios_sectors == 0))
		return "failed to read disklabel";

	/* MBR is a harddisk thing */
	if (bd->bios_number & 0x80) {
		start = findopenbsd(bd, &err);
		if (start == (u_int)-1) {
			if (err != NULL)
				return (err);
 			return "no OpenBSD partition\n";
		}
	}
	start = LABELSECTOR + start;

	/* Load BSD disklabel */
	buf = alloca(DEV_BSIZE);
#ifdef BIOS_DEBUG
	if (debug)
		printf("loading disklabel @ %u\n", start);
#endif
	/* read disklabel */
	error = biosd_io(F_READ, bd, start, 1, buf);

	if (error)
		return "failed to read disklabel";

	/* Fill in disklabel */
	return (getdisklabel(buf, label));
}

int
biosopen(struct open_file *f, ...)
{
#ifdef SOFTRAID
	struct sr_boot_volume *bv;
#endif
	register char *cp, **file;
	dev_t maj, unit, part;
	struct diskinfo *dip;
	int biosdev, devlen;
	const char *st;
	va_list ap;
	char *dev;

	va_start(ap, f);
	cp = *(file = va_arg(ap, char **));
	va_end(ap);

#ifdef BIOS_DEBUG
	if (debug)
		printf("%s\n", cp);
#endif

	f->f_devdata = NULL;

	/* Search for device specification. */
	dev = cp;
	if (cp[4] == ':')
		devlen = 2;
	else if (cp[5] == ':')
		devlen = 3;
	else
		return ENOENT;
	cp += devlen;

	/* Get unit. */
	if ('0' <= *cp && *cp <= '9')
		unit = *cp++ - '0';
	else {
		printf("Bad unit number\n");
		return EUNIT;
	}

	/* Get partition. */
	if ('a' <= *cp && *cp <= 'p')
		part = *cp++ - 'a';
	else {
		printf("Bad partition\n");
		return EPART;
	}

	/* Get filename. */
	cp++;	/* skip ':' */
	if (*cp != 0)
		*file = cp;
	else
		f->f_flags |= F_RAW;

#ifdef SOFTRAID
	/* Intercept softraid disks. */
	if (strncmp("sr", dev, 2) == 0) {

		/* Create a fake diskinfo for this softraid volume. */
		SLIST_FOREACH(bv, &sr_volumes, sbv_link)
			if (bv->sbv_unit == unit)
				break;
		if (bv == NULL) {
			printf("Unknown device: sr%d\n", unit);
			return EADAPT;
		}

		if (bv->sbv_level == 'C' && bv->sbv_keys == NULL)
			if (sr_crypto_decrypt_keys(bv) != 0)
				return EPERM;

		if (bv->sbv_diskinfo == NULL) {
			dip = alloc(sizeof(struct diskinfo));
			bzero(dip, sizeof(*dip));
			bv->sbv_diskinfo = dip;
			dip->sr_vol = bv;
			dip->bios_info.flags |= BDI_BADLABEL;
		}

		dip = bv->sbv_diskinfo;

		if (dip->bios_info.flags & BDI_BADLABEL) {
			/* Attempt to read disklabel. */
			bv->sbv_part = 'c';
			if (sr_getdisklabel(bv, &dip->disklabel))
				return ERDLAB;
			dip->bios_info.flags &= ~BDI_BADLABEL;
		}

		bv->sbv_part = part + 'a';

		bootdev_dip = dip;
		f->f_devdata = dip;

		return 0;
	}
#endif

	for (maj = 0; maj < nbdevs &&
	    strncmp(dev, bdevs[maj], devlen); maj++);
	if (maj >= nbdevs) {
		printf("Unknown device: ");
		for (cp = *file; *cp != ':'; cp++)
			putchar(*cp);
		putchar('\n');
		return EADAPT;
	}

	biosdev = unit;
	switch (maj) {
	case 0:  /* wd */
	case 4:  /* sd */
	case 17: /* hd */
		biosdev |= 0x80;
		break;
	case 2:  /* fd */
		break;
	case 6:  /* cd */
		biosdev = bios_bootdev & 0xff;
		break;
	default:
		return ENXIO;
	}

	/* Find device */
	bootdev_dip = dip = dklookup(biosdev);

	/* Fix up bootdev */
	{ dev_t bsd_dev;
		bsd_dev = dip->bios_info.bsd_dev;
		dip->bsddev = MAKEBOOTDEV(B_TYPE(bsd_dev), B_ADAPTOR(bsd_dev),
		    B_CONTROLLER(bsd_dev), unit, part);
		dip->bootdev = MAKEBOOTDEV(B_TYPE(bsd_dev), B_ADAPTOR(bsd_dev),
		    B_CONTROLLER(bsd_dev), B_UNIT(bsd_dev), part);
	}

#if 0
	dip->bios_info.bsd_dev = dip->bootdev;
	bootdev = dip->bootdev;
#endif

#ifdef BIOS_DEBUG
	if (debug) {
		printf("BIOS geometry: heads=%u, s/t=%u; EDD=%d\n",
		    dip->bios_info.bios_heads, dip->bios_info.bios_sectors,
		    dip->bios_info.bios_edd);
	}
#endif

	/* Try for disklabel again (might be removable media) */
	if (dip->bios_info.flags & BDI_BADLABEL) {
		st = bios_getdisklabel(&dip->bios_info, &dip->disklabel);
#ifdef BIOS_DEBUG
		if (debug && st)
			printf("%s\n", st);
#endif
		if (!st) {
			dip->bios_info.flags &= ~BDI_BADLABEL;
			dip->bios_info.flags |= BDI_GOODLABEL;
		} else
			return ERDLAB;
	}

	f->f_devdata = dip;

	return 0;
}

const u_char bidos_errs[] =
/* ignored	"\x00" "successful completion\0" */
		"\x01" "invalid function/parameter\0"
		"\x02" "address mark not found\0"
		"\x03" "write-protected\0"
		"\x04" "sector not found\0"
		"\x05" "reset failed\0"
		"\x06" "disk changed\0"
		"\x07" "drive parameter activity failed\0"
		"\x08" "DMA overrun\0"
		"\x09" "data boundary error\0"
		"\x0A" "bad sector detected\0"
		"\x0B" "bad track detected\0"
		"\x0C" "invalid media\0"
		"\x0E" "control data address mark detected\0"
		"\x0F" "DMA arbitration level out of range\0"
		"\x10" "uncorrectable CRC or ECC error on read\0"
/* ignored	"\x11" "data ECC corrected\0" */
		"\x20" "controller failure\0"
		"\x31" "no media in drive\0"
		"\x32" "incorrect drive type in CMOS\0"
		"\x40" "seek failed\0"
		"\x80" "operation timed out\0"
		"\xAA" "drive not ready\0"
		"\xB0" "volume not locked in drive\0"
		"\xB1" "volume locked in drive\0"
		"\xB2" "volume not removable\0"
		"\xB3" "volume in use\0"
		"\xB4" "lock count exceeded\0"
		"\xB5" "valid eject request failed\0"
		"\xBB" "undefined error\0"
		"\xCC" "write fault\0"
		"\xE0" "status register error\0"
		"\xFF" "sense operation failed\0"
		"\x00" "\0";

static const char *
biosdisk_err(u_int error)
{
	register const u_char *p = bidos_errs;

	while (*p && *p != error)
		while (*p++);

	return ++p;
}

const struct biosdisk_errors {
	u_char error;
	u_char errno;
} tab[] = {
	{ 0x01, EINVAL },
	{ 0x03, EROFS },
	{ 0x08, EINVAL },
	{ 0x09, EINVAL },
	{ 0x0A, EBSE },
	{ 0x0B, EBSE },
	{ 0x0C, ENXIO },
	{ 0x0D, EINVAL },
	{ 0x10, EECC },
	{ 0x20, EHER },
	{ 0x31, ENXIO },
	{ 0x32, ENXIO },
	{ 0x00, EIO }
};

static int
biosdisk_errno(u_int error)
{
	register const struct biosdisk_errors *p;

	if (error == 0)
		return 0;

	for (p = tab; p->error && p->error != error; p++);

	return p->errno;
}

int
biosstrategy(void *devdata, int rw, daddr32_t blk, size_t size, void *buf,
    size_t *rsize)
{
	struct diskinfo *dip = (struct diskinfo *)devdata;
	bios_diskinfo_t *bd = &dip->bios_info;
	u_int8_t error = 0;
	size_t nsect;

#ifdef SOFTRAID
	/* Intercept strategy for softraid volumes. */
	if (dip->sr_vol)
		return sr_strategy(dip->sr_vol, rw, blk, size, buf, rsize);
#endif

	nsect = (size + DEV_BSIZE - 1) / DEV_BSIZE;
	blk += dip->disklabel.d_partitions[B_PARTITION(dip->bsddev)].p_offset;

	/* Read all, sub-functions handle track boundaries */
	if (blk < 0)
		error = EINVAL;
	else
		error = biosd_io(rw, bd, blk, nsect, buf);

#ifdef BIOS_DEBUG
	if (debug) {
		if (error != 0)
			printf("=0x%x(%s)", error, biosdisk_err(error));
		putchar('\n');
	}
#endif

	if (rsize != NULL)
		*rsize = nsect * DEV_BSIZE;

	return (biosdisk_errno(error));
}

int
biosclose(struct open_file *f)
{
	f->f_devdata = NULL;

	return 0;
}

int
biosioctl(struct open_file *f, u_long cmd, void *data)
{
	return 0;
}