diff options
author | kevans <kevans@FreeBSD.org> | 2018-02-12 01:08:44 +0000 |
---|---|---|
committer | kevans <kevans@FreeBSD.org> | 2018-02-12 01:08:44 +0000 |
commit | 7d97ee5b28b409c00bfaf12daf5ab497a6038b9d (patch) | |
tree | 245306b754606bcf49c0ff17b131b58609b6c7a6 /stand/i386 | |
parent | 43b278e1b66cf4de337a17034087ea785031bd6f (diff) | |
download | FreeBSD-src-7d97ee5b28b409c00bfaf12daf5ab497a6038b9d.zip FreeBSD-src-7d97ee5b28b409c00bfaf12daf5ab497a6038b9d.tar.gz |
MFC r325834,r325997,326502: Move sys/boot to stand/
This is effectively a direct commit to stable/11, due to differences between
stable/11 and head. Changes to DTS in sys/boot/fdt/dts were often
accompanied by kernel changes. Many of these were also risc-v updates that
likely had many more dependencies to MFC.
Because of this, sys/boot/fdt/dts remains as-is while everything else in
sys/boot relocates to stand/.
r325834: Move sys/boot to stand. Fix all references to new location
r325997: Remove empty directories.
r326502: Document the sys/boot -> stand move in hier.7 and the top-level README.
Diffstat (limited to 'stand/i386')
119 files changed, 20965 insertions, 0 deletions
diff --git a/stand/i386/Makefile b/stand/i386/Makefile new file mode 100644 index 0000000..9a1663a --- /dev/null +++ b/stand/i386/Makefile @@ -0,0 +1,25 @@ +# $FreeBSD$ + +.include <bsd.init.mk> + +SUBDIR= mbr pmbr boot0 boot0sio btx boot2 cdboot gptboot \ + libi386 + +.if ${MK_LOADER_FIREWIRE} == "yes" +SUBDIR+= libfirewire +.endif + +SUBDIR+= loader + +# special boot programs, 'self-extracting boot2+loader' +SUBDIR+= pxeldr + +.if ${MACHINE_CPUARCH} == "i386" +SUBDIR+= kgzldr +.endif + +.if ${MK_ZFS} != "no" +SUBDIR+= zfsboot gptzfsboot zfsloader +.endif + +.include <bsd.subdir.mk> diff --git a/stand/i386/Makefile.inc b/stand/i386/Makefile.inc new file mode 100644 index 0000000..6a4bea7 --- /dev/null +++ b/stand/i386/Makefile.inc @@ -0,0 +1,36 @@ +# Common defines for all of /stand/i386/ +# +# $FreeBSD$ + +LOADER_ADDRESS?=0x200000 +CFLAGS+= -march=i386 -ffreestanding +CFLAGS.gcc+= -mpreferred-stack-boundary=2 +CFLAGS+= ${CFLAGS_NO_SIMD} -msoft-float +LDFLAGS+= -nostdlib + +# BTX components +BTXDIR= ${BOOTOBJ}/i386/btx +BTXLDR= ${BTXDIR}/btxldr/btxldr +BTXKERN= ${BTXDIR}/btx/btx +BTXCRT= ${BTXDIR}/lib/crt0.o + +BTXSRC= ${BOOTSRC}/i386/btx +BTXLIB= ${BTXSRC}/lib + +# compact binary with no padding between text, data, bss +LDSCRIPT= ${BOOTSRC}/i386/boot.ldscript +# LDFLAGS_BIN=-e start -Ttext ${ORG} -Wl,-T,${LDSCRIPT},-S,--oformat,binary +# LD_FLAGS_BIN=-static -T ${LDSCRIPT} --gc-sections +LDFLAGS_BIN=-e start -Ttext ${ORG} -Wl,-N,-S,--oformat,binary +LD_FLAGS_BIN=-static -N --gc-sections + +.if ${MACHINE_CPUARCH} == "amd64" +DO32=1 +.endif + +.if defined(LOADER_FIREWIRE_SUPPORT) +MK_LOADER_FIREWIRE=yes +.warning "LOADER_FIREWIRE_SUPPORT deprecated, please move to WITH_LOADER_FIREWIRE" +.endif + +.include "../Makefile.inc" diff --git a/stand/i386/boot.ldscript b/stand/i386/boot.ldscript new file mode 100644 index 0000000..04ea39d --- /dev/null +++ b/stand/i386/boot.ldscript @@ -0,0 +1,11 @@ +/* $FreeBSD$ */ +/* Merge text, data and bss together almost no padding */ +OUTPUT_FORMAT("elf32-i386-freebsd") +OUTPUT_ARCH(i386) +ENTRY(_start) +SECTIONS { + . = 0x08048000 + SIZEOF_HEADERS; + .text : { *(.text) } =0x90909090 /* Pad with nops, if needed */ + .data : { *(.data) } _edata = .; + .bss : { *(.bss) } _end = .; +} diff --git a/stand/i386/boot0/Makefile b/stand/i386/boot0/Makefile new file mode 100644 index 0000000..39c0072 --- /dev/null +++ b/stand/i386/boot0/Makefile @@ -0,0 +1,80 @@ +# $FreeBSD$ + +PROG?= boot0 +STRIP= +BINMODE=${NOBINMODE} +MAN= +SRCS= ${PROG}.S + +# Additional options that you can specify with make OPTS="..." +# (these only apply to boot0.S) +# +# -DVOLUME_SERIAL support volume serial number (NT, XP, Vista) +# -DSIO do I/O using COM1: +# -DPXE fallback to INT18/PXE with F6 +# -DCHECK_DRIVE enable checking drive number +# -DONLY_F_KEYS accept only Fx keys in console +# -DTEST print drive number on entry +# +OPTS ?= -DVOLUME_SERIAL -DPXE +CFLAGS += ${OPTS} + +# Flags used in the boot0.S code: +# 0x0f all valid partitions enabled. +# 0x80 'packet', use BIOS EDD (LBA) extensions instead of CHS +# to read from disk. boot0.S does not check that the extensions +# are supported, but all modern BIOSes should have them. +# 0x40 'noupdate', disable writing boot0 back to disk so that +# the current selection is not preserved across reboots. +# 0x20 'setdrv', override the drive number supplied by the bios +# with the one in the boot sector. + +# Default boot flags: +BOOT_BOOT0_FLAGS?= 0x8f + +# The number of timer ticks to wait for a keypress before assuming the default +# selection. Since there are 18.2 ticks per second, the default value of +# 0xb6 (182d) corresponds to 10 seconds. +BOOT_BOOT0_TICKS?= 0xb6 + +# The base address that we the boot0 code to to run it. Don't change this +# unless you are glutton for punishment. +BOOT_BOOT0_ORG?= 0x600 +ORG=${BOOT_BOOT0_ORG} + +# Comm settings for boot0sio. +# Bit(s) Description +# 7-5 data rate (110,150,300,600,1200,2400,4800,9600 bps) +# 4-3 parity (00 or 10 = none, 01 = odd, 11 = even) +# 2 stop bits (set = 2, clear = 1) +# 1-0 data bits (00 = 5, 01 = 6, 10 = 7, 11 = 8) +.if !defined(BOOT_BOOT0_COMCONSOLE_SPEED) +BOOT_COMCONSOLE_SPEED?= 9600 +.if ${BOOT_COMCONSOLE_SPEED} == 9600 +BOOT_BOOT0_COMCONSOLE_SPEED= "7 << 5 + 3" +.elif ${BOOT_COMCONSOLE_SPEED} == 4800 +BOOT_BOOT0_COMCONSOLE_SPEED= "6 << 5 + 3" +.elif ${BOOT_COMCONSOLE_SPEED} == 2400 +BOOT_BOOT0_COMCONSOLE_SPEED= "5 << 5 + 3" +.elif ${BOOT_COMCONSOLE_SPEED} == 1200 +BOOT_BOOT0_COMCONSOLE_SPEED= "4 << 5 + 3" +.elif ${BOOT_COMCONSOLE_SPEED} == 600 +BOOT_BOOT0_COMCONSOLE_SPEED= "3 << 5 + 3" +.elif ${BOOT_COMCONSOLE_SPEED} == 300 +BOOT_BOOT0_COMCONSOLE_SPEED= "2 << 5 + 3" +.elif ${BOOT_COMCONSOLE_SPEED} == 150 +BOOT_BOOT0_COMCONSOLE_SPEED= "1 << 5 + 3" +.elif ${BOOT_COMCONSOLE_SPEED} == 110 +BOOT_BOOT0_COMCONSOLE_SPEED= "0 << 5 + 3" +.else +BOOT_BOOT0_COMCONSOLE_SPEED= "7 << 5 + 3" +.endif +.endif + +CFLAGS+=-DFLAGS=${BOOT_BOOT0_FLAGS} \ + -DTICKS=${BOOT_BOOT0_TICKS} \ + -DCOMSPEED=${BOOT_BOOT0_COMCONSOLE_SPEED} + +LDFLAGS+=${LDFLAGS_BIN} + +.include <bsd.prog.mk> diff --git a/stand/i386/boot0/Makefile.depend b/stand/i386/boot0/Makefile.depend new file mode 100644 index 0000000..f80275d --- /dev/null +++ b/stand/i386/boot0/Makefile.depend @@ -0,0 +1,11 @@ +# $FreeBSD$ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/stand/i386/boot0/boot0.S b/stand/i386/boot0/boot0.S new file mode 100644 index 0000000..708f093 --- /dev/null +++ b/stand/i386/boot0/boot0.S @@ -0,0 +1,682 @@ +/* + * Copyright (c) 2008 Luigi Rizzo (mostly documentation) + * Copyright (c) 2002 Bruce M. Simpson + * Copyright (c) 1998 Robert Nordier + * All rights reserved. + * + * Redistribution and use in source and binary forms are freely + * permitted provided that the above copyright notice and this + * paragraph and the following disclaimer are duplicated in all + * such forms. + * + * This software is provided "AS IS" and without any express or + * implied warranties, including, without limitation, the implied + * warranties of merchantability and fitness for a particular + * purpose. + * + * $FreeBSD$ + */ + +/* build options: */ +#ifdef SIO /* use serial console on COM1. */ +#endif + +#ifdef PXE /* enable PXE/INT18 booting with F6 */ +#define SAVE_MORE_MEMORY +#endif + +#ifdef CHECK_DRIVE /* make sure we boot from a HD. */ +#endif + +#ifdef ONLY_F_KEYS /* Only F1..F6, no digits on console */ +#endif + +#ifdef VOLUME_SERIAL /* support Volume serial number */ +#define B0_BASE 0x1ae /* move the internal data area */ +#define SAVE_MEMORY +#else +#define B0_BASE 0x1b2 +#endif + +#ifdef TEST /* enable some test code */ +#define SAVE_MEMORY +#define SAVE_MORE_MEMORY +#endif + +/* + * Note - this code uses many tricks to save space and fit in one sector. + * This includes using side effects of certain instructions, reusing + * register values from previous operations, etc. + * Be extremely careful when changing the code, even for simple things. + */ + +/* + * BOOT BLOCK STRUCTURE + * + * This code implements a Master Boot Record (MBR) for an Intel/PC disk. + * It is 512 bytes long and it is normally loaded by the BIOS (or another + * bootloader) at 0:0x7c00. This code depends on %cs:%ip being 0:0x7c00 + * + * The initial chunk of instructions is used as a signature by external + * tools (e.g. boot0cfg) which can manipulate the block itself. + * + * The area at offset 0x1b2 contains a magic string ('Drive '), also + * used as a signature to detect the block, and some variables that can + * be updated by boot0cfg (and optionally written back to the disk). + * These variables control the operation of the bootloader itself, + * e.g. which partitions to enable, the timeout, the use of LBA + * (called 'packet') or CHS mode, whether to force a drive number, + * and whether to write back the user's selection back to disk. + * + * As in every Master Boot Record, the partition table is at 0x1be, + * made of four 16-byte entries each containing: + * + * OFF SIZE DESCRIPTION + * 0 1 status (0x80: bootable, 0: non bootable) + * 1 3 start sector CHS + * 8:head, 6:sector, 2:cyl bit 9..8, 8:cyl bit 7..0 + * 4 1 partition type + * 5 3 end sector CHS + * 8 4 LBA of first sector + * 12 4 partition size in sectors + * + * and followed by the two bytes 0x55, 0xAA (MBR signature). + */ + + +/* + * BOOT BLOCK OPERATION + * + * On entry, the registers contain the following values: + * + * %cs:%ip 0:0x7c00 + * %dl drive number (0x80, 0x81, ... ) + * %si pointer to the partition table from which we were loaded. + * Some boot code (e.g. syslinux) use this info to relocate + * themselves, so we want to pass a valid one to the next stage. + * NOTE: the use of %si is not a standard. + * + * This boot block first relocates itself at a different address (0:0x600), + * to free the space at 0:0x7c00 for the next stage boot block. + * + * It then initializes some memory at 0:0x800 and above (pointed by %bp) + * to store the original drive number (%dl) passed to us, and to construct a + * fake partition entry. The latter is used by the disk I/O routine and, + * in some cases, passed in %si to the next stage boot code. + * + * The variables at 0x1b2 are accessed as negative offsets from %bp. + * + * After the relocation, the code scans the partition table printing + * out enabled partition or disks, and waits for user input. + * + * When a partition is selected, or a timeout expires, the currently + * selected partition is used to load the next stage boot code, + * %dl and %si are set appropriately as when we were called, and + * control is transferred to the newly loaded code at 0:0x7c00. + */ + +/* + * CONSTANTS + * + * NHRDRV is the address in segment 0 where the BIOS writes the + * total number of hard disks in the system. + * LOAD is the original load address and cannot be changed. + * ORIGIN is the relocation address. If you change it, you also need + * to change the value passed to the linker in the Makefile + * PRT_OFF is the location of the partition table (from the MBR standard). + * B0_OFF is the location of the data area, known to boot0cfg so + * it cannot be changed. Computed as a negative offset from 0x200 + * MAGIC is the signature of a boot block. + */ + + .set NHRDRV,0x475 # Number of hard drives + .set ORIGIN,0x600 # Execution address + .set LOAD,0x7c00 # Load address + + .set PRT_OFF,0x1be # Partition table + .set B0_OFF,(B0_BASE-0x200) # Offset of boot0 data + + .set MAGIC,0xaa55 # Magic: bootable + + .set KEY_ENTER,0x1c # Enter key scan code + .set KEY_F1,0x3b # F1 key scan code + .set KEY_1,0x02 # #1 key scan code + + .set ASCII_BEL,'#' # ASCII code for <BEL> + .set ASCII_CR,0x0D # ASCII code for <CR> + +/* + * Offsets of variables in the block at B0_OFF, and in the volatile + * data area, computed as displacement from %bp. + * We need to define them as constant as the assembler cannot + * compute them in its single pass. + */ + .set _NXTDRV, B0_OFF+6 # Next drive + .set _OPT, B0_OFF+7 # Default option + .set _SETDRV, B0_OFF+8 # Drive to force + .set _FLAGS, B0_OFF+9 # Flags + .set SETDRV, 0x20 # the 'setdrv' flag + .set NOUPDATE, 0x40 # the 'noupdate' flag + .set USEPACKET, 0x80 # the 'packet' flag + + /* ticks is at a fixed position */ + .set _TICKS, (PRT_OFF - 0x200 - 2) # Timeout ticks + .set _MNUOPT, 0x10 # Saved menu entries + + .set TLEN, (desc_ofs - bootable_ids) # size of bootable ids + .globl start # Entry point + .code16 # This runs in real mode + +/* + * MAIN ENTRY POINT + * Initialise segments and registers to known values. + * segments start at 0. + * The stack is immediately below the address we were loaded to. + * NOTE: the initial section of the code (up to movw $LOAD,%sp) + * is used by boot0cfg, together with the 'Drive ' string and + * the 0x55, 0xaa at the end, as an identifier for version 1.0 + * of the boot code. Do not change it. + * In version 1.0 the parameter table (_NEXTDRV etc) is at 0x1b9 + */ +start: cld # String ops inc + xorw %ax,%ax # Zero + movw %ax,%es # Address + movw %ax,%ds # data + movw %ax,%ss # Set up + movw $LOAD,%sp # stack + + /* + * Copy this code to the address it was linked for, 0x600 by default. + */ + movw %sp,%si # Source + movw $start,%di # Destination + movw $0x100,%cx # Word count + rep # Relocate + movsw # code + /* + * After the code, (i.e. at %di+0, 0x800) create a partition entry, + * initialized to LBA 0 / CHS 0:0:1. + * Set %bp to point to the partition and also, with negative offsets, + * to the variables embedded in the bootblock (nextdrv and so on). + */ + movw %di,%bp # Address variables + movb $0x8,%cl # Words to clear + rep # Zero + stosw # them + incb -0xe(%di) # Set the S field to 1 + + jmp main-LOAD+ORIGIN # Jump to relocated code + +main: +#if defined(SIO) && COMSPEED != 0 + /* + * Init the serial port. bioscom preserves the driver number in DX. + */ + movw $COMSPEED,%ax # defined by Makefile + callw bioscom +#endif + + /* + * If the 'setdrv' flag is set in the boot sector, use the drive + * number from the boot sector at 'setdrv_num'. + * Optionally, do the same if the BIOS gives us an invalid number + * (note though that the override prevents booting from a floppy + * or a ZIP/flash drive in floppy emulation). + * The test costs 4 bytes of code so it is disabled by default. + */ + testb $SETDRV,_FLAGS(%bp) # Set drive number? +#ifndef CHECK_DRIVE /* disable drive checks */ + jz save_curdrive # no, use the default +#else + jnz disable_update # Yes + testb %dl,%dl # Drive number valid? + js save_curdrive # Possibly (0x80 set) +#endif + /* + * Disable updates if the drive number is forced. + */ +disable_update: orb $NOUPDATE,_FLAGS(%bp) # Disable updates + movb _SETDRV(%bp),%dl # Use stored drive number + + /* + * Whatever drive we decided to use, store it at (%bp). The byte + * is normally used for the state of the partition (0x80 or 0x00), + * but we abuse it as it is very convenient to access at offset 0. + * The value is read back after 'check_selection' + */ +save_curdrive: movb %dl, (%bp) # Save drive number + pushw %dx # Also in the stack +#ifdef TEST /* test code, print internal bios drive */ + rolb $1, %dl + movw $drive, %si + call putkey +#endif + callw putn # Print a newline + /* + * Start out with a pointer to the 4th byte of the first table entry + * so that after 4 iterations it's beyond the end of the sector + * and beyond a 256 byte boundary. We use the latter trick to check for + * end of the loop without using an extra register (see start.5). + */ + movw $(partbl+0x4),%bx # Partition table (+4) + xorw %dx,%dx # Item number + + /* + * Loop around on the partition table, printing values until we + * pass a 256 byte boundary. + */ +read_entry: movb %ch,-0x4(%bx) # Zero active flag (ch == 0) + btw %dx,_FLAGS(%bp) # Entry enabled? + jnc next_entry # No + movb (%bx),%al # Load type + test %al, %al # skip empty partition + jz next_entry + /* + * Scan the table of bootable ids, which starts at %di and has + * length TLEN. On a match, %di points to the element following the + * match; the corresponding offset to the description is $(TLEN-1) + * bytes ahead. We use a count of TLEN+1 so if we don't find a match + * within the first TLEN entries, we hit the 'unknown' entry. + */ + movw $bootable_ids,%di # Lookup tables + movb $(TLEN+1),%cl # Number of entries + repne # Locate + scasb # type + /* + * Get the matching element in the next array. + * The byte at $(TLEN-1)(%di) contains the offset of the description + * string from %di, so we add the number and print the string. + */ + addw $(TLEN-1), %di # Adjust + movb (%di),%cl # Partition + addw %cx,%di # description + callw putx # Display it + +next_entry: incw %dx # Next item + addb $0x10,%bl # Next entry + jnc read_entry # Till done + /* + * We are past a 256 byte boundary: the partition table is finished. + * Add one to the drive number and check it is valid. + * Note that if we started from a floppy, %dl was 0 so we still + * get an entry for the next drive, which is the first Hard Disk. + */ + popw %ax # Drive number + subb $0x80-0x1,%al # Does next + cmpb NHRDRV,%al # drive exist? (from BIOS?) + jb print_drive # Yes + /* + * If this is the only drive, don't display it as an option. + */ + decw %ax # Already drive 0? + jz print_prompt # Yes + /* + * If it was illegal or we cycled through them, go back to drive 0. + */ + xorb %al,%al # Drive 0 + /* + * Whatever drive we selected, make it an ascii digit and save it + * back to the "nxtdrv" location in case we want to save it to disk. + * This digit is also part of the printed drive string, so add 0x80 + * to indicate end of string. + */ +print_drive: addb $'0'|0x80,%al # Save next + movb %al,_NXTDRV(%bp) # drive number + movw $drive,%di # Display + callw putx # item + /* + * Menu is complete, display a prompt followed by current selection. + * 'decw %si' makes the register point to the space after 'Boot: ' + * so we do not see an extra CRLF on the screen. + */ +print_prompt: movw $prompt,%si # Display + callw putstr # prompt + movb _OPT(%bp),%dl # Display + decw %si # default + callw putkey # key + jmp start_input # Skip beep + +/* + * Here we have the code waiting for user input or a timeout. + */ +beep: movb $ASCII_BEL,%al # Input error, print or beep + callw putchr + +start_input: + /* + * Actual Start of input loop. Take note of time + */ + xorb %ah,%ah # BIOS: Get + int $0x1a # system time + movw %dx,%di # Ticks when + addw _TICKS(%bp),%di # timeout +read_key: + /* + * Busy loop, looking for keystrokes but keeping one eye on the time. + */ +#ifndef SIO + movb $0x1,%ah # BIOS: Check + int $0x16 # for keypress +#else /* SIO */ + movb $0x03,%ah # BIOS: Read COM + call bioscom + testb $0x01,%ah # Check line status + # (bit 1 indicates input) +#endif /* SIO */ + jnz got_key # Have input + xorb %ah,%ah # BIOS: int 0x1a, 00 + int $0x1a # get system time + cmpw %di,%dx # Timeout? + jb read_key # No + + /* + * Timed out or default selection + */ +use_default: movb _OPT(%bp),%al # Load default + orb $NOUPDATE,_FLAGS(%bp) # Disable updates + jmp check_selection # Join common code + + /* + * Get the keystroke. + * ENTER or CR confirm the current selection (same as a timeout). + * Otherwise convert F1..F6 (or '1'..'6') to 0..5 and check if the + * selection is valid. + * The SIO code uses ascii chars, the console code uses scancodes. + */ +got_key: +#ifndef SIO + xorb %ah,%ah # BIOS: int 0x16, 00 + int $0x16 # get keypress + movb %ah,%al # move scan code to %al + cmpb $KEY_ENTER,%al +#else + movb $0x02,%ah # BIOS: Receive + call bioscom + cmpb $ASCII_CR,%al +#endif + je use_default # enter -> default + /* + * Check if the key is acceptable, and loop back if not. + * The console (non-SIO) code looks at scancodes and accepts + * both F1..F6 and 1..6 (the latter costs 6 bytes of code), + * relying on the fact that F1..F6 have higher scancodes than 1..6 + * The SIO code only takes 1..6 + */ +#ifdef SIO /* SIO mode, use ascii values */ + subb $'1',%al # Subtract '1' ascii code +#else /* console mode -- use scancodes */ + subb $KEY_F1,%al /* Subtract F1 scan code */ +#if !defined(ONLY_F_KEYS) + cmpb $0x5,%al # F1..F6 + jna 3f # Yes + subb $(KEY_1 - KEY_F1),%al # Less #1 scan code + 3: +#endif /* ONLY_F_KEYS */ +#endif /* SIO */ +check_selection: + cmpb $0x5,%al # F1..F6 or 1..6 ? +#ifdef PXE /* enable PXE/INT18 using F6 */ + jne 1f; + int $0x18 # found F6, try INT18 + 1: +#endif /* PXE */ + jae beep # Not in F1..F5, beep + + /* + * We have a selection. If it's a bad selection go back to complain. + * The bits in MNUOPT were set when the options were printed. + * Anything not printed is not an option. + */ + cbtw # Extend (%ah=0 used later) + btw %ax,_MNUOPT(%bp) # Option enabled? + jnc beep # No + /* + * Save the info in the original tables + * for rewriting to the disk. + */ + movb %al,_OPT(%bp) # Save option + + /* + * Make %si and %bx point to the fake partition at LBA 0 (CHS 0:0:1). + * Because the correct address is already in %bp, just use it. + * Set %dl with the drive number saved in byte 0. + * If we have pressed F5 or 5, then this is a good, fake value + * to present to the next stage boot code. + */ + movw %bp,%si # Partition for write + movb (%si),%dl # Drive number, saved above + movw %si,%bx # Partition for read + cmpb $0x4,%al # F5/#5 pressed? + pushf # Save results for later + je 1f # Yes, F5 + + /* + * F1..F4 was pressed, so make %bx point to the currently + * selected partition, and leave the drive number unchanged. + */ + shlb $0x4,%al # Point to + addw $partbl,%ax # selected + xchgw %bx,%ax # partition + movb $0x80,(%bx) # Flag active + /* + * If not asked to do a write-back (flags 0x40) don't do one. + * Around the call, save the partition pointer to %bx and + * restore to %si which is where the next stage expects it. + */ + 1: pushw %bx # Save + testb $NOUPDATE,_FLAGS(%bp) # No updates? + jnz 2f # skip update + movw $start,%bx # Data to write + movb $0x3,%ah # Write sector + callw intx13 # to disk + 2: popw %si # Restore + + /* + * If going to next drive, replace drive with selected one. + * Remember to un-ascii it. Hey 0x80 is already set, cool! + */ + popf # Restore %al test results + jne 3f # If not F5/#5 + movb _NXTDRV(%bp),%dl # Next drive + subb $'0',%dl # number + /* + * Load selected bootsector to the LOAD location in RAM. If read + * fails or there is no 0x55aa marker, treat it as a bad selection. + */ + 3: movw $LOAD,%bx # Address for read + movb $0x2,%ah # Read sector + callw intx13 # from disk + jc beep # If error + cmpw $MAGIC,0x1fe(%bx) # Bootable? + jne beep # No + pushw %si # Save ptr to selected part. + callw putn # Leave some space + popw %si # Restore, next stage uses it + jmp *%bx # Invoke bootstrap + +/* + * Display routines + * putkey prints the option selected in %dl (F1..F5 or 1..5) followed by + * the string at %si + * putx: print the option in %dl followed by the string at %di + * also record the drive as valid. + * putn: print a crlf + * putstr: print the string at %si + * putchr: print the char in al + */ + +/* + * Display the option and record the drive as valid in the options. + * That last point is done using the btsw instruction which does + * a test and set. We don't care for the test part. + */ +putx: btsw %dx,_MNUOPT(%bp) # Enable menu option + movw $item,%si # Display + callw putkey # key + movw %di,%si # Display the rest + callw putstr # Display string + +putn: movw $crlf,%si # To next line + jmp putstr + +putkey: +#ifndef SIO + movb $'F',%al # Display + callw putchr # 'F' +#endif + movb $'1',%al # Prepare + addb %dl,%al # digit + +putstr.1: callw putchr # Display char +putstr: lodsb # Get byte + testb $0x80,%al # End of string? + jz putstr.1 # No + andb $~0x80,%al # Clear MSB then print last + +putchr: +#ifndef SIO + pushw %bx # Save + movw $0x7,%bx # Page:attribute + movb $0xe,%ah # BIOS: Display + int $0x10 # character + popw %bx # Restore +#else /* SIO */ + movb $0x01,%ah # BIOS: Send character +bioscom: + pushw %dx # Save + xorw %dx,%dx # Use COM1 + int $0x14 # BIOS: Serial I/O + popw %dx # Restore +#endif /* SIO */ + retw # To caller + +/* One-sector disk I/O routine */ + +/* + * %dl: drive, %si partition entry, %es:%bx transfer buffer. + * Load the CHS values and possibly the LBA address from the block + * at %si, and use the appropriate method to load the sector. + * Don't use packet mode for a floppy. + */ +intx13: # Prepare CHS parameters + movb 0x1(%si),%dh # Load head + movw 0x2(%si),%cx # Load cylinder:sector + movb $0x1,%al # Sector count + pushw %si # Save + movw %sp,%di # Save +#ifndef CHECK_DRIVE /* floppy support */ + testb %dl, %dl # is this a floppy ? + jz 1f # Yes, use CHS mode +#endif + testb $USEPACKET,_FLAGS(%bp) # Use packet interface? + jz 1f # No + pushl $0x0 # Set the + pushl 0x8(%si) # LBA address + pushw %es # Set the transfer + pushw %bx # buffer address + push $0x1 # Block count + push $0x10 # Packet size + movw %sp,%si # Packet pointer + decw %ax # Verify off + orb $0x40,%ah # Use disk packet + 1: int $0x13 # BIOS: Disk I/O + movw %di,%sp # Restore + popw %si # Restore + retw # To caller + +/* + * Various menu strings. 'item' goes after 'prompt' to save space. + * Also use shorter versions to make room for the PXE/INT18 code. + */ +prompt: +#ifdef PXE + .ascii "\nF6 PXE\r" +#endif + .ascii "\nBoot:" +item: .ascii " "; .byte ' '|0x80 +crlf: .ascii "\r"; .byte '\n'|0x80 + +/* Partition type tables */ + +bootable_ids: + /* + * These values indicate bootable types we know about. + * Corresponding descriptions are at desc_ofs: + * Entries don't need to be sorted. + */ + .byte 0x83, 0xa5, 0xa6, 0xa9, 0x06, 0x07, 0x0b +#ifndef SAVE_MORE_MEMORY + .byte 0x05 # extended partition +#endif +#ifndef SAVE_MEMORY /* other DOS partitions */ + .byte 0x01 # FAT12 + .byte 0x04 # FAT16 < 32M +#endif + +desc_ofs: + /* + * Offsets that match the known types above, used to point to the + * actual partition name. The last entry must point to os_misc, + * which is used for non-matching names. + */ + .byte os_linux-. # 131, Linux + .byte os_freebsd-. # 165, FreeBSD + .byte os_bsd-. # 166, OpenBSD + .byte os_bsd-. # 169, NetBSD + .byte os_dos-. # 6, FAT16 >= 32M + .byte os_win-. # 7, NTFS + .byte os_win-. # 11, FAT32 + +#ifndef SAVE_MORE_MEMORY + .byte os_ext-. # 5, DOS Ext +#endif +#ifndef SAVE_MEMORY + .byte os_dos-. # 1, FAT12 DOS + .byte os_dos-. # 4, FAT16 <32M +#endif + .byte os_misc-. # Unknown + + /* + * And here are the strings themselves. The last byte of + * the string has bit 7 set. + */ +os_misc: .byte '?'|0x80 +os_dos: +#ifndef SAVE_MORE_MEMORY /* 'DOS' remapped to 'WIN' if no room */ + .ascii "DO"; .byte 'S'|0x80 +#endif +os_win: .ascii "Wi"; .byte 'n'|0x80 +os_linux: .ascii "Linu"; .byte 'x'|0x80 +os_freebsd: .ascii "Free" +os_bsd: .ascii "BS"; .byte 'D'|0x80 +#ifndef SAVE_MORE_MEMORY +os_ext: .ascii "EX"; .byte 'T'|0x80 +#endif + + .org (0x200 + B0_OFF),0x90 +/* + * The boot0 version 1.0 parameter table. + * Do not move it nor change the "Drive " string, boot0cfg + * uses its offset and content to identify the boot sector. + * The other fields are sometimes changed before writing back to the drive + * Be especially careful that nxtdrv: must come after drive:, as it + * is part of the same string. + */ +drive: .ascii "Drive " +nxtdrv: .byte 0x0 # Next drive number +opt: .byte 0x0 # Option +setdrv_num: .byte 0x80 # Drive to force +flags: .byte FLAGS # Flags +#ifdef VOLUME_SERIAL + .byte 0xa8,0xa8,0xa8,0xa8 # Volume Serial Number +#endif +ticks: .word TICKS # Delay + + .org PRT_OFF +/* + * Here is the 64 byte partition table that fdisk would fiddle with. + */ +partbl: .fill 0x40,0x1,0x0 # Partition table + .word MAGIC # Magic number + .org 0x200 # again, safety check +endblock: diff --git a/stand/i386/boot0sio/Makefile b/stand/i386/boot0sio/Makefile new file mode 100644 index 0000000..1321dd3 --- /dev/null +++ b/stand/i386/boot0sio/Makefile @@ -0,0 +1,8 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../boot0 + +PROGNAME= boot0sio +CFLAGS+= -DSIO + +.include "${.CURDIR}/../boot0/Makefile" diff --git a/stand/i386/boot0sio/Makefile.depend b/stand/i386/boot0sio/Makefile.depend new file mode 100644 index 0000000..f80275d --- /dev/null +++ b/stand/i386/boot0sio/Makefile.depend @@ -0,0 +1,11 @@ +# $FreeBSD$ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/stand/i386/boot2/Makefile b/stand/i386/boot2/Makefile new file mode 100644 index 0000000..c3146b3 --- /dev/null +++ b/stand/i386/boot2/Makefile @@ -0,0 +1,99 @@ +# $FreeBSD$ + +.include <bsd.init.mk> + +FILES= boot boot1 boot2 + +NM?= nm + +# A value of 0x80 enables LBA support. +BOOT_BOOT1_FLAGS?= 0x80 + +BOOT_COMCONSOLE_PORT?= 0x3f8 +BOOT_COMCONSOLE_SPEED?= 9600 +B2SIOFMT?= 0x3 + +REL1= 0x700 +ORG1= 0x7c00 +ORG2= 0x2000 + +# Decide level of UFS support. +BOOT2_UFS?= UFS1_AND_UFS2 +#BOOT2_UFS?= UFS2_ONLY +#BOOT2_UFS?= UFS1_ONLY + +CFLAGS+=-fomit-frame-pointer \ + -mrtd \ + -mregparm=3 \ + -D${BOOT2_UFS} \ + -DFLAGS=${BOOT_BOOT1_FLAGS} \ + -DSIOPRT=${BOOT_COMCONSOLE_PORT} \ + -DSIOFMT=${B2SIOFMT} \ + -DSIOSPD=${BOOT_COMCONSOLE_SPEED} \ + -I${LDRSRC} \ + -I${BTXLIB} \ + -Wall -Waggregate-return -Wbad-function-cast -Wno-cast-align \ + -Wmissing-declarations -Wmissing-prototypes -Wnested-externs \ + -Wpointer-arith -Wshadow -Wstrict-prototypes -Wwrite-strings \ + -Winline + +CFLAGS.gcc+= -Os \ + -fno-asynchronous-unwind-tables \ + --param max-inline-insns-single=100 +.if ${COMPILER_TYPE} == "gcc" && ${COMPILER_VERSION} <= 40201 +CFLAGS.gcc+= -mno-align-long-strings +.endif + +CFLAGS.clang+= -Oz ${CLANG_OPT_SMALL} + +LD_FLAGS+=${LD_FLAGS_BIN} + +CLEANFILES+= boot + +boot: boot1 boot2 + cat boot1 boot2 > boot + +CLEANFILES+= boot1 boot1.out boot1.o + +boot1: boot1.out + ${OBJCOPY} -S -O binary boot1.out ${.TARGET} + +boot1.out: boot1.o + ${LD} ${LD_FLAGS} -e start -Ttext ${ORG1} -o ${.TARGET} boot1.o + +CLEANFILES+= boot2 boot2.ld boot2.ldr boot2.bin boot2.out boot2.o \ + boot2.h sio.o + +BOOT2SIZE= 7680 + +boot2: boot2.ld + @set -- `ls -l ${.ALLSRC}`; x=$$((${BOOT2SIZE}-$$5)); \ + echo "$$x bytes available"; test $$x -ge 0 + ${DD} if=${.ALLSRC} of=${.TARGET} obs=${BOOT2SIZE} conv=osync + +boot2.ld: boot2.ldr boot2.bin ${BTXKERN} + btxld -v -E ${ORG2} -f bin -b ${BTXKERN} -l boot2.ldr \ + -o ${.TARGET} -P 1 boot2.bin + +boot2.ldr: + ${DD} if=/dev/zero of=${.TARGET} bs=512 count=1 + +boot2.bin: boot2.out + ${OBJCOPY} -S -O binary boot2.out ${.TARGET} + +boot2.out: ${BTXCRT} boot2.o sio.o + ${LD} ${LD_FLAGS} -Ttext ${ORG2} -o ${.TARGET} ${.ALLSRC} + +SRCS= boot2.c boot2.h + +boot2.h: boot1.out + ${NM} -t d ${.ALLSRC} | awk '/([0-9])+ T xread/ \ + { x = $$1 - ORG1; \ + printf("#define XREADORG %#x\n", REL1 + x) }' \ + ORG1=`printf "%d" ${ORG1}` \ + REL1=`printf "%d" ${REL1}` > ${.TARGET} + +.include <bsd.prog.mk> + +# XXX: clang integrated-as doesn't grok .codeNN directives yet +CFLAGS.boot1.S= ${CLANG_NO_IAS} diff --git a/stand/i386/boot2/Makefile.depend b/stand/i386/boot2/Makefile.depend new file mode 100644 index 0000000..d5a67a6 --- /dev/null +++ b/stand/i386/boot2/Makefile.depend @@ -0,0 +1,14 @@ +# $FreeBSD$ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + include \ + sys/boot/i386/btx/btx \ + sys/boot/i386/btx/lib \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/stand/i386/boot2/boot1.S b/stand/i386/boot2/boot1.S new file mode 100644 index 0000000..984ab52 --- /dev/null +++ b/stand/i386/boot2/boot1.S @@ -0,0 +1,373 @@ +/* + * Copyright (c) 1998 Robert Nordier + * All rights reserved. + * + * Redistribution and use in source and binary forms are freely + * permitted provided that the above copyright notice and this + * paragraph and the following disclaimer are duplicated in all + * such forms. + * + * This software is provided "AS IS" and without any express or + * implied warranties, including, without limitation, the implied + * warranties of merchantability and fitness for a particular + * purpose. + * + * $FreeBSD$ + */ + +/* Memory Locations */ + .set MEM_REL,0x700 # Relocation address + .set MEM_ARG,0x900 # Arguments + .set MEM_ORG,0x7c00 # Origin + .set MEM_BUF,0x8c00 # Load area + .set MEM_BTX,0x9000 # BTX start + .set MEM_JMP,0x9010 # BTX entry point + .set MEM_USR,0xa000 # Client start + .set BDA_BOOT,0x472 # Boot howto flag + +/* Partition Constants */ + .set PRT_OFF,0x1be # Partition offset + .set PRT_NUM,0x4 # Partitions + .set PRT_BSD,0xa5 # Partition type + +/* Flag Bits */ + .set FL_PACKET,0x80 # Packet mode + +/* Misc. Constants */ + .set SIZ_PAG,0x1000 # Page size + .set SIZ_SEC,0x200 # Sector size + + .set NSECT,0x10 + .globl start + .globl xread + .code16 + +start: jmp main # Start recognizably + +/* + * This is the start of a standard BIOS Parameter Block (BPB). Most bootable + * FAT disks have this at the start of their MBR. While normal BIOS's will + * work fine without this section, IBM's El Torito emulation "fixes" up the + * BPB by writing into the memory copy of the MBR. Rather than have data + * written into our xread routine, we'll define a BPB to work around it. + * The data marked with (T) indicates a field required for a ThinkPad to + * recognize the disk and (W) indicates fields written from IBM BIOS code. + * The use of the BPB is based on what OpenBSD and NetBSD implemented in + * their boot code but the required fields were determined by trial and error. + * + * Note: If additional space is needed in boot1, one solution would be to + * move the "prompt" message data (below) to replace the OEM ID. + */ + .org 0x03, 0x00 +oemid: .space 0x08, 0x00 # OEM ID + + .org 0x0b, 0x00 +bpb: .word 512 # sector size (T) + .byte 0 # sectors/clustor + .word 0 # reserved sectors + .byte 0 # number of FATs + .word 0 # root entries + .word 0 # small sectors + .byte 0 # media type (W) + .word 0 # sectors/fat + .word 18 # sectors per track (T) + .word 2 # number of heads (T) + .long 0 # hidden sectors (W) + .long 0 # large sectors + + .org 0x24, 0x00 +ebpb: .byte 0 # BIOS physical drive number (W) + + .org 0x25,0x90 +/* + * Trampoline used by boot2 to call read to read data from the disk via + * the BIOS. Call with: + * + * %cx:%ax - long - LBA to read in + * %es:(%bx) - caddr_t - buffer to read data into + * %dl - byte - drive to read from + * %dh - byte - num sectors to read + */ + +xread: push %ss # Address + pop %ds # data +/* + * Setup an EDD disk packet and pass it to read + */ +xread.1: # Starting + pushl $0x0 # absolute + push %cx # block + push %ax # number + push %es # Address of + push %bx # transfer buffer + xor %ax,%ax # Number of + movb %dh,%al # blocks to + push %ax # transfer + push $0x10 # Size of packet + mov %sp,%bp # Packet pointer + callw read # Read from disk + lea 0x10(%bp),%sp # Clear stack + lret # To far caller +/* + * Load the rest of boot2 and BTX up, copy the parts to the right locations, + * and start it all up. + */ + +/* + * Setup the segment registers to flat addressing (segment 0) and setup the + * stack to end just below the start of our code. + */ +main: cld # String ops inc + xor %cx,%cx # Zero + mov %cx,%es # Address + mov %cx,%ds # data + mov %cx,%ss # Set up + mov $start,%sp # stack +/* + * Relocate ourself to MEM_REL. Since %cx == 0, the inc %ch sets + * %cx == 0x100. Note that boot1 does not use this relocated copy + * of itself while loading boot2; however, BTX reclaims the memory + * used by boot1 during its initialization. As a result, boot2 uses + * xread from the relocated copy. + */ + mov %sp,%si # Source + mov $MEM_REL,%di # Destination + incb %ch # Word count + rep # Copy + movsw # code +/* + * If we are on a hard drive, then load the MBR and look for the first + * FreeBSD slice. We use the fake partition entry below that points to + * the MBR when we call nread. The first pass looks for the first active + * FreeBSD slice. The second pass looks for the first non-active FreeBSD + * slice if the first one fails. + */ + mov $part4,%si # Partition + cmpb $0x80,%dl # Hard drive? + jb main.4 # No + movb $0x1,%dh # Block count + callw nread # Read MBR + mov $0x1,%cx # Two passes +main.1: mov $MEM_BUF+PRT_OFF,%si # Partition table + movb $0x1,%dh # Partition +main.2: cmpb $PRT_BSD,0x4(%si) # Our partition type? + jne main.3 # No + jcxz main.5 # If second pass + testb $0x80,(%si) # Active? + jnz main.5 # Yes +main.3: add $0x10,%si # Next entry + incb %dh # Partition + cmpb $0x1+PRT_NUM,%dh # In table? + jb main.2 # Yes + dec %cx # Do two + jcxz main.1 # passes +/* + * If we get here, we didn't find any FreeBSD slices at all, so print an + * error message and die. + */ + mov $msg_part,%si # Message + jmp error # Error +/* + * Floppies use partition 0 of drive 0. + */ +main.4: xor %dx,%dx # Partition:drive +/* + * Ok, we have a slice and drive in %dx now, so use that to locate and load + * boot2. %si references the start of the slice we are looking for, so go + * ahead and load up the first 16 sectors (boot1 + boot2) from that. When + * we read it in, we conveniently use 0x8c00 as our transfer buffer. Thus, + * boot1 ends up at 0x8c00, and boot2 starts at 0x8c00 + 0x200 = 0x8e00. + * The first part of boot2 is the disklabel, which is 0x200 bytes long. + * The second part is BTX, which is thus loaded into 0x9000, which is where + * it also runs from. The boot2.bin binary starts right after the end of + * BTX, so we have to figure out where the start of it is and then move the + * binary to 0xc000. Normally, BTX clients start at MEM_USR, or 0xa000, but + * when we use btxld to create boot2, we use an entry point of 0x2000. That + * entry point is relative to MEM_USR; thus boot2.bin starts at 0xc000. + */ +main.5: mov %dx,MEM_ARG # Save args + movb $NSECT,%dh # Sector count + callw nread # Read disk + mov $MEM_BTX,%bx # BTX + mov 0xa(%bx),%si # Get BTX length and set + add %bx,%si # %si to start of boot2.bin + mov $MEM_USR+SIZ_PAG*2,%di # Client page 2 + mov $MEM_BTX+(NSECT-1)*SIZ_SEC,%cx # Byte + sub %si,%cx # count + rep # Relocate + movsb # client + +/* + * Enable A20 so we can access memory above 1 meg. + * Use the zero-valued %cx as a timeout for embedded hardware which do not + * have a keyboard controller. + */ +seta20: cli # Disable interrupts +seta20.1: dec %cx # Timeout? + jz seta20.3 # Yes + inb $0x64,%al # Get status + testb $0x2,%al # Busy? + jnz seta20.1 # Yes + movb $0xd1,%al # Command: Write + outb %al,$0x64 # output port +seta20.2: inb $0x64,%al # Get status + testb $0x2,%al # Busy? + jnz seta20.2 # Yes + movb $0xdf,%al # Enable + outb %al,$0x60 # A20 +seta20.3: sti # Enable interrupts + + jmp start+MEM_JMP-MEM_ORG # Start BTX + + +/* + * Trampoline used to call read from within boot1. + */ +nread: mov $MEM_BUF,%bx # Transfer buffer + mov 0x8(%si),%ax # Get + mov 0xa(%si),%cx # LBA + push %cs # Read from + callw xread.1 # disk + jnc return # If success, return + mov $msg_read,%si # Otherwise, set the error + # message and fall through to + # the error routine +/* + * Print out the error message pointed to by %ds:(%si) followed + * by a prompt, wait for a keypress, and then reboot the machine. + */ +error: callw putstr # Display message + mov $prompt,%si # Display + callw putstr # prompt + xorb %ah,%ah # BIOS: Get + int $0x16 # keypress + movw $0x1234, BDA_BOOT # Do a warm boot + ljmp $0xf000,$0xfff0 # reboot the machine +/* + * Display a null-terminated string using the BIOS output. + */ +putstr.0: mov $0x7,%bx # Page:attribute + movb $0xe,%ah # BIOS: Display + int $0x10 # character +putstr: lodsb # Get char + testb %al,%al # End of string? + jne putstr.0 # No + +/* + * Overused return code. ereturn is used to return an error from the + * read function. Since we assume putstr succeeds, we (ab)use the + * same code when we return from putstr. + */ +ereturn: movb $0x1,%ah # Invalid + stc # argument +return: retw # To caller +/* + * Reads sectors from the disk. If EDD is enabled, then check if it is + * installed and use it if it is. If it is not installed or not enabled, then + * fall back to using CHS. Since we use a LBA, if we are using CHS, we have to + * fetch the drive parameters from the BIOS and divide it out ourselves. + * Call with: + * + * %dl - byte - drive number + * stack - 10 bytes - EDD Packet + */ +read: testb $FL_PACKET,%cs:MEM_REL+flags-start # LBA support enabled? + jz read.1 # No, use CHS + cmpb $0x80,%dl # Hard drive? + jb read.1 # No, use CHS + mov $0x55aa,%bx # Magic + push %dx # Save + movb $0x41,%ah # BIOS: Check + int $0x13 # extensions present + pop %dx # Restore + jc read.1 # If error, use CHS + cmp $0xaa55,%bx # Magic? + jne read.1 # No, so use CHS + testb $0x1,%cl # Packet interface? + jz read.1 # No, so use CHS + mov %bp,%si # Disk packet + movb $0x42,%ah # BIOS: Extended + int $0x13 # read + retw # To caller +read.1: push %dx # Save + movb $0x8,%ah # BIOS: Get drive + int $0x13 # parameters + movb %dh,%ch # Max head number + pop %dx # Restore + jc return # If error + andb $0x3f,%cl # Sectors per track + jz ereturn # If zero + cli # Disable interrupts + mov 0x8(%bp),%eax # Get LBA + push %dx # Save + movzbl %cl,%ebx # Divide by + xor %edx,%edx # sectors + div %ebx # per track + movb %ch,%bl # Max head number + movb %dl,%ch # Sector number + inc %bx # Divide by + xorb %dl,%dl # number + div %ebx # of heads + movb %dl,%bh # Head number + pop %dx # Restore + cmpl $0x3ff,%eax # Cylinder number supportable? + sti # Enable interrupts + ja ereturn # No, return an error + xchgb %al,%ah # Set up cylinder + rorb $0x2,%al # number + orb %ch,%al # Merge + inc %ax # sector + xchg %ax,%cx # number + movb %bh,%dh # Head number + subb %ah,%al # Sectors this track + mov 0x2(%bp),%ah # Blocks to read + cmpb %ah,%al # To read + jb read.2 # this +#ifdef TRACK_AT_A_TIME + movb %ah,%al # track +#else + movb $1,%al # one sector +#endif +read.2: mov $0x5,%di # Try count +read.3: les 0x4(%bp),%bx # Transfer buffer + push %ax # Save + movb $0x2,%ah # BIOS: Read + int $0x13 # from disk + pop %bx # Restore + jnc read.4 # If success + dec %di # Retry? + jz read.6 # No + xorb %ah,%ah # BIOS: Reset + int $0x13 # disk system + xchg %bx,%ax # Block count + jmp read.3 # Continue +read.4: movzbw %bl,%ax # Sectors read + add %ax,0x8(%bp) # Adjust + jnc read.5 # LBA, + incw 0xa(%bp) # transfer +read.5: shlb %bl # buffer + add %bl,0x5(%bp) # pointer, + sub %al,0x2(%bp) # block count + ja read.1 # If not done +read.6: retw # To caller + +/* Messages */ + +msg_read: .asciz "Read" +msg_part: .asciz "Boot" + +prompt: .asciz " error\r\n" + +flags: .byte FLAGS # Flags + + .org PRT_OFF,0x90 + +/* Partition table */ + + .fill 0x30,0x1,0x0 +part4: .byte 0x80, 0x00, 0x01, 0x00 + .byte 0xa5, 0xfe, 0xff, 0xff + .byte 0x00, 0x00, 0x00, 0x00 + .byte 0x50, 0xc3, 0x00, 0x00 # 50000 sectors long, bleh + + .word 0xaa55 # Magic number diff --git a/stand/i386/boot2/boot2.c b/stand/i386/boot2/boot2.c new file mode 100644 index 0000000..cc5d76f --- /dev/null +++ b/stand/i386/boot2/boot2.c @@ -0,0 +1,646 @@ +/*- + * Copyright (c) 1998 Robert Nordier + * All rights reserved. + * + * Redistribution and use in source and binary forms are freely + * permitted provided that the above copyright notice and this + * paragraph and the following disclaimer are duplicated in all + * such forms. + * + * This software is provided "AS IS" and without any express or + * implied warranties, including, without limitation, the implied + * warranties of merchantability and fitness for a particular + * purpose. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/disklabel.h> +#include <sys/diskmbr.h> +#include <sys/dirent.h> +#include <sys/reboot.h> + +#include <machine/bootinfo.h> +#include <machine/elf.h> + +#include <stdarg.h> + +#include <a.out.h> + +#include <btxv86.h> + +#include "boot2.h" +#include "lib.h" +#include "paths.h" +#include "rbx.h" + +/* Define to 0 to omit serial support */ +#ifndef SERIAL +#define SERIAL 1 +#endif + +#define IO_KEYBOARD 1 +#define IO_SERIAL 2 + +#if SERIAL +#define DO_KBD (ioctrl & IO_KEYBOARD) +#define DO_SIO (ioctrl & IO_SERIAL) +#else +#define DO_KBD (1) +#define DO_SIO (0) +#endif + +#define SECOND 18 /* Circa that many ticks in a second. */ + +#define ARGS 0x900 +#define NOPT 14 +#define NDEV 3 +#define MEM_BASE 0x12 +#define MEM_EXT 0x15 + +#define DRV_HARD 0x80 +#define DRV_MASK 0x7f + +#define TYPE_AD 0 +#define TYPE_DA 1 +#define TYPE_MAXHARD TYPE_DA +#define TYPE_FD 2 + +extern uint32_t _end; + +static const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */ +static const unsigned char flags[NOPT] = { + RBX_DUAL, + RBX_SERIAL, + RBX_ASKNAME, + RBX_CDROM, + RBX_CONFIG, + RBX_KDB, + RBX_GDB, + RBX_MUTE, + RBX_NOINTR, + RBX_PAUSE, + RBX_QUIET, + RBX_DFLTROOT, + RBX_SINGLE, + RBX_VERBOSE +}; + +static const char *const dev_nm[NDEV] = {"ad", "da", "fd"}; +static const unsigned char dev_maj[NDEV] = {30, 4, 2}; + +static struct dsk { + unsigned drive; + unsigned type; + unsigned unit; + uint8_t slice; + uint8_t part; + unsigned start; + int init; +} dsk; +static char cmd[512], cmddup[512], knamebuf[1024]; +static const char *kname; +uint32_t opts; +static struct bootinfo bootinfo; +#if SERIAL +static int comspeed = SIOSPD; +static uint8_t ioctrl = IO_KEYBOARD; +#endif + +int main(void); +void exit(int); +static void load(void); +static int parse(void); +static int dskread(void *, unsigned, unsigned); +static void printf(const char *,...); +static void putchar(int); +static int drvread(void *, unsigned, unsigned); +static int keyhit(unsigned); +static int xputc(int); +static int xgetc(int); +static inline int getc(int); + +static void memcpy(void *, const void *, int); +static void +memcpy(void *dst, const void *src, int len) +{ + const char *s = src; + char *d = dst; + + while (len--) + *d++ = *s++; +} + +static inline int +strcmp(const char *s1, const char *s2) +{ + for (; *s1 == *s2 && *s1; s1++, s2++); + return (unsigned char)*s1 - (unsigned char)*s2; +} + +#define UFS_SMALL_CGBASE +#include "ufsread.c" + +static int +xfsread(ufs_ino_t inode, void *buf, size_t nbyte) +{ + if ((size_t)fsread(inode, buf, nbyte) != nbyte) { + printf("Invalid %s\n", "format"); + return -1; + } + return 0; +} + +static inline void +getstr(void) +{ + char *s; + int c; + + s = cmd; + for (;;) { + switch (c = xgetc(0)) { + case 0: + break; + case '\177': + case '\b': + if (s > cmd) { + s--; + printf("\b \b"); + } + break; + case '\n': + case '\r': + *s = 0; + return; + default: + if (s - cmd < sizeof(cmd) - 1) + *s++ = c; + putchar(c); + } + } +} + +static inline void +putc(int c) +{ + v86.addr = 0x10; + v86.eax = 0xe00 | (c & 0xff); + v86.ebx = 0x7; + v86int(); +} + +int +main(void) +{ + uint8_t autoboot; + ufs_ino_t ino; + size_t nbyte; + + dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base); + v86.ctl = V86_FLAGS; + v86.efl = PSL_RESERVED_DEFAULT | PSL_I; + dsk.drive = *(uint8_t *)PTOV(ARGS); + dsk.type = dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD; + dsk.unit = dsk.drive & DRV_MASK; + dsk.slice = *(uint8_t *)PTOV(ARGS + 1) + 1; + bootinfo.bi_version = BOOTINFO_VERSION; + bootinfo.bi_size = sizeof(bootinfo); + + /* Process configuration file */ + + autoboot = 1; + + if ((ino = lookup(PATH_CONFIG)) || + (ino = lookup(PATH_DOTCONFIG))) { + nbyte = fsread(ino, cmd, sizeof(cmd) - 1); + cmd[nbyte] = '\0'; + } + + if (*cmd) { + memcpy(cmddup, cmd, sizeof(cmd)); + if (parse()) + autoboot = 0; + if (!OPT_CHECK(RBX_QUIET)) + printf("%s: %s", PATH_CONFIG, cmddup); + /* Do not process this command twice */ + *cmd = 0; + } + + /* + * Try to exec stage 3 boot loader. If interrupted by a keypress, + * or in case of failure, try to load a kernel directly instead. + */ + + if (!kname) { + kname = PATH_LOADER; + if (autoboot && !keyhit(3*SECOND)) { + load(); + kname = PATH_KERNEL; + } + } + + /* Present the user with the boot2 prompt. */ + + for (;;) { + if (!autoboot || !OPT_CHECK(RBX_QUIET)) + printf("\nFreeBSD/x86 boot\n" + "Default: %u:%s(%u,%c)%s\n" + "boot: ", + dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit, + 'a' + dsk.part, kname); + if (DO_SIO) + sio_flush(); + if (!autoboot || keyhit(3*SECOND)) + getstr(); + else if (!autoboot || !OPT_CHECK(RBX_QUIET)) + putchar('\n'); + autoboot = 0; + if (parse()) + putchar('\a'); + else + load(); + } +} + +/* XXX - Needed for btxld to link the boot2 binary; do not remove. */ +void +exit(int x) +{ +} + +static void +load(void) +{ + union { + struct exec ex; + Elf32_Ehdr eh; + } hdr; + static Elf32_Phdr ep[2]; + static Elf32_Shdr es[2]; + caddr_t p; + ufs_ino_t ino; + uint32_t addr; + int k; + uint8_t i, j; + + if (!(ino = lookup(kname))) { + if (!ls) + printf("No %s\n", kname); + return; + } + if (xfsread(ino, &hdr, sizeof(hdr))) + return; + + if (N_GETMAGIC(hdr.ex) == ZMAGIC) { + addr = hdr.ex.a_entry & 0xffffff; + p = PTOV(addr); + fs_off = PAGE_SIZE; + if (xfsread(ino, p, hdr.ex.a_text)) + return; + p += roundup2(hdr.ex.a_text, PAGE_SIZE); + if (xfsread(ino, p, hdr.ex.a_data)) + return; + } else if (IS_ELF(hdr.eh)) { + fs_off = hdr.eh.e_phoff; + for (j = k = 0; k < hdr.eh.e_phnum && j < 2; k++) { + if (xfsread(ino, ep + j, sizeof(ep[0]))) + return; + if (ep[j].p_type == PT_LOAD) + j++; + } + for (i = 0; i < 2; i++) { + p = PTOV(ep[i].p_paddr & 0xffffff); + fs_off = ep[i].p_offset; + if (xfsread(ino, p, ep[i].p_filesz)) + return; + } + p += roundup2(ep[1].p_memsz, PAGE_SIZE); + bootinfo.bi_symtab = VTOP(p); + if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) { + fs_off = hdr.eh.e_shoff + sizeof(es[0]) * + (hdr.eh.e_shstrndx + 1); + if (xfsread(ino, &es, sizeof(es))) + return; + for (i = 0; i < 2; i++) { + *(Elf32_Word *)p = es[i].sh_size; + p += sizeof(es[i].sh_size); + fs_off = es[i].sh_offset; + if (xfsread(ino, p, es[i].sh_size)) + return; + p += es[i].sh_size; + } + } + addr = hdr.eh.e_entry & 0xffffff; + bootinfo.bi_esymtab = VTOP(p); + } else { + printf("Invalid %s\n", "format"); + return; + } + + bootinfo.bi_kernelname = VTOP(kname); + bootinfo.bi_bios_dev = dsk.drive; + __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK), + MAKEBOOTDEV(dev_maj[dsk.type], dsk.slice, dsk.unit, dsk.part), + 0, 0, 0, VTOP(&bootinfo)); +} + +static int +parse() +{ + char *arg = cmd; + char *ep, *p, *q; + const char *cp; + unsigned int drv; + int c, i, j; + size_t k; + + while ((c = *arg++)) { + if (c == ' ' || c == '\t' || c == '\n') + continue; + for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++); + ep = p; + if (*p) + *p++ = 0; + if (c == '-') { + while ((c = *arg++)) { + if (c == 'P') { + if (*(uint8_t *)PTOV(0x496) & 0x10) { + cp = "yes"; + } else { + opts |= OPT_SET(RBX_DUAL) | OPT_SET(RBX_SERIAL); + cp = "no"; + } + printf("Keyboard: %s\n", cp); + continue; +#if SERIAL + } else if (c == 'S') { + j = 0; + while ((unsigned int)(i = *arg++ - '0') <= 9) + j = j * 10 + i; + if (j > 0 && i == -'0') { + comspeed = j; + break; + } + /* Fall through to error below ('S' not in optstr[]). */ +#endif + } + for (i = 0; c != optstr[i]; i++) + if (i == NOPT - 1) + return -1; + opts ^= OPT_SET(flags[i]); + } +#if SERIAL + ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) : + OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD; + if (DO_SIO) { + if (sio_init(115200 / comspeed) != 0) + ioctrl &= ~IO_SERIAL; + } +#endif + } else { + for (q = arg--; *q && *q != '('; q++); + if (*q) { + drv = -1; + if (arg[1] == ':') { + drv = *arg - '0'; + if (drv > 9) + return (-1); + arg += 2; + } + if (q - arg != 2) + return -1; + for (i = 0; arg[0] != dev_nm[i][0] || + arg[1] != dev_nm[i][1]; i++) + if (i == NDEV - 1) + return -1; + dsk.type = i; + arg += 3; + dsk.unit = *arg - '0'; + if (arg[1] != ',' || dsk.unit > 9) + return -1; + arg += 2; + dsk.slice = WHOLE_DISK_SLICE; + if (arg[1] == ',') { + dsk.slice = *arg - '0' + 1; + if (dsk.slice > NDOSPART + 1) + return -1; + arg += 2; + } + if (arg[1] != ')') + return -1; + dsk.part = *arg - 'a'; + if (dsk.part > 7) + return (-1); + arg += 2; + if (drv == -1) + drv = dsk.unit; + dsk.drive = (dsk.type <= TYPE_MAXHARD + ? DRV_HARD : 0) + drv; + dsk_meta = 0; + } + k = ep - arg; + if (k > 0) { + if (k >= sizeof(knamebuf)) + return -1; + memcpy(knamebuf, arg, k + 1); + kname = knamebuf; + } + } + arg = p; + } + return 0; +} + +static int +dskread(void *buf, unsigned lba, unsigned nblk) +{ + struct dos_partition *dp; + struct disklabel *d; + char *sec; + unsigned i; + uint8_t sl; + const char *reason; + + if (!dsk_meta) { + sec = dmadat->secbuf; + dsk.start = 0; + if (drvread(sec, DOSBBSECTOR, 1)) + return -1; + dp = (void *)(sec + DOSPARTOFF); + sl = dsk.slice; + if (sl < BASE_SLICE) { + for (i = 0; i < NDOSPART; i++) + if (dp[i].dp_typ == DOSPTYP_386BSD && + (dp[i].dp_flag & 0x80 || sl < BASE_SLICE)) { + sl = BASE_SLICE + i; + if (dp[i].dp_flag & 0x80 || + dsk.slice == COMPATIBILITY_SLICE) + break; + } + if (dsk.slice == WHOLE_DISK_SLICE) + dsk.slice = sl; + } + if (sl != WHOLE_DISK_SLICE) { + if (sl != COMPATIBILITY_SLICE) + dp += sl - BASE_SLICE; + if (dp->dp_typ != DOSPTYP_386BSD) { + reason = "slice"; + goto error; + } + dsk.start = dp->dp_start; + } + if (drvread(sec, dsk.start + LABELSECTOR, 1)) + return -1; + d = (void *)(sec + LABELOFFSET); + if (d->d_magic != DISKMAGIC || d->d_magic2 != DISKMAGIC) { + if (dsk.part != RAW_PART) { + reason = "label"; + goto error; + } + } else { + if (!dsk.init) { + if (d->d_type == DTYPE_SCSI) + dsk.type = TYPE_DA; + dsk.init++; + } + if (dsk.part >= d->d_npartitions || + !d->d_partitions[dsk.part].p_size) { + reason = "partition"; + goto error; + } + dsk.start += d->d_partitions[dsk.part].p_offset; + dsk.start -= d->d_partitions[RAW_PART].p_offset; + } + } + return drvread(buf, dsk.start + lba, nblk); +error: + printf("Invalid %s\n", reason); + return -1; +} + +static void +printf(const char *fmt,...) +{ + va_list ap; + static char buf[10]; + char *s; + unsigned u; + int c; + + va_start(ap, fmt); + while ((c = *fmt++)) { + if (c == '%') { + c = *fmt++; + switch (c) { + case 'c': + putchar(va_arg(ap, int)); + continue; + case 's': + for (s = va_arg(ap, char *); *s; s++) + putchar(*s); + continue; + case 'u': + u = va_arg(ap, unsigned); + s = buf; + do + *s++ = '0' + u % 10U; + while (u /= 10U); + while (--s >= buf) + putchar(*s); + continue; + } + } + putchar(c); + } + va_end(ap); + return; +} + +static void +putchar(int c) +{ + if (c == '\n') + xputc('\r'); + xputc(c); +} + +static int +drvread(void *buf, unsigned lba, unsigned nblk) +{ + static unsigned c = 0x2d5c7c2f; + + if (!OPT_CHECK(RBX_QUIET)) { + xputc(c = c << 8 | c >> 24); + xputc('\b'); + } + v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS; + v86.addr = XREADORG; /* call to xread in boot1 */ + v86.es = VTOPSEG(buf); + v86.eax = lba; + v86.ebx = VTOPOFF(buf); + v86.ecx = lba >> 16; + v86.edx = nblk << 8 | dsk.drive; + v86int(); + v86.ctl = V86_FLAGS; + if (V86_CY(v86.efl)) { + printf("error %u lba %u\n", v86.eax >> 8 & 0xff, lba); + return -1; + } + return 0; +} + +static int +keyhit(unsigned ticks) +{ + uint32_t t0, t1; + + if (OPT_CHECK(RBX_NOINTR)) + return 0; + t0 = 0; + for (;;) { + if (xgetc(1)) + return 1; + t1 = *(uint32_t *)PTOV(0x46c); + if (!t0) + t0 = t1; + if ((uint32_t)(t1 - t0) >= ticks) + return 0; + } +} + +static int +xputc(int c) +{ + if (DO_KBD) + putc(c); + if (DO_SIO) + sio_putc(c); + return c; +} + +static int +getc(int fn) +{ + v86.addr = 0x16; + v86.eax = fn << 8; + v86int(); + return fn == 0 ? v86.eax & 0xff : !V86_ZR(v86.efl); +} + +static int +xgetc(int fn) +{ + if (OPT_CHECK(RBX_NOINTR)) + return 0; + for (;;) { + if (DO_KBD && getc(1)) + return fn ? 1 : getc(0); + if (DO_SIO && sio_ischar()) + return fn ? 1 : sio_getc(); + if (fn) + return 0; + } +} diff --git a/stand/i386/boot2/lib.h b/stand/i386/boot2/lib.h new file mode 100644 index 0000000..d8d3317 --- /dev/null +++ b/stand/i386/boot2/lib.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 1998 Robert Nordier + * All rights reserved. + * + * Redistribution and use in source and binary forms are freely + * permitted provided that the above copyright notice and this + * paragraph and the following disclaimer are duplicated in all + * such forms. + * + * This software is provided "AS IS" and without any express or + * implied warranties, including, without limitation, the implied + * warranties of merchantability and fitness for a particular + * purpose. + */ + +/* + * $FreeBSD$ + */ + +int sio_init(int) __attribute__((regparm (3))); +int sio_flush(void); +void sio_putc(int) __attribute__((regparm (3))); +int sio_getc(void); +int sio_ischar(void); diff --git a/stand/i386/boot2/sio.S b/stand/i386/boot2/sio.S new file mode 100644 index 0000000..ca9d0a2 --- /dev/null +++ b/stand/i386/boot2/sio.S @@ -0,0 +1,84 @@ +/* + * Copyright (c) 1998 Robert Nordier + * All rights reserved. + * + * Redistribution and use in source and binary forms are freely + * permitted provided that the above copyright notice and this + * paragraph and the following disclaimer are duplicated in all + * such forms. + * + * This software is provided "AS IS" and without any express or + * implied warranties, including, without limitation, the implied + * warranties of merchantability and fitness for a particular + * purpose. + * + * $FreeBSD$ + */ + + .set SIO_PRT,SIOPRT # Base port + .set SIO_FMT,SIOFMT # 8N1 + + .globl sio_init + .globl sio_flush + .globl sio_putc + .globl sio_getc + .globl sio_ischar + +/* int sio_init(int div) */ + +sio_init: pushl %eax + movw $SIO_PRT+0x3,%dx # Data format reg + movb $SIO_FMT|0x80,%al # Set format + outb %al,(%dx) # and DLAB + subb $0x3,%dl # Divisor latch reg + popl %eax + outw %ax,(%dx) # BPS + movw $SIO_PRT+0x3,%dx # Data format reg + movb $SIO_FMT,%al # Clear + outb %al,(%dx) # DLAB + incl %edx # Modem control reg + movb $0x3,%al # Set RTS, + outb %al,(%dx) # DTR + incl %edx # Line status reg + # Fallthrough + +/* int sio_flush(void) */ + +sio_flush: xorl %ecx,%ecx # Timeout + movb $0x80,%ch # counter +sio_flush.1: call sio_ischar # Check for character + jz sio_flush.2 # Till none + loop sio_flush.1 # or counter is zero + movb $1, %al # Exhausted all tries +sio_flush.2: ret # To caller + +/* void sio_putc(int c) */ + +sio_putc: pushl %eax + movw $SIO_PRT+0x5,%dx # Line status reg + xor %ecx,%ecx # Timeout + movb $0x40,%ch # counter +sio_putc.1: inb (%dx),%al # Transmitter + testb $0x20,%al # buffer empty? + loopz sio_putc.1 # No + jz sio_putc.2 # If timeout + popl %eax # Get the character + subb $0x5,%dl # Transmitter hold reg + outb %al,(%dx) # Write character +sio_putc.2: ret # To caller + +/* int sio_getc(void) */ + +sio_getc: call sio_ischar # Character available? + jz sio_getc # No +sio_getc.1: subb $0x5,%dl # Receiver buffer reg + inb (%dx),%al # Read character + ret # To caller + +/* int sio_ischar(void) */ + +sio_ischar: movw $SIO_PRT+0x5,%dx # Line status register + xorl %eax,%eax # Zero + inb (%dx),%al # Received data + andb $0x1,%al # ready? + ret # To caller diff --git a/stand/i386/btx/Makefile b/stand/i386/btx/Makefile new file mode 100644 index 0000000..39f78ed --- /dev/null +++ b/stand/i386/btx/Makefile @@ -0,0 +1,5 @@ +# $FreeBSD$ + +SUBDIR= btx btxldr lib + +.include <bsd.subdir.mk> diff --git a/stand/i386/btx/Makefile.inc b/stand/i386/btx/Makefile.inc new file mode 100644 index 0000000..265f86d --- /dev/null +++ b/stand/i386/btx/Makefile.inc @@ -0,0 +1,3 @@ +# $FreeBSD$ + +.include "../Makefile.inc" diff --git a/stand/i386/btx/btx/Makefile b/stand/i386/btx/btx/Makefile new file mode 100644 index 0000000..f42e20d --- /dev/null +++ b/stand/i386/btx/btx/Makefile @@ -0,0 +1,35 @@ +# $FreeBSD$ + +.include <bsd.init.mk> + +PROG= btx +INTERNALPROG= +MAN= +SRCS= btx.S + +.if defined(BOOT_BTX_NOHANG) +BOOT_BTX_FLAGS=0x1 +.else +BOOT_BTX_FLAGS=0x0 +.endif + +CFLAGS+=-DBTX_FLAGS=${BOOT_BTX_FLAGS} +CFLAGS+=-I${BOOTSRC}/i386/common + +.if defined(BTX_SERIAL) +BOOT_COMCONSOLE_PORT?= 0x3f8 +BOOT_COMCONSOLE_SPEED?= 9600 +B2SIOFMT?= 0x3 + +CFLAGS+=-DBTX_SERIAL -DSIOPRT=${BOOT_COMCONSOLE_PORT} \ + -DSIOFMT=${B2SIOFMT} -DSIOSPD=${BOOT_COMCONSOLE_SPEED} +.endif + +ORG= 0x9000 + +LDFLAGS+=${LDFLAGS_BIN} + +.include <bsd.prog.mk> + +# XXX: clang integrated-as doesn't grok .codeNN directives yet +CFLAGS.btx.S= ${CLANG_NO_IAS} diff --git a/stand/i386/btx/btx/Makefile.depend b/stand/i386/btx/btx/Makefile.depend new file mode 100644 index 0000000..f80275d --- /dev/null +++ b/stand/i386/btx/btx/Makefile.depend @@ -0,0 +1,11 @@ +# $FreeBSD$ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/stand/i386/btx/btx/btx.S b/stand/i386/btx/btx/btx.S new file mode 100644 index 0000000..87d09a5 --- /dev/null +++ b/stand/i386/btx/btx/btx.S @@ -0,0 +1,1082 @@ +/* + * Copyright (c) 1998 Robert Nordier + * All rights reserved. + * + * Redistribution and use in source and binary forms are freely + * permitted provided that the above copyright notice and this + * paragraph and the following disclaimer are duplicated in all + * such forms. + * + * This software is provided "AS IS" and without any express or + * implied warranties, including, without limitation, the implied + * warranties of merchantability and fitness for a particular + * purpose. + * + * $FreeBSD$ + */ + +#include <bootargs.h> + +/* + * Memory layout. + */ + .set MEM_BTX,0x1000 # Start of BTX memory + .set MEM_ESP0,0x1800 # Supervisor stack + .set MEM_BUF,0x1800 # Scratch buffer + .set MEM_ESPR,0x5e00 # Real mode stack + .set MEM_IDT,0x5e00 # IDT + .set MEM_TSS,0x5f98 # TSS + .set MEM_MAP,0x6000 # I/O bit map + .set MEM_TSS_END,0x7fff # End of TSS + .set MEM_ORG,0x9000 # BTX code + .set MEM_USR,0xa000 # Start of user memory +/* + * Paging control. + */ + .set PAG_SIZ,0x1000 # Page size + .set PAG_CNT,0x1000 # Pages to map +/* + * Fields in %eflags. + */ + .set PSL_RESERVED_DEFAULT,0x00000002 + .set PSL_T,0x00000100 # Trap flag + .set PSL_I,0x00000200 # Interrupt enable flag + .set PSL_D,0x00000400 # String instruction direction + .set PSL_NT,0x00004000 # Nested task flag + .set PSL_VM,0x00020000 # Virtual 8086 mode flag + .set PSL_AC,0x00040000 # Alignment check flag +/* + * Segment selectors. + */ + .set SEL_SCODE,0x8 # Supervisor code + .set SEL_SDATA,0x10 # Supervisor data + .set SEL_RCODE,0x18 # Real mode code + .set SEL_RDATA,0x20 # Real mode data + .set SEL_UCODE,0x28|3 # User code + .set SEL_UDATA,0x30|3 # User data + .set SEL_TSS,0x38 # TSS +/* + * Task state segment fields. + */ + .set TSS_ESP0,0x4 # PL 0 ESP + .set TSS_SS0,0x8 # PL 0 SS + .set TSS_MAP,0x66 # I/O bit map base +/* + * System calls. + */ + .set SYS_EXIT,0x0 # Exit + .set SYS_EXEC,0x1 # Exec +/* + * Fields in V86 interface structure. + */ + .set V86_CTL,0x0 # Control flags + .set V86_ADDR,0x4 # Int number/address + .set V86_ES,0x8 # V86 ES + .set V86_DS,0xc # V86 DS + .set V86_FS,0x10 # V86 FS + .set V86_GS,0x14 # V86 GS +/* + * V86 control flags. + */ + .set V86F_ADDR,0x10000 # Segment:offset address + .set V86F_CALLF,0x20000 # Emulate far call + .set V86F_FLAGS,0x40000 # Return flags +/* + * Dump format control bytes. + */ + .set DMP_X16,0x1 # Word + .set DMP_X32,0x2 # Long + .set DMP_MEM,0x4 # Memory + .set DMP_EOL,0x8 # End of line +/* + * Screen defaults and assumptions. + */ + .set SCR_MAT,0x7 # Mode/attribute + .set SCR_COL,0x50 # Columns per row + .set SCR_ROW,0x19 # Rows per screen +/* + * BIOS Data Area locations. + */ + .set BDA_MEM,0x413 # Free memory + .set BDA_SCR,0x449 # Video mode + .set BDA_POS,0x450 # Cursor position + .set BDA_BOOT,0x472 # Boot howto flag +/* + * Derivations, for brevity. + */ + .set _ESP0H,MEM_ESP0>>0x8 # Byte 1 of ESP0 + .set _TSSIO,MEM_MAP-MEM_TSS # TSS I/O base + .set _TSSLM,MEM_TSS_END-MEM_TSS # TSS limit + .set _IDTLM,MEM_TSS-MEM_IDT-1 # IDT limit +/* + * Code segment. + */ + .globl start + .code16 +start: # Start of code +/* + * BTX header. + */ +btx_hdr: .byte 0xeb # Machine ID + .byte 0xe # Header size + .ascii "BTX" # Magic + .byte 0x1 # Major version + .byte 0x2 # Minor version + .byte BTX_FLAGS # Flags + .word PAG_CNT-MEM_ORG>>0xc # Paging control + .word break-start # Text size + .long 0x0 # Entry address +/* + * Initialization routine. + */ +init: cli # Disable interrupts + xor %ax,%ax # Zero/segment + mov %ax,%ss # Set up + mov $MEM_ESP0,%sp # stack + mov %ax,%es # Address + mov %ax,%ds # data + pushl $0x2 # Clear + popfl # flags +/* + * Initialize memory. + */ + mov $MEM_IDT,%di # Memory to initialize + mov $(MEM_ORG-MEM_IDT)/2,%cx # Words to zero + rep # Zero-fill + stosw # memory +/* + * Update real mode IDT for reflecting hardware interrupts. + */ + mov $intr20,%bx # Address first handler + mov $0x10,%cx # Number of handlers + mov $0x20*4,%di # First real mode IDT entry +init.0: mov %bx,(%di) # Store IP + inc %di # Address next + inc %di # entry + stosw # Store CS + add $4,%bx # Next handler + loop init.0 # Next IRQ +/* + * Create IDT. + */ + mov $MEM_IDT,%di + mov $idtctl,%si # Control string +init.1: lodsb # Get entry + cbw # count + xchg %ax,%cx # as word + jcxz init.4 # If done + lodsb # Get segment + xchg %ax,%dx # P:DPL:type + lodsw # Get control + xchg %ax,%bx # set + lodsw # Get handler offset + mov $SEL_SCODE,%dh # Segment selector +init.2: shr %bx # Handle this int? + jnc init.3 # No + mov %ax,(%di) # Set handler offset + mov %dh,0x2(%di) # and selector + mov %dl,0x5(%di) # Set P:DPL:type + add $0x4,%ax # Next handler +init.3: lea 0x8(%di),%di # Next entry + loop init.2 # Till set done + jmp init.1 # Continue +/* + * Initialize TSS. + */ +init.4: movb $_ESP0H,TSS_ESP0+1(%di) # Set ESP0 + movb $SEL_SDATA,TSS_SS0(%di) # Set SS0 + movb $_TSSIO,TSS_MAP(%di) # Set I/O bit map base +/* + * Bring up the system. + */ + mov $0x2820,%bx # Set protected mode + callw setpic # IRQ offsets + lidt idtdesc # Set IDT + lgdt gdtdesc # Set GDT + mov %cr0,%eax # Switch to protected + inc %ax # mode + mov %eax,%cr0 # + ljmp $SEL_SCODE,$init.8 # To 32-bit code + .code32 +init.8: xorl %ecx,%ecx # Zero + movb $SEL_SDATA,%cl # To 32-bit + movw %cx,%ss # stack +/* + * Launch user task. + */ + movb $SEL_TSS,%cl # Set task + ltr %cx # register + movl $MEM_USR,%edx # User base address + movzwl %ss:BDA_MEM,%eax # Get free memory + shll $0xa,%eax # To bytes + subl $ARGSPACE,%eax # Less arg space + subl %edx,%eax # Less base + movb $SEL_UDATA,%cl # User data selector + pushl %ecx # Set SS + pushl %eax # Set ESP + push $0x202 # Set flags (IF set) + push $SEL_UCODE # Set CS + pushl btx_hdr+0xc # Set EIP + pushl %ecx # Set GS + pushl %ecx # Set FS + pushl %ecx # Set DS + pushl %ecx # Set ES + pushl %edx # Set EAX + movb $0x7,%cl # Set remaining +init.9: push $0x0 # general + loop init.9 # registers +#ifdef BTX_SERIAL + call sio_init # setup the serial console +#endif + popa # and initialize + popl %es # Initialize + popl %ds # user + popl %fs # segment + popl %gs # registers + iret # To user mode +/* + * Exit routine. + */ +exit: cli # Disable interrupts + movl $MEM_ESP0,%esp # Clear stack +/* + * Turn off paging. + */ + movl %cr0,%eax # Get CR0 + andl $~0x80000000,%eax # Disable + movl %eax,%cr0 # paging + xorl %ecx,%ecx # Zero + movl %ecx,%cr3 # Flush TLB +/* + * Restore the GDT in case we caught a kernel trap. + */ + lgdt %cs:gdtdesc # Set GDT +/* + * To 16 bits. + */ + ljmpw $SEL_RCODE,$exit.1 # Reload CS + .code16 +exit.1: mov $SEL_RDATA,%cl # 16-bit selector + mov %cx,%ss # Reload SS + mov %cx,%ds # Load + mov %cx,%es # remaining + mov %cx,%fs # segment + mov %cx,%gs # registers +/* + * To real-address mode. + */ + dec %ax # Switch to + mov %eax,%cr0 # real mode + ljmp $0x0,$exit.2 # Reload CS +exit.2: xor %ax,%ax # Real mode segment + mov %ax,%ss # Reload SS + mov %ax,%ds # Address data + mov $0x7008,%bx # Set real mode + callw setpic # IRQ offsets + lidt ivtdesc # Set IVT +/* + * Reboot or await reset. + */ + sti # Enable interrupts + testb $0x1,btx_hdr+0x7 # Reboot? +exit.3: jz exit.3 # No + movw $0x1234, BDA_BOOT # Do a warm boot + ljmp $0xf000,$0xfff0 # reboot the machine +/* + * Set IRQ offsets by reprogramming 8259A PICs. + */ +setpic: in $0x21,%al # Save master + push %ax # IMR + in $0xa1,%al # Save slave + push %ax # IMR + movb $0x11,%al # ICW1 to + outb %al,$0x20 # master, + outb %al,$0xa0 # slave + movb %bl,%al # ICW2 to + outb %al,$0x21 # master + movb %bh,%al # ICW2 to + outb %al,$0xa1 # slave + movb $0x4,%al # ICW3 to + outb %al,$0x21 # master + movb $0x2,%al # ICW3 to + outb %al,$0xa1 # slave + movb $0x1,%al # ICW4 to + outb %al,$0x21 # master, + outb %al,$0xa1 # slave + pop %ax # Restore slave + outb %al,$0xa1 # IMR + pop %ax # Restore master + outb %al,$0x21 # IMR + retw # To caller + .code32 +/* + * Exception jump table. + */ +intx00: push $0x0 # Int 0x0: #DE + jmp ex_noc # Divide error + push $0x1 # Int 0x1: #DB + jmp ex_noc # Debug + push $0x3 # Int 0x3: #BP + jmp ex_noc # Breakpoint + push $0x4 # Int 0x4: #OF + jmp ex_noc # Overflow + push $0x5 # Int 0x5: #BR + jmp ex_noc # BOUND range exceeded + push $0x6 # Int 0x6: #UD + jmp ex_noc # Invalid opcode + push $0x7 # Int 0x7: #NM + jmp ex_noc # Device not available + push $0x8 # Int 0x8: #DF + jmp except # Double fault + push $0xa # Int 0xa: #TS + jmp except # Invalid TSS + push $0xb # Int 0xb: #NP + jmp except # Segment not present + push $0xc # Int 0xc: #SS + jmp except # Stack segment fault + push $0xd # Int 0xd: #GP + jmp except # General protection + push $0xe # Int 0xe: #PF + jmp except # Page fault +intx10: push $0x10 # Int 0x10: #MF + jmp ex_noc # Floating-point error +/* + * Save a zero error code. + */ +ex_noc: pushl (%esp,1) # Duplicate int no + movb $0x0,0x4(%esp,1) # Fake error code +/* + * Handle exception. + */ +except: cld # String ops inc + pushl %ds # Save + pushl %es # most + pusha # registers + pushl %gs # Set GS + pushl %fs # Set FS + pushl %ds # Set DS + pushl %es # Set ES + cmpw $SEL_SCODE,0x44(%esp,1) # Supervisor mode? + jne except.1 # No + pushl %ss # Set SS + jmp except.2 # Join common code +except.1: pushl 0x50(%esp,1) # Set SS +except.2: pushl 0x50(%esp,1) # Set ESP + push $SEL_SDATA # Set up + popl %ds # to + pushl %ds # address + popl %es # data + movl %esp,%ebx # Stack frame + movl $dmpfmt,%esi # Dump format string + movl $MEM_BUF,%edi # Buffer + pushl %edi # Dump to + call dump # buffer + popl %esi # and + call putstr # display + leal 0x18(%esp,1),%esp # Discard frame + popa # Restore + popl %es # registers + popl %ds # saved + cmpb $0x3,(%esp,1) # Breakpoint? + je except.3 # Yes + cmpb $0x1,(%esp,1) # Debug? + jne except.2a # No + testl $PSL_T,0x10(%esp,1) # Trap flag set? + jnz except.3 # Yes +except.2a: jmp exit # Exit +except.3: leal 0x8(%esp,1),%esp # Discard err, int no + iret # From interrupt + +/* + * Reboot the machine by setting the reboot flag and exiting + */ +reboot: orb $0x1,btx_hdr+0x7 # Set the reboot flag + jmp exit # Terminate BTX and reboot + +/* + * Protected Mode Hardware interrupt jump table. + */ +intx20: push $0x8 # Int 0x20: IRQ0 + jmp int_hw # V86 int 0x8 + push $0x9 # Int 0x21: IRQ1 + jmp int_hw # V86 int 0x9 + push $0xa # Int 0x22: IRQ2 + jmp int_hw # V86 int 0xa + push $0xb # Int 0x23: IRQ3 + jmp int_hw # V86 int 0xb + push $0xc # Int 0x24: IRQ4 + jmp int_hw # V86 int 0xc + push $0xd # Int 0x25: IRQ5 + jmp int_hw # V86 int 0xd + push $0xe # Int 0x26: IRQ6 + jmp int_hw # V86 int 0xe + push $0xf # Int 0x27: IRQ7 + jmp int_hw # V86 int 0xf + push $0x70 # Int 0x28: IRQ8 + jmp int_hw # V86 int 0x70 + push $0x71 # Int 0x29: IRQ9 + jmp int_hw # V86 int 0x71 + push $0x72 # Int 0x2a: IRQ10 + jmp int_hw # V86 int 0x72 + push $0x73 # Int 0x2b: IRQ11 + jmp int_hw # V86 int 0x73 + push $0x74 # Int 0x2c: IRQ12 + jmp int_hw # V86 int 0x74 + push $0x75 # Int 0x2d: IRQ13 + jmp int_hw # V86 int 0x75 + push $0x76 # Int 0x2e: IRQ14 + jmp int_hw # V86 int 0x76 + push $0x77 # Int 0x2f: IRQ15 + jmp int_hw # V86 int 0x77 + +/* + * Invoke real mode interrupt/function call from user mode with arguments. + */ +intx31: pushl $-1 # Dummy int no for btx_v86 +/* + * Invoke real mode interrupt/function call from protected mode. + * + * We place a trampoline on the user stack that will return to rret_tramp + * which will reenter protected mode and then finally return to the user + * client. + * + * Kernel frame %esi points to: Real mode stack frame at MEM_ESPR: + * + * -0x00 user %ss -0x04 kernel %esp (with full frame) + * -0x04 user %esp -0x08 btx_v86 pointer + * -0x08 user %eflags -0x0c flags (only used if interrupt) + * -0x0c user %cs -0x10 real mode CS:IP return trampoline + * -0x10 user %eip -0x12 real mode flags + * -0x14 int no -0x16 real mode CS:IP (target) + * -0x18 %eax + * -0x1c %ecx + * -0x20 %edx + * -0x24 %ebx + * -0x28 %esp + * -0x2c %ebp + * -0x30 %esi + * -0x34 %edi + * -0x38 %gs + * -0x3c %fs + * -0x40 %ds + * -0x44 %es + * -0x48 zero %eax (hardware int only) + * -0x4c zero %ecx (hardware int only) + * -0x50 zero %edx (hardware int only) + * -0x54 zero %ebx (hardware int only) + * -0x58 zero %esp (hardware int only) + * -0x5c zero %ebp (hardware int only) + * -0x60 zero %esi (hardware int only) + * -0x64 zero %edi (hardware int only) + * -0x68 zero %gs (hardware int only) + * -0x6c zero %fs (hardware int only) + * -0x70 zero %ds (hardware int only) + * -0x74 zero %es (hardware int only) + */ +int_hw: cld # String ops inc + pusha # Save gp regs + pushl %gs # Save + pushl %fs # seg + pushl %ds # regs + pushl %es + push $SEL_SDATA # Set up + popl %ds # to + pushl %ds # address + popl %es # data + leal 0x44(%esp,1),%esi # Base of frame + movl %esp,MEM_ESPR-0x04 # Save kernel stack pointer + movl -0x14(%esi),%eax # Get Int no + cmpl $-1,%eax # Hardware interrupt? + jne intusr.1 # Yes +/* + * v86 calls save the btx_v86 pointer on the real mode stack and read + * the address and flags from the btx_v86 structure. For interrupt + * handler invocations (VM86 INTx requests), disable interrupts, + * tracing, and alignment checking while the handler runs. + */ + movl $MEM_USR,%ebx # User base + movl %ebx,%edx # address + addl -0x4(%esi),%ebx # User ESP + movl (%ebx),%ebp # btx_v86 pointer + addl %ebp,%edx # Flatten btx_v86 ptr + movl %edx,MEM_ESPR-0x08 # Save btx_v86 ptr + movl V86_ADDR(%edx),%eax # Get int no/address + movl V86_CTL(%edx),%edx # Get control flags + movl -0x08(%esi),%ebx # Save user flags in %ebx + testl $V86F_ADDR,%edx # Segment:offset? + jnz intusr.4 # Yes + andl $~(PSL_I|PSL_T|PSL_AC),%ebx # Disable interrupts, tracing, + # and alignment checking for + # interrupt handler + jmp intusr.3 # Skip hardware interrupt +/* + * Hardware interrupts store a NULL btx_v86 pointer and use the + * address (interrupt number) from the stack with empty flags. Also, + * push a dummy frame of zeros onto the stack for all the general + * purpose and segment registers and clear %eflags. This gives the + * hardware interrupt handler a clean slate. + */ +intusr.1: xorl %edx,%edx # Control flags + movl %edx,MEM_ESPR-0x08 # NULL btx_v86 ptr + movl $12,%ecx # Frame is 12 dwords +intusr.2: pushl $0x0 # Fill frame + loop intusr.2 # with zeros + movl $PSL_RESERVED_DEFAULT,%ebx # Set clean %eflags +/* + * Look up real mode IDT entry for hardware interrupts and VM86 INTx + * requests. + */ +intusr.3: shll $0x2,%eax # Scale + movl (%eax),%eax # Load int vector + jmp intusr.5 # Skip CALLF test +/* + * Panic if V86F_CALLF isn't set with V86F_ADDR. + */ +intusr.4: testl $V86F_CALLF,%edx # Far call? + jnz intusr.5 # Ok + movl %edx,0x30(%esp,1) # Place VM86 flags in int no + movl $badvm86,%esi # Display bad + call putstr # VM86 call + popl %es # Restore + popl %ds # seg + popl %fs # regs + popl %gs + popal # Restore gp regs + jmp ex_noc # Panic +/* + * %eax now holds the segment:offset of the function. + * %ebx now holds the %eflags to pass to real mode. + * %edx now holds the V86F_* flags. + */ +intusr.5: movw %bx,MEM_ESPR-0x12 # Pass user flags to real mode + # target +/* + * If this is a v86 call, copy the seg regs out of the btx_v86 structure. + */ + movl MEM_ESPR-0x08,%ecx # Get btx_v86 ptr + jecxz intusr.6 # Skip for hardware ints + leal -0x44(%esi),%edi # %edi => kernel stack seg regs + pushl %esi # Save + leal V86_ES(%ecx),%esi # %esi => btx_v86 seg regs + movl $4,%ecx # Copy seg regs + rep # from btx_v86 + movsl # to kernel stack + popl %esi # Restore +intusr.6: movl -0x08(%esi),%ebx # Copy user flags to real + movl %ebx,MEM_ESPR-0x0c # mode return trampoline + movl $rret_tramp,%ebx # Set return trampoline + movl %ebx,MEM_ESPR-0x10 # CS:IP + movl %eax,MEM_ESPR-0x16 # Real mode target CS:IP + ljmpw $SEL_RCODE,$intusr.7 # Change to 16-bit segment + .code16 +intusr.7: movl %cr0,%eax # Leave + dec %al # protected + movl %eax,%cr0 # mode + ljmpw $0x0,$intusr.8 +intusr.8: xorw %ax,%ax # Reset %ds + movw %ax,%ds # and + movw %ax,%ss # %ss + lidt ivtdesc # Set IVT + popl %es # Restore + popl %ds # seg + popl %fs # regs + popl %gs + popal # Restore gp regs + movw $MEM_ESPR-0x16,%sp # Switch to real mode stack + iret # Call target routine +/* + * For the return to real mode we setup a stack frame like this on the real + * mode stack. Note that callf calls won't pop off the flags, but we just + * ignore that by repositioning %sp to be just above the btx_v86 pointer + * so it is aligned. The stack is relative to MEM_ESPR. + * + * -0x04 kernel %esp + * -0x08 btx_v86 + * -0x0c %eax + * -0x10 %ecx + * -0x14 %edx + * -0x18 %ebx + * -0x1c %esp + * -0x20 %ebp + * -0x24 %esi + * -0x28 %edi + * -0x2c %gs + * -0x30 %fs + * -0x34 %ds + * -0x38 %es + * -0x3c %eflags + */ +rret_tramp: movw $MEM_ESPR-0x08,%sp # Reset stack pointer + pushal # Save gp regs + pushl %gs # Save + pushl %fs # seg + pushl %ds # regs + pushl %es + pushfl # Save %eflags + pushl $PSL_RESERVED_DEFAULT|PSL_D # Use clean %eflags with + popfl # string ops dec + xorw %ax,%ax # Reset seg + movw %ax,%ds # regs + movw %ax,%es # (%ss is already 0) + lidt idtdesc # Set IDT + lgdt gdtdesc # Set GDT + mov %cr0,%eax # Switch to protected + inc %ax # mode + mov %eax,%cr0 # + ljmp $SEL_SCODE,$rret_tramp.1 # To 32-bit code + .code32 +rret_tramp.1: xorl %ecx,%ecx # Zero + movb $SEL_SDATA,%cl # Setup + movw %cx,%ss # 32-bit + movw %cx,%ds # seg + movw %cx,%es # regs + movl MEM_ESPR-0x04,%esp # Switch to kernel stack + leal 0x44(%esp,1),%esi # Base of frame + andb $~0x2,tss_desc+0x5 # Clear TSS busy + movb $SEL_TSS,%cl # Set task + ltr %cx # register +/* + * Now we are back in protected mode. The kernel stack frame set up + * before entering real mode is still intact. For hardware interrupts, + * leave the frame unchanged. + */ + cmpl $0,MEM_ESPR-0x08 # Leave saved regs unchanged + jz rret_tramp.3 # for hardware ints +/* + * For V86 calls, copy the registers off of the real mode stack onto + * the kernel stack as we want their updated values. Also, initialize + * the segment registers on the kernel stack. + * + * Note that the %esp in the kernel stack after this is garbage, but popa + * ignores it, so we don't have to fix it up. + */ + leal -0x18(%esi),%edi # Kernel stack GP regs + pushl %esi # Save + movl $MEM_ESPR-0x0c,%esi # Real mode stack GP regs + movl $8,%ecx # Copy GP regs from + rep # real mode stack + movsl # to kernel stack + movl $SEL_UDATA,%eax # Selector for data seg regs + movl $4,%ecx # Initialize %ds, + rep # %es, %fs, and + stosl # %gs +/* + * For V86 calls, copy the saved seg regs on the real mode stack back + * over to the btx_v86 structure. Also, conditionally update the + * saved eflags on the kernel stack based on the flags from the user. + */ + movl MEM_ESPR-0x08,%ecx # Get btx_v86 ptr + leal V86_GS(%ecx),%edi # %edi => btx_v86 seg regs + leal MEM_ESPR-0x2c,%esi # %esi => real mode seg regs + xchgl %ecx,%edx # Save btx_v86 ptr + movl $4,%ecx # Copy seg regs + rep # from real mode stack + movsl # to btx_v86 + popl %esi # Restore + movl V86_CTL(%edx),%edx # Read V86 control flags + testl $V86F_FLAGS,%edx # User wants flags? + jz rret_tramp.3 # No + movl MEM_ESPR-0x3c,%eax # Read real mode flags + andl $~(PSL_T|PSL_NT),%eax # Clear unsafe flags + movw %ax,-0x08(%esi) # Update user flags (low 16) +/* + * Return to the user task + */ +rret_tramp.3: popl %es # Restore + popl %ds # seg + popl %fs # regs + popl %gs + popal # Restore gp regs + addl $4,%esp # Discard int no + iret # Return to user mode + +/* + * System Call. + */ +intx30: cmpl $SYS_EXEC,%eax # Exec system call? + jne intx30.1 # No + pushl %ss # Set up + popl %es # all + pushl %es # segment + popl %ds # registers + pushl %ds # for the + popl %fs # program + pushl %fs # we're + popl %gs # invoking + movl $MEM_USR,%eax # User base address + addl 0xc(%esp,1),%eax # Change to user + leal 0x4(%eax),%esp # stack + popl %eax # Call + call *%eax # program +intx30.1: orb $0x1,%ss:btx_hdr+0x7 # Flag reboot + jmp exit # Exit +/* + * Dump structure [EBX] to [EDI], using format string [ESI]. + */ +dump.0: stosb # Save char +dump: lodsb # Load char + testb %al,%al # End of string? + jz dump.10 # Yes + testb $0x80,%al # Control? + jz dump.0 # No + movb %al,%ch # Save control + movb $'=',%al # Append + stosb # '=' + lodsb # Get offset + pushl %esi # Save + movsbl %al,%esi # To + addl %ebx,%esi # pointer + testb $DMP_X16,%ch # Dump word? + jz dump.1 # No + lodsw # Get and + call hex16 # dump it +dump.1: testb $DMP_X32,%ch # Dump long? + jz dump.2 # No + lodsl # Get and + call hex32 # dump it +dump.2: testb $DMP_MEM,%ch # Dump memory? + jz dump.8 # No + pushl %ds # Save + testl $PSL_VM,0x50(%ebx) # V86 mode? + jnz dump.3 # Yes + verr 0x4(%esi) # Readable selector? + jnz dump.3 # No + ldsl (%esi),%esi # Load pointer + jmp dump.4 # Join common code +dump.3: lodsl # Set offset + xchgl %eax,%edx # Save + lodsl # Get segment + shll $0x4,%eax # * 0x10 + addl %edx,%eax # + offset + xchgl %eax,%esi # Set pointer +dump.4: movb $2,%dl # Num lines +dump.4a: movb $0x10,%cl # Bytes to dump +dump.5: lodsb # Get byte and + call hex8 # dump it + decb %cl # Keep count + jz dump.6a # If done + movb $'-',%al # Separator + cmpb $0x8,%cl # Half way? + je dump.6 # Yes + movb $' ',%al # Use space +dump.6: stosb # Save separator + jmp dump.5 # Continue +dump.6a: decb %dl # Keep count + jz dump.7 # If done + movb $0xa,%al # Line feed + stosb # Save one + movb $7,%cl # Leading + movb $' ',%al # spaces +dump.6b: stosb # Dump + decb %cl # spaces + jnz dump.6b + jmp dump.4a # Next line +dump.7: popl %ds # Restore +dump.8: popl %esi # Restore + movb $0xa,%al # Line feed + testb $DMP_EOL,%ch # End of line? + jnz dump.9 # Yes + movb $' ',%al # Use spaces + stosb # Save one +dump.9: jmp dump.0 # Continue +dump.10: stosb # Terminate string + ret # To caller +/* + * Convert EAX, AX, or AL to hex, saving the result to [EDI]. + */ +hex32: pushl %eax # Save + shrl $0x10,%eax # Do upper + call hex16 # 16 + popl %eax # Restore +hex16: call hex16.1 # Do upper 8 +hex16.1: xchgb %ah,%al # Save/restore +hex8: pushl %eax # Save + shrb $0x4,%al # Do upper + call hex8.1 # 4 + popl %eax # Restore +hex8.1: andb $0xf,%al # Get lower 4 + cmpb $0xa,%al # Convert + sbbb $0x69,%al # to hex + das # digit + orb $0x20,%al # To lower case + stosb # Save char + ret # (Recursive) +/* + * Output zero-terminated string [ESI] to the console. + */ +putstr.0: call putchr # Output char +putstr: lodsb # Load char + testb %al,%al # End of string? + jnz putstr.0 # No + ret # To caller +#ifdef BTX_SERIAL + .set SIO_PRT,SIOPRT # Base port + .set SIO_FMT,SIOFMT # 8N1 + .set SIO_DIV,(115200/SIOSPD) # 115200 / SPD + +/* + * int sio_init(void) + */ +sio_init: movw $SIO_PRT+0x3,%dx # Data format reg + movb $SIO_FMT|0x80,%al # Set format + outb %al,(%dx) # and DLAB + pushl %edx # Save + subb $0x3,%dl # Divisor latch reg + movw $SIO_DIV,%ax # Set + outw %ax,(%dx) # BPS + popl %edx # Restore + movb $SIO_FMT,%al # Clear + outb %al,(%dx) # DLAB + incl %edx # Modem control reg + movb $0x3,%al # Set RTS, + outb %al,(%dx) # DTR + incl %edx # Line status reg + call sio_getc.1 # Get character + +/* + * int sio_flush(void) + */ +sio_flush: xorl %eax,%eax # Return value + xorl %ecx,%ecx # Timeout + movb $0x80,%ch # counter +sio_flush.1: call sio_ischar # Check for character + jz sio_flush.2 # Till none + loop sio_flush.1 # or counter is zero + movb $1, %al # Exhausted all tries +sio_flush.2: ret # To caller + +/* + * void sio_putc(int c) + */ +sio_putc: movw $SIO_PRT+0x5,%dx # Line status reg + xor %ecx,%ecx # Timeout + movb $0x40,%ch # counter +sio_putc.1: inb (%dx),%al # Transmitter + testb $0x20,%al # buffer empty? + loopz sio_putc.1 # No + jz sio_putc.2 # If timeout + movb 0x4(%esp,1),%al # Get character + subb $0x5,%dl # Transmitter hold reg + outb %al,(%dx) # Write character +sio_putc.2: ret $0x4 # To caller + +/* + * int sio_getc(void) + */ +sio_getc: call sio_ischar # Character available? + jz sio_getc # No +sio_getc.1: subb $0x5,%dl # Receiver buffer reg + inb (%dx),%al # Read character + ret # To caller + +/* + * int sio_ischar(void) + */ +sio_ischar: movw $SIO_PRT+0x5,%dx # Line status register + xorl %eax,%eax # Zero + inb (%dx),%al # Received data + andb $0x1,%al # ready? + ret # To caller + +/* + * Output character AL to the serial console. + */ +putchr: pusha # Save + cmpb $10, %al # is it a newline? + jne putchr.1 # no?, then leave + push $13 # output a carriage + call sio_putc # return first + movb $10, %al # restore %al +putchr.1: pushl %eax # Push the character + # onto the stack + call sio_putc # Output the character + popa # Restore + ret # To caller +#else +/* + * Output character AL to the console. + */ +putchr: pusha # Save + xorl %ecx,%ecx # Zero for loops + movb $SCR_MAT,%ah # Mode/attribute + movl $BDA_POS,%ebx # BDA pointer + movw (%ebx),%dx # Cursor position + movl $0xb8000,%edi # Regen buffer (color) + cmpb %ah,BDA_SCR-BDA_POS(%ebx) # Mono mode? + jne putchr.1 # No + xorw %di,%di # Regen buffer (mono) +putchr.1: cmpb $0xa,%al # New line? + je putchr.2 # Yes + xchgl %eax,%ecx # Save char + movb $SCR_COL,%al # Columns per row + mulb %dh # * row position + addb %dl,%al # + column + adcb $0x0,%ah # position + shll %eax # * 2 + xchgl %eax,%ecx # Swap char, offset + movw %ax,(%edi,%ecx,1) # Write attr:char + incl %edx # Bump cursor + cmpb $SCR_COL,%dl # Beyond row? + jb putchr.3 # No +putchr.2: xorb %dl,%dl # Zero column + incb %dh # Bump row +putchr.3: cmpb $SCR_ROW,%dh # Beyond screen? + jb putchr.4 # No + leal 2*SCR_COL(%edi),%esi # New top line + movw $(SCR_ROW-1)*SCR_COL/2,%cx # Words to move + rep # Scroll + movsl # screen + movb $0x20,%al # Space + movb $SCR_COL,%cl # Columns to clear + rep # Clear + stosw # line + movb $SCR_ROW-1,%dh # Bottom line +putchr.4: movw %dx,(%ebx) # Update position + popa # Restore + ret # To caller +#endif + + .code16 +/* + * Real Mode Hardware interrupt jump table. + */ +intr20: push $0x8 # Int 0x20: IRQ0 + jmp int_hwr # V86 int 0x8 + push $0x9 # Int 0x21: IRQ1 + jmp int_hwr # V86 int 0x9 + push $0xa # Int 0x22: IRQ2 + jmp int_hwr # V86 int 0xa + push $0xb # Int 0x23: IRQ3 + jmp int_hwr # V86 int 0xb + push $0xc # Int 0x24: IRQ4 + jmp int_hwr # V86 int 0xc + push $0xd # Int 0x25: IRQ5 + jmp int_hwr # V86 int 0xd + push $0xe # Int 0x26: IRQ6 + jmp int_hwr # V86 int 0xe + push $0xf # Int 0x27: IRQ7 + jmp int_hwr # V86 int 0xf + push $0x70 # Int 0x28: IRQ8 + jmp int_hwr # V86 int 0x70 + push $0x71 # Int 0x29: IRQ9 + jmp int_hwr # V86 int 0x71 + push $0x72 # Int 0x2a: IRQ10 + jmp int_hwr # V86 int 0x72 + push $0x73 # Int 0x2b: IRQ11 + jmp int_hwr # V86 int 0x73 + push $0x74 # Int 0x2c: IRQ12 + jmp int_hwr # V86 int 0x74 + push $0x75 # Int 0x2d: IRQ13 + jmp int_hwr # V86 int 0x75 + push $0x76 # Int 0x2e: IRQ14 + jmp int_hwr # V86 int 0x76 + push $0x77 # Int 0x2f: IRQ15 + jmp int_hwr # V86 int 0x77 +/* + * Reflect hardware interrupts in real mode. + */ +int_hwr: push %ax # Save + push %ds # Save + push %bp # Save + mov %sp,%bp # Address stack frame + xchg %bx,6(%bp) # Swap BX, int no + xor %ax,%ax # Set %ds:%bx to + shl $2,%bx # point to + mov %ax,%ds # IDT entry + mov (%bx),%ax # Load IP + mov 2(%bx),%bx # Load CS + xchg %ax,4(%bp) # Swap saved %ax,%bx with + xchg %bx,6(%bp) # CS:IP of handler + pop %bp # Restore + pop %ds # Restore + lret # Jump to handler + + .p2align 4 +/* + * Global descriptor table. + */ +gdt: .word 0x0,0x0,0x0,0x0 # Null entry + .word 0xffff,0x0,0x9a00,0xcf # SEL_SCODE + .word 0xffff,0x0,0x9200,0xcf # SEL_SDATA + .word 0xffff,0x0,0x9a00,0x0 # SEL_RCODE + .word 0xffff,0x0,0x9200,0x0 # SEL_RDATA + .word 0xffff,MEM_USR,0xfa00,0xcf# SEL_UCODE + .word 0xffff,MEM_USR,0xf200,0xcf# SEL_UDATA +tss_desc: .word _TSSLM,MEM_TSS,0x8900,0x0 # SEL_TSS +gdt.1: +/* + * Pseudo-descriptors. + */ +gdtdesc: .word gdt.1-gdt-1,gdt,0x0 # GDT +idtdesc: .word _IDTLM,MEM_IDT,0x0 # IDT +ivtdesc: .word 0x400-0x0-1,0x0,0x0 # IVT +/* + * IDT construction control string. + */ +idtctl: .byte 0x10, 0x8e # Int 0x0-0xf + .word 0x7dfb,intx00 # (exceptions) + .byte 0x10, 0x8e # Int 0x10 + .word 0x1, intx10 # (exception) + .byte 0x10, 0x8e # Int 0x20-0x2f + .word 0xffff,intx20 # (hardware) + .byte 0x1, 0xee # int 0x30 + .word 0x1, intx30 # (system call) + .byte 0x2, 0xee # Int 0x31-0x32 + .word 0x1, intx31 # (V86, null) + .byte 0x0 # End of string +/* + * Dump format string. + */ +dmpfmt: .byte '\n' # "\n" + .ascii "int" # "int=" + .byte 0x80|DMP_X32, 0x40 # "00000000 " + .ascii "err" # "err=" + .byte 0x80|DMP_X32, 0x44 # "00000000 " + .ascii "efl" # "efl=" + .byte 0x80|DMP_X32, 0x50 # "00000000 " + .ascii "eip" # "eip=" + .byte 0x80|DMP_X32|DMP_EOL,0x48 # "00000000\n" + .ascii "eax" # "eax=" + .byte 0x80|DMP_X32, 0x34 # "00000000 " + .ascii "ebx" # "ebx=" + .byte 0x80|DMP_X32, 0x28 # "00000000 " + .ascii "ecx" # "ecx=" + .byte 0x80|DMP_X32, 0x30 # "00000000 " + .ascii "edx" # "edx=" + .byte 0x80|DMP_X32|DMP_EOL,0x2c # "00000000\n" + .ascii "esi" # "esi=" + .byte 0x80|DMP_X32, 0x1c # "00000000 " + .ascii "edi" # "edi=" + .byte 0x80|DMP_X32, 0x18 # "00000000 " + .ascii "ebp" # "ebp=" + .byte 0x80|DMP_X32, 0x20 # "00000000 " + .ascii "esp" # "esp=" + .byte 0x80|DMP_X32|DMP_EOL,0x0 # "00000000\n" + .ascii "cs" # "cs=" + .byte 0x80|DMP_X16, 0x4c # "0000 " + .ascii "ds" # "ds=" + .byte 0x80|DMP_X16, 0xc # "0000 " + .ascii "es" # "es=" + .byte 0x80|DMP_X16, 0x8 # "0000 " + .ascii " " # " " + .ascii "fs" # "fs=" + .byte 0x80|DMP_X16, 0x10 # "0000 " + .ascii "gs" # "gs=" + .byte 0x80|DMP_X16, 0x14 # "0000 " + .ascii "ss" # "ss=" + .byte 0x80|DMP_X16|DMP_EOL,0x4 # "0000\n" + .ascii "cs:eip" # "cs:eip=" + .byte 0x80|DMP_MEM|DMP_EOL,0x48 # "00 00 ... 00 00\n" + .ascii "ss:esp" # "ss:esp=" + .byte 0x80|DMP_MEM|DMP_EOL,0x0 # "00 00 ... 00 00\n" + .asciz "BTX halted\n" # End +/* + * Bad VM86 call panic + */ +badvm86: .asciz "Invalid VM86 Request\n" + +/* + * End of BTX memory. + */ + .p2align 4 +break: diff --git a/stand/i386/btx/btxldr/Makefile b/stand/i386/btx/btxldr/Makefile new file mode 100644 index 0000000..3279597 --- /dev/null +++ b/stand/i386/btx/btxldr/Makefile @@ -0,0 +1,23 @@ +# $FreeBSD$ + +.include <bsd.init.mk> + +PROG= btxldr +INTERNALPROG= +MAN= +SRCS= btxldr.S + +CFLAGS+=-DLOADER_ADDRESS=${LOADER_ADDRESS} +CFLAGS+=-I${BOOTSRC}/i386/common + +.if defined(BTXLDR_VERBOSE) +CFLAGS+=-DBTXLDR_VERBOSE +.endif + +ORG=${LOADER_ADDRESS} +LDFLAGS+=${LDFLAGS_BIN} + +.include <bsd.prog.mk> + +# XXX: clang integrated-as doesn't grok .codeNN directives yet +CFLAGS.btxldr.S= ${CLANG_NO_IAS} diff --git a/stand/i386/btx/btxldr/Makefile.depend b/stand/i386/btx/btxldr/Makefile.depend new file mode 100644 index 0000000..f80275d --- /dev/null +++ b/stand/i386/btx/btxldr/Makefile.depend @@ -0,0 +1,11 @@ +# $FreeBSD$ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/stand/i386/btx/btxldr/btxldr.S b/stand/i386/btx/btxldr/btxldr.S new file mode 100644 index 0000000..1a0f5f4 --- /dev/null +++ b/stand/i386/btx/btxldr/btxldr.S @@ -0,0 +1,409 @@ +/* + * Copyright (c) 1998 Robert Nordier + * All rights reserved. + * + * Redistribution and use in source and binary forms are freely + * permitted provided that the above copyright notice and this + * paragraph and the following disclaimer are duplicated in all + * such forms. + * + * This software is provided "AS IS" and without any express or + * implied warranties, including, without limitation, the implied + * warranties of merchantability and fitness for a particular + * purpose. + * + * $FreeBSD$ + */ + +#include <bootargs.h> + +#define RBX_MUTE 0x10 /* -m */ +#define OPT_SET(opt) (1 << (opt)) + +/* + * Prototype BTX loader program, written in a couple of hours. The + * real thing should probably be more flexible, and in C. + */ + +/* + * Memory locations. + */ + .set MEM_STUB,0x600 # Real mode stub + .set MEM_ESP,0x1000 # New stack pointer + .set MEM_TBL,0x5000 # BTX page tables + .set MEM_ENTRY,0x9010 # BTX entry point + .set MEM_DATA,start+0x1000 # Data segment +/* + * Segment selectors. + */ + .set SEL_SCODE,0x8 # 4GB code + .set SEL_SDATA,0x10 # 4GB data + .set SEL_RCODE,0x18 # 64K code + .set SEL_RDATA,0x20 # 64K data +/* + * Paging constants. + */ + .set PAG_SIZ,0x1000 # Page size + .set PAG_ENT,0x4 # Page entry size +/* + * Screen constants. + */ + .set SCR_MAT,0x7 # Mode/attribute + .set SCR_COL,0x50 # Columns per row + .set SCR_ROW,0x19 # Rows per screen +/* + * BIOS Data Area locations. + */ + .set BDA_MEM,0x413 # Free memory + .set BDA_SCR,0x449 # Video mode + .set BDA_POS,0x450 # Cursor position +/* + * Required by aout gas inadequacy. + */ + .set SIZ_STUB,0x1a # Size of stub +/* + * We expect to be loaded by boot2 at the origin defined in ./Makefile. + */ + .globl start +/* + * BTX program loader for ELF clients. + */ +start: cld # String ops inc + testl $OPT_SET(RBX_MUTE), 4(%esp) # Check first argument + setnz muted # for RBX_MUTE, set flag + movl $m_logo,%esi # Identify + call putstr # ourselves + movzwl BDA_MEM,%eax # Get base memory + shll $0xa,%eax # in bytes + movl %eax,%ebp # Base of user stack +#ifdef BTXLDR_VERBOSE + movl $m_mem,%esi # Display + call hexout # amount of + call putstr # base memory +#endif + lgdt gdtdesc # Load new GDT +/* + * Relocate caller's arguments. + */ +#ifdef BTXLDR_VERBOSE + movl $m_esp,%esi # Display + movl %esp,%eax # caller + call hexout # stack + call putstr # pointer + movl $m_args,%esi # Format string + leal 0x4(%esp),%ebx # First argument + movl $0x6,%ecx # Count +start.1: movl (%ebx),%eax # Get argument and + addl $0x4,%ebx # bump pointer + call hexout # Display it + loop start.1 # Till done + call putstr # End message +#endif + movl BA_BOOTINFO+4(%esp),%esi # Source: bootinfo + cmpl $0x0, %esi # If the bootinfo pointer + je start_null_bi # is null, don't copy it + movl BI_SIZE(%esi),%ecx # Allocate space + subl %ecx,%ebp # for bootinfo + movl %ebp,%edi # Destination + rep # Copy + movsb # it + movl %ebp,BA_BOOTINFO+4(%esp) # Update pointer + movl %edi,%ebp # Restore base pointer +#ifdef BTXLDR_VERBOSE + movl $m_rel_bi,%esi # Display + movl %ebp,%eax # bootinfo + call hexout # relocation + call putstr # message +#endif +start_null_bi: movl $BOOTARGS_SIZE,%ecx # Fixed size of arguments + testl $KARGS_FLAGS_EXTARG, BA_BOOTFLAGS+4(%esp) # Check for extra data + jz start_fixed # Skip if the flag is not set + addl BOOTARGS_SIZE+4(%esp),%ecx # Add size of variable args +start_fixed: subl $ARGOFF,%ebp # Place args at fixed offset + leal 0x4(%esp),%esi # Source + movl %ebp,%edi # Destination + rep # Copy + movsb # them +#ifdef BTXLDR_VERBOSE + movl $m_rel_args,%esi # Display + movl %ebp,%eax # argument + call hexout # relocation + call putstr # message +#endif +/* + * Set up BTX kernel. + */ + movl $MEM_ESP,%esp # Set up new stack + movl $MEM_DATA,%ebx # Data segment + movl $m_vers,%esi # Display BTX + call putstr # version message + movb 0x5(%ebx),%al # Get major version + addb $'0',%al # Display + call putchr # it + movb $'.',%al # And a + call putchr # dot + movb 0x6(%ebx),%al # Get minor + xorb %ah,%ah # version + movb $0xa,%dl # Divide + divb %dl,%al # by 10 + addb $'0',%al # Display + call putchr # tens + movb %ah,%al # Get units + addb $'0',%al # Display + call putchr # units + call putstr # End message + movl %ebx,%esi # BTX image + movzwl 0x8(%ebx),%edi # Compute + orl $PAG_SIZ/PAG_ENT-1,%edi # the + incl %edi # BTX + shll $0x2,%edi # load + addl $MEM_TBL,%edi # address + pushl %edi # Save load address + movzwl 0xa(%ebx),%ecx # Image size +#ifdef BTXLDR_VERBOSE + pushl %ecx # Save image size +#endif + rep # Relocate + movsb # BTX + movl %esi,%ebx # Keep place +#ifdef BTXLDR_VERBOSE + movl $m_rel_btx,%esi # Restore + popl %eax # parameters + call hexout # and +#endif + popl %ebp # display +#ifdef BTXLDR_VERBOSE + movl %ebp,%eax # the + call hexout # relocation + call putstr # message +#endif + addl $PAG_SIZ,%ebp # Display +#ifdef BTXLDR_VERBOSE + movl $m_base,%esi # the + movl %ebp,%eax # user + call hexout # base + call putstr # address +#endif +/* + * Set up ELF-format client program. + */ + cmpl $0x464c457f,(%ebx) # ELF magic number? + je start.3 # Yes + movl $e_fmt,%esi # Display error + call putstr # message +start.2: jmp start.2 # Hang +start.3: +#ifdef BTXLDR_VERBOSE + movl $m_elf,%esi # Display ELF + call putstr # message + movl $m_segs,%esi # Format string +#endif + movl 0x1c(%ebx),%edx # Get e_phoff + addl %ebx,%edx # To pointer + movzwl 0x2c(%ebx),%ecx # Get e_phnum +start.4: cmpl $0x1,(%edx) # Is p_type PT_LOAD? + jne start.6 # No +#ifdef BTXLDR_VERBOSE + movl 0x4(%edx),%eax # Display + call hexout # p_offset + movl 0x8(%edx),%eax # Display + call hexout # p_vaddr + movl 0x10(%edx),%eax # Display + call hexout # p_filesz + movl 0x14(%edx),%eax # Display + call hexout # p_memsz + call putstr # End message +#endif + pushl %esi # Save + pushl %ecx # working registers + movl 0x4(%edx),%esi # Get p_offset + addl %ebx,%esi # as pointer + movl 0x8(%edx),%edi # Get p_vaddr + addl %ebp,%edi # as pointer + movl 0x10(%edx),%ecx # Get p_filesz + rep # Set up + movsb # segment + movl 0x14(%edx),%ecx # Any bytes + subl 0x10(%edx),%ecx # to zero? + jz start.5 # No + xorb %al,%al # Then + rep # zero + stosb # them +start.5: popl %ecx # Restore + popl %esi # registers +start.6: addl $0x20,%edx # To next entry + loop start.4 # Till done +#ifdef BTXLDR_VERBOSE + movl $m_done,%esi # Display done + call putstr # message +#endif + movl $start.8,%esi # Real mode stub + movl $MEM_STUB,%edi # Destination + movl $start.9-start.8,%ecx # Size + rep # Relocate + movsb # it + ljmp $SEL_RCODE,$MEM_STUB # To 16-bit code + .code16 +start.8: xorw %ax,%ax # Data + movb $SEL_RDATA,%al # selector + movw %ax,%ss # Reload SS + movw %ax,%ds # Reset + movw %ax,%es # other + movw %ax,%fs # segment + movw %ax,%gs # limits + movl %cr0,%eax # Switch to + decw %ax # real + movl %eax,%cr0 # mode + ljmp $0,$MEM_ENTRY # Jump to BTX entry point +start.9: + .code32 +/* + * Output message [ESI] followed by EAX in hex. + */ +hexout: pushl %eax # Save + call putstr # Display message + popl %eax # Restore + pushl %esi # Save + pushl %edi # caller's + movl $buf,%edi # Buffer + pushl %edi # Save + call hex32 # To hex + xorb %al,%al # Terminate + stosb # string + popl %esi # Restore +hexout.1: lodsb # Get a char + cmpb $'0',%al # Leading zero? + je hexout.1 # Yes + testb %al,%al # End of string? + jne hexout.2 # No + decl %esi # Undo +hexout.2: decl %esi # Adjust for inc + call putstr # Display hex + popl %edi # Restore + popl %esi # caller's + ret # To caller +/* + * Output zero-terminated string [ESI] to the console. + */ +putstr.0: call putchr # Output char +putstr: lodsb # Load char + testb %al,%al # End of string? + jne putstr.0 # No + ret # To caller +/* + * Output character AL to the console. + */ +putchr: testb $1,muted # Check muted + jnz putchr.5 # do a nop + pusha # Save + xorl %ecx,%ecx # Zero for loops + movb $SCR_MAT,%ah # Mode/attribute + movl $BDA_POS,%ebx # BDA pointer + movw (%ebx),%dx # Cursor position + movl $0xb8000,%edi # Regen buffer (color) + cmpb %ah,BDA_SCR-BDA_POS(%ebx) # Mono mode? + jne putchr.1 # No + xorw %di,%di # Regen buffer (mono) +putchr.1: cmpb $0xa,%al # New line? + je putchr.2 # Yes + xchgl %eax,%ecx # Save char + movb $SCR_COL,%al # Columns per row + mulb %dh # * row position + addb %dl,%al # + column + adcb $0x0,%ah # position + shll %eax # * 2 + xchgl %eax,%ecx # Swap char, offset + movw %ax,(%edi,%ecx,1) # Write attr:char + incl %edx # Bump cursor + cmpb $SCR_COL,%dl # Beyond row? + jb putchr.3 # No +putchr.2: xorb %dl,%dl # Zero column + incb %dh # Bump row +putchr.3: cmpb $SCR_ROW,%dh # Beyond screen? + jb putchr.4 # No + leal 2*SCR_COL(%edi),%esi # New top line + movw $(SCR_ROW-1)*SCR_COL/2,%cx # Words to move + rep # Scroll + movsl # screen + movb $' ',%al # Space + movb $SCR_COL,%cl # Columns to clear + rep # Clear + stosw # line + movb $SCR_ROW-1,%dh # Bottom line +putchr.4: movw %dx,(%ebx) # Update position + popa # Restore +putchr.5: ret # To caller +/* + * Convert EAX, AX, or AL to hex, saving the result to [EDI]. + */ +hex32: pushl %eax # Save + shrl $0x10,%eax # Do upper + call hex16 # 16 + popl %eax # Restore +hex16: call hex16.1 # Do upper 8 +hex16.1: xchgb %ah,%al # Save/restore +hex8: pushl %eax # Save + shrb $0x4,%al # Do upper + call hex8.1 # 4 + popl %eax # Restore +hex8.1: andb $0xf,%al # Get lower 4 + cmpb $0xa,%al # Convert + sbbb $0x69,%al # to hex + das # digit + orb $0x20,%al # To lower case + stosb # Save char + ret # (Recursive) + + .data + .p2align 4 +/* + * Global descriptor table. + */ +gdt: .word 0x0,0x0,0x0,0x0 # Null entry + .word 0xffff,0x0,0x9a00,0xcf # SEL_SCODE + .word 0xffff,0x0,0x9200,0xcf # SEL_SDATA + .word 0xffff,0x0,0x9a00,0x0 # SEL_RCODE + .word 0xffff,0x0,0x9200,0x0 # SEL_RDATA +gdt.1: +gdtdesc: .word gdt.1-gdt-1 # Limit + .long gdt # Base +/* + * Messages. + */ +m_logo: .asciz " \nBTX loader 1.00 " +m_vers: .asciz "BTX version is \0\n" +e_fmt: .asciz "Error: Client format not supported\n" +#ifdef BTXLDR_VERBOSE +m_mem: .asciz "Starting in protected mode (base mem=\0)\n" +m_esp: .asciz "Arguments passed (esp=\0):\n" +m_args: .asciz "<howto=" + .asciz " bootdev=" + .asciz " junk=" + .asciz " " + .asciz " " + .asciz " bootinfo=\0>\n" +m_rel_bi: .asciz "Relocated bootinfo (size=48) to \0\n" +m_rel_args: .asciz "Relocated arguments (size=18) to \0\n" +m_rel_btx: .asciz "Relocated kernel (size=\0) to \0\n" +m_base: .asciz "Client base address is \0\n" +m_elf: .asciz "Client format is ELF\n" +m_segs: .asciz "text segment: offset=" + .asciz " vaddr=" + .asciz " filesz=" + .asciz " memsz=\0\n" + .asciz "data segment: offset=" + .asciz " vaddr=" + .asciz " filesz=" + .asciz " memsz=\0\n" +m_done: .asciz "Loading complete\n" +#endif + +/* + * Flags + */ +muted: .byte 0x0 + +/* + * Uninitialized data area. + */ +buf: # Scratch buffer diff --git a/stand/i386/btx/lib/Makefile b/stand/i386/btx/lib/Makefile new file mode 100644 index 0000000..8de6255 --- /dev/null +++ b/stand/i386/btx/lib/Makefile @@ -0,0 +1,12 @@ +# $FreeBSD$ + +.include <bsd.init.mk> + +PROG= crt0.o +INTERNALPROG= +MAN= +SRCS= btxcsu.S btxsys.s btxv86.s +CFLAGS+=-I${BOOTSRC}/i386/common +LDFLAGS+=-Wl,-r + +.include <bsd.prog.mk> diff --git a/stand/i386/btx/lib/Makefile.depend b/stand/i386/btx/lib/Makefile.depend new file mode 100644 index 0000000..f80275d --- /dev/null +++ b/stand/i386/btx/lib/Makefile.depend @@ -0,0 +1,11 @@ +# $FreeBSD$ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/stand/i386/btx/lib/btxcsu.S b/stand/i386/btx/lib/btxcsu.S new file mode 100644 index 0000000..c46f809 --- /dev/null +++ b/stand/i386/btx/lib/btxcsu.S @@ -0,0 +1,49 @@ +# +# Copyright (c) 1998 Robert Nordier +# All rights reserved. +# +# Redistribution and use in source and binary forms are freely +# permitted provided that the above copyright notice and this +# paragraph and the following disclaimer are duplicated in all +# such forms. +# +# This software is provided "AS IS" and without any express or +# implied warranties, including, without limitation, the implied +# warranties of merchantability and fitness for a particular +# purpose. +# + +# $FreeBSD$ + +#include <bootargs.h> + +# +# BTX C startup code (ELF). +# + +# +# Globals. +# + .global _start +# +# Client entry point. +# +_start: cld + pushl %eax + movl $_edata,%edi + movl $_end,%ecx + subl %edi, %ecx + xorb %al, %al + rep + stosb + popl __base + movl %esp,%eax # Set + addl $ARGADJ,%eax # argument + movl %eax,__args # pointer + call main # Invoke client main() + call exit # Invoke client exit() +# +# Data. +# + .comm __base,4 # Client base address + .comm __args,4 # Client arguments diff --git a/stand/i386/btx/lib/btxsys.s b/stand/i386/btx/lib/btxsys.s new file mode 100644 index 0000000..9c77b42 --- /dev/null +++ b/stand/i386/btx/lib/btxsys.s @@ -0,0 +1,40 @@ +# +# Copyright (c) 1998 Robert Nordier +# All rights reserved. +# +# Redistribution and use in source and binary forms are freely +# permitted provided that the above copyright notice and this +# paragraph and the following disclaimer are duplicated in all +# such forms. +# +# This software is provided "AS IS" and without any express or +# implied warranties, including, without limitation, the implied +# warranties of merchantability and fitness for a particular +# purpose. +# + +# $FreeBSD$ + +# +# BTX system calls. +# + +# +# Globals. +# + .global __exit + .global __exec +# +# Constants. +# + .set INT_SYS,0x30 # Interrupt number +# +# System call: exit +# +__exit: xorl %eax,%eax # BTX system + int $INT_SYS # call 0x0 +# +# System call: exec +# +__exec: movl $0x1,%eax # BTX system + int $INT_SYS # call 0x1 diff --git a/stand/i386/btx/lib/btxv86.h b/stand/i386/btx/lib/btxv86.h new file mode 100644 index 0000000..f04ce5e --- /dev/null +++ b/stand/i386/btx/lib/btxv86.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 1998 Robert Nordier + * All rights reserved. + * + * Redistribution and use in source and binary forms are freely + * permitted provided that the above copyright notice and this + * paragraph and the following disclaimer are duplicated in all + * such forms. + * + * This software is provided "AS IS" and without any express or + * implied warranties, including, without limitation, the implied + * warranties of merchantability and fitness for a particular + * purpose. + */ + +/* + * $FreeBSD$ + */ + +#ifndef _BTXV86_H_ +#define _BTXV86_H_ + +#include <sys/types.h> +#include <machine/psl.h> + +/* + * Memory buffer space for real mode IO. + * Just one page is not much, but the space is rather limited. + * See ../btx/btx.S for details. + */ +#define V86_IO_BUFFER 0x8000 +#define V86_IO_BUFFER_SIZE 0x1000 + +#define V86_ADDR 0x10000 /* Segment:offset address */ +#define V86_CALLF 0x20000 /* Emulate far call */ +#define V86_FLAGS 0x40000 /* Return flags */ + +struct __v86 { + uint32_t ctl; /* Control flags */ + uint32_t addr; /* Interrupt number or address */ + uint32_t es; /* V86 ES register */ + uint32_t ds; /* V86 DS register */ + uint32_t fs; /* V86 FS register */ + uint32_t gs; /* V86 GS register */ + uint32_t eax; /* V86 EAX register */ + uint32_t ecx; /* V86 ECX register */ + uint32_t edx; /* V86 EDX register */ + uint32_t ebx; /* V86 EBX register */ + uint32_t efl; /* V86 eflags register */ + uint32_t ebp; /* V86 EBP register */ + uint32_t esi; /* V86 ESI register */ + uint32_t edi; /* V86 EDI register */ +}; + +extern struct __v86 __v86; /* V86 interface structure */ +void __v86int(void); + +#define v86 __v86 +#define v86int __v86int + +extern u_int32_t __base; +extern u_int32_t __args; + +#define PTOV(pa) ((caddr_t)(pa) - __base) +#define VTOP(va) ((vm_offset_t)(va) + __base) +#define VTOPSEG(va) (u_int16_t)(VTOP((caddr_t)va) >> 4) +#define VTOPOFF(va) (u_int16_t)(VTOP((caddr_t)va) & 0xf) + +#define V86_CY(x) ((x) & PSL_C) +#define V86_ZR(x) ((x) & PSL_Z) + +void __exit(int) __attribute__((__noreturn__)); +void __exec(caddr_t, ...); + +#endif /* !_BTXV86_H_ */ diff --git a/stand/i386/btx/lib/btxv86.s b/stand/i386/btx/lib/btxv86.s new file mode 100644 index 0000000..0d7d111 --- /dev/null +++ b/stand/i386/btx/lib/btxv86.s @@ -0,0 +1,85 @@ +# +# Copyright (c) 1998 Robert Nordier +# All rights reserved. +# +# Redistribution and use in source and binary forms are freely +# permitted provided that the above copyright notice and this +# paragraph and the following disclaimer are duplicated in all +# such forms. +# +# This software is provided "AS IS" and without any express or +# implied warranties, including, without limitation, the implied +# warranties of merchantability and fitness for a particular +# purpose. +# + +# $FreeBSD$ + +# +# BTX V86 interface. +# + +# +# Globals. +# + .global __v86int +# +# Fields in V86 interface structure. +# + .set V86_CTL,0x0 # Control flags + .set V86_ADDR,0x4 # Int number/address + .set V86_ES,0x8 # V86 ES + .set V86_DS,0xc # V86 DS + .set V86_FS,0x10 # V86 FS + .set V86_GS,0x14 # V86 GS + .set V86_EAX,0x18 # V86 EAX + .set V86_ECX,0x1c # V86 ECX + .set V86_EDX,0x20 # V86 EDX + .set V86_EBX,0x24 # V86 EBX + .set V86_EFL,0x28 # V86 eflags + .set V86_EBP,0x2c # V86 EBP + .set V86_ESI,0x30 # V86 ESI + .set V86_EDI,0x34 # V86 EDI +# +# Other constants. +# + .set INT_V86,0x31 # Interrupt number + .set SIZ_V86,0x38 # Size of V86 structure +# +# V86 interface function. +# +__v86int: popl __v86ret # Save return address + pushl $__v86 # Push pointer + call __v86_swap # Load V86 registers + int $INT_V86 # To BTX + call __v86_swap # Load user registers + addl $0x4,%esp # Discard pointer + pushl __v86ret # Restore return address + ret # To user +# +# Swap V86 and user registers. +# +__v86_swap: xchgl %ebp,0x4(%esp,1) # Swap pointer, EBP + xchgl %eax,V86_EAX(%ebp) # Swap EAX + xchgl %ecx,V86_ECX(%ebp) # Swap ECX + xchgl %edx,V86_EDX(%ebp) # Swap EDX + xchgl %ebx,V86_EBX(%ebp) # Swap EBX + pushl %eax # Save + pushf # Put eflags + popl %eax # in EAX + xchgl %eax,V86_EFL(%ebp) # Swap + pushl %eax # Put EAX + popf # in eflags + movl 0x8(%esp,1),%eax # Load EBP + xchgl %eax,V86_EBP(%ebp) # Swap + movl %eax,0x8(%esp,1) # Save EBP + popl %eax # Restore + xchgl %esi,V86_ESI(%ebp) # Swap ESI + xchgl %edi,V86_EDI(%ebp) # Swap EDI + xchgl %ebp,0x4(%esp,1) # Swap pointer, EBP + ret # To caller +# +# V86 interface structure. +# + .comm __v86,SIZ_V86 + .comm __v86ret,4 diff --git a/stand/i386/cdboot/Makefile b/stand/i386/cdboot/Makefile new file mode 100644 index 0000000..7acd404 --- /dev/null +++ b/stand/i386/cdboot/Makefile @@ -0,0 +1,20 @@ +# $FreeBSD$ + +.include <bsd.init.mk> + +PROG= cdboot +STRIP= +BINMODE=${NOBINMODE} +MAN= +SRCS= ${PROG}.S + +CFLAGS+=-I${BOOTSRC}/i386/common + +ORG= 0x7c00 + +LDFLAGS+=${LDFLAGS_BIN} + +.include <bsd.prog.mk> + +# XXX: clang integrated-as doesn't grok .codeNN directives yet +CFLAGS.cdboot.S= ${CLANG_NO_IAS} diff --git a/stand/i386/cdboot/Makefile.depend b/stand/i386/cdboot/Makefile.depend new file mode 100644 index 0000000..f80275d --- /dev/null +++ b/stand/i386/cdboot/Makefile.depend @@ -0,0 +1,11 @@ +# $FreeBSD$ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/stand/i386/cdboot/cdboot.S b/stand/i386/cdboot/cdboot.S new file mode 100644 index 0000000..9c3fb15 --- /dev/null +++ b/stand/i386/cdboot/cdboot.S @@ -0,0 +1,594 @@ +# +# 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. +# +# 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. +# + +# $FreeBSD$ + +# +# This program is a freestanding boot program to load an a.out binary +# from a CD-ROM booted with no emulation mode as described by the El +# Torito standard. Due to broken BIOSen that do not load the desired +# number of sectors, we try to fit this in as small a space as possible. +# +# Basically, we first create a set of boot arguments to pass to the loaded +# binary. Then we attempt to load /boot/loader from the CD we were booted +# off of. +# + +#include <bootargs.h> + +# +# Memory locations. +# + .set MEM_PAGE_SIZE,0x1000 # memory page size, 4k + .set MEM_ARG,0x900 # Arguments at start + .set MEM_ARG_BTX,0xa100 # Where we move them to so the + # BTX client can see them + .set MEM_ARG_SIZE,0x18 # Size of the arguments + .set MEM_BTX_ADDRESS,0x9000 # where BTX lives + .set MEM_BTX_ENTRY,0x9010 # where BTX starts to execute + .set MEM_BTX_OFFSET,MEM_PAGE_SIZE # offset of BTX in the loader + .set MEM_BTX_CLIENT,0xa000 # where BTX clients live +# +# a.out header fields +# + .set AOUT_TEXT,0x04 # text segment size + .set AOUT_DATA,0x08 # data segment size + .set AOUT_BSS,0x0c # zero'd BSS size + .set AOUT_SYMBOLS,0x10 # symbol table + .set AOUT_ENTRY,0x14 # entry point + .set AOUT_HEADER,MEM_PAGE_SIZE # size of the a.out header +# +# Segment selectors. +# + .set SEL_SDATA,0x8 # Supervisor data + .set SEL_RDATA,0x10 # Real mode data + .set SEL_SCODE,0x18 # PM-32 code + .set SEL_SCODE16,0x20 # PM-16 code +# +# BTX constants +# + .set INT_SYS,0x30 # BTX syscall interrupt +# +# Constants for reading from the CD. +# + .set ERROR_TIMEOUT,0x80 # BIOS timeout on read + .set NUM_RETRIES,3 # Num times to retry + .set SECTOR_SIZE,0x800 # size of a sector + .set SECTOR_SHIFT,11 # number of place to shift + .set BUFFER_LEN,0x100 # number of sectors in buffer + .set MAX_READ,0x10000 # max we can read at a time + .set MAX_READ_SEC,MAX_READ >> SECTOR_SHIFT + .set MEM_READ_BUFFER,0x9000 # buffer to read from CD + .set MEM_VOLDESC,MEM_READ_BUFFER # volume descriptor + .set MEM_DIR,MEM_VOLDESC+SECTOR_SIZE # Lookup buffer + .set VOLDESC_LBA,0x10 # LBA of vol descriptor + .set VD_PRIMARY,1 # Primary VD + .set VD_END,255 # VD Terminator + .set VD_ROOTDIR,156 # Offset of Root Dir Record + .set DIR_LEN,0 # Offset of Dir Record length + .set DIR_EA_LEN,1 # Offset of EA length + .set DIR_EXTENT,2 # Offset of 64-bit LBA + .set DIR_SIZE,10 # Offset of 64-bit length + .set DIR_NAMELEN,32 # Offset of 8-bit name len + .set DIR_NAME,33 # Offset of dir name +# +# We expect to be loaded by the BIOS at 0x7c00 (standard boot loader entry +# point) +# + .code16 + .globl start + .org 0x0, 0x0 +# +# Program start. +# +start: cld # string ops inc + xor %ax,%ax # zero %ax + mov %ax,%ss # setup the + mov $start,%sp # stack + mov %ax,%ds # setup the + mov %ax,%es # data segments + mov %dl,drive # Save BIOS boot device + mov $msg_welcome,%si # %ds:(%si) -> welcome message + call putstr # display the welcome message +# +# Setup the arguments that the loader is expecting from boot[12] +# + mov $msg_bootinfo,%si # %ds:(%si) -> boot args message + call putstr # display the message + mov $MEM_ARG,%bx # %ds:(%bx) -> boot args + mov %bx,%di # %es:(%di) -> boot args + xor %eax,%eax # zero %eax + mov $(MEM_ARG_SIZE/4),%cx # Size of arguments in 32-bit + # dwords + rep # Clear the arguments + stosl # to zero + mov drive,%dl # Store BIOS boot device + mov %dl,0x4(%bx) # in kargs->bootdev + orb $KARGS_FLAGS_CD,0x8(%bx) # kargs->bootflags |= + # KARGS_FLAGS_CD +# +# Load Volume Descriptor +# + mov $VOLDESC_LBA,%eax # Set LBA of first VD +load_vd: push %eax # Save %eax + mov $1,%dh # One sector + mov $MEM_VOLDESC,%ebx # Destination + call read # Read it in + cmpb $VD_PRIMARY,(%bx) # Primary VD? + je have_vd # Yes + pop %eax # Prepare to + inc %eax # try next + cmpb $VD_END,(%bx) # Last VD? + jne load_vd # No, read next + mov $msg_novd,%si # No VD + jmp error # Halt +have_vd: # Have Primary VD +# +# Try to look up the loader binary using the paths in the loader_paths +# array. +# + mov $loader_paths,%si # Point to start of array +lookup_path: push %si # Save file name pointer + call lookup # Try to find file + pop %di # Restore file name pointer + jnc lookup_found # Found this file + xor %al,%al # Look for next + mov $0xffff,%cx # path name by + repnz # scanning for + scasb # nul char + mov %di,%si # Point %si at next path + mov (%si),%al # Get first char of next path + or %al,%al # Is it double nul? + jnz lookup_path # No, try it. + mov $msg_failed,%si # Failed message + jmp error # 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 64k chunks. +# + mov 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 + mov $msg_load2big,%si # Error message + call error +load_sizeok: movzbw %al,%cx # Num sectors to read + mov DIR_EXTENT(%bx),%eax # Load extent + xor %edx,%edx + mov DIR_EA_LEN(%bx),%dl + add %edx,%eax # Skip extended + mov $MEM_READ_BUFFER,%ebx # Read into the buffer +load_loop: mov %cl,%dh + cmp $MAX_READ_SEC,%cl # Truncate to max read size + jbe load_notrunc + mov $MAX_READ_SEC,%dh +load_notrunc: sub %dh,%cl # Update count + push %eax # Save + call read # Read it in + pop %eax # Restore + add $MAX_READ_SEC,%eax # Update LBA + add $MAX_READ,%ebx # Update dest addr + jcxz load_done # Done? + jmp load_loop # Keep going +load_done: +# +# Turn on the A20 address line +# + call seta20 # Turn A20 on +# +# Relocate the loader and BTX using a very lazy protected mode +# + mov $msg_relocate,%si # Display the + call putstr # relocation message + mov MEM_READ_BUFFER+AOUT_ENTRY,%edi # %edi is the destination + mov $(MEM_READ_BUFFER+AOUT_HEADER),%esi # %esi is + # the start of the text + # segment + mov MEM_READ_BUFFER+AOUT_TEXT,%ecx # %ecx = length of the text + # segment + push %edi # Save entry point for later + lgdt gdtdesc # setup our own gdt + cli # turn off interrupts + mov %cr0,%eax # Turn on + or $0x1,%al # protected + mov %eax,%cr0 # mode + ljmp $SEL_SCODE,$pm_start # long jump to clear the + # instruction pre-fetch queue + .code32 +pm_start: mov $SEL_SDATA,%ax # Initialize + mov %ax,%ds # %ds and + mov %ax,%es # %es to a flat selector + rep # Relocate the + movsb # text segment + add $(MEM_PAGE_SIZE - 1),%edi # pad %edi out to a new page + and $~(MEM_PAGE_SIZE - 1),%edi # for the data segment + mov MEM_READ_BUFFER+AOUT_DATA,%ecx # size of the data segment + rep # Relocate the + movsb # data segment + mov MEM_READ_BUFFER+AOUT_BSS,%ecx # size of the bss + xor %eax,%eax # zero %eax + add $3,%cl # round %ecx up to + shr $2,%ecx # a multiple of 4 + rep # zero the + stosl # bss + mov MEM_READ_BUFFER+AOUT_ENTRY,%esi # %esi -> relocated loader + add $MEM_BTX_OFFSET,%esi # %esi -> BTX in the loader + mov $MEM_BTX_ADDRESS,%edi # %edi -> where BTX needs to go + movzwl 0xa(%esi),%ecx # %ecx -> length of BTX + rep # Relocate + movsb # BTX + ljmp $SEL_SCODE16,$pm_16 # Jump to 16-bit PM + .code16 +pm_16: mov $SEL_RDATA,%ax # Initialize + mov %ax,%ds # %ds and + mov %ax,%es # %es to a real mode selector + mov %cr0,%eax # Turn off + and $~0x1,%al # protected + mov %eax,%cr0 # mode + ljmp $0,$pm_end # Long jump to clear the + # instruction pre-fetch queue +pm_end: sti # Turn interrupts back on now +# +# Copy the BTX client to MEM_BTX_CLIENT +# + xor %ax,%ax # zero %ax and set + mov %ax,%ds # %ds and %es + mov %ax,%es # to segment 0 + mov $MEM_BTX_CLIENT,%di # Prepare to relocate + mov $btx_client,%si # the simple btx client + mov $(btx_client_end-btx_client),%cx # length of btx client + rep # Relocate the + movsb # simple BTX client +# +# Copy the boot[12] args to where the BTX client can see them +# + mov $MEM_ARG,%si # where the args are at now + mov $MEM_ARG_BTX,%di # where the args are moving to + mov $(MEM_ARG_SIZE/4),%cx # size of the arguments in longs + rep # Relocate + movsl # the words +# +# Save the entry point so the client can get to it later on +# + pop %eax # Restore saved entry point + stosl # and add it to the end of + # the arguments +# +# Now we just start up BTX and let it do the rest +# + mov $msg_jump,%si # Display the + call putstr # jump message + ljmp $0,$MEM_BTX_ENTRY # Jump to the BTX entry point + +# +# 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: mov $VD_ROOTDIR+MEM_VOLDESC,%bx # Root directory record + push %si + mov $msg_lookup,%si # Display lookup message + call putstr + pop %si + push %si + call putstr + mov $msg_lookup2,%si + call putstr + pop %si +lookup_dir: lodsb # Get first char of path + cmp $0,%al # Are we done? + je lookup_done # Yes + cmp $'/',%al # Skip path separator. + je lookup_dir + dec %si # Undo lodsb side effect + call find_file # Lookup first path item + jnc lookup_dir # Try next component + mov $msg_lookupfail,%si # Not found message + call putstr + stc # Set carry + ret + jmp error +lookup_done: mov $msg_lookupok,%si # Success message + call putstr + 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 + je 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 [EBX]. +# +# Trashes: EAX +# +read: push %si # Save + push %cx # Save since some BIOSs trash + mov %eax,edd_lba # LBA to read from + mov %ebx,%eax # Convert address + shr $4,%eax # to segment + mov %ax,edd_addr+0x2 # and store +read.retry: call twiddle # Entertain the user + push %dx # Save + mov $edd_packet,%si # Address Packet + mov %dh,edd_len # Set length + mov drive,%dl # BIOS Device + mov $0x42,%ah # BIOS: Extended Read + int $0x13 # Call BIOS + pop %dx # Restore + jc read.fail # Worked? + pop %cx # Restore + pop %si + ret # Return +read.fail: cmp $ERROR_TIMEOUT,%ah # Timeout? + je read.retry # Yes, Retry. +read.error: mov %ah,%al # Save error + mov $hex_error,%di # Format it + call hex8 # as hex + mov $msg_badread,%si # Display Read error message + +# +# Display error message at [SI] and halt. +# +error: call putstr # Display message +halt: hlt + jmp halt # Spin + +# +# Display a null-terminated string. +# +# Trashes: AX, SI +# +putstr: push %bx # Save +putstr.load: lodsb # load %al from %ds:(%si) + test %al,%al # stop at null + jnz putstr.putc # if the char != null, output it + pop %bx # Restore + ret # return when null is hit +putstr.putc: call putc # output char + jmp putstr.load # next char + +# +# Display a single char. +# +putc: mov $0x7,%bx # attribute for output + mov $0xe,%ah # BIOS: put_char + int $0x10 # call BIOS, print char in %al + ret # Return to caller + +# +# 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 putc # Output it + mov $8,%al # Backspace + call putc # Output it + pop %bx # Restore + pop %ax # Restore + ret + +# +# Enable A20. Put an upper limit on the amount of time we wait for the +# keyboard controller to get ready (65K x ISA access time). If +# we wait more than that amount, the hardware is probably +# legacy-free and simply doesn't have a keyboard controller. +# Thus, the A20 line is already enabled. +# +seta20: cli # Disable interrupts + xor %cx,%cx # Clear +seta20.1: inc %cx # Increment, overflow? + jz seta20.3 # Yes + in $0x64,%al # Get status + test $0x2,%al # Busy? + jnz seta20.1 # Yes + mov $0xd1,%al # Command: Write + out %al,$0x64 # output port +seta20.2: in $0x64,%al # Get status + test $0x2,%al # Busy? + jnz seta20.2 # Yes + mov $0xdf,%al # Enable + out %al,$0x60 # A20 +seta20.3: sti # Enable interrupts + ret # To caller + +# +# Convert AL to hex, saving the result to [EDI]. +# +hex8: pushl %eax # Save + shrb $0x4,%al # Do upper + call hex8.1 # 4 + popl %eax # Restore +hex8.1: andb $0xf,%al # Get lower 4 + cmpb $0xa,%al # Convert + sbbb $0x69,%al # to hex + das # digit + orb $0x20,%al # To lower case + stosb # Save char + ret # (Recursive) + +# +# BTX client to start btxldr +# + .code32 +btx_client: mov $(MEM_ARG_BTX-MEM_BTX_CLIENT+MEM_ARG_SIZE-4), %esi + # %ds:(%esi) -> end + # of boot[12] args + mov $(MEM_ARG_SIZE/4),%ecx # Number of words to push + std # Go backwards +push_arg: lodsl # Read argument + push %eax # Push it onto the stack + loop push_arg # Push all of the arguments + cld # In case anyone depends on this + pushl MEM_ARG_BTX-MEM_BTX_CLIENT+MEM_ARG_SIZE # Entry point of + # the loader + push %eax # Emulate a near call + mov $0x1,%eax # 'exec' system call + int $INT_SYS # BTX system call +btx_client_end: + .code16 + + .p2align 4 +# +# Global descriptor table. +# +gdt: .word 0x0,0x0,0x0,0x0 # Null entry + .word 0xffff,0x0,0x9200,0xcf # SEL_SDATA + .word 0xffff,0x0,0x9200,0x0 # SEL_RDATA + .word 0xffff,0x0,0x9a00,0xcf # SEL_SCODE (32-bit) + .word 0xffff,0x0,0x9a00,0x8f # SEL_SCODE16 (16-bit) +gdt.1: +# +# Pseudo-descriptors. +# +gdtdesc: .word gdt.1-gdt-1 # Limit + .long gdt # Base +# +# EDD Packet +# +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 + +drive: .byte 0 + +# +# 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 + +msg_welcome: .asciz "CD Loader 1.2\r\n\n" +msg_bootinfo: .asciz "Building the boot loader arguments\r\n" +msg_relocate: .asciz "Relocating the loader and the BTX\r\n" +msg_jump: .asciz "Starting the BTX loader\r\n" +msg_badread: .ascii "Read Error: 0x" +hex_error: .asciz "00\r\n" +msg_novd: .asciz "Could not find Primary Volume Descriptor\r\n" +msg_lookup: .asciz "Looking up " +msg_lookup2: .asciz "... " +msg_lookupok: .asciz "Found\r\n" +msg_lookupfail: .asciz "File not found\r\n" +msg_load2big: .asciz "File too big\r\n" +msg_failed: .asciz "Boot failed\r\n" +twiddle_chars: .ascii "|/-\\" +loader_paths: .asciz "/BOOT/LOADER" + .asciz "/boot/loader" + .byte 0 + diff --git a/stand/i386/common/bootargs.h b/stand/i386/common/bootargs.h new file mode 100644 index 0000000..df55807 --- /dev/null +++ b/stand/i386/common/bootargs.h @@ -0,0 +1,93 @@ +/*- + * Copyright (c) 2012 Andriy Gapon <avg@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms are freely + * permitted provided that the above copyright notice and this + * paragraph and the following disclaimer are duplicated in all + * such forms. + * + * This software is provided "AS IS" and without any express or + * implied warranties, including, without limitation, the implied + * warranties of merchantability and fitness for a particular + * purpose. + * + * $FreeBSD$ + */ + +#ifndef _BOOT_I386_ARGS_H_ +#define _BOOT_I386_ARGS_H_ + +#define KARGS_FLAGS_CD 0x1 +#define KARGS_FLAGS_PXE 0x2 +#define KARGS_FLAGS_ZFS 0x4 +#define KARGS_FLAGS_EXTARG 0x8 /* variably sized extended argument */ + +#define BOOTARGS_SIZE 24 /* sizeof(struct bootargs) */ +#define BA_BOOTFLAGS 8 /* offsetof(struct bootargs, bootflags) */ +#define BA_BOOTINFO 20 /* offsetof(struct bootargs, bootinfo) */ +#define BI_SIZE 48 /* offsetof(struct bootinfo, bi_size) */ + +/* + * We reserve some space above BTX allocated stack for the arguments + * and certain data that could hang off them. Currently only struct bootinfo + * is supported in that category. The bootinfo is placed at the top + * of the arguments area and the actual arguments are placed at ARGOFF offset + * from the top and grow towards the top. Hopefully we have enough space + * for bootinfo and the arguments to not run into each other. + * Arguments area below ARGOFF is reserved for future use. + */ +#define ARGSPACE 0x1000 /* total size of the BTX args area */ +#define ARGOFF 0x800 /* actual args offset within the args area */ +#define ARGADJ (ARGSPACE - ARGOFF) + +#ifndef __ASSEMBLER__ + +struct bootargs +{ + uint32_t howto; + uint32_t bootdev; + uint32_t bootflags; + union { + struct { + uint32_t pxeinfo; + uint32_t reserved; + }; + uint64_t zfspool; + }; + uint32_t bootinfo; + + /* + * If KARGS_FLAGS_EXTARG is set in bootflags, then the above fields + * are followed by a uint32_t field that specifies a size of the + * extended arguments (including the size field). + */ +}; + +#ifdef LOADER_GELI_SUPPORT +#include <crypto/intake.h> +#endif + +struct geli_boot_args +{ + uint32_t size; + union { + char gelipw[256]; + struct { + char notapw; /* + * single null byte to stop keybuf + * being interpreted as a password + */ + uint32_t keybuf_sentinel; +#ifdef LOADER_GELI_SUPPORT + struct keybuf *keybuf; +#else + void *keybuf; +#endif + }; + }; +}; + +#endif /*__ASSEMBLER__*/ + +#endif /* !_BOOT_I386_ARGS_H_ */ diff --git a/stand/i386/common/cons.c b/stand/i386/common/cons.c new file mode 100644 index 0000000..5fb1a93 --- /dev/null +++ b/stand/i386/common/cons.c @@ -0,0 +1,177 @@ +/*- + * Copyright (c) 1998 Robert Nordier + * All rights reserved. + * + * Redistribution and use in source and binary forms are freely + * permitted provided that the above copyright notice and this + * paragraph and the following disclaimer are duplicated in all + * such forms. + * + * This software is provided "AS IS" and without any express or + * implied warranties, including, without limitation, the implied + * warranties of merchantability and fitness for a particular + * purpose. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> + +#include <machine/psl.h> + +#include <btxv86.h> + +#include "lib.h" +#include "rbx.h" +#include "util.h" +#include "cons.h" + +#define SECOND 18 /* Circa that many ticks in a second. */ + +uint8_t ioctrl = IO_KEYBOARD; + +void +putc(int c) +{ + + v86.ctl = V86_FLAGS; + v86.addr = 0x10; + v86.eax = 0xe00 | (c & 0xff); + v86.ebx = 0x7; + v86int(); +} + +void +xputc(int c) +{ + + if (ioctrl & IO_KEYBOARD) + putc(c); + if (ioctrl & IO_SERIAL) + sio_putc(c); +} + +void +putchar(int c) +{ + + if (c == '\n') + xputc('\r'); + xputc(c); +} + +int +getc(int fn) +{ + + v86.ctl = V86_FLAGS; + v86.addr = 0x16; + v86.eax = fn << 8; + v86int(); + + if (fn == 0) + return (v86.eax); + + if (V86_ZR(v86.efl)) + return (0); + return (v86.eax); +} + +int +xgetc(int fn) +{ + + if (OPT_CHECK(RBX_NOINTR)) + return (0); + for (;;) { + if (ioctrl & IO_KEYBOARD && getc(1)) + return (fn ? 1 : getc(0)); + if (ioctrl & IO_SERIAL && sio_ischar()) + return (fn ? 1 : sio_getc()); + if (fn) + return (0); + } + /* NOTREACHED */ +} + +int +getchar(void) +{ + + return (xgetc(0)); +} + +int +keyhit(unsigned int secs) +{ + uint32_t t0, t1, c; + + if (OPT_CHECK(RBX_NOINTR)) + return (0); + secs *= SECOND; + t0 = 0; + for (;;) { + /* + * The extra comparison is an attempt to work around + * what appears to be a bug in QEMU and Bochs. Both emulators + * sometimes report a key-press with scancode one and ascii zero + * when no such key is pressed in reality. As far as I can tell, + * this only happens shortly after a reboot. + */ + c = xgetc(1); + if (c != 0 && c != 0x0100) + return (1); + if (secs > 0) { + t1 = *(uint32_t *)PTOV(0x46c); + if (!t0) + t0 = t1; + if (t1 < t0 || t1 >= t0 + secs) + return (0); + } + } + /* NOTREACHED */ +} + +void +getstr(char *cmdstr, size_t cmdstrsize) +{ + char *s; + int c; + + s = cmdstr; + for (;;) { + c = xgetc(0); + + /* Translate some extended codes. */ + switch (c) { + case 0x5300: /* delete */ + c = '\177'; + break; + default: + c &= 0xff; + break; + } + + switch (c) { + case '\177': + case '\b': + if (s > cmdstr) { + s--; + printf("\b \b"); + } + break; + case '\n': + case '\r': + *s = 0; + return; + default: + if (c >= 0x20 && c <= 0x7e) { + if (s - cmdstr < cmdstrsize - 1) + *s++ = c; + putchar(c); + } + break; + } + } +} diff --git a/stand/i386/common/cons.h b/stand/i386/common/cons.h new file mode 100644 index 0000000..73474fb --- /dev/null +++ b/stand/i386/common/cons.h @@ -0,0 +1,35 @@ +/*- + * Copyright (c) 1998 Robert Nordier + * All rights reserved. + * + * Redistribution and use in source and binary forms are freely + * permitted provided that the above copyright notice and this + * paragraph and the following disclaimer are duplicated in all + * such forms. + * + * This software is provided "AS IS" and without any express or + * implied warranties, including, without limitation, the implied + * warranties of merchantability and fitness for a particular + * purpose. + * + * $FreeBSD$ + */ + +#ifndef _CONS_H_ +#define _CONS_H_ + +#define IO_KEYBOARD 1 +#define IO_SERIAL 2 + +extern uint8_t ioctrl; + +void putc(int c); +void xputc(int c); +void putchar(int c); +int getc(int fn); +int xgetc(int fn); +int getchar(void); +int keyhit(unsigned int secs); +void getstr(char *cmdstr, size_t cmdstrsize); + +#endif /* !_CONS_H_ */ diff --git a/stand/i386/common/drv.c b/stand/i386/common/drv.c new file mode 100644 index 0000000..a805c41 --- /dev/null +++ b/stand/i386/common/drv.c @@ -0,0 +1,102 @@ +/*- + * Copyright (c) 1998 Robert Nordier + * Copyright (c) 2010 Pawel Jakub Dawidek <pjd@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms are freely + * permitted provided that the above copyright notice and this + * paragraph and the following disclaimer are duplicated in all + * such forms. + * + * This software is provided "AS IS" and without any express or + * implied warranties, including, without limitation, the implied + * warranties of merchantability and fitness for a particular + * purpose. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> + +#include <btxv86.h> + +#include "rbx.h" +#include "util.h" +#include "drv.h" +#include "edd.h" + +static struct edd_params params; + +uint64_t +drvsize(struct dsk *dskp) +{ + + params.len = sizeof(struct edd_params); + v86.ctl = V86_FLAGS; + v86.addr = 0x13; + v86.eax = 0x4800; + v86.edx = dskp->drive; + v86.ds = VTOPSEG(¶ms); + v86.esi = VTOPOFF(¶ms); + v86int(); + if (V86_CY(v86.efl)) { + printf("error %u\n", v86.eax >> 8 & 0xff); + return (0); + } + return (params.sectors); +} + +static struct edd_packet packet; + +int +drvread(struct dsk *dskp, void *buf, daddr_t lba, unsigned nblk) +{ + static unsigned c = 0x2d5c7c2f; + + if (!OPT_CHECK(RBX_QUIET)) + printf("%c\b", c = c << 8 | c >> 24); + packet.len = sizeof(struct edd_packet); + packet.count = nblk; + packet.off = VTOPOFF(buf); + packet.seg = VTOPSEG(buf); + packet.lba = lba; + v86.ctl = V86_FLAGS; + v86.addr = 0x13; + v86.eax = 0x4200; + v86.edx = dskp->drive; + v86.ds = VTOPSEG(&packet); + v86.esi = VTOPOFF(&packet); + v86int(); + if (V86_CY(v86.efl)) { + printf("%s: error %u lba %u\n", + BOOTPROG, v86.eax >> 8 & 0xff, lba); + return (-1); + } + return (0); +} + +#if defined(GPT) || defined(ZFS) +int +drvwrite(struct dsk *dskp, void *buf, daddr_t lba, unsigned nblk) +{ + + packet.len = sizeof(struct edd_packet); + packet.count = nblk; + packet.off = VTOPOFF(buf); + packet.seg = VTOPSEG(buf); + packet.lba = lba; + v86.ctl = V86_FLAGS; + v86.addr = 0x13; + v86.eax = 0x4300; + v86.edx = dskp->drive; + v86.ds = VTOPSEG(&packet); + v86.esi = VTOPOFF(&packet); + v86int(); + if (V86_CY(v86.efl)) { + printf("error %u lba %u\n", v86.eax >> 8 & 0xff, lba); + return (-1); + } + return (0); +} +#endif /* GPT || ZFS */ diff --git a/stand/i386/common/drv.h b/stand/i386/common/drv.h new file mode 100644 index 0000000..c0995df --- /dev/null +++ b/stand/i386/common/drv.h @@ -0,0 +1,48 @@ +/*- + * Copyright (c) 2010 Pawel Jakub Dawidek <pjd@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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS 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 AUTHORS 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. + * + * $FreeBSD$ + */ + +#ifndef _DRV_H_ +#define _DRV_H_ + +struct dsk { + unsigned int drive; + unsigned int type; + unsigned int unit; + unsigned int slice; + int part; + daddr_t start; + uint64_t size; +}; + +int drvread(struct dsk *dskp, void *buf, daddr_t lba, unsigned nblk); +#if defined(GPT) || defined(ZFS) +int drvwrite(struct dsk *dskp, void *buf, daddr_t lba, unsigned nblk); +#endif /* GPT || ZFS */ +uint64_t drvsize(struct dsk *dskp); + +#endif /* !_DRV_H_ */ diff --git a/stand/i386/common/edd.h b/stand/i386/common/edd.h new file mode 100644 index 0000000..7d1f450 --- /dev/null +++ b/stand/i386/common/edd.h @@ -0,0 +1,110 @@ +/*- + * Copyright (c) 2011 Hudson River Trading LLC + * Written by: John H. 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. + * + * 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. + * + * $FreeBSD$ + */ + +#ifndef _EDD_H_ +#define _EDD_H_ + +/* Supported interfaces for "Check Extensions Present". */ +#define EDD_INTERFACE_FIXED_DISK 0x01 +#define EDD_INTERFACE_EJECT 0x02 +#define EDD_INTERFACE_EDD 0x04 + +struct edd_packet { + uint16_t len; + uint16_t count; + uint16_t off; + uint16_t seg; + uint64_t lba; +}; + +struct edd_packet_v3 { + uint16_t len; + uint16_t count; + uint16_t off; + uint16_t seg; + uint64_t lba; + uint64_t phys_addr; +}; + +struct edd_params { + uint16_t len; + uint16_t flags; + uint32_t cylinders; + uint32_t heads; + uint32_t sectors_per_track; + uint64_t sectors; + uint16_t sector_size; + uint16_t edd_params_seg; + uint16_t edd_params_off; +} __packed; + +struct edd_device_path_v3 { + uint16_t key; + uint8_t len; + uint8_t reserved[3]; + char host_bus[4]; + char interface[8]; + uint64_t interface_path; + uint64_t device_path; + uint8_t reserved2[1]; + uint8_t checksum; +} __packed; + +struct edd_params_v3 { + struct edd_params params; + struct edd_device_path_v3 device_path; +} __packed; + +struct edd_device_path_v4 { + uint16_t key; + uint8_t len; + uint8_t reserved[3]; + char host_bus[4]; + char interface[8]; + uint64_t interface_path; + uint64_t device_path[2]; + uint8_t reserved2[1]; + uint8_t checksum; +} __packed; + +struct edd_params_v4 { + struct edd_params params; + struct edd_device_path_v4 device_path; +} __packed; + +#define EDD_FLAGS_DMA_BOUNDARY_HANDLING 0x0001 +#define EDD_FLAGS_REMOVABLE_MEDIA 0x0002 +#define EDD_FLAGS_WRITE_VERIFY 0x0004 +#define EDD_FLAGS_MEDIA_CHANGE_NOTIFICATION 0x0008 +#define EDD_FLAGS_LOCKABLE_MEDIA 0x0010 +#define EDD_FLAGS_NO_MEDIA_PRESENT 0x0020 + +#define EDD_DEVICE_PATH_KEY 0xbedd + +#endif /* !_EDD_H_ */ diff --git a/stand/i386/gptboot/Makefile b/stand/i386/gptboot/Makefile new file mode 100644 index 0000000..9e62007 --- /dev/null +++ b/stand/i386/gptboot/Makefile @@ -0,0 +1,75 @@ +# $FreeBSD$ + +HAVE_GELI= yes + +.include <bsd.init.mk> + +.PATH: ${BOOTSRC}/i386/boot2 ${BOOTSRC}/i386/common ${SASRC} + +FILES= gptboot +MAN= gptboot.8 + +NM?= nm + +BOOT_COMCONSOLE_PORT?= 0x3f8 +BOOT_COMCONSOLE_SPEED?= 9600 +B2SIOFMT?= 0x3 + +REL1= 0x700 +ORG1= 0x7c00 +ORG2= 0x0 + +# Decide level of UFS support. +GPTBOOT_UFS?= UFS1_AND_UFS2 +#GPTBOOT_UFS?= UFS2_ONLY +#GPTBOOT_UFS?= UFS1_ONLY + +CFLAGS+=-DBOOTPROG=\"gptboot\" \ + -O1 \ + -DGPT \ + -D${GPTBOOT_UFS} \ + -DSIOPRT=${BOOT_COMCONSOLE_PORT} \ + -DSIOFMT=${B2SIOFMT} \ + -DSIOSPD=${BOOT_COMCONSOLE_SPEED} \ + -I${LDRSRC} \ + -I${BOOTSRC}/i386/common \ + -I${BTXLIB} \ + -I${BOOTSRC}/i386/boot2 \ + -Wall -Waggregate-return -Wbad-function-cast -Wno-cast-align \ + -Wmissing-declarations -Wmissing-prototypes -Wnested-externs \ + -Wpointer-arith -Wshadow -Wstrict-prototypes -Wwrite-strings \ + -Winline -Wno-pointer-sign + +CFLAGS.gcc+= --param max-inline-insns-single=100 + +LD_FLAGS+=${LD_FLAGS_BIN} + +CLEANFILES+= gptboot + +gptboot: gptldr.bin gptboot.bin ${BTXKERN} + btxld -v -E ${ORG2} -f bin -b ${BTXKERN} -l gptldr.bin \ + -o ${.TARGET} gptboot.bin + +CLEANFILES+= gptldr.bin gptldr.out gptldr.o + +gptldr.bin: gptldr.out + ${OBJCOPY} -S -O binary gptldr.out ${.TARGET} + +gptldr.out: gptldr.o + ${LD} ${LD_FLAGS} -e start -Ttext ${ORG1} -o ${.TARGET} gptldr.o + +CLEANFILES+= gptboot.bin gptboot.out gptboot.o sio.o crc32.o drv.o \ + cons.o ${OPENCRYPTO_XTS} + +gptboot.bin: gptboot.out + ${OBJCOPY} -S -O binary gptboot.out ${.TARGET} + +gptboot.out: ${BTXCRT} gptboot.o sio.o crc32.o drv.o cons.o ${OPENCRYPTO_XTS} + ${LD} ${LD_FLAGS} -Ttext ${ORG2} -o ${.TARGET} ${.ALLSRC} ${LIBGELIBOOT} ${LIBSA32} + +gptboot.o: ${SASRC}/ufsread.c + +.include <bsd.prog.mk> + +# XXX: clang integrated-as doesn't grok .codeNN directives yet +CFLAGS.gptldr.S= ${CLANG_NO_IAS} diff --git a/stand/i386/gptboot/Makefile.depend b/stand/i386/gptboot/Makefile.depend new file mode 100644 index 0000000..295be1a --- /dev/null +++ b/stand/i386/gptboot/Makefile.depend @@ -0,0 +1,19 @@ +# $FreeBSD$ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + include \ + include/xlocale \ + lib/libmd \ + lib/libstand \ + sys/boot/geli \ + sys/boot/i386/btx/btx \ + sys/boot/i386/btx/lib \ + sys/boot/libstand32 \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/stand/i386/gptboot/gptboot.8 b/stand/i386/gptboot/gptboot.8 new file mode 100644 index 0000000..a7afd81 --- /dev/null +++ b/stand/i386/gptboot/gptboot.8 @@ -0,0 +1,245 @@ +.\" Copyright (c) 2013 Warren Block +.\" 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 AUTHORS 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 AUTHORS 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. +.\" +.\" $FreeBSD$ +.\" +.Dd February 5, 2014 +.Dt GPTBOOT 8 +.Os +.Sh NAME +.Nm gptboot +.Nd GPT bootcode for UFS on BIOS-based computers +.Sh DESCRIPTION +.Nm +is used on BIOS-based computers to boot from a UFS partition on a +GPT-partitioned disk. +.Nm +is installed in a +.Cm freebsd-boot +partition with +.Xr gpart 8 . +.Sh IMPLEMENTATION NOTES +The GPT standard allows a variable number of partitions, but +.Nm +only boots from tables with 128 partitions or less. +.Sh PARTITION ATTRIBUTES +.Nm +checks and manages several attributes of GPT UFS partitions. +.Bl -tag -width ".Cm bootfailed" +.It Cm bootme +Attempt to boot from this partition. +If more than one partition has the +.Cm bootme +attribute set, +.Nm +will attempt to boot each one until successful. +.It Cm bootonce +Attempt to boot from this partition only one time. +Setting this attribute with +.Xr gpart 8 +automatically also sets the +.Cm bootme +attribute. +Multiple partitions may have the +.Cm bootonce +and +.Cm bootme +attributes set. +.It Cm bootfailed +The +.Cm bootfailed +attribute marks partitions that had the +.Cm bootonce +attribute set, but failed to boot. +This attribute is managed by the system. +See +.Sx "BOOTING" +and +.Sx "POST-BOOT ACTIONS" +below for details. +.El +.Sh USAGE +For normal usage, the user does not have to set or manage any of the +partition attributes. +.Nm +will boot from the first UFS partition found. +.Pp +The +.Cm bootonce +attribute can be used for testing an upgraded operating system on +an already-working computer. +The existing system partition is left untouched, and the new version +of the operating system to be tested is installed on another partition. +The +.Cm bootonce +attribute is set on that new test partition. +The next boot is attempted from the test partition. +Success or failure will be shown in the system log files. +After a successful boot of the test partition, a user script can check +the logs and change the +.Cm bootme +attributes so the test partition becomes the new system partition. +Because the +.Cm bootonce +attribute is cleared after an attempted boot, a failed boot will not +leave the system attempting to boot from a partition that will never +succeed. +Instead, the system will boot from the older, known-working operating +system that has not been modified. +If the +.Cm bootme +attribute is set on any partitions, booting will be attempted from them +first. +If no partitions with +.Cm bootme +attributes are found, booting will be attempted from the first UFS +partition found. +.Sh BOOTING +.Nm +first reads the partition table. +All +.Cm freebsd-ufs +partitions with only the +.Cm bootonce +attribute set, indicating a failed boot, are set to +.Cm bootfailed . +.Nm +then scans through all of the +.Cm freebsd-ufs +partitions. +Boot behavior depends on the combination of +.Cm bootme +and +.Cm bootonce +attributes set on those partitions. +.Bl -tag -width ".Cm bootonce + .Cm bootme" +.It Cm bootonce + Cm bootme +Highest priority: booting is attempted from each of the +.Cm freebsd-ufs +partitions with both of these attributes. +On each partition, the +.Cm bootme +attribute is removed and the boot attempted. +.It Cm bootme +Middle priority: booting is attempted from each of the +.Cm freebsd-ufs +partitions with the +.Cm bootme +attribute. +.El +.Pp +If neither +.Cm bootonce +nor +.Cm bootme +attributes are found on any partitions, booting is attempted from the +first +.Cm freebsd-ufs +partition on the disk. +.Sh POST-BOOT ACTIONS +The startup script +.Pa /etc/rc.d/gptboot +checks the attributes of +.Cm freebsd-ufs +partitions on all GPT disks. +Partitions with the +.Cm bootfailed +attribute generate a +.Dq boot from X failed +system log message. +Partitions with only the +.Cm bootonce +attribute, indicating a partition that successfully booted, generate a +.Dq boot from X succeeded +system log message. +The +.Cm bootfailed +attributes are cleared from all the partitions. +The +.Cm bootonce +attribute is cleared from the partition that successfully booted. +There is normally only one of these. +.Sh FILES +.Bl -tag -width /boot/gptboot -compact +.It Pa /boot/gptboot +bootcode binary +.It Pa /boot.config +parameters for the boot blocks +.Pq optional +.El +.Sh EXAMPLES +.Nm +is installed in a +.Cm freebsd-boot +partition, usually the first partition on the disk. +A +.Dq protective MBR +.Po +see +.Xr gpart 8 +.Pc +is typically installed in combination with +.Nm . +.Pp +Install +.Nm +on the +.Pa ada0 +drive: +.Bd -literal -offset indent +gpart bootcode -b /boot/pmbr -p /boot/gptboot -i 1 ada0 +.Ed +.Pp +.Nm +can also be installed without the PMBR: +.Bd -literal -offset indent +gpart bootcode -p /boot/gptboot -i 1 ada0 +.Ed +.Pp +Set the +.Cm bootme +attribute for partition 2: +.Bd -literal -offset indent +gpart set -a bootme -i 2 ada0 +.Ed +.Pp +Set the +.Cm bootonce +attribute for partition 2, automatically also setting the +.Cm bootme +attribute: +.Bd -literal -offset indent +gpart set -a bootonce -i 2 ada0 +.Ed +.Sh SEE ALSO +.Xr boot.config 5 , +.Xr rc.conf 5 , +.Xr boot 8 , +.Xr gpart 8 +.Sh HISTORY +.Nm +appeared in FreeBSD 7.1. +.Sh AUTHORS +This manual page written by +.An Warren Block Aq wblock@FreeBSD.org . diff --git a/stand/i386/gptboot/gptboot.c b/stand/i386/gptboot/gptboot.c new file mode 100644 index 0000000..dd06f0c --- /dev/null +++ b/stand/i386/gptboot/gptboot.c @@ -0,0 +1,648 @@ +/*- + * Copyright (c) 1998 Robert Nordier + * All rights reserved. + * + * Redistribution and use in source and binary forms are freely + * permitted provided that the above copyright notice and this + * paragraph and the following disclaimer are duplicated in all + * such forms. + * + * This software is provided "AS IS" and without any express or + * implied warranties, including, without limitation, the implied + * warranties of merchantability and fitness for a particular + * purpose. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/gpt.h> +#include <sys/dirent.h> +#include <sys/reboot.h> + +#include <machine/bootinfo.h> +#include <machine/elf.h> +#include <machine/pc/bios.h> +#include <machine/psl.h> + +#include <stdarg.h> + +#include <a.out.h> + +#include <btxv86.h> + +#include "bootargs.h" +#include "lib.h" +#include "rbx.h" +#include "drv.h" +#include "util.h" +#include "cons.h" +#include "gpt.h" +#include "paths.h" + +#define ARGS 0x900 +#define NOPT 14 +#define NDEV 3 +#define MEM_BASE 0x12 +#define MEM_EXT 0x15 + +#define DRV_HARD 0x80 +#define DRV_MASK 0x7f + +#define TYPE_AD 0 +#define TYPE_DA 1 +#define TYPE_MAXHARD TYPE_DA +#define TYPE_FD 2 + +extern uint32_t _end; + +static const uuid_t freebsd_ufs_uuid = GPT_ENT_TYPE_FREEBSD_UFS; +static const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */ +static const unsigned char flags[NOPT] = { + RBX_DUAL, + RBX_SERIAL, + RBX_ASKNAME, + RBX_CDROM, + RBX_CONFIG, + RBX_KDB, + RBX_GDB, + RBX_MUTE, + RBX_NOINTR, + RBX_PAUSE, + RBX_QUIET, + RBX_DFLTROOT, + RBX_SINGLE, + RBX_VERBOSE +}; +uint32_t opts; + +static const char *const dev_nm[NDEV] = {"ad", "da", "fd"}; +static const unsigned char dev_maj[NDEV] = {30, 4, 2}; + +static struct dsk dsk; +static char kname[1024]; +static int comspeed = SIOSPD; +static struct bootinfo bootinfo; +#ifdef LOADER_GELI_SUPPORT +static struct geli_boot_args geliargs; +#endif + +static vm_offset_t high_heap_base; +static uint32_t bios_basemem, bios_extmem, high_heap_size; + +static struct bios_smap smap; + +/* + * The minimum amount of memory to reserve in bios_extmem for the heap. + */ +#define HEAP_MIN (3 * 1024 * 1024) + +static char *heap_next; +static char *heap_end; + +void exit(int); +static void load(void); +static int parse_cmds(char *, int *); +static int dskread(void *, daddr_t, unsigned); +void *malloc(size_t n); +void free(void *ptr); +#ifdef LOADER_GELI_SUPPORT +static int vdev_read(void *vdev __unused, void *priv, off_t off, void *buf, + size_t bytes); +#endif + +void * +malloc(size_t n) +{ + char *p = heap_next; + if (p + n > heap_end) { + printf("malloc failure\n"); + for (;;) + ; + /* NOTREACHED */ + return (0); + } + heap_next += n; + return (p); +} + +void +free(void *ptr) +{ + + return; +} + +#include "ufsread.c" +#include "gpt.c" +#ifdef LOADER_GELI_SUPPORT +#include "geliboot.c" +static char gelipw[GELI_PW_MAXLEN]; +static struct keybuf *gelibuf; +#endif + +static inline int +xfsread(ufs_ino_t inode, void *buf, size_t nbyte) +{ + + if ((size_t)fsread(inode, buf, nbyte) != nbyte) { + printf("Invalid %s\n", "format"); + return (-1); + } + return (0); +} + +static void +bios_getmem(void) +{ + uint64_t size; + + /* Parse system memory map */ + v86.ebx = 0; + do { + v86.ctl = V86_FLAGS; + v86.addr = MEM_EXT; /* int 0x15 function 0xe820*/ + v86.eax = 0xe820; + v86.ecx = sizeof(struct bios_smap); + v86.edx = SMAP_SIG; + v86.es = VTOPSEG(&smap); + v86.edi = VTOPOFF(&smap); + v86int(); + if ((v86.efl & 1) || (v86.eax != SMAP_SIG)) + break; + /* look for a low-memory segment that's large enough */ + if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0) && + (smap.length >= (512 * 1024))) + bios_basemem = smap.length; + /* look for the first segment in 'extended' memory */ + if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0x100000)) { + bios_extmem = smap.length; + } + + /* + * Look for the largest segment in 'extended' memory beyond + * 1MB but below 4GB. + */ + if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base > 0x100000) && + (smap.base < 0x100000000ull)) { + size = smap.length; + + /* + * If this segment crosses the 4GB boundary, truncate it. + */ + if (smap.base + size > 0x100000000ull) + size = 0x100000000ull - smap.base; + + if (size > high_heap_size) { + high_heap_size = size; + high_heap_base = smap.base; + } + } + } while (v86.ebx != 0); + + /* Fall back to the old compatibility function for base memory */ + if (bios_basemem == 0) { + v86.ctl = 0; + v86.addr = 0x12; /* int 0x12 */ + v86int(); + + bios_basemem = (v86.eax & 0xffff) * 1024; + } + + /* Fall back through several compatibility functions for extended memory */ + if (bios_extmem == 0) { + v86.ctl = V86_FLAGS; + v86.addr = 0x15; /* int 0x15 function 0xe801*/ + v86.eax = 0xe801; + v86int(); + if (!(v86.efl & 1)) { + bios_extmem = ((v86.ecx & 0xffff) + ((v86.edx & 0xffff) * 64)) * 1024; + } + } + if (bios_extmem == 0) { + v86.ctl = 0; + v86.addr = 0x15; /* int 0x15 function 0x88*/ + v86.eax = 0x8800; + v86int(); + bios_extmem = (v86.eax & 0xffff) * 1024; + } + + /* + * If we have extended memory and did not find a suitable heap + * region in the SMAP, use the last 3MB of 'extended' memory as a + * high heap candidate. + */ + if (bios_extmem >= HEAP_MIN && high_heap_size < HEAP_MIN) { + high_heap_size = HEAP_MIN; + high_heap_base = bios_extmem + 0x100000 - HEAP_MIN; + } +} + +static int +gptinit(void) +{ + + if (gptread(&freebsd_ufs_uuid, &dsk, dmadat->secbuf) == -1) { + printf("%s: unable to load GPT\n", BOOTPROG); + return (-1); + } + if (gptfind(&freebsd_ufs_uuid, &dsk, dsk.part) == -1) { + printf("%s: no UFS partition was found\n", BOOTPROG); + return (-1); + } +#ifdef LOADER_GELI_SUPPORT + if (geli_taste(vdev_read, &dsk, (gpttable[curent].ent_lba_end - + gpttable[curent].ent_lba_start)) == 0) { + if (geli_havekey(&dsk) != 0 && geli_passphrase(&gelipw, + dsk.unit, 'p', curent + 1, &dsk) != 0) { + printf("%s: unable to decrypt GELI key\n", BOOTPROG); + return (-1); + } + } +#endif + + dsk_meta = 0; + return (0); +} + +int +main(void) +{ + char cmd[512], cmdtmp[512]; + ssize_t sz; + int autoboot, dskupdated; + ufs_ino_t ino; + + dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base); + + bios_getmem(); + + if (high_heap_size > 0) { + heap_end = PTOV(high_heap_base + high_heap_size); + heap_next = PTOV(high_heap_base); + } else { + heap_next = (char *)dmadat + sizeof(*dmadat); + heap_end = (char *)PTOV(bios_basemem); + } + + v86.ctl = V86_FLAGS; + v86.efl = PSL_RESERVED_DEFAULT | PSL_I; + dsk.drive = *(uint8_t *)PTOV(ARGS); + dsk.type = dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD; + dsk.unit = dsk.drive & DRV_MASK; + dsk.part = -1; + dsk.start = 0; + bootinfo.bi_version = BOOTINFO_VERSION; + bootinfo.bi_size = sizeof(bootinfo); + bootinfo.bi_basemem = bios_basemem / 1024; + bootinfo.bi_extmem = bios_extmem / 1024; + bootinfo.bi_memsizes_valid++; + bootinfo.bi_bios_dev = dsk.drive; + +#ifdef LOADER_GELI_SUPPORT + geli_init(); +#endif + /* Process configuration file */ + + if (gptinit() != 0) + return (-1); + + autoboot = 1; + *cmd = '\0'; + + for (;;) { + *kname = '\0'; + if ((ino = lookup(PATH_CONFIG)) || + (ino = lookup(PATH_DOTCONFIG))) { + sz = fsread(ino, cmd, sizeof(cmd) - 1); + cmd[(sz < 0) ? 0 : sz] = '\0'; + } + if (*cmd != '\0') { + memcpy(cmdtmp, cmd, sizeof(cmdtmp)); + if (parse_cmds(cmdtmp, &dskupdated)) + break; + if (dskupdated && gptinit() != 0) + break; + if (!OPT_CHECK(RBX_QUIET)) + printf("%s: %s", PATH_CONFIG, cmd); + *cmd = '\0'; + } + + if (autoboot && keyhit(3)) { + if (*kname == '\0') + memcpy(kname, PATH_LOADER, sizeof(PATH_LOADER)); + break; + } + autoboot = 0; + + /* + * Try to exec stage 3 boot loader. If interrupted by a + * keypress, or in case of failure, try to load a kernel + * directly instead. + */ + if (*kname != '\0') + load(); + memcpy(kname, PATH_LOADER, sizeof(PATH_LOADER)); + load(); + memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL)); + load(); + gptbootfailed(&dsk); + if (gptfind(&freebsd_ufs_uuid, &dsk, -1) == -1) + break; + dsk_meta = 0; + } + + /* Present the user with the boot2 prompt. */ + + for (;;) { + if (!OPT_CHECK(RBX_QUIET)) { + printf("\nFreeBSD/x86 boot\n" + "Default: %u:%s(%up%u)%s\n" + "boot: ", + dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit, + dsk.part, kname); + } + if (ioctrl & IO_SERIAL) + sio_flush(); + *cmd = '\0'; + if (keyhit(0)) + getstr(cmd, sizeof(cmd)); + else if (!OPT_CHECK(RBX_QUIET)) + putchar('\n'); + if (parse_cmds(cmd, &dskupdated)) { + putchar('\a'); + continue; + } + if (dskupdated && gptinit() != 0) + continue; + load(); + } + /* NOTREACHED */ +} + +/* XXX - Needed for btxld to link the boot2 binary; do not remove. */ +void +exit(int x) +{ +} + +static void +load(void) +{ + union { + struct exec ex; + Elf32_Ehdr eh; + } hdr; + static Elf32_Phdr ep[2]; + static Elf32_Shdr es[2]; + caddr_t p; + ufs_ino_t ino; + uint32_t addr, x; + int fmt, i, j; + + if (!(ino = lookup(kname))) { + if (!ls) { + printf("%s: No %s on %u:%s(%up%u)\n", BOOTPROG, + kname, dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit, + dsk.part); + } + return; + } + if (xfsread(ino, &hdr, sizeof(hdr))) + return; + if (N_GETMAGIC(hdr.ex) == ZMAGIC) + fmt = 0; + else if (IS_ELF(hdr.eh)) + fmt = 1; + else { + printf("Invalid %s\n", "format"); + return; + } + if (fmt == 0) { + addr = hdr.ex.a_entry & 0xffffff; + p = PTOV(addr); + fs_off = PAGE_SIZE; + if (xfsread(ino, p, hdr.ex.a_text)) + return; + p += roundup2(hdr.ex.a_text, PAGE_SIZE); + if (xfsread(ino, p, hdr.ex.a_data)) + return; + p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE); + bootinfo.bi_symtab = VTOP(p); + memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms)); + p += sizeof(hdr.ex.a_syms); + if (hdr.ex.a_syms) { + if (xfsread(ino, p, hdr.ex.a_syms)) + return; + p += hdr.ex.a_syms; + if (xfsread(ino, p, sizeof(int))) + return; + x = *(uint32_t *)p; + p += sizeof(int); + x -= sizeof(int); + if (xfsread(ino, p, x)) + return; + p += x; + } + } else { + fs_off = hdr.eh.e_phoff; + for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) { + if (xfsread(ino, ep + j, sizeof(ep[0]))) + return; + if (ep[j].p_type == PT_LOAD) + j++; + } + for (i = 0; i < 2; i++) { + p = PTOV(ep[i].p_paddr & 0xffffff); + fs_off = ep[i].p_offset; + if (xfsread(ino, p, ep[i].p_filesz)) + return; + } + p += roundup2(ep[1].p_memsz, PAGE_SIZE); + bootinfo.bi_symtab = VTOP(p); + if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) { + fs_off = hdr.eh.e_shoff + sizeof(es[0]) * + (hdr.eh.e_shstrndx + 1); + if (xfsread(ino, &es, sizeof(es))) + return; + for (i = 0; i < 2; i++) { + memcpy(p, &es[i].sh_size, sizeof(es[i].sh_size)); + p += sizeof(es[i].sh_size); + fs_off = es[i].sh_offset; + if (xfsread(ino, p, es[i].sh_size)) + return; + p += es[i].sh_size; + } + } + addr = hdr.eh.e_entry & 0xffffff; + } + bootinfo.bi_esymtab = VTOP(p); + bootinfo.bi_kernelname = VTOP(kname); + bootinfo.bi_bios_dev = dsk.drive; +#ifdef LOADER_GELI_SUPPORT + geliargs.size = sizeof(geliargs); + explicit_bzero(gelipw, sizeof(gelipw)); + gelibuf = malloc(sizeof(struct keybuf) + (GELI_MAX_KEYS * sizeof(struct keybuf_ent))); + geli_fill_keybuf(gelibuf); + geliargs.notapw = '\0'; + geliargs.keybuf_sentinel = KEYBUF_SENTINEL; + geliargs.keybuf = gelibuf; +#endif + __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK), + MAKEBOOTDEV(dev_maj[dsk.type], dsk.part + 1, dsk.unit, 0xff), + KARGS_FLAGS_EXTARG, 0, 0, VTOP(&bootinfo) +#ifdef LOADER_GELI_SUPPORT + , geliargs +#endif + ); +} + +static int +parse_cmds(char *cmdstr, int *dskupdated) +{ + char *arg = cmdstr; + char *ep, *p, *q; + const char *cp; + unsigned int drv; + int c, i, j; + + *dskupdated = 0; + while ((c = *arg++)) { + if (c == ' ' || c == '\t' || c == '\n') + continue; + for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++); + ep = p; + if (*p) + *p++ = 0; + if (c == '-') { + while ((c = *arg++)) { + if (c == 'P') { + if (*(uint8_t *)PTOV(0x496) & 0x10) { + cp = "yes"; + } else { + opts |= OPT_SET(RBX_DUAL) | OPT_SET(RBX_SERIAL); + cp = "no"; + } + printf("Keyboard: %s\n", cp); + continue; + } else if (c == 'S') { + j = 0; + while ((unsigned int)(i = *arg++ - '0') <= 9) + j = j * 10 + i; + if (j > 0 && i == -'0') { + comspeed = j; + break; + } + /* Fall through to error below ('S' not in optstr[]). */ + } + for (i = 0; c != optstr[i]; i++) + if (i == NOPT - 1) + return -1; + opts ^= OPT_SET(flags[i]); + } + ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) : + OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD; + if (ioctrl & IO_SERIAL) { + if (sio_init(115200 / comspeed) != 0) + ioctrl &= ~IO_SERIAL; + } + } else { + for (q = arg--; *q && *q != '('; q++); + if (*q) { + drv = -1; + if (arg[1] == ':') { + drv = *arg - '0'; + if (drv > 9) + return (-1); + arg += 2; + } + if (q - arg != 2) + return -1; + for (i = 0; arg[0] != dev_nm[i][0] || + arg[1] != dev_nm[i][1]; i++) + if (i == NDEV - 1) + return -1; + dsk.type = i; + arg += 3; + dsk.unit = *arg - '0'; + if (arg[1] != 'p' || dsk.unit > 9) + return -1; + arg += 2; + dsk.part = *arg - '0'; + if (dsk.part < 1 || dsk.part > 9) + return -1; + arg++; + if (arg[0] != ')') + return -1; + arg++; + if (drv == -1) + drv = dsk.unit; + dsk.drive = (dsk.type <= TYPE_MAXHARD + ? DRV_HARD : 0) + drv; + *dskupdated = 1; + } + if ((i = ep - arg)) { + if ((size_t)i >= sizeof(kname)) + return -1; + memcpy(kname, arg, i + 1); + } + } + arg = p; + } + return 0; +} + +static int +dskread(void *buf, daddr_t lba, unsigned nblk) +{ + int err; + + err = drvread(&dsk, buf, lba + dsk.start, nblk); + +#ifdef LOADER_GELI_SUPPORT + if (err == 0 && is_geli(&dsk) == 0) { + /* Decrypt */ + if (geli_read(&dsk, lba * DEV_BSIZE, buf, nblk * DEV_BSIZE)) + return (err); + } +#endif + + return (err); +} + +#ifdef LOADER_GELI_SUPPORT +/* + * Read function compartible with the ZFS callback, required to keep the GELI + * Implementation the same for both UFS and ZFS + */ +static int +vdev_read(void *vdev __unused, void *priv, off_t off, void *buf, size_t bytes) +{ + char *p; + daddr_t lba; + unsigned int nb; + struct dsk *dskp = (struct dsk *) priv; + + if ((off & (DEV_BSIZE - 1)) || (bytes & (DEV_BSIZE - 1))) + return (-1); + + p = buf; + lba = off / DEV_BSIZE; + lba += dskp->start; + + while (bytes > 0) { + nb = bytes / DEV_BSIZE; + if (nb > VBLKSIZE / DEV_BSIZE) + nb = VBLKSIZE / DEV_BSIZE; + if (drvread(dskp, dmadat->blkbuf, lba, nb)) + return (-1); + memcpy(p, dmadat->blkbuf, nb * DEV_BSIZE); + p += nb * DEV_BSIZE; + lba += nb; + bytes -= nb * DEV_BSIZE; + } + + return (0); +} +#endif /* LOADER_GELI_SUPPORT */ diff --git a/stand/i386/gptboot/gptldr.S b/stand/i386/gptboot/gptldr.S new file mode 100644 index 0000000..088122f --- /dev/null +++ b/stand/i386/gptboot/gptldr.S @@ -0,0 +1,142 @@ +/*- + * Copyright (c) 2007 Yahoo!, Inc. + * All rights reserved. + * Written by: John Baldwin <jhb@FreeBSD.org> + * + * 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. + * + * $FreeBSD$ + * + * Partly from: src/sys/boot/i386/boot2/boot1.S 1.31 + */ + +/* Memory Locations */ + .set MEM_REL,0x700 # Relocation address + .set MEM_ARG,0x900 # Arguments + .set MEM_ORG,0x7c00 # Origin + .set MEM_BUF,0x8cec # Load area + .set MEM_BTX,0x9000 # BTX start + .set MEM_JMP,0x9010 # BTX entry point + .set MEM_USR,0xa000 # Client start + .set BDA_BOOT,0x472 # Boot howto flag + +/* Misc. Constants */ + .set SIZ_PAG,0x1000 # Page size + .set SIZ_SEC,0x200 # Sector size + .set COPY_BLKS,0x8 # Number of blocks + # to copy for boot2 + .set COPY_BLK_SZ,0x8000 # Copy in 32k blocks; must be + # a multiple of 16 bytes + + .globl start + .code16 + +/* + * Copy BTX and boot2 to the right locations and start it all up. + */ + +/* + * Setup the segment registers to flat addressing (segment 0) and setup the + * stack to end just below the start of our code. + */ +start: xor %cx,%cx # Zero + mov %cx,%es # Address + mov %cx,%ds # data + mov %cx,%ss # Set up + mov $start,%sp # stack + +/* + * BTX is right after us at 'end'. We read the length of BTX out of + * its header to find boot2. We need to copy boot2 to MEM_USR and BTX + * to MEM_BTX. Since those might overlap, we have to copy boot2 + * backwards first and then copy BTX. We aren't sure exactly how long + * boot2 is, but it's currently under 128kB so we'll copy 4 blocks of 32kB + * each; this can be adjusted via COPY_BLK and COPY_BLK_SZ above. + */ + mov $end,%bx # BTX + mov 0xa(%bx),%si # Get BTX length and set + add %bx,%si # %si to start of boot2 + dec %si # Set %ds:%si to point at the + mov %si,%ax # last byte we want to copy + shr $4,%ax # from boot2, with %si made as + add $(COPY_BLKS*COPY_BLK_SZ/16),%ax # small as possible. + and $0xf,%si # + mov %ax,%ds # + mov $MEM_USR/16,%ax # Set %es:(-1) to point at + add $(COPY_BLKS*COPY_BLK_SZ/16),%ax # the last byte we + mov %ax,%es # want to copy boot2 into. + mov $COPY_BLKS,%bx # Copy COPY_BLKS 32k blocks +copyloop: + add $COPY_BLK_SZ,%si # Adjust %ds:%si to point at + mov %ds,%ax # the end of the next 32k to + sub $COPY_BLK_SZ/16,%ax # copy from boot2 + mov %ax,%ds + mov $COPY_BLK_SZ-1,%di # Adjust %es:%di to point at + mov %es,%ax # the end of the next 32k into + sub $COPY_BLK_SZ/16,%ax # which we want boot2 copied + mov %ax,%es + mov $COPY_BLK_SZ,%cx # Copy 32k + std + rep movsb + dec %bx + jnz copyloop + mov %cx,%ds # Reset %ds and %es + mov %cx,%es + mov $end,%bx # BTX + mov 0xa(%bx),%cx # Get BTX length and set + mov %bx,%si # %si to end of BTX + mov $MEM_BTX,%di # %di -> end of BTX at + add %cx,%si # MEM_BTX + add %cx,%di + dec %si + dec %di + rep movsb # Move BTX + cld # String ops inc +/* + * Enable A20 so we can access memory above 1 meg. + * Use the zero-valued %cx as a timeout for embedded hardware which do not + * have a keyboard controller. + */ +seta20: cli # Disable interrupts +seta20.1: dec %cx # Timeout? + jz seta20.3 # Yes + inb $0x64,%al # Get status + testb $0x2,%al # Busy? + jnz seta20.1 # Yes + movb $0xd1,%al # Command: Write + outb %al,$0x64 # output port +seta20.2: inb $0x64,%al # Get status + testb $0x2,%al # Busy? + jnz seta20.2 # Yes + movb $0xdf,%al # Enable + outb %al,$0x60 # A20 +seta20.3: sti # Enable interrupts + +/* + * Save drive number from BIOS so boot2 can see it and start BTX. + */ + movb %dl,MEM_ARG + jmp MEM_JMP # Start BTX +end: diff --git a/stand/i386/gptzfsboot/Makefile b/stand/i386/gptzfsboot/Makefile new file mode 100644 index 0000000..97f1954 --- /dev/null +++ b/stand/i386/gptzfsboot/Makefile @@ -0,0 +1,87 @@ +# $FreeBSD$ + +HAVE_GPT= yes + +.include <bsd.init.mk> + +.PATH: ${BOOTSRC}/i386/boot2 ${BOOTSRC}/i386/gptboot \ + ${BOOTSRC}/i386/zfsboot ${BOOTSRC}/i386/common \ + ${SASRC} + +FILES= gptzfsboot +MAN= gptzfsboot.8 + +NM?= nm + +BOOT_COMCONSOLE_PORT?= 0x3f8 +BOOT_COMCONSOLE_SPEED?= 9600 +B2SIOFMT?= 0x3 + +REL1= 0x700 +ORG1= 0x7c00 +ORG2= 0x0 + +CFLAGS+=-DBOOTPROG=\"gptzfsboot\" \ + -O1 \ + -DGPT -DZFS -DBOOT2 \ + -DSIOPRT=${BOOT_COMCONSOLE_PORT} \ + -DSIOFMT=${B2SIOFMT} \ + -DSIOSPD=${BOOT_COMCONSOLE_SPEED} \ + -I${LDRSRC} \ + -I${BOOTSRC}/i386/common \ + -I${ZFSSRC} \ + -I${SYSDIR}/cddl/boot/zfs \ + -I${BOOTSRC}/i386/btx/lib \ + -I${BOOTSRC}/i386/boot2 \ + -Wall -Waggregate-return -Wbad-function-cast \ + -Wmissing-declarations -Wmissing-prototypes -Wnested-externs \ + -Wpointer-arith -Wshadow -Wstrict-prototypes -Wwrite-strings \ + -Winline -Wno-pointer-sign + +NO_WCAST_ALIGN= + +.if ${COMPILER_TYPE} == "clang" || \ + (${COMPILER_TYPE} == "gcc" && ${COMPILER_VERSION} > 40201) +CFLAGS+= -Wno-tentative-definition-incomplete-type +.endif + +.if ${MACHINE} == "amd64" +LIBZFSBOOT=${BOOTOBJ}/zfs32/libzfsboot.a +.else +LIBZFSBOOT=${BOOTOBJ}/zfs/libzfsboot.a +.endif + +CFLAGS.gcc+= --param max-inline-insns-single=100 + +LD_FLAGS+=${LD_FLAGS_BIN} + +CLEANFILES+= gptzfsboot + +gptzfsboot: gptldr.bin gptzfsboot.bin ${BTXKERN} + btxld -v -E ${ORG2} -f bin -b ${BTXKERN} -l gptldr.bin \ + -o ${.TARGET} gptzfsboot.bin + +CLEANFILES+= gptldr.bin gptldr.out gptldr.o + +gptldr.bin: gptldr.out + ${OBJCOPY} -S -O binary gptldr.out ${.TARGET} + +gptldr.out: gptldr.o + ${LD} ${LD_FLAGS} -e start -Ttext ${ORG1} -o ${.TARGET} gptldr.o + +CLEANFILES+= gptzfsboot.bin gptzfsboot.out zfsboot.o sio.o cons.o \ + drv.o gpt.o util.o ${OPENCRYPTO_XTS} + +gptzfsboot.bin: gptzfsboot.out + ${OBJCOPY} -S -O binary gptzfsboot.out ${.TARGET} + +gptzfsboot.out: ${BTXCRT} zfsboot.o sio.o gpt.o drv.o cons.o util.o \ + ${OPENCRYPTO_XTS} + ${LD} ${LD_FLAGS} -Ttext ${ORG2} -o ${.TARGET} ${.ALLSRC} ${LIBGELIBOOT} ${LIBZFSBOOT} ${LIBSA32} + +zfsboot.o: ${ZFSSRC}/zfsimpl.c + +.include <bsd.prog.mk> + +# XXX: clang integrated-as doesn't grok .codeNN directives yet +CFLAGS.gptldr.S= ${CLANG_NO_IAS} diff --git a/stand/i386/gptzfsboot/Makefile.depend b/stand/i386/gptzfsboot/Makefile.depend new file mode 100644 index 0000000..295be1a --- /dev/null +++ b/stand/i386/gptzfsboot/Makefile.depend @@ -0,0 +1,19 @@ +# $FreeBSD$ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + include \ + include/xlocale \ + lib/libmd \ + lib/libstand \ + sys/boot/geli \ + sys/boot/i386/btx/btx \ + sys/boot/i386/btx/lib \ + sys/boot/libstand32 \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/stand/i386/gptzfsboot/gptzfsboot.8 b/stand/i386/gptzfsboot/gptzfsboot.8 new file mode 100644 index 0000000..66feb6d --- /dev/null +++ b/stand/i386/gptzfsboot/gptzfsboot.8 @@ -0,0 +1,193 @@ +.\" Copyright (c) 2014 Andriy Gapon <avg@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. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS 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 AUTHORS 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. +.\" +.\" $FreeBSD$ +.\" +.Dd September 15, 2014 +.Dt GPTZFSBOOT 8 +.Os +.Sh NAME +.Nm gptzfsboot +.Nd GPT bootcode for ZFS on BIOS-based computers +.Sh DESCRIPTION +.Nm +is used on BIOS-based computers to boot from a filesystem in +a ZFS pool. +.Nm +is installed in a +.Cm freebsd-boot +partition of a GPT-partitioned disk with +.Xr gpart 8 . +.Sh IMPLEMENTATION NOTES +The GPT standard allows a variable number of partitions, but +.Nm +only boots from tables with 128 partitions or less. +.Sh BOOTING +.Nm +tries to find all ZFS pools that are composed of BIOS-visible +hard disks or partitions on them. +.Nm +looks for ZFS device labels on all visible disks and in discovered +supported partitions for all supported partition scheme types. +The search starts with the disk from which +.Nm +itself was loaded. +Other disks are probed in BIOS defined order. +After a disk is probed and +.Nm +determines that the whole disk is not a ZFS pool member, the +individual partitions are probed in their partition table order. +Currently GPT and MBR partition schemes are supported. +With the GPT scheme, only partitions of type +.Cm freebsd-zfs +are probed. +The first pool seen during probing is used as a default boot pool. +.Pp +The filesystem specified by the +.Cm bootfs +property of the pool is used as a default boot filesystem. +If the +.Cm bootfs +property is not set, then the root filesystem of the pool is used as +the default. +.Xr zfsloader 8 +is loaded from the boot filesystem. +If +.Pa /boot.config +or +.Pa /boot/config +is present in the boot filesystem, boot options are read from it +in the same way as +.Xr boot 8 . +.Pp +The ZFS GUIDs of the first successfully probed device and the first +detected pool are made available to +.Xr zfsloader 8 +in the +.Cm vfs.zfs.boot.primary_vdev +and +.Cm vfs.zfs.boot.primary_pool +variables. +.Sh USAGE +Normally +.Nm +will boot in fully automatic mode. +However, like +.Xr boot 8 , +it is possible to interrupt the automatic boot process and interact with +.Nm +through a prompt. +.Nm +accepts all the options that +.Xr boot 8 +supports. +.Pp +The filesystem specification and the path to +.Xr zfsloader 8 +are different from +.Xr boot 8 . +The format is +.Pp +.Sm off +.Oo zfs:pool/filesystem: Oc Oo /path/to/loader Oc +.Sm on +.Pp +Both the filesystem and the path can be specified. +If only a path is specified, then the default filesystem is used. +If only a pool and filesystem are specified, then +.Pa /boot/zfsloader +is used as a path. +.Pp +Additionally, the +.Ic status +command can be used to query information about discovered pools. +The output format is similar to that of +.Cm zpool status +.Pq see Xr zpool 8 . +.Pp +The configured or automatically determined ZFS boot filesystem is +stored in the +.Xr zfsloader 8 +.Cm loaddev +variable, and also set as the initial value of the +.Cm currdev +variable. +.Sh FILES +.Bl -tag -width /boot/gptzfsboot -compact +.It Pa /boot/gptzfsboot +boot code binary +.It Pa /boot.config +parameters for the boot block +.Pq optional +.It Pa /boot/config +alternative parameters for the boot block +.Pq optional +.El +.Sh EXAMPLES +.Nm +is typically installed in combination with a +.Dq protective MBR +.Po +see +.Xr gpart 8 +.Pc . +To install +.Nm +on the +.Pa ada0 +drive: +.Bd -literal -offset indent +gpart bootcode -b /boot/pmbr -p /boot/gptzfsboot -i 1 ada0 +.Ed +.Pp +.Nm +can also be installed without the PMBR: +.Bd -literal -offset indent +gpart bootcode -p /boot/gptzfsboot -i 1 ada0 +.Ed +.Sh SEE ALSO +.Xr boot.config 5 , +.Xr boot 8 , +.Xr gpart 8 , +.Xr loader 8 , +.Xr zfsloader 8 , +.Xr zpool 8 +.Sh HISTORY +.Nm +appeared in FreeBSD 7.3. +.Sh AUTHORS +This manual page was written by +.An Andriy Gapon Aq avg@FreeBSD.org . +.Sh BUGS +.Nm +looks for ZFS meta-data only in MBR partitions +.Pq known on FreeBSD as slices . +It does not look into BSD +.Xr disklabel 8 +partitions that are traditionally called partitions. +If a disklabel partition happens to be placed so that ZFS meta-data can be +found at the fixed offsets relative to a slice, then +.Nm +will recognize the partition as a part of a ZFS pool, +but this is not guaranteed to happen. diff --git a/stand/i386/kgzldr/Makefile b/stand/i386/kgzldr/Makefile new file mode 100644 index 0000000..5a03b83 --- /dev/null +++ b/stand/i386/kgzldr/Makefile @@ -0,0 +1,21 @@ +# $FreeBSD$ + +.include <bsd.init.mk> + +PROG= kgzldr.o +STRIP= +BINMODE=${LIBMODE} +BINDIR= ${LIBDIR} +MAN= + +SRCS= start.s boot.c inflate.c lib.c crt.s sio.s +CFLAGS= -Os +CFLAGS+=-DKZIP +NO_SHARED= +LDFLAGS+=-Wl,-r +.PATH: ${SYSDIR}/kern + +BOOT_COMCONSOLE_PORT?= 0x3f8 +AFLAGS+=--defsym SIO_PRT=${BOOT_COMCONSOLE_PORT} + +.include <bsd.prog.mk> diff --git a/stand/i386/kgzldr/Makefile.depend b/stand/i386/kgzldr/Makefile.depend new file mode 100644 index 0000000..79506ce --- /dev/null +++ b/stand/i386/kgzldr/Makefile.depend @@ -0,0 +1,12 @@ +# $FreeBSD$ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + include \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/stand/i386/kgzldr/boot.c b/stand/i386/kgzldr/boot.c new file mode 100644 index 0000000..45ed2ee --- /dev/null +++ b/stand/i386/kgzldr/boot.c @@ -0,0 +1,129 @@ +/*- + * Copyright (c) 1999 Global Technology Associates, Inc. + * 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 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <sys/reboot.h> +#include <sys/inflate.h> + +#include "kgzldr.h" + +#define KGZ_HEAD 0xa /* leading bytes to ignore */ +#define KGZ_TAIL 0x8 /* trailing bytes to ignore */ + +#define E_FMT 1 /* Error: Invalid format */ +#define E_MEM 2 /* Error: Out of memory */ + +struct kgz_hdr { + char ident[4]; /* identification */ + uint32_t dload; /* decoded image load address */ + uint32_t dsize; /* decoded image size */ + uint32_t isize; /* image size in memory */ + uint32_t entry; /* program entry point */ + uint32_t nsize; /* encoded image size */ +}; +extern struct kgz_hdr kgz; /* header */ +extern uint8_t kgz_ndata[]; /* encoded image */ + +static const char *const msg[] = { + "done", + "invalid format", + "out of memory" +}; + +static const u_char *ip; /* input pointer */ +static u_char *op; /* output pointer */ + +static struct inflate infl; /* inflate() parameters */ + +static int decode(void); +static int input(void *); +static int output(void *, u_char *, u_long); + +/* + * Uncompress and boot a kernel. + */ +int +boot(int howto) +{ + int err; + + kgz_con = howto & RB_SERIAL ? KGZ_SIO : KGZ_CRT; + putstr("Uncompressing ... "); + err = decode(); + putstr(msg[err]); + putstr("\n"); + if (err) { + putstr("System halted"); + for (;;) + ; + } + return err; +} + +/* + * Interface with inflate() to uncompress the data. + */ +static int +decode(void) +{ + static u_char slide[GZ_WSIZE]; + int err; + + ip = kgz_ndata + KGZ_HEAD; + op = (u_char *)kgz.dload; + infl.gz_input = input; + infl.gz_output = output; + infl.gz_slide = slide; + err = inflate(&infl); + return err ? err == 3 ? E_MEM : E_FMT : 0; +} + +/* + * Read a byte. + */ +static int +input(void *dummy) +{ + if ((size_t)(ip - kgz_ndata) + KGZ_TAIL > kgz.nsize) + return GZ_EOF; + return *ip++; +} + +/* + * Write some bytes. + */ +static int +output(void *dummy, u_char * ptr, u_long len) +{ + if (op - (u_char *)kgz.dload + len > kgz.dsize) + return -1; + while (len--) + *op++ = *ptr++; + return 0; +} diff --git a/stand/i386/kgzldr/crt.s b/stand/i386/kgzldr/crt.s new file mode 100644 index 0000000..cfb479f --- /dev/null +++ b/stand/i386/kgzldr/crt.s @@ -0,0 +1,83 @@ +# +# Copyright (c) 1999 Global Technology Associates, Inc. +# 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 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. +# +# From: btx.s 1.10 1999/02/25 16:27:41 rnordier +# $FreeBSD$ +# + +# Screen defaults and assumptions. + + .set SCR_MAT,0x7 # Mode/attribute + .set SCR_COL,0x50 # Columns per row + .set SCR_ROW,0x19 # Rows per screen + +# BIOS Data Area locations. + + .set BDA_SCR,0x449 # Video mode + .set BDA_POS,0x450 # Cursor position + + .globl crt_putchr + +# void crt_putchr(int c) + +crt_putchr: movb 0x4(%esp,1),%al # Get character + pusha # Save + xorl %ecx,%ecx # Zero for loops + movb $SCR_MAT,%ah # Mode/attribute + movl $BDA_POS,%ebx # BDA pointer + movw (%ebx),%dx # Cursor position + movl $0xb8000,%edi # Regen buffer (color) + cmpb %ah,BDA_SCR-BDA_POS(%ebx) # Mono mode? + jne crt_putchr.1 # No + xorw %di,%di # Regen buffer (mono) +crt_putchr.1: cmpb $0xa,%al # New line? + je crt_putchr.2 # Yes + xchgl %eax,%ecx # Save char + movb $SCR_COL,%al # Columns per row + mulb %dh # * row position + addb %dl,%al # + column + adcb $0x0,%ah # position + shll %eax # * 2 + xchgl %eax,%ecx # Swap char, offset + movw %ax,(%edi,%ecx,1) # Write attr:char + incl %edx # Bump cursor + cmpb $SCR_COL,%dl # Beyond row? + jb crt_putchr.3 # No +crt_putchr.2: xorb %dl,%dl # Zero column + incb %dh # Bump row +crt_putchr.3: cmpb $SCR_ROW,%dh # Beyond screen? + jb crt_putchr.4 # No + leal 2*SCR_COL(%edi),%esi # New top line + movw $(SCR_ROW-1)*SCR_COL/2,%cx # Words to move + rep # Scroll + movsl # screen + movb $' ',%al # Space + movb $SCR_COL,%cl # Columns to clear + rep # Clear + stosw # line + movb $SCR_ROW-1,%dh # Bottom line +crt_putchr.4: movw %dx,(%ebx) # Update position + popa # Restore + ret # To caller diff --git a/stand/i386/kgzldr/kgzldr.h b/stand/i386/kgzldr/kgzldr.h new file mode 100644 index 0000000..5cd5b44 --- /dev/null +++ b/stand/i386/kgzldr/kgzldr.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 1999 Global Technology Associates, Inc. + * 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 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. + * + * $FreeBSD$ + */ + +#define KGZ_CRT 0x1 /* Video console */ +#define KGZ_SIO 0x2 /* Serial console */ + +extern int kgz_con; + +int boot(int); + +unsigned char *kzipmalloc(int); +void kzipfree(void *); +void putstr(const char *); + +void crt_putchr(int); +void sio_putchr(int); diff --git a/stand/i386/kgzldr/lib.c b/stand/i386/kgzldr/lib.c new file mode 100644 index 0000000..538875b --- /dev/null +++ b/stand/i386/kgzldr/lib.c @@ -0,0 +1,88 @@ +/*- + * Copyright (c) 1999 Global Technology Associates, Inc. + * 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 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <stddef.h> + +#include "kgzldr.h" + +#define MEMSIZ 0x8000 /* Memory pool size */ + +int kgz_con; /* Console control */ + +static size_t memtot; /* Memory allocated: bytes */ +static u_int memcnt; /* Memory allocated: blocks */ + +/* + * Library functions required by inflate(). + */ + +/* + * Allocate memory block. + */ +unsigned char * +kzipmalloc(int size) +{ + static u_char mem[MEMSIZ]; + void *ptr; + + if (memtot + size > MEMSIZ) + return NULL; + ptr = mem + memtot; + memtot += size; + memcnt++; + return ptr; +} + +/* + * Free allocated memory block. + */ +void +kzipfree(void *ptr) +{ + memcnt--; + if (!memcnt) + memtot = 0; +} + +/* + * Write a string to the console. + */ +void +putstr(const char *str) +{ + int c; + + while ((c = *str++)) { + if (kgz_con & KGZ_CRT) + crt_putchr(c); + if (kgz_con & KGZ_SIO) + sio_putchr(c); + } +} diff --git a/stand/i386/kgzldr/sio.s b/stand/i386/kgzldr/sio.s new file mode 100644 index 0000000..ff174eb --- /dev/null +++ b/stand/i386/kgzldr/sio.s @@ -0,0 +1,44 @@ +# +# Copyright (c) 1999 Global Technology Associates, Inc. +# 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 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. +# +# From: sio.s 1.3 1999/01/10 14:48:03 rnordier +# $FreeBSD$ +# + + .globl sio_putchr + +# void sio_putchr(int c) + +sio_putchr: movw $SIO_PRT+0x5,%dx # Line status reg + xor %ecx,%ecx # Timeout + movb $0x40,%ch # counter +sio_putchr.1: inb %dx,%al # Transmitter + testb $0x20,%al # buffer empty? + loopz sio_putchr.1 # No + jz sio_putchr.2 # If timeout + movb 0x4(%esp,1),%al # Get character + subb $0x5,%dl # Transmitter hold reg + outb %al,%dx # Write character +sio_putchr.2: ret # To caller diff --git a/stand/i386/kgzldr/start.s b/stand/i386/kgzldr/start.s new file mode 100644 index 0000000..550fa52 --- /dev/null +++ b/stand/i386/kgzldr/start.s @@ -0,0 +1,45 @@ +# +# Copyright (c) 1999 Global Technology Associates, Inc. +# 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 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. +# +# $FreeBSD$ +# + + .set entry,0x10 # kgz.entry + + .globl _start + +# C startup code for kgzldr. + +_start: cld # String ops inc + movl $_edata,%edi # Start of bss + movl $_end,%ecx # Compute + subl %edi,%ecx # size + xorl %eax,%eax # Zero + rep # Clear + stosb # bss + pushl 0x4(%esp) # Pass howto flags + call boot # Call C code + popl %ecx # Clear stack + jmp *kgz+entry # To loaded code diff --git a/stand/i386/libfirewire/Makefile b/stand/i386/libfirewire/Makefile new file mode 100644 index 0000000..d3f88c4 --- /dev/null +++ b/stand/i386/libfirewire/Makefile @@ -0,0 +1,20 @@ +# $FreeBSD$ + +.include <bsd.init.mk> + +LIB= firewire +INTERNALLIB= + +.PATH: ${SYSDIR}/dev/dcons ${SYSDIR}/dev/firewire +SRCS+= firewire.c fwohci.c dconsole.c +SRCS+= dcons.c fwcrom.c + +CFLAGS+= -D_BOOT + +CFLAGS+= -I${LDRSRC} +CFLAGS+= -I${BTXLIB} +CFLAGS+= -I${BOOTSRC}/i386/libi386 + +CFLAGS+= -Wformat -Wall + +.include <bsd.lib.mk> diff --git a/stand/i386/libfirewire/Makefile.depend b/stand/i386/libfirewire/Makefile.depend new file mode 100644 index 0000000..18be76b --- /dev/null +++ b/stand/i386/libfirewire/Makefile.depend @@ -0,0 +1,13 @@ +# $FreeBSD$ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + include \ + include/xlocale \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/stand/i386/libfirewire/dconsole.c b/stand/i386/libfirewire/dconsole.c new file mode 100644 index 0000000..1528faf --- /dev/null +++ b/stand/i386/libfirewire/dconsole.c @@ -0,0 +1,127 @@ +/*- + * Copyright (c) 2004 Hidetoshi Shimokawa + * + * 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 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stand.h> +#include <bootstrap.h> +#include <sys/param.h> +#include <btxv86.h> +#include <dev/dcons/dcons.h> + +void fw_enable(void); +void fw_poll(void); + +static void dconsole_probe(struct console *cp); +static int dconsole_init(int arg); +static void dconsole_putchar(int c); +static int dconsole_getchar(void); +static int dconsole_ischar(void); + +static int dcons_started = 0; + +#define DCONS_BUF_SIZE (64*1024) +static struct dcons_softc sc[DCONS_NPORT]; +uint32_t dcons_paddr; + +/* The buffer must be allocated in BSS becase: + * - The dcons driver in the kernel is initialized before VM/pmap is + * initialized, so that the buffer must be allocate in the region + * that is mapped at the very early boot state. + * - We expect identiy map only for regions before KERNLOAD + * (i386:4MB amd64:1MB). + * - It seems that heap in conventional memory(640KB) is not sufficient + * and we move it to high address as LOADER_SUPPORT_BZIP2. + * - BSS is placed in conventional memory. + */ +static char dcons_buffer[DCONS_BUF_SIZE + PAGE_SIZE]; + +struct console dconsole = { + "dcons", + "dumb console port", + 0, + dconsole_probe, + dconsole_init, + dconsole_putchar, + dconsole_getchar, + dconsole_ischar +}; + +#define DCONSOLE_AS_MULTI_CONSOLE 1 + +static void +dconsole_probe(struct console *cp) +{ + /* XXX check the BIOS equipment list? */ + cp->c_flags |= (C_PRESENTIN | C_PRESENTOUT); +#if DCONSOLE_AS_MULTI_CONSOLE + dconsole_init(0); + cp->c_flags |= (C_ACTIVEIN | C_ACTIVEOUT); +#endif +} + +static int +dconsole_init(int arg) +{ + char buf[16], *dbuf; + int size; + + if (dcons_started && arg == 0) + return 0; + dcons_started = 1; + + size = DCONS_BUF_SIZE; + dbuf = (char *)round_page((vm_offset_t)&dcons_buffer[0]); + dcons_paddr = VTOP(dbuf); + sprintf(buf, "0x%08x", dcons_paddr); + setenv("dcons.addr", buf, 1); + + dcons_init((struct dcons_buf *)dbuf, size, sc); + sprintf(buf, "%d", size); + setenv("dcons.size", buf, 1); + fw_enable(); + return(0); +} + +static void +dconsole_putchar(int c) +{ + dcons_putc(&sc[0], c); +} + +static int +dconsole_getchar(void) +{ + fw_poll(); + return (dcons_checkc(&sc[0])); +} + +static int +dconsole_ischar(void) +{ + fw_poll(); + return (dcons_ischar(&sc[0])); +} diff --git a/stand/i386/libfirewire/firewire.c b/stand/i386/libfirewire/firewire.c new file mode 100644 index 0000000..4840325 --- /dev/null +++ b/stand/i386/libfirewire/firewire.c @@ -0,0 +1,484 @@ +/*- + * Copyright (c) 2004 Hidetoshi Shimokawa <simokawa@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. + * + * 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * FireWire disk device handling. + * + */ + +#include <stand.h> + +#include <machine/bootinfo.h> + +#include <stdarg.h> + +#include <bootstrap.h> +#include <btxv86.h> +#include <libi386.h> +#include "fwohci.h" +#include <dev/dcons/dcons.h> + +/* XXX */ +#define BIT4x2(x,y) uint8_t y:4, x:4 +#define BIT16x2(x,y) uint32_t y:16, x:16 +#define _KERNEL +#include <dev/firewire/iec13213.h> + +extern uint32_t dcons_paddr; +extern struct console dconsole; + +struct crom_src_buf { + struct crom_src src; + struct crom_chunk root; + struct crom_chunk vendor; + struct crom_chunk hw; + /* for dcons */ + struct crom_chunk unit; + struct crom_chunk spec; + struct crom_chunk ver; +}; + +static int fw_init(void); +static int fw_strategy(void *devdata, int flag, daddr_t dblk, + size_t size, char *buf, size_t *rsize); +static int fw_open(struct open_file *f, ...); +static int fw_close(struct open_file *f); +static int fw_print(int verbose); +static void fw_cleanup(void); + +void fw_enable(void); + +struct devsw fwohci = { + "FW1394", /* 7 chars at most */ + DEVT_NET, + fw_init, + fw_strategy, + fw_open, + fw_close, + noioctl, + fw_print, + fw_cleanup +}; + +static struct fwohci_softc fwinfo[MAX_OHCI]; +static int fw_initialized = 0; + +static void +fw_probe(int index, struct fwohci_softc *sc) +{ + int err; + + sc->state = FWOHCI_STATE_INIT; + err = biospci_find_devclass( + 0x0c0010 /* Serial:FireWire:OHCI */, + index /* index */, + &sc->locator); + + if (err != 0) { + sc->state = FWOHCI_STATE_DEAD; + return; + } + + biospci_write_config(sc->locator, + 0x4 /* command */, + 0x6 /* enable bus master and memory mapped I/O */, + 1 /* word */); + + biospci_read_config(sc->locator, 0x00 /*devid*/, 2 /*dword*/, + &sc->devid); + biospci_read_config(sc->locator, 0x10 /*base_addr*/, 2 /*dword*/, + &sc->base_addr); + + sc->handle = (uint32_t)PTOV(sc->base_addr); + sc->bus_id = OREAD(sc, OHCI_BUS_ID); + + return; +} + +static int +fw_init(void) +{ + int i, avail; + struct fwohci_softc *sc; + + if (fw_initialized) + return (0); + + avail = 0; + for (i = 0; i < MAX_OHCI; i ++) { + sc = &fwinfo[i]; + fw_probe(i, sc); + if (sc->state == FWOHCI_STATE_DEAD) + break; + avail ++; + break; + } + fw_initialized = 1; + + return (0); +} + + +/* + * Print information about OHCI chips + */ +static int +fw_print(int verbose) +{ + char line[80]; + int i, ret = 0; + struct fwohci_softc *sc; + + printf("%s devices:", fwohci.dv_name); + if ((ret = pager_output("\n")) != 0) + return (ret); + + for (i = 0; i < MAX_OHCI; i ++) { + sc = &fwinfo[i]; + if (sc->state == FWOHCI_STATE_DEAD) + break; + snprintf(line, sizeof(line), "%d: locator=0x%04x devid=0x%08x" + " base_addr=0x%08x handle=0x%08x bus_id=0x%08x\n", + i, sc->locator, sc->devid, + sc->base_addr, sc->handle, sc->bus_id); + ret = pager_output(line); + if (ret != 0) + break; + } + return (ret); +} + +static int +fw_open(struct open_file *f, ...) +{ +#if 0 + va_list ap; + struct i386_devdesc *dev; + struct open_disk *od; + int error; + + va_start(ap, f); + dev = va_arg(ap, struct i386_devdesc *); + va_end(ap); +#endif + + return (ENXIO); +} + +static int +fw_close(struct open_file *f) +{ + return (0); +} + +static void +fw_cleanup() +{ + struct dcons_buf *db; + + /* invalidate dcons buffer */ + if (dcons_paddr) { + db = (struct dcons_buf *)PTOV(dcons_paddr); + db->magic = 0; + } +} + +static int +fw_strategy(void *devdata, int rw, daddr_t dblk, size_t size, + char *buf, size_t *rsize) +{ + return (EIO); +} + +static void +fw_init_crom(struct fwohci_softc *sc) +{ + struct crom_src *src; + + printf("fw_init_crom\n"); + sc->crom_src_buf = (struct crom_src_buf *) + malloc(sizeof(struct crom_src_buf)); + if (sc->crom_src_buf == NULL) + return; + + src = &sc->crom_src_buf->src; + bzero(src, sizeof(struct crom_src)); + + /* BUS info sample */ + src->hdr.info_len = 4; + + src->businfo.bus_name = CSR_BUS_NAME_IEEE1394; + + src->businfo.irmc = 1; + src->businfo.cmc = 1; + src->businfo.isc = 1; + src->businfo.bmc = 1; + src->businfo.pmc = 0; + src->businfo.cyc_clk_acc = 100; + src->businfo.max_rec = sc->maxrec; + src->businfo.max_rom = MAXROM_4; +#define FW_GENERATION_CHANGEABLE 2 + src->businfo.generation = FW_GENERATION_CHANGEABLE; + src->businfo.link_spd = sc->speed; + + src->businfo.eui64.hi = sc->eui.hi; + src->businfo.eui64.lo = sc->eui.lo; + + STAILQ_INIT(&src->chunk_list); + + sc->crom_src = src; + sc->crom_root = &sc->crom_src_buf->root; +} + +static void +fw_reset_crom(struct fwohci_softc *sc) +{ + struct crom_src_buf *buf; + struct crom_src *src; + struct crom_chunk *root; + + printf("fw_reset\n"); + if (sc->crom_src_buf == NULL) + fw_init_crom(sc); + + buf = sc->crom_src_buf; + src = sc->crom_src; + root = sc->crom_root; + + STAILQ_INIT(&src->chunk_list); + + bzero(root, sizeof(struct crom_chunk)); + crom_add_chunk(src, NULL, root, 0); + crom_add_entry(root, CSRKEY_NCAP, 0x0083c0); /* XXX */ + /* private company_id */ + crom_add_entry(root, CSRKEY_VENDOR, CSRVAL_VENDOR_PRIVATE); +#ifdef __DragonFly__ + crom_add_simple_text(src, root, &buf->vendor, "DragonFly Project"); +#else + crom_add_simple_text(src, root, &buf->vendor, "FreeBSD Project"); +#endif +} + + +#define ADDR_HI(x) (((x) >> 24) & 0xffffff) +#define ADDR_LO(x) ((x) & 0xffffff) + +static void +dcons_crom(struct fwohci_softc *sc) +{ + struct crom_src_buf *buf; + struct crom_src *src; + struct crom_chunk *root; + + buf = sc->crom_src_buf; + src = sc->crom_src; + root = sc->crom_root; + + bzero(&buf->unit, sizeof(struct crom_chunk)); + + crom_add_chunk(src, root, &buf->unit, CROM_UDIR); + crom_add_entry(&buf->unit, CSRKEY_SPEC, CSRVAL_VENDOR_PRIVATE); + crom_add_simple_text(src, &buf->unit, &buf->spec, "FreeBSD"); + crom_add_entry(&buf->unit, CSRKEY_VER, DCONS_CSR_VAL_VER); + crom_add_simple_text(src, &buf->unit, &buf->ver, "dcons"); + crom_add_entry(&buf->unit, DCONS_CSR_KEY_HI, ADDR_HI(dcons_paddr)); + crom_add_entry(&buf->unit, DCONS_CSR_KEY_LO, ADDR_LO(dcons_paddr)); +} + +void +fw_crom(struct fwohci_softc *sc) +{ + struct crom_src *src; + void *newrom; + + fw_reset_crom(sc); + dcons_crom(sc); + + newrom = malloc(CROMSIZE); + src = &sc->crom_src_buf->src; + crom_load(src, (uint32_t *)newrom, CROMSIZE); + if (bcmp(newrom, sc->config_rom, CROMSIZE) != 0) { + /* Bump generation and reload. */ + src->businfo.generation++; + + /* Handle generation count wraps. */ + if (src->businfo.generation < 2) + src->businfo.generation = 2; + + /* Recalculate CRC to account for generation change. */ + crom_load(src, (uint32_t *)newrom, CROMSIZE); + bcopy(newrom, (void *)sc->config_rom, CROMSIZE); + } + free(newrom); +} + +static int +fw_busreset(struct fwohci_softc *sc) +{ + int count; + + if (sc->state < FWOHCI_STATE_ENABLED) { + printf("fwohci not enabled\n"); + return(CMD_OK); + } + fw_crom(sc); + fwohci_ibr(sc); + count = 0; + while (sc->state< FWOHCI_STATE_NORMAL) { + fwohci_poll(sc); + count ++; + if (count > 1000) { + printf("give up to wait bus initialize\n"); + return (-1); + } + } + printf("poll count = %d\n", count); + return (0); +} + +void +fw_enable(void) +{ + struct fwohci_softc *sc; + int i; + + if (fw_initialized == 0) + fw_init(); + + for (i = 0; i < MAX_OHCI; i ++) { + sc = &fwinfo[i]; + if (sc->state != FWOHCI_STATE_INIT) + break; + + sc->config_rom = (uint32_t *) + (((uint32_t)sc->config_rom_buf + + (CROMSIZE - 1)) & ~(CROMSIZE - 1)); +#if 0 + printf("configrom: %08p %08p\n", + sc->config_rom_buf, sc->config_rom); +#endif + if (fwohci_init(sc, 0) == 0) { + sc->state = FWOHCI_STATE_ENABLED; + fw_busreset(sc); + } else + sc->state = FWOHCI_STATE_DEAD; + } +} + +void +fw_poll(void) +{ + struct fwohci_softc *sc; + int i; + + if (fw_initialized == 0) + return; + + for (i = 0; i < MAX_OHCI; i ++) { + sc = &fwinfo[i]; + if (sc->state < FWOHCI_STATE_ENABLED) + break; + fwohci_poll(sc); + } +} + +#if 0 /* for debug */ +static int +fw_busreset_cmd(int argc, char *argv[]) +{ + struct fwohci_softc *sc; + int i; + + for (i = 0; i < MAX_OHCI; i ++) { + sc = &fwinfo[i]; + if (sc->state < FWOHCI_STATE_INIT) + break; + fw_busreset(sc); + } + return(CMD_OK); +} + +static int +fw_poll_cmd(int argc, char *argv[]) +{ + fw_poll(); + return(CMD_OK); +} + +static int +fw_enable_cmd(int argc, char *argv[]) +{ + fw_print(0); + fw_enable(); + return(CMD_OK); +} + + +static int +dcons_enable(int argc, char *argv[]) +{ + dconsole.c_init(0); + fw_enable(); + dconsole.c_flags |= C_ACTIVEIN | C_ACTIVEOUT; + return(CMD_OK); +} + +static int +dcons_read(int argc, char *argv[]) +{ + char c; + while (dconsole.c_ready()) { + c = dconsole.c_in(); + printf("%c", c); + } + printf("\r\n"); + return(CMD_OK); +} + +static int +dcons_write(int argc, char *argv[]) +{ + int len, i; + if (argc < 2) + return(CMD_OK); + + len = strlen(argv[1]); + for (i = 0; i < len; i ++) + dconsole.c_out(argv[1][i]); + dconsole.c_out('\r'); + dconsole.c_out('\n'); + return(CMD_OK); +} +COMMAND_SET(firewire, "firewire", "enable firewire", fw_enable_cmd); +COMMAND_SET(fwbusreset, "fwbusreset", "firewire busreset", fw_busreset_cmd); +COMMAND_SET(fwpoll, "fwpoll", "firewire poll", fw_poll_cmd); +COMMAND_SET(dcons, "dcons", "enable dcons", dcons_enable); +COMMAND_SET(dread, "dread", "read from dcons", dcons_read); +COMMAND_SET(dwrite, "dwrite", "write to dcons", dcons_write); +#endif diff --git a/stand/i386/libfirewire/fwohci.c b/stand/i386/libfirewire/fwohci.c new file mode 100644 index 0000000..567abcf --- /dev/null +++ b/stand/i386/libfirewire/fwohci.c @@ -0,0 +1,479 @@ +/* + * Copyright (c) 2003 Hidetoshi Shimokawa + * Copyright (c) 1998-2002 Katsushi Kobayashi and Hidetoshi Shimokawa + * 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. All advertising materials mentioning features or use of this software + * must display the acknowledgement as bellow: + * + * This product includes software developed by K. Kobayashi and H. Shimokawa + * + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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 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. + * + * $FreeBSD$ + * + */ + +#include <stand.h> +#include <btxv86.h> +#include <bootstrap.h> + +#include "fwohci.h" +#include "fwohcireg.h" +#include <dev/firewire/firewire_phy.h> + +static uint32_t fwphy_wrdata ( struct fwohci_softc *, uint32_t, uint32_t); +static uint32_t fwphy_rddata ( struct fwohci_softc *, uint32_t); +int firewire_debug=0; + +#if 0 +#define device_printf(a, x, ...) printf("FW1394: " x, ## __VA_ARGS__) +#else +#define device_printf(a, x, ...) +#endif + +#define device_t int +#define DELAY(x) delay(x) + +#define MAX_SPEED 3 +#define MAXREC(x) (2 << (x)) +char *linkspeed[] = { + "S100", "S200", "S400", "S800", + "S1600", "S3200", "undef", "undef" +}; + +#define FW_EUI64_BYTE(eui, x) \ + ((((x)<4)? \ + ((eui)->hi >> (8*(3-(x)))): \ + ((eui)->lo >> (8*(7-(x)))) \ + ) & 0xff) + +/* + * Communication with PHY device + */ +static uint32_t +fwphy_wrdata( struct fwohci_softc *sc, uint32_t addr, uint32_t data) +{ + uint32_t fun; + + addr &= 0xf; + data &= 0xff; + + fun = (PHYDEV_WRCMD | (addr << PHYDEV_REGADDR) | (data << PHYDEV_WRDATA)); + OWRITE(sc, OHCI_PHYACCESS, fun); + DELAY(100); + + return(fwphy_rddata( sc, addr)); +} + +static uint32_t +fwphy_rddata(struct fwohci_softc *sc, u_int addr) +{ + uint32_t fun, stat; + u_int i, retry = 0; + + addr &= 0xf; +#define MAX_RETRY 100 +again: + OWRITE(sc, FWOHCI_INTSTATCLR, OHCI_INT_REG_FAIL); + fun = PHYDEV_RDCMD | (addr << PHYDEV_REGADDR); + OWRITE(sc, OHCI_PHYACCESS, fun); + for ( i = 0 ; i < MAX_RETRY ; i ++ ){ + fun = OREAD(sc, OHCI_PHYACCESS); + if ((fun & PHYDEV_RDCMD) == 0 && (fun & PHYDEV_RDDONE) != 0) + break; + DELAY(100); + } + if(i >= MAX_RETRY) { + if (firewire_debug) + device_printf(sc->fc.dev, "phy read failed(1).\n"); + if (++retry < MAX_RETRY) { + DELAY(100); + goto again; + } + } + /* Make sure that SCLK is started */ + stat = OREAD(sc, FWOHCI_INTSTAT); + if ((stat & OHCI_INT_REG_FAIL) != 0 || + ((fun >> PHYDEV_REGADDR) & 0xf) != addr) { + if (firewire_debug) + device_printf(sc->fc.dev, "phy read failed(2).\n"); + if (++retry < MAX_RETRY) { + DELAY(100); + goto again; + } + } + if (firewire_debug || retry >= MAX_RETRY) + device_printf(sc->fc.dev, + "fwphy_rddata: 0x%x loop=%d, retry=%d\n", addr, i, retry); +#undef MAX_RETRY + return((fun >> PHYDEV_RDDATA )& 0xff); +} + + +static int +fwohci_probe_phy(struct fwohci_softc *sc, device_t dev) +{ + uint32_t reg, reg2; + int e1394a = 1; + int nport, speed; +/* + * probe PHY parameters + * 0. to prove PHY version, whether compliance of 1394a. + * 1. to probe maximum speed supported by the PHY and + * number of port supported by core-logic. + * It is not actually available port on your PC . + */ + OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_LPS); + DELAY(500); + + reg = fwphy_rddata(sc, FW_PHY_SPD_REG); + + if((reg >> 5) != 7 ){ + nport = reg & FW_PHY_NP; + speed = reg & FW_PHY_SPD >> 6; + if (speed > MAX_SPEED) { + device_printf(dev, "invalid speed %d (fixed to %d).\n", + speed, MAX_SPEED); + speed = MAX_SPEED; + } + device_printf(dev, + "Phy 1394 only %s, %d ports.\n", + linkspeed[speed], nport); + }else{ + reg2 = fwphy_rddata(sc, FW_PHY_ESPD_REG); + nport = reg & FW_PHY_NP; + speed = (reg2 & FW_PHY_ESPD) >> 5; + if (speed > MAX_SPEED) { + device_printf(dev, "invalid speed %d (fixed to %d).\n", + speed, MAX_SPEED); + speed = MAX_SPEED; + } + device_printf(dev, + "Phy 1394a available %s, %d ports.\n", + linkspeed[speed], nport); + + /* check programPhyEnable */ + reg2 = fwphy_rddata(sc, 5); +#if 0 + if (e1394a && (OREAD(sc, OHCI_HCCCTL) & OHCI_HCC_PRPHY)) { +#else /* XXX force to enable 1394a */ + if (e1394a) { +#endif + if (firewire_debug) + device_printf(dev, + "Enable 1394a Enhancements\n"); + /* enable EAA EMC */ + reg2 |= 0x03; + /* set aPhyEnhanceEnable */ + OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_PHYEN); + OWRITE(sc, OHCI_HCCCTLCLR, OHCI_HCC_PRPHY); + } else { + /* for safe */ + reg2 &= ~0x83; + } + reg2 = fwphy_wrdata(sc, 5, reg2); + } + sc->speed = speed; + + reg = fwphy_rddata(sc, FW_PHY_SPD_REG); + if((reg >> 5) == 7 ){ + reg = fwphy_rddata(sc, 4); + reg |= 1 << 6; + fwphy_wrdata(sc, 4, reg); + reg = fwphy_rddata(sc, 4); + } + return 0; +} + + +void +fwohci_reset(struct fwohci_softc *sc, device_t dev) +{ + int i, max_rec, speed; + uint32_t reg, reg2; + + /* Disable interrupts */ + OWRITE(sc, FWOHCI_INTMASKCLR, ~0); + + /* FLUSH FIFO and reset Transmitter/Receiver */ + OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_RESET); + if (firewire_debug) + device_printf(dev, "resetting OHCI..."); + i = 0; + while(OREAD(sc, OHCI_HCCCTL) & OHCI_HCC_RESET) { + if (i++ > 100) break; + DELAY(1000); + } + if (firewire_debug) + printf("done (loop=%d)\n", i); + + /* Probe phy */ + fwohci_probe_phy(sc, dev); + + /* Probe link */ + reg = OREAD(sc, OHCI_BUS_OPT); + reg2 = reg | OHCI_BUSFNC; + max_rec = (reg & 0x0000f000) >> 12; + speed = (reg & 0x00000007); + device_printf(dev, "Link %s, max_rec %d bytes.\n", + linkspeed[speed], MAXREC(max_rec)); + /* XXX fix max_rec */ + sc->maxrec = sc->speed + 8; + if (max_rec != sc->maxrec) { + reg2 = (reg2 & 0xffff0fff) | (sc->maxrec << 12); + device_printf(dev, "max_rec %d -> %d\n", + MAXREC(max_rec), MAXREC(sc->maxrec)); + } + if (firewire_debug) + device_printf(dev, "BUS_OPT 0x%x -> 0x%x\n", reg, reg2); + OWRITE(sc, OHCI_BUS_OPT, reg2); + + /* Initialize registers */ + OWRITE(sc, OHCI_CROMHDR, sc->config_rom[0]); + OWRITE(sc, OHCI_CROMPTR, VTOP(sc->config_rom)); +#if 0 + OWRITE(sc, OHCI_SID_BUF, sc->sid_dma.bus_addr); +#endif + OWRITE(sc, OHCI_HCCCTLCLR, OHCI_HCC_BIGEND); + OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_POSTWR); +#if 0 + OWRITE(sc, OHCI_LNKCTL, OHCI_CNTL_SID); +#endif + + /* Enable link */ + OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_LINKEN); +} + +int +fwohci_init(struct fwohci_softc *sc, device_t dev) +{ + int i, mver; + uint32_t reg; + uint8_t ui[8]; + +/* OHCI version */ + reg = OREAD(sc, OHCI_VERSION); + mver = (reg >> 16) & 0xff; + device_printf(dev, "OHCI version %x.%x (ROM=%d)\n", + mver, reg & 0xff, (reg>>24) & 1); + if (mver < 1 || mver > 9) { + device_printf(dev, "invalid OHCI version\n"); + return (ENXIO); + } + +/* Available Isochronous DMA channel probe */ + OWRITE(sc, OHCI_IT_MASK, 0xffffffff); + OWRITE(sc, OHCI_IR_MASK, 0xffffffff); + reg = OREAD(sc, OHCI_IT_MASK) & OREAD(sc, OHCI_IR_MASK); + OWRITE(sc, OHCI_IT_MASKCLR, 0xffffffff); + OWRITE(sc, OHCI_IR_MASKCLR, 0xffffffff); + for (i = 0; i < 0x20; i++) + if ((reg & (1 << i)) == 0) + break; + device_printf(dev, "No. of Isochronous channels is %d.\n", i); + if (i == 0) + return (ENXIO); + +#if 0 +/* SID receive buffer must align 2^11 */ +#define OHCI_SIDSIZE (1 << 11) + sc->sid_buf = fwdma_malloc(&sc->fc, OHCI_SIDSIZE, OHCI_SIDSIZE, + &sc->sid_dma, BUS_DMA_WAITOK); + if (sc->sid_buf == NULL) { + device_printf(dev, "sid_buf alloc failed."); + return ENOMEM; + } +#endif + + sc->eui.hi = OREAD(sc, FWOHCIGUID_H); + sc->eui.lo = OREAD(sc, FWOHCIGUID_L); + for( i = 0 ; i < 8 ; i ++) + ui[i] = FW_EUI64_BYTE(&sc->eui,i); + device_printf(dev, "EUI64 %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", + ui[0], ui[1], ui[2], ui[3], ui[4], ui[5], ui[6], ui[7]); + fwohci_reset(sc, dev); + + return 0; +} + +void +fwohci_ibr(struct fwohci_softc *sc) +{ + uint32_t fun; + + device_printf(sc->dev, "Initiate bus reset\n"); + + /* + * Make sure our cached values from the config rom are + * initialised. + */ + OWRITE(sc, OHCI_CROMHDR, ntohl(sc->config_rom[0])); + OWRITE(sc, OHCI_BUS_OPT, ntohl(sc->config_rom[2])); + + /* + * Set root hold-off bit so that non cyclemaster capable node + * shouldn't became the root node. + */ +#if 1 + fun = fwphy_rddata(sc, FW_PHY_IBR_REG); + fun |= FW_PHY_IBR; + fun = fwphy_wrdata(sc, FW_PHY_IBR_REG, fun); +#else /* Short bus reset */ + fun = fwphy_rddata(sc, FW_PHY_ISBR_REG); + fun |= FW_PHY_ISBR; + fun = fwphy_wrdata(sc, FW_PHY_ISBR_REG, fun); +#endif +} + + +void +fwohci_sid(struct fwohci_softc *sc) +{ + uint32_t node_id; + int plen; + + node_id = OREAD(sc, FWOHCI_NODEID); + if (!(node_id & OHCI_NODE_VALID)) { +#if 0 + printf("Bus reset failure\n"); +#endif + return; + } + + /* Enable bus reset interrupt */ + OWRITE(sc, FWOHCI_INTMASK, OHCI_INT_PHY_BUS_R); + /* Allow async. request to us */ + OWRITE(sc, OHCI_AREQHI, 1 << 31); + /* XXX insecure ?? */ + OWRITE(sc, OHCI_PREQHI, 0x7fffffff); + OWRITE(sc, OHCI_PREQLO, 0xffffffff); + OWRITE(sc, OHCI_PREQUPPER, 0x10000); + /* Set ATRetries register */ + OWRITE(sc, OHCI_ATRETRY, 1<<(13+16) | 0xfff); +/* +** Checking whether the node is root or not. If root, turn on +** cycle master. +*/ + plen = OREAD(sc, OHCI_SID_CNT); + device_printf(fc->dev, "node_id=0x%08x, gen=%d, ", + node_id, (plen >> 16) & 0xff); + if (node_id & OHCI_NODE_ROOT) { + device_printf(sc->dev, "CYCLEMASTER mode\n"); + OWRITE(sc, OHCI_LNKCTL, + OHCI_CNTL_CYCMTR | OHCI_CNTL_CYCTIMER); + } else { + device_printf(sc->dev, "non CYCLEMASTER mode\n"); + OWRITE(sc, OHCI_LNKCTLCLR, OHCI_CNTL_CYCMTR); + OWRITE(sc, OHCI_LNKCTL, OHCI_CNTL_CYCTIMER); + } + if (plen & OHCI_SID_ERR) { + device_printf(fc->dev, "SID Error\n"); + return; + } + device_printf(sc->dev, "bus reset phase done\n"); + sc->state = FWOHCI_STATE_NORMAL; +} + +static void +fwohci_intr_body(struct fwohci_softc *sc, uint32_t stat, int count) +{ +#undef OHCI_DEBUG +#ifdef OHCI_DEBUG +#if 0 + if(stat & OREAD(sc, FWOHCI_INTMASK)) +#else + if (1) +#endif + device_printf(fc->dev, "INTERRUPT < %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s> 0x%08x, 0x%08x\n", + stat & OHCI_INT_EN ? "DMA_EN ":"", + stat & OHCI_INT_PHY_REG ? "PHY_REG ":"", + stat & OHCI_INT_CYC_LONG ? "CYC_LONG ":"", + stat & OHCI_INT_ERR ? "INT_ERR ":"", + stat & OHCI_INT_CYC_ERR ? "CYC_ERR ":"", + stat & OHCI_INT_CYC_LOST ? "CYC_LOST ":"", + stat & OHCI_INT_CYC_64SECOND ? "CYC_64SECOND ":"", + stat & OHCI_INT_CYC_START ? "CYC_START ":"", + stat & OHCI_INT_PHY_INT ? "PHY_INT ":"", + stat & OHCI_INT_PHY_BUS_R ? "BUS_RESET ":"", + stat & OHCI_INT_PHY_SID ? "SID ":"", + stat & OHCI_INT_LR_ERR ? "DMA_LR_ERR ":"", + stat & OHCI_INT_PW_ERR ? "DMA_PW_ERR ":"", + stat & OHCI_INT_DMA_IR ? "DMA_IR ":"", + stat & OHCI_INT_DMA_IT ? "DMA_IT " :"", + stat & OHCI_INT_DMA_PRRS ? "DMA_PRRS " :"", + stat & OHCI_INT_DMA_PRRQ ? "DMA_PRRQ " :"", + stat & OHCI_INT_DMA_ARRS ? "DMA_ARRS " :"", + stat & OHCI_INT_DMA_ARRQ ? "DMA_ARRQ " :"", + stat & OHCI_INT_DMA_ATRS ? "DMA_ATRS " :"", + stat & OHCI_INT_DMA_ATRQ ? "DMA_ATRQ " :"", + stat, OREAD(sc, FWOHCI_INTMASK) + ); +#endif +/* Bus reset */ + if(stat & OHCI_INT_PHY_BUS_R ){ + device_printf(fc->dev, "BUS reset\n"); + if (sc->state == FWOHCI_STATE_BUSRESET) + goto busresetout; + sc->state = FWOHCI_STATE_BUSRESET; + /* Disable bus reset interrupt until sid recv. */ + OWRITE(sc, FWOHCI_INTMASKCLR, OHCI_INT_PHY_BUS_R); + + OWRITE(sc, FWOHCI_INTMASKCLR, OHCI_INT_CYC_LOST); + OWRITE(sc, OHCI_LNKCTLCLR, OHCI_CNTL_CYCSRC); + + OWRITE(sc, OHCI_CROMHDR, ntohl(sc->config_rom[0])); + OWRITE(sc, OHCI_BUS_OPT, ntohl(sc->config_rom[2])); + } else if (sc->state == FWOHCI_STATE_BUSRESET) { + fwohci_sid(sc); + } +busresetout: + return; +} + +static uint32_t +fwochi_check_stat(struct fwohci_softc *sc) +{ + uint32_t stat; + + stat = OREAD(sc, FWOHCI_INTSTAT); + if (stat == 0xffffffff) { + device_printf(sc->fc.dev, + "device physically ejected?\n"); + return(stat); + } + if (stat) + OWRITE(sc, FWOHCI_INTSTATCLR, stat); + return(stat); +} + +void +fwohci_poll(struct fwohci_softc *sc) +{ + uint32_t stat; + + stat = fwochi_check_stat(sc); + if (stat != 0xffffffff) + fwohci_intr_body(sc, stat, 1); +} diff --git a/stand/i386/libfirewire/fwohci.h b/stand/i386/libfirewire/fwohci.h new file mode 100644 index 0000000..4a93220 --- /dev/null +++ b/stand/i386/libfirewire/fwohci.h @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2007 Hidetoshi Shimokawa + * 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. All advertising materials mentioning features or use of this software + * must display the acknowledgement as bellow: + * + * This product includes software developed by K. Kobayashi and H. Shimokawa + * + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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 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. + * + * $FreeBSD$ + * + */ + +#define MAX_OHCI 5 +#define CROMSIZE 0x400 + +struct fw_eui64 { + uint32_t hi, lo; +}; + +struct fwohci_softc { + uint32_t locator; + uint32_t devid; + uint32_t base_addr; + uint32_t bus_id; + uint32_t handle; + int32_t state; + struct crom_src_buf *crom_src_buf; + struct crom_src *crom_src; + struct crom_chunk *crom_root; + struct fw_eui64 eui; + int speed; + int maxrec; + uint32_t *config_rom; + char config_rom_buf[CROMSIZE*2]; /* double size for alignment */ +}; + +int fwohci_init(struct fwohci_softc *, int); +void fwohci_ibr(struct fwohci_softc *); +void fwohci_poll(struct fwohci_softc *); + +#define FWOHCI_STATE_DEAD (-1) +#define FWOHCI_STATE_INIT 0 +#define FWOHCI_STATE_ENABLED 1 +#define FWOHCI_STATE_BUSRESET 2 +#define FWOHCI_STATE_NORMAL 3 + +#define OREAD(f, o) (*(volatile uint32_t *)((f)->handle + (o))) +#define OWRITE(f, o, v) (*(volatile uint32_t *)((f)->handle + (o)) = (v)) + +#define OHCI_VERSION 0x00 +#define OHCI_ATRETRY 0x08 +#define OHCI_CROMHDR 0x18 +#define OHCI_BUS_ID 0x1c +#define OHCI_BUS_OPT 0x20 +#define OHCI_BUSIRMC (1U << 31) +#define OHCI_BUSCMC (1 << 30) +#define OHCI_BUSISC (1 << 29) +#define OHCI_BUSBMC (1 << 28) +#define OHCI_BUSPMC (1 << 27) +#define OHCI_BUSFNC OHCI_BUSIRMC | OHCI_BUSCMC | OHCI_BUSISC |\ + OHCI_BUSBMC | OHCI_BUSPMC + +#define OHCI_EUID_HI 0x24 +#define OHCI_EUID_LO 0x28 + +#define OHCI_CROMPTR 0x34 +#define OHCI_HCCCTL 0x50 +#define OHCI_HCCCTLCLR 0x54 +#define OHCI_AREQHI 0x100 +#define OHCI_AREQHICLR 0x104 +#define OHCI_AREQLO 0x108 +#define OHCI_AREQLOCLR 0x10c +#define OHCI_PREQHI 0x110 +#define OHCI_PREQHICLR 0x114 +#define OHCI_PREQLO 0x118 +#define OHCI_PREQLOCLR 0x11c +#define OHCI_PREQUPPER 0x120 + +#define OHCI_SID_BUF 0x64 +#define OHCI_SID_CNT 0x68 +#define OHCI_SID_ERR (1U << 31) +#define OHCI_SID_CNT_MASK 0xffc + +#define OHCI_IT_STAT 0x90 +#define OHCI_IT_STATCLR 0x94 +#define OHCI_IT_MASK 0x98 +#define OHCI_IT_MASKCLR 0x9c + +#define OHCI_IR_STAT 0xa0 +#define OHCI_IR_STATCLR 0xa4 +#define OHCI_IR_MASK 0xa8 +#define OHCI_IR_MASKCLR 0xac + +#define OHCI_LNKCTL 0xe0 +#define OHCI_LNKCTLCLR 0xe4 + +#define OHCI_PHYACCESS 0xec +#define OHCI_CYCLETIMER 0xf0 + +#define OHCI_DMACTL(off) (off) +#define OHCI_DMACTLCLR(off) (off + 4) +#define OHCI_DMACMD(off) (off + 0xc) +#define OHCI_DMAMATCH(off) (off + 0x10) + +#define OHCI_ATQOFF 0x180 +#define OHCI_ATQCTL OHCI_ATQOFF +#define OHCI_ATQCTLCLR (OHCI_ATQOFF + 4) +#define OHCI_ATQCMD (OHCI_ATQOFF + 0xc) +#define OHCI_ATQMATCH (OHCI_ATQOFF + 0x10) + +#define OHCI_ATSOFF 0x1a0 +#define OHCI_ATSCTL OHCI_ATSOFF +#define OHCI_ATSCTLCLR (OHCI_ATSOFF + 4) +#define OHCI_ATSCMD (OHCI_ATSOFF + 0xc) +#define OHCI_ATSMATCH (OHCI_ATSOFF + 0x10) + +#define OHCI_ARQOFF 0x1c0 +#define OHCI_ARQCTL OHCI_ARQOFF +#define OHCI_ARQCTLCLR (OHCI_ARQOFF + 4) +#define OHCI_ARQCMD (OHCI_ARQOFF + 0xc) +#define OHCI_ARQMATCH (OHCI_ARQOFF + 0x10) + +#define OHCI_ARSOFF 0x1e0 +#define OHCI_ARSCTL OHCI_ARSOFF +#define OHCI_ARSCTLCLR (OHCI_ARSOFF + 4) +#define OHCI_ARSCMD (OHCI_ARSOFF + 0xc) +#define OHCI_ARSMATCH (OHCI_ARSOFF + 0x10) + +#define OHCI_ITOFF(CH) (0x200 + 0x10 * (CH)) +#define OHCI_ITCTL(CH) (OHCI_ITOFF(CH)) +#define OHCI_ITCTLCLR(CH) (OHCI_ITOFF(CH) + 4) +#define OHCI_ITCMD(CH) (OHCI_ITOFF(CH) + 0xc) + +#define OHCI_IROFF(CH) (0x400 + 0x20 * (CH)) +#define OHCI_IRCTL(CH) (OHCI_IROFF(CH)) +#define OHCI_IRCTLCLR(CH) (OHCI_IROFF(CH) + 4) +#define OHCI_IRCMD(CH) (OHCI_IROFF(CH) + 0xc) +#define OHCI_IRMATCH(CH) (OHCI_IROFF(CH) + 0x10) diff --git a/stand/i386/libfirewire/fwohcireg.h b/stand/i386/libfirewire/fwohcireg.h new file mode 100644 index 0000000..d57870c --- /dev/null +++ b/stand/i386/libfirewire/fwohcireg.h @@ -0,0 +1,369 @@ +/* + * Copyright (c) 2003 Hidetoshi Shimokawa + * Copyright (c) 1998-2002 Katsushi Kobayashi and Hidetoshi Shimokawa + * 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. All advertising materials mentioning features or use of this software + * must display the acknowledgement as bellow: + * + * This product includes software developed by K. Kobayashi and H. Shimokawa + * + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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 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. + * + * $FreeBSD$ + * + */ +#define PCI_CBMEM PCIR_BAR(0) + +#define FW_VENDORID_NATSEMI 0x100B +#define FW_VENDORID_NEC 0x1033 +#define FW_VENDORID_SIS 0x1039 +#define FW_VENDORID_TI 0x104c +#define FW_VENDORID_SONY 0x104d +#define FW_VENDORID_VIA 0x1106 +#define FW_VENDORID_RICOH 0x1180 +#define FW_VENDORID_APPLE 0x106b +#define FW_VENDORID_LUCENT 0x11c1 +#define FW_VENDORID_INTEL 0x8086 +#define FW_VENDORID_ADAPTEC 0x9004 + +#define FW_DEVICE_CS4210 (0x000f << 16) +#define FW_DEVICE_UPD861 (0x0063 << 16) +#define FW_DEVICE_UPD871 (0x00ce << 16) +#define FW_DEVICE_UPD72870 (0x00cd << 16) +#define FW_DEVICE_UPD72873 (0x00e7 << 16) +#define FW_DEVICE_UPD72874 (0x00f2 << 16) +#define FW_DEVICE_TITSB22 (0x8009 << 16) +#define FW_DEVICE_TITSB23 (0x8019 << 16) +#define FW_DEVICE_TITSB26 (0x8020 << 16) +#define FW_DEVICE_TITSB43 (0x8021 << 16) +#define FW_DEVICE_TITSB43A (0x8023 << 16) +#define FW_DEVICE_TITSB43AB23 (0x8024 << 16) +#define FW_DEVICE_TITSB82AA2 (0x8025 << 16) +#define FW_DEVICE_TITSB43AB21 (0x8026 << 16) +#define FW_DEVICE_TIPCI4410A (0x8017 << 16) +#define FW_DEVICE_TIPCI4450 (0x8011 << 16) +#define FW_DEVICE_TIPCI4451 (0x8027 << 16) +#define FW_DEVICE_CXD1947 (0x8009 << 16) +#define FW_DEVICE_CXD3222 (0x8039 << 16) +#define FW_DEVICE_VT6306 (0x3044 << 16) +#define FW_DEVICE_R5C551 (0x0551 << 16) +#define FW_DEVICE_R5C552 (0x0552 << 16) +#define FW_DEVICE_PANGEA (0x0030 << 16) +#define FW_DEVICE_UNINORTH (0x0031 << 16) +#define FW_DEVICE_AIC5800 (0x5800 << 16) +#define FW_DEVICE_FW322 (0x5811 << 16) +#define FW_DEVICE_7007 (0x7007 << 16) +#define FW_DEVICE_82372FB (0x7605 << 16) + +#define PCI_INTERFACE_OHCI 0x10 + +#define FW_OHCI_BASE_REG 0x10 + +#define OHCI_DMA_ITCH 0x20 +#define OHCI_DMA_IRCH 0x20 + +#define OHCI_MAX_DMA_CH (0x4 + OHCI_DMA_ITCH + OHCI_DMA_IRCH) + + +typedef uint32_t fwohcireg_t; + +/* for PCI */ +#if BYTE_ORDER == BIG_ENDIAN +#define FWOHCI_DMA_WRITE(x, y) ((x) = htole32(y)) +#define FWOHCI_DMA_READ(x) le32toh(x) +#define FWOHCI_DMA_SET(x, y) ((x) |= htole32(y)) +#define FWOHCI_DMA_CLEAR(x, y) ((x) &= htole32(~(y))) +#else +#define FWOHCI_DMA_WRITE(x, y) ((x) = (y)) +#define FWOHCI_DMA_READ(x) (x) +#define FWOHCI_DMA_SET(x, y) ((x) |= (y)) +#define FWOHCI_DMA_CLEAR(x, y) ((x) &= ~(y)) +#endif + +struct fwohcidb { + union { + struct { + uint32_t cmd; + uint32_t addr; + uint32_t depend; + uint32_t res; + } desc; + uint32_t immed[4]; + } db; +#define OHCI_STATUS_SHIFT 16 +#define OHCI_COUNT_MASK 0xffff +#define OHCI_OUTPUT_MORE (0 << 28) +#define OHCI_OUTPUT_LAST (1 << 28) +#define OHCI_INPUT_MORE (2 << 28) +#define OHCI_INPUT_LAST (3 << 28) +#define OHCI_STORE_QUAD (4 << 28) +#define OHCI_LOAD_QUAD (5 << 28) +#define OHCI_NOP (6 << 28) +#define OHCI_STOP (7 << 28) +#define OHCI_STORE (8 << 28) +#define OHCI_CMD_MASK (0xf << 28) + +#define OHCI_UPDATE (1 << 27) + +#define OHCI_KEY_ST0 (0 << 24) +#define OHCI_KEY_ST1 (1 << 24) +#define OHCI_KEY_ST2 (2 << 24) +#define OHCI_KEY_ST3 (3 << 24) +#define OHCI_KEY_REGS (5 << 24) +#define OHCI_KEY_SYS (6 << 24) +#define OHCI_KEY_DEVICE (7 << 24) +#define OHCI_KEY_MASK (7 << 24) + +#define OHCI_INTERRUPT_NEVER (0 << 20) +#define OHCI_INTERRUPT_TRUE (1 << 20) +#define OHCI_INTERRUPT_FALSE (2 << 20) +#define OHCI_INTERRUPT_ALWAYS (3 << 20) + +#define OHCI_BRANCH_NEVER (0 << 18) +#define OHCI_BRANCH_TRUE (1 << 18) +#define OHCI_BRANCH_FALSE (2 << 18) +#define OHCI_BRANCH_ALWAYS (3 << 18) +#define OHCI_BRANCH_MASK (3 << 18) + +#define OHCI_WAIT_NEVER (0 << 16) +#define OHCI_WAIT_TRUE (1 << 16) +#define OHCI_WAIT_FALSE (2 << 16) +#define OHCI_WAIT_ALWAYS (3 << 16) +}; + +#define OHCI_SPD_S100 0x4 +#define OHCI_SPD_S200 0x1 +#define OHCI_SPD_S400 0x2 + + +#define FWOHCIEV_NOSTAT 0 +#define FWOHCIEV_LONGP 2 +#define FWOHCIEV_MISSACK 3 +#define FWOHCIEV_UNDRRUN 4 +#define FWOHCIEV_OVRRUN 5 +#define FWOHCIEV_DESCERR 6 +#define FWOHCIEV_DTRDERR 7 +#define FWOHCIEV_DTWRERR 8 +#define FWOHCIEV_BUSRST 9 +#define FWOHCIEV_TIMEOUT 0xa +#define FWOHCIEV_TCODERR 0xb +#define FWOHCIEV_UNKNOWN 0xe +#define FWOHCIEV_FLUSHED 0xf +#define FWOHCIEV_ACKCOMPL 0x11 +#define FWOHCIEV_ACKPEND 0x12 +#define FWOHCIEV_ACKBSX 0x14 +#define FWOHCIEV_ACKBSA 0x15 +#define FWOHCIEV_ACKBSB 0x16 +#define FWOHCIEV_ACKTARD 0x1b +#define FWOHCIEV_ACKDERR 0x1d +#define FWOHCIEV_ACKTERR 0x1e + +#define FWOHCIEV_MASK 0x1f + +struct ohci_dma{ + fwohcireg_t cntl; + +#define OHCI_CNTL_CYCMATCH_S (0x1 << 31) + +#define OHCI_CNTL_BUFFIL (0x1 << 31) +#define OHCI_CNTL_ISOHDR (0x1 << 30) +#define OHCI_CNTL_CYCMATCH_R (0x1 << 29) +#define OHCI_CNTL_MULTICH (0x1 << 28) + +#define OHCI_CNTL_DMA_RUN (0x1 << 15) +#define OHCI_CNTL_DMA_WAKE (0x1 << 12) +#define OHCI_CNTL_DMA_DEAD (0x1 << 11) +#define OHCI_CNTL_DMA_ACTIVE (0x1 << 10) +#define OHCI_CNTL_DMA_BT (0x1 << 8) +#define OHCI_CNTL_DMA_BAD (0x1 << 7) +#define OHCI_CNTL_DMA_STAT (0xff) + + fwohcireg_t cntl_clr; + fwohcireg_t dummy0; + fwohcireg_t cmd; + fwohcireg_t match; + fwohcireg_t dummy1; + fwohcireg_t dummy2; + fwohcireg_t dummy3; +}; + +struct ohci_itdma{ + fwohcireg_t cntl; + fwohcireg_t cntl_clr; + fwohcireg_t dummy0; + fwohcireg_t cmd; +}; + +struct ohci_registers { + fwohcireg_t ver; /* Version No. 0x0 */ + fwohcireg_t guid; /* GUID_ROM No. 0x4 */ + fwohcireg_t retry; /* AT retries 0x8 */ +#define FWOHCI_RETRY 0x8 + fwohcireg_t csr_data; /* CSR data 0xc */ + fwohcireg_t csr_cmp; /* CSR compare 0x10 */ + fwohcireg_t csr_cntl; /* CSR compare 0x14 */ + fwohcireg_t rom_hdr; /* config ROM ptr. 0x18 */ + fwohcireg_t bus_id; /* BUS_ID 0x1c */ + fwohcireg_t bus_opt; /* BUS option 0x20 */ +#define FWOHCIGUID_H 0x24 +#define FWOHCIGUID_L 0x28 + fwohcireg_t guid_hi; /* GUID hi 0x24 */ + fwohcireg_t guid_lo; /* GUID lo 0x28 */ + fwohcireg_t dummy0[2]; /* dummy 0x2c-0x30 */ + fwohcireg_t config_rom; /* config ROM map 0x34 */ + fwohcireg_t post_wr_lo; /* post write addr lo 0x38 */ + fwohcireg_t post_wr_hi; /* post write addr hi 0x3c */ + fwohcireg_t vendor; /* vendor ID 0x40 */ + fwohcireg_t dummy1[3]; /* dummy 0x44-0x4c */ + fwohcireg_t hcc_cntl_set; /* HCC control set 0x50 */ + fwohcireg_t hcc_cntl_clr; /* HCC control clr 0x54 */ +#define OHCI_HCC_BIBIV (1U << 31) /* BIBimage Valid */ +#define OHCI_HCC_BIGEND (1 << 30) /* noByteSwapData */ +#define OHCI_HCC_PRPHY (1 << 23) /* programPhyEnable */ +#define OHCI_HCC_PHYEN (1 << 22) /* aPhyEnhanceEnable */ +#define OHCI_HCC_LPS (1 << 19) /* LPS */ +#define OHCI_HCC_POSTWR (1 << 18) /* postedWriteEnable */ +#define OHCI_HCC_LINKEN (1 << 17) /* linkEnable */ +#define OHCI_HCC_RESET (1 << 16) /* softReset */ + fwohcireg_t dummy2[2]; /* dummy 0x58-0x5c */ + fwohcireg_t dummy3[1]; /* dummy 0x60 */ + fwohcireg_t sid_buf; /* self id buffer 0x64 */ + fwohcireg_t sid_cnt; /* self id count 0x68 */ + fwohcireg_t dummy4[1]; /* dummy 0x6c */ + fwohcireg_t ir_mask_hi_set; /* ir mask hi set 0x70 */ + fwohcireg_t ir_mask_hi_clr; /* ir mask hi set 0x74 */ + fwohcireg_t ir_mask_lo_set; /* ir mask hi set 0x78 */ + fwohcireg_t ir_mask_lo_clr; /* ir mask hi set 0x7c */ +#define FWOHCI_INTSTAT 0x80 +#define FWOHCI_INTSTATCLR 0x84 +#define FWOHCI_INTMASK 0x88 +#define FWOHCI_INTMASKCLR 0x8c + fwohcireg_t int_stat; /* 0x80 */ + fwohcireg_t int_clear; /* 0x84 */ + fwohcireg_t int_mask; /* 0x88 */ + fwohcireg_t int_mask_clear; /* 0x8c */ + fwohcireg_t it_int_stat; /* 0x90 */ + fwohcireg_t it_int_clear; /* 0x94 */ + fwohcireg_t it_int_mask; /* 0x98 */ + fwohcireg_t it_mask_clear; /* 0x9c */ + fwohcireg_t ir_int_stat; /* 0xa0 */ + fwohcireg_t ir_int_clear; /* 0xa4 */ + fwohcireg_t ir_int_mask; /* 0xa8 */ + fwohcireg_t ir_mask_clear; /* 0xac */ + fwohcireg_t dummy5[11]; /* dummy 0xb0-d8 */ + fwohcireg_t fairness; /* fairness control 0xdc */ + fwohcireg_t link_cntl; /* Chip control 0xe0*/ + fwohcireg_t link_cntl_clr; /* Chip control clear 0xe4*/ +#define FWOHCI_NODEID 0xe8 + fwohcireg_t node; /* Node ID 0xe8 */ +#define OHCI_NODE_VALID (1U << 31) +#define OHCI_NODE_ROOT (1 << 30) + +#define OHCI_ASYSRCBUS 1 + + fwohcireg_t phy_access; /* PHY cntl 0xec */ +#define PHYDEV_RDDONE (1<<31) +#define PHYDEV_RDCMD (1<<15) +#define PHYDEV_WRCMD (1<<14) +#define PHYDEV_REGADDR 8 +#define PHYDEV_WRDATA 0 +#define PHYDEV_RDADDR 24 +#define PHYDEV_RDDATA 16 + + fwohcireg_t cycle_timer; /* Cycle Timer 0xf0 */ + fwohcireg_t dummy6[3]; /* dummy 0xf4-fc */ + fwohcireg_t areq_hi; /* Async req. filter hi 0x100 */ + fwohcireg_t areq_hi_clr; /* Async req. filter hi 0x104 */ + fwohcireg_t areq_lo; /* Async req. filter lo 0x108 */ + fwohcireg_t areq_lo_clr; /* Async req. filter lo 0x10c */ + fwohcireg_t preq_hi; /* Async req. filter hi 0x110 */ + fwohcireg_t preq_hi_clr; /* Async req. filter hi 0x114 */ + fwohcireg_t preq_lo; /* Async req. filter lo 0x118 */ + fwohcireg_t preq_lo_clr; /* Async req. filter lo 0x11c */ + + fwohcireg_t pys_upper; /* Physical Upper bound 0x120 */ + + fwohcireg_t dummy7[23]; /* dummy 0x124-0x17c */ + + /* 0x180, 0x184, 0x188, 0x18c */ + /* 0x190, 0x194, 0x198, 0x19c */ + /* 0x1a0, 0x1a4, 0x1a8, 0x1ac */ + /* 0x1b0, 0x1b4, 0x1b8, 0x1bc */ + /* 0x1c0, 0x1c4, 0x1c8, 0x1cc */ + /* 0x1d0, 0x1d4, 0x1d8, 0x1dc */ + /* 0x1e0, 0x1e4, 0x1e8, 0x1ec */ + /* 0x1f0, 0x1f4, 0x1f8, 0x1fc */ + struct ohci_dma dma_ch[0x4]; + + /* 0x200, 0x204, 0x208, 0x20c */ + /* 0x210, 0x204, 0x208, 0x20c */ + struct ohci_itdma dma_itch[0x20]; + + /* 0x400, 0x404, 0x408, 0x40c */ + /* 0x410, 0x404, 0x408, 0x40c */ + struct ohci_dma dma_irch[0x20]; +}; + +#define OHCI_CNTL_CYCSRC (0x1 << 22) +#define OHCI_CNTL_CYCMTR (0x1 << 21) +#define OHCI_CNTL_CYCTIMER (0x1 << 20) +#define OHCI_CNTL_PHYPKT (0x1 << 10) +#define OHCI_CNTL_SID (0x1 << 9) + +#define OHCI_INT_DMA_ATRQ (0x1 << 0) +#define OHCI_INT_DMA_ATRS (0x1 << 1) +#define OHCI_INT_DMA_ARRQ (0x1 << 2) +#define OHCI_INT_DMA_ARRS (0x1 << 3) +#define OHCI_INT_DMA_PRRQ (0x1 << 4) +#define OHCI_INT_DMA_PRRS (0x1 << 5) +#define OHCI_INT_DMA_IT (0x1 << 6) +#define OHCI_INT_DMA_IR (0x1 << 7) +#define OHCI_INT_PW_ERR (0x1 << 8) +#define OHCI_INT_LR_ERR (0x1 << 9) + +#define OHCI_INT_PHY_SID (0x1 << 16) +#define OHCI_INT_PHY_BUS_R (0x1 << 17) + +#define OHCI_INT_REG_FAIL (0x1 << 18) + +#define OHCI_INT_PHY_INT (0x1 << 19) +#define OHCI_INT_CYC_START (0x1 << 20) +#define OHCI_INT_CYC_64SECOND (0x1 << 21) +#define OHCI_INT_CYC_LOST (0x1 << 22) +#define OHCI_INT_CYC_ERR (0x1 << 23) + +#define OHCI_INT_ERR (0x1 << 24) +#define OHCI_INT_CYC_LONG (0x1 << 25) +#define OHCI_INT_PHY_REG (0x1 << 26) + +#define OHCI_INT_EN (0x1 << 31) + +#define IP_CHANNELS 0x0234 +#define FWOHCI_MAXREC 2048 + +#define OHCI_ISORA 0x02 +#define OHCI_ISORB 0x04 + +#define FWOHCITCODE_PHY 0xe diff --git a/stand/i386/libi386/Makefile b/stand/i386/libi386/Makefile new file mode 100644 index 0000000..8d65513 --- /dev/null +++ b/stand/i386/libi386/Makefile @@ -0,0 +1,61 @@ +# $FreeBSD$ + +HAVE_GPT= yes +HAVE_GELI= yes + +.include <bsd.init.mk> + +LIB= i386 +INTERNALLIB= + +SRCS= biosacpi.c bioscd.c biosdisk.c biosmem.c biospnp.c \ + biospci.c biossmap.c bootinfo.c bootinfo32.c bootinfo64.c \ + comconsole.c devicename.c elf32_freebsd.c \ + elf64_freebsd.c multiboot.c multiboot_tramp.S relocater_tramp.S \ + i386_copy.c i386_module.c nullconsole.c pxe.c pxetramp.s \ + smbios.c time.c vidconsole.c amd64_tramp.S spinconsole.c +.PATH: ${ZFSSRC} +SRCS+= devicename_stubs.c + +BOOT_COMCONSOLE_PORT?= 0x3f8 +CFLAGS+= -DCOMPORT=${BOOT_COMCONSOLE_PORT} + +BOOT_COMCONSOLE_SPEED?= 9600 +CFLAGS+= -DCOMSPEED=${BOOT_COMCONSOLE_SPEED} + +.ifdef(BOOT_BIOSDISK_DEBUG) +# Make the disk code more talkative +CFLAGS+= -DDISK_DEBUG +.endif + +.if !defined(BOOT_HIDE_SERIAL_NUMBERS) +# Export serial numbers, UUID, and asset tag from loader. +CFLAGS+= -DSMBIOS_SERIAL_NUMBERS +.if defined(BOOT_LITTLE_ENDIAN_UUID) +# Use little-endian UUID format as defined in SMBIOS 2.6. +CFLAGS+= -DSMBIOS_LITTLE_ENDIAN_UUID +.elif defined(BOOT_NETWORK_ENDIAN_UUID) +# Use network-endian UUID format for backward compatibility. +CFLAGS+= -DSMBIOS_NETWORK_ENDIAN_UUID +.endif +.endif + +# Include simple terminal emulation (cons25-compatible) +CFLAGS+= -DTERM_EMU + +# XXX: make alloca() useable +CFLAGS+= -Dalloca=__builtin_alloca + +CFLAGS+= -I${BOOTSRC}/ficl -I${BOOTSRC}/ficl/i386 \ + -I${LDRSRC} -I${BOOTSRC}/i386/common \ + -I${BTXLIB} \ + -I${SYSDIR}/contrib/dev/acpica/include + +# Handle FreeBSD specific %b and %D printf format specifiers +CFLAGS+= ${FORMAT_EXTENSIONS} + +.include <bsd.lib.mk> + +# XXX: clang integrated-as doesn't grok .codeNN directives yet +CFLAGS.amd64_tramp.S= ${CLANG_NO_IAS} +CFLAGS.multiboot_tramp.S= ${CLANG_NO_IAS} diff --git a/stand/i386/libi386/Makefile.depend b/stand/i386/libi386/Makefile.depend new file mode 100644 index 0000000..df20c96 --- /dev/null +++ b/stand/i386/libi386/Makefile.depend @@ -0,0 +1,14 @@ +# $FreeBSD$ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + include \ + include/xlocale \ + lib/libmd \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/stand/i386/libi386/amd64_tramp.S b/stand/i386/libi386/amd64_tramp.S new file mode 100644 index 0000000..6359b90 --- /dev/null +++ b/stand/i386/libi386/amd64_tramp.S @@ -0,0 +1,113 @@ +/*- + * Copyright (c) 2003 Peter Wemm <peter@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. + * + * 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. + * + * $FreeBSD$ + */ + +/* + * Quick and dirty trampoline to get into 64 bit (long) mode and running + * with paging enabled so that we enter the kernel at its linked address. + */ +#define MSR_EFER 0xc0000080 +#define EFER_LME 0x00000100 +#define CR4_PAE 0x00000020 +#define CR4_PSE 0x00000010 +#define CR0_PG 0x80000000 + +/* GRRR. Deal with BTX that links us for a non-zero location */ +#define VPBASE 0xa000 +#define VTOP(x) ((x) + VPBASE) + + .data + + .p2align 12,0x40 + + .globl PT4 +PT4: + .space 0x1000 + .globl PT3 +PT3: + .space 0x1000 + .globl PT2 +PT2: + .space 0x1000 + +gdtdesc: + .word gdtend - gdt + .long VTOP(gdt) # low + .long 0 # high + +gdt: + .long 0 # null descriptor + .long 0 + .long 0x00000000 # %cs + .long 0x00209800 + .long 0x00000000 # %ds + .long 0x00008000 +gdtend: + + .text + .code32 + + .globl amd64_tramp +amd64_tramp: + /* Be sure that interrupts are disabled */ + cli + + /* Turn on EFER.LME */ + movl $MSR_EFER, %ecx + rdmsr + orl $EFER_LME, %eax + wrmsr + + /* Turn on PAE */ + movl %cr4, %eax + orl $CR4_PAE, %eax + movl %eax, %cr4 + + /* Set %cr3 for PT4 */ + movl $VTOP(PT4), %eax + movl %eax, %cr3 + + /* Turn on paging (implicitly sets EFER.LMA) */ + movl %cr0, %eax + orl $CR0_PG, %eax + movl %eax, %cr0 + + /* Now we're in compatibility mode. set %cs for long mode */ + movl $VTOP(gdtdesc), %eax + movl VTOP(entry_hi), %esi + movl VTOP(entry_lo), %edi + lgdt (%eax) + ljmp $0x8, $VTOP(longmode) + + .code64 +longmode: + /* We're still running V=P, jump to entry point */ + movl %esi, %eax + salq $32, %rax + orq %rdi, %rax + pushq %rax + ret diff --git a/stand/i386/libi386/biosacpi.c b/stand/i386/libi386/biosacpi.c new file mode 100644 index 0000000..8167fca --- /dev/null +++ b/stand/i386/libi386/biosacpi.c @@ -0,0 +1,144 @@ +/*- + * Copyright (c) 2001 Michael Smith <msmith@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. + * + * 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stand.h> +#include <machine/stdarg.h> +#include <bootstrap.h> +#include <btxv86.h> +#include "libi386.h" + +#include "platform/acfreebsd.h" +#include "acconfig.h" +#define ACPI_SYSTEM_XFACE +#include "actypes.h" +#include "actbl.h" + +/* + * Detect ACPI and export information about the ACPI BIOS into the + * environment. + */ + +static ACPI_TABLE_RSDP *biosacpi_find_rsdp(void); +static ACPI_TABLE_RSDP *biosacpi_search_rsdp(char *base, int length); + +#define RSDP_CHECKSUM_LENGTH 20 + +void +biosacpi_detect(void) +{ + ACPI_TABLE_RSDP *rsdp; + char buf[24]; + int revision; + + /* locate and validate the RSDP */ + if ((rsdp = biosacpi_find_rsdp()) == NULL) + return; + + /* + * Report the RSDP to the kernel. While this can be found with + * a BIOS boot, the RSDP may be elsewhere when booted from UEFI. + * The old code used the 'hints' method to communite this to + * the kernel. However, while convenient, the 'hints' method + * is fragile and does not work when static hints are compiled + * into the kernel. Instead, move to setting different tunables + * that start with acpi. The old 'hints' can be removed before + * we branch for FreeBSD 12. + */ + sprintf(buf, "0x%08x", VTOP(rsdp)); + setenv("hint.acpi.0.rsdp", buf, 1); + setenv("acpi.rsdp", buf, 1); + revision = rsdp->Revision; + if (revision == 0) + revision = 1; + sprintf(buf, "%d", revision); + setenv("hint.acpi.0.revision", buf, 1); + setenv("acpi.revision", buf, 1); + strncpy(buf, rsdp->OemId, sizeof(rsdp->OemId)); + buf[sizeof(rsdp->OemId)] = '\0'; + setenv("hint.acpi.0.oem", buf, 1); + setenv("acpi.oem", buf, 1); + sprintf(buf, "0x%08x", rsdp->RsdtPhysicalAddress); + setenv("hint.acpi.0.rsdt", buf, 1); + setenv("acpi.rsdt", buf, 1); + if (revision >= 2) { + /* XXX extended checksum? */ + sprintf(buf, "0x%016llx", rsdp->XsdtPhysicalAddress); + setenv("hint.acpi.0.xsdt", buf, 1); + setenv("acpi.xsdt", buf, 1); + sprintf(buf, "%d", rsdp->Length); + setenv("hint.acpi.0.xsdt_length", buf, 1); + setenv("acpi.xsdt_length", buf, 1); + } +} + +/* + * Find the RSDP in low memory. See section 5.2.2 of the ACPI spec. + */ +static ACPI_TABLE_RSDP * +biosacpi_find_rsdp(void) +{ + ACPI_TABLE_RSDP *rsdp; + uint16_t *addr; + + /* EBDA is the 1 KB addressed by the 16 bit pointer at 0x40E. */ + addr = (uint16_t *)PTOV(0x40E); + if ((rsdp = biosacpi_search_rsdp((char *)(*addr << 4), 0x400)) != NULL) + return (rsdp); + + /* Check the upper memory BIOS space, 0xe0000 - 0xfffff. */ + if ((rsdp = biosacpi_search_rsdp((char *)0xe0000, 0x20000)) != NULL) + return (rsdp); + + return (NULL); +} + +static ACPI_TABLE_RSDP * +biosacpi_search_rsdp(char *base, int length) +{ + ACPI_TABLE_RSDP *rsdp; + u_int8_t *cp, sum; + int ofs, idx; + + /* search on 16-byte boundaries */ + for (ofs = 0; ofs < length; ofs += 16) { + rsdp = (ACPI_TABLE_RSDP *)PTOV(base + ofs); + + /* compare signature, validate checksum */ + if (!strncmp(rsdp->Signature, ACPI_SIG_RSDP, strlen(ACPI_SIG_RSDP))) { + cp = (u_int8_t *)rsdp; + sum = 0; + for (idx = 0; idx < RSDP_CHECKSUM_LENGTH; idx++) + sum += *(cp + idx); + if (sum != 0) + continue; + return(rsdp); + } + } + return(NULL); +} diff --git a/stand/i386/libi386/bioscd.c b/stand/i386/libi386/bioscd.c new file mode 100644 index 0000000..2e8fc3b --- /dev/null +++ b/stand/i386/libi386/bioscd.c @@ -0,0 +1,452 @@ +/*- + * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> + * Copyright (c) 2001 John H. 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. + * + * 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * BIOS CD device handling for CD's that have been booted off of via no + * emulation booting as defined in the El Torito standard. + * + * Ideas and algorithms from: + * + * - FreeBSD libi386/biosdisk.c + * + */ + +#include <stand.h> + +#include <sys/param.h> +#include <machine/bootinfo.h> + +#include <stdarg.h> + +#include <bootstrap.h> +#include <btxv86.h> +#include <edd.h> +#include "libi386.h" + +#define BIOSCD_SECSIZE 2048 +#define BUFSIZE (1 * BIOSCD_SECSIZE) +#define MAXBCDEV 1 + +/* Major numbers for devices we frontend for. */ +#define ACDMAJOR 117 +#define CDMAJOR 15 + +#ifdef DISK_DEBUG +# define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args) +#else +# define DEBUG(fmt, args...) +#endif + +struct specification_packet { + u_char sp_size; + u_char sp_bootmedia; + u_char sp_drive; + u_char sp_controller; + u_int sp_lba; + u_short sp_devicespec; + u_short sp_buffersegment; + u_short sp_loadsegment; + u_short sp_sectorcount; + u_short sp_cylsec; + u_char sp_head; +}; + +/* + * List of BIOS devices, translation from disk unit number to + * BIOS unit number. + */ +static struct bcinfo { + int bc_unit; /* BIOS unit number */ + struct specification_packet bc_sp; + int bc_open; /* reference counter */ + void *bc_bcache; /* buffer cache data */ +} bcinfo [MAXBCDEV]; +static int nbcinfo = 0; + +#define BC(dev) (bcinfo[(dev)->d_unit]) + +static int bc_read(int unit, daddr_t dblk, int blks, caddr_t dest); +static int bc_init(void); +static int bc_strategy(void *devdata, int flag, daddr_t dblk, + size_t size, char *buf, size_t *rsize); +static int bc_realstrategy(void *devdata, int flag, daddr_t dblk, + size_t size, char *buf, size_t *rsize); +static int bc_open(struct open_file *f, ...); +static int bc_close(struct open_file *f); +static int bc_print(int verbose); + +struct devsw bioscd = { + "cd", + DEVT_CD, + bc_init, + bc_strategy, + bc_open, + bc_close, + noioctl, + bc_print, + NULL +}; + +/* + * Translate between BIOS device numbers and our private unit numbers. + */ +int +bc_bios2unit(int biosdev) +{ + int i; + + DEBUG("looking for bios device 0x%x", biosdev); + for (i = 0; i < nbcinfo; i++) { + DEBUG("bc unit %d is BIOS device 0x%x", i, bcinfo[i].bc_unit); + if (bcinfo[i].bc_unit == biosdev) + return(i); + } + return(-1); +} + +int +bc_unit2bios(int unit) +{ + if ((unit >= 0) && (unit < nbcinfo)) + return(bcinfo[unit].bc_unit); + return(-1); +} + +/* + * We can't quiz, we have to be told what device to use, so this functoin + * doesn't do anything. Instead, the loader calls bc_add() with the BIOS + * device number to add. + */ +static int +bc_init(void) +{ + + return (0); +} + +int +bc_add(int biosdev) +{ + + if (nbcinfo >= MAXBCDEV) + return (-1); + bcinfo[nbcinfo].bc_unit = biosdev; + v86.ctl = V86_FLAGS; + v86.addr = 0x13; + v86.eax = 0x4b01; + v86.edx = biosdev; + v86.ds = VTOPSEG(&bcinfo[nbcinfo].bc_sp); + v86.esi = VTOPOFF(&bcinfo[nbcinfo].bc_sp); + v86int(); + if ((v86.eax & 0xff00) != 0) + return (-1); + + printf("BIOS CD is cd%d\n", nbcinfo); + nbcinfo++; + bcache_add_dev(nbcinfo); /* register cd device in bcache */ + return(0); +} + +/* + * Print information about disks + */ +static int +bc_print(int verbose) +{ + char line[80]; + int i, ret = 0; + + if (nbcinfo == 0) + return (0); + + printf("%s devices:", bioscd.dv_name); + if ((ret = pager_output("\n")) != 0) + return (ret); + + for (i = 0; i < nbcinfo; i++) { + snprintf(line, sizeof(line), " cd%d: Device 0x%x\n", i, + bcinfo[i].bc_sp.sp_devicespec); + if ((ret = pager_output(line)) != 0) + break; + } + return (ret); +} + +/* + * Attempt to open the disk described by (dev) for use by (f). + */ +static int +bc_open(struct open_file *f, ...) +{ + va_list ap; + struct i386_devdesc *dev; + + va_start(ap, f); + dev = va_arg(ap, struct i386_devdesc *); + va_end(ap); + if (dev->d_unit >= nbcinfo) { + DEBUG("attempt to open nonexistent disk"); + return(ENXIO); + } + + BC(dev).bc_open++; + if (BC(dev).bc_bcache == NULL) + BC(dev).bc_bcache = bcache_allocate(); + return(0); +} + +static int +bc_close(struct open_file *f) +{ + struct i386_devdesc *dev; + + dev = (struct i386_devdesc *)f->f_devdata; + BC(dev).bc_open--; + if (BC(dev).bc_open == 0) { + bcache_free(BC(dev).bc_bcache); + BC(dev).bc_bcache = NULL; + } + return(0); +} + +static int +bc_strategy(void *devdata, int rw, daddr_t dblk, size_t size, + char *buf, size_t *rsize) +{ + struct bcache_devdata bcd; + struct i386_devdesc *dev; + + dev = (struct i386_devdesc *)devdata; + bcd.dv_strategy = bc_realstrategy; + bcd.dv_devdata = devdata; + bcd.dv_cache = BC(dev).bc_bcache; + + return (bcache_strategy(&bcd, rw, dblk, size, buf, rsize)); +} + +static int +bc_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size, + char *buf, size_t *rsize) +{ + struct i386_devdesc *dev; + int unit; + int blks; +#ifdef BD_SUPPORT_FRAGS + char fragbuf[BIOSCD_SECSIZE]; + size_t fragsize; + + fragsize = size % BIOSCD_SECSIZE; +#else + if (size % BIOSCD_SECSIZE) + return (EINVAL); +#endif + + if ((rw & F_MASK) != F_READ) + return(EROFS); + dev = (struct i386_devdesc *)devdata; + unit = dev->d_unit; + blks = size / BIOSCD_SECSIZE; + if (dblk % (BIOSCD_SECSIZE / DEV_BSIZE) != 0) + return (EINVAL); + dblk /= (BIOSCD_SECSIZE / DEV_BSIZE); + DEBUG("read %d from %lld to %p", blks, dblk, buf); + + if (rsize) + *rsize = 0; + if ((blks = bc_read(unit, dblk, blks, buf)) < 0) { + DEBUG("read error"); + return (EIO); + } else { + if (size / BIOSCD_SECSIZE > blks) { + if (rsize) + *rsize = blks * BIOSCD_SECSIZE; + return (0); + } + } +#ifdef BD_SUPPORT_FRAGS + DEBUG("frag read %d from %lld+%d to %p", + fragsize, dblk, blks, buf + (blks * BIOSCD_SECSIZE)); + if (fragsize && bc_read(unit, dblk + blks, 1, fragbuf) != 1) { + if (blks) { + if (rsize) + *rsize = blks * BIOSCD_SECSIZE; + return (0); + } + DEBUG("frag read error"); + return(EIO); + } + bcopy(fragbuf, buf + (blks * BIOSCD_SECSIZE), fragsize); +#endif + if (rsize) + *rsize = size; + return (0); +} + +/* return negative value for an error, otherwise blocks read */ +static int +bc_read(int unit, daddr_t dblk, int blks, caddr_t dest) +{ + u_int maxfer, resid, result, retry, x; + caddr_t bbuf, p, xp; + static struct edd_packet packet; + int biosdev; +#ifdef DISK_DEBUG + int error; +#endif + + /* Just in case some idiot actually tries to read -1 blocks... */ + if (blks < 0) + return (-1); + + /* If nothing to do, just return succcess. */ + if (blks == 0) + return (0); + + /* Decide whether we have to bounce */ + if (VTOP(dest) >> 20 != 0) { + /* + * The destination buffer is above first 1MB of + * physical memory so we have to arrange a suitable + * bounce buffer. + */ + x = V86_IO_BUFFER_SIZE / BIOSCD_SECSIZE; + x = min(x, (unsigned)blks); + bbuf = PTOV(V86_IO_BUFFER); + maxfer = x; + } else { + bbuf = NULL; + maxfer = 0; + } + + biosdev = bc_unit2bios(unit); + resid = blks; + p = dest; + + while (resid > 0) { + if (bbuf) + xp = bbuf; + else + xp = p; + x = resid; + if (maxfer > 0) + x = min(x, maxfer); + + /* + * Loop retrying the operation a couple of times. The BIOS + * may also retry. + */ + for (retry = 0; retry < 3; retry++) { + /* If retrying, reset the drive */ + if (retry > 0) { + v86.ctl = V86_FLAGS; + v86.addr = 0x13; + v86.eax = 0; + v86.edx = biosdev; + v86int(); + } + + packet.len = sizeof(struct edd_packet); + packet.count = x; + packet.off = VTOPOFF(xp); + packet.seg = VTOPSEG(xp); + packet.lba = dblk; + v86.ctl = V86_FLAGS; + v86.addr = 0x13; + v86.eax = 0x4200; + v86.edx = biosdev; + v86.ds = VTOPSEG(&packet); + v86.esi = VTOPOFF(&packet); + v86int(); + result = V86_CY(v86.efl); + if (result == 0) + break; + /* fall back to 1 sector read */ + x = 1; + } + +#ifdef DISK_DEBUG + error = (v86.eax >> 8) & 0xff; +#endif + DEBUG("%d sectors from %lld to %p (0x%x) %s", x, dblk, p, + VTOP(p), result ? "failed" : "ok"); + DEBUG("unit %d status 0x%x", unit, error); + + /* still an error? break off */ + if (result != 0) + break; + + if (bbuf != NULL) + bcopy(bbuf, p, x * BIOSCD_SECSIZE); + p += (x * BIOSCD_SECSIZE); + dblk += x; + resid -= x; + } + +/* hexdump(dest, (blks * BIOSCD_SECSIZE)); */ + + if (blks - resid == 0) + return (-1); /* read failed */ + + return (blks - resid); +} + +/* + * Return a suitable dev_t value for (dev). + */ +int +bc_getdev(struct i386_devdesc *dev) +{ + int biosdev, unit; + int major; + int rootdev; + + unit = dev->d_unit; + biosdev = bc_unit2bios(unit); + DEBUG("unit %d BIOS device %d", unit, biosdev); + if (biosdev == -1) /* not a BIOS device */ + return(-1); + + /* + * XXX: Need to examine device spec here to figure out if SCSI or + * ATAPI. No idea on how to figure out device number. All we can + * really pass to the kernel is what bus and device on which bus we + * were booted from, which dev_t isn't well suited to since those + * number don't match to unit numbers very well. We may just need + * to engage in a hack where we pass -C to the boot args if we are + * the boot device. + */ + major = ACDMAJOR; + unit = 0; /* XXX */ + + /* XXX: Assume partition 'a'. */ + rootdev = MAKEBOOTDEV(major, 0, unit, 0); + DEBUG("dev is 0x%x\n", rootdev); + return(rootdev); +} diff --git a/stand/i386/libi386/biosdisk.c b/stand/i386/libi386/biosdisk.c new file mode 100644 index 0000000..a2147e2 --- /dev/null +++ b/stand/i386/libi386/biosdisk.c @@ -0,0 +1,1013 @@ +/*- + * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> + * Copyright (c) 2012 Andrey V. Elsukov <ae@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. + * + * 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * BIOS disk device handling. + * + * Ideas and algorithms from: + * + * - NetBSD libi386/biosdisk.c + * - FreeBSD biosboot/disk.c + * + */ + +#include <sys/disk.h> +#include <sys/limits.h> +#include <stand.h> +#include <machine/bootinfo.h> +#include <stdarg.h> + +#include <bootstrap.h> +#include <btxv86.h> +#include <edd.h> +#include "disk.h" +#include "libi386.h" + +#ifdef LOADER_GELI_SUPPORT +#include "cons.h" +#include "drv.h" +#include "gpt.h" +#include "part.h" +#include <uuid.h> +struct pentry { + struct ptable_entry part; + uint64_t flags; + union { + uint8_t bsd; + uint8_t mbr; + uuid_t gpt; + uint16_t vtoc8; + } type; + STAILQ_ENTRY(pentry) entry; +}; +struct ptable { + enum ptable_type type; + uint16_t sectorsize; + uint64_t sectors; + + STAILQ_HEAD(, pentry) entries; +}; + +#include "geliboot.c" +#endif /* LOADER_GELI_SUPPORT */ + +CTASSERT(sizeof(struct i386_devdesc) >= sizeof(struct disk_devdesc)); + +#define BIOS_NUMDRIVES 0x475 +#define BIOSDISK_SECSIZE 512 +#define BUFSIZE (1 * BIOSDISK_SECSIZE) + +#define DT_ATAPI 0x10 /* disk type for ATAPI floppies */ +#define WDMAJOR 0 /* major numbers for devices we frontend for */ +#define WFDMAJOR 1 +#define FDMAJOR 2 +#define DAMAJOR 4 + +#ifdef DISK_DEBUG +# define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args) +#else +# define DEBUG(fmt, args...) +#endif + +/* + * List of BIOS devices, translation from disk unit number to + * BIOS unit number. + */ +static struct bdinfo +{ + int bd_unit; /* BIOS unit number */ + int bd_cyl; /* BIOS geometry */ + int bd_hds; + int bd_sec; + int bd_flags; +#define BD_MODEINT13 0x0000 +#define BD_MODEEDD1 0x0001 +#define BD_MODEEDD3 0x0002 +#define BD_MODEMASK 0x0003 +#define BD_FLOPPY 0x0004 + int bd_type; /* BIOS 'drive type' (floppy only) */ + uint16_t bd_sectorsize; /* Sector size */ + uint64_t bd_sectors; /* Disk size */ + int bd_open; /* reference counter */ + void *bd_bcache; /* buffer cache data */ +} bdinfo [MAXBDDEV]; +static int nbdinfo = 0; + +#define BD(dev) (bdinfo[(dev)->d_unit]) + +static int bd_read(struct disk_devdesc *dev, daddr_t dblk, int blks, + caddr_t dest); +static int bd_write(struct disk_devdesc *dev, daddr_t dblk, int blks, + caddr_t dest); +static int bd_int13probe(struct bdinfo *bd); + +static int bd_init(void); +static int bd_strategy(void *devdata, int flag, daddr_t dblk, size_t size, + char *buf, size_t *rsize); +static int bd_realstrategy(void *devdata, int flag, daddr_t dblk, size_t size, + char *buf, size_t *rsize); +static int bd_open(struct open_file *f, ...); +static int bd_close(struct open_file *f); +static int bd_ioctl(struct open_file *f, u_long cmd, void *data); +static int bd_print(int verbose); + +#ifdef LOADER_GELI_SUPPORT +enum isgeli { + ISGELI_UNKNOWN, + ISGELI_NO, + ISGELI_YES +}; +static enum isgeli geli_status[MAXBDDEV][MAXTBLENTS]; + +int bios_read(void *vdev __unused, struct dsk *priv, off_t off, char *buf, + size_t bytes); +#endif /* LOADER_GELI_SUPPORT */ + +struct devsw biosdisk = { + "disk", + DEVT_DISK, + bd_init, + bd_strategy, + bd_open, + bd_close, + bd_ioctl, + bd_print, + NULL +}; + +/* + * Translate between BIOS device numbers and our private unit numbers. + */ +int +bd_bios2unit(int biosdev) +{ + int i; + + DEBUG("looking for bios device 0x%x", biosdev); + for (i = 0; i < nbdinfo; i++) { + DEBUG("bd unit %d is BIOS device 0x%x", i, bdinfo[i].bd_unit); + if (bdinfo[i].bd_unit == biosdev) + return (i); + } + return (-1); +} + +int +bd_unit2bios(int unit) +{ + + if ((unit >= 0) && (unit < nbdinfo)) + return (bdinfo[unit].bd_unit); + return (-1); +} + +/* + * Quiz the BIOS for disk devices, save a little info about them. + */ +static int +bd_init(void) +{ + int base, unit, nfd = 0; + +#ifdef LOADER_GELI_SUPPORT + geli_init(); +#endif + /* sequence 0, 0x80 */ + for (base = 0; base <= 0x80; base += 0x80) { + for (unit = base; (nbdinfo < MAXBDDEV); unit++) { +#ifndef VIRTUALBOX + /* + * Check the BIOS equipment list for number + * of fixed disks. + */ + if(base == 0x80 && + (nfd >= *(unsigned char *)PTOV(BIOS_NUMDRIVES))) + break; +#endif + bdinfo[nbdinfo].bd_open = 0; + bdinfo[nbdinfo].bd_bcache = NULL; + bdinfo[nbdinfo].bd_unit = unit; + bdinfo[nbdinfo].bd_flags = unit < 0x80 ? BD_FLOPPY: 0; + if (!bd_int13probe(&bdinfo[nbdinfo])) + break; + + /* XXX we need "disk aliases" to make this simpler */ + printf("BIOS drive %c: is disk%d\n", (unit < 0x80) ? + ('A' + unit): ('C' + unit - 0x80), nbdinfo); + nbdinfo++; + if (base == 0x80) + nfd++; + } + } + bcache_add_dev(nbdinfo); + return(0); +} + +/* + * Try to detect a device supported by the legacy int13 BIOS + */ +static int +bd_int13probe(struct bdinfo *bd) +{ + struct edd_params params; + int ret = 1; /* assume success */ + + v86.ctl = V86_FLAGS; + v86.addr = 0x13; + v86.eax = 0x800; + v86.edx = bd->bd_unit; + v86int(); + + /* Don't error out if we get bad sector number, try EDD as well */ + if (V86_CY(v86.efl) || /* carry set */ + (v86.edx & 0xff) <= (unsigned)(bd->bd_unit & 0x7f)) /* unit # bad */ + return (0); /* skip device */ + + if ((v86.ecx & 0x3f) == 0) /* absurd sector number */ + ret = 0; /* set error */ + + /* Convert max cyl # -> # of cylinders */ + bd->bd_cyl = ((v86.ecx & 0xc0) << 2) + ((v86.ecx & 0xff00) >> 8) + 1; + /* Convert max head # -> # of heads */ + bd->bd_hds = ((v86.edx & 0xff00) >> 8) + 1; + bd->bd_sec = v86.ecx & 0x3f; + bd->bd_type = v86.ebx & 0xff; + bd->bd_flags |= BD_MODEINT13; + + /* Calculate sectors count from the geometry */ + bd->bd_sectors = bd->bd_cyl * bd->bd_hds * bd->bd_sec; + bd->bd_sectorsize = BIOSDISK_SECSIZE; + DEBUG("unit 0x%x geometry %d/%d/%d", bd->bd_unit, bd->bd_cyl, + bd->bd_hds, bd->bd_sec); + + /* Determine if we can use EDD with this device. */ + v86.ctl = V86_FLAGS; + v86.addr = 0x13; + v86.eax = 0x4100; + v86.edx = bd->bd_unit; + v86.ebx = 0x55aa; + v86int(); + if (V86_CY(v86.efl) || /* carry set */ + (v86.ebx & 0xffff) != 0xaa55 || /* signature */ + (v86.ecx & EDD_INTERFACE_FIXED_DISK) == 0) + return (ret); /* return code from int13 AH=08 */ + + /* EDD supported */ + bd->bd_flags |= BD_MODEEDD1; + if ((v86.eax & 0xff00) >= 0x3000) + bd->bd_flags |= BD_MODEEDD3; + /* Get disk params */ + params.len = sizeof(struct edd_params); + v86.ctl = V86_FLAGS; + v86.addr = 0x13; + v86.eax = 0x4800; + v86.edx = bd->bd_unit; + v86.ds = VTOPSEG(¶ms); + v86.esi = VTOPOFF(¶ms); + v86int(); + if (!V86_CY(v86.efl)) { + uint64_t total; + + /* + * Sector size must be a multiple of 512 bytes. + * An alternate test would be to check power of 2, + * powerof2(params.sector_size). + */ + if (params.sector_size % BIOSDISK_SECSIZE) + bd->bd_sectorsize = BIOSDISK_SECSIZE; + else + bd->bd_sectorsize = params.sector_size; + + total = bd->bd_sectorsize * params.sectors; + if (params.sectors != 0) { + /* Only update if we did not overflow. */ + if (total > params.sectors) + bd->bd_sectors = params.sectors; + } + + total = (uint64_t)params.cylinders * + params.heads * params.sectors_per_track; + if (bd->bd_sectors < total) + bd->bd_sectors = total; + + ret = 1; + } + DEBUG("unit 0x%x flags %x, sectors %llu, sectorsize %u", + bd->bd_unit, bd->bd_flags, bd->bd_sectors, bd->bd_sectorsize); + return (ret); +} + +/* + * Print information about disks + */ +static int +bd_print(int verbose) +{ + static char line[80]; + struct disk_devdesc dev; + int i, ret = 0; + + if (nbdinfo == 0) + return (0); + + printf("%s devices:", biosdisk.dv_name); + if ((ret = pager_output("\n")) != 0) + return (ret); + + for (i = 0; i < nbdinfo; i++) { + snprintf(line, sizeof(line), + " disk%d: BIOS drive %c (%ju X %u):\n", i, + (bdinfo[i].bd_unit < 0x80) ? ('A' + bdinfo[i].bd_unit): + ('C' + bdinfo[i].bd_unit - 0x80), + (uintmax_t)bdinfo[i].bd_sectors, + bdinfo[i].bd_sectorsize); + if ((ret = pager_output(line)) != 0) + break; + dev.d_dev = &biosdisk; + dev.d_unit = i; + dev.d_slice = -1; + dev.d_partition = -1; + if (disk_open(&dev, + bdinfo[i].bd_sectorsize * bdinfo[i].bd_sectors, + bdinfo[i].bd_sectorsize) == 0) { + snprintf(line, sizeof(line), " disk%d", i); + ret = disk_print(&dev, line, verbose); + disk_close(&dev); + if (ret != 0) + return (ret); + } + } + return (ret); +} + +/* + * Attempt to open the disk described by (dev) for use by (f). + * + * Note that the philosophy here is "give them exactly what + * they ask for". This is necessary because being too "smart" + * about what the user might want leads to complications. + * (eg. given no slice or partition value, with a disk that is + * sliced - are they after the first BSD slice, or the DOS + * slice before it?) + */ +static int +bd_open(struct open_file *f, ...) +{ + struct disk_devdesc *dev, rdev; + struct disk_devdesc disk; + int err, g_err; + va_list ap; + uint64_t size; + + va_start(ap, f); + dev = va_arg(ap, struct disk_devdesc *); + va_end(ap); + + if (dev->d_unit < 0 || dev->d_unit >= nbdinfo) + return (EIO); + BD(dev).bd_open++; + if (BD(dev).bd_bcache == NULL) + BD(dev).bd_bcache = bcache_allocate(); + + /* + * Read disk size from partition. + * This is needed to work around buggy BIOS systems returning + * wrong (truncated) disk media size. + * During bd_probe() we tested if the mulitplication of bd_sectors + * would overflow so it should be safe to perform here. + */ + disk.d_dev = dev->d_dev; + disk.d_type = dev->d_type; + disk.d_unit = dev->d_unit; + disk.d_opendata = NULL; + disk.d_slice = -1; + disk.d_partition = -1; + disk.d_offset = 0; + if (disk_open(&disk, BD(dev).bd_sectors * BD(dev).bd_sectorsize, + BD(dev).bd_sectorsize) == 0) { + + if (disk_ioctl(&disk, DIOCGMEDIASIZE, &size) == 0) { + size /= BD(dev).bd_sectorsize; + if (size > BD(dev).bd_sectors) + BD(dev).bd_sectors = size; + } + disk_close(&disk); + } + + err = disk_open(dev, BD(dev).bd_sectors * BD(dev).bd_sectorsize, + BD(dev).bd_sectorsize); + +#ifdef LOADER_GELI_SUPPORT + static char gelipw[GELI_PW_MAXLEN]; + char *passphrase; + + if (err) + return (err); + + /* if we already know there is no GELI, skip the rest */ + if (geli_status[dev->d_unit][dev->d_slice] != ISGELI_UNKNOWN) + return (err); + + struct dsk dskp; + struct ptable *table = NULL; + struct ptable_entry part; + struct pentry *entry; + int geli_part = 0; + + dskp.drive = bd_unit2bios(dev->d_unit); + dskp.type = dev->d_type; + dskp.unit = dev->d_unit; + dskp.slice = dev->d_slice; + dskp.part = dev->d_partition; + dskp.start = dev->d_offset; + + memcpy(&rdev, dev, sizeof(rdev)); + /* to read the GPT table, we need to read the first sector */ + rdev.d_offset = 0; + /* We need the LBA of the end of the partition */ + table = ptable_open(&rdev, BD(dev).bd_sectors, + BD(dev).bd_sectorsize, ptblread); + if (table == NULL) { + DEBUG("Can't read partition table"); + /* soft failure, return the exit status of disk_open */ + return (err); + } + + if (table->type == PTABLE_GPT) + dskp.part = 255; + + STAILQ_FOREACH(entry, &table->entries, entry) { + dskp.slice = entry->part.index; + dskp.start = entry->part.start; + if (is_geli(&dskp) == 0) { + geli_status[dev->d_unit][dskp.slice] = ISGELI_YES; + return (0); + } + if (geli_taste(bios_read, &dskp, + entry->part.end - entry->part.start) == 0) { + if (geli_havekey(&dskp) == 0) { + geli_status[dev->d_unit][dskp.slice] = ISGELI_YES; + geli_part++; + continue; + } + if ((passphrase = getenv("kern.geom.eli.passphrase")) + != NULL) { + /* Use the cached passphrase */ + bcopy(passphrase, &gelipw, GELI_PW_MAXLEN); + } + if (geli_passphrase(&gelipw, dskp.unit, 'p', + (dskp.slice > 0 ? dskp.slice : dskp.part), + &dskp) == 0) { + setenv("kern.geom.eli.passphrase", &gelipw, 1); + bzero(gelipw, sizeof(gelipw)); + geli_status[dev->d_unit][dskp.slice] = ISGELI_YES; + geli_part++; + continue; + } + } else + geli_status[dev->d_unit][dskp.slice] = ISGELI_NO; + } + + /* none of the partitions on this disk have GELI */ + if (geli_part == 0) { + /* found no GELI */ + geli_status[dev->d_unit][dev->d_slice] = ISGELI_NO; + } +#endif /* LOADER_GELI_SUPPORT */ + + return (err); +} + +static int +bd_close(struct open_file *f) +{ + struct disk_devdesc *dev; + + dev = (struct disk_devdesc *)f->f_devdata; + BD(dev).bd_open--; + if (BD(dev).bd_open == 0) { + bcache_free(BD(dev).bd_bcache); + BD(dev).bd_bcache = NULL; + } + return (disk_close(dev)); +} + +static int +bd_ioctl(struct open_file *f, u_long cmd, void *data) +{ + struct disk_devdesc *dev; + int rc; + + dev = (struct disk_devdesc *)f->f_devdata; + + rc = disk_ioctl(dev, cmd, data); + if (rc != ENOTTY) + return (rc); + + switch (cmd) { + case DIOCGSECTORSIZE: + *(u_int *)data = BD(dev).bd_sectorsize; + break; + case DIOCGMEDIASIZE: + *(uint64_t *)data = BD(dev).bd_sectors * BD(dev).bd_sectorsize; + break; + default: + return (ENOTTY); + } + return (0); +} + +static int +bd_strategy(void *devdata, int rw, daddr_t dblk, size_t size, + char *buf, size_t *rsize) +{ + struct bcache_devdata bcd; + struct disk_devdesc *dev; + + dev = (struct disk_devdesc *)devdata; + bcd.dv_strategy = bd_realstrategy; + bcd.dv_devdata = devdata; + bcd.dv_cache = BD(dev).bd_bcache; + return (bcache_strategy(&bcd, rw, dblk + dev->d_offset, + size, buf, rsize)); +} + +static int +bd_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size, + char *buf, size_t *rsize) +{ + struct disk_devdesc *dev = (struct disk_devdesc *)devdata; + uint64_t disk_blocks; + int blks, rc; +#ifdef BD_SUPPORT_FRAGS /* XXX: sector size */ + char fragbuf[BIOSDISK_SECSIZE]; + size_t fragsize; + + fragsize = size % BIOSDISK_SECSIZE; +#else + if (size % BD(dev).bd_sectorsize) + panic("bd_strategy: %d bytes I/O not multiple of block size", size); +#endif + + DEBUG("open_disk %p", dev); + + /* + * Check the value of the size argument. We do have quite small + * heap (64MB), but we do not know good upper limit, so we check against + * INT_MAX here. This will also protect us against possible overflows + * while translating block count to bytes. + */ + if (size > INT_MAX) { + DEBUG("too large read: %zu bytes", size); + return (EIO); + } + + blks = size / BD(dev).bd_sectorsize; + if (dblk > dblk + blks) + return (EIO); + + if (rsize) + *rsize = 0; + + /* Get disk blocks, this value is either for whole disk or for partition */ + if (disk_ioctl(dev, DIOCGMEDIASIZE, &disk_blocks)) { + /* DIOCGMEDIASIZE does return bytes. */ + disk_blocks /= BD(dev).bd_sectorsize; + } else { + /* We should not get here. Just try to survive. */ + disk_blocks = BD(dev).bd_sectors - dev->d_offset; + } + + /* Validate source block address. */ + if (dblk < dev->d_offset || dblk >= dev->d_offset + disk_blocks) + return (EIO); + + /* + * Truncate if we are crossing disk or partition end. + */ + if (dblk + blks >= dev->d_offset + disk_blocks) { + blks = dev->d_offset + disk_blocks - dblk; + size = blks * BD(dev).bd_sectorsize; + DEBUG("short read %d", blks); + } + + switch (rw & F_MASK) { + case F_READ: + DEBUG("read %d from %lld to %p", blks, dblk, buf); + + if (blks && (rc = bd_read(dev, dblk, blks, buf))) { + /* Filter out floppy controller errors */ + if (BD(dev).bd_flags != BD_FLOPPY || rc != 0x20) { + printf("read %d from %lld to %p, error: 0x%x", blks, dblk, + buf, rc); + } + return (EIO); + } +#ifdef BD_SUPPORT_FRAGS /* XXX: sector size */ + DEBUG("bd_strategy: frag read %d from %d+%d to %p", + fragsize, dblk, blks, buf + (blks * BIOSDISK_SECSIZE)); + if (fragsize && bd_read(od, dblk + blks, 1, fragsize)) { + DEBUG("frag read error"); + return(EIO); + } + bcopy(fragbuf, buf + (blks * BIOSDISK_SECSIZE), fragsize); +#endif + break; + case F_WRITE : + DEBUG("write %d from %d to %p", blks, dblk, buf); + + if (blks && bd_write(dev, dblk, blks, buf)) { + DEBUG("write error"); + return (EIO); + } +#ifdef BD_SUPPORT_FRAGS + if(fragsize) { + DEBUG("Attempted to write a frag"); + return (EIO); + } +#endif + break; + default: + /* DO NOTHING */ + return (EROFS); + } + + if (rsize) + *rsize = size; + return (0); +} + +static int +bd_edd_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest, + int write) +{ + static struct edd_packet packet; + + packet.len = sizeof(struct edd_packet); + packet.count = blks; + packet.off = VTOPOFF(dest); + packet.seg = VTOPSEG(dest); + packet.lba = dblk; + v86.ctl = V86_FLAGS; + v86.addr = 0x13; + if (write) + /* Should we Write with verify ?? 0x4302 ? */ + v86.eax = 0x4300; + else + v86.eax = 0x4200; + v86.edx = BD(dev).bd_unit; + v86.ds = VTOPSEG(&packet); + v86.esi = VTOPOFF(&packet); + v86int(); + if (V86_CY(v86.efl)) + return (v86.eax >> 8); + return (0); +} + +static int +bd_chs_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest, + int write) +{ + u_int x, bpc, cyl, hd, sec; + + bpc = BD(dev).bd_sec * BD(dev).bd_hds; /* blocks per cylinder */ + x = dblk; + cyl = x / bpc; /* block # / blocks per cylinder */ + x %= bpc; /* block offset into cylinder */ + hd = x / BD(dev).bd_sec; /* offset / blocks per track */ + sec = x % BD(dev).bd_sec; /* offset into track */ + + /* correct sector number for 1-based BIOS numbering */ + sec++; + + if (cyl > 1023) + /* CHS doesn't support cylinders > 1023. */ + return (1); + + v86.ctl = V86_FLAGS; + v86.addr = 0x13; + if (write) + v86.eax = 0x300 | blks; + else + v86.eax = 0x200 | blks; + v86.ecx = ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec; + v86.edx = (hd << 8) | BD(dev).bd_unit; + v86.es = VTOPSEG(dest); + v86.ebx = VTOPOFF(dest); + v86int(); + if (V86_CY(v86.efl)) + return (v86.eax >> 8); + return (0); +} + +static int +bd_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest, int write) +{ + u_int x, sec, result, resid, retry, maxfer; + caddr_t p, xp, bbuf; + + /* Just in case some idiot actually tries to read/write -1 blocks... */ + if (blks < 0) + return (-1); + + resid = blks; + p = dest; + + /* Decide whether we have to bounce */ + if (VTOP(dest) >> 20 != 0 || (BD(dev).bd_unit < 0x80 && + (VTOP(dest) >> 16) != (VTOP(dest + + blks * BD(dev).bd_sectorsize) >> 16))) { + + /* + * There is a 64k physical boundary somewhere in the + * destination buffer, or the destination buffer is above + * first 1MB of physical memory so we have to arrange a + * suitable bounce buffer. Allocate a buffer twice as large + * as we need to. Use the bottom half unless there is a break + * there, in which case we use the top half. + */ + x = V86_IO_BUFFER_SIZE / BD(dev).bd_sectorsize; + x = min(x, (unsigned)blks); + bbuf = PTOV(V86_IO_BUFFER); + maxfer = x; /* limit transfers to bounce region size */ + } else { + bbuf = NULL; + maxfer = 0; + } + + while (resid > 0) { + /* + * Play it safe and don't cross track boundaries. + * (XXX this is probably unnecessary) + */ + sec = dblk % BD(dev).bd_sec; /* offset into track */ + x = min(BD(dev).bd_sec - sec, resid); + if (maxfer > 0) + x = min(x, maxfer); /* fit bounce buffer */ + + /* where do we transfer to? */ + xp = bbuf == NULL ? p : bbuf; + + /* + * Put your Data In, Put your Data out, + * Put your Data In, and shake it all about + */ + if (write && bbuf != NULL) + bcopy(p, bbuf, x * BD(dev).bd_sectorsize); + + /* + * Loop retrying the operation a couple of times. The BIOS + * may also retry. + */ + for (retry = 0; retry < 3; retry++) { + /* if retrying, reset the drive */ + if (retry > 0) { + v86.ctl = V86_FLAGS; + v86.addr = 0x13; + v86.eax = 0; + v86.edx = BD(dev).bd_unit; + v86int(); + } + + if (BD(dev).bd_flags & BD_MODEEDD1) + result = bd_edd_io(dev, dblk, x, xp, write); + else + result = bd_chs_io(dev, dblk, x, xp, write); + if (result == 0) + break; + } + + if (write) + DEBUG("Write %d sector(s) from %p (0x%x) to %lld %s", x, + p, VTOP(p), dblk, result ? "failed" : "ok"); + else + DEBUG("Read %d sector(s) from %lld to %p (0x%x) %s", x, + dblk, p, VTOP(p), result ? "failed" : "ok"); + if (result) { + return (result); + } + if (!write && bbuf != NULL) + bcopy(bbuf, p, x * BD(dev).bd_sectorsize); + p += (x * BD(dev).bd_sectorsize); + dblk += x; + resid -= x; + } + +/* hexdump(dest, (blks * BD(dev).bd_sectorsize)); */ + return(0); +} + +static int +bd_read(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest) +{ +#ifdef LOADER_GELI_SUPPORT + struct dsk dskp; + off_t p_off, diff; + daddr_t alignlba; + int err, n, alignblks; + char *tmpbuf; + + /* if we already know there is no GELI, skip the rest */ + if (geli_status[dev->d_unit][dev->d_slice] != ISGELI_YES) + return (bd_io(dev, dblk, blks, dest, 0)); + + if (geli_status[dev->d_unit][dev->d_slice] == ISGELI_YES) { + /* + * Align reads to DEV_GELIBOOT_BSIZE bytes because partial + * sectors cannot be decrypted. Round the requested LBA down to + * nearest multiple of DEV_GELIBOOT_BSIZE bytes. + */ + alignlba = rounddown2(dblk * BD(dev).bd_sectorsize, + DEV_GELIBOOT_BSIZE) / BD(dev).bd_sectorsize; + /* + * Round number of blocks to read up to nearest multiple of + * DEV_GELIBOOT_BSIZE + */ + diff = (dblk - alignlba) * BD(dev).bd_sectorsize; + alignblks = roundup2(blks * BD(dev).bd_sectorsize + diff, + DEV_GELIBOOT_BSIZE) / BD(dev).bd_sectorsize; + + /* + * If the read is rounded up to a larger size, use a temporary + * buffer here because the buffer provided by the caller may be + * too small. + */ + if (diff == 0) { + tmpbuf = dest; + } else { + tmpbuf = malloc(alignblks * BD(dev).bd_sectorsize); + if (tmpbuf == NULL) { + return (-1); + } + } + + err = bd_io(dev, alignlba, alignblks, tmpbuf, 0); + if (err) + return (err); + + dskp.drive = bd_unit2bios(dev->d_unit); + dskp.type = dev->d_type; + dskp.unit = dev->d_unit; + dskp.slice = dev->d_slice; + dskp.part = dev->d_partition; + dskp.start = dev->d_offset; + + /* GELI needs the offset relative to the partition start */ + p_off = alignlba - dskp.start; + + err = geli_read(&dskp, p_off * BD(dev).bd_sectorsize, tmpbuf, + alignblks * BD(dev).bd_sectorsize); + if (err) + return (err); + + if (tmpbuf != dest) { + bcopy(tmpbuf + diff, dest, blks * BD(dev).bd_sectorsize); + free(tmpbuf); + } + return (0); + } +#endif /* LOADER_GELI_SUPPORT */ + + return (bd_io(dev, dblk, blks, dest, 0)); +} + +static int +bd_write(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest) +{ + + return (bd_io(dev, dblk, blks, dest, 1)); +} + +/* + * Return the BIOS geometry of a given "fixed drive" in a format + * suitable for the legacy bootinfo structure. Since the kernel is + * expecting raw int 0x13/0x8 values for N_BIOS_GEOM drives, we + * prefer to get the information directly, rather than rely on being + * able to put it together from information already maintained for + * different purposes and for a probably different number of drives. + * + * For valid drives, the geometry is expected in the format (31..0) + * "000000cc cccccccc hhhhhhhh 00ssssss"; and invalid drives are + * indicated by returning the geometry of a "1.2M" PC-format floppy + * disk. And, incidentally, what is returned is not the geometry as + * such but the highest valid cylinder, head, and sector numbers. + */ +u_int32_t +bd_getbigeom(int bunit) +{ + + v86.ctl = V86_FLAGS; + v86.addr = 0x13; + v86.eax = 0x800; + v86.edx = 0x80 + bunit; + v86int(); + if (V86_CY(v86.efl)) + return 0x4f010f; + return ((v86.ecx & 0xc0) << 18) | ((v86.ecx & 0xff00) << 8) | + (v86.edx & 0xff00) | (v86.ecx & 0x3f); +} + +/* + * Return a suitable dev_t value for (dev). + * + * In the case where it looks like (dev) is a SCSI disk, we allow the number of + * IDE disks to be specified in $num_ide_disks. There should be a Better Way. + */ +int +bd_getdev(struct i386_devdesc *d) +{ + struct disk_devdesc *dev; + int biosdev; + int major; + int rootdev; + char *nip, *cp; + int i, unit; + + dev = (struct disk_devdesc *)d; + biosdev = bd_unit2bios(dev->d_unit); + DEBUG("unit %d BIOS device %d", dev->d_unit, biosdev); + if (biosdev == -1) /* not a BIOS device */ + return(-1); + if (disk_open(dev, BD(dev).bd_sectors * BD(dev).bd_sectorsize, + BD(dev).bd_sectorsize) != 0) /* oops, not a viable device */ + return (-1); + else + disk_close(dev); + + if (biosdev < 0x80) { + /* floppy (or emulated floppy) or ATAPI device */ + if (bdinfo[dev->d_unit].bd_type == DT_ATAPI) { + /* is an ATAPI disk */ + major = WFDMAJOR; + } else { + /* is a floppy disk */ + major = FDMAJOR; + } + } else { + /* assume an IDE disk */ + major = WDMAJOR; + } + /* default root disk unit number */ + unit = biosdev & 0x7f; + + /* XXX a better kludge to set the root disk unit number */ + if ((nip = getenv("root_disk_unit")) != NULL) { + i = strtol(nip, &cp, 0); + /* check for parse error */ + if ((cp != nip) && (*cp == 0)) + unit = i; + } + + rootdev = MAKEBOOTDEV(major, dev->d_slice + 1, unit, dev->d_partition); + DEBUG("dev is 0x%x\n", rootdev); + return(rootdev); +} + +#ifdef LOADER_GELI_SUPPORT +int +bios_read(void *vdev __unused, struct dsk *priv, off_t off, char *buf, size_t bytes) +{ + struct disk_devdesc dev; + + dev.d_dev = &biosdisk; + dev.d_type = priv->type; + dev.d_unit = priv->unit; + dev.d_slice = priv->slice; + dev.d_partition = priv->part; + dev.d_offset = priv->start; + + off = off / BD(&dev).bd_sectorsize; + /* GELI gives us the offset relative to the partition start */ + off += dev.d_offset; + bytes = bytes / BD(&dev).bd_sectorsize; + + return (bd_io(&dev, off, bytes, buf, 0)); +} +#endif /* LOADER_GELI_SUPPORT */ diff --git a/stand/i386/libi386/biosmem.c b/stand/i386/libi386/biosmem.c new file mode 100644 index 0000000..7105757 --- /dev/null +++ b/stand/i386/libi386/biosmem.c @@ -0,0 +1,258 @@ +/*- + * Copyright (c) 1998 Michael Smith <msmith@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. + * + * 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * Obtain memory configuration information from the BIOS + */ +#include <stand.h> +#include <machine/pc/bios.h> +#include "bootstrap.h" +#include "libi386.h" +#include "btxv86.h" +#include "smbios.h" + +vm_offset_t memtop, memtop_copyin, high_heap_base; +uint32_t bios_basemem, bios_extmem, high_heap_size; + +static struct bios_smap_xattr smap; + +/* + * Used to track which method was used to set BIOS memory + * regions. + */ +static uint8_t b_bios_probed; +#define B_BASEMEM_E820 0x1 +#define B_BASEMEM_12 0x2 +#define B_EXTMEM_E820 0x4 +#define B_EXTMEM_E801 0x8 +#define B_EXTMEM_8800 0x10 + +/* + * The minimum amount of memory to reserve in bios_extmem for the heap. + */ +#define HEAP_MIN (64 * 1024 * 1024) + +/* + * Products in this list need quirks to detect + * memory correctly. You need both maker and product as + * reported by smbios. + */ +/* e820 might not return useful extended memory */ +#define BQ_DISTRUST_E820_EXTMEM 0x1 +struct bios_getmem_quirks { + const char *bios_vendor; + const char *maker; + const char *product; + int quirk; +}; + +static struct bios_getmem_quirks quirks[] = { + {"coreboot", "Acer", "Peppy", BQ_DISTRUST_E820_EXTMEM}, + {NULL, NULL, NULL, 0} +}; + +static int +bios_getquirks(void) +{ + int i; + + for (i = 0; quirks[i].quirk != 0; ++i) { + if (smbios_match(quirks[i].bios_vendor, quirks[i].maker, + quirks[i].product)) + return (quirks[i].quirk); + } + + return (0); +} + +void +bios_getmem(void) +{ + uint64_t size; + + /* Parse system memory map */ + v86.ebx = 0; + do { + v86.ctl = V86_FLAGS; + v86.addr = 0x15; /* int 0x15 function 0xe820 */ + v86.eax = 0xe820; + v86.ecx = sizeof(struct bios_smap_xattr); + v86.edx = SMAP_SIG; + v86.es = VTOPSEG(&smap); + v86.edi = VTOPOFF(&smap); + v86int(); + if ((V86_CY(v86.efl)) || (v86.eax != SMAP_SIG)) + break; + /* look for a low-memory segment that's large enough */ + if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0) && + (smap.length >= (512 * 1024))) { + bios_basemem = smap.length; + b_bios_probed |= B_BASEMEM_E820; + } + + /* look for the first segment in 'extended' memory */ + if ((smap.type == SMAP_TYPE_MEMORY) && + (smap.base == 0x100000) && + !(bios_getquirks() & BQ_DISTRUST_E820_EXTMEM)) { + bios_extmem = smap.length; + b_bios_probed |= B_EXTMEM_E820; + } + + /* + * Look for the highest segment in 'extended' memory beyond + * 1MB but below 4GB. + */ + if ((smap.type == SMAP_TYPE_MEMORY) && + (smap.base > 0x100000) && + (smap.base < 0x100000000ull)) { + size = smap.length; + + /* + * If this segment crosses the 4GB boundary, + * truncate it. + */ + if (smap.base + size > 0x100000000ull) + size = 0x100000000ull - smap.base; + + /* + * To make maximum space for the kernel and the modules, + * set heap to use highest HEAP_MIN bytes below 4GB. + */ + if (high_heap_base < smap.base && size >= HEAP_MIN) { + high_heap_base = smap.base + size - HEAP_MIN; + high_heap_size = HEAP_MIN; + } + } + } while (v86.ebx != 0); + + /* Fall back to the old compatibility function for base memory */ + if (bios_basemem == 0) { + v86.ctl = 0; + v86.addr = 0x12; /* int 0x12 */ + v86int(); + + bios_basemem = (v86.eax & 0xffff) * 1024; + b_bios_probed |= B_BASEMEM_12; + } + + /* + * Fall back through several compatibility functions for extended + * memory. + */ + if (bios_extmem == 0) { + v86.ctl = V86_FLAGS; + v86.addr = 0x15; /* int 0x15 function 0xe801 */ + v86.eax = 0xe801; + v86int(); + if (!(V86_CY(v86.efl))) { + /* + * Clear high_heap; it may end up overlapping + * with the segment we're determining here. + * Let the default "steal stuff from top of + * bios_extmem" code below pick up on it. + */ + high_heap_size = 0; + high_heap_base = 0; + + /* + * %cx is the number of 1KiB blocks between 1..16MiB. + * It can only be up to 0x3c00; if it's smaller then + * there's a PC AT memory hole so we can't treat + * it as contiguous. + */ + bios_extmem = (v86.ecx & 0xffff) * 1024; + if (bios_extmem == (1024 * 0x3c00)) + bios_extmem += (v86.edx & 0xffff) * 64 * 1024; + + /* truncate bios_extmem */ + if (bios_extmem > 0x3ff00000) + bios_extmem = 0x3ff00000; + + b_bios_probed |= B_EXTMEM_E801; + } + } + if (bios_extmem == 0) { + v86.ctl = 0; + v86.addr = 0x15; /* int 0x15 function 0x88 */ + v86.eax = 0x8800; + v86int(); + bios_extmem = (v86.eax & 0xffff) * 1024; + b_bios_probed |= B_EXTMEM_8800; + } + + /* Set memtop to actual top of memory */ + if (high_heap_size != 0) { + memtop = memtop_copyin = high_heap_base; + } else { + memtop = memtop_copyin = 0x100000 + bios_extmem; + } + + /* + * If we have extended memory and did not find a suitable heap + * region in the SMAP, use the last HEAP_MIN of 'extended' memory as a + * high heap candidate. + */ + if (bios_extmem >= HEAP_MIN && high_heap_size < HEAP_MIN) { + high_heap_size = HEAP_MIN; + high_heap_base = memtop - HEAP_MIN; + memtop = memtop_copyin = high_heap_base; + } +} + +static int +command_biosmem(int argc, char *argv[]) +{ + int bq = bios_getquirks(); + + printf("bios_basemem: 0x%llx\n", (unsigned long long)bios_basemem); + printf("bios_extmem: 0x%llx\n", (unsigned long long)bios_extmem); + printf("memtop: 0x%llx\n", (unsigned long long)memtop); + printf("high_heap_base: 0x%llx\n", (unsigned long long)high_heap_base); + printf("high_heap_size: 0x%llx\n", (unsigned long long)high_heap_size); + printf("bios_quirks: 0x%02x", bq); + if (bq & BQ_DISTRUST_E820_EXTMEM) + printf(" BQ_DISTRUST_E820_EXTMEM"); + printf("\n"); + printf("b_bios_probed: 0x%02x", (int)b_bios_probed); + if (b_bios_probed & B_BASEMEM_E820) + printf(" B_BASEMEM_E820"); + if (b_bios_probed & B_BASEMEM_12) + printf(" B_BASEMEM_12"); + if (b_bios_probed & B_EXTMEM_E820) + printf(" B_EXTMEM_E820"); + if (b_bios_probed & B_EXTMEM_E801) + printf(" B_EXTMEM_E801"); + if (b_bios_probed & B_EXTMEM_8800) + printf(" B_EXTMEM_8800"); + printf("\n"); + + return (CMD_OK); +} + +COMMAND_SET(biosmem, "biosmem", "show BIOS memory setup", command_biosmem); diff --git a/stand/i386/libi386/biospci.c b/stand/i386/libi386/biospci.c new file mode 100644 index 0000000..098e30c --- /dev/null +++ b/stand/i386/libi386/biospci.c @@ -0,0 +1,588 @@ +/*- + * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> + * Copyright (c) 2016 Netflix, Inc + * 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 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * PnP enumerator using the PCI BIOS. + */ + +#include <stand.h> +#include <machine/stdarg.h> +#include <bootstrap.h> +#include <isapnp.h> +#include <btxv86.h> +#include "libi386.h" +#ifdef BOOT_FORTH +#include "ficl.h" +#endif + +/* + * Stupid PCI BIOS interface doesn't let you simply enumerate everything + * that's there, instead you have to ask it if it has something. + * + * So we have to scan by class code, subclass code and sometimes programming + * interface. + */ + +struct pci_progif +{ + int pi_code; + const char *pi_name; +}; + +static struct pci_progif progif_null[] = { + {0x0, NULL}, + {-1, NULL} +}; + +static struct pci_progif progif_display[] = { + {0x0, "VGA"}, + {0x1, "8514"}, + {-1, NULL} +}; + +static struct pci_progif progif_ide[] = { + {0x00, NULL}, + {0x01, NULL}, + {0x02, NULL}, + {0x03, NULL}, + {0x04, NULL}, + {0x05, NULL}, + {0x06, NULL}, + {0x07, NULL}, + {0x08, NULL}, + {0x09, NULL}, + {0x0a, NULL}, + {0x0b, NULL}, + {0x0c, NULL}, + {0x0d, NULL}, + {0x0e, NULL}, + {0x0f, NULL}, + {0x80, NULL}, + {0x81, NULL}, + {0x82, NULL}, + {0x83, NULL}, + {0x84, NULL}, + {0x85, NULL}, + {0x86, NULL}, + {0x87, NULL}, + {0x88, NULL}, + {0x89, NULL}, + {0x8a, NULL}, + {0x8b, NULL}, + {0x8c, NULL}, + {0x8d, NULL}, + {0x8e, NULL}, + {0x8f, NULL}, + {-1, NULL} +}; + +static struct pci_progif progif_serial[] = { + {0x0, "8250"}, + {0x1, "16450"}, + {0x2, "16550"}, + {-1, NULL} +}; + +static struct pci_progif progif_parallel[] = { + {0x0, "Standard"}, + {0x1, "Bidirectional"}, + {0x2, "ECP"}, + {-1, NULL} +}; + +static struct pci_progif progif_firewire[] = { + {0x10, "OHCI"}, + {-1, NULL} +}; + +struct pci_subclass +{ + int ps_subclass; + const char *ps_name; + struct pci_progif *ps_progif; /* if set, use for programming interface value(s) */ +}; + +static struct pci_subclass subclass_old[] = { + {0x0, "Old non-VGA", progif_null}, + {0x1, "Old VGA", progif_null}, + {-1, NULL, NULL} +}; + +static struct pci_subclass subclass_mass[] = { + {0x0, "SCSI", progif_null}, + {0x1, "IDE", progif_ide}, + {0x2, "Floppy disk", progif_null}, + {0x3, "IPI", progif_null}, + {0x4, "RAID", progif_null}, + {0x80, "mass storage", progif_null}, + {-1, NULL, NULL} +}; + +static struct pci_subclass subclass_net[] = { + {0x0, "Ethernet", progif_null}, + {0x1, "Token ring", progif_null}, + {0x2, "FDDI", progif_null}, + {0x3, "ATM", progif_null}, + {0x80, "network", progif_null}, + {-1, NULL, NULL} +}; + +static struct pci_subclass subclass_display[] = { + {0x0, NULL, progif_display}, + {0x1, "XGA", progif_null}, + {0x80, "other", progif_null}, + {-1, NULL, NULL} +}; + +static struct pci_subclass subclass_comms[] = { + {0x0, "serial", progif_serial}, + {0x1, "parallel", progif_parallel}, + {0x80, "communications", progif_null}, + {-1, NULL, NULL} +}; + +static struct pci_subclass subclass_serial[] = { + {0x0, "FireWire", progif_firewire}, + {0x1, "ACCESS.bus", progif_null}, + {0x2, "SSA", progif_null}, + {0x3, "USB", progif_null}, + {0x4, "Fibrechannel", progif_null}, + {-1, NULL, NULL} +}; + +static struct pci_class +{ + int pc_class; + const char *pc_name; + struct pci_subclass *pc_subclass; +} pci_classes[] = { + {0x0, "device", subclass_old}, + {0x1, "controller", subclass_mass}, + {0x2, "controller", subclass_net}, + {0x3, "display", subclass_display}, + {0x7, "controller", subclass_comms}, + {0xc, "controller", subclass_serial}, + {-1, NULL, NULL} +}; + +static void biospci_enumerate(void); +static void biospci_addinfo(int devid, struct pci_class *pc, struct pci_subclass *psc, struct pci_progif *ppi); + +struct pnphandler biospcihandler = +{ + "PCI BIOS", + biospci_enumerate +}; +static int biospci_version; + +#define PCI_BIOS_PRESENT 0xb101 +#define FIND_PCI_DEVICE 0xb102 +#define FIND_PCI_CLASS_CODE 0xb103 +#define GENERATE_SPECIAL_CYCLE 0xb106 +#define READ_CONFIG_BYTE 0xb108 +#define READ_CONFIG_WORD 0xb109 +#define READ_CONFIG_DWORD 0xb10a +#define WRITE_CONFIG_BYTE 0xb10b +#define WRITE_CONFIG_WORD 0xb10c +#define WRITE_CONFIG_DWORD 0xb10d +#define GET_IRQ_ROUTING_OPTIONS 0xb10e +#define SET_PCI_IRQ 0xb10f + +#define PCI_INT 0x1a + +#define PCI_SIGNATURE 0x20494350 /* AKA "PCI " */ + +void +biospci_detect(void) +{ + uint16_t version, hwcap, maxbus; + char buf[24]; + + /* Find the PCI BIOS */ + v86.ctl = V86_FLAGS; + v86.addr = PCI_INT; + v86.eax = PCI_BIOS_PRESENT; + v86.edi = 0x0; + v86int(); + + /* Check for OK response */ + if (V86_CY(v86.efl) || ((v86.eax & 0xff00) != 0) || + (v86.edx != PCI_SIGNATURE)) + return; + + version = v86.ebx & 0xffff; + hwcap = v86.eax & 0xff; + maxbus = v86.ecx & 0xff; +#if 0 + printf("PCI BIOS %d.%d%s%s maxbus %d\n", + bcd2bin((version >> 8) & 0xf), bcd2bin(version & 0xf), + (hwcap & 1) ? " config1" : "", (hwcap & 2) ? " config2" : "", + maxbus); +#endif + sprintf(buf, "%d", bcd2bin((version >> 8) & 0xf)); + setenv("pcibios.major", buf, 1); + sprintf(buf, "%d", bcd2bin(version & 0xf)); + setenv("pcibios.minor", buf, 1); + sprintf(buf, "%d", !!(hwcap & 1)); + setenv("pcibios.config1", buf, 1); + sprintf(buf, "%d", !!(hwcap & 2)); + setenv("pcibios.config2", buf, 1); + sprintf(buf, "%d", maxbus); + setenv("pcibios.maxbus", buf, 1); + biospci_version = bcd2bin((version >> 8) & 0xf) * 10 + bcd2bin(version & 0xf); +} + +static void +biospci_enumerate(void) +{ + int device_index, err; + uint32_t locator, devid; + struct pci_class *pc; + struct pci_subclass *psc; + struct pci_progif *ppi; + + /* Iterate over known classes */ + for (pc = pci_classes; pc->pc_class >= 0; pc++) { + /* Iterate over subclasses */ + for (psc = pc->pc_subclass; psc->ps_subclass >= 0; psc++) { + /* Iterate over programming interfaces */ + for (ppi = psc->ps_progif; ppi->pi_code >= 0; ppi++) { + + /* Scan for matches */ + for (device_index = 0; ; device_index++) { + /* Look for a match */ + err = biospci_find_devclass((pc->pc_class << 16) + + (psc->ps_subclass << 8) + ppi->pi_code, + device_index, &locator); + if (err != 0) + break; + + /* Read the device identifier from the nominated device */ + err = biospci_read_config(locator, 0, 2, &devid); + if (err != 0) + break; + + /* We have the device ID, create a PnP object and save everything */ + biospci_addinfo(devid, pc, psc, ppi); + } + } + } + } +} + +static void +biospci_addinfo(int devid, struct pci_class *pc, struct pci_subclass *psc, struct pci_progif *ppi) +{ + struct pnpinfo *pi; + char desc[80]; + + + /* build the description */ + desc[0] = 0; + if (ppi->pi_name != NULL) { + strcat(desc, ppi->pi_name); + strcat(desc, " "); + } + if (psc->ps_name != NULL) { + strcat(desc, psc->ps_name); + strcat(desc, " "); + } + if (pc->pc_name != NULL) + strcat(desc, pc->pc_name); + + pi = pnp_allocinfo(); + pi->pi_desc = strdup(desc); + sprintf(desc,"0x%08x", devid); + pnp_addident(pi, desc); + pnp_addinfo(pi); +} + +int +biospci_find_devclass(uint32_t class, int index, uint32_t *locator) +{ + v86.ctl = V86_FLAGS; + v86.addr = PCI_INT; + v86.eax = FIND_PCI_CLASS_CODE; + v86.ecx = class; + v86.esi = index; + v86int(); + + /* error */ + if (V86_CY(v86.efl) || (v86.eax & 0xff00)) + return (-1); + + *locator = v86.ebx; + return (0); +} + +static int +biospci_find_device(uint32_t devid, int index, uint32_t *locator) +{ + v86.ctl = V86_FLAGS; + v86.addr = PCI_INT; + v86.eax = FIND_PCI_DEVICE; + v86.edx = devid & 0xffff; /* EDX - Vendor ID */ + v86.ecx = (devid >> 16) & 0xffff; /* ECX - Device ID */ + v86.esi = index; + v86int(); + + /* error */ + if (V86_CY(v86.efl) || (v86.eax & 0xff00)) + return (-1); + + *locator = v86.ebx; + return (0); +} +/* + * Configuration space access methods. + * width = 0(byte), 1(word) or 2(dword). + */ +int +biospci_write_config(uint32_t locator, int offset, int width, uint32_t val) +{ + v86.ctl = V86_FLAGS; + v86.addr = PCI_INT; + v86.eax = WRITE_CONFIG_BYTE + width; + v86.ebx = locator; + v86.edi = offset; + v86.ecx = val; + v86int(); + + /* error */ + if (V86_CY(v86.efl) || (v86.eax & 0xff00)) + return (-1); + + return(0); +} + +int +biospci_read_config(uint32_t locator, int offset, int width, uint32_t *val) +{ + v86.ctl = V86_FLAGS; + v86.addr = PCI_INT; + v86.eax = READ_CONFIG_BYTE + width; + v86.ebx = locator; + v86.edi = offset; + v86int(); + + /* error */ + if (V86_CY(v86.efl) || (v86.eax & 0xff00)) + return (-1); + + *val = v86.ecx; + return (0); +} + +uint32_t +biospci_locator(int8_t bus, uint8_t device, uint8_t function) +{ + + return ((bus << 8) | ((device & 0x1f) << 3) | (function & 0x7)); +} + +/* + * Counts the number of instances of devid we have in the system, as least as + * far as the PCI BIOS is able to tell. + */ +static int +biospci_count_device_type(uint32_t devid) +{ + int i; + + for (i = 0; 1; i++) { + v86.ctl = V86_FLAGS; + v86.addr = PCI_INT; + v86.eax = FIND_PCI_DEVICE; + v86.edx = devid & 0xffff; /* EDX - Vendor ID */ + v86.ecx = (devid >> 16) & 0xffff; /* ECX - Device ID */ + v86.esi = i; + v86int(); + if (V86_CY(v86.efl) || (v86.eax & 0xff00)) + break; + + } + return i; +} + +#ifdef BOOT_FORTH +/* + * pcibios-device-count (devid -- count) + * + * Returns the PCI BIOS' count of how many devices matching devid are in the system. + * devid is the 32-bit vendor + device. + */ +static void +ficlPciBiosCountDevices(FICL_VM *pVM) +{ + uint32_t devid; + int i; + + devid = stackPopINT(pVM->pStack); + + i = biospci_count_device_type(devid); + + stackPushINT(pVM->pStack, i); +} + +/* + * pcibios-write-config (locator offset width value -- ) + * + * Writes the specified config register. + * Locator is bus << 8 | device << 3 | fuction + * offset is the pci config register + * width is 0 for byte, 1 for word, 2 for dword + * value is the value to write + */ +static void +ficlPciBiosWriteConfig(FICL_VM *pVM) +{ + uint32_t value, width, offset, locator; + + value = stackPopINT(pVM->pStack); + width = stackPopINT(pVM->pStack); + offset = stackPopINT(pVM->pStack); + locator = stackPopINT(pVM->pStack); + + biospci_write_config(locator, offset, width, value); +} + +/* + * pcibios-read-config (locator offset width -- value) + * + * Reads the specified config register. + * Locator is bus << 8 | device << 3 | fuction + * offset is the pci config register + * width is 0 for byte, 1 for word, 2 for dword + * value is the value to read from the register + */ +static void +ficlPciBiosReadConfig(FICL_VM *pVM) +{ + uint32_t value, width, offset, locator; + + width = stackPopINT(pVM->pStack); + offset = stackPopINT(pVM->pStack); + locator = stackPopINT(pVM->pStack); + + biospci_read_config(locator, offset, width, &value); + + stackPushINT(pVM->pStack, value); +} + +/* + * pcibios-find-devclass (class index -- locator) + * + * Finds the index'th instance of class in the pci tree. + * must be an exact match. + * class is the class to search for. + * index 0..N (set to 0, increment until error) + * + * Locator is bus << 8 | device << 3 | fuction (or -1 on error) + */ +static void +ficlPciBiosFindDevclass(FICL_VM *pVM) +{ + uint32_t index, class, locator; + + index = stackPopINT(pVM->pStack); + class = stackPopINT(pVM->pStack); + + if (biospci_find_devclass(class, index, &locator)) + locator = 0xffffffff; + + stackPushINT(pVM->pStack, locator); +} + +/* + * pcibios-find-device(devid index -- locator) + * + * Finds the index'th instance of devid in the pci tree. + * must be an exact match. + * class is the class to search for. + * index 0..N (set to 0, increment until error) + * + * Locator is bus << 8 | device << 3 | fuction (or -1 on error) + */ +static void +ficlPciBiosFindDevice(FICL_VM *pVM) +{ + uint32_t index, devid, locator; + + index = stackPopINT(pVM->pStack); + devid = stackPopINT(pVM->pStack); + + if (biospci_find_device(devid, index, &locator)) + locator = 0xffffffff; + + stackPushINT(pVM->pStack, locator); +} + +/* + * pcibios-find-device(bus device function -- locator) + * + * converts bus, device, function to locator. + * + * Locator is bus << 8 | device << 3 | fuction + */ +static void +ficlPciBiosLocator(FICL_VM *pVM) +{ + uint32_t bus, device, function, locator; + + function = stackPopINT(pVM->pStack); + device = stackPopINT(pVM->pStack); + bus = stackPopINT(pVM->pStack); + + locator = biospci_locator(bus, device, function); + + stackPushINT(pVM->pStack, locator); +} + +/* + * Glue function to add the appropriate forth words to access pci bios + * functionality. + */ +static void ficlCompilePciBios(FICL_SYSTEM *pSys) +{ + FICL_DICT *dp = pSys->dp; + assert (dp); + + dictAppendWord(dp, "pcibios-device-count", ficlPciBiosCountDevices, FW_DEFAULT); + dictAppendWord(dp, "pcibios-read-config", ficlPciBiosReadConfig, FW_DEFAULT); + dictAppendWord(dp, "pcibios-write-config", ficlPciBiosWriteConfig, FW_DEFAULT); + dictAppendWord(dp, "pcibios-find-devclass", ficlPciBiosFindDevclass, FW_DEFAULT); + dictAppendWord(dp, "pcibios-find-device", ficlPciBiosFindDevice, FW_DEFAULT); + dictAppendWord(dp, "pcibios-locator", ficlPciBiosLocator, FW_DEFAULT); + + ficlSetEnv(pSys, "pcibios-version", biospci_version); +} + +FICL_COMPILE_SET(ficlCompilePciBios); +#endif diff --git a/stand/i386/libi386/biospnp.c b/stand/i386/libi386/biospnp.c new file mode 100644 index 0000000..30e55fc --- /dev/null +++ b/stand/i386/libi386/biospnp.c @@ -0,0 +1,292 @@ +/*- + * Copyright (c) 1998 Michael Smith <msmith@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. + * + * 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * PnP BIOS enumerator. + */ + +#include <stand.h> +#include <machine/stdarg.h> +#include <bootstrap.h> +#include <isapnp.h> +#include <btxv86.h> + + +static int biospnp_init(void); +static void biospnp_enumerate(void); + +struct pnphandler biospnphandler = +{ + "PnP BIOS", + biospnp_enumerate +}; + +struct pnp_ICstructure +{ + u_int8_t pnp_signature[4]; + u_int8_t pnp_version; + u_int8_t pnp_length; + u_int16_t pnp_BIOScontrol; + u_int8_t pnp_checksum; + u_int32_t pnp_eventflag; + u_int16_t pnp_rmip; + u_int16_t pnp_rmcs; + u_int16_t pnp_pmip; + u_int32_t pnp_pmcs; + u_int8_t pnp_OEMdev[4]; + u_int16_t pnp_rmds; + u_int32_t pnp_pmds; +} __packed; + +struct pnp_devNode +{ + u_int16_t dn_size; + u_int8_t dn_handle; + u_int8_t dn_id[4]; + u_int8_t dn_type[3]; + u_int16_t dn_attrib; + u_int8_t dn_data[1]; +} __packed; + +struct pnp_isaConfiguration +{ + u_int8_t ic_revision; + u_int8_t ic_nCSN; + u_int16_t ic_rdport; + u_int16_t ic_reserved; +} __packed; + +static struct pnp_ICstructure *pnp_Icheck = NULL; +static u_int16_t pnp_NumNodes; +static u_int16_t pnp_NodeSize; + +static void biospnp_scanresdata(struct pnpinfo *pi, struct pnp_devNode *dn); +static int biospnp_call(int func, const char *fmt, ...); + +#define vsegofs(vptr) (((u_int32_t)VTOPSEG(vptr) << 16) + VTOPOFF(vptr)) + +typedef void v86bios_t(u_int32_t, u_int32_t, u_int32_t, u_int32_t); +v86bios_t *v86bios = (v86bios_t *)v86int; + +#define biospnp_f00(NumNodes, NodeSize) biospnp_call(0x00, "ll", NumNodes, NodeSize) +#define biospnp_f01(Node, devNodeBuffer, Control) biospnp_call(0x01, "llw", Node, devNodeBuffer, Control) +#define biospnp_f40(Configuration) biospnp_call(0x40, "l", Configuration) + +/* PnP BIOS return codes */ +#define PNP_SUCCESS 0x00 +#define PNP_FUNCTION_NOT_SUPPORTED 0x80 + +/* + * Initialisation: locate the PnP BIOS, test that we can call it. + * Returns nonzero if the PnP BIOS is not usable on this system. + */ +static int +biospnp_init(void) +{ + struct pnp_isaConfiguration icfg; + char *sigptr; + int result; + + /* Search for the $PnP signature */ + pnp_Icheck = NULL; + for (sigptr = PTOV(0xf0000); sigptr < PTOV(0xfffff); sigptr += 16) + if (!bcmp(sigptr, "$PnP", 4)) { + pnp_Icheck = (struct pnp_ICstructure *)sigptr; + break; + } + + /* No signature, no BIOS */ + if (pnp_Icheck == NULL) + return(1); + + /* + * Fetch the system table parameters as a test of the BIOS + */ + result = biospnp_f00(vsegofs(&pnp_NumNodes), vsegofs(&pnp_NodeSize)); + if (result != PNP_SUCCESS) { + return(1); + } + + /* + * Look for the PnP ISA configuration table + */ + result = biospnp_f40(vsegofs(&icfg)); + switch (result) { + case PNP_SUCCESS: + /* If the BIOS found some PnP devices, take its hint for the read port */ + if ((icfg.ic_revision == 1) && (icfg.ic_nCSN > 0)) + isapnp_readport = icfg.ic_rdport; + break; + case PNP_FUNCTION_NOT_SUPPORTED: + /* The BIOS says there is no ISA bus (should we trust that this works?) */ + printf("PnP BIOS claims no ISA bus\n"); + isapnp_readport = -1; + break; + } + return(0); +} + +static void +biospnp_enumerate(void) +{ + u_int8_t Node; + struct pnp_devNode *devNodeBuffer; + int result; + struct pnpinfo *pi; + int count; + + /* Init/check state */ + if (biospnp_init()) + return; + + devNodeBuffer = (struct pnp_devNode *)alloca(pnp_NodeSize); + Node = 0; + count = 1000; + while((Node != 0xff) && (count-- > 0)) { + result = biospnp_f01(vsegofs(&Node), vsegofs(devNodeBuffer), 0x1); + if (result != PNP_SUCCESS) { + printf("PnP BIOS node %d: error 0x%x\n", Node, result); + } else { + pi = pnp_allocinfo(); + pnp_addident(pi, pnp_eisaformat(devNodeBuffer->dn_id)); + biospnp_scanresdata(pi, devNodeBuffer); + pnp_addinfo(pi); + } + } +} + +/* + * Scan the resource data in the node's data area for compatible device IDs + * and descriptions. + */ +static void +biospnp_scanresdata(struct pnpinfo *pi, struct pnp_devNode *dn) +{ + u_int tag, i, rlen, dlen; + u_int8_t *p; + char *str; + + p = dn->dn_data; /* point to resource data */ + dlen = dn->dn_size - (p - (u_int8_t *)dn); /* length of resource data */ + + for (i = 0; i < dlen; i+= rlen) { + tag = p[i]; + i++; + if (PNP_RES_TYPE(tag) == 0) { + rlen = PNP_SRES_LEN(tag); + /* small resource */ + switch (PNP_SRES_NUM(tag)) { + + case COMP_DEVICE_ID: + /* got a compatible device ID */ + pnp_addident(pi, pnp_eisaformat(p + i)); + break; + + case END_TAG: + return; + } + } else { + /* large resource */ + rlen = *(u_int16_t *)(p + i); + i += sizeof(u_int16_t); + + switch(PNP_LRES_NUM(tag)) { + + case ID_STRING_ANSI: + str = malloc(rlen + 1); + bcopy(p + i, str, rlen); + str[rlen] = 0; + if (pi->pi_desc == NULL) { + pi->pi_desc = str; + } else { + free(str); + } + break; + } + } + } +} + + +/* + * Make a 16-bit realmode PnP BIOS call. + * + * The first argument passed is the function number, the last is the + * BIOS data segment selector. Intermediate arguments may be 16 or + * 32 bytes in length, and are described by the format string. + * + * Arguments to the BIOS functions must be packed on the stack, hence + * this evil. + */ +static int +biospnp_call(int func, const char *fmt, ...) +{ + va_list ap; + const char *p; + u_int8_t *argp; + u_int32_t args[4]; + u_int32_t i; + + /* function number first */ + argp = (u_int8_t *)args; + *(u_int16_t *)argp = func; + argp += sizeof(u_int16_t); + + /* take args according to format */ + va_start(ap, fmt); + for (p = fmt; *p != 0; p++) { + switch(*p) { + + case 'w': + i = va_arg(ap, u_int); + *(u_int16_t *)argp = i; + argp += sizeof(u_int16_t); + break; + + case 'l': + i = va_arg(ap, u_int32_t); + *(u_int32_t *)argp = i; + argp += sizeof(u_int32_t); + break; + } + } + va_end(ap); + + /* BIOS segment last */ + *(u_int16_t *)argp = pnp_Icheck->pnp_rmds; + argp += sizeof(u_int16_t); + + /* prepare for call */ + v86.ctl = V86_ADDR | V86_CALLF; + v86.addr = ((u_int32_t)pnp_Icheck->pnp_rmcs << 16) + pnp_Icheck->pnp_rmip; + + /* call with packed stack and return */ + v86bios(args[0], args[1], args[2], args[3]); + return(v86.eax & 0xffff); +} diff --git a/stand/i386/libi386/biossmap.c b/stand/i386/libi386/biossmap.c new file mode 100644 index 0000000..e95ea64 --- /dev/null +++ b/stand/i386/libi386/biossmap.c @@ -0,0 +1,159 @@ +/*- + * Copyright (c) 1998 Michael Smith <msmith@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. + * + * 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * Obtain memory configuration information from the BIOS + */ +#include <stand.h> +#include <sys/param.h> +#include <sys/linker.h> +#include <sys/queue.h> +#include <sys/stddef.h> +#include <machine/metadata.h> +#include <machine/pc/bios.h> +#include "bootstrap.h" +#include "libi386.h" +#include "btxv86.h" + +struct smap_buf { + struct bios_smap smap; + uint32_t xattr; /* Extended attribute from ACPI 3.0 */ + STAILQ_ENTRY(smap_buf) bufs; +}; + +#define SMAP_BUFSIZE offsetof(struct smap_buf, bufs) + +static struct bios_smap *smapbase; +static uint32_t *smapattr; +static u_int smaplen; + +void +bios_getsmap(void) +{ + struct smap_buf buf; + STAILQ_HEAD(smap_head, smap_buf) head = + STAILQ_HEAD_INITIALIZER(head); + struct smap_buf *cur, *next; + u_int n, x; + + STAILQ_INIT(&head); + n = 0; + x = 0; + v86.ebx = 0; + do { + v86.ctl = V86_FLAGS; + v86.addr = 0x15; + v86.eax = 0xe820; /* int 0x15 function 0xe820 */ + v86.ecx = SMAP_BUFSIZE; + v86.edx = SMAP_SIG; + v86.es = VTOPSEG(&buf); + v86.edi = VTOPOFF(&buf); + v86int(); + if (V86_CY(v86.efl) || v86.eax != SMAP_SIG || + v86.ecx < sizeof(buf.smap) || v86.ecx > SMAP_BUFSIZE) + break; + + next = malloc(sizeof(*next)); + if (next == NULL) + break; + next->smap = buf.smap; + if (v86.ecx == SMAP_BUFSIZE) { + next->xattr = buf.xattr; + x++; + } + STAILQ_INSERT_TAIL(&head, next, bufs); + n++; + } while (v86.ebx != 0); + smaplen = n; + + if (smaplen > 0) { + smapbase = malloc(smaplen * sizeof(*smapbase)); + if (smapbase != NULL) { + n = 0; + STAILQ_FOREACH(cur, &head, bufs) + smapbase[n++] = cur->smap; + } + if (smaplen == x) { + smapattr = malloc(smaplen * sizeof(*smapattr)); + if (smapattr != NULL) { + n = 0; + STAILQ_FOREACH(cur, &head, bufs) + smapattr[n++] = cur->xattr & + SMAP_XATTR_MASK; + } + } else + smapattr = NULL; + cur = STAILQ_FIRST(&head); + while (cur != NULL) { + next = STAILQ_NEXT(cur, bufs); + free(cur); + cur = next; + } + } +} + +void +bios_addsmapdata(struct preloaded_file *kfp) +{ + size_t size; + + if (smapbase == NULL || smaplen == 0) + return; + size = smaplen * sizeof(*smapbase); + file_addmetadata(kfp, MODINFOMD_SMAP, size, smapbase); + if (smapattr != NULL) { + size = smaplen * sizeof(*smapattr); + file_addmetadata(kfp, MODINFOMD_SMAP_XATTR, size, smapattr); + } +} + +COMMAND_SET(smap, "smap", "show BIOS SMAP", command_smap); + +static int +command_smap(int argc, char *argv[]) +{ + u_int i; + + if (smapbase == NULL || smaplen == 0) + return (CMD_ERROR); + if (smapattr != NULL) + for (i = 0; i < smaplen; i++) + printf("SMAP type=%02x base=%016llx len=%016llx attr=%02x\n", + (unsigned int)smapbase[i].type, + (unsigned long long)smapbase[i].base, + (unsigned long long)smapbase[i].length, + (unsigned int)smapattr[i]); + else + for (i = 0; i < smaplen; i++) + printf("SMAP type=%02x base=%016llx len=%016llx\n", + (unsigned int)smapbase[i].type, + (unsigned long long)smapbase[i].base, + (unsigned long long)smapbase[i].length); + return (CMD_OK); +} diff --git a/stand/i386/libi386/bootinfo.c b/stand/i386/libi386/bootinfo.c new file mode 100644 index 0000000..c33cec7 --- /dev/null +++ b/stand/i386/libi386/bootinfo.c @@ -0,0 +1,170 @@ +/*- + * Copyright (c) 1998 Michael Smith <msmith@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. + * + * 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stand.h> +#include <sys/param.h> +#include <sys/reboot.h> +#include <sys/linker.h> +#include <sys/boot.h> +#include "bootstrap.h" +#include "libi386.h" +#include "btxv86.h" + +int +bi_getboothowto(char *kargs) +{ + char *cp; + char *curpos, *next, *string; + int howto; + int active; + int i; + int vidconsole; + + /* Parse kargs */ + howto = 0; + if (kargs != NULL) { + cp = kargs; + active = 0; + while (*cp != 0) { + if (!active && (*cp == '-')) { + active = 1; + } else if (active) + switch (*cp) { + case 'a': + howto |= RB_ASKNAME; + break; + case 'C': + howto |= RB_CDROM; + break; + case 'd': + howto |= RB_KDB; + break; + case 'D': + howto |= RB_MULTIPLE; + break; + case 'm': + howto |= RB_MUTE; + break; + case 'g': + howto |= RB_GDB; + break; + case 'h': + howto |= RB_SERIAL; + break; + case 'p': + howto |= RB_PAUSE; + break; + case 'r': + howto |= RB_DFLTROOT; + break; + case 's': + howto |= RB_SINGLE; + break; + case 'v': + howto |= RB_VERBOSE; + break; + default: + active = 0; + break; + } + cp++; + } + } + /* get equivalents from the environment */ + for (i = 0; howto_names[i].ev != NULL; i++) + if (getenv(howto_names[i].ev) != NULL) + howto |= howto_names[i].mask; + + /* Enable selected consoles */ + string = next = strdup(getenv("console")); + vidconsole = 0; + while (next != NULL) { + curpos = strsep(&next, " ,"); + if (*curpos == '\0') + continue; + if (!strcmp(curpos, "vidconsole")) + vidconsole = 1; + else if (!strcmp(curpos, "comconsole")) + howto |= RB_SERIAL; + else if (!strcmp(curpos, "nullconsole")) + howto |= RB_MUTE; + } + + if (vidconsole && (howto & RB_SERIAL)) + howto |= RB_MULTIPLE; + + /* + * XXX: Note that until the kernel is ready to respect multiple consoles + * for the boot messages, the first named console is the primary console + */ + if (!strcmp(string, "vidconsole")) + howto &= ~RB_SERIAL; + + free(string); + + return(howto); +} + +void +bi_setboothowto(int howto) +{ + int i; + + for (i = 0; howto_names[i].ev != NULL; i++) + if (howto & howto_names[i].mask) + setenv(howto_names[i].ev, "YES", 1); +} + +/* + * Copy the environment into the load area starting at (addr). + * Each variable is formatted as <name>=<value>, with a single nul + * separating each variable, and a double nul terminating the environment. + */ +vm_offset_t +bi_copyenv(vm_offset_t addr) +{ + struct env_var *ep; + + /* traverse the environment */ + for (ep = environ; ep != NULL; ep = ep->ev_next) { + i386_copyin(ep->ev_name, addr, strlen(ep->ev_name)); + addr += strlen(ep->ev_name); + i386_copyin("=", addr, 1); + addr++; + if (ep->ev_value != NULL) { + i386_copyin(ep->ev_value, addr, strlen(ep->ev_value)); + addr += strlen(ep->ev_value); + } + i386_copyin("", addr, 1); + addr++; + } + i386_copyin("", addr, 1); + addr++; + return(addr); +} diff --git a/stand/i386/libi386/bootinfo32.c b/stand/i386/libi386/bootinfo32.c new file mode 100644 index 0000000..494688f --- /dev/null +++ b/stand/i386/libi386/bootinfo32.c @@ -0,0 +1,291 @@ +/*- + * Copyright (c) 1998 Michael Smith <msmith@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. + * + * 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stand.h> +#include <sys/param.h> +#include <sys/reboot.h> +#include <sys/linker.h> +#include <machine/bootinfo.h> +#include <machine/metadata.h> +#include "bootstrap.h" +#include "libi386.h" +#include "btxv86.h" + +#ifdef LOADER_GELI_SUPPORT +#include "geliboot.h" + +static const size_t keybuf_size = sizeof(struct keybuf) + + (GELI_MAX_KEYS * sizeof(struct keybuf_ent)); +#endif + +static struct bootinfo bi; + +/* + * Copy module-related data into the load area, where it can be + * used as a directory for loaded modules. + * + * Module data is presented in a self-describing format. Each datum + * is preceded by a 32-bit identifier and a 32-bit size field. + * + * Currently, the following data are saved: + * + * MOD_NAME (variable) module name (string) + * MOD_TYPE (variable) module type (string) + * MOD_ARGS (variable) module parameters (string) + * MOD_ADDR sizeof(vm_offset_t) module load address + * MOD_SIZE sizeof(size_t) module size + * MOD_METADATA (variable) type-specific metadata + */ +#define COPY32(v, a, c) { \ + u_int32_t x = (v); \ + if (c) \ + i386_copyin(&x, a, sizeof(x)); \ + a += sizeof(x); \ +} + +#define MOD_STR(t, a, s, c) { \ + COPY32(t, a, c); \ + COPY32(strlen(s) + 1, a, c); \ + if (c) \ + i386_copyin(s, a, strlen(s) + 1); \ + a += roundup(strlen(s) + 1, sizeof(u_long));\ +} + +#define MOD_NAME(a, s, c) MOD_STR(MODINFO_NAME, a, s, c) +#define MOD_TYPE(a, s, c) MOD_STR(MODINFO_TYPE, a, s, c) +#define MOD_ARGS(a, s, c) MOD_STR(MODINFO_ARGS, a, s, c) + +#define MOD_VAR(t, a, s, c) { \ + COPY32(t, a, c); \ + COPY32(sizeof(s), a, c); \ + if (c) \ + i386_copyin(&s, a, sizeof(s)); \ + a += roundup(sizeof(s), sizeof(u_long)); \ +} + +#define MOD_ADDR(a, s, c) MOD_VAR(MODINFO_ADDR, a, s, c) +#define MOD_SIZE(a, s, c) MOD_VAR(MODINFO_SIZE, a, s, c) + +#define MOD_METADATA(a, mm, c) { \ + COPY32(MODINFO_METADATA | mm->md_type, a, c); \ + COPY32(mm->md_size, a, c); \ + if (c) \ + i386_copyin(mm->md_data, a, mm->md_size); \ + a += roundup(mm->md_size, sizeof(u_long));\ +} + +#define MOD_END(a, c) { \ + COPY32(MODINFO_END, a, c); \ + COPY32(0, a, c); \ +} + +static vm_offset_t +bi_copymodules32(vm_offset_t addr) +{ + struct preloaded_file *fp; + struct file_metadata *md; + int c; + + c = addr != 0; + /* start with the first module on the list, should be the kernel */ + for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) { + + MOD_NAME(addr, fp->f_name, c); /* this field must come first */ + MOD_TYPE(addr, fp->f_type, c); + if (fp->f_args) + MOD_ARGS(addr, fp->f_args, c); + MOD_ADDR(addr, fp->f_addr, c); + MOD_SIZE(addr, fp->f_size, c); + for (md = fp->f_metadata; md != NULL; md = md->md_next) + if (!(md->md_type & MODINFOMD_NOCOPY)) + MOD_METADATA(addr, md, c); + } + MOD_END(addr, c); + return(addr); +} + +/* + * Load the information expected by an i386 kernel. + * + * - The 'boothowto' argument is constructed + * - The 'bootdev' argument is constructed + * - The 'bootinfo' struct is constructed, and copied into the kernel space. + * - The kernel environment is copied into kernel space. + * - Module metadata are formatted and placed in kernel space. + */ +int +bi_load32(char *args, int *howtop, int *bootdevp, vm_offset_t *bip, vm_offset_t *modulep, vm_offset_t *kernendp) +{ + struct preloaded_file *xp, *kfp; + struct i386_devdesc *rootdev; + struct file_metadata *md; + vm_offset_t addr; + vm_offset_t kernend; + vm_offset_t envp; + vm_offset_t size; + vm_offset_t ssym, esym; + char *rootdevname; + int bootdevnr, i, howto; + char *kernelname; + const char *kernelpath; +#ifdef LOADER_GELI_SUPPORT + char buf[keybuf_size]; + struct keybuf *keybuf = (struct keybuf *)buf; +#endif + + howto = bi_getboothowto(args); + + /* + * Allow the environment variable 'rootdev' to override the supplied device + * This should perhaps go to MI code and/or have $rootdev tested/set by + * MI code before launching the kernel. + */ + rootdevname = getenv("rootdev"); + i386_getdev((void **)(&rootdev), rootdevname, NULL); + if (rootdev == NULL) { /* bad $rootdev/$currdev */ + printf("can't determine root device\n"); + return(EINVAL); + } + + /* Try reading the /etc/fstab file to select the root device */ + getrootmount(i386_fmtdev((void *)rootdev)); + + /* Do legacy rootdev guessing */ + + /* XXX - use a default bootdev of 0. Is this ok??? */ + bootdevnr = 0; + + switch(rootdev->d_type) { + case DEVT_CD: + /* Pass in BIOS device number. */ + bi.bi_bios_dev = bc_unit2bios(rootdev->d_unit); + bootdevnr = bc_getdev(rootdev); + break; + + case DEVT_DISK: + /* pass in the BIOS device number of the current disk */ + bi.bi_bios_dev = bd_unit2bios(rootdev->d_unit); + bootdevnr = bd_getdev(rootdev); + break; + + case DEVT_NET: + case DEVT_ZFS: + break; + + default: + printf("WARNING - don't know how to boot from device type %d\n", rootdev->d_type); + } + if (bootdevnr == -1) { + printf("root device %s invalid\n", i386_fmtdev(rootdev)); + return (EINVAL); + } + free(rootdev); + + /* find the last module in the chain */ + addr = 0; + for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) { + if (addr < (xp->f_addr + xp->f_size)) + addr = xp->f_addr + xp->f_size; + } + /* pad to a page boundary */ + addr = roundup(addr, PAGE_SIZE); + + /* copy our environment */ + envp = addr; + addr = bi_copyenv(addr); + + /* pad to a page boundary */ + addr = roundup(addr, PAGE_SIZE); + + kfp = file_findfile(NULL, "elf kernel"); + if (kfp == NULL) + kfp = file_findfile(NULL, "elf32 kernel"); + if (kfp == NULL) + panic("can't find kernel file"); + kernend = 0; /* fill it in later */ + file_addmetadata(kfp, MODINFOMD_HOWTO, sizeof howto, &howto); + file_addmetadata(kfp, MODINFOMD_ENVP, sizeof envp, &envp); + file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof kernend, &kernend); + bios_addsmapdata(kfp); +#ifdef LOADER_GELI_SUPPORT + geli_fill_keybuf(keybuf); + file_addmetadata(kfp, MODINFOMD_KEYBUF, keybuf_size, buf); + bzero(buf, sizeof(buf)); +#endif + + /* Figure out the size and location of the metadata */ + *modulep = addr; + size = bi_copymodules32(0); + kernend = roundup(addr + size, PAGE_SIZE); + *kernendp = kernend; + + /* patch MODINFOMD_KERNEND */ + md = file_findmetadata(kfp, MODINFOMD_KERNEND); + bcopy(&kernend, md->md_data, sizeof kernend); + + /* copy module list and metadata */ + (void)bi_copymodules32(addr); + + ssym = esym = 0; + md = file_findmetadata(kfp, MODINFOMD_SSYM); + if (md != NULL) + ssym = *((vm_offset_t *)&(md->md_data)); + md = file_findmetadata(kfp, MODINFOMD_ESYM); + if (md != NULL) + esym = *((vm_offset_t *)&(md->md_data)); + if (ssym == 0 || esym == 0) + ssym = esym = 0; /* sanity */ + + /* legacy bootinfo structure */ + kernelname = getenv("kernelname"); + i386_getdev(NULL, kernelname, &kernelpath); + bi.bi_version = BOOTINFO_VERSION; + bi.bi_kernelname = 0; /* XXX char * -> kernel name */ + bi.bi_nfs_diskless = 0; /* struct nfs_diskless * */ + bi.bi_n_bios_used = 0; /* XXX would have to hook biosdisk driver for these */ + for (i = 0; i < N_BIOS_GEOM; i++) + bi.bi_bios_geom[i] = bd_getbigeom(i); + bi.bi_size = sizeof(bi); + bi.bi_memsizes_valid = 1; + bi.bi_basemem = bios_basemem / 1024; + bi.bi_extmem = bios_extmem / 1024; + bi.bi_envp = envp; + bi.bi_modulep = *modulep; + bi.bi_kernend = kernend; + bi.bi_kernelname = VTOP(kernelpath); + bi.bi_symtab = ssym; /* XXX this is only the primary kernel symtab */ + bi.bi_esymtab = esym; + + /* legacy boot arguments */ + *howtop = howto | RB_BOOTINFO; + *bootdevp = bootdevnr; + *bip = VTOP(&bi); + + return(0); +} diff --git a/stand/i386/libi386/bootinfo64.c b/stand/i386/libi386/bootinfo64.c new file mode 100644 index 0000000..02fcf72 --- /dev/null +++ b/stand/i386/libi386/bootinfo64.c @@ -0,0 +1,280 @@ +/*- + * Copyright (c) 1998 Michael Smith <msmith@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. + * + * 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stand.h> +#include <sys/param.h> +#include <sys/reboot.h> +#include <sys/linker.h> +#include <machine/bootinfo.h> +#include <machine/cpufunc.h> +#include <machine/metadata.h> +#include <machine/psl.h> +#include <machine/specialreg.h> +#include "bootstrap.h" +#include "libi386.h" +#include "btxv86.h" + +#ifdef LOADER_GELI_SUPPORT +#include "geliboot.h" + +static const size_t keybuf_size = sizeof(struct keybuf) + + (GELI_MAX_KEYS * sizeof(struct keybuf_ent)); +#endif + +/* + * Copy module-related data into the load area, where it can be + * used as a directory for loaded modules. + * + * Module data is presented in a self-describing format. Each datum + * is preceded by a 32-bit identifier and a 32-bit size field. + * + * Currently, the following data are saved: + * + * MOD_NAME (variable) module name (string) + * MOD_TYPE (variable) module type (string) + * MOD_ARGS (variable) module parameters (string) + * MOD_ADDR sizeof(vm_offset_t) module load address + * MOD_SIZE sizeof(size_t) module size + * MOD_METADATA (variable) type-specific metadata + */ +#define COPY32(v, a, c) { \ + u_int32_t x = (v); \ + if (c) \ + i386_copyin(&x, a, sizeof(x)); \ + a += sizeof(x); \ +} + +#define MOD_STR(t, a, s, c) { \ + COPY32(t, a, c); \ + COPY32(strlen(s) + 1, a, c); \ + if (c) \ + i386_copyin(s, a, strlen(s) + 1); \ + a += roundup(strlen(s) + 1, sizeof(u_int64_t));\ +} + +#define MOD_NAME(a, s, c) MOD_STR(MODINFO_NAME, a, s, c) +#define MOD_TYPE(a, s, c) MOD_STR(MODINFO_TYPE, a, s, c) +#define MOD_ARGS(a, s, c) MOD_STR(MODINFO_ARGS, a, s, c) + +#define MOD_VAR(t, a, s, c) { \ + COPY32(t, a, c); \ + COPY32(sizeof(s), a, c); \ + if (c) \ + i386_copyin(&s, a, sizeof(s)); \ + a += roundup(sizeof(s), sizeof(u_int64_t)); \ +} + +#define MOD_ADDR(a, s, c) MOD_VAR(MODINFO_ADDR, a, s, c) +#define MOD_SIZE(a, s, c) MOD_VAR(MODINFO_SIZE, a, s, c) + +#define MOD_METADATA(a, mm, c) { \ + COPY32(MODINFO_METADATA | mm->md_type, a, c); \ + COPY32(mm->md_size, a, c); \ + if (c) \ + i386_copyin(mm->md_data, a, mm->md_size); \ + a += roundup(mm->md_size, sizeof(u_int64_t));\ +} + +#define MOD_END(a, c) { \ + COPY32(MODINFO_END, a, c); \ + COPY32(0, a, c); \ +} + +static vm_offset_t +bi_copymodules64(vm_offset_t addr) +{ + struct preloaded_file *fp; + struct file_metadata *md; + int c; + u_int64_t v; + + c = addr != 0; + /* start with the first module on the list, should be the kernel */ + for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) { + + MOD_NAME(addr, fp->f_name, c); /* this field must come first */ + MOD_TYPE(addr, fp->f_type, c); + if (fp->f_args) + MOD_ARGS(addr, fp->f_args, c); + v = fp->f_addr; + MOD_ADDR(addr, v, c); + v = fp->f_size; + MOD_SIZE(addr, v, c); + for (md = fp->f_metadata; md != NULL; md = md->md_next) + if (!(md->md_type & MODINFOMD_NOCOPY)) + MOD_METADATA(addr, md, c); + } + MOD_END(addr, c); + return(addr); +} + +/* + * Check to see if this CPU supports long mode. + */ +static int +bi_checkcpu(void) +{ + char *cpu_vendor; + int vendor[3]; + int eflags; + unsigned int regs[4]; + + /* Check for presence of "cpuid". */ + eflags = read_eflags(); + write_eflags(eflags ^ PSL_ID); + if (!((eflags ^ read_eflags()) & PSL_ID)) + return (0); + + /* Fetch the vendor string. */ + do_cpuid(0, regs); + vendor[0] = regs[1]; + vendor[1] = regs[3]; + vendor[2] = regs[2]; + cpu_vendor = (char *)vendor; + + /* Check for vendors that support AMD features. */ + if (strncmp(cpu_vendor, INTEL_VENDOR_ID, 12) != 0 && + strncmp(cpu_vendor, AMD_VENDOR_ID, 12) != 0 && + strncmp(cpu_vendor, CENTAUR_VENDOR_ID, 12) != 0) + return (0); + + /* Has to support AMD features. */ + do_cpuid(0x80000000, regs); + if (!(regs[0] >= 0x80000001)) + return (0); + + /* Check for long mode. */ + do_cpuid(0x80000001, regs); + return (regs[3] & AMDID_LM); +} + +/* + * Load the information expected by an amd64 kernel. + * + * - The 'boothowto' argument is constructed + * - The 'bootdev' argument is constructed + * - The 'bootinfo' struct is constructed, and copied into the kernel space. + * - The kernel environment is copied into kernel space. + * - Module metadata are formatted and placed in kernel space. + */ +int +bi_load64(char *args, vm_offset_t addr, vm_offset_t *modulep, + vm_offset_t *kernendp, int add_smap) +{ + struct preloaded_file *xp, *kfp; + struct i386_devdesc *rootdev; + struct file_metadata *md; + u_int64_t kernend; + u_int64_t envp; + u_int64_t module; + vm_offset_t size; + char *rootdevname; + int howto; +#ifdef LOADER_GELI_SUPPORT + char buf[keybuf_size]; + struct keybuf *keybuf = (struct keybuf *)buf; +#endif + + if (!bi_checkcpu()) { + printf("CPU doesn't support long mode\n"); + return (EINVAL); + } + + howto = bi_getboothowto(args); + + /* + * Allow the environment variable 'rootdev' to override the supplied device + * This should perhaps go to MI code and/or have $rootdev tested/set by + * MI code before launching the kernel. + */ + rootdevname = getenv("rootdev"); + i386_getdev((void **)(&rootdev), rootdevname, NULL); + if (rootdev == NULL) { /* bad $rootdev/$currdev */ + printf("can't determine root device\n"); + return(EINVAL); + } + + /* Try reading the /etc/fstab file to select the root device */ + getrootmount(i386_fmtdev((void *)rootdev)); + + if (addr == 0) { + /* find the last module in the chain */ + for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) { + if (addr < (xp->f_addr + xp->f_size)) + addr = xp->f_addr + xp->f_size; + } + } + /* pad to a page boundary */ + addr = roundup(addr, PAGE_SIZE); + + /* place the metadata before anything */ + module = *modulep = addr; + + kfp = file_findfile(NULL, "elf kernel"); + if (kfp == NULL) + kfp = file_findfile(NULL, "elf64 kernel"); + if (kfp == NULL) + panic("can't find kernel file"); + kernend = 0; /* fill it in later */ + file_addmetadata(kfp, MODINFOMD_HOWTO, sizeof howto, &howto); + file_addmetadata(kfp, MODINFOMD_ENVP, sizeof envp, &envp); + file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof kernend, &kernend); + file_addmetadata(kfp, MODINFOMD_MODULEP, sizeof module, &module); + if (add_smap != 0) + bios_addsmapdata(kfp); + +#ifdef LOADER_GELI_SUPPORT + geli_fill_keybuf(keybuf); + file_addmetadata(kfp, MODINFOMD_KEYBUF, keybuf_size, buf); + bzero(buf, sizeof(buf)); +#endif + + size = bi_copymodules64(0); + + /* copy our environment */ + envp = roundup(addr + size, PAGE_SIZE); + addr = bi_copyenv(envp); + + /* set kernend */ + kernend = roundup(addr, PAGE_SIZE); + *kernendp = kernend; + + /* patch MODINFOMD_KERNEND */ + md = file_findmetadata(kfp, MODINFOMD_KERNEND); + bcopy(&kernend, md->md_data, sizeof kernend); + + /* patch MODINFOMD_ENVP */ + md = file_findmetadata(kfp, MODINFOMD_ENVP); + bcopy(&envp, md->md_data, sizeof envp); + + /* copy module list and metadata */ + (void)bi_copymodules64(*modulep); + + return(0); +} diff --git a/stand/i386/libi386/comconsole.c b/stand/i386/libi386/comconsole.c new file mode 100644 index 0000000..f98a1f7 --- /dev/null +++ b/stand/i386/libi386/comconsole.c @@ -0,0 +1,385 @@ +/*- + * Copyright (c) 1998 Michael Smith (msmith@freebsd.org) + * + * 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 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stand.h> +#include <bootstrap.h> +#include <machine/cpufunc.h> +#include <dev/ic/ns16550.h> +#include <dev/pci/pcireg.h> +#include "libi386.h" + +#define COMC_FMT 0x3 /* 8N1 */ +#define COMC_TXWAIT 0x40000 /* transmit timeout */ +#define COMC_BPS(x) (115200 / (x)) /* speed to DLAB divisor */ +#define COMC_DIV2BPS(x) (115200 / (x)) /* DLAB divisor to speed */ + +#ifndef COMPORT +#define COMPORT 0x3f8 +#endif +#ifndef COMSPEED +#define COMSPEED 9600 +#endif + +static void comc_probe(struct console *cp); +static int comc_init(int arg); +static void comc_putchar(int c); +static int comc_getchar(void); +static int comc_getspeed(void); +static int comc_ischar(void); +static int comc_parseint(const char *string); +static uint32_t comc_parse_pcidev(const char *string); +static int comc_pcidev_set(struct env_var *ev, int flags, + const void *value); +static int comc_pcidev_handle(uint32_t locator); +static int comc_port_set(struct env_var *ev, int flags, + const void *value); +static void comc_setup(int speed, int port); +static int comc_speed_set(struct env_var *ev, int flags, + const void *value); + +static int comc_curspeed; +static int comc_port = COMPORT; +static uint32_t comc_locator; + +struct console comconsole = { + "comconsole", + "serial port", + 0, + comc_probe, + comc_init, + comc_putchar, + comc_getchar, + comc_ischar +}; + +static void +comc_probe(struct console *cp) +{ + char intbuf[16]; + char *cons, *env; + int speed, port; + uint32_t locator; + + if (comc_curspeed == 0) { + comc_curspeed = COMSPEED; + /* + * Assume that the speed was set by an earlier boot loader if + * comconsole is already the preferred console. + */ + cons = getenv("console"); + if ((cons != NULL && strcmp(cons, comconsole.c_name) == 0) || + getenv("boot_multicons") != NULL) { + comc_curspeed = comc_getspeed(); + } + + env = getenv("comconsole_speed"); + if (env != NULL) { + speed = comc_parseint(env); + if (speed > 0) + comc_curspeed = speed; + } + + sprintf(intbuf, "%d", comc_curspeed); + unsetenv("comconsole_speed"); + env_setenv("comconsole_speed", EV_VOLATILE, intbuf, comc_speed_set, + env_nounset); + + env = getenv("comconsole_port"); + if (env != NULL) { + port = comc_parseint(env); + if (port > 0) + comc_port = port; + } + + sprintf(intbuf, "%d", comc_port); + unsetenv("comconsole_port"); + env_setenv("comconsole_port", EV_VOLATILE, intbuf, comc_port_set, + env_nounset); + + env = getenv("comconsole_pcidev"); + if (env != NULL) { + locator = comc_parse_pcidev(env); + if (locator != 0) + comc_pcidev_handle(locator); + } + + unsetenv("comconsole_pcidev"); + env_setenv("comconsole_pcidev", EV_VOLATILE, env, comc_pcidev_set, + env_nounset); + } + comc_setup(comc_curspeed, comc_port); +} + +static int +comc_init(int arg) +{ + + comc_setup(comc_curspeed, comc_port); + + if ((comconsole.c_flags & (C_PRESENTIN | C_PRESENTOUT)) == + (C_PRESENTIN | C_PRESENTOUT)) + return (CMD_OK); + return (CMD_ERROR); +} + +static void +comc_putchar(int c) +{ + int wait; + + for (wait = COMC_TXWAIT; wait > 0; wait--) + if (inb(comc_port + com_lsr) & LSR_TXRDY) { + outb(comc_port + com_data, (u_char)c); + break; + } +} + +static int +comc_getchar(void) +{ + return (comc_ischar() ? inb(comc_port + com_data) : -1); +} + +static int +comc_ischar(void) +{ + return (inb(comc_port + com_lsr) & LSR_RXRDY); +} + +static int +comc_speed_set(struct env_var *ev, int flags, const void *value) +{ + int speed; + + if (value == NULL || (speed = comc_parseint(value)) <= 0) { + printf("Invalid speed\n"); + return (CMD_ERROR); + } + + if (comc_curspeed != speed) + comc_setup(speed, comc_port); + + env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); + + return (CMD_OK); +} + +static int +comc_port_set(struct env_var *ev, int flags, const void *value) +{ + int port; + + if (value == NULL || (port = comc_parseint(value)) <= 0) { + printf("Invalid port\n"); + return (CMD_ERROR); + } + + if (comc_port != port) + comc_setup(comc_curspeed, port); + + env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); + + return (CMD_OK); +} + +/* + * Input: bus:dev:func[:bar]. If bar is not specified, it is 0x10. + * Output: bar[24:16] bus[15:8] dev[7:3] func[2:0] + */ +static uint32_t +comc_parse_pcidev(const char *string) +{ +#ifdef NO_PCI + return (0); +#else + char *p, *p1; + uint8_t bus, dev, func, bar; + uint32_t locator; + int pres; + + pres = strtol(string, &p, 0); + if (p == string || *p != ':' || pres < 0 ) + return (0); + bus = pres; + p1 = ++p; + + pres = strtol(p1, &p, 0); + if (p == string || *p != ':' || pres < 0 ) + return (0); + dev = pres; + p1 = ++p; + + pres = strtol(p1, &p, 0); + if (p == string || (*p != ':' && *p != '\0') || pres < 0 ) + return (0); + func = pres; + + if (*p == ':') { + p1 = ++p; + pres = strtol(p1, &p, 0); + if (p == string || *p != '\0' || pres <= 0 ) + return (0); + bar = pres; + } else + bar = 0x10; + + locator = (bar << 16) | biospci_locator(bus, dev, func); + return (locator); +#endif +} + +static int +comc_pcidev_handle(uint32_t locator) +{ +#ifdef NO_PCI + return (CMD_ERROR); +#else + char intbuf[64]; + uint32_t port; + + if (biospci_read_config(locator & 0xffff, + (locator & 0xff0000) >> 16, 2, &port) == -1) { + printf("Cannot read bar at 0x%x\n", locator); + return (CMD_ERROR); + } + + /* + * biospci_read_config() sets port == 0xffffffff if the pcidev + * isn't found on the bus. Check for 0xffffffff and return to not + * panic in BTX. + */ + if (port == 0xffffffff) { + printf("Cannot find specified pcidev\n"); + return (CMD_ERROR); + } + if (!PCI_BAR_IO(port)) { + printf("Memory bar at 0x%x\n", locator); + return (CMD_ERROR); + } + port &= PCIM_BAR_IO_BASE; + + sprintf(intbuf, "%d", port); + unsetenv("comconsole_port"); + env_setenv("comconsole_port", EV_VOLATILE, intbuf, + comc_port_set, env_nounset); + + comc_setup(comc_curspeed, port); + comc_locator = locator; + + return (CMD_OK); +#endif +} + +static int +comc_pcidev_set(struct env_var *ev, int flags, const void *value) +{ + uint32_t locator; + int error; + + if (value == NULL || (locator = comc_parse_pcidev(value)) <= 0) { + printf("Invalid pcidev\n"); + return (CMD_ERROR); + } + if ((comconsole.c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) != 0 && + comc_locator != locator) { + error = comc_pcidev_handle(locator); + if (error != CMD_OK) + return (error); + } + env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); + return (CMD_OK); +} + +static void +comc_setup(int speed, int port) +{ + static int TRY_COUNT = 1000000; + char intbuf[64]; + int tries; + + unsetenv("hw.uart.console"); + comc_curspeed = speed; + comc_port = port; + if ((comconsole.c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) == 0) + return; + + outb(comc_port + com_cfcr, CFCR_DLAB | COMC_FMT); + outb(comc_port + com_dlbl, COMC_BPS(speed) & 0xff); + outb(comc_port + com_dlbh, COMC_BPS(speed) >> 8); + outb(comc_port + com_cfcr, COMC_FMT); + outb(comc_port + com_mcr, MCR_RTS | MCR_DTR); + + tries = 0; + do + inb(comc_port + com_data); + while (inb(comc_port + com_lsr) & LSR_RXRDY && ++tries < TRY_COUNT); + + if (tries < TRY_COUNT) { + comconsole.c_flags |= (C_PRESENTIN | C_PRESENTOUT); + sprintf(intbuf, "io:%d,br:%d", comc_port, comc_curspeed); + env_setenv("hw.uart.console", EV_VOLATILE, intbuf, NULL, NULL); + } else + comconsole.c_flags &= ~(C_PRESENTIN | C_PRESENTOUT); +} + +static int +comc_parseint(const char *speedstr) +{ + char *p; + int speed; + + speed = strtol(speedstr, &p, 0); + if (p == speedstr || *p != '\0' || speed <= 0) + return (-1); + + return (speed); +} + +static int +comc_getspeed(void) +{ + u_int divisor; + u_char dlbh; + u_char dlbl; + u_char cfcr; + + cfcr = inb(comc_port + com_cfcr); + outb(comc_port + com_cfcr, CFCR_DLAB | cfcr); + + dlbl = inb(comc_port + com_dlbl); + dlbh = inb(comc_port + com_dlbh); + + outb(comc_port + com_cfcr, cfcr); + + divisor = dlbh << 8 | dlbl; + + /* XXX there should be more sanity checking. */ + if (divisor == 0) + return (COMSPEED); + return (COMC_DIV2BPS(divisor)); +} diff --git a/stand/i386/libi386/devicename.c b/stand/i386/libi386/devicename.c new file mode 100644 index 0000000..c7705d7 --- /dev/null +++ b/stand/i386/libi386/devicename.c @@ -0,0 +1,206 @@ +/*- + * Copyright (c) 1998 Michael Smith <msmith@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. + * + * 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stand.h> +#include <string.h> +#include "bootstrap.h" +#include "disk.h" +#include "libi386.h" +#include "../zfs/libzfs.h" + +static int i386_parsedev(struct i386_devdesc **dev, const char *devspec, const char **path); + +/* + * Point (dev) at an allocated device specifier for the device matching the + * path in (devspec). If it contains an explicit device specification, + * use that. If not, use the default device. + */ +int +i386_getdev(void **vdev, const char *devspec, const char **path) +{ + struct i386_devdesc **dev = (struct i386_devdesc **)vdev; + int rv; + + /* + * If it looks like this is just a path and no + * device, go with the current device. + */ + if ((devspec == NULL) || + (devspec[0] == '/') || + (strchr(devspec, ':') == NULL)) { + + if (((rv = i386_parsedev(dev, getenv("currdev"), NULL)) == 0) && + (path != NULL)) + *path = devspec; + return(rv); + } + + /* + * Try to parse the device name off the beginning of the devspec + */ + return(i386_parsedev(dev, devspec, path)); +} + +/* + * Point (dev) at an allocated device specifier matching the string version + * at the beginning of (devspec). Return a pointer to the remaining + * text in (path). + * + * In all cases, the beginning of (devspec) is compared to the names + * of known devices in the device switch, and then any following text + * is parsed according to the rules applied to the device type. + * + * For disk-type devices, the syntax is: + * + * disk<unit>[s<slice>][<partition>]: + * + */ +static int +i386_parsedev(struct i386_devdesc **dev, const char *devspec, const char **path) +{ + struct i386_devdesc *idev; + struct devsw *dv; + int i, unit, err; + char *cp; + const char *np; + + /* minimum length check */ + if (strlen(devspec) < 2) + return(EINVAL); + + /* look for a device that matches */ + for (i = 0, dv = NULL; devsw[i] != NULL; i++) { + if (!strncmp(devspec, devsw[i]->dv_name, strlen(devsw[i]->dv_name))) { + dv = devsw[i]; + break; + } + } + if (dv == NULL) + return(ENOENT); + idev = malloc(sizeof(struct i386_devdesc)); + err = 0; + np = (devspec + strlen(dv->dv_name)); + + switch(dv->dv_type) { + case DEVT_NONE: /* XXX what to do here? Do we care? */ + break; + + case DEVT_DISK: + err = disk_parsedev((struct disk_devdesc *)idev, np, path); + if (err != 0) + goto fail; + break; + + case DEVT_CD: + case DEVT_NET: + unit = 0; + + if (*np && (*np != ':')) { + unit = strtol(np, &cp, 0); /* get unit number if present */ + if (cp == np) { + err = EUNIT; + goto fail; + } + } else { + cp = (char *)np; + } + if (*cp && (*cp != ':')) { + err = EINVAL; + goto fail; + } + + idev->d_unit = unit; + if (path != NULL) + *path = (*cp == 0) ? cp : cp + 1; + break; + case DEVT_ZFS: + err = zfs_parsedev((struct zfs_devdesc *)idev, np, path); + if (err != 0) + goto fail; + break; + default: + err = EINVAL; + goto fail; + } + idev->d_dev = dv; + idev->d_type = dv->dv_type; + if (dev == NULL) { + free(idev); + } else { + *dev = idev; + } + return(0); + + fail: + free(idev); + return(err); +} + + +char * +i386_fmtdev(void *vdev) +{ + struct i386_devdesc *dev = (struct i386_devdesc *)vdev; + static char buf[128]; /* XXX device length constant? */ + + switch(dev->d_type) { + case DEVT_NONE: + strcpy(buf, "(no device)"); + break; + + case DEVT_CD: + case DEVT_NET: + sprintf(buf, "%s%d:", dev->d_dev->dv_name, dev->d_unit); + break; + + case DEVT_DISK: + return (disk_fmtdev(vdev)); + + case DEVT_ZFS: + return(zfs_fmtdev(vdev)); + } + return(buf); +} + + +/* + * Set currdev to suit the value being supplied in (value) + */ +int +i386_setcurrdev(struct env_var *ev, int flags, const void *value) +{ + struct i386_devdesc *ncurr; + int rv; + + if ((rv = i386_parsedev(&ncurr, value, NULL)) != 0) + return(rv); + free(ncurr); + env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); + return(0); +} diff --git a/stand/i386/libi386/elf32_freebsd.c b/stand/i386/libi386/elf32_freebsd.c new file mode 100644 index 0000000..641e273 --- /dev/null +++ b/stand/i386/libi386/elf32_freebsd.c @@ -0,0 +1,84 @@ +/*- + * Copyright (c) 1998 Michael Smith <msmith@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. + * + * 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/exec.h> +#include <sys/linker.h> +#include <string.h> +#include <machine/bootinfo.h> +#include <machine/elf.h> +#include <stand.h> + +#include "bootstrap.h" +#include "libi386.h" +#include "btxv86.h" + +static int elf32_exec(struct preloaded_file *amp); +static int elf32_obj_exec(struct preloaded_file *amp); + +struct file_format i386_elf = { elf32_loadfile, elf32_exec }; +struct file_format i386_elf_obj = { elf32_obj_loadfile, elf32_obj_exec }; + +/* + * There is an ELF kernel and one or more ELF modules loaded. + * We wish to start executing the kernel image, so make such + * preparations as are required, and do so. + */ +static int +elf32_exec(struct preloaded_file *fp) +{ + struct file_metadata *md; + Elf_Ehdr *ehdr; + vm_offset_t entry, bootinfop, modulep, kernend; + int boothowto, err, bootdev; + + if ((md = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) + return(EFTYPE); + ehdr = (Elf_Ehdr *)&(md->md_data); + + err = bi_load32(fp->f_args, &boothowto, &bootdev, &bootinfop, &modulep, &kernend); + if (err != 0) + return(err); + entry = ehdr->e_entry & 0xffffff; + +#ifdef DEBUG + printf("Start @ 0x%lx ...\n", entry); +#endif + + dev_cleanup(); + __exec((void *)entry, boothowto, bootdev, 0, 0, 0, bootinfop, modulep, kernend); + + panic("exec returned"); +} + +static int +elf32_obj_exec(struct preloaded_file *fp) +{ + return (EFTYPE); +} diff --git a/stand/i386/libi386/elf64_freebsd.c b/stand/i386/libi386/elf64_freebsd.c new file mode 100644 index 0000000..338a745 --- /dev/null +++ b/stand/i386/libi386/elf64_freebsd.c @@ -0,0 +1,126 @@ +/*- + * Copyright (c) 1998 Michael Smith <msmith@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. + * + * 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#define __ELF_WORD_SIZE 64 +#include <sys/param.h> +#include <sys/exec.h> +#include <sys/linker.h> +#include <string.h> +#include <machine/bootinfo.h> +#include <machine/elf.h> +#include <stand.h> + +#include "bootstrap.h" +#include "libi386.h" +#include "btxv86.h" + +static int elf64_exec(struct preloaded_file *amp); +static int elf64_obj_exec(struct preloaded_file *amp); + +struct file_format amd64_elf = { elf64_loadfile, elf64_exec }; +struct file_format amd64_elf_obj = { elf64_obj_loadfile, elf64_obj_exec }; + +#define PG_V 0x001 +#define PG_RW 0x002 +#define PG_U 0x004 +#define PG_PS 0x080 + +typedef u_int64_t p4_entry_t; +typedef u_int64_t p3_entry_t; +typedef u_int64_t p2_entry_t; +extern p4_entry_t PT4[]; +extern p3_entry_t PT3[]; +extern p2_entry_t PT2[]; + +u_int32_t entry_hi; +u_int32_t entry_lo; + +extern void amd64_tramp(); + +/* + * There is an ELF kernel and one or more ELF modules loaded. + * We wish to start executing the kernel image, so make such + * preparations as are required, and do so. + */ +static int +elf64_exec(struct preloaded_file *fp) +{ + struct file_metadata *md; + Elf_Ehdr *ehdr; + vm_offset_t modulep, kernend; + int err; + int i; + + if ((md = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) + return(EFTYPE); + ehdr = (Elf_Ehdr *)&(md->md_data); + + err = bi_load64(fp->f_args, 0, &modulep, &kernend, 1); + if (err != 0) + return(err); + + bzero(PT4, PAGE_SIZE); + bzero(PT3, PAGE_SIZE); + bzero(PT2, PAGE_SIZE); + + /* + * This is kinda brutal, but every single 1GB VM memory segment points to + * the same first 1GB of physical memory. But it is more than adequate. + */ + for (i = 0; i < 512; i++) { + /* Each slot of the level 4 pages points to the same level 3 page */ + PT4[i] = (p4_entry_t)VTOP((uintptr_t)&PT3[0]); + PT4[i] |= PG_V | PG_RW | PG_U; + + /* Each slot of the level 3 pages points to the same level 2 page */ + PT3[i] = (p3_entry_t)VTOP((uintptr_t)&PT2[0]); + PT3[i] |= PG_V | PG_RW | PG_U; + + /* The level 2 page slots are mapped with 2MB pages for 1GB. */ + PT2[i] = i * (2 * 1024 * 1024); + PT2[i] |= PG_V | PG_RW | PG_PS | PG_U; + } + + entry_lo = ehdr->e_entry & 0xffffffff; + entry_hi = (ehdr->e_entry >> 32) & 0xffffffff; +#ifdef DEBUG + printf("Start @ %#llx ...\n", ehdr->e_entry); +#endif + + dev_cleanup(); + __exec((void *)VTOP(amd64_tramp), modulep, kernend); + + panic("exec returned"); +} + +static int +elf64_obj_exec(struct preloaded_file *fp) +{ + return (EFTYPE); +} diff --git a/stand/i386/libi386/i386_copy.c b/stand/i386/libi386/i386_copy.c new file mode 100644 index 0000000..3c05241 --- /dev/null +++ b/stand/i386/libi386/i386_copy.c @@ -0,0 +1,75 @@ +/*- + * Copyright (c) 1998 Michael Smith <msmith@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. + * + * 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * MD primitives supporting placement of module data + * + * XXX should check load address/size against memory top. + */ +#include <stand.h> + +#include "libi386.h" +#include "btxv86.h" + +ssize_t +i386_copyin(const void *src, vm_offset_t dest, const size_t len) +{ + if (dest + len >= memtop) { + errno = EFBIG; + return(-1); + } + + bcopy(src, PTOV(dest), len); + return(len); +} + +ssize_t +i386_copyout(const vm_offset_t src, void *dest, const size_t len) +{ + if (src + len >= memtop) { + errno = EFBIG; + return(-1); + } + + bcopy(PTOV(src), dest, len); + return(len); +} + + +ssize_t +i386_readin(const int fd, vm_offset_t dest, const size_t len) +{ + + if (dest + len >= memtop_copyin) { + errno = EFBIG; + return(-1); + } + + return (read(fd, PTOV(dest), len)); +} diff --git a/stand/i386/libi386/i386_module.c b/stand/i386/libi386/i386_module.c new file mode 100644 index 0000000..78ab61b --- /dev/null +++ b/stand/i386/libi386/i386_module.c @@ -0,0 +1,44 @@ +/*- + * Copyright (c) 1998 Michael Smith <msmith@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. + * + * 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * i386-specific module functionality. + * + */ + +/* + * Use voodoo to load modules required by current hardware. + */ +int +i386_autoload(void) +{ + + /* XXX use PnP to locate stuff here */ + return(0); +} diff --git a/stand/i386/libi386/libi386.h b/stand/i386/libi386/libi386.h new file mode 100644 index 0000000..e65a060 --- /dev/null +++ b/stand/i386/libi386/libi386.h @@ -0,0 +1,156 @@ +/*- + * Copyright (c) 1998 Michael Smith <msmith@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. + * + * 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. + * + * $FreeBSD$ + */ + + +/* + * i386 fully-qualified device descriptor. + * Note, this must match the 'struct devdesc' declaration + * in bootstrap.h and also with struct zfs_devdesc for zfs + * support. + */ +struct i386_devdesc +{ + struct devsw *d_dev; + int d_type; + int d_unit; + union + { + struct + { + void *data; + int slice; + int partition; + off_t offset; + } biosdisk; + struct + { + void *data; + } bioscd; + struct + { + void *data; + uint64_t pool_guid; + uint64_t root_guid; + } zfs; + } d_kind; +}; + +/* + * relocater trampoline support. + */ +struct relocate_data { + uint32_t src; + uint32_t dest; + uint32_t size; +}; + +extern void relocater(void); + +/* + * The relocater_data[] is fixed size array allocated in relocater_tramp.S + */ +extern struct relocate_data relocater_data[]; +extern uint32_t relocater_size; + +extern uint16_t relocator_ip; +extern uint16_t relocator_cs; +extern uint16_t relocator_ds; +extern uint16_t relocator_es; +extern uint16_t relocator_fs; +extern uint16_t relocator_gs; +extern uint16_t relocator_ss; +extern uint16_t relocator_sp; +extern uint32_t relocator_esi; +extern uint32_t relocator_eax; +extern uint32_t relocator_ebx; +extern uint32_t relocator_edx; +extern uint32_t relocator_ebp; +extern uint16_t relocator_a20_enabled; + +int i386_getdev(void **vdev, const char *devspec, const char **path); +char *i386_fmtdev(void *vdev); +int i386_setcurrdev(struct env_var *ev, int flags, const void *value); + +extern struct devdesc currdev; /* our current device */ + +#define MAXDEV 31 /* maximum number of distinct devices */ +#define MAXBDDEV MAXDEV + +/* exported devices XXX rename? */ +extern struct devsw bioscd; +extern struct devsw biosdisk; +extern struct devsw pxedisk; +extern struct fs_ops pxe_fsops; + +int bc_add(int biosdev); /* Register CD booted from. */ +int bc_getdev(struct i386_devdesc *dev); /* return dev_t for (dev) */ +int bc_bios2unit(int biosdev); /* xlate BIOS device -> bioscd unit */ +int bc_unit2bios(int unit); /* xlate bioscd unit -> BIOS device */ +uint32_t bd_getbigeom(int bunit); /* return geometry in bootinfo format */ +int bd_bios2unit(int biosdev); /* xlate BIOS device -> biosdisk unit */ +int bd_unit2bios(int unit); /* xlate biosdisk unit -> BIOS device */ +int bd_getdev(struct i386_devdesc *dev); /* return dev_t for (dev) */ + +ssize_t i386_copyin(const void *src, vm_offset_t dest, const size_t len); +ssize_t i386_copyout(const vm_offset_t src, void *dest, const size_t len); +ssize_t i386_readin(const int fd, vm_offset_t dest, const size_t len); + +struct preloaded_file; +void bios_addsmapdata(struct preloaded_file *); +void bios_getsmap(void); + +void bios_getmem(void); +extern uint32_t bios_basemem; /* base memory in bytes */ +extern uint32_t bios_extmem; /* extended memory in bytes */ +extern vm_offset_t memtop; /* last address of physical memory + 1 */ +extern vm_offset_t memtop_copyin; /* memtop less heap size for the cases */ + /* when heap is at the top of */ + /* extended memory; for other cases */ + /* just the same as memtop */ +extern uint32_t high_heap_size; /* extended memory region available */ +extern vm_offset_t high_heap_base; /* for use as the heap */ + +void biospci_detect(void); +int biospci_find_devclass(uint32_t class, int index, uint32_t *locator); +int biospci_read_config(uint32_t locator, int offset, int width, uint32_t *val); +uint32_t biospci_locator(int8_t bus, uint8_t device, uint8_t function); +int biospci_write_config(uint32_t locator, int offset, int width, uint32_t val); + +void biosacpi_detect(void); + +int i386_autoload(void); + +int bi_getboothowto(char *kargs); +void bi_setboothowto(int howto); +vm_offset_t bi_copyenv(vm_offset_t addr); +int bi_load32(char *args, int *howtop, int *bootdevp, vm_offset_t *bip, + vm_offset_t *modulep, vm_offset_t *kernend); +int bi_load64(char *args, vm_offset_t addr, vm_offset_t *modulep, + vm_offset_t *kernend, int add_smap); + +void pxe_enable(void *pxeinfo); diff --git a/stand/i386/libi386/multiboot.c b/stand/i386/libi386/multiboot.c new file mode 100644 index 0000000..9aac640 --- /dev/null +++ b/stand/i386/libi386/multiboot.c @@ -0,0 +1,467 @@ +/*- + * Copyright (c) 2014 Roger Pau Monné <royger@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. + * + * 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. + */ + +/* + * This multiboot implementation only implements a subset of the full + * multiboot specification in order to be able to boot Xen and a + * FreeBSD Dom0. Trying to use it to boot other multiboot compliant + * kernels will most surely fail. + * + * The full multiboot specification can be found here: + * http://www.gnu.org/software/grub/manual/multiboot/multiboot.html + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/exec.h> +#include <sys/linker.h> +#include <sys/module.h> +#include <sys/stdint.h> +#define _MACHINE_ELF_WANT_32BIT +#include <machine/elf.h> +#include <string.h> +#include <stand.h> + +#include "bootstrap.h" +#include "multiboot.h" +#include "../i386/libi386/libi386.h" +#include "../i386/btx/lib/btxv86.h" + +#define MULTIBOOT_SUPPORTED_FLAGS \ + (MULTIBOOT_PAGE_ALIGN|MULTIBOOT_MEMORY_INFO) +#define NUM_MODULES 2 +#define METADATA_FIXED_SIZE (PAGE_SIZE*4) +#define METADATA_MODULE_SIZE PAGE_SIZE + +#define METADATA_RESV_SIZE(mod_num) \ + roundup(METADATA_FIXED_SIZE + METADATA_MODULE_SIZE * mod_num, PAGE_SIZE) + +extern int elf32_loadfile_raw(char *filename, u_int64_t dest, + struct preloaded_file **result, int multiboot); +extern int elf64_load_modmetadata(struct preloaded_file *fp, u_int64_t dest); +extern int elf64_obj_loadfile(char *filename, u_int64_t dest, + struct preloaded_file **result); + +static int multiboot_loadfile(char *, u_int64_t, struct preloaded_file **); +static int multiboot_exec(struct preloaded_file *); + +static int multiboot_obj_loadfile(char *, u_int64_t, struct preloaded_file **); +static int multiboot_obj_exec(struct preloaded_file *fp); + +struct file_format multiboot = { multiboot_loadfile, multiboot_exec }; +struct file_format multiboot_obj = + { multiboot_obj_loadfile, multiboot_obj_exec }; + +extern void multiboot_tramp(); + +static const char mbl_name[] = "FreeBSD Loader"; + +static int +num_modules(struct preloaded_file *kfp) +{ + struct kernel_module *kmp; + int mod_num = 0; + + for (kmp = kfp->f_modules; kmp != NULL; kmp = kmp->m_next) + mod_num++; + + return (mod_num); +} + +static vm_offset_t +max_addr(void) +{ + struct preloaded_file *fp; + vm_offset_t addr = 0; + + for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) { + if (addr < (fp->f_addr + fp->f_size)) + addr = fp->f_addr + fp->f_size; + } + + return (addr); +} + +static int +multiboot_loadfile(char *filename, u_int64_t dest, + struct preloaded_file **result) +{ + uint32_t *magic; + int i, error; + caddr_t header_search; + ssize_t search_size; + int fd; + struct multiboot_header *header; + char *cmdline; + + /* + * Read MULTIBOOT_SEARCH size in order to search for the + * multiboot magic header. + */ + if (filename == NULL) + return (EFTYPE); + if ((fd = open(filename, O_RDONLY)) == -1) + return (errno); + header_search = malloc(MULTIBOOT_SEARCH); + if (header_search == NULL) { + close(fd); + return (ENOMEM); + } + search_size = read(fd, header_search, MULTIBOOT_SEARCH); + magic = (uint32_t *)header_search; + + header = NULL; + for (i = 0; i < (search_size / sizeof(uint32_t)); i++) { + if (magic[i] == MULTIBOOT_HEADER_MAGIC) { + header = (struct multiboot_header *)&magic[i]; + break; + } + } + + if (header == NULL) { + error = EFTYPE; + goto out; + } + + /* Valid multiboot header has been found, validate checksum */ + if (header->magic + header->flags + header->checksum != 0) { + printf( + "Multiboot checksum failed, magic: 0x%x flags: 0x%x checksum: 0x%x\n", + header->magic, header->flags, header->checksum); + error = EFTYPE; + goto out; + } + + if ((header->flags & ~MULTIBOOT_SUPPORTED_FLAGS) != 0) { + printf("Unsupported multiboot flags found: 0x%x\n", + header->flags); + error = EFTYPE; + goto out; + } + + error = elf32_loadfile_raw(filename, dest, result, 1); + if (error != 0) { + printf( + "elf32_loadfile_raw failed: %d unable to load multiboot kernel\n", + error); + goto out; + } + + /* + * f_addr is already aligned to PAGE_SIZE, make sure + * f_size it's also aligned so when the modules are loaded + * they are aligned to PAGE_SIZE. + */ + (*result)->f_size = roundup((*result)->f_size, PAGE_SIZE); + +out: + free(header_search); + close(fd); + return (error); +} + +static int +multiboot_exec(struct preloaded_file *fp) +{ + vm_offset_t module_start, last_addr, metadata_size; + vm_offset_t modulep, kernend, entry; + struct file_metadata *md; + Elf_Ehdr *ehdr; + struct multiboot_info *mb_info = NULL; + struct multiboot_mod_list *mb_mod = NULL; + char *cmdline = NULL; + size_t len; + int error, mod_num; + + /* + * Don't pass the memory size found by the bootloader, the memory + * available to Dom0 will be lower than that. + */ + unsetenv("smbios.memory.enabled"); + + /* Allocate the multiboot struct and fill the basic details. */ + mb_info = malloc(sizeof(struct multiboot_info)); + if (mb_info == NULL) { + error = ENOMEM; + goto error; + } + bzero(mb_info, sizeof(struct multiboot_info)); + mb_info->flags = MULTIBOOT_INFO_MEMORY|MULTIBOOT_INFO_BOOT_LOADER_NAME; + mb_info->mem_lower = bios_basemem / 1024; + mb_info->mem_upper = bios_extmem / 1024; + mb_info->boot_loader_name = VTOP(mbl_name); + + /* Set the Xen command line. */ + if (fp->f_args == NULL) { + /* Add the Xen command line if it is set. */ + cmdline = getenv("xen_cmdline"); + if (cmdline != NULL) { + fp->f_args = strdup(cmdline); + if (fp->f_args == NULL) { + error = ENOMEM; + goto error; + } + } + } + if (fp->f_args != NULL) { + len = strlen(fp->f_name) + 1 + strlen(fp->f_args) + 1; + cmdline = malloc(len); + if (cmdline == NULL) { + error = ENOMEM; + goto error; + } + snprintf(cmdline, len, "%s %s", fp->f_name, fp->f_args); + mb_info->cmdline = VTOP(cmdline); + mb_info->flags |= MULTIBOOT_INFO_CMDLINE; + } + + /* Find the entry point of the Xen kernel and save it for later */ + if ((md = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) { + printf("Unable to find %s entry point\n", fp->f_name); + error = EINVAL; + goto error; + } + ehdr = (Elf_Ehdr *)&(md->md_data); + entry = ehdr->e_entry & 0xffffff; + + /* + * Prepare the multiboot module list, Xen assumes the first + * module is the Dom0 kernel, and the second one is the initramfs. + * This is not optimal for FreeBSD, that doesn't have a initramfs + * but instead loads modules dynamically and creates the metadata + * info on-the-fly. + * + * As expected, the first multiboot module is going to be the + * FreeBSD kernel loaded as a raw file. The second module is going + * to contain the metadata info and the loaded modules. + * + * On native FreeBSD loads all the modules and then places the + * metadata info at the end, but this is painful when running on Xen, + * because it relocates the second multiboot module wherever it + * likes. In order to workaround this limitation the metadata + * information is placed at the start of the second module and + * the original modulep value is saved together with the other + * metadata, so we can relocate everything. + * + * Native layout: + * fp->f_addr + fp->f_size + * +---------+----------------+------------+ + * | | | | + * | Kernel | Modules | Metadata | + * | | | | + * +---------+----------------+------------+ + * fp->f_addr modulep kernend + * + * Xen layout: + * + * Initial: + * fp->f_addr + fp->f_size + * +---------+----------+----------------+------------+ + * | | | | | + * | Kernel | Reserved | Modules | Metadata | + * | | | | dry run | + * +---------+----------+----------------+------------+ + * fp->f_addr + * + * After metadata polacement (ie: final): + * fp->f_addr + fp->f_size + * +-----------+---------+----------+----------------+ + * | | | | | + * | Kernel | Free | Metadata | Modules | + * | | | | | + * +-----------+---------+----------+----------------+ + * fp->f_addr modulep kernend + * \__________/ \__________________________/ + * Multiboot module 0 Multiboot module 1 + */ + + fp = file_findfile(NULL, "elf kernel"); + if (fp == NULL) { + printf("No FreeBSD kernel provided, aborting\n"); + error = EINVAL; + goto error; + } + + if (fp->f_metadata != NULL) { + printf("FreeBSD kernel already contains metadata, aborting\n"); + error = EINVAL; + goto error; + } + + + mb_mod = malloc(sizeof(struct multiboot_mod_list) * NUM_MODULES); + if (mb_mod == NULL) { + error = ENOMEM; + goto error; + } + + bzero(mb_mod, sizeof(struct multiboot_mod_list) * NUM_MODULES); + + /* + * Calculate how much memory is needed for the metatdata. We did + * an approximation of the maximum size when loading the kernel, + * but now we know the exact size, so we can release some of this + * preallocated memory if not needed. + */ + last_addr = roundup(max_addr(), PAGE_SIZE); + mod_num = num_modules(fp); + + /* + * Place the metadata after the last used address in order to + * calculate it's size, this will not be used. + */ + error = bi_load64(fp->f_args, last_addr, &modulep, &kernend, 0); + if (error != 0) { + printf("bi_load64 failed: %d\n", error); + goto error; + } + metadata_size = roundup(kernend - last_addr, PAGE_SIZE); + + /* Check that the size is not greater than what we have reserved */ + if (metadata_size > METADATA_RESV_SIZE(mod_num)) { + printf("Required memory for metadata is greater than reserved " + "space, please increase METADATA_FIXED_SIZE and " + "METADATA_MODULE_SIZE and rebuild the loader\n"); + error = ENOMEM; + goto error; + } + + /* Clean the metadata added to the kernel in the bi_load64 dry run */ + file_removemetadata(fp); + + /* + * This is the position where the second multiboot module + * will be placed. + */ + module_start = fp->f_addr + fp->f_size - metadata_size; + + error = bi_load64(fp->f_args, module_start, &modulep, &kernend, 0); + if (error != 0) { + printf("bi_load64 failed: %d\n", error); + goto error; + } + + mb_mod[0].mod_start = fp->f_addr; + mb_mod[0].mod_end = fp->f_addr + fp->f_size; + mb_mod[0].mod_end -= METADATA_RESV_SIZE(mod_num); + + mb_mod[1].mod_start = module_start; + mb_mod[1].mod_end = last_addr; + + mb_info->mods_count = NUM_MODULES; + mb_info->mods_addr = VTOP(mb_mod); + mb_info->flags |= MULTIBOOT_INFO_MODS; + + dev_cleanup(); + __exec((void *)VTOP(multiboot_tramp), (void *)entry, + (void *)VTOP(mb_info)); + + panic("exec returned"); + +error: + if (mb_mod) + free(mb_mod); + if (mb_info) + free(mb_info); + if (cmdline) + free(cmdline); + return (error); +} + +static int +multiboot_obj_loadfile(char *filename, u_int64_t dest, + struct preloaded_file **result) +{ + struct preloaded_file *mfp, *kfp, *rfp; + struct kernel_module *kmp; + int error, mod_num; + + /* See if there's a multiboot kernel loaded */ + mfp = file_findfile(NULL, "elf multiboot kernel"); + if (mfp == NULL) + return (EFTYPE); + + /* + * We have a multiboot kernel loaded, see if there's a FreeBSD + * kernel loaded also. + */ + kfp = file_findfile(NULL, "elf kernel"); + if (kfp == NULL) { + /* + * No kernel loaded, this must be it. The kernel has to + * be loaded as a raw file, it will be processed by + * Xen and correctly loaded as an ELF file. + */ + rfp = file_loadraw(filename, "elf kernel", 0); + if (rfp == NULL) { + printf( + "Unable to load %s as a multiboot payload kernel\n", + filename); + return (EINVAL); + } + + /* Load kernel metadata... */ + setenv("kernelname", filename, 1); + error = elf64_load_modmetadata(rfp, rfp->f_addr + rfp->f_size); + if (error) { + printf("Unable to load kernel %s metadata error: %d\n", + rfp->f_name, error); + return (EINVAL); + } + + /* + * Save space at the end of the kernel in order to place + * the metadata information. We do an approximation of the + * max metadata size, this is not optimal but it's probably + * the best we can do at this point. Once all modules are + * loaded and the size of the metadata is known this + * space will be recovered if not used. + */ + mod_num = num_modules(rfp); + rfp->f_size = roundup(rfp->f_size, PAGE_SIZE); + rfp->f_size += METADATA_RESV_SIZE(mod_num); + *result = rfp; + } else { + /* The rest should be loaded as regular modules */ + error = elf64_obj_loadfile(filename, dest, result); + if (error != 0) { + printf("Unable to load %s as an object file, error: %d", + filename, error); + return (error); + } + } + + return (0); +} + +static int +multiboot_obj_exec(struct preloaded_file *fp) +{ + + return (EFTYPE); +} diff --git a/stand/i386/libi386/multiboot.h b/stand/i386/libi386/multiboot.h new file mode 100644 index 0000000..819fa2e --- /dev/null +++ b/stand/i386/libi386/multiboot.h @@ -0,0 +1,225 @@ +/* multiboot.h - Multiboot header file. */ +/* Copyright (C) 1999,2003,2007,2008,2009 Free Software Foundation, Inc. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to +* deal in the Software without restriction, including without limitation the +* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +* sell copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ANY +* DEVELOPER OR DISTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +* IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +* $FreeBSD$ +*/ + +#ifndef MULTIBOOT_HEADER +#define MULTIBOOT_HEADER 1 + +/* How many bytes from the start of the file we search for the header. */ +#define MULTIBOOT_SEARCH 8192 + +/* The magic field should contain this. */ +#define MULTIBOOT_HEADER_MAGIC 0x1BADB002 + +/* This should be in %eax. */ +#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002 + +/* The bits in the required part of flags field we don't support. */ +#define MULTIBOOT_UNSUPPORTED 0x0000fffc + +/* Alignment of multiboot modules. */ +#define MULTIBOOT_MOD_ALIGN 0x00001000 + +/* Alignment of the multiboot info structure. */ +#define MULTIBOOT_INFO_ALIGN 0x00000004 + +/* Flags set in the 'flags' member of the multiboot header. */ + +/* Align all boot modules on i386 page (4KB) boundaries. */ +#define MULTIBOOT_PAGE_ALIGN 0x00000001 + +/* Must pass memory information to OS. */ +#define MULTIBOOT_MEMORY_INFO 0x00000002 + +/* Must pass video information to OS. */ +#define MULTIBOOT_VIDEO_MODE 0x00000004 + +/* This flag indicates the use of the address fields in the header. */ +#define MULTIBOOT_AOUT_KLUDGE 0x00010000 + +/* Flags to be set in the 'flags' member of the multiboot info structure. */ + +/* is there basic lower/upper memory information? */ +#define MULTIBOOT_INFO_MEMORY 0x00000001 +/* is there a boot device set? */ +#define MULTIBOOT_INFO_BOOTDEV 0x00000002 +/* is the command-line defined? */ +#define MULTIBOOT_INFO_CMDLINE 0x00000004 +/* are there modules to do something with? */ +#define MULTIBOOT_INFO_MODS 0x00000008 + +/* These next two are mutually exclusive */ + +/* is there a symbol table loaded? */ +#define MULTIBOOT_INFO_AOUT_SYMS 0x00000010 +/* is there an ELF section header table? */ +#define MULTIBOOT_INFO_ELF_SHDR 0X00000020 + +/* is there a full memory map? */ +#define MULTIBOOT_INFO_MEM_MAP 0x00000040 + +/* Is there drive info? */ +#define MULTIBOOT_INFO_DRIVE_INFO 0x00000080 + +/* Is there a config table? */ +#define MULTIBOOT_INFO_CONFIG_TABLE 0x00000100 + +/* Is there a boot loader name? */ +#define MULTIBOOT_INFO_BOOT_LOADER_NAME 0x00000200 + +/* Is there a APM table? */ +#define MULTIBOOT_INFO_APM_TABLE 0x00000400 + +/* Is there video information? */ +#define MULTIBOOT_INFO_VIDEO_INFO 0x00000800 + +#ifndef ASM_FILE + +typedef unsigned short multiboot_uint16_t; +typedef unsigned int multiboot_uint32_t; +typedef unsigned long long multiboot_uint64_t; + +struct multiboot_header +{ +/* Must be MULTIBOOT_MAGIC - see above. */ + multiboot_uint32_t magic; + +/* Feature flags. */ + multiboot_uint32_t flags; + +/* The above fields plus this one must equal 0 mod 2^32. */ + multiboot_uint32_t checksum; + +/* These are only valid if MULTIBOOT_AOUT_KLUDGE is set. */ + multiboot_uint32_t header_addr; + multiboot_uint32_t load_addr; + multiboot_uint32_t load_end_addr; + multiboot_uint32_t bss_end_addr; + multiboot_uint32_t entry_addr; + +/* These are only valid if MULTIBOOT_VIDEO_MODE is set. */ + multiboot_uint32_t mode_type; + multiboot_uint32_t width; + multiboot_uint32_t height; + multiboot_uint32_t depth; +}; + +/* The symbol table for a.out. */ +struct multiboot_aout_symbol_table +{ + multiboot_uint32_t tabsize; + multiboot_uint32_t strsize; + multiboot_uint32_t addr; + multiboot_uint32_t reserved; +}; +typedef struct multiboot_aout_symbol_table multiboot_aout_symbol_table_t; + +/* The section header table for ELF. */ +struct multiboot_elf_section_header_table +{ + multiboot_uint32_t num; + multiboot_uint32_t size; + multiboot_uint32_t addr; + multiboot_uint32_t shndx; +}; +typedef struct multiboot_elf_section_header_table multiboot_elf_section_header_table_t; + +struct multiboot_info +{ +/* Multiboot info version number */ + multiboot_uint32_t flags; + +/* Available memory from BIOS */ + multiboot_uint32_t mem_lower; + multiboot_uint32_t mem_upper; + +/* "root" partition */ + multiboot_uint32_t boot_device; + +/* Kernel command line */ + multiboot_uint32_t cmdline; + +/* Boot-Module list */ + multiboot_uint32_t mods_count; + multiboot_uint32_t mods_addr; + + union + { + multiboot_aout_symbol_table_t aout_sym; + multiboot_elf_section_header_table_t elf_sec; + } u; + +/* Memory Mapping buffer */ + multiboot_uint32_t mmap_length; + multiboot_uint32_t mmap_addr; + +/* Drive Info buffer */ + multiboot_uint32_t drives_length; + multiboot_uint32_t drives_addr; + +/* ROM configuration table */ + multiboot_uint32_t config_table; + +/* Boot Loader Name */ + multiboot_uint32_t boot_loader_name; + +/* APM table */ + multiboot_uint32_t apm_table; + +/* Video */ + multiboot_uint32_t vbe_control_info; + multiboot_uint32_t vbe_mode_info; + multiboot_uint16_t vbe_mode; + multiboot_uint16_t vbe_interface_seg; + multiboot_uint16_t vbe_interface_off; + multiboot_uint16_t vbe_interface_len; +}; +typedef struct multiboot_info multiboot_info_t; + +struct multiboot_mmap_entry +{ + multiboot_uint32_t size; + multiboot_uint64_t addr; + multiboot_uint64_t len; +#define MULTIBOOT_MEMORY_AVAILABLE 1 +#define MULTIBOOT_MEMORY_RESERVED 2 + multiboot_uint32_t type; +} __attribute__((packed)); +typedef struct multiboot_mmap_entry multiboot_memory_map_t; + +struct multiboot_mod_list +{ +/* the memory used goes from bytes 'mod_start' to 'mod_end-1' inclusive */ + multiboot_uint32_t mod_start; + multiboot_uint32_t mod_end; + +/* Module command line */ + multiboot_uint32_t cmdline; + +/* padding to take it to 16 bytes (must be zero) */ + multiboot_uint32_t pad; +}; +typedef struct multiboot_mod_list multiboot_module_t; + +#endif /* ! ASM_FILE */ + +#endif /* ! MULTIBOOT_HEADER */ diff --git a/stand/i386/libi386/multiboot_tramp.S b/stand/i386/libi386/multiboot_tramp.S new file mode 100644 index 0000000..0bd6043 --- /dev/null +++ b/stand/i386/libi386/multiboot_tramp.S @@ -0,0 +1,51 @@ +/*- + * Copyright (c) 2014 Roger Pau Monné <royger@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. + * + * 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. + * + * $FreeBSD$ + */ + +#define ASM_FILE +#include "multiboot.h" + +/* + * The multiboot specification requires the executable to be launched + * with %cs set to a flat read/execute segment with offset 0 and limit + * 0xFFFFFFFF, and the rest of the segment registers (%ds, %es, %fs, + * %gs, %ss) to flat read/write segments with the same offset and limit. + * This is already done by the BTX code before calling multiboot_tramp, + * so there is no need to do anything here. + */ + + .globl multiboot_tramp +multiboot_tramp: + /* Be sure that interrupts are disabled. */ + cli + + movl $MULTIBOOT_BOOTLOADER_MAGIC, %eax + /* Get the entry point and address of the multiboot_info parameter. */ + movl 8(%esp), %ebx + movl 4(%esp), %ecx + + call *%ecx diff --git a/stand/i386/libi386/nullconsole.c b/stand/i386/libi386/nullconsole.c new file mode 100644 index 0000000..ebb1e7e --- /dev/null +++ b/stand/i386/libi386/nullconsole.c @@ -0,0 +1,88 @@ +/*- + * nullconsole.c + * + * Author: Doug Ambrisko <ambrisko@whistle.com> + * Copyright (c) 2000 Whistle Communications, Inc. + * All rights reserved. + * + * Subject to the following obligations and disclaimer of warranty, use and + * redistribution of this software, in source or object code forms, with or + * without modifications are expressly permitted by Whistle Communications; + * provided, however, that: + * 1. Any and all reproductions of the source or object code must include the + * copyright notice above and the following disclaimer of warranties; and + * 2. No rights are granted, in any manner or form, to use Whistle + * Communications, Inc. trademarks, including the mark "WHISTLE + * COMMUNICATIONS" on advertising, endorsements, or otherwise except as + * such appears in the above copyright notice or in the software. + * + * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND + * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO + * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, + * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. + * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY + * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS + * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. + * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES + * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING + * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER 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 WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stand.h> +#include <bootstrap.h> + +static void nullc_probe(struct console *cp); +static int nullc_init(int arg); +static void nullc_putchar(int c); +static int nullc_getchar(void); +static int nullc_ischar(void); + +struct console nullconsole = { + "nullconsole", + "null port", + 0, + nullc_probe, + nullc_init, + nullc_putchar, + nullc_getchar, + nullc_ischar +}; + +static void +nullc_probe(struct console *cp) +{ + cp->c_flags |= (C_PRESENTIN | C_PRESENTOUT); +} + +static int +nullc_init(int arg) +{ + return(0); +} + +static void +nullc_putchar(int c) +{ +} + +static int +nullc_getchar(void) +{ + return(-1); +} + +static int +nullc_ischar(void) +{ + return(0); +} diff --git a/stand/i386/libi386/pread.c b/stand/i386/libi386/pread.c new file mode 100644 index 0000000..870e254 --- /dev/null +++ b/stand/i386/libi386/pread.c @@ -0,0 +1,80 @@ +/* + * $NetBSD: pread.c,v 1.2 1997/03/22 01:48:38 thorpej Exp $ + */ + +/*- + * Copyright (c) 1996 + * Matthias Drochner. 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the NetBSD Project + * by Matthias Drochner. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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 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/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* read into destination in flat addr space */ + +#include <stand.h> + +#include "libi386.h" + +#ifdef SAVE_MEMORY +#define BUFSIZE (1*1024) +#else +#define BUFSIZE (4*1024) +#endif + +static char buf[BUFSIZE]; + +int +pread(fd, dest, size) + int fd; + vm_offset_t dest; + int size; +{ + int rsize; + + rsize = size; + while (rsize > 0) { + int count, got; + + count = (rsize < BUFSIZE ? rsize : BUFSIZE); + + got = read(fd, buf, count); + if (got < 0) + return (-1); + + /* put to physical space */ + vpbcopy(buf, dest, got); + + dest += got; + rsize -= got; + if (got < count) + break; /* EOF */ + } + return (size - rsize); +} diff --git a/stand/i386/libi386/pxe.c b/stand/i386/libi386/pxe.c new file mode 100644 index 0000000..7cb4833 --- /dev/null +++ b/stand/i386/libi386/pxe.c @@ -0,0 +1,540 @@ +/*- + * Copyright (c) 2000 Alfred Perlstein <alfred@freebsd.org> + * Copyright (c) 2000 Paul Saab <ps@freebsd.org> + * Copyright (c) 2000 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. + * + * 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stand.h> +#include <stddef.h> +#include <string.h> +#include <stdarg.h> +#include <sys/param.h> + +#include <net/ethernet.h> +#include <netinet/in_systm.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/udp.h> + +#include <net.h> +#include <netif.h> +#include <nfsv2.h> +#include <iodesc.h> + +#include <bootp.h> +#include <bootstrap.h> +#include "btxv86.h" +#include "pxe.h" + +/* + * Allocate the PXE buffers statically instead of sticking grimy fingers into + * BTX's private data area. The scratch buffer is used to send information to + * the PXE BIOS, and the data buffer is used to receive data from the PXE BIOS. + */ +#define PXE_BUFFER_SIZE 0x2000 +static char scratch_buffer[PXE_BUFFER_SIZE]; +static char data_buffer[PXE_BUFFER_SIZE]; + +static pxenv_t *pxenv_p = NULL; /* PXENV+ */ +static pxe_t *pxe_p = NULL; /* !PXE */ + +#ifdef PXE_DEBUG +static int pxe_debug = 0; +#endif + +void pxe_enable(void *pxeinfo); +static void (*pxe_call)(int func); +static void pxenv_call(int func); +static void bangpxe_call(int func); + +static int pxe_init(void); +static int pxe_print(int verbose); +static void pxe_cleanup(void); + +static void pxe_perror(int error); +static int pxe_netif_match(struct netif *nif, void *machdep_hint); +static int pxe_netif_probe(struct netif *nif, void *machdep_hint); +static void pxe_netif_init(struct iodesc *desc, void *machdep_hint); +static ssize_t pxe_netif_get(struct iodesc *, void **, time_t); +static ssize_t pxe_netif_put(struct iodesc *desc, void *pkt, size_t len); +static void pxe_netif_end(struct netif *nif); + +extern struct netif_stats pxe_st[]; +extern u_int16_t __bangpxeseg; +extern u_int16_t __bangpxeoff; +extern void __bangpxeentry(void); +extern u_int16_t __pxenvseg; +extern u_int16_t __pxenvoff; +extern void __pxenventry(void); + +struct netif_dif pxe_ifs[] = { +/* dif_unit dif_nsel dif_stats dif_private */ + {0, 1, &pxe_st[0], 0} +}; + +struct netif_stats pxe_st[nitems(pxe_ifs)]; + +struct netif_driver pxenetif = { + .netif_bname = "pxenet", + .netif_match = pxe_netif_match, + .netif_probe = pxe_netif_probe, + .netif_init = pxe_netif_init, + .netif_get = pxe_netif_get, + .netif_put = pxe_netif_put, + .netif_end = pxe_netif_end, + .netif_ifs = pxe_ifs, + .netif_nifs = nitems(pxe_ifs) +}; + +struct netif_driver *netif_drivers[] = { + &pxenetif, + NULL +}; + +struct devsw pxedisk = { + .dv_name = "net", + .dv_type = DEVT_NET, + .dv_init = pxe_init, + .dv_strategy = NULL, /* Will be set in pxe_init */ + .dv_open = NULL, /* Will be set in pxe_init */ + .dv_close = NULL, /* Will be set in pxe_init */ + .dv_ioctl = noioctl, + .dv_print = pxe_print, + .dv_cleanup = pxe_cleanup +}; + +/* + * This function is called by the loader to enable PXE support if we + * are booted by PXE. The passed in pointer is a pointer to the PXENV+ + * structure. + */ +void +pxe_enable(void *pxeinfo) +{ + pxenv_p = (pxenv_t *)pxeinfo; + pxe_p = (pxe_t *)PTOV(pxenv_p->PXEPtr.segment * 16 + + pxenv_p->PXEPtr.offset); + pxe_call = NULL; +} + +/* + * return true if pxe structures are found/initialized, + * also figures out our IP information via the pxe cached info struct + */ +static int +pxe_init(void) +{ + t_PXENV_GET_CACHED_INFO *gci_p; + int counter; + uint8_t checksum; + uint8_t *checkptr; + extern struct devsw netdev; + + if (pxenv_p == NULL) + return (0); + + /* look for "PXENV+" */ + if (bcmp((void *)pxenv_p->Signature, S_SIZE("PXENV+"))) { + pxenv_p = NULL; + return (0); + } + + /* make sure the size is something we can handle */ + if (pxenv_p->Length > sizeof(*pxenv_p)) { + printf("PXENV+ structure too large, ignoring\n"); + pxenv_p = NULL; + return (0); + } + + /* + * do byte checksum: + * add up each byte in the structure, the total should be 0 + */ + checksum = 0; + checkptr = (uint8_t *) pxenv_p; + for (counter = 0; counter < pxenv_p->Length; counter++) + checksum += *checkptr++; + if (checksum != 0) { + printf("PXENV+ structure failed checksum, ignoring\n"); + pxenv_p = NULL; + return (0); + } + + /* + * PXENV+ passed, so use that if !PXE is not available or + * the checksum fails. + */ + pxe_call = pxenv_call; + if (pxenv_p->Version >= 0x0200) { + for (;;) { + if (bcmp((void *)pxe_p->Signature, S_SIZE("!PXE"))) { + pxe_p = NULL; + break; + } + checksum = 0; + checkptr = (uint8_t *)pxe_p; + for (counter = 0; counter < pxe_p->StructLength; + counter++) + checksum += *checkptr++; + if (checksum != 0) { + pxe_p = NULL; + break; + } + pxe_call = bangpxe_call; + break; + } + } + + pxedisk.dv_open = netdev.dv_open; + pxedisk.dv_close = netdev.dv_close; + pxedisk.dv_strategy = netdev.dv_strategy; + + printf("\nPXE version %d.%d, real mode entry point ", + (uint8_t) (pxenv_p->Version >> 8), + (uint8_t) (pxenv_p->Version & 0xFF)); + if (pxe_call == bangpxe_call) + printf("@%04x:%04x\n", + pxe_p->EntryPointSP.segment, + pxe_p->EntryPointSP.offset); + else + printf("@%04x:%04x\n", + pxenv_p->RMEntry.segment, pxenv_p->RMEntry.offset); + + gci_p = (t_PXENV_GET_CACHED_INFO *) scratch_buffer; + bzero(gci_p, sizeof(*gci_p)); + gci_p->PacketType = PXENV_PACKET_TYPE_BINL_REPLY; + pxe_call(PXENV_GET_CACHED_INFO); + if (gci_p->Status != 0) { + pxe_perror(gci_p->Status); + pxe_p = NULL; + return (0); + } + free(bootp_response); + if ((bootp_response = malloc(gci_p->BufferSize)) != NULL) { + bootp_response_size = gci_p->BufferSize; + bcopy(PTOV((gci_p->Buffer.segment << 4) + gci_p->Buffer.offset), + bootp_response, bootp_response_size); + } + return (1); +} + +static int +pxe_print(int verbose) +{ + if (pxe_call == NULL) + return (0); + + printf("%s devices:", pxedisk.dv_name); + if (pager_output("\n") != 0) + return (1); + printf(" %s0:", pxedisk.dv_name); + if (verbose) { + printf(" %s:%s", inet_ntoa(rootip), rootpath); + } + return (pager_output("\n")); +} + +static void +pxe_cleanup(void) +{ +#ifdef PXE_DEBUG + t_PXENV_UNLOAD_STACK *unload_stack_p = + (t_PXENV_UNLOAD_STACK *)scratch_buffer; + t_PXENV_UNDI_SHUTDOWN *undi_shutdown_p = + (t_PXENV_UNDI_SHUTDOWN *)scratch_buffer; +#endif + + if (pxe_call == NULL) + return; + + pxe_call(PXENV_UNDI_SHUTDOWN); + +#ifdef PXE_DEBUG + if (pxe_debug && undi_shutdown_p->Status != 0) + printf("pxe_cleanup: UNDI_SHUTDOWN failed %x\n", + undi_shutdown_p->Status); +#endif + + pxe_call(PXENV_UNLOAD_STACK); + +#ifdef PXE_DEBUG + if (pxe_debug && unload_stack_p->Status != 0) + printf("pxe_cleanup: UNLOAD_STACK failed %x\n", + unload_stack_p->Status); +#endif +} + +void +pxe_perror(int err) +{ + return; +} + +void +pxenv_call(int func) +{ +#ifdef PXE_DEBUG + if (pxe_debug) + printf("pxenv_call %x\n", func); +#endif + + bzero(&v86, sizeof(v86)); + bzero(data_buffer, sizeof(data_buffer)); + + __pxenvseg = pxenv_p->RMEntry.segment; + __pxenvoff = pxenv_p->RMEntry.offset; + + v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS; + v86.es = VTOPSEG(scratch_buffer); + v86.edi = VTOPOFF(scratch_buffer); + v86.addr = (VTOPSEG(__pxenventry) << 16) | VTOPOFF(__pxenventry); + v86.ebx = func; + v86int(); + v86.ctl = V86_FLAGS; +} + +void +bangpxe_call(int func) +{ +#ifdef PXE_DEBUG + if (pxe_debug) + printf("bangpxe_call %x\n", func); +#endif + + bzero(&v86, sizeof(v86)); + bzero(data_buffer, sizeof(data_buffer)); + + __bangpxeseg = pxe_p->EntryPointSP.segment; + __bangpxeoff = pxe_p->EntryPointSP.offset; + + v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS; + v86.edx = VTOPSEG(scratch_buffer); + v86.eax = VTOPOFF(scratch_buffer); + v86.addr = (VTOPSEG(__bangpxeentry) << 16) | VTOPOFF(__bangpxeentry); + v86.ebx = func; + v86int(); + v86.ctl = V86_FLAGS; +} + + +static int +pxe_netif_match(struct netif *nif, void *machdep_hint) +{ + return (1); +} + +static int +pxe_netif_probe(struct netif *nif, void *machdep_hint) +{ + if (pxe_call == NULL) + return (-1); + + return (0); +} + +static void +pxe_netif_end(struct netif *nif) +{ + t_PXENV_UNDI_CLOSE *undi_close_p; + + undi_close_p = (t_PXENV_UNDI_CLOSE *)scratch_buffer; + bzero(undi_close_p, sizeof(*undi_close_p)); + pxe_call(PXENV_UNDI_CLOSE); + if (undi_close_p->Status != 0) + printf("undi close failed: %x\n", undi_close_p->Status); +} + +static void +pxe_netif_init(struct iodesc *desc, void *machdep_hint) +{ + t_PXENV_UNDI_GET_INFORMATION *undi_info_p; + t_PXENV_UNDI_OPEN *undi_open_p; + uint8_t *mac; + int i, len; + + undi_info_p = (t_PXENV_UNDI_GET_INFORMATION *)scratch_buffer; + bzero(undi_info_p, sizeof(*undi_info_p)); + pxe_call(PXENV_UNDI_GET_INFORMATION); + if (undi_info_p->Status != 0) { + printf("undi get info failed: %x\n", undi_info_p->Status); + return; + } + + /* Make sure the CurrentNodeAddress is valid. */ + for (i = 0; i < undi_info_p->HwAddrLen; ++i) { + if (undi_info_p->CurrentNodeAddress[i] != 0) + break; + } + if (i < undi_info_p->HwAddrLen) { + for (i = 0; i < undi_info_p->HwAddrLen; ++i) { + if (undi_info_p->CurrentNodeAddress[i] != 0xff) + break; + } + } + if (i < undi_info_p->HwAddrLen) + mac = undi_info_p->CurrentNodeAddress; + else + mac = undi_info_p->PermNodeAddress; + + len = min(sizeof (desc->myea), undi_info_p->HwAddrLen); + for (i = 0; i < len; ++i) + desc->myea[i] = mac[i]; + + if (bootp_response != NULL) + desc->xid = bootp_response->bp_xid; + else + desc->xid = 0; + + undi_open_p = (t_PXENV_UNDI_OPEN *)scratch_buffer; + bzero(undi_open_p, sizeof(*undi_open_p)); + undi_open_p->PktFilter = FLTR_DIRECTED | FLTR_BRDCST; + pxe_call(PXENV_UNDI_OPEN); + if (undi_open_p->Status != 0) + printf("undi open failed: %x\n", undi_open_p->Status); +} + +static int +pxe_netif_receive(void **pkt) +{ + t_PXENV_UNDI_ISR *isr = (t_PXENV_UNDI_ISR *)scratch_buffer; + char *buf, *ptr, *frame; + size_t size, rsize; + + bzero(isr, sizeof(*isr)); + isr->FuncFlag = PXENV_UNDI_ISR_IN_START; + pxe_call(PXENV_UNDI_ISR); + if (isr->Status != 0) + return (-1); + + bzero(isr, sizeof(*isr)); + isr->FuncFlag = PXENV_UNDI_ISR_IN_PROCESS; + pxe_call(PXENV_UNDI_ISR); + if (isr->Status != 0) + return (-1); + + while (isr->FuncFlag == PXENV_UNDI_ISR_OUT_TRANSMIT) { + /* + * Wait till transmit is done. + */ + bzero(isr, sizeof(*isr)); + isr->FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT; + pxe_call(PXENV_UNDI_ISR); + if (isr->Status != 0 || + isr->FuncFlag == PXENV_UNDI_ISR_OUT_DONE) + return (-1); + } + + while (isr->FuncFlag != PXENV_UNDI_ISR_OUT_RECEIVE) { + if (isr->Status != 0 || + isr->FuncFlag == PXENV_UNDI_ISR_OUT_DONE) { + return (-1); + } + bzero(isr, sizeof(*isr)); + isr->FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT; + pxe_call(PXENV_UNDI_ISR); + } + + size = isr->FrameLength; + buf = malloc(size + ETHER_ALIGN); + if (buf == NULL) + return (-1); + ptr = buf + ETHER_ALIGN; + rsize = 0; + + while (rsize < size) { + frame = (char *)((uintptr_t)isr->Frame.segment << 4); + frame += isr->Frame.offset; + bcopy(PTOV(frame), ptr, isr->BufferLength); + ptr += isr->BufferLength; + rsize += isr->BufferLength; + + bzero(isr, sizeof(*isr)); + isr->FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT; + pxe_call(PXENV_UNDI_ISR); + if (isr->Status != 0) { + free(buf); + return (-1); + } + + /* Did we got another update? */ + if (isr->FuncFlag == PXENV_UNDI_ISR_OUT_RECEIVE) + continue; + break; + } + + *pkt = buf; + return (rsize); +} + +static ssize_t +pxe_netif_get(struct iodesc *desc, void **pkt, time_t timeout) +{ + time_t t; + void *ptr; + int ret = -1; + + t = getsecs(); + while ((getsecs() - t) < timeout) { + ret = pxe_netif_receive(&ptr); + if (ret != -1) { + *pkt = ptr; + break; + } + } + return (ret); +} + +static ssize_t +pxe_netif_put(struct iodesc *desc, void *pkt, size_t len) +{ + t_PXENV_UNDI_TRANSMIT *trans_p; + t_PXENV_UNDI_TBD *tbd_p; + char *data; + + trans_p = (t_PXENV_UNDI_TRANSMIT *)scratch_buffer; + bzero(trans_p, sizeof(*trans_p)); + tbd_p = (t_PXENV_UNDI_TBD *)(scratch_buffer + sizeof(*trans_p)); + bzero(tbd_p, sizeof(*tbd_p)); + + data = scratch_buffer + sizeof(*trans_p) + sizeof(*tbd_p); + + trans_p->TBD.segment = VTOPSEG(tbd_p); + trans_p->TBD.offset = VTOPOFF(tbd_p); + + tbd_p->ImmedLength = len; + tbd_p->Xmit.segment = VTOPSEG(data); + tbd_p->Xmit.offset = VTOPOFF(data); + bcopy(pkt, data, len); + + pxe_call(PXENV_UNDI_TRANSMIT); + if (trans_p->Status != 0) { + return (-1); + } + + return (len); +} diff --git a/stand/i386/libi386/pxe.h b/stand/i386/libi386/pxe.h new file mode 100644 index 0000000..119c86b --- /dev/null +++ b/stand/i386/libi386/pxe.h @@ -0,0 +1,513 @@ +/* + * Copyright (c) 2000 Alfred Perlstein <alfred@freebsd.org> + * All rights reserved. + * Copyright (c) 2000 Paul Saab <ps@freebsd.org> + * All rights reserved. + * Copyright (c) 2000 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. + * + * 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. + * + * $FreeBSD$ + */ + +/* + * The typedefs and structures declared in this file + * clearly violate style(9), the reason for this is to conform to the + * typedefs/structure-names used in the Intel literature to avoid confusion. + * + * It's for your own good. :) + */ + +/* It seems that intel didn't think about ABI, + * either that or 16bit ABI != 32bit ABI (which seems reasonable) + * I have to thank Intel for the hair loss I incurred trying to figure + * out why PXE was mis-reading structures I was passing it (at least + * from my point of view) + * + * Solution: use gcc's '__packed' to correctly align + * structures passed into PXE + * Question: does this really work for PXE's expected ABI? + */ +#define PACKED __packed + +#define S_SIZE(s) s, sizeof(s) - 1 + +#define PXENFSROOTPATH "/pxeroot" + +typedef struct { + uint16_t offset; + uint16_t segment; +} SEGOFF16_t; + +typedef struct { + uint16_t Seg_Addr; + uint32_t Phy_Addr; + uint16_t Seg_Size; +} SEGDESC_t; + +typedef uint16_t SEGSEL_t; +typedef uint16_t PXENV_STATUS_t; +typedef uint32_t IP4_t; +typedef uint32_t ADDR32_t; +typedef uint16_t UDP_PORT_t; + +#define MAC_ADDR_LEN 16 +typedef uint8_t MAC_ADDR[MAC_ADDR_LEN]; + +/* PXENV+ */ +typedef struct { + uint8_t Signature[6]; /* 'PXENV+' */ + uint16_t Version; /* MSB = major, LSB = minor */ + uint8_t Length; /* structure length */ + uint8_t Checksum; /* checksum pad */ + SEGOFF16_t RMEntry; /* SEG:OFF to PXE entry point */ + /* don't use PMOffset and PMSelector (from the 2.1 PXE manual) */ + uint32_t PMOffset; /* Protected mode entry */ + SEGSEL_t PMSelector; /* Protected mode selector */ + SEGSEL_t StackSeg; /* Stack segment address */ + uint16_t StackSize; /* Stack segment size (bytes) */ + SEGSEL_t BC_CodeSeg; /* BC Code segment address */ + uint16_t BC_CodeSize; /* BC Code segment size (bytes) */ + SEGSEL_t BC_DataSeg; /* BC Data segment address */ + uint16_t BC_DataSize; /* BC Data segment size (bytes) */ + SEGSEL_t UNDIDataSeg; /* UNDI Data segment address */ + uint16_t UNDIDataSize; /* UNDI Data segment size (bytes) */ + SEGSEL_t UNDICodeSeg; /* UNDI Code segment address */ + uint16_t UNDICodeSize; /* UNDI Code segment size (bytes) */ + SEGOFF16_t PXEPtr; /* SEG:OFF to !PXE struct, + only present when Version > 2.1 */ +} PACKED pxenv_t; + +/* !PXE */ +typedef struct { + uint8_t Signature[4]; + uint8_t StructLength; + uint8_t StructCksum; + uint8_t StructRev; + uint8_t reserved_1; + SEGOFF16_t UNDIROMID; + SEGOFF16_t BaseROMID; + SEGOFF16_t EntryPointSP; + SEGOFF16_t EntryPointESP; + SEGOFF16_t StatusCallout; + uint8_t reserved_2; + uint8_t SegDescCn; + SEGSEL_t FirstSelector; + SEGDESC_t Stack; + SEGDESC_t UNDIData; + SEGDESC_t UNDICode; + SEGDESC_t UNDICodeWrite; + SEGDESC_t BC_Data; + SEGDESC_t BC_Code; + SEGDESC_t BC_CodeWrite; +} PACKED pxe_t; + +#define PXENV_START_UNDI 0x0000 +typedef struct { + PXENV_STATUS_t Status; + uint16_t ax; + uint16_t bx; + uint16_t dx; + uint16_t di; + uint16_t es; +} PACKED t_PXENV_START_UNDI; + +#define PXENV_UNDI_STARTUP 0x0001 +typedef struct { + PXENV_STATUS_t Status; +} PACKED t_PXENV_UNDI_STARTUP; + +#define PXENV_UNDI_CLEANUP 0x0002 +typedef struct { + PXENV_STATUS_t Status; +} PACKED t_PXENV_UNDI_CLEANUP; + +#define PXENV_UNDI_INITIALIZE 0x0003 +typedef struct { + PXENV_STATUS_t Status; + ADDR32_t ProtocolIni; /* Phys addr of a copy of the driver module */ + uint8_t reserved[8]; +} PACKED t_PXENV_UNDI_INITALIZE; + + +#define MAXNUM_MCADDR 8 +typedef struct { + PXENV_STATUS_t Status; + uint16_t MCastAddrCount; + MAC_ADDR McastAddr[MAXNUM_MCADDR]; +} PACKED t_PXENV_UNDI_MCAST_ADDRESS; + +#define PXENV_UNDI_RESET_ADAPTER 0x0004 +typedef struct { + PXENV_STATUS_t Status; + t_PXENV_UNDI_MCAST_ADDRESS R_Mcast_Buf; +} PACKED t_PXENV_UNDI_RESET; + +#define PXENV_UNDI_SHUTDOWN 0x0005 +typedef struct { + PXENV_STATUS_t Status; +} PACKED t_PXENV_UNDI_SHUTDOWN; + +#define PXENV_UNDI_OPEN 0x0006 +typedef struct { + PXENV_STATUS_t Status; + uint16_t OpenFlag; + uint16_t PktFilter; +# define FLTR_DIRECTED 0x0001 +# define FLTR_BRDCST 0x0002 +# define FLTR_PRMSCS 0x0004 +# define FLTR_SRC_RTG 0x0008 + + t_PXENV_UNDI_MCAST_ADDRESS R_Mcast_Buf; +} PACKED t_PXENV_UNDI_OPEN; + +#define PXENV_UNDI_CLOSE 0x0007 +typedef struct { + PXENV_STATUS_t Status; +} PACKED t_PXENV_UNDI_CLOSE; + +#define PXENV_UNDI_TRANSMIT 0x0008 +typedef struct { + PXENV_STATUS_t Status; + uint8_t Protocol; +# define P_UNKNOWN 0 +# define P_IP 1 +# define P_ARP 2 +# define P_RARP 3 + + uint8_t XmitFlag; +# define XMT_DESTADDR 0x0000 +# define XMT_BROADCAST 0x0001 + + SEGOFF16_t DestAddr; + SEGOFF16_t TBD; + uint32_t Reserved[2]; +} PACKED t_PXENV_UNDI_TRANSMIT; + +#define MAX_DATA_BLKS 8 +typedef struct { + uint16_t ImmedLength; + SEGOFF16_t Xmit; + uint16_t DataBlkCount; + struct DataBlk { + uint8_t TDPtrType; + uint8_t TDRsvdByte; + uint16_t TDDataLen; + SEGOFF16_t TDDataPtr; + } DataBlock[MAX_DATA_BLKS]; +} PACKED t_PXENV_UNDI_TBD; + +#define PXENV_UNDI_SET_MCAST_ADDRESS 0x0009 +typedef struct { + PXENV_STATUS_t Status; + t_PXENV_UNDI_MCAST_ADDRESS R_Mcast_Buf; +} PACKED t_PXENV_UNDI_SET_MCAST_ADDR; + +#define PXENV_UNDI_SET_STATION_ADDRESS 0x000A +typedef struct { + PXENV_STATUS_t Status; + MAC_ADDR StationAddress; /* Temp MAC address to use */ +} PACKED t_PXENV_UNDI_SET_STATION_ADDR; + +#define PXENV_UNDI_SET_PACKET_FILTER 0x000B +typedef struct { + PXENV_STATUS_t Status; + uint8_t filter; /* see UNDI_OPEN (0x0006) */ +} PACKED t_PXENV_UNDI_SET_PACKET_FILTER; + +#define PXENV_UNDI_GET_INFORMATION 0x000C +typedef struct { + PXENV_STATUS_t Status; + uint16_t BaseIo; /* Adapter base I/O address */ + uint16_t IntNumber; /* Adapter IRQ number */ + uint16_t MaxTranUnit; /* Adapter maximum transmit unit */ + uint16_t HwType; /* Type of protocol at the hardware addr */ +# define ETHER_TYPE 1 +# define EXP_ETHER_TYPE 2 +# define IEEE_TYPE 6 +# define ARCNET_TYPE 7 + + uint16_t HwAddrLen; /* Length of hardware address */ + MAC_ADDR CurrentNodeAddress; /* Current hardware address */ + MAC_ADDR PermNodeAddress; /* Permanent hardware address */ + SEGSEL_t ROMAddress; /* Real mode ROM segment address */ + uint16_t RxBufCt; /* Receive queue length */ + uint16_t TxBufCt; /* Transmit queue length */ +} PACKED t_PXENV_UNDI_GET_INFORMATION; + +#define PXENV_UNDI_GET_STATISTICS 0x000D +typedef struct { + PXENV_STATUS_t Status; + uint32_t XmitGoodFrames; /* Number of successful transmissions */ + uint32_t RcvGoodFrames; /* Number of good frames received */ + uint32_t RcvCRCErrors; /* Number of frames with CRC errors */ + uint32_t RcvResourceErrors; /* Number of frames dropped */ +} PACKED t_PXENV_UNDI_GET_STATISTICS; + +#define PXENV_UNDI_CLEAR_STATISTICS 0x000E +typedef struct { + PXENV_STATUS_t Status; +} PACKED t_PXENV_UNDI_CLEAR_STATISTICS; + +#define PXENV_UNDI_INITIATE_DIAGS 0x000F +typedef struct { + PXENV_STATUS_t Status; +} PACKED t_PXENV_UNDI_INITIATE_DIAGS; + +#define PXENV_UNDI_FORCE_INTERRUPT 0x0010 +typedef struct { + PXENV_STATUS_t Status; +} PACKED t_PXENV_UNDI_FORCE_INTERRUPT; + +#define PXENV_UNDI_GET_MCAST_ADDRESS 0x0011 +typedef struct { + PXENV_STATUS_t Status; + IP4_t InetAddr; /* IP mulicast address */ + MAC_ADDR MediaAddr; /* MAC multicast address */ +} PACKED t_PXENV_UNDI_GET_MCAST_ADDR; + +#define PXENV_UNDI_GET_NIC_TYPE 0x0012 +typedef struct { + PXENV_STATUS_t Status; + uint8_t NicType; /* Type of NIC */ +# define PCI_NIC 2 +# define PnP_NIC 3 +# define CardBus_NIC 4 + + union { + struct { + uint16_t Vendor_ID; + uint16_t Dev_ID; + uint8_t Base_Class; + uint8_t Sub_Class; + uint8_t Prog_Intf; + uint8_t Rev; + uint16_t BusDevFunc; + uint16_t SubVendor_ID; + uint16_t SubDevice_ID; + } pci, cardbus; + struct { + uint32_t EISA_Dev_ID; + uint8_t Base_Class; + uint8_t Sub_Class; + uint8_t Prog_Intf; + uint16_t CardSelNum; + } pnp; + } info; +} PACKED t_PXENV_UNDI_GET_NIC_TYPE; + +#define PXENV_UNDI_GET_IFACE_INFO 0x0013 +typedef struct { + PXENV_STATUS_t Status; + uint8_t IfaceType[16]; /* Name of MAC type in ASCII. */ + uint32_t LinkSpeed; /* Defined in NDIS 2.0 spec */ + uint32_t ServiceFlags; /* Defined in NDIS 2.0 spec */ + uint32_t Reserved[4]; /* must be 0 */ +} PACKED t_PXENV_UNDI_GET_NDIS_INFO; + +#define PXENV_UNDI_ISR 0x0014 +typedef struct { + PXENV_STATUS_t Status; + uint16_t FuncFlag; /* PXENV_UNDI_ISR_OUT_xxx */ + uint16_t BufferLength; /* Length of Frame */ + uint16_t FrameLength; /* Total length of receiver frame */ + uint16_t FrameHeaderLength; /* Length of the media header in Frame */ + SEGOFF16_t Frame; /* receive buffer */ + uint8_t ProtType; /* Protocol type */ + uint8_t PktType; /* Packet Type */ +# define PXENV_UNDI_ISR_IN_START 1 +# define PXENV_UNDI_ISR_IN_PROCESS 2 +# define PXENV_UNDI_ISR_IN_GET_NEXT 3 + + /* one of these will be returned for PXENV_UNDI_ISR_IN_START */ +# define PXENV_UNDI_ISR_OUT_OURS 0 +# define PXENV_UNDI_ISR_OUT_NOT_OUTS 1 + + /* + * one of these will bre returned for PXEND_UNDI_ISR_IN_PROCESS + * and PXENV_UNDI_ISR_IN_GET_NEXT + */ +# define PXENV_UNDI_ISR_OUT_DONE 0 +# define PXENV_UNDI_ISR_OUT_TRANSMIT 2 +# define PXENV_UNDI_ISR_OUT_RECEIVE 3 +# define PXENV_UNDI_ISR_OUT_BUSY 4 +} PACKED t_PXENV_UNDI_ISR; + +#define PXENV_STOP_UNDI 0x0015 +typedef struct { + PXENV_STATUS_t Status; +} PACKED t_PXENV_STOP_UNDI; + +#define PXENV_TFTP_OPEN 0x0020 +typedef struct { + PXENV_STATUS_t Status; + IP4_t ServerIPAddress; + IP4_t GatewayIPAddress; + uint8_t FileName[128]; + UDP_PORT_t TFTPPort; + uint16_t PacketSize; +} PACKED t_PXENV_TFTP_OPEN; + +#define PXENV_TFTP_CLOSE 0x0021 +typedef struct { + PXENV_STATUS_t Status; +} PACKED t_PXENV_TFTP_CLOSE; + +#define PXENV_TFTP_READ 0x0022 +typedef struct { + PXENV_STATUS_t Status; + uint16_t PacketNumber; + uint16_t BufferSize; + SEGOFF16_t Buffer; +} PACKED t_PXENV_TFTP_READ; + +#define PXENV_TFTP_READ_FILE 0x0023 +typedef struct { + PXENV_STATUS_t Status; + uint8_t FileName[128]; + uint32_t BufferSize; + ADDR32_t Buffer; + IP4_t ServerIPAddress; + IP4_t GatewayIPAdress; + IP4_t McastIPAdress; + UDP_PORT_t TFTPClntPort; + UDP_PORT_t TFTPSrvPort; + uint16_t TFTPOpenTimeOut; + uint16_t TFTPReopenDelay; +} PACKED t_PXENV_TFTP_READ_FILE; + +#define PXENV_TFTP_GET_FSIZE 0x0025 +typedef struct { + PXENV_STATUS_t Status; + IP4_t ServerIPAddress; + IP4_t GatewayIPAdress; + uint8_t FileName[128]; + uint32_t FileSize; +} PACKED t_PXENV_TFTP_GET_FSIZE; + +#define PXENV_UDP_OPEN 0x0030 +typedef struct { + PXENV_STATUS_t status; + IP4_t src_ip; /* IP address of this station */ +} PACKED t_PXENV_UDP_OPEN; + +#define PXENV_UDP_CLOSE 0x0031 +typedef struct { + PXENV_STATUS_t status; +} PACKED t_PXENV_UDP_CLOSE; + +#define PXENV_UDP_READ 0x0032 +typedef struct { + PXENV_STATUS_t status; + IP4_t src_ip; /* IP of sender */ + IP4_t dest_ip; /* Only accept packets sent to this IP */ + UDP_PORT_t s_port; /* UDP source port of sender */ + UDP_PORT_t d_port; /* Only accept packets sent to this port */ + uint16_t buffer_size; /* Size of the packet buffer */ + SEGOFF16_t buffer; /* SEG:OFF to the packet buffer */ +} PACKED t_PXENV_UDP_READ; + +#define PXENV_UDP_WRITE 0x0033 +typedef struct { + PXENV_STATUS_t status; + IP4_t ip; /* dest ip addr */ + IP4_t gw; /* ip gateway */ + UDP_PORT_t src_port; /* source udp port */ + UDP_PORT_t dst_port; /* destination udp port */ + uint16_t buffer_size; /* Size of the packet buffer */ + SEGOFF16_t buffer; /* SEG:OFF to the packet buffer */ +} PACKED t_PXENV_UDP_WRITE; + +#define PXENV_UNLOAD_STACK 0x0070 +typedef struct { + PXENV_STATUS_t Status; + uint8_t reserved[10]; +} PACKED t_PXENV_UNLOAD_STACK; + + +#define PXENV_GET_CACHED_INFO 0x0071 +typedef struct { + PXENV_STATUS_t Status; + uint16_t PacketType; /* type (defined right here) */ +# define PXENV_PACKET_TYPE_DHCP_DISCOVER 1 +# define PXENV_PACKET_TYPE_DHCP_ACK 2 +# define PXENV_PACKET_TYPE_BINL_REPLY 3 + uint16_t BufferSize; /* max to copy, leave at 0 for pointer */ + SEGOFF16_t Buffer; /* copy to, leave at 0 for pointer */ + uint16_t BufferLimit; /* max size of buffer in BC dataseg ? */ +} PACKED t_PXENV_GET_CACHED_INFO; + + +/* structure filled in by PXENV_GET_CACHED_INFO + * (how we determine which IP we downloaded the initial bootstrap from) + * words can't describe... + */ +typedef struct { + uint8_t opcode; +# define BOOTP_REQ 1 +# define BOOTP_REP 2 + uint8_t Hardware; /* hardware type */ + uint8_t Hardlen; /* hardware addr len */ + uint8_t Gatehops; /* zero it */ + uint32_t ident; /* random number chosen by client */ + uint16_t seconds; /* seconds since did initial bootstrap */ + uint16_t Flags; /* seconds since did initial bootstrap */ +# define BOOTP_BCAST 0x8000 /* ? */ + IP4_t cip; /* Client IP */ + IP4_t yip; /* Your IP */ + IP4_t sip; /* IP to use for next boot stage */ + IP4_t gip; /* Relay IP ? */ + MAC_ADDR CAddr; /* Client hardware address */ + uint8_t Sname[64]; /* Server's hostname (Optional) */ + uint8_t bootfile[128]; /* boot filename */ + union { +# if 1 +# define BOOTP_DHCPVEND 1024 /* DHCP extended vendor field size */ +# else +# define BOOTP_DHCPVEND 312 /* DHCP standard vendor field size */ +# endif + uint8_t d[BOOTP_DHCPVEND]; /* raw array of vendor/dhcp options */ + struct { + uint8_t magic[4]; /* DHCP magic cookie */ +# ifndef VM_RFC1048 +# define VM_RFC1048 0x63825363L /* ? */ +# endif + uint32_t flags; /* bootp flags/opcodes */ + uint8_t pad[56]; /* I don't think intel knows what a + union does... */ + } v; + } vendor; +} PACKED BOOTPLAYER; + +#define PXENV_RESTART_TFTP 0x0073 +#define t_PXENV_RESTART_TFTP t_PXENV_TFTP_READ_FILE + +#define PXENV_START_BASE 0x0075 +typedef struct { + PXENV_STATUS_t Status; +} PACKED t_PXENV_START_BASE; + +#define PXENV_STOP_BASE 0x0076 +typedef struct { + PXENV_STATUS_t Status; +} PACKED t_PXENV_STOP_BASE; diff --git a/stand/i386/libi386/pxetramp.s b/stand/i386/libi386/pxetramp.s new file mode 100644 index 0000000..dcf1441 --- /dev/null +++ b/stand/i386/libi386/pxetramp.s @@ -0,0 +1,38 @@ +# +# Copyright (c) 2000 Peter Wemm +# All rights reserved. +# +# Redistribution and use in source and binary forms are freely +# permitted provided that the above copyright notice and this +# paragraph and the following disclaimer are duplicated in all +# such forms. +# +# This software is provided "AS IS" and without any express or +# implied warranties, including, without limitation, the implied +# warranties of merchantability and fitness for a particular +# purpose. +# +# $FreeBSD$ + +# ph33r this + + .globl __bangpxeentry, __bangpxeseg, __bangpxeoff + .globl __pxenventry, __pxenvseg, __pxenvoff + + .code16 + .p2align 4,0x90 +__bangpxeentry: + push %dx # seg:data + push %ax # off:data + push %bx # int16 func + .byte 0x9a # far call +__bangpxeoff: .word 0x0000 # offset +__bangpxeseg: .word 0x0000 # segment + add $6, %sp # restore stack + .byte 0xcb # to vm86int +# +__pxenventry: + .byte 0x9a # far call +__pxenvoff: .word 0x0000 # offset +__pxenvseg: .word 0x0000 # segment + .byte 0xcb # to vm86int diff --git a/stand/i386/libi386/relocater_tramp.S b/stand/i386/libi386/relocater_tramp.S new file mode 100644 index 0000000..6db4a75 --- /dev/null +++ b/stand/i386/libi386/relocater_tramp.S @@ -0,0 +1,358 @@ +/*- + * Copyright 2015 Toomas Soome <tsoome@me.com> + * 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 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. + * + * $FreeBSD$ + */ + + +/* + * relocate is needed to support loading code which has to be located + * below 1MB, as both BTX and loader are using low memory area. + * + * relocate and start loaded code. Since loaded code may need to be + * placed to already occupied memory area, this code is moved to safe + * memory area and then btx __exec will be called with physical pointer + * to this area. __exec will set pointer to %eax and use call *%eax, + * so on entry, we have new "base" address in %eax. + * + * Relocate will first set up and load new safe GDT to shut down BTX, + * then loaded code will be relocated to final memory location, + * then machine will be switched from 32bit protected mode to 16bit + * protected mode following by switch to real mode with A20 enabled or + * disabled. Finally the loaded code will be started and it will take + * over the whole system. + * + * For now, the known "safe" memory area for relocate is 0x600, + * the actual "free" memory is supposed to start from 0x500, leaving + * first 0x100 bytes in reserve. As relocate code+data is very small, + * it will leave enough space to set up boot blocks to 0:7c00 or load + * linux kernel below 1MB space. + */ +/* + * segment selectors + */ + .set SEL_SCODE,0x8 + .set SEL_SDATA,0x10 + .set SEL_RCODE,0x18 + .set SEL_RDATA,0x20 + + .p2align 4 + .globl relocater +relocater: + cli + /* + * set up GDT from new location + */ + movl %eax, %esi /* our base address */ + add $(relocater.1-relocater), %eax + jmp *%eax +relocater.1: + /* set up jump */ + lea (relocater.2-relocater)(%esi), %eax + movl %eax, (jump_vector-relocater) (%esi) + + /* set up gdt */ + lea (gdt-relocater) (%esi), %eax + movl %eax, (gdtaddr-relocater) (%esi) + + /* load gdt */ + lgdt (gdtdesc - relocater) (%esi) + lidt (idt-relocater) (%esi) + + /* update cs */ + ljmp *(jump_vector-relocater) (%esi) + + .code32 +relocater.2: + xorl %eax, %eax + movb $SEL_SDATA, %al + movw %ax, %ss + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + movl %cr0, %eax /* disable paging */ + andl $~0x80000000,%eax + movl %eax, %cr0 + xorl %ecx, %ecx /* flush TLB */ + movl %ecx, %cr3 + cld +/* + * relocate data loop. load source, dest and size from + * relocater_data[i], 0 value will stop the loop. + * registers used for move: %esi, %edi, %ecx. + * %ebx to keep base + * %edx for relocater_data offset + */ + movl %esi, %ebx /* base address */ + xorl %edx, %edx +loop.1: + movl (relocater_data-relocater)(%ebx, %edx, 4), %eax + testl %eax, %eax + jz loop.2 + movl (relocater_data-relocater)(%ebx, %edx, 4), %esi + inc %edx + movl (relocater_data-relocater)(%ebx, %edx, 4), %edi + inc %edx + movl (relocater_data-relocater)(%ebx, %edx, 4), %ecx + inc %edx + rep + movsb + jmp loop.1 +loop.2: + movl %ebx, %esi /* restore esi */ + /* + * data is relocated, switch to 16bit mode + */ + lea (relocater.3-relocater)(%esi), %eax + movl %eax, (jump_vector-relocater) (%esi) + movl $SEL_RCODE, %eax + movl %eax, (jump_vector-relocater+4) (%esi) + + ljmp *(jump_vector-relocater) (%esi) +relocater.3: + .code16 + + movw $SEL_RDATA, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + movw %ax, %ss + lidt (idt-relocater) (%esi) + lea (relocater.4-relocater)(%esi), %eax + movl %eax, (jump_vector-relocater) (%esi) + xorl %eax, %eax + movl %eax, (jump_vector-relocater+4) (%esi) + /* clear PE */ + movl %cr0, %eax + dec %al + movl %eax, %cr0 + ljmp *(jump_vector-relocater) (%esi) +relocater.4: + xorw %ax, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + movw %ax, %ss + /* + * set real mode irq offsets + */ + movw $0x7008,%bx + in $0x21,%al # Save master + push %ax # IMR + in $0xa1,%al # Save slave + push %ax # IMR + movb $0x11,%al # ICW1 to + outb %al,$0x20 # master, + outb %al,$0xa0 # slave + movb %bl,%al # ICW2 to + outb %al,$0x21 # master + movb %bh,%al # ICW2 to + outb %al,$0xa1 # slave + movb $0x4,%al # ICW3 to + outb %al,$0x21 # master + movb $0x2,%al # ICW3 to + outb %al,$0xa1 # slave + movb $0x1,%al # ICW4 to + outb %al,$0x21 # master, + outb %al,$0xa1 # slave + pop %ax # Restore slave + outb %al,$0xa1 # IMR + pop %ax # Restore master + outb %al,$0x21 # IMR + # done + /* + * Should A20 be left enabled? + */ + /* movw imm16, %ax */ + .byte 0xb8 + .globl relocator_a20_enabled +relocator_a20_enabled: + .word 0 + test %ax, %ax + jnz a20_done + + movw $0xa00, %ax + movw %ax, %sp + movw %ax, %bp + + /* Disable A20 */ + movw $0x2400, %ax + int $0x15 +# jnc a20_done + + call a20_check_state + testb %al, %al + jz a20_done + + inb $0x92 + andb $(~0x03), %al + outb $0x92 + jmp a20_done + +a20_check_state: + movw $100, %cx +1: + xorw %ax, %ax + movw %ax, %ds + decw %ax + movw %ax, %es + xorw %ax, %ax + movw $0x8000, %ax + movw %ax, %si + addw $0x10, %ax + movw %ax, %di + movb %ds:(%si), %dl + movb %es:(%di), %al + movb %al, %dh + decb %dh + movb %dh, %ds:(%si) + outb %al, $0x80 + outb %al, $0x80 + movb %es:(%di), %dh + subb %dh, %al + xorb $1, %al + movb %dl, %ds:(%si) + testb %al, %al + jz a20_done + loop 1b + ret +a20_done: + /* + * set up registers + */ + /* movw imm16, %ax. */ + .byte 0xb8 + .globl relocator_ds +relocator_ds: .word 0 + movw %ax, %ds + + /* movw imm16, %ax. */ + .byte 0xb8 + .globl relocator_es +relocator_es: .word 0 + movw %ax, %es + + /* movw imm16, %ax. */ + .byte 0xb8 + .globl relocator_fs +relocator_fs: .word 0 + movw %ax, %fs + + /* movw imm16, %ax. */ + .byte 0xb8 + .globl relocator_gs +relocator_gs: .word 0 + movw %ax, %gs + + /* movw imm16, %ax. */ + .byte 0xb8 + .globl relocator_ss +relocator_ss: .word 0 + movw %ax, %ss + + /* movw imm16, %ax. */ + .byte 0xb8 + .globl relocator_sp +relocator_sp: .word 0 + movzwl %ax, %esp + + /* movw imm32, %eax. */ + .byte 0x66, 0xb8 + .globl relocator_esi +relocator_esi: .long 0 + movl %eax, %esi + + /* movw imm32, %edx. */ + .byte 0x66, 0xba + .globl relocator_edx +relocator_edx: .long 0 + + /* movw imm32, %ebx. */ + .byte 0x66, 0xbb + .globl relocator_ebx +relocator_ebx: .long 0 + + /* movw imm32, %eax. */ + .byte 0x66, 0xb8 + .globl relocator_eax +relocator_eax: .long 0 + + /* movw imm32, %ebp. */ + .byte 0x66, 0xbd + .globl relocator_ebp +relocator_ebp: .long 0 + + sti + .byte 0xea /* ljmp */ + .globl relocator_ip +relocator_ip: + .word 0 + .globl relocator_cs +relocator_cs: + .word 0 + +/* GDT to reset BTX */ + .code32 + .p2align 4 +jump_vector: .long 0 + .long SEL_SCODE + +gdt: .word 0x0, 0x0 /* null entry */ + .byte 0x0, 0x0, 0x0, 0x0 + .word 0xffff, 0x0 /* SEL_SCODE */ + .byte 0x0, 0x9a, 0xcf, 0x0 + .word 0xffff, 0x0 /* SEL_SDATA */ + .byte 0x0, 0x92, 0xcf, 0x0 + .word 0xffff, 0x0 /* SEL_RCODE */ + .byte 0x0, 0x9a, 0x0f, 0x0 + .word 0xffff, 0x0 /* SEL_RDATA */ + .byte 0x0, 0x92, 0x0f, 0x0 +gdt.1: + +gdtdesc: .word gdt.1 - gdt - 1 /* limit */ +gdtaddr: .long 0 /* base */ + +idt: .word 0x3ff + .long 0 + + .globl relocater_data +relocater_data: + .long 0 /* src */ + .long 0 /* dest */ + .long 0 /* size */ + .long 0 /* src */ + .long 0 /* dest */ + .long 0 /* size */ + .long 0 /* src */ + .long 0 /* dest */ + .long 0 /* size */ + .long 0 + + .globl relocater_size +relocater_size: + .long relocater_size-relocater diff --git a/stand/i386/libi386/smbios.c b/stand/i386/libi386/smbios.c new file mode 100644 index 0000000..2aa62fa --- /dev/null +++ b/stand/i386/libi386/smbios.c @@ -0,0 +1,454 @@ +/*- + * Copyright (c) 2005-2009 Jung-uk Kim <jkim@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. + * + * 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stand.h> +#include <bootstrap.h> +#include <sys/endian.h> + +#ifdef EFI +/* In EFI, we don't need PTOV(). */ +#define PTOV(x) (caddr_t)(x) +#else +#include "btxv86.h" +#endif +#include "smbios.h" + +/* + * Detect SMBIOS and export information about the SMBIOS into the + * environment. + * + * System Management BIOS Reference Specification, v2.6 Final + * http://www.dmtf.org/standards/published_documents/DSP0134_2.6.0.pdf + */ + +/* + * 2.1.1 SMBIOS Structure Table Entry Point + * + * "On non-EFI systems, the SMBIOS Entry Point structure, described below, can + * be located by application software by searching for the anchor-string on + * paragraph (16-byte) boundaries within the physical memory address range + * 000F0000h to 000FFFFFh. This entry point encapsulates an intermediate anchor + * string that is used by some existing DMI browsers." + */ +#define SMBIOS_START 0xf0000 +#define SMBIOS_LENGTH 0x10000 +#define SMBIOS_STEP 0x10 +#define SMBIOS_SIG "_SM_" +#define SMBIOS_DMI_SIG "_DMI_" + +#define SMBIOS_GET8(base, off) (*(uint8_t *)((base) + (off))) +#define SMBIOS_GET16(base, off) (*(uint16_t *)((base) + (off))) +#define SMBIOS_GET32(base, off) (*(uint32_t *)((base) + (off))) + +#define SMBIOS_GETLEN(base) SMBIOS_GET8(base, 0x01) +#define SMBIOS_GETSTR(base) ((base) + SMBIOS_GETLEN(base)) + +struct smbios_attr { + int probed; + caddr_t addr; + size_t length; + size_t count; + int major; + int minor; + int ver; + const char* bios_vendor; + const char* maker; + const char* product; + uint32_t enabled_memory; + uint32_t old_enabled_memory; + uint8_t enabled_sockets; + uint8_t populated_sockets; +}; + +static struct smbios_attr smbios; + +static uint8_t +smbios_checksum(const caddr_t addr, const uint8_t len) +{ + uint8_t sum; + int i; + + for (sum = 0, i = 0; i < len; i++) + sum += SMBIOS_GET8(addr, i); + return (sum); +} + +static caddr_t +smbios_sigsearch(const caddr_t addr, const uint32_t len) +{ + caddr_t cp; + + /* Search on 16-byte boundaries. */ + for (cp = addr; cp < addr + len; cp += SMBIOS_STEP) + if (strncmp(cp, SMBIOS_SIG, 4) == 0 && + smbios_checksum(cp, SMBIOS_GET8(cp, 0x05)) == 0 && + strncmp(cp + 0x10, SMBIOS_DMI_SIG, 5) == 0 && + smbios_checksum(cp + 0x10, 0x0f) == 0) + return (cp); + return (NULL); +} + +static const char* +smbios_getstring(caddr_t addr, const int offset) +{ + caddr_t cp; + int i, idx; + + idx = SMBIOS_GET8(addr, offset); + if (idx != 0) { + cp = SMBIOS_GETSTR(addr); + for (i = 1; i < idx; i++) + cp += strlen(cp) + 1; + return cp; + } + return (NULL); +} + +static void +smbios_setenv(const char *name, caddr_t addr, const int offset) +{ + const char* val; + + val = smbios_getstring(addr, offset); + if (val != NULL) + setenv(name, val, 1); +} + +#ifdef SMBIOS_SERIAL_NUMBERS + +#define UUID_SIZE 16 +#define UUID_TYPE uint32_t +#define UUID_STEP sizeof(UUID_TYPE) +#define UUID_ALL_BITS (UUID_SIZE / UUID_STEP) +#define UUID_GET(base, off) (*(UUID_TYPE *)((base) + (off))) + +static void +smbios_setuuid(const char *name, const caddr_t addr, const int ver) +{ + char uuid[37]; + int byteorder, i, ones, zeros; + UUID_TYPE n; + uint32_t f1; + uint16_t f2, f3; + + for (i = 0, ones = 0, zeros = 0; i < UUID_SIZE; i += UUID_STEP) { + n = UUID_GET(addr, i) + 1; + if (zeros == 0 && n == 0) + ones++; + else if (ones == 0 && n == 1) + zeros++; + else + break; + } + + if (ones != UUID_ALL_BITS && zeros != UUID_ALL_BITS) { + /* + * 3.3.2.1 System UUID + * + * "Although RFC 4122 recommends network byte order for all + * fields, the PC industry (including the ACPI, UEFI, and + * Microsoft specifications) has consistently used + * little-endian byte encoding for the first three fields: + * time_low, time_mid, time_hi_and_version. The same encoding, + * also known as wire format, should also be used for the + * SMBIOS representation of the UUID." + * + * Note: We use network byte order for backward compatibility + * unless SMBIOS version is 2.6+ or little-endian is forced. + */ +#if defined(SMBIOS_LITTLE_ENDIAN_UUID) + byteorder = LITTLE_ENDIAN; +#elif defined(SMBIOS_NETWORK_ENDIAN_UUID) + byteorder = BIG_ENDIAN; +#else + byteorder = ver < 0x0206 ? BIG_ENDIAN : LITTLE_ENDIAN; +#endif + if (byteorder != LITTLE_ENDIAN) { + f1 = ntohl(SMBIOS_GET32(addr, 0)); + f2 = ntohs(SMBIOS_GET16(addr, 4)); + f3 = ntohs(SMBIOS_GET16(addr, 6)); + } else { + f1 = le32toh(SMBIOS_GET32(addr, 0)); + f2 = le16toh(SMBIOS_GET16(addr, 4)); + f3 = le16toh(SMBIOS_GET16(addr, 6)); + } + sprintf(uuid, + "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + f1, f2, f3, SMBIOS_GET8(addr, 8), SMBIOS_GET8(addr, 9), + SMBIOS_GET8(addr, 10), SMBIOS_GET8(addr, 11), + SMBIOS_GET8(addr, 12), SMBIOS_GET8(addr, 13), + SMBIOS_GET8(addr, 14), SMBIOS_GET8(addr, 15)); + setenv(name, uuid, 1); + } +} + +#undef UUID_SIZE +#undef UUID_TYPE +#undef UUID_STEP +#undef UUID_ALL_BITS +#undef UUID_GET + +#endif + +static caddr_t +smbios_parse_table(const caddr_t addr) +{ + caddr_t cp; + int proc, size, osize, type; + + type = SMBIOS_GET8(addr, 0); /* 3.1.2 Structure Header Format */ + switch(type) { + case 0: /* 3.3.1 BIOS Information (Type 0) */ + smbios_setenv("smbios.bios.vendor", addr, 0x04); + smbios_setenv("smbios.bios.version", addr, 0x05); + smbios_setenv("smbios.bios.reldate", addr, 0x08); + break; + + case 1: /* 3.3.2 System Information (Type 1) */ + smbios_setenv("smbios.system.maker", addr, 0x04); + smbios_setenv("smbios.system.product", addr, 0x05); + smbios_setenv("smbios.system.version", addr, 0x06); +#ifdef SMBIOS_SERIAL_NUMBERS + smbios_setenv("smbios.system.serial", addr, 0x07); + smbios_setuuid("smbios.system.uuid", addr + 0x08, smbios.ver); +#endif + if (smbios.major > 2 || + (smbios.major == 2 && smbios.minor >= 4)) { + smbios_setenv("smbios.system.sku", addr, 0x19); + smbios_setenv("smbios.system.family", addr, 0x1a); + } + break; + + case 2: /* 3.3.3 Base Board (or Module) Information (Type 2) */ + smbios_setenv("smbios.planar.maker", addr, 0x04); + smbios_setenv("smbios.planar.product", addr, 0x05); + smbios_setenv("smbios.planar.version", addr, 0x06); +#ifdef SMBIOS_SERIAL_NUMBERS + smbios_setenv("smbios.planar.serial", addr, 0x07); + smbios_setenv("smbios.planar.tag", addr, 0x08); +#endif + smbios_setenv("smbios.planar.location", addr, 0x0a); + break; + + case 3: /* 3.3.4 System Enclosure or Chassis (Type 3) */ + smbios_setenv("smbios.chassis.maker", addr, 0x04); + smbios_setenv("smbios.chassis.version", addr, 0x06); +#ifdef SMBIOS_SERIAL_NUMBERS + smbios_setenv("smbios.chassis.serial", addr, 0x07); + smbios_setenv("smbios.chassis.tag", addr, 0x08); +#endif + break; + + case 4: /* 3.3.5 Processor Information (Type 4) */ + /* + * Offset 18h: Processor Status + * + * Bit 7 Reserved, must be 0 + * Bit 6 CPU Socket Populated + * 1 - CPU Socket Populated + * 0 - CPU Socket Unpopulated + * Bit 5:3 Reserved, must be zero + * Bit 2:0 CPU Status + * 0h - Unknown + * 1h - CPU Enabled + * 2h - CPU Disabled by User via BIOS Setup + * 3h - CPU Disabled by BIOS (POST Error) + * 4h - CPU is Idle, waiting to be enabled + * 5-6h - Reserved + * 7h - Other + */ + proc = SMBIOS_GET8(addr, 0x18); + if ((proc & 0x07) == 1) + smbios.enabled_sockets++; + if ((proc & 0x40) != 0) + smbios.populated_sockets++; + break; + + case 6: /* 3.3.7 Memory Module Information (Type 6, Obsolete) */ + /* + * Offset 0Ah: Enabled Size + * + * Bit 7 Bank connection + * 1 - Double-bank connection + * 0 - Single-bank connection + * Bit 6:0 Size (n), where 2**n is the size in MB + * 7Dh - Not determinable (Installed Size only) + * 7Eh - Module is installed, but no memory + * has been enabled + * 7Fh - Not installed + */ + osize = SMBIOS_GET8(addr, 0x0a) & 0x7f; + if (osize > 0 && osize < 22) + smbios.old_enabled_memory += 1 << (osize + 10); + break; + + case 17: /* 3.3.18 Memory Device (Type 17) */ + /* + * Offset 0Ch: Size + * + * Bit 15 Granularity + * 1 - Value is in kilobytes units + * 0 - Value is in megabytes units + * Bit 14:0 Size + */ + size = SMBIOS_GET16(addr, 0x0c); + if (size != 0 && size != 0xffff) + smbios.enabled_memory += (size & 0x8000) != 0 ? + (size & 0x7fff) : (size << 10); + break; + + default: /* skip other types */ + break; + } + + /* Find structure terminator. */ + cp = SMBIOS_GETSTR(addr); + while (SMBIOS_GET16(cp, 0) != 0) + cp++; + + return (cp + 2); +} + +static caddr_t +smbios_find_struct(int type) +{ + caddr_t dmi; + size_t i; + + if (smbios.addr == NULL) + return (NULL); + + for (dmi = smbios.addr, i = 0; + dmi < smbios.addr + smbios.length && i < smbios.count; i++) { + if (SMBIOS_GET8(dmi, 0) == type) + return dmi; + /* Find structure terminator. */ + dmi = SMBIOS_GETSTR(dmi); + while (SMBIOS_GET16(dmi, 0) != 0) + dmi++; + dmi += 2; + } + + return (NULL); +} + +static void +smbios_probe(const caddr_t addr) +{ + caddr_t saddr, info; + uintptr_t paddr; + + if (smbios.probed) + return; + smbios.probed = 1; + + /* Search signatures and validate checksums. */ + saddr = smbios_sigsearch(addr ? addr : PTOV(SMBIOS_START), + SMBIOS_LENGTH); + if (saddr == NULL) + return; + + smbios.length = SMBIOS_GET16(saddr, 0x16); /* Structure Table Length */ + paddr = SMBIOS_GET32(saddr, 0x18); /* Structure Table Address */ + smbios.count = SMBIOS_GET16(saddr, 0x1c); /* No of SMBIOS Structures */ + smbios.ver = SMBIOS_GET8(saddr, 0x1e); /* SMBIOS BCD Revision */ + + if (smbios.ver != 0) { + smbios.major = smbios.ver >> 4; + smbios.minor = smbios.ver & 0x0f; + if (smbios.major > 9 || smbios.minor > 9) + smbios.ver = 0; + } + if (smbios.ver == 0) { + smbios.major = SMBIOS_GET8(saddr, 0x06);/* SMBIOS Major Version */ + smbios.minor = SMBIOS_GET8(saddr, 0x07);/* SMBIOS Minor Version */ + } + smbios.ver = (smbios.major << 8) | smbios.minor; + smbios.addr = PTOV(paddr); + + /* Get system information from SMBIOS */ + info = smbios_find_struct(0x00); + if (info != NULL) { + smbios.bios_vendor = smbios_getstring(info, 0x04); + } + info = smbios_find_struct(0x01); + if (info != NULL) { + smbios.maker = smbios_getstring(info, 0x04); + smbios.product = smbios_getstring(info, 0x05); + } +} + +void +smbios_detect(const caddr_t addr) +{ + char buf[16]; + caddr_t dmi; + size_t i; + + smbios_probe(addr); + if (smbios.addr == NULL) + return; + + for (dmi = smbios.addr, i = 0; + dmi < smbios.addr + smbios.length && i < smbios.count; i++) + dmi = smbios_parse_table(dmi); + + sprintf(buf, "%d.%d", smbios.major, smbios.minor); + setenv("smbios.version", buf, 1); + if (smbios.enabled_memory > 0 || smbios.old_enabled_memory > 0) { + sprintf(buf, "%u", smbios.enabled_memory > 0 ? + smbios.enabled_memory : smbios.old_enabled_memory); + setenv("smbios.memory.enabled", buf, 1); + } + if (smbios.enabled_sockets > 0) { + sprintf(buf, "%u", smbios.enabled_sockets); + setenv("smbios.socket.enabled", buf, 1); + } + if (smbios.populated_sockets > 0) { + sprintf(buf, "%u", smbios.populated_sockets); + setenv("smbios.socket.populated", buf, 1); + } +} + +static int +smbios_match_str(const char* s1, const char* s2) +{ + return (s1 == NULL || (s2 != NULL && !strcmp(s1, s2))); +} + +int +smbios_match(const char* bios_vendor, const char* maker, + const char* product) +{ + /* XXXRP currently, only called from non-EFI. */ + smbios_probe(NULL); + return (smbios_match_str(bios_vendor, smbios.bios_vendor) && + smbios_match_str(maker, smbios.maker) && + smbios_match_str(product, smbios.product)); +} diff --git a/stand/i386/libi386/smbios.h b/stand/i386/libi386/smbios.h new file mode 100644 index 0000000..03fc07e --- /dev/null +++ b/stand/i386/libi386/smbios.h @@ -0,0 +1,34 @@ +/*- + * Copyright (c) 2015 Rui Paulo <rpaulo@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. + * + * 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 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. + * + * $FreeBSD$ + */ +#ifndef _SMBIOS_H_ +#define _SMBIOS_H_ + +void smbios_detect(const caddr_t); +int smbios_match(const char *, const char *, const char *); + +#endif /* _SMBIOS_H_ */ diff --git a/stand/i386/libi386/spinconsole.c b/stand/i386/libi386/spinconsole.c new file mode 100644 index 0000000..1daac35 --- /dev/null +++ b/stand/i386/libi386/spinconsole.c @@ -0,0 +1,112 @@ +/*- + * spinconsole.c + * + * Author: Maksym Sobolyev <sobomax@sippysoft.com> + * Copyright (c) 2009 Sippy Software, Inc. + * All rights reserved. + * + * Subject to the following obligations and disclaimer of warranty, use and + * redistribution of this software, in source or object code forms, with or + * without modifications are expressly permitted by Whistle Communications; + * provided, however, that: + * 1. Any and all reproductions of the source or object code must include the + * copyright notice above and the following disclaimer of warranties; and + * 2. No rights are granted, in any manner or form, to use Whistle + * Communications, Inc. trademarks, including the mark "WHISTLE + * COMMUNICATIONS" on advertising, endorsements, or otherwise except as + * such appears in the above copyright notice or in the software. + * + * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND + * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO + * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, + * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. + * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY + * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS + * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. + * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES + * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING + * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER 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 WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stand.h> +#include <bootstrap.h> + +static void spinc_probe(struct console *cp); +static int spinc_init(int arg); +static void spinc_putchar(int c); +static int spinc_getchar(void); +static int spinc_ischar(void); + +extern struct console *consoles[]; + +struct console spinconsole = { + "spinconsole", + "spin port", + 0, + spinc_probe, + spinc_init, + spinc_putchar, + spinc_getchar, + spinc_ischar +}; + +static struct console *parent = NULL; + +static void +spinc_probe(struct console *cp) +{ + + if (parent == NULL) + parent = consoles[0]; + parent->c_probe(cp); +} + +static int +spinc_init(int arg) +{ + + return(parent->c_init(arg)); +} + +static void +spinc_putchar(int c) +{ + static unsigned tw_chars = 0x5C2D2F7C; /* "\-/|" */ + static time_t lasttime = 0; + time_t now; + + now = time(0); + if (now < (lasttime + 1)) + return; +#ifdef TERM_EMU + if (lasttime > 0) + parent->c_out('\b'); +#endif + lasttime = now; + parent->c_out((char)tw_chars); + tw_chars = (tw_chars >> 8) | ((tw_chars & (unsigned long)0xFF) << 24); +} + +static int +spinc_getchar(void) +{ + + return(-1); +} + +static int +spinc_ischar(void) +{ + + return(0); +} diff --git a/stand/i386/libi386/time.c b/stand/i386/libi386/time.c new file mode 100644 index 0000000..7636ace --- /dev/null +++ b/stand/i386/libi386/time.c @@ -0,0 +1,118 @@ +/*- + * Copyright (c) 1998 Michael Smith <msmith@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. + * + * 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stand.h> +#include <btxv86.h> +#include "bootstrap.h" +#include "libi386.h" + +time_t getsecs(void); +static int bios_seconds(void); + +/* + * Return the BIOS time-of-day value. + * + * XXX uses undocumented BCD support from libstand. + */ +static int +bios_seconds(void) +{ + int hr, minute, sec; + + v86.ctl = 0; + v86.addr = 0x1a; /* int 0x1a, function 2 */ + v86.eax = 0x0200; + v86int(); + + hr = bcd2bin((v86.ecx & 0xff00) >> 8); /* hour in %ch */ + minute = bcd2bin(v86.ecx & 0xff); /* minute in %cl */ + sec = bcd2bin((v86.edx & 0xff00) >> 8); /* second in %dh */ + + return (hr * 3600 + minute * 60 + sec); +} + +/* + * Return the time in seconds since the beginning of the day. + * + * Some BIOSes (notably qemu) don't correctly read the RTC + * registers in an atomic way, sometimes returning bogus values. + * Therefore we "debounce" the reading by accepting it only when + * we got 8 identical values in succession. + * + * If we pass midnight, don't wrap back to 0. + */ +time_t +time(time_t *t) +{ + static time_t lasttime; + time_t now, check; + int same, try; + + same = try = 0; + check = bios_seconds(); + do { + now = check; + check = bios_seconds(); + if (check != now) + same = 0; + } while (++same < 8 && ++try < 1000); + + if (now < lasttime) + now += 24 * 3600; + lasttime = now; + + if (t != NULL) + *t = now; + return(now); +} + +time_t +getsecs(void) +{ + time_t n = 0; + time(&n); + return n; +} + +/* + * Use the BIOS Wait function to pause for (period) microseconds. + * + * Resolution of this function is variable, but typically around + * 1ms. + */ +void +delay(int period) +{ + v86.ctl = 0; + v86.addr = 0x15; /* int 0x15, function 0x86 */ + v86.eax = 0x8600; + v86.ecx = period >> 16; + v86.edx = period & 0xffff; + v86int(); +} diff --git a/stand/i386/libi386/vidconsole.c b/stand/i386/libi386/vidconsole.c new file mode 100644 index 0000000..073d531 --- /dev/null +++ b/stand/i386/libi386/vidconsole.c @@ -0,0 +1,632 @@ +/*- + * Copyright (c) 1998 Michael Smith (msmith@freebsd.org) + * Copyright (c) 1997 Kazutaka YOKOTA (yokota@zodiac.mech.utsunomiya-u.ac.jp) + * 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 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. + * + * Id: probe_keyboard.c,v 1.13 1997/06/09 05:10:55 bde Exp + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stand.h> +#include <bootstrap.h> +#include <btxv86.h> +#include <machine/psl.h> +#include "libi386.h" + +#if KEYBOARD_PROBE +#include <machine/cpufunc.h> + +static int probe_keyboard(void); +#endif +static void vidc_probe(struct console *cp); +static int vidc_init(int arg); +static void vidc_putchar(int c); +static int vidc_getchar(void); +static int vidc_ischar(void); + +static int vidc_started; + +#ifdef TERM_EMU +#define MAXARGS 8 +#define DEFAULT_FGCOLOR 7 +#define DEFAULT_BGCOLOR 0 + +void end_term(void); +void bail_out(int c); +void vidc_term_emu(int c); +void get_pos(int *x, int *y); +void curs_move(int *_x, int *_y, int x, int y); +void write_char(int c, int fg, int bg); +void scroll_up(int rows, int fg, int bg); +void CD(void); +void CM(void); +void HO(void); + +static int args[MAXARGS], argc; +static int fg_c, bg_c, curx, cury; +static int esc; +#endif + + +struct console vidconsole = { + "vidconsole", + "internal video/keyboard", + 0, + vidc_probe, + vidc_init, + vidc_putchar, + vidc_getchar, + vidc_ischar +}; + +static void +vidc_probe(struct console *cp) +{ + + /* look for a keyboard */ +#if KEYBOARD_PROBE + if (probe_keyboard()) +#endif + { + + cp->c_flags |= C_PRESENTIN; + } + + /* XXX for now, always assume we can do BIOS screen output */ + cp->c_flags |= C_PRESENTOUT; +} + +static int +vidc_init(int arg) +{ + int i; + + if (vidc_started && arg == 0) + return (0); + vidc_started = 1; +#ifdef TERM_EMU + /* Init terminal emulator */ + end_term(); + get_pos(&curx, &cury); + curs_move(&curx, &cury, curx, cury); + fg_c = DEFAULT_FGCOLOR; + bg_c = DEFAULT_BGCOLOR; +#endif + for (i = 0; i < 10 && vidc_ischar(); i++) + (void)vidc_getchar(); + return (0); /* XXX reinit? */ +} + +void +vidc_biosputchar(int c) +{ + + v86.ctl = 0; + v86.addr = 0x10; + v86.eax = 0xe00 | (c & 0xff); + v86.ebx = 0x7; + v86int(); +} + +static void +vidc_rawputchar(int c) +{ + int i; + + if (c == '\t') + /* lame tab expansion */ + for (i = 0; i < 8; i++) + vidc_rawputchar(' '); + else { +#ifndef TERM_EMU + vidc_biosputchar(c); +#else + /* Emulate AH=0eh (teletype output) */ + switch(c) { + case '\a': + vidc_biosputchar(c); + return; + case '\r': + curx = 0; + curs_move(&curx, &cury, curx, cury); + return; + case '\n': + cury++; + if (cury > 24) { + scroll_up(1, fg_c, bg_c); + cury--; + } else { + curs_move(&curx, &cury, curx, cury); + } + return; + case '\b': + if (curx > 0) { + curx--; + curs_move(&curx, &cury, curx, cury); + /* write_char(' ', fg_c, bg_c); XXX destructive(!) */ + return; + } + return; + default: + write_char(c, fg_c, bg_c); + curx++; + if (curx > 79) { + curx = 0; + cury++; + } + if (cury > 24) { + curx = 0; + scroll_up(1, fg_c, bg_c); + cury--; + } + } + curs_move(&curx, &cury, curx, cury); +#endif + } +} + +#ifdef TERM_EMU + +/* Get cursor position on the screen. Result is in edx. Sets + * curx and cury appropriately. + */ +void +get_pos(int *x, int *y) +{ + + v86.ctl = 0; + v86.addr = 0x10; + v86.eax = 0x0300; + v86.ebx = 0x0; + v86int(); + *x = v86.edx & 0x00ff; + *y = (v86.edx & 0xff00) >> 8; +} + +/* Move cursor to x rows and y cols (0-based). */ +void +curs_move(int *_x, int *_y, int x, int y) +{ + + v86.ctl = 0; + v86.addr = 0x10; + v86.eax = 0x0200; + v86.ebx = 0x0; + v86.edx = ((0x00ff & y) << 8) + (0x00ff & x); + v86int(); + *_x = x; + *_y = y; + /* If there is ctrl char at this position, cursor would be invisible. + * Make it a space instead. + */ + v86.ctl = 0; + v86.addr = 0x10; + v86.eax = 0x0800; + v86.ebx = 0x0; + v86int(); +#define isvisible(c) (((c) >= 32) && ((c) < 255)) + if (!isvisible(v86.eax & 0x00ff)) { + write_char(' ', fg_c, bg_c); + } +} + +/* Scroll up the whole window by a number of rows. If rows==0, + * clear the window. fg and bg are attributes for the new lines + * inserted in the window. + */ +void +scroll_up(int rows, int fgcol, int bgcol) +{ + + if (rows == 0) + rows = 25; + v86.ctl = 0; + v86.addr = 0x10; + v86.eax = 0x0600 + (0x00ff & rows); + v86.ebx = (bgcol << 12) + (fgcol << 8); + v86.ecx = 0x0; + v86.edx = 0x184f; + v86int(); +} + +/* Write character and attribute at cursor position. */ +void +write_char(int c, int fgcol, int bgcol) +{ + + v86.ctl = 0; + v86.addr = 0x10; + v86.eax = 0x0900 + (0x00ff & c); + v86.ebx = (bgcol << 4) + fgcol; + v86.ecx = 0x1; + v86int(); +} + +/**************************************************************/ +/* + * Screen manipulation functions. They use accumulated data in + * args[] and argc variables. + * + */ + +/* Clear display from current position to end of screen */ +void +CD(void) +{ + + get_pos(&curx, &cury); + if (curx > 0) { + v86.ctl = 0; + v86.addr = 0x10; + v86.eax = 0x0600; + v86.ebx = (bg_c << 4) + fg_c; + v86.ecx = (cury << 8) + curx; + v86.edx = (cury << 8) + 79; + v86int(); + if (++cury > 24) { + end_term(); + return; + } + } + v86.ctl = 0; + v86.addr = 0x10; + v86.eax = 0x0600; + v86.ebx = (bg_c << 4) + fg_c; + v86.ecx = (cury << 8) + 0; + v86.edx = (24 << 8) + 79; + v86int(); + end_term(); +} + +/* Absolute cursor move to args[0] rows and args[1] columns + * (the coordinates are 1-based). + */ +void +CM(void) +{ + + if (args[0] > 0) + args[0]--; + if (args[1] > 0) + args[1]--; + curs_move(&curx, &cury, args[1], args[0]); + end_term(); +} + +/* Home cursor (left top corner) */ +void +HO(void) +{ + + argc = 1; + args[0] = args[1] = 1; + CM(); +} + +/* Clear internal state of the terminal emulation code */ +void +end_term(void) +{ + + esc = 0; + argc = -1; +} + +/* Gracefully exit ESC-sequence processing in case of misunderstanding */ +void +bail_out(int c) +{ + char buf[16], *ch; + int i; + + if (esc) { + vidc_rawputchar('\033'); + if (esc != '\033') + vidc_rawputchar(esc); + for (i = 0; i <= argc; ++i) { + sprintf(buf, "%d", args[i]); + ch = buf; + while (*ch) + vidc_rawputchar(*ch++); + } + } + vidc_rawputchar(c); + end_term(); +} + +static void +get_arg(int c) +{ + + if (argc < 0) + argc = 0; + args[argc] *= 10; + args[argc] += c - '0'; +} + +/* Emulate basic capabilities of cons25 terminal */ +void +vidc_term_emu(int c) +{ + static int ansi_col[] = { + 0, 4, 2, 6, 1, 5, 3, 7, + }; + int t; + int i; + + switch (esc) { + case 0: + switch (c) { + case '\033': + esc = c; + break; + default: + vidc_rawputchar(c); + break; + } + break; + + case '\033': + switch (c) { + case '[': + esc = c; + args[0] = 0; + argc = -1; + break; + default: + bail_out(c); + break; + } + break; + + case '[': + switch (c) { + case ';': + if (argc < 0) /* XXX */ + argc = 0; + else if (argc + 1 >= MAXARGS) + bail_out(c); + else + args[++argc] = 0; + break; + case 'H': + if (argc < 0) + HO(); + else if (argc == 1) + CM(); + else + bail_out(c); + break; + case 'J': + if (argc < 0) + CD(); + else + bail_out(c); + break; + case 'm': + if (argc < 0) { + fg_c = DEFAULT_FGCOLOR; + bg_c = DEFAULT_BGCOLOR; + } + for (i = 0; i <= argc; ++i) { + switch (args[i]) { + case 0: /* back to normal */ + fg_c = DEFAULT_FGCOLOR; + bg_c = DEFAULT_BGCOLOR; + break; + case 1: /* bold */ + fg_c |= 0x8; + break; + case 4: /* underline */ + case 5: /* blink */ + bg_c |= 0x8; + break; + case 7: /* reverse */ + t = fg_c; + fg_c = bg_c; + bg_c = t; + break; + case 30: case 31: case 32: case 33: + case 34: case 35: case 36: case 37: + fg_c = ansi_col[args[i] - 30]; + break; + case 39: /* normal */ + fg_c = DEFAULT_FGCOLOR; + break; + case 40: case 41: case 42: case 43: + case 44: case 45: case 46: case 47: + bg_c = ansi_col[args[i] - 40]; + break; + case 49: /* normal */ + bg_c = DEFAULT_BGCOLOR; + break; + } + } + end_term(); + break; + default: + if (isdigit(c)) + get_arg(c); + else + bail_out(c); + break; + } + break; + + default: + bail_out(c); + break; + } +} +#endif + +static void +vidc_putchar(int c) +{ +#ifdef TERM_EMU + vidc_term_emu(c); +#else + vidc_rawputchar(c); +#endif +} + +static int +vidc_getchar(void) +{ + + if (vidc_ischar()) { + v86.ctl = 0; + v86.addr = 0x16; + v86.eax = 0x0; + v86int(); + return (v86.eax & 0xff); + } else { + return (-1); + } +} + +static int +vidc_ischar(void) +{ + + v86.ctl = V86_FLAGS; + v86.addr = 0x16; + v86.eax = 0x100; + v86int(); + return (!V86_ZR(v86.efl)); +} + +#if KEYBOARD_PROBE + +#define PROBE_MAXRETRY 5 +#define PROBE_MAXWAIT 400 +#define IO_DUMMY 0x84 +#define IO_KBD 0x060 /* 8042 Keyboard */ + +/* selected defines from kbdio.h */ +#define KBD_STATUS_PORT 4 /* status port, read */ +#define KBD_DATA_PORT 0 /* data port, read/write + * also used as keyboard command + * and mouse command port + */ +#define KBDC_ECHO 0x00ee +#define KBDS_ANY_BUFFER_FULL 0x0001 +#define KBDS_INPUT_BUFFER_FULL 0x0002 +#define KBD_ECHO 0x00ee + +/* 7 microsec delay necessary for some keyboard controllers */ +static void +delay7(void) +{ + /* + * I know this is broken, but no timer is available yet at this stage... + * See also comments in `delay1ms()'. + */ + inb(IO_DUMMY); inb(IO_DUMMY); + inb(IO_DUMMY); inb(IO_DUMMY); + inb(IO_DUMMY); inb(IO_DUMMY); +} + +/* + * This routine uses an inb to an unused port, the time to execute that + * inb is approximately 1.25uS. This value is pretty constant across + * all CPU's and all buses, with the exception of some PCI implentations + * that do not forward this I/O address to the ISA bus as they know it + * is not a valid ISA bus address, those machines execute this inb in + * 60 nS :-(. + * + */ +static void +delay1ms(void) +{ + int i = 800; + while (--i >= 0) + (void)inb(0x84); +} + +/* + * We use the presence/absence of a keyboard to determine whether the internal + * console can be used for input. + * + * Perform a simple test on the keyboard; issue the ECHO command and see + * if the right answer is returned. We don't do anything as drastic as + * full keyboard reset; it will be too troublesome and take too much time. + */ +static int +probe_keyboard(void) +{ + int retry = PROBE_MAXRETRY; + int wait; + int i; + + while (--retry >= 0) { + /* flush any noise */ + while (inb(IO_KBD + KBD_STATUS_PORT) & KBDS_ANY_BUFFER_FULL) { + delay7(); + inb(IO_KBD + KBD_DATA_PORT); + delay1ms(); + } + + /* wait until the controller can accept a command */ + for (wait = PROBE_MAXWAIT; wait > 0; --wait) { + if (((i = inb(IO_KBD + KBD_STATUS_PORT)) + & (KBDS_INPUT_BUFFER_FULL | KBDS_ANY_BUFFER_FULL)) == 0) + break; + if (i & KBDS_ANY_BUFFER_FULL) { + delay7(); + inb(IO_KBD + KBD_DATA_PORT); + } + delay1ms(); + } + if (wait <= 0) + continue; + + /* send the ECHO command */ + outb(IO_KBD + KBD_DATA_PORT, KBDC_ECHO); + + /* wait for a response */ + for (wait = PROBE_MAXWAIT; wait > 0; --wait) { + if (inb(IO_KBD + KBD_STATUS_PORT) & KBDS_ANY_BUFFER_FULL) + break; + delay1ms(); + } + if (wait <= 0) + continue; + + delay7(); + i = inb(IO_KBD + KBD_DATA_PORT); +#ifdef PROBE_KBD_BEBUG + printf("probe_keyboard: got 0x%x.\n", i); +#endif + if (i == KBD_ECHO) { + /* got the right answer */ + return (1); + } + } + + return (0); +} +#endif /* KEYBOARD_PROBE */ diff --git a/stand/i386/loader/Makefile b/stand/i386/loader/Makefile new file mode 100644 index 0000000..40ac46c --- /dev/null +++ b/stand/i386/loader/Makefile @@ -0,0 +1,89 @@ +# $FreeBSD$ + +HAVE_GELI= yes + +LOADER_NET_SUPPORT?= yes +LOADER_NFS_SUPPORT?= yes +LOADER_TFTP_SUPPORT?= yes +LOADER_CD9660_SUPPORT?= no +LOADER_EXT2FS_SUPPORT?= no +LOADER_MSDOS_SUPPORT?= no +LOADER_UFS_SUPPORT?= yes +LOADER_GZIP_SUPPORT?= yes +LOADER_BZIP2_SUPPORT?= yes + +.include <bsd.init.mk> + +MK_SSP= no + +LOADER?= loader +PROG= ${LOADER}.sym +MAN= +INTERNALPROG= +NEWVERSWHAT?= "bootstrap loader" x86 +VERSION_FILE= ${.CURDIR}/../loader/version + +.PATH: ${BOOTSRC}/i386/loader + +# architecture-specific loader code +SRCS= main.c conf.c vers.c chain.c + +# Include bcache code. +HAVE_BCACHE= yes + +# Enable PnP and ISA-PnP code. +HAVE_PNP= yes +HAVE_ISABUS= yes + +.if ${MK_LOADER_FIREWIRE} == "yes" +CFLAGS+= -DLOADER_FIREWIRE_SUPPORT +LIBFIREWIRE= ${BOOTOBJ}/i386/libfirewire/libfirewire.a +.endif + +.if exists(${.CURDIR}/help.i386) +HELP_FILES+= help.i386 +.else +HELP_FILES= +.endif + +# Always add MI sources +.include "${BOOTSRC}/loader.mk" + +CLEANFILES+= ${LOADER} ${LOADER}.bin + +CFLAGS+= -Wall +LDFLAGS+= -static -Ttext 0x0 + +# i386 standalone support library +LIBI386= ${BOOTOBJ}/i386/libi386/libi386.a +CFLAGS+= -I${BOOTSRC}/i386 + +# BTX components +CFLAGS+= -I${BTXLIB} + +# Debug me! +#CFLAGS+= -g +#LDFLAGS+= -g + +${LOADER}: ${LOADER}.bin ${BTXLDR} ${BTXKERN} + btxld -v -f aout -e ${LOADER_ADDRESS} -o ${.TARGET} -l ${BTXLDR} \ + -b ${BTXKERN} ${LOADER}.bin + +${LOADER}.bin: ${LOADER}.sym + strip -R .comment -R .note -o ${.TARGET} ${.ALLSRC} + +FILES+= ${LOADER} +# XXX INSTALLFLAGS_loader= -b +FILESMODE_${LOADER}= ${BINMODE} -b + +# XXX crt0.o needs to be first for pxeboot(8) to work +OBJS= ${BTXCRT} + +DPADD= ${LIBFICL32} ${LIBFIREWIRE} ${LIBZFSBOOT} ${LIBI386} ${LIBGELIBOOT} ${LIBSA32} +LDADD= ${LIBFICL32} ${LIBFIREWIRE} ${LIBZFSBOOT} ${LIBI386} ${LIBGELIBOOT} ${LIBSA32} + +.if ${MACHINE_CPUARCH} == "amd64" +CFLAGS+= -DLOADER_PREFER_AMD64 +.endif + +.include <bsd.prog.mk> diff --git a/stand/i386/loader/Makefile.depend b/stand/i386/loader/Makefile.depend new file mode 100644 index 0000000..89d5422 --- /dev/null +++ b/stand/i386/loader/Makefile.depend @@ -0,0 +1,21 @@ +# $FreeBSD$ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + include \ + include/xlocale \ + lib/libstand \ + sys/boot/ficl32 \ + sys/boot/geli \ + sys/boot/i386/btx/btx \ + sys/boot/i386/btx/btxldr \ + sys/boot/i386/btx/lib \ + sys/boot/i386/libi386 \ + sys/boot/libstand32 \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/stand/i386/loader/chain.c b/stand/i386/loader/chain.c new file mode 100644 index 0000000..d6810ef --- /dev/null +++ b/stand/i386/loader/chain.c @@ -0,0 +1,134 @@ +/*- + * Copyright 2015 Toomas Soome <tsoome@me.com> + * 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 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. + */ + +/* + * Chain loader to load BIOS boot block either from MBR or PBR. + * + * Note the boot block location 0000:7c000 conflicts with loader, so we need to + * read in to temporary space and relocate on exec, when btx is stopped. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stand.h> +#include <sys/param.h> +#include <sys/linker.h> +#include <sys/diskmbr.h> + +#include "bootstrap.h" +#include "libi386/libi386.h" +#include "btxv86.h" + +/* + * The MBR/VBR is located in first sector of disk/partition. + * Read 512B to temporary location and set up relocation. Then + * exec relocator. + */ +#define SECTOR_SIZE (512) + +COMMAND_SET(chain, "chain", "chain load boot block from device", command_chain); + +static int +command_chain(int argc, char *argv[]) +{ + int fd, len, size = SECTOR_SIZE; + struct stat st; + vm_offset_t mem = 0x100000; + struct i386_devdesc *rootdev; + + if (argc == 1) { + command_errmsg = "no device or file name specified"; + return (CMD_ERROR); + } + if (argc != 2) { + command_errmsg = "invalid trailing arguments"; + return (CMD_ERROR); + } + + fd = open(argv[1], O_RDONLY); + if (fd == -1) { + command_errmsg = "open failed"; + return (CMD_ERROR); + } + + len = strlen(argv[1]); + if (argv[1][len-1] != ':') { + if (fstat(fd, &st) == -1) { + command_errmsg = "stat failed"; + close(fd); + return (CMD_ERROR); + } + size = st.st_size; + } else if (strncmp(argv[1], "disk", 4) != 0) { + command_errmsg = "can only use disk device"; + close(fd); + return (CMD_ERROR); + } + + i386_getdev((void **)(&rootdev), argv[1], NULL); + if (rootdev == NULL) { + command_errmsg = "can't determine root device"; + return (CMD_ERROR); + } + + if (archsw.arch_readin(fd, mem, SECTOR_SIZE) != SECTOR_SIZE) { + command_errmsg = "failed to read disk"; + close(fd); + return (CMD_ERROR); + } + close(fd); + + if (*((uint16_t *)PTOV(mem + DOSMAGICOFFSET)) != DOSMAGIC) { + command_errmsg = "wrong magic"; + return (CMD_ERROR); + } + + relocater_data[0].src = mem; + relocater_data[0].dest = 0x7C00; + relocater_data[0].size = SECTOR_SIZE; + + relocator_edx = bd_unit2bios(rootdev->d_unit); + relocator_esi = relocater_size; + relocator_ds = 0; + relocator_es = 0; + relocator_fs = 0; + relocator_gs = 0; + relocator_ss = 0; + relocator_cs = 0; + relocator_sp = 0x7C00; + relocator_ip = 0x7C00; + relocator_a20_enabled = 0; + + i386_copyin(relocater, 0x600, relocater_size); + + dev_cleanup(); + + __exec((void *)0x600); + + panic("exec returned"); + return (CMD_ERROR); /* not reached */ +} diff --git a/stand/i386/loader/conf.c b/stand/i386/loader/conf.c new file mode 100644 index 0000000..29ce1e3 --- /dev/null +++ b/stand/i386/loader/conf.c @@ -0,0 +1,159 @@ +/*- + * Copyright (c) 1998 Michael Smith <msmith@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. + * + * 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stand.h> +#include <bootstrap.h> +#include "libi386/libi386.h" +#if defined(LOADER_ZFS_SUPPORT) +#include "../zfs/libzfs.h" +#endif + +/* + * We could use linker sets for some or all of these, but + * then we would have to control what ended up linked into + * the bootstrap. So it's easier to conditionalise things + * here. + * + * XXX rename these arrays to be consistent and less namespace-hostile + * + * XXX as libi386 and biosboot merge, some of these can become linker sets. + */ + +#if defined(LOADER_FIREWIRE_SUPPORT) +extern struct devsw fwohci; +#endif + +/* Exported for libstand */ +struct devsw *devsw[] = { + &bioscd, + &biosdisk, +#if defined(LOADER_NFS_SUPPORT) || defined(LOADER_TFTP_SUPPORT) + &pxedisk, +#endif +#if defined(LOADER_FIREWIRE_SUPPORT) + &fwohci, +#endif +#if defined(LOADER_ZFS_SUPPORT) + &zfs_dev, +#endif + NULL +}; + +struct fs_ops *file_system[] = { +#if defined(LOADER_ZFS_SUPPORT) + &zfs_fsops, +#endif + &ufs_fsops, + &ext2fs_fsops, + &dosfs_fsops, + &cd9660_fsops, +#if defined(LOADER_NANDFS_SUPPORT) + &nandfs_fsops, +#endif +#ifdef LOADER_NFS_SUPPORT + &nfs_fsops, +#endif +#ifdef LOADER_TFTP_SUPPORT + &tftp_fsops, +#endif +#ifdef LOADER_GZIP_SUPPORT + &gzipfs_fsops, +#endif +#ifdef LOADER_BZIP2_SUPPORT + &bzipfs_fsops, +#endif +#ifdef LOADER_SPLIT_SUPPORT + &splitfs_fsops, +#endif + NULL +}; + +/* Exported for i386 only */ +/* + * Sort formats so that those that can detect based on arguments + * rather than reading the file go first. + */ +extern struct file_format i386_elf; +extern struct file_format i386_elf_obj; +extern struct file_format amd64_elf; +extern struct file_format amd64_elf_obj; +extern struct file_format multiboot; +extern struct file_format multiboot_obj; + +struct file_format *file_formats[] = { + &multiboot, + &multiboot_obj, +#ifdef LOADER_PREFER_AMD64 + &amd64_elf, + &amd64_elf_obj, +#endif + &i386_elf, + &i386_elf_obj, +#ifndef LOADER_PREFER_AMD64 + &amd64_elf, + &amd64_elf_obj, +#endif + NULL +}; + +/* + * Consoles + * + * We don't prototype these in libi386.h because they require + * data structures from bootstrap.h as well. + */ +extern struct console vidconsole; +extern struct console comconsole; +#if defined(LOADER_FIREWIRE_SUPPORT) +extern struct console dconsole; +#endif +extern struct console nullconsole; +extern struct console spinconsole; + +struct console *consoles[] = { + &vidconsole, + &comconsole, +#if defined(LOADER_FIREWIRE_SUPPORT) + &dconsole, +#endif + &nullconsole, + &spinconsole, + NULL +}; + +extern struct pnphandler isapnphandler; +extern struct pnphandler biospnphandler; +extern struct pnphandler biospcihandler; + +struct pnphandler *pnphandlers[] = { + &biospnphandler, /* should go first, as it may set isapnp_readport */ + &isapnphandler, + &biospcihandler, + NULL +}; diff --git a/stand/i386/loader/help.i386 b/stand/i386/loader/help.i386 new file mode 100644 index 0000000..0ff6286 --- /dev/null +++ b/stand/i386/loader/help.i386 @@ -0,0 +1,54 @@ +################################################################################ +# Treboot DReboot the system + + reboot + + Causes the system to immediately reboot. + +################################################################################ +# Theap DDisplay memory management statistics + + heap + + Requests debugging output from the heap manager. For debugging use + only. + +################################################################################ +# Tset Snum_ide_disks DSet the number of IDE disks + + NOTE: this variable is deprecated, use root_disk_unit instead. + + set num_ide_disks=<value> + + When booting from a SCSI disk on a system with one or more IDE disks, + and where the IDE disks are the default boot device, it is necessary + to tell the kernel how many IDE disks there are in order to have it + correctly locate the SCSI disk you are booting from. + +################################################################################ +# Tset Sroot_disk_unit DForce the root disk unit number. + + set root_disk_unit=<value> + + If the code which detects the disk unit number for the root disk is + confused, eg. by a mix of SCSI and IDE disks, or IDE disks with + gaps in the sequence (eg. no primary slave), the unit number can be + forced by setting this variable. + +################################################################################ +# Tsmap DDisplay BIOS SMAP table + + smap + + Displays the BIOS SMAP (system memory map) table. + +################################################################################ +# Tchain DChain load disk block + + chain disk: + + chain will read stage1 (MBR or VBR) boot block from specified device + to address 0000:7C00 and attempts to run it. Use lsdev to get available + device names. Disk name must end with colon. + +################################################################################ diff --git a/stand/i386/loader/loader.rc b/stand/i386/loader/loader.rc new file mode 100644 index 0000000..287c05e --- /dev/null +++ b/stand/i386/loader/loader.rc @@ -0,0 +1,18 @@ +\ Loader.rc +\ $FreeBSD$ +\ +\ Includes additional commands +include /boot/loader.4th +try-include /boot/loader.rc.local + +\ Reads and processes loader.conf variables +initialize + +\ Tests for password -- executes autoboot first if a password was defined +check-password + +\ Load in the boot menu +include /boot/beastie.4th + +\ Start the boot menu +beastie-start diff --git a/stand/i386/loader/main.c b/stand/i386/loader/main.c new file mode 100644 index 0000000..81bc2ff --- /dev/null +++ b/stand/i386/loader/main.c @@ -0,0 +1,477 @@ +/*- + * Copyright (c) 1998 Michael Smith <msmith@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. + * + * 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * MD bootstrap main() and assorted miscellaneous + * commands. + */ + +#include <stand.h> +#include <stddef.h> +#include <string.h> +#include <machine/bootinfo.h> +#include <machine/cpufunc.h> +#include <machine/psl.h> +#include <sys/disk.h> +#include <sys/reboot.h> +#include <common/drv.h> + +#include "bootstrap.h" +#include "common/bootargs.h" +#include "libi386/libi386.h" +#include "libi386/smbios.h" +#include "btxv86.h" + +#ifdef LOADER_ZFS_SUPPORT +#include "../zfs/libzfs.h" +#endif + +CTASSERT(sizeof(struct bootargs) == BOOTARGS_SIZE); +CTASSERT(offsetof(struct bootargs, bootinfo) == BA_BOOTINFO); +CTASSERT(offsetof(struct bootargs, bootflags) == BA_BOOTFLAGS); +CTASSERT(offsetof(struct bootinfo, bi_size) == BI_SIZE); + +/* Arguments passed in from the boot1/boot2 loader */ +static struct bootargs *kargs; + +static u_int32_t initial_howto; +static u_int32_t initial_bootdev; +static struct bootinfo *initial_bootinfo; + +struct arch_switch archsw; /* MI/MD interface boundary */ + +static void extract_currdev(void); +static int isa_inb(int port); +static void isa_outb(int port, int value); +void exit(int code); +#ifdef LOADER_GELI_SUPPORT +#include "geliboot.h" +struct geli_boot_args *gargs; +#endif +#ifdef LOADER_ZFS_SUPPORT +struct zfs_boot_args *zargs; +static void i386_zfs_probe(void); +#endif + +/* from vers.c */ +extern char bootprog_info[]; + +/* XXX debugging */ +extern char end[]; + +static void *heap_top; +static void *heap_bottom; + +int +main(void) +{ + int i; + + /* Pick up arguments */ + kargs = (void *)__args; + initial_howto = kargs->howto; + initial_bootdev = kargs->bootdev; + initial_bootinfo = kargs->bootinfo ? (struct bootinfo *)PTOV(kargs->bootinfo) : NULL; + + /* Initialize the v86 register set to a known-good state. */ + bzero(&v86, sizeof(v86)); + v86.efl = PSL_RESERVED_DEFAULT | PSL_I; + + /* + * Initialise the heap as early as possible. Once this is done, malloc() is usable. + */ + bios_getmem(); + +#if defined(LOADER_BZIP2_SUPPORT) || defined(LOADER_FIREWIRE_SUPPORT) || \ + defined(LOADER_GPT_SUPPORT) || defined(LOADER_ZFS_SUPPORT) + if (high_heap_size > 0) { + heap_top = PTOV(high_heap_base + high_heap_size); + heap_bottom = PTOV(high_heap_base); + if (high_heap_base < memtop_copyin) + memtop_copyin = high_heap_base; + } else +#endif + { + heap_top = (void *)PTOV(bios_basemem); + heap_bottom = (void *)end; + } + setheap(heap_bottom, heap_top); + + /* + * XXX Chicken-and-egg problem; we want to have console output early, but some + * console attributes may depend on reading from eg. the boot device, which we + * can't do yet. + * + * We can use printf() etc. once this is done. + * If the previous boot stage has requested a serial console, prefer that. + */ + bi_setboothowto(initial_howto); + if (initial_howto & RB_MULTIPLE) { + if (initial_howto & RB_SERIAL) + setenv("console", "comconsole vidconsole", 1); + else + setenv("console", "vidconsole comconsole", 1); + } else if (initial_howto & RB_SERIAL) + setenv("console", "comconsole", 1); + else if (initial_howto & RB_MUTE) + setenv("console", "nullconsole", 1); + cons_probe(); + + /* + * Initialise the block cache. Set the upper limit. + */ + bcache_init(32768, 512); + + /* + * Special handling for PXE and CD booting. + */ + if (kargs->bootinfo == 0) { + /* + * We only want the PXE disk to try to init itself in the below + * walk through devsw if we actually booted off of PXE. + */ + if (kargs->bootflags & KARGS_FLAGS_PXE) + pxe_enable(kargs->pxeinfo ? PTOV(kargs->pxeinfo) : NULL); + else if (kargs->bootflags & KARGS_FLAGS_CD) + bc_add(initial_bootdev); + } + + archsw.arch_autoload = i386_autoload; + archsw.arch_getdev = i386_getdev; + archsw.arch_copyin = i386_copyin; + archsw.arch_copyout = i386_copyout; + archsw.arch_readin = i386_readin; + archsw.arch_isainb = isa_inb; + archsw.arch_isaoutb = isa_outb; +#ifdef LOADER_ZFS_SUPPORT + archsw.arch_zfs_probe = i386_zfs_probe; + +#ifdef LOADER_GELI_SUPPORT + if ((kargs->bootflags & KARGS_FLAGS_EXTARG) != 0) { + zargs = (struct zfs_boot_args *)(kargs + 1); + if (zargs != NULL && zargs->size >= offsetof(struct zfs_boot_args, gelipw)) { + if (zargs->size >= offsetof(struct zfs_boot_args, keybuf_sentinel) && + zargs->keybuf_sentinel == KEYBUF_SENTINEL) { + geli_save_keybuf(zargs->keybuf); + } + if (zargs->gelipw[0] != '\0') { + setenv("kern.geom.eli.passphrase", zargs->gelipw, 1); + explicit_bzero(zargs->gelipw, sizeof(zargs->gelipw)); + } + } + } +#endif /* LOADER_GELI_SUPPORT */ +#else /* !LOADER_ZFS_SUPPORT */ +#ifdef LOADER_GELI_SUPPORT + if ((kargs->bootflags & KARGS_FLAGS_EXTARG) != 0) { + gargs = (struct geli_boot_args *)(kargs + 1); + if (gargs != NULL && gargs->size >= offsetof(struct geli_boot_args, gelipw)) { + if (gargs->keybuf_sentinel == KEYBUF_SENTINEL) { + geli_save_keybuf(gargs->keybuf); + } + if (gargs->gelipw[0] != '\0') { + setenv("kern.geom.eli.passphrase", gargs->gelipw, 1); + explicit_bzero(gargs->gelipw, sizeof(gargs->gelipw)); + } + } + } +#endif /* LOADER_GELI_SUPPORT */ +#endif /* LOADER_ZFS_SUPPORT */ + + /* + * March through the device switch probing for things. + */ + for (i = 0; devsw[i] != NULL; i++) + if (devsw[i]->dv_init != NULL) + (devsw[i]->dv_init)(); + printf("BIOS %dkB/%dkB available memory\n", bios_basemem / 1024, bios_extmem / 1024); + if (initial_bootinfo != NULL) { + initial_bootinfo->bi_basemem = bios_basemem / 1024; + initial_bootinfo->bi_extmem = bios_extmem / 1024; + } + + /* detect ACPI for future reference */ + biosacpi_detect(); + + /* detect SMBIOS for future reference */ + smbios_detect(NULL); + + /* detect PCI BIOS for future reference */ + biospci_detect(); + + printf("\n%s", bootprog_info); + + extract_currdev(); /* set $currdev and $loaddev */ + setenv("LINES", "24", 1); /* optional */ + + bios_getsmap(); + + interact(NULL); + + /* if we ever get here, it is an error */ + return (1); +} + +/* + * Set the 'current device' by (if possible) recovering the boot device as + * supplied by the initial bootstrap. + * + * XXX should be extended for netbooting. + */ +static void +extract_currdev(void) +{ + struct i386_devdesc new_currdev; +#ifdef LOADER_ZFS_SUPPORT + char buf[20]; +#endif + int biosdev = -1; + + /* Assume we are booting from a BIOS disk by default */ + new_currdev.d_dev = &biosdisk; + + /* new-style boot loaders such as pxeldr and cdldr */ + if (kargs->bootinfo == 0) { + if ((kargs->bootflags & KARGS_FLAGS_CD) != 0) { + /* we are booting from a CD with cdboot */ + new_currdev.d_dev = &bioscd; + new_currdev.d_unit = bc_bios2unit(initial_bootdev); + } else if ((kargs->bootflags & KARGS_FLAGS_PXE) != 0) { + /* we are booting from pxeldr */ + new_currdev.d_dev = &pxedisk; + new_currdev.d_unit = 0; + } else { + /* we don't know what our boot device is */ + new_currdev.d_kind.biosdisk.slice = -1; + new_currdev.d_kind.biosdisk.partition = 0; + biosdev = -1; + } +#ifdef LOADER_ZFS_SUPPORT + } else if ((kargs->bootflags & KARGS_FLAGS_ZFS) != 0) { + zargs = NULL; + /* check for new style extended argument */ + if ((kargs->bootflags & KARGS_FLAGS_EXTARG) != 0) + zargs = (struct zfs_boot_args *)(kargs + 1); + + if (zargs != NULL && + zargs->size >= offsetof(struct zfs_boot_args, primary_pool)) { + /* sufficient data is provided */ + new_currdev.d_kind.zfs.pool_guid = zargs->pool; + new_currdev.d_kind.zfs.root_guid = zargs->root; + if (zargs->size >= sizeof(*zargs) && zargs->primary_vdev != 0) { + sprintf(buf, "%llu", zargs->primary_pool); + setenv("vfs.zfs.boot.primary_pool", buf, 1); + sprintf(buf, "%llu", zargs->primary_vdev); + setenv("vfs.zfs.boot.primary_vdev", buf, 1); + } + } else { + /* old style zfsboot block */ + new_currdev.d_kind.zfs.pool_guid = kargs->zfspool; + new_currdev.d_kind.zfs.root_guid = 0; + } + new_currdev.d_dev = &zfs_dev; +#endif + } else if ((initial_bootdev & B_MAGICMASK) != B_DEVMAGIC) { + /* The passed-in boot device is bad */ + new_currdev.d_kind.biosdisk.slice = -1; + new_currdev.d_kind.biosdisk.partition = 0; + biosdev = -1; + } else { + new_currdev.d_kind.biosdisk.slice = B_SLICE(initial_bootdev) - 1; + new_currdev.d_kind.biosdisk.partition = B_PARTITION(initial_bootdev); + biosdev = initial_bootinfo->bi_bios_dev; + + /* + * If we are booted by an old bootstrap, we have to guess at the BIOS + * unit number. We will lose if there is more than one disk type + * and we are not booting from the lowest-numbered disk type + * (ie. SCSI when IDE also exists). + */ + if ((biosdev == 0) && (B_TYPE(initial_bootdev) != 2)) /* biosdev doesn't match major */ + biosdev = 0x80 + B_UNIT(initial_bootdev); /* assume harddisk */ + } + new_currdev.d_type = new_currdev.d_dev->dv_type; + + /* + * If we are booting off of a BIOS disk and we didn't succeed in determining + * which one we booted off of, just use disk0: as a reasonable default. + */ + if ((new_currdev.d_type == biosdisk.dv_type) && + ((new_currdev.d_unit = bd_bios2unit(biosdev)) == -1)) { + printf("Can't work out which disk we are booting from.\n" + "Guessed BIOS device 0x%x not found by probes, defaulting to disk0:\n", biosdev); + new_currdev.d_unit = 0; + } + +#ifdef LOADER_ZFS_SUPPORT + if (new_currdev.d_type == DEVT_ZFS) + init_zfs_bootenv(zfs_fmtdev(&new_currdev)); +#endif + + env_setenv("currdev", EV_VOLATILE, i386_fmtdev(&new_currdev), + i386_setcurrdev, env_nounset); + env_setenv("loaddev", EV_VOLATILE, i386_fmtdev(&new_currdev), env_noset, + env_nounset); +} + +COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot); + +static int +command_reboot(int argc, char *argv[]) +{ + int i; + + for (i = 0; devsw[i] != NULL; ++i) + if (devsw[i]->dv_cleanup != NULL) + (devsw[i]->dv_cleanup)(); + + printf("Rebooting...\n"); + delay(1000000); + __exit(0); +} + +/* provide this for panic, as it's not in the startup code */ +void +exit(int code) +{ + __exit(code); +} + +COMMAND_SET(heap, "heap", "show heap usage", command_heap); + +static int +command_heap(int argc, char *argv[]) +{ + mallocstats(); + printf("heap base at %p, top at %p, upper limit at %p\n", heap_bottom, + sbrk(0), heap_top); + return(CMD_OK); +} + +#ifdef LOADER_ZFS_SUPPORT +COMMAND_SET(lszfs, "lszfs", "list child datasets of a zfs dataset", + command_lszfs); + +static int +command_lszfs(int argc, char *argv[]) +{ + int err; + + if (argc != 2) { + command_errmsg = "wrong number of arguments"; + return (CMD_ERROR); + } + + err = zfs_list(argv[1]); + if (err != 0) { + command_errmsg = strerror(err); + return (CMD_ERROR); + } + + return (CMD_OK); +} + +COMMAND_SET(reloadbe, "reloadbe", "refresh the list of ZFS Boot Environments", + command_reloadbe); + +static int +command_reloadbe(int argc, char *argv[]) +{ + int err; + char *root; + + if (argc > 2) { + command_errmsg = "wrong number of arguments"; + return (CMD_ERROR); + } + + if (argc == 2) { + err = zfs_bootenv(argv[1]); + } else { + root = getenv("zfs_be_root"); + if (root == NULL) { + /* There does not appear to be a ZFS pool here, exit without error */ + return (CMD_OK); + } + err = zfs_bootenv(root); + } + + if (err != 0) { + command_errmsg = strerror(err); + return (CMD_ERROR); + } + + return (CMD_OK); +} +#endif + +/* ISA bus access functions for PnP. */ +static int +isa_inb(int port) +{ + + return (inb(port)); +} + +static void +isa_outb(int port, int value) +{ + + outb(port, value); +} + +#ifdef LOADER_ZFS_SUPPORT +static void +i386_zfs_probe(void) +{ + char devname[32]; + int unit; + + /* + * Open all the disks we can find and see if we can reconstruct + * ZFS pools from them. + */ + for (unit = 0; unit < MAXBDDEV; unit++) { + if (bd_unit2bios(unit) == -1) + break; + sprintf(devname, "disk%d:", unit); + zfs_probe_dev(devname, NULL); + } +} + +uint64_t +ldi_get_size(void *priv) +{ + int fd = (uintptr_t) priv; + uint64_t size; + + ioctl(fd, DIOCGMEDIASIZE, &size); + return (size); +} +#endif diff --git a/stand/i386/loader/version b/stand/i386/loader/version new file mode 100644 index 0000000..7a2acaf --- /dev/null +++ b/stand/i386/loader/version @@ -0,0 +1,14 @@ +$FreeBSD$ + +NOTE ANY CHANGES YOU MAKE TO THE BOOTBLOCKS HERE. The format of this +file is important. Make sure the current version number is on line 6. + +1.1: New calling conventions for fopen. +1.0: New semantics for finding the kernel, new boot. +0.8: Set/getenv & cia, copyin/out. +0.7: Supports large KVM +0.6: Increased dictionary size -- supports loader.4th +0.5: First release version +0.2: Initial integration with BTX +0.1: Initial i386 version, inspiration and some structure from the + NetBSD version. diff --git a/stand/i386/mbr/Makefile b/stand/i386/mbr/Makefile new file mode 100644 index 0000000..9bfa0bd --- /dev/null +++ b/stand/i386/mbr/Makefile @@ -0,0 +1,17 @@ +# $FreeBSD$ + +PROG= mbr +STRIP= +BINMODE=${NOBINMODE} +MAN= +SRCS= ${PROG}.s + +# MBR flags: 0x80 -- try packet interface (also known as EDD or LBA) +BOOT_MBR_FLAGS?= 0x80 + +ORG= 0x600 + +AFLAGS+=--defsym FLAGS=${BOOT_MBR_FLAGS} +LDFLAGS+=${LDFLAGS_BIN} + +.include <bsd.prog.mk> diff --git a/stand/i386/mbr/Makefile.depend b/stand/i386/mbr/Makefile.depend new file mode 100644 index 0000000..f80275d --- /dev/null +++ b/stand/i386/mbr/Makefile.depend @@ -0,0 +1,11 @@ +# $FreeBSD$ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/stand/i386/mbr/mbr.s b/stand/i386/mbr/mbr.s new file mode 100644 index 0000000..3cfc20d --- /dev/null +++ b/stand/i386/mbr/mbr.s @@ -0,0 +1,157 @@ +# +# Copyright (c) 1999 Robert Nordier +# All rights reserved. +# +# Redistribution and use in source and binary forms are freely +# permitted provided that the above copyright notice and this +# paragraph and the following disclaimer are duplicated in all +# such forms. +# +# This software is provided "AS IS" and without any express or +# implied warranties, including, without limitation, the implied +# warranties of merchantability and fitness for a particular +# purpose. +# + +# $FreeBSD$ + +# A 512 byte MBR boot manager that simply boots the active partition. + + .set LOAD,0x7c00 # Load address + .set EXEC,0x600 # Execution address + .set PT_OFF,0x1be # Partition table + .set MAGIC,0xaa55 # Magic: bootable + .set FL_PACKET,0x80 # Flag: try EDD + + .set NHRDRV,0x475 # Number of hard drives + + .globl start # Entry point + .code16 + +# +# Setup the segment registers for flat addressing and setup the stack. +# +start: cld # String ops inc + xorw %ax,%ax # Zero + movw %ax,%es # Address + movw %ax,%ds # data + movw %ax,%ss # Set up + movw $LOAD,%sp # stack +# +# Relocate ourself to a lower address so that we are out of the way when +# we load in the bootstrap from the partition to boot. +# + movw $main-EXEC+LOAD,%si # Source + movw $main,%di # Destination + movw $0x200-(main-start),%cx # Byte count + rep # Relocate + movsb # code +# +# Jump to the relocated code. +# + jmp main-LOAD+EXEC # To relocated code +# +# Scan the partition table looking for an active entry. Note that %ch is +# zero from the repeated string instruction above. We save the offset of +# the active partition in %si and scan the entire table to ensure that only +# one partition is marked active. +# +main: xorw %si,%si # No active partition + movw $partbl,%bx # Partition table + movb $0x4,%cl # Number of entries +main.1: cmpb %ch,(%bx) # Null entry? + je main.2 # Yes + jg err_pt # If 0x1..0x7f + testw %si,%si # Active already found? + jnz err_pt # Yes + movw %bx,%si # Point to active +main.2: addb $0x10,%bl # Till + loop main.1 # done + testw %si,%si # Active found? + jnz main.3 # Yes + int $0x18 # BIOS: Diskless boot +# +# Ok, we've found a possible active partition. Check to see that the drive +# is a valid hard drive number. +# +main.3: cmpb $0x80,%dl # Drive valid? + jb main.4 # No + movb NHRDRV,%dh # Calculate the highest + addb $0x80,%dh # drive number available + cmpb %dh,%dl # Within range? + jb main.5 # Yes +main.4: movb (%si),%dl # Load drive +# +# Ok, now that we have a valid drive and partition entry, load the CHS from +# the partition entry and read the sector from the disk. +# +main.5: movw %sp,%di # Save stack pointer + movb 0x1(%si),%dh # Load head + movw 0x2(%si),%cx # Load cylinder:sector + movw $LOAD,%bx # Transfer buffer + testb $FL_PACKET,flags # Try EDD? + jz main.7 # No. + pushw %cx # Save %cx + pushw %bx # Save %bx + movw $0x55aa,%bx # Magic + movb $0x41,%ah # BIOS: EDD extensions + int $0x13 # present? + jc main.6 # No. + cmpw $0xaa55,%bx # Magic ok? + jne main.6 # No. + testb $0x1,%cl # Packet mode present? + jz main.6 # No. + popw %bx # Restore %bx + pushl $0x0 # Set the LBA + pushl 0x8(%si) # address + pushw %es # Set the address of + pushw %bx # the transfer buffer + pushw $0x1 # Read 1 sector + pushw $0x10 # Packet length + movw %sp,%si # Packer pointer + movw $0x4200,%ax # BIOS: LBA Read from disk + jmp main.8 # Skip the CHS setup +main.6: popw %bx # Restore %bx + popw %cx # Restore %cx +main.7: movw $0x201,%ax # BIOS: Read from disk +main.8: int $0x13 # Call the BIOS + movw %di,%sp # Restore stack + jc err_rd # If error +# +# Now that we've loaded the bootstrap, check for the 0xaa55 signature. If it +# is present, execute the bootstrap we just loaded. +# + cmpw $MAGIC,0x1fe(%bx) # Bootable? + jne err_os # No + jmp *%bx # Invoke bootstrap +# +# Various error message entry points. +# +err_pt: movw $msg_pt,%si # "Invalid partition + jmp putstr # table" + +err_rd: movw $msg_rd,%si # "Error loading + jmp putstr # operating system" + +err_os: movw $msg_os,%si # "Missing operating + jmp putstr # system" +# +# Output an ASCIZ string to the console via the BIOS. +# +putstr.0: movw $0x7,%bx # Page:attribute + movb $0xe,%ah # BIOS: Display + int $0x10 # character +putstr: lodsb # Get character + testb %al,%al # End of string? + jnz putstr.0 # No +putstr.1: jmp putstr.1 # Await reset + +msg_pt: .asciz "Invalid partition table" +msg_rd: .asciz "Error loading operating system" +msg_os: .asciz "Missing operating system" + + .org PT_OFF-1,0x90 +flags: .byte FLAGS # Flags + +partbl: .fill 0x10,0x4,0x0 # Partition table + .word MAGIC # Magic number diff --git a/stand/i386/pmbr/Makefile b/stand/i386/pmbr/Makefile new file mode 100644 index 0000000..1bfe0ee --- /dev/null +++ b/stand/i386/pmbr/Makefile @@ -0,0 +1,14 @@ +# $FreeBSD$ + +PROG= pmbr +STRIP= +BINMODE=${NOBINMODE} +MAN= +SRCS= ${PROG}.s + +ORG= 0x600 + +AFLAGS+=--defsym FLAGS=${BOOT_MBR_FLAGS} +LDFLAGS+=${LDFLAGS_BIN} + +.include <bsd.prog.mk> diff --git a/stand/i386/pmbr/Makefile.depend b/stand/i386/pmbr/Makefile.depend new file mode 100644 index 0000000..f80275d --- /dev/null +++ b/stand/i386/pmbr/Makefile.depend @@ -0,0 +1,11 @@ +# $FreeBSD$ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/stand/i386/pmbr/pmbr.s b/stand/i386/pmbr/pmbr.s new file mode 100644 index 0000000..1a75881 --- /dev/null +++ b/stand/i386/pmbr/pmbr.s @@ -0,0 +1,252 @@ +#- +# Copyright (c) 2007 Yahoo!, Inc. +# All rights reserved. +# Written by: John Baldwin <jhb@FreeBSD.org> +# +# 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. +# +# $FreeBSD$ +# +# Partly from: src/sys/boot/i386/mbr/mbr.s 1.7 + +# A 512 byte PMBR boot manager that looks for a FreeBSD boot GPT partition +# and boots it. + + .set LOAD,0x7c00 # Load address + .set EXEC,0x600 # Execution address + .set MAGIC,0xaa55 # Magic: bootable + .set SECSIZE,0x200 # Size of a single disk sector + .set DISKSIG,440 # Disk signature offset + .set STACK,EXEC+SECSIZE*4 # Stack address + .set GPT_ADDR,STACK # GPT header address + .set GPT_SIG,0 + .set GPT_SIG_0,0x20494645 # "EFI " + .set GPT_SIG_1,0x54524150 # "PART" + .set GPT_MYLBA,24 + .set GPT_PART_LBA,72 + .set GPT_NPART,80 + .set GPT_PART_SIZE,84 + .set PART_ADDR,GPT_ADDR+SECSIZE # GPT partition array address + .set PART_TYPE,0 + .set PART_START_LBA,32 + .set PART_END_LBA,40 + .set DPBUF,PART_ADDR+SECSIZE + .set DPBUF_SEC,0x10 # Number of sectors + + .set NHRDRV,0x475 # Number of hard drives + + .globl start # Entry point + .code16 + +# +# Setup the segment registers for flat addressing and setup the stack. +# +start: cld # String ops inc + xorw %ax,%ax # Zero + movw %ax,%es # Address + movw %ax,%ds # data + movw %ax,%ss # Set up + movw $STACK,%sp # stack +# +# Relocate ourself to a lower address so that we have more room to load +# other sectors. +# + movw $main-EXEC+LOAD,%si # Source + movw $main,%di # Destination + movw $SECSIZE-(main-start),%cx # Byte count + rep # Relocate + movsb # code +# +# Jump to the relocated code. +# + jmp main-LOAD+EXEC # To relocated code +# +# Validate drive number in %dl. +# +main: cmpb $0x80,%dl # Drive valid? + jb main.1 # No + movb NHRDRV,%dh # Calculate the highest + addb $0x80,%dh # drive number available + cmpb %dh,%dl # Within range? + jb main.2 # Yes +main.1: movb $0x80,%dl # Assume drive 0x80 +# +# Load the GPT header and verify signature. Try LBA 1 for the primary one and +# the last LBA for the backup if it is broken. +# +main.2: call getdrvparams # Read drive parameters + movb $1,%dh # %dh := 1 (reading primary) +main.2a: movw $GPT_ADDR,%bx + movw $lba,%si + call read # Read header and check GPT sig + cmpl $GPT_SIG_0,GPT_ADDR+GPT_SIG + jnz main.2b + cmpl $GPT_SIG_1,GPT_ADDR+GPT_SIG+4 + jnz main.2b + jmp load_part +main.2b: cmpb $1,%dh # Reading primary? + jne err_pt # If no - invalid table found +# +# Try alternative LBAs from the last sector for the GPT header. +# +main.3: movb $0,%dh # %dh := 0 (reading backup) + movw $DPBUF+DPBUF_SEC,%si # %si = last sector + 1 + movw $lba,%di # %di = $lba +main.3a: decl (%si) # 0x0(%si) = last sec (0-31) + movw $2,%cx + rep + movsw # $lastsec--, copy it to $lba + jmp main.2a # Read the next sector +# +# Load a partition table sector from disk and look for a FreeBSD boot +# partition. +# +load_part: movw $GPT_ADDR+GPT_PART_LBA,%si + movw $PART_ADDR,%bx + call read +scan: movw %bx,%si # Compare partition UUID + movw $boot_uuid,%di # with FreeBSD boot UUID + movb $0x10,%cl + repe cmpsb + jnz next_part # Didn't match, next partition +# +# We found a boot partition. Load it into RAM starting at 0x7c00. +# + movw %bx,%di # Save partition pointer in %di + leaw PART_START_LBA(%di),%si + movw $LOAD/16,%bx + movw %bx,%es + xorw %bx,%bx +load_boot: push %si # Save %si + call read + pop %si # Restore + movl PART_END_LBA(%di),%eax # See if this was the last LBA + cmpl (%si),%eax + jnz next_boot + movl PART_END_LBA+4(%di),%eax + cmpl 4(%si),%eax + jnz next_boot + mov %bx,%es # Reset %es to zero + jmp LOAD # Jump to boot code +next_boot: incl (%si) # Next LBA + adcl $0,4(%si) + mov %es,%ax # Adjust segment for next + addw $SECSIZE/16,%ax # sector + cmp $0x9000,%ax # Don't load past 0x90000, + jae err_big # 545k should be enough for + mov %ax,%es # any boot code. :) + jmp load_boot +# +# Move to the next partition. If we walk off the end of the sector, load +# the next sector. We assume that partition entries are smaller than 64k +# and that they won't span a sector boundary. +# +# XXX: Should we int 0x18 instead of err_noboot if we hit the end of the table? +# +next_part: decl GPT_ADDR+GPT_NPART # Was this the last partition? + jz err_noboot + movw GPT_ADDR+GPT_PART_SIZE,%ax + addw %ax,%bx # Next partition + cmpw $PART_ADDR+0x200,%bx # Still in sector? + jb scan + incl GPT_ADDR+GPT_PART_LBA # Next sector + adcl $0,GPT_ADDR+GPT_PART_LBA+4 + jmp load_part +# +# Load a sector (64-bit LBA at %si) from disk %dl into %es:%bx by creating +# a EDD packet on the stack and passing it to the BIOS. Trashes %ax and %si. +# +read: pushl 0x4(%si) # Set the LBA + pushl 0x0(%si) # address + pushw %es # Set the address of + pushw %bx # the transfer buffer + pushw $0x1 # Read 1 sector + pushw $0x10 # Packet length + movw %sp,%si # Packer pointer + movw $0x4200,%ax # BIOS: LBA Read from disk + int $0x13 # Call the BIOS + add $0x10,%sp # Restore stack + jc err_rd # If error + ret +# +# Check the number of LBAs on the drive index %dx. Trashes %ax and %si. +# +getdrvparams: + movw $DPBUF,%si # Set the address of result buf + movw $0x001e,(%si) # len + movw $0x4800,%ax # BIOS: Read Drive Parameters + int $0x13 # Call the BIOS + jc err_rd # "I/O error" if error + ret +# +# Various error message entry points. +# +err_big: movw $msg_big,%si # "Boot loader too + jmp putstr # large" + +err_pt: movw $msg_pt,%si # "Invalid partition + jmp putstr # table" + +err_rd: movw $msg_rd,%si # "I/O error loading + jmp putstr # boot loader" + +err_noboot: movw $msg_noboot,%si # "Missing boot + jmp putstr # loader" +# +# Output an ASCIZ string to the console via the BIOS. +# +putstr.0: movw $0x7,%bx # Page:attribute + movb $0xe,%ah # BIOS: Display + int $0x10 # character +putstr: lodsb # Get character + testb %al,%al # End of string? + jnz putstr.0 # No +putstr.1: jmp putstr.1 # Await reset + +msg_big: .asciz "Boot loader too large" +msg_pt: .asciz "Invalid partition table" +msg_rd: .asciz "I/O error loading boot loader" +msg_noboot: .asciz "Missing boot loader" + +lba: .quad 1 # LBA of GPT header + +boot_uuid: .long 0x83bd6b9d + .word 0x7f41 + .word 0x11dc + .byte 0xbe + .byte 0x0b + .byte 0x00 + .byte 0x15 + .byte 0x60 + .byte 0xb8 + .byte 0x4f + .byte 0x0f + + .org DISKSIG,0x90 +sig: .long 0 # OS Disk Signature + .word 0 # "Unknown" in PMBR + +partbl: .fill 0x10,0x4,0x0 # Partition table + .word MAGIC # Magic number diff --git a/stand/i386/pxeldr/Makefile b/stand/i386/pxeldr/Makefile new file mode 100644 index 0000000..819283c --- /dev/null +++ b/stand/i386/pxeldr/Makefile @@ -0,0 +1,47 @@ +# $FreeBSD$ + +.include <bsd.init.mk> + +PROG= ${LDR} +INTERNALPROG= +FILES= ${BOOT} +MAN= ${BOOT}.8 +SRCS= ${LDR}.S +CLEANFILES+= ${BOOT} + +BOOT= pxeboot +LDR= pxeldr +ORG= 0x7c00 +LOADER= loader + +.if defined(BOOT_PXELDR_PROBE_KEYBOARD) +CFLAGS+=-DPROBE_KEYBOARD +.endif + +.if defined(BOOT_PXELDR_ALWAYS_SERIAL) +CFLAGS+=-DALWAYS_SERIAL +.endif + +CFLAGS+=-I${BOOTSRC}/i386/common + +LOADERBIN= ${BOOTOBJ}/i386/loader/loader.bin + +CLEANFILES+= ${BOOT}.tmp + +${BOOT}: ${LDR} ${LOADER} + cat ${LDR} ${LOADER} > ${.TARGET}.tmp + ${DD} if=${.TARGET}.tmp of=${.TARGET} obs=2k conv=osync + rm ${.TARGET}.tmp + +LDFLAGS+=${LDFLAGS_BIN} + +CLEANFILES+= ${LOADER} + +${LOADER}: ${LOADERBIN} ${BTXLDR} ${BTXKERN} + btxld -v -f aout -e ${LOADER_ADDRESS} -o ${.TARGET} -l ${BTXLDR} \ + -b ${BTXKERN} ${LOADERBIN} + +.include <bsd.prog.mk> + +# XXX: clang integrated-as doesn't grok .codeNN directives yet +CFLAGS.pxeldr.S= ${CLANG_NO_IAS} diff --git a/stand/i386/pxeldr/Makefile.depend b/stand/i386/pxeldr/Makefile.depend new file mode 100644 index 0000000..c6f574a --- /dev/null +++ b/stand/i386/pxeldr/Makefile.depend @@ -0,0 +1,15 @@ +# $FreeBSD$ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + include \ + sys/boot/i386/btx/btx \ + sys/boot/i386/btx/btxldr \ + sys/boot/i386/loader \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/stand/i386/pxeldr/pxeboot.8 b/stand/i386/pxeldr/pxeboot.8 new file mode 100644 index 0000000..0194e21 --- /dev/null +++ b/stand/i386/pxeldr/pxeboot.8 @@ -0,0 +1,158 @@ +.\" Copyright (c) 1999 Doug White +.\" 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 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. +.\" +.\" $FreeBSD$ +.\" +.Dd May 27, 2017 +.Dt PXEBOOT 8 +.Os +.Sh NAME +.Nm pxeboot +.Nd Preboot Execution Environment (PXE) bootloader +.Sh DESCRIPTION +The +.Nm +bootloader is a modified version of the system third-stage bootstrap +.Xr loader 8 +configured to run under Intel's Preboot Execution Environment (PXE) system. +PXE is a form of smart boot ROM, built into Intel EtherExpress Pro/100 and +3Com 3c905c Ethernet cards, and Ethernet-equipped Intel motherboards. +PXE supports DHCP configuration and provides low-level NIC access services. +.Pp +The DHCP client will set a DHCP user class named +.Va FreeBSD +to allow flexible configuration of the DHCP server. +.Pp +The +.Nm +bootloader retrieves the kernel, modules, +and other files either via NFS over UDP or by TFTP, +selectable through compile-time options. +In combination with a memory file system image or NFS-mounted root file system, +.Nm +allows for easy, +EEPROM-burner free construction of diskless machines. +.Pp +The +.Nm +binary is loaded just like any other boot file, +by specifying it in the DHCP server's configuration file. +Below is a sample configuration for the ISC DHCP v2 server: +.Bd -literal -offset indent +option domain-name "example.com"; +option routers 10.0.0.1; +option subnet-mask 255.255.255.0; +option broadcast-address 10.0.0.255; +option domain-name-servers 10.0.0.1; +server-name "DHCPserver"; +server-identifier 10.0.0.1; + +default-lease-time 120; +max-lease-time 120; + +subnet 10.0.0.0 netmask 255.255.255.0 { + filename "pxeboot"; + range 10.0.0.10 10.0.0.254; + if exists user-class and option user-class = "FreeBSD" { + option root-path "tftp://10.0.0.1/FreeBSD"; + } +} + +.Ed +.Nm +recognizes +.Va next-server +and +.Va option root-path +directives as the server and path to NFS mount for file requests, +respectively, or the server to make TFTP requests to. +Note that +.Nm +expects to fetch +.Pa /boot/loader.rc +from the specified server before loading any other files. +.Pp +Valid +.Va option root-path +Syntax is the following +.Bl -tag -width <scheme>://ip/path indent +.It /path +path to the root filesystem on the NFS server +.It ip:/path +path to the root filesystem on the NFS server +.Ar ip +.It nfs:/path +path to the root filesystem on the NFS server +.It nfs://ip/path +path to the root filesystem on the NFS server +.Ar ip +.It tftp:/path +path to the root filesystem on the TFTP server +.It tftp://ip/path +path to the root filesystem on the TFTP server +.Ar ip +.El +.Pp +.Nm +defaults to a conservative 1024 byte NFS data packet size. +This may be changed by setting the +.Va nfs.read_size +variable in +.Pa /boot/loader.conf . +Valid values range from 1024 to 16384 bytes. +.Pp +In all other respects, +.Nm +acts just like +.Xr loader 8 . +.Pp +As PXE is still in its infancy, some firmware versions may not work +properly. +The +.Nm +bootloader has been extensively tested on version 0.99 of Intel firmware; +pre-release versions of the newer 2.0 firmware are known to have +problems. +Check with the device's manufacturer for their latest stable release. +.Pp +For further information on Intel's PXE specifications and Wired for +Management (WfM) systems, see +.Li http://www.intel.com/design/archives/wfm/ . +.Sh SEE ALSO +.Xr loader 8 +.Sh HISTORY +The +.Nm +bootloader first appeared in +.Fx 4.1 . +.Sh AUTHORS +.An -nosplit +The +.Nm +bootloader was written by +.An John Baldwin Aq jhb@FreeBSD.org +and +.An Paul Saab Aq ps@FreeBSD.org . +This manual page was written by +.An Doug White Aq dwhite@FreeBSD.org . diff --git a/stand/i386/pxeldr/pxeldr.S b/stand/i386/pxeldr/pxeldr.S new file mode 100644 index 0000000..ee1e18a --- /dev/null +++ b/stand/i386/pxeldr/pxeldr.S @@ -0,0 +1,301 @@ +/* + * Copyright (c) 2000 John Baldwin + * 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 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. + * + * $FreeBSD$ + */ + +/* + * This simple program is a preloader for the normal boot3 loader. It is simply + * prepended to the beginning of a fully built and btxld'd loader. It then + * copies the loader to the address boot2 normally loads it, emulates the + * boot[12] environment (protected mode, a bootinfo struct, etc.), and then jumps + * to the start of btxldr to start the boot process. This method allows a stock + * /boot/loader to be booted over the network via PXE w/o having to write a + * separate PXE-aware client just to load the loader. + */ + +#include <sys/reboot.h> +#include <bootargs.h> + +/* + * Memory locations. + */ + .set MEM_PAGE_SIZE,0x1000 # memory page size, 4k + .set MEM_ARG,0x900 # Arguments at start + .set MEM_ARG_BTX,0xa100 # Where we move them to so the + # BTX client can see them + .set MEM_ARG_SIZE,0x18 # Size of the arguments + .set MEM_BTX_ADDRESS,0x9000 # where BTX lives + .set MEM_BTX_ENTRY,0x9010 # where BTX starts to execute + .set MEM_BTX_OFFSET,MEM_PAGE_SIZE # offset of BTX in the loader + .set MEM_BTX_CLIENT,0xa000 # where BTX clients live + .set MEM_BIOS_KEYBOARD,0x496 # BDA byte with keyboard bit +/* + * a.out header fields + */ + .set AOUT_TEXT,0x04 # text segment size + .set AOUT_DATA,0x08 # data segment size + .set AOUT_BSS,0x0c # zero'd BSS size + .set AOUT_SYMBOLS,0x10 # symbol table + .set AOUT_ENTRY,0x14 # entry point + .set AOUT_HEADER,MEM_PAGE_SIZE # size of the a.out header +/* + * Segment selectors. + */ + .set SEL_SDATA,0x8 # Supervisor data + .set SEL_RDATA,0x10 # Real mode data + .set SEL_SCODE,0x18 # PM-32 code + .set SEL_SCODE16,0x20 # PM-16 code +/* + * BTX constants + */ + .set INT_SYS,0x30 # BTX syscall interrupt +/* + * Bit in MEM_BIOS_KEYBOARD that is set if an enhanced keyboard is present + */ + .set KEYBOARD_BIT,0x10 +/* + * We expect to be loaded by the BIOS at 0x7c00 (standard boot loader entry + * point) + */ + .code16 + .globl start + .org 0x0, 0x0 +/* + * BTX program loader for PXE network booting + */ +start: cld # string ops inc + xorw %ax, %ax # zero %ax + movw %ax, %ss # setup the + movw $start, %sp # stack + movw %es, %cx # save PXENV+ segment + movw %ax, %ds # setup the + movw %ax, %es # data segments + andl $0xffff, %ecx # clear upper words + andl $0xffff, %ebx # of %ebx and %ecx + shll $4, %ecx # calculate the offset of + addl %ebx, %ecx # the PXENV+ struct and + pushl %ecx # save it on the stack + movw $welcome_msg, %si # %ds:(%si) -> welcome message + callw putstr # display the welcome message +/* + * Setup the arguments that the loader is expecting from boot[12] + */ + movw $bootinfo_msg, %si # %ds:(%si) -> boot args message + callw putstr # display the message + movw $MEM_ARG, %bx # %ds:(%bx) -> boot args + movw %bx, %di # %es:(%di) -> boot args + xorl %eax, %eax # zero %eax + movw $(MEM_ARG_SIZE/4), %cx # Size of arguments in 32-bit + # dwords + rep # Clear the arguments + stosl # to zero + orb $KARGS_FLAGS_PXE, 0x8(%bx) # kargs->bootflags |= + # KARGS_FLAGS_PXE + popl 0xc(%bx) # kargs->pxeinfo = *PXENV+ +#ifdef ALWAYS_SERIAL +/* + * set the RBX_SERIAL bit in the howto byte. + */ + orl $RB_SERIAL, (%bx) # enable serial console +#endif +#ifdef PROBE_KEYBOARD +/* + * Look at the BIOS data area to see if we have an enhanced keyboard. If not, + * set the RBX_DUAL and RBX_SERIAL bits in the howto byte. + */ + testb $KEYBOARD_BIT, MEM_BIOS_KEYBOARD # keyboard present? + jnz keyb # yes, so skip + orl $(RB_MULTIPLE | RB_SERIAL), (%bx) # enable serial console +keyb: +#endif +/* + * Turn on the A20 address line + */ + callw seta20 # Turn A20 on +/* + * Relocate the loader and BTX using a very lazy protected mode + */ + movw $relocate_msg, %si # Display the + callw putstr # relocation message + movl end+AOUT_ENTRY, %edi # %edi is the destination + movl $(end+AOUT_HEADER), %esi # %esi is + # the start of the text + # segment + movl end+AOUT_TEXT, %ecx # %ecx = length of the text + # segment + lgdt gdtdesc # setup our own gdt + cli # turn off interrupts + movl %cr0, %eax # Turn on + orb $0x1, %al # protected + movl %eax, %cr0 # mode + ljmp $SEL_SCODE,$pm_start # long jump to clear the + # instruction pre-fetch queue + .code32 +pm_start: movw $SEL_SDATA, %ax # Initialize + movw %ax, %ds # %ds and + movw %ax, %es # %es to a flat selector + rep # Relocate the + movsb # text segment + addl $(MEM_PAGE_SIZE - 1), %edi # pad %edi out to a new page + andl $~(MEM_PAGE_SIZE - 1), %edi # for the data segment + movl end+AOUT_DATA, %ecx # size of the data segment + rep # Relocate the + movsb # data segment + movl end+AOUT_BSS, %ecx # size of the bss + xorl %eax, %eax # zero %eax + addb $3, %cl # round %ecx up to + shrl $2, %ecx # a multiple of 4 + rep # zero the + stosl # bss + movl end+AOUT_ENTRY, %esi # %esi -> relocated loader + addl $MEM_BTX_OFFSET, %esi # %esi -> BTX in the loader + movl $MEM_BTX_ADDRESS, %edi # %edi -> where BTX needs to go + movzwl 0xa(%esi), %ecx # %ecx -> length of BTX + rep # Relocate + movsb # BTX + ljmp $SEL_SCODE16,$pm_16 # Jump to 16-bit PM + .code16 +pm_16: movw $SEL_RDATA, %ax # Initialize + movw %ax, %ds # %ds and + movw %ax, %es # %es to a real mode selector + movl %cr0, %eax # Turn off + andb $~0x1, %al # protected + movl %eax, %cr0 # mode + ljmp $0,$pm_end # Long jump to clear the + # instruction pre-fetch queue +pm_end: sti # Turn interrupts back on now +/* + * Copy the BTX client to MEM_BTX_CLIENT + */ + xorw %ax, %ax # zero %ax and set + movw %ax, %ds # %ds and %es + movw %ax, %es # to segment 0 + movw $MEM_BTX_CLIENT, %di # Prepare to relocate + movw $btx_client, %si # the simple btx client + movw $(btx_client_end-btx_client), %cx # length of btx client + rep # Relocate the + movsb # simple BTX client +/* + * Copy the boot[12] args to where the BTX client can see them + */ + movw $MEM_ARG, %si # where the args are at now + movw $MEM_ARG_BTX, %di # where the args are moving to + movw $(MEM_ARG_SIZE/4), %cx # size of the arguments in longs + rep # Relocate + movsl # the words +/* + * Save the entry point so the client can get to it later on + */ + movl end+AOUT_ENTRY, %eax # load the entry point + stosl # add it to the end of the + # arguments +/* + * Now we just start up BTX and let it do the rest + */ + movw $jump_message, %si # Display the + callw putstr # jump message + ljmp $0,$MEM_BTX_ENTRY # Jump to the BTX entry point + +/* + * Display a null-terminated string + */ +putstr: lodsb # load %al from %ds:(%si) + testb %al,%al # stop at null + jnz putc # if the char != null, output it + retw # return when null is hit +putc: movw $0x7,%bx # attribute for output + movb $0xe,%ah # BIOS: put_char + int $0x10 # call BIOS, print char in %al + jmp putstr # keep looping + +/* + * Enable A20. Put an upper limit on the amount of time we wait for the + * keyboard controller to get ready (65K x ISA access time). If + * we wait more than that amount, the hardware is probably + * legacy-free and simply doesn't have a keyboard controller. + * Thus, the A20 line is already enabled. + */ +seta20: cli # Disable interrupts + xor %cx,%cx # Clear +seta20.1: inc %cx # Increment, overflow? + jz seta20.3 # Yes + inb $0x64,%al # Get status + testb $0x2,%al # Busy? + jnz seta20.1 # Yes + movb $0xd1,%al # Command: Write + outb %al,$0x64 # output port +seta20.2: inb $0x64,%al # Get status + testb $0x2,%al # Busy? + jnz seta20.2 # Yes + movb $0xdf,%al # Enable + outb %al,$0x60 # A20 +seta20.3: sti # Enable interrupts + retw # To caller + +/* + * BTX client to start btxldr + */ + .code32 +btx_client: movl $(MEM_ARG_BTX-MEM_BTX_CLIENT+MEM_ARG_SIZE-4), %esi + # %ds:(%esi) -> end + # of boot[12] args + movl $(MEM_ARG_SIZE/4), %ecx # Number of words to push + std # Go backwards +push_arg: lodsl # Read argument + pushl %eax # Push it onto the stack + loop push_arg # Push all of the arguments + cld # In case anyone depends on this + pushl MEM_ARG_BTX-MEM_BTX_CLIENT+MEM_ARG_SIZE # Entry point of + # the loader + pushl %eax # Emulate a near call + movl $0x1, %eax # 'exec' system call + int $INT_SYS # BTX system call +btx_client_end: + .code16 + + .p2align 4 +/* + * Global descriptor table. + */ +gdt: .word 0x0,0x0,0x0,0x0 # Null entry + .word 0xffff,0x0,0x9200,0xcf # SEL_SDATA + .word 0xffff,0x0,0x9200,0x0 # SEL_RDATA + .word 0xffff,0x0,0x9a00,0xcf # SEL_SCODE (32-bit) + .word 0xffff,0x0,0x9a00,0x8f # SEL_SCODE16 (16-bit) +gdt.1: +/* + * Pseudo-descriptors. + */ +gdtdesc: .word gdt.1-gdt-1 # Limit + .long gdt # Base + +welcome_msg: .asciz "PXE Loader 1.00\r\n\n" +bootinfo_msg: .asciz "Building the boot loader arguments\r\n" +relocate_msg: .asciz "Relocating the loader and the BTX\r\n" +jump_message: .asciz "Starting the BTX loader\r\n" + + .p2align 4 +end: diff --git a/stand/i386/zfsboot/Makefile b/stand/i386/zfsboot/Makefile new file mode 100644 index 0000000..f0523df --- /dev/null +++ b/stand/i386/zfsboot/Makefile @@ -0,0 +1,93 @@ +# $FreeBSD$ + +HAVE_GELI=yes + +.include <bsd.init.mk> + +.PATH: ${BOOTSRC}/i386/boot2 ${BOOTSRC}/i386/common ${SASRC} + +FILES= zfsboot +MAN= zfsboot.8 + +NM?= nm + +BOOT_COMCONSOLE_PORT?= 0x3f8 +BOOT_COMCONSOLE_SPEED?= 9600 +B2SIOFMT?= 0x3 + +REL1= 0x700 +ORG1= 0x7c00 +ORG2= 0x2000 + +CFLAGS+=-DBOOTPROG=\"zfsboot\" \ + -O1 \ + -DZFS -DBOOT2 \ + -DSIOPRT=${BOOT_COMCONSOLE_PORT} \ + -DSIOFMT=${B2SIOFMT} \ + -DSIOSPD=${BOOT_COMCONSOLE_SPEED} \ + -I${LDRSRC} \ + -I${BOOTSRC}/i386/common \ + -I${BOOTSRC}/i386 \ + -I${ZFSSRC} \ + -I${SYSDIR}/cddl/boot/zfs \ + -I${BTXLIB} \ + -I${BOOTSRC}/i386/boot2 \ + -Wall -Waggregate-return -Wbad-function-cast -Wno-cast-align \ + -Wmissing-declarations -Wmissing-prototypes -Wnested-externs \ + -Wpointer-arith -Wshadow -Wstrict-prototypes -Wwrite-strings \ + -Winline + +CFLAGS.gcc+= --param max-inline-insns-single=100 +.if ${MACHINE} == "amd64" +LIBZFSBOOT=${BOOTOBJ}/zfs32/libzfsboot.a +.else +LIBZFSBOOT=${BOOTOBJ}/zfs/libzfsboot.a +.endif + +LD_FLAGS+=${LD_FLAGS_BIN} + +CLEANFILES+= zfsboot + +zfsboot: zfsboot1 zfsboot2 + cat zfsboot1 zfsboot2 > zfsboot + +CLEANFILES+= zfsboot1 zfsldr.out zfsldr.o + +zfsboot1: zfsldr.out + ${OBJCOPY} -S -O binary zfsldr.out ${.TARGET} + +zfsldr.out: zfsldr.o + ${LD} ${LD_FLAGS} -e start -Ttext ${ORG1} -o ${.TARGET} zfsldr.o + +CLEANFILES+= zfsboot2 zfsboot.ld zfsboot.ldr zfsboot.bin zfsboot.out \ + zfsboot.o zfsboot.s zfsboot.s.tmp sio.o cons.o drv.o + +# We currently allow 128k bytes for zfsboot - in practice it could be +# any size up to 3.5Mb but keeping it fixed size simplifies zfsldr. +# +BOOT2SIZE= 131072 + +zfsboot2: zfsboot.ld + @set -- `ls -l ${.ALLSRC}`; x=$$((${BOOT2SIZE}-$$5)); \ + echo "$$x bytes available"; test $$x -ge 0 + ${DD} if=${.ALLSRC} of=${.TARGET} obs=${BOOT2SIZE} conv=osync + +zfsboot.ld: zfsboot.ldr zfsboot.bin ${BTXKERN} + btxld -v -E ${ORG2} -f bin -b ${BTXKERN} -l zfsboot.ldr \ + -o ${.TARGET} -P 1 zfsboot.bin + +zfsboot.ldr: + cp /dev/null ${.TARGET} + +zfsboot.bin: zfsboot.out + ${OBJCOPY} -S -O binary zfsboot.out ${.TARGET} + +zfsboot.out: ${BTXCRT} zfsboot.o sio.o drv.o cons.o + ${LD} ${LD_FLAGS} -Ttext ${ORG2} -o ${.TARGET} ${.ALLSRC} ${LIBZFSBOOT} ${LIBGELIBOOT} ${LIBSA32} + +SRCS= zfsboot.c + +.include <bsd.prog.mk> + +# XXX: clang integrated-as doesn't grok .codeNN directives yet +CFLAGS.zfsldr.S= ${CLANG_NO_IAS} diff --git a/stand/i386/zfsboot/Makefile.depend b/stand/i386/zfsboot/Makefile.depend new file mode 100644 index 0000000..63a43d8 --- /dev/null +++ b/stand/i386/zfsboot/Makefile.depend @@ -0,0 +1,16 @@ +# $FreeBSD$ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + include \ + include/xlocale \ + sys/boot/i386/btx/btx \ + sys/boot/i386/btx/lib \ + sys/boot/libstand32 \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/stand/i386/zfsboot/zfsboot.8 b/stand/i386/zfsboot/zfsboot.8 new file mode 100644 index 0000000..6450939 --- /dev/null +++ b/stand/i386/zfsboot/zfsboot.8 @@ -0,0 +1,133 @@ +.\" Copyright (c) 2014 Andriy Gapon <avg@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. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS 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 AUTHORS 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. +.\" +.\" $FreeBSD$ +.\" +.Dd September 15, 2014 +.Dt ZFSBOOT 8 +.Os +.Sh NAME +.Nm zfsboot +.Nd bootcode for ZFS on BIOS-based computers +.Sh DESCRIPTION +.Nm +is used on BIOS-based computers to boot from a filesystem in +a ZFS pool. +.Nm +is installed in two parts on a disk or a partition used by a ZFS pool. +The first part, a single-sector starter boot block, is installed +at the beginning of the disk or partition. +The second part, a main boot block, is installed at a special offset +within the disk or partition. +Both areas are reserved by the ZFS on-disk specification for boot use. +If +.Nm +is installed in a partition, then that partition should be made +bootable using appropriate configuration and boot blocks described in +.Xr boot 8 . +.Sh BOOTING +The +.Nm +boot process is very similar to that of +.Xr gptzfsboot 8 . +One significant difference is that +.Nm +does not currently support the GPT partitioning scheme. +Thus only whole disks and MBR partitions, traditionally referred to as +slices, are probed for ZFS disk labels. +See the BUGS section in +.Xr gptzfsboot 8 +for some limitations of the MBR scheme support. +.Sh USAGE +.Nm +supports all the same prompt and configuration file arguments as +.Xr gptzfsboot 8 . +.Sh FILES +.Bl -tag -width /boot/zfsboot -compact +.It Pa /boot/zfsboot +boot code binary +.It Pa /boot.config +parameters for the boot block +.Pq optional +.It Pa /boot/config +alternative parameters for the boot block +.Pq optional +.El +.Sh EXAMPLES +.Nm +is typically installed using +.Xr dd 1 . +To install +.Nm +on the +.Pa ada0 +drive: +.Bd -literal -offset indent +dd if=/boot/zfsboot of=/dev/ada0 count=1 +dd if=/boot/zfsboot of=/dev/ada0 iseek=1 oseek=1024 +.Ed +.Pp +If the drive is currently in use, the GEOM safety will prevent writes +and must be disabled before running the above commands: +.Bd -literal -offset indent +sysctl kern.geom.debugflags=0x10 +.Ed +.Pp +.Nm +can also be installed in an MBR slice: +.Bd -literal -offset indent +gpart create -s mbr ada0 +gpart add -t freebsd ada0 +gpart create -s BSD ada0s1 +gpart bootcode -b /boot/boot0 ada0 +gpart set -a active -i 1 ada0 +dd if=/boot/zfsboot of=/dev/ada0s1 count=1 +dd if=/boot/zfsboot of=/dev/ada0s1 iseek=1 oseek=1024 +.Ed +.Pp +Note that commands to create and populate a pool are not shown +in the example above. +.Sh SEE ALSO +.Xr dd 1 , +.Xr boot.config 5 , +.Xr boot 8 , +.Xr gptzfsboot 8 , +.Xr loader 8 , +.Xr zfsloader 8 , +.Xr zpool 8 +.Sh HISTORY +.Nm +appeared in FreeBSD 7.3. +.Sh AUTHORS +This manual page was written by +.An Andriy Gapon Aq avg@FreeBSD.org . +.Sh BUGS +Installing +.Nm +with +.Xr dd 1 +is a hack. +ZFS needs a command to properly install +.Nm +onto a ZFS-controlled disk or partition. diff --git a/stand/i386/zfsboot/zfsboot.c b/stand/i386/zfsboot/zfsboot.c new file mode 100644 index 0000000..88b5231 --- /dev/null +++ b/stand/i386/zfsboot/zfsboot.c @@ -0,0 +1,1151 @@ +/*- + * Copyright (c) 1998 Robert Nordier + * All rights reserved. + * + * Redistribution and use in source and binary forms are freely + * permitted provided that the above copyright notice and this + * paragraph and the following disclaimer are duplicated in all + * such forms. + * + * This software is provided "AS IS" and without any express or + * implied warranties, including, without limitation, the implied + * warranties of merchantability and fitness for a particular + * purpose. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/errno.h> +#include <sys/diskmbr.h> +#ifdef GPT +#include <sys/gpt.h> +#endif +#include <sys/reboot.h> +#include <sys/queue.h> + +#include <machine/bootinfo.h> +#include <machine/elf.h> +#include <machine/pc/bios.h> + +#include <stdarg.h> +#include <stddef.h> + +#include <a.out.h> + +#include <btxv86.h> + +#include "lib.h" +#include "rbx.h" +#include "drv.h" +#include "edd.h" +#include "util.h" +#include "cons.h" +#include "bootargs.h" +#include "paths.h" + +#include "libzfs.h" + +#define ARGS 0x900 +#define NOPT 14 +#define NDEV 3 + +#define BIOS_NUMDRIVES 0x475 +#define DRV_HARD 0x80 +#define DRV_MASK 0x7f + +#define TYPE_AD 0 +#define TYPE_DA 1 +#define TYPE_MAXHARD TYPE_DA +#define TYPE_FD 2 + +#define DEV_GELIBOOT_BSIZE 4096 + +extern uint32_t _end; + +#ifdef GPT +static const uuid_t freebsd_zfs_uuid = GPT_ENT_TYPE_FREEBSD_ZFS; +#endif +static const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */ +static const unsigned char flags[NOPT] = { + RBX_DUAL, + RBX_SERIAL, + RBX_ASKNAME, + RBX_CDROM, + RBX_CONFIG, + RBX_KDB, + RBX_GDB, + RBX_MUTE, + RBX_NOINTR, + RBX_PAUSE, + RBX_QUIET, + RBX_DFLTROOT, + RBX_SINGLE, + RBX_VERBOSE +}; +uint32_t opts; + +static const unsigned char dev_maj[NDEV] = {30, 4, 2}; + +static char cmd[512]; +static char cmddup[512]; +static char kname[1024]; +static char rootname[256]; +static int comspeed = SIOSPD; +static struct bootinfo bootinfo; +static uint32_t bootdev; +static struct zfs_boot_args zfsargs; +static struct zfsmount zfsmount; + +vm_offset_t high_heap_base; +uint32_t bios_basemem, bios_extmem, high_heap_size; + +static struct bios_smap smap; + +/* + * The minimum amount of memory to reserve in bios_extmem for the heap. + */ +#define HEAP_MIN (64 * 1024 * 1024) + +static char *heap_next; +static char *heap_end; + +/* Buffers that must not span a 64k boundary. */ +#define READ_BUF_SIZE 8192 +struct dmadat { + char rdbuf[READ_BUF_SIZE]; /* for reading large things */ + char secbuf[READ_BUF_SIZE]; /* for MBR/disklabel */ +}; +static struct dmadat *dmadat; + +void exit(int); +void reboot(void); +static void load(void); +static int parse_cmd(void); +static void bios_getmem(void); +void *malloc(size_t n); +void free(void *ptr); +int main(void); + +void * +malloc(size_t n) +{ + char *p = heap_next; + if (p + n > heap_end) { + printf("malloc failure\n"); + for (;;) + ; + /* NOTREACHED */ + return (0); + } + heap_next += n; + return (p); +} + +void +free(void *ptr) +{ + + return; +} + +static char * +strdup(const char *s) +{ + char *p = malloc(strlen(s) + 1); + strcpy(p, s); + return (p); +} + +#ifdef LOADER_GELI_SUPPORT +#include "geliboot.c" +static char gelipw[GELI_PW_MAXLEN]; +static struct keybuf *gelibuf; +#endif + +#include "zfsimpl.c" + +/* + * Read from a dnode (which must be from a ZPL filesystem). + */ +static int +zfs_read(spa_t *spa, const dnode_phys_t *dnode, off_t *offp, void *start, size_t size) +{ + const znode_phys_t *zp = (const znode_phys_t *) dnode->dn_bonus; + size_t n; + int rc; + + n = size; + if (*offp + n > zp->zp_size) + n = zp->zp_size - *offp; + + rc = dnode_read(spa, dnode, *offp, start, n); + if (rc) + return (-1); + *offp += n; + + return (n); +} + +/* + * Current ZFS pool + */ +static spa_t *spa; +static spa_t *primary_spa; +static vdev_t *primary_vdev; + +/* + * A wrapper for dskread that doesn't have to worry about whether the + * buffer pointer crosses a 64k boundary. + */ +static int +vdev_read(vdev_t *vdev, void *priv, off_t off, void *buf, size_t bytes) +{ + char *p; + daddr_t lba, alignlba; + off_t diff; + unsigned int nb, alignnb; + struct dsk *dsk = (struct dsk *) priv; + + if ((off & (DEV_BSIZE - 1)) || (bytes & (DEV_BSIZE - 1))) + return -1; + + p = buf; + lba = off / DEV_BSIZE; + lba += dsk->start; + /* + * Align reads to 4k else 4k sector GELIs will not decrypt. + * Round LBA down to nearest multiple of DEV_GELIBOOT_BSIZE bytes. + */ + alignlba = rounddown2(off, DEV_GELIBOOT_BSIZE) / DEV_BSIZE; + /* + * The read must be aligned to DEV_GELIBOOT_BSIZE bytes relative to the + * start of the GELI partition, not the start of the actual disk. + */ + alignlba += dsk->start; + diff = (lba - alignlba) * DEV_BSIZE; + + while (bytes > 0) { + nb = bytes / DEV_BSIZE; + /* + * Ensure that the read size plus the leading offset does not + * exceed the size of the read buffer. + */ + if (nb > (READ_BUF_SIZE - diff) / DEV_BSIZE) + nb = (READ_BUF_SIZE - diff) / DEV_BSIZE; + /* + * Round the number of blocks to read up to the nearest multiple + * of DEV_GELIBOOT_BSIZE. + */ + alignnb = roundup2(nb * DEV_BSIZE + diff, DEV_GELIBOOT_BSIZE) + / DEV_BSIZE; + + if (drvread(dsk, dmadat->rdbuf, alignlba, alignnb)) + return -1; +#ifdef LOADER_GELI_SUPPORT + /* decrypt */ + if (is_geli(dsk) == 0) { + if (geli_read(dsk, ((alignlba - dsk->start) * + DEV_BSIZE), dmadat->rdbuf, alignnb * DEV_BSIZE)) + return (-1); + } +#endif + memcpy(p, dmadat->rdbuf + diff, nb * DEV_BSIZE); + p += nb * DEV_BSIZE; + lba += nb; + alignlba += alignnb; + bytes -= nb * DEV_BSIZE; + /* Don't need the leading offset after the first block. */ + diff = 0; + } + + return 0; +} + +static int +vdev_write(vdev_t *vdev, void *priv, off_t off, void *buf, size_t bytes) +{ + char *p; + daddr_t lba; + unsigned int nb; + struct dsk *dsk = (struct dsk *) priv; + + if ((off & (DEV_BSIZE - 1)) || (bytes & (DEV_BSIZE - 1))) + return -1; + + p = buf; + lba = off / DEV_BSIZE; + lba += dsk->start; + while (bytes > 0) { + nb = bytes / DEV_BSIZE; + if (nb > READ_BUF_SIZE / DEV_BSIZE) + nb = READ_BUF_SIZE / DEV_BSIZE; + memcpy(dmadat->rdbuf, p, nb * DEV_BSIZE); + if (drvwrite(dsk, dmadat->rdbuf, lba, nb)) + return -1; + p += nb * DEV_BSIZE; + lba += nb; + bytes -= nb * DEV_BSIZE; + } + + return 0; +} + +static int +xfsread(const dnode_phys_t *dnode, off_t *offp, void *buf, size_t nbyte) +{ + if ((size_t)zfs_read(spa, dnode, offp, buf, nbyte) != nbyte) { + printf("Invalid format\n"); + return -1; + } + return 0; +} + +/* + * Read Pad2 (formerly "Boot Block Header") area of the first + * vdev label of the given vdev. + */ +static int +vdev_read_pad2(vdev_t *vdev, char *buf, size_t size) +{ + blkptr_t bp; + char *tmp = zap_scratch; + off_t off = offsetof(vdev_label_t, vl_pad2); + + if (size > VDEV_PAD_SIZE) + size = VDEV_PAD_SIZE; + + BP_ZERO(&bp); + BP_SET_LSIZE(&bp, VDEV_PAD_SIZE); + BP_SET_PSIZE(&bp, VDEV_PAD_SIZE); + BP_SET_CHECKSUM(&bp, ZIO_CHECKSUM_LABEL); + BP_SET_COMPRESS(&bp, ZIO_COMPRESS_OFF); + DVA_SET_OFFSET(BP_IDENTITY(&bp), off); + if (vdev_read_phys(vdev, &bp, tmp, off, 0)) + return (EIO); + memcpy(buf, tmp, size); + return (0); +} + +static int +vdev_clear_pad2(vdev_t *vdev) +{ + char *zeroes = zap_scratch; + uint64_t *end; + off_t off = offsetof(vdev_label_t, vl_pad2); + + memset(zeroes, 0, VDEV_PAD_SIZE); + end = (uint64_t *)(zeroes + VDEV_PAD_SIZE); + /* ZIO_CHECKSUM_LABEL magic and pre-calcualted checksum for all zeros */ + end[-5] = 0x0210da7ab10c7a11; + end[-4] = 0x97f48f807f6e2a3f; + end[-3] = 0xaf909f1658aacefc; + end[-2] = 0xcbd1ea57ff6db48b; + end[-1] = 0x6ec692db0d465fab; + if (vdev_write(vdev, vdev->v_read_priv, off, zeroes, VDEV_PAD_SIZE)) + return (EIO); + return (0); +} + +static void +bios_getmem(void) +{ + uint64_t size; + + /* Parse system memory map */ + v86.ebx = 0; + do { + v86.ctl = V86_FLAGS; + v86.addr = 0x15; /* int 0x15 function 0xe820*/ + v86.eax = 0xe820; + v86.ecx = sizeof(struct bios_smap); + v86.edx = SMAP_SIG; + v86.es = VTOPSEG(&smap); + v86.edi = VTOPOFF(&smap); + v86int(); + if (V86_CY(v86.efl) || (v86.eax != SMAP_SIG)) + break; + /* look for a low-memory segment that's large enough */ + if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0) && + (smap.length >= (512 * 1024))) + bios_basemem = smap.length; + /* look for the first segment in 'extended' memory */ + if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0x100000)) { + bios_extmem = smap.length; + } + + /* + * Look for the largest segment in 'extended' memory beyond + * 1MB but below 4GB. + */ + if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base > 0x100000) && + (smap.base < 0x100000000ull)) { + size = smap.length; + + /* + * If this segment crosses the 4GB boundary, truncate it. + */ + if (smap.base + size > 0x100000000ull) + size = 0x100000000ull - smap.base; + + if (size > high_heap_size) { + high_heap_size = size; + high_heap_base = smap.base; + } + } + } while (v86.ebx != 0); + + /* Fall back to the old compatibility function for base memory */ + if (bios_basemem == 0) { + v86.ctl = 0; + v86.addr = 0x12; /* int 0x12 */ + v86int(); + + bios_basemem = (v86.eax & 0xffff) * 1024; + } + + /* Fall back through several compatibility functions for extended memory */ + if (bios_extmem == 0) { + v86.ctl = V86_FLAGS; + v86.addr = 0x15; /* int 0x15 function 0xe801*/ + v86.eax = 0xe801; + v86int(); + if (!V86_CY(v86.efl)) { + bios_extmem = ((v86.ecx & 0xffff) + ((v86.edx & 0xffff) * 64)) * 1024; + } + } + if (bios_extmem == 0) { + v86.ctl = 0; + v86.addr = 0x15; /* int 0x15 function 0x88*/ + v86.eax = 0x8800; + v86int(); + bios_extmem = (v86.eax & 0xffff) * 1024; + } + + /* + * If we have extended memory and did not find a suitable heap + * region in the SMAP, use the last 3MB of 'extended' memory as a + * high heap candidate. + */ + if (bios_extmem >= HEAP_MIN && high_heap_size < HEAP_MIN) { + high_heap_size = HEAP_MIN; + high_heap_base = bios_extmem + 0x100000 - HEAP_MIN; + } +} + +/* + * Try to detect a device supported by the legacy int13 BIOS + */ +static int +int13probe(int drive) +{ + v86.ctl = V86_FLAGS; + v86.addr = 0x13; + v86.eax = 0x800; + v86.edx = drive; + v86int(); + + if (!V86_CY(v86.efl) && /* carry clear */ + ((v86.edx & 0xff) != (drive & DRV_MASK))) { /* unit # OK */ + if ((v86.ecx & 0x3f) == 0) { /* absurd sector size */ + return(0); /* skip device */ + } + return (1); + } + return(0); +} + +/* + * We call this when we find a ZFS vdev - ZFS consumes the dsk + * structure so we must make a new one. + */ +static struct dsk * +copy_dsk(struct dsk *dsk) +{ + struct dsk *newdsk; + + newdsk = malloc(sizeof(struct dsk)); + *newdsk = *dsk; + return (newdsk); +} + +/* + * Get disk size from eax=0x800 and 0x4800. We need to probe both + * because 0x4800 may not be available and we would like to get more + * or less correct disk size - if it is possible at all. + * Note we do not really want to touch drv.c because that code is shared + * with boot2 and we can not afford to grow that code. + */ +static uint64_t +drvsize_ext(struct dsk *dskp) +{ + uint64_t size, tmp; + int cyl, hds, sec; + + v86.ctl = V86_FLAGS; + v86.addr = 0x13; + v86.eax = 0x800; + v86.edx = dskp->drive; + v86int(); + + /* Don't error out if we get bad sector number, try EDD as well */ + if (V86_CY(v86.efl) || /* carry set */ + (v86.edx & 0xff) <= (unsigned)(dskp->drive & 0x7f)) /* unit # bad */ + return (0); + + cyl = ((v86.ecx & 0xc0) << 2) + ((v86.ecx & 0xff00) >> 8) + 1; + /* Convert max head # -> # of heads */ + hds = ((v86.edx & 0xff00) >> 8) + 1; + sec = v86.ecx & 0x3f; + + size = (uint64_t)cyl * hds * sec; + + /* Determine if we can use EDD with this device. */ + v86.ctl = V86_FLAGS; + v86.addr = 0x13; + v86.eax = 0x4100; + v86.edx = dskp->drive; + v86.ebx = 0x55aa; + v86int(); + if (V86_CY(v86.efl) || /* carry set */ + (v86.ebx & 0xffff) != 0xaa55 || /* signature */ + (v86.ecx & EDD_INTERFACE_FIXED_DISK) == 0) + return (size); + + tmp = drvsize(dskp); + if (tmp > size) + size = tmp; + + return (size); +} + +/* + * The "layered" ioctl to read disk/partition size. Unfortunately + * the zfsboot case is hardest, because we do not have full software + * stack available, so we need to do some manual work here. + */ +uint64_t +ldi_get_size(void *priv) +{ + struct dsk *dskp = priv; + uint64_t size = dskp->size; + + if (dskp->start == 0) + size = drvsize_ext(dskp); + + return (size * DEV_BSIZE); +} + +static void +probe_drive(struct dsk *dsk) +{ +#ifdef GPT + struct gpt_hdr hdr; + struct gpt_ent *ent; + unsigned part, entries_per_sec; + daddr_t slba; +#endif +#if defined(GPT) || defined(LOADER_GELI_SUPPORT) + daddr_t elba; +#endif + + struct dos_partition *dp; + char *sec; + unsigned i; + + /* + * If we find a vdev on the whole disk, stop here. + */ + if (vdev_probe(vdev_read, dsk, NULL) == 0) + return; + +#ifdef LOADER_GELI_SUPPORT + /* + * Taste the disk, if it is GELI encrypted, decrypt it and check to see if + * it is a usable vdev then. Otherwise dig + * out the partition table and probe each slice/partition + * in turn for a vdev or GELI encrypted vdev. + */ + elba = drvsize_ext(dsk); + if (elba > 0) { + elba--; + } + if (geli_taste(vdev_read, dsk, elba) == 0) { + if (geli_havekey(dsk) == 0 || geli_passphrase(&gelipw, dsk->unit, + ':', 0, dsk) == 0) { + if (vdev_probe(vdev_read, dsk, NULL) == 0) { + return; + } + } + } +#endif /* LOADER_GELI_SUPPORT */ + + sec = dmadat->secbuf; + dsk->start = 0; + +#ifdef GPT + /* + * First check for GPT. + */ + if (drvread(dsk, sec, 1, 1)) { + return; + } + memcpy(&hdr, sec, sizeof(hdr)); + if (memcmp(hdr.hdr_sig, GPT_HDR_SIG, sizeof(hdr.hdr_sig)) != 0 || + hdr.hdr_lba_self != 1 || hdr.hdr_revision < 0x00010000 || + hdr.hdr_entsz < sizeof(*ent) || DEV_BSIZE % hdr.hdr_entsz != 0) { + goto trymbr; + } + + /* + * Probe all GPT partitions for the presence of ZFS pools. We + * return the spa_t for the first we find (if requested). This + * will have the effect of booting from the first pool on the + * disk. + * + * If no vdev is found, GELI decrypting the device and try again + */ + entries_per_sec = DEV_BSIZE / hdr.hdr_entsz; + slba = hdr.hdr_lba_table; + elba = slba + hdr.hdr_entries / entries_per_sec; + while (slba < elba) { + dsk->start = 0; + if (drvread(dsk, sec, slba, 1)) + return; + for (part = 0; part < entries_per_sec; part++) { + ent = (struct gpt_ent *)(sec + part * hdr.hdr_entsz); + if (memcmp(&ent->ent_type, &freebsd_zfs_uuid, + sizeof(uuid_t)) == 0) { + dsk->start = ent->ent_lba_start; + dsk->size = ent->ent_lba_end - ent->ent_lba_start + 1; + dsk->slice = part + 1; + dsk->part = 255; + if (vdev_probe(vdev_read, dsk, NULL) == 0) { + /* + * This slice had a vdev. We need a new dsk + * structure now since the vdev now owns this one. + */ + dsk = copy_dsk(dsk); + } +#ifdef LOADER_GELI_SUPPORT + else if (geli_taste(vdev_read, dsk, ent->ent_lba_end - + ent->ent_lba_start) == 0) { + if (geli_havekey(dsk) == 0 || geli_passphrase(&gelipw, + dsk->unit, 'p', dsk->slice, dsk) == 0) { + /* + * This slice has GELI, check it for ZFS. + */ + if (vdev_probe(vdev_read, dsk, NULL) == 0) { + /* + * This slice had a vdev. We need a new dsk + * structure now since the vdev now owns this one. + */ + dsk = copy_dsk(dsk); + } + break; + } + } +#endif /* LOADER_GELI_SUPPORT */ + } + } + slba++; + } + return; +trymbr: +#endif /* GPT */ + + if (drvread(dsk, sec, DOSBBSECTOR, 1)) + return; + dp = (void *)(sec + DOSPARTOFF); + + for (i = 0; i < NDOSPART; i++) { + if (!dp[i].dp_typ) + continue; + dsk->start = dp[i].dp_start; + dsk->size = dp[i].dp_size; + dsk->slice = i + 1; + if (vdev_probe(vdev_read, dsk, NULL) == 0) { + dsk = copy_dsk(dsk); + } +#ifdef LOADER_GELI_SUPPORT + else if (geli_taste(vdev_read, dsk, dp[i].dp_size - + dp[i].dp_start) == 0) { + if (geli_havekey(dsk) == 0 || geli_passphrase(&gelipw, dsk->unit, + 's', i, dsk) == 0) { + /* + * This slice has GELI, check it for ZFS. + */ + if (vdev_probe(vdev_read, dsk, NULL) == 0) { + /* + * This slice had a vdev. We need a new dsk + * structure now since the vdev now owns this one. + */ + dsk = copy_dsk(dsk); + } + break; + } + } +#endif /* LOADER_GELI_SUPPORT */ + } +} + +int +main(void) +{ + dnode_phys_t dn; + off_t off; + struct dsk *dsk; + int autoboot, i; + int nextboot; + int rc; + + dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base); + + bios_getmem(); + + if (high_heap_size > 0) { + heap_end = PTOV(high_heap_base + high_heap_size); + heap_next = PTOV(high_heap_base); + } else { + heap_next = (char *)dmadat + sizeof(*dmadat); + heap_end = (char *)PTOV(bios_basemem); + } + + dsk = malloc(sizeof(struct dsk)); + dsk->drive = *(uint8_t *)PTOV(ARGS); + dsk->type = dsk->drive & DRV_HARD ? TYPE_AD : TYPE_FD; + dsk->unit = dsk->drive & DRV_MASK; + dsk->slice = *(uint8_t *)PTOV(ARGS + 1) + 1; + dsk->part = 0; + dsk->start = 0; + dsk->size = 0; + + bootinfo.bi_version = BOOTINFO_VERSION; + bootinfo.bi_size = sizeof(bootinfo); + bootinfo.bi_basemem = bios_basemem / 1024; + bootinfo.bi_extmem = bios_extmem / 1024; + bootinfo.bi_memsizes_valid++; + bootinfo.bi_bios_dev = dsk->drive; + + bootdev = MAKEBOOTDEV(dev_maj[dsk->type], + dsk->slice, dsk->unit, dsk->part); + + /* Process configuration file */ + + autoboot = 1; + +#ifdef LOADER_GELI_SUPPORT + geli_init(); +#endif + zfs_init(); + + /* + * Probe the boot drive first - we will try to boot from whatever + * pool we find on that drive. + */ + probe_drive(dsk); + + /* + * Probe the rest of the drives that the bios knows about. This + * will find any other available pools and it may fill in missing + * vdevs for the boot pool. + */ +#ifndef VIRTUALBOX + for (i = 0; i < *(unsigned char *)PTOV(BIOS_NUMDRIVES); i++) +#else + for (i = 0; i < MAXBDDEV; i++) +#endif + { + if ((i | DRV_HARD) == *(uint8_t *)PTOV(ARGS)) + continue; + + if (!int13probe(i | DRV_HARD)) + break; + + dsk = malloc(sizeof(struct dsk)); + dsk->drive = i | DRV_HARD; + dsk->type = dsk->drive & TYPE_AD; + dsk->unit = i; + dsk->slice = 0; + dsk->part = 0; + dsk->start = 0; + dsk->size = 0; + probe_drive(dsk); + } + + /* + * The first discovered pool, if any, is the pool. + */ + spa = spa_get_primary(); + if (!spa) { + printf("%s: No ZFS pools located, can't boot\n", BOOTPROG); + for (;;) + ; + } + + primary_spa = spa; + primary_vdev = spa_get_primary_vdev(spa); + + nextboot = 0; + rc = vdev_read_pad2(primary_vdev, cmd, sizeof(cmd)); + if (vdev_clear_pad2(primary_vdev)) + printf("failed to clear pad2 area of primary vdev\n"); + if (rc == 0) { + if (*cmd) { + /* + * We could find an old-style ZFS Boot Block header here. + * Simply ignore it. + */ + if (*(uint64_t *)cmd != 0x2f5b007b10c) { + /* + * Note that parse() is destructive to cmd[] and we also want + * to honor RBX_QUIET option that could be present in cmd[]. + */ + nextboot = 1; + memcpy(cmddup, cmd, sizeof(cmd)); + if (parse_cmd()) { + printf("failed to parse pad2 area of primary vdev\n"); + reboot(); + } + if (!OPT_CHECK(RBX_QUIET)) + printf("zfs nextboot: %s\n", cmddup); + } + /* Do not process this command twice */ + *cmd = 0; + } + } else + printf("failed to read pad2 area of primary vdev\n"); + + /* Mount ZFS only if it's not already mounted via nextboot parsing. */ + if (zfsmount.spa == NULL && + (zfs_spa_init(spa) != 0 || zfs_mount(spa, 0, &zfsmount) != 0)) { + printf("%s: failed to mount default pool %s\n", + BOOTPROG, spa->spa_name); + autoboot = 0; + } else if (zfs_lookup(&zfsmount, PATH_CONFIG, &dn) == 0 || + zfs_lookup(&zfsmount, PATH_DOTCONFIG, &dn) == 0) { + off = 0; + zfs_read(spa, &dn, &off, cmd, sizeof(cmd)); + } + + if (*cmd) { + /* + * Note that parse_cmd() is destructive to cmd[] and we also want + * to honor RBX_QUIET option that could be present in cmd[]. + */ + memcpy(cmddup, cmd, sizeof(cmd)); + if (parse_cmd()) + autoboot = 0; + if (!OPT_CHECK(RBX_QUIET)) + printf("%s: %s\n", PATH_CONFIG, cmddup); + /* Do not process this command twice */ + *cmd = 0; + } + + /* Do not risk waiting at the prompt forever. */ + if (nextboot && !autoboot) + reboot(); + + /* + * Try to exec /boot/loader. If interrupted by a keypress, + * or in case of failure, try to load a kernel directly instead. + */ + + if (autoboot && !*kname) { + memcpy(kname, PATH_LOADER_ZFS, sizeof(PATH_LOADER_ZFS)); + if (!keyhit(3)) { + load(); + memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL)); + } + } + + /* Present the user with the boot2 prompt. */ + + for (;;) { + if (!autoboot || !OPT_CHECK(RBX_QUIET)) { + printf("\nFreeBSD/x86 boot\n"); + if (zfs_rlookup(spa, zfsmount.rootobj, rootname) != 0) + printf("Default: %s/<0x%llx>:%s\n" + "boot: ", + spa->spa_name, zfsmount.rootobj, kname); + else if (rootname[0] != '\0') + printf("Default: %s/%s:%s\n" + "boot: ", + spa->spa_name, rootname, kname); + else + printf("Default: %s:%s\n" + "boot: ", + spa->spa_name, kname); + } + if (ioctrl & IO_SERIAL) + sio_flush(); + if (!autoboot || keyhit(5)) + getstr(cmd, sizeof(cmd)); + else if (!autoboot || !OPT_CHECK(RBX_QUIET)) + putchar('\n'); + autoboot = 0; + if (parse_cmd()) + putchar('\a'); + else + load(); + } +} + +/* XXX - Needed for btxld to link the boot2 binary; do not remove. */ +void +exit(int x) +{ + __exit(x); +} + +void +reboot(void) +{ + __exit(0); +} + +static void +load(void) +{ + union { + struct exec ex; + Elf32_Ehdr eh; + } hdr; + static Elf32_Phdr ep[2]; + static Elf32_Shdr es[2]; + caddr_t p; + dnode_phys_t dn; + off_t off; + uint32_t addr, x; + int fmt, i, j; + + if (zfs_lookup(&zfsmount, kname, &dn)) { + printf("\nCan't find %s\n", kname); + return; + } + off = 0; + if (xfsread(&dn, &off, &hdr, sizeof(hdr))) + return; + if (N_GETMAGIC(hdr.ex) == ZMAGIC) + fmt = 0; + else if (IS_ELF(hdr.eh)) + fmt = 1; + else { + printf("Invalid %s\n", "format"); + return; + } + if (fmt == 0) { + addr = hdr.ex.a_entry & 0xffffff; + p = PTOV(addr); + off = PAGE_SIZE; + if (xfsread(&dn, &off, p, hdr.ex.a_text)) + return; + p += roundup2(hdr.ex.a_text, PAGE_SIZE); + if (xfsread(&dn, &off, p, hdr.ex.a_data)) + return; + p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE); + bootinfo.bi_symtab = VTOP(p); + memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms)); + p += sizeof(hdr.ex.a_syms); + if (hdr.ex.a_syms) { + if (xfsread(&dn, &off, p, hdr.ex.a_syms)) + return; + p += hdr.ex.a_syms; + if (xfsread(&dn, &off, p, sizeof(int))) + return; + x = *(uint32_t *)p; + p += sizeof(int); + x -= sizeof(int); + if (xfsread(&dn, &off, p, x)) + return; + p += x; + } + } else { + off = hdr.eh.e_phoff; + for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) { + if (xfsread(&dn, &off, ep + j, sizeof(ep[0]))) + return; + if (ep[j].p_type == PT_LOAD) + j++; + } + for (i = 0; i < 2; i++) { + p = PTOV(ep[i].p_paddr & 0xffffff); + off = ep[i].p_offset; + if (xfsread(&dn, &off, p, ep[i].p_filesz)) + return; + } + p += roundup2(ep[1].p_memsz, PAGE_SIZE); + bootinfo.bi_symtab = VTOP(p); + if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) { + off = hdr.eh.e_shoff + sizeof(es[0]) * + (hdr.eh.e_shstrndx + 1); + if (xfsread(&dn, &off, &es, sizeof(es))) + return; + for (i = 0; i < 2; i++) { + memcpy(p, &es[i].sh_size, sizeof(es[i].sh_size)); + p += sizeof(es[i].sh_size); + off = es[i].sh_offset; + if (xfsread(&dn, &off, p, es[i].sh_size)) + return; + p += es[i].sh_size; + } + } + addr = hdr.eh.e_entry & 0xffffff; + } + bootinfo.bi_esymtab = VTOP(p); + bootinfo.bi_kernelname = VTOP(kname); + zfsargs.size = sizeof(zfsargs); + zfsargs.pool = zfsmount.spa->spa_guid; + zfsargs.root = zfsmount.rootobj; + zfsargs.primary_pool = primary_spa->spa_guid; +#ifdef LOADER_GELI_SUPPORT + explicit_bzero(gelipw, sizeof(gelipw)); + gelibuf = malloc(sizeof(struct keybuf) + (GELI_MAX_KEYS * sizeof(struct keybuf_ent))); + geli_fill_keybuf(gelibuf); + zfsargs.notapw = '\0'; + zfsargs.keybuf_sentinel = KEYBUF_SENTINEL; + zfsargs.keybuf = gelibuf; +#else + zfsargs.gelipw[0] = '\0'; +#endif + if (primary_vdev != NULL) + zfsargs.primary_vdev = primary_vdev->v_guid; + else + printf("failed to detect primary vdev\n"); + __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK), + bootdev, + KARGS_FLAGS_ZFS | KARGS_FLAGS_EXTARG, + (uint32_t) spa->spa_guid, + (uint32_t) (spa->spa_guid >> 32), + VTOP(&bootinfo), + zfsargs); +} + +static int +zfs_mount_ds(char *dsname) +{ + uint64_t newroot; + spa_t *newspa; + char *q; + + q = strchr(dsname, '/'); + if (q) + *q++ = '\0'; + newspa = spa_find_by_name(dsname); + if (newspa == NULL) { + printf("\nCan't find ZFS pool %s\n", dsname); + return -1; + } + + if (zfs_spa_init(newspa)) + return -1; + + newroot = 0; + if (q) { + if (zfs_lookup_dataset(newspa, q, &newroot)) { + printf("\nCan't find dataset %s in ZFS pool %s\n", + q, newspa->spa_name); + return -1; + } + } + if (zfs_mount(newspa, newroot, &zfsmount)) { + printf("\nCan't mount ZFS dataset\n"); + return -1; + } + spa = newspa; + return (0); +} + +static int +parse_cmd(void) +{ + char *arg = cmd; + char *ep, *p, *q; + const char *cp; + int c, i, j; + + while ((c = *arg++)) { + if (c == ' ' || c == '\t' || c == '\n') + continue; + for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++); + ep = p; + if (*p) + *p++ = 0; + if (c == '-') { + while ((c = *arg++)) { + if (c == 'P') { + if (*(uint8_t *)PTOV(0x496) & 0x10) { + cp = "yes"; + } else { + opts |= OPT_SET(RBX_DUAL) | OPT_SET(RBX_SERIAL); + cp = "no"; + } + printf("Keyboard: %s\n", cp); + continue; + } else if (c == 'S') { + j = 0; + while ((unsigned int)(i = *arg++ - '0') <= 9) + j = j * 10 + i; + if (j > 0 && i == -'0') { + comspeed = j; + break; + } + /* Fall through to error below ('S' not in optstr[]). */ + } + for (i = 0; c != optstr[i]; i++) + if (i == NOPT - 1) + return -1; + opts ^= OPT_SET(flags[i]); + } + ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) : + OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD; + if (ioctrl & IO_SERIAL) { + if (sio_init(115200 / comspeed) != 0) + ioctrl &= ~IO_SERIAL; + } + } if (c == '?') { + dnode_phys_t dn; + + if (zfs_lookup(&zfsmount, arg, &dn) == 0) { + zap_list(spa, &dn); + } + return -1; + } else { + arg--; + + /* + * Report pool status if the comment is 'status'. Lets + * hope no-one wants to load /status as a kernel. + */ + if (!strcmp(arg, "status")) { + spa_all_status(); + return -1; + } + + /* + * If there is "zfs:" prefix simply ignore it. + */ + if (strncmp(arg, "zfs:", 4) == 0) + arg += 4; + + /* + * If there is a colon, switch pools. + */ + q = strchr(arg, ':'); + if (q) { + *q++ = '\0'; + if (zfs_mount_ds(arg) != 0) + return -1; + arg = q; + } + if ((i = ep - arg)) { + if ((size_t)i >= sizeof(kname)) + return -1; + memcpy(kname, arg, i + 1); + } + } + arg = p; + } + return 0; +} diff --git a/stand/i386/zfsboot/zfsldr.S b/stand/i386/zfsboot/zfsldr.S new file mode 100644 index 0000000..3e85e27 --- /dev/null +++ b/stand/i386/zfsboot/zfsldr.S @@ -0,0 +1,283 @@ +/* + * Copyright (c) 1998 Robert Nordier + * All rights reserved. + * + * Redistribution and use in source and binary forms are freely + * permitted provided that the above copyright notice and this + * paragraph and the following disclaimer are duplicated in all + * such forms. + * + * This software is provided "AS IS" and without any express or + * implied warranties, including, without limitation, the implied + * warranties of merchantability and fitness for a particular + * purpose. + * + * $FreeBSD$ + */ + +/* Memory Locations */ + .set MEM_ARG,0x900 # Arguments + .set MEM_ORG,0x7c00 # Origin + .set MEM_BUF,0x8000 # Load area + .set MEM_BTX,0x9000 # BTX start + .set MEM_JMP,0x9010 # BTX entry point + .set MEM_USR,0xa000 # Client start + .set BDA_BOOT,0x472 # Boot howto flag + +/* Partition Constants */ + .set PRT_OFF,0x1be # Partition offset + .set PRT_NUM,0x4 # Partitions + .set PRT_BSD,0xa5 # Partition type + +/* Misc. Constants */ + .set SIZ_PAG,0x1000 # Page size + .set SIZ_SEC,0x200 # Sector size + .set COPY_BLKS,0x8 # Number of blocks + # to copy for boot2 + .set COPY_BLK_SZ,0x8000 # Copy in 32k blocks; must be + # a multiple of 16 bytes + .set NSECT,(COPY_BLK_SZ / SIZ_SEC * COPY_BLKS) + .globl start + .code16 + +/* + * Load the rest of zfsboot2 and BTX up, copy the parts to the right locations, + * and start it all up. + */ + +/* + * Setup the segment registers to flat addressing (segment 0) and setup the + * stack to end just below the start of our code. + */ +start: cld # String ops inc + xor %cx,%cx # Zero + mov %cx,%es # Address + mov %cx,%ds # data + mov %cx,%ss # Set up + mov $start,%sp # stack +/* + * Load the MBR and look for the first FreeBSD slice. We use the fake + * partition entry below that points to the MBR when we call read. + * The first pass looks for the first active FreeBSD slice. The + * second pass looks for the first non-active FreeBSD slice if the + * first one fails. + */ + call check_edd # Make sure EDD works + mov $part4,%si # Dummy partition + xor %eax,%eax # Read MBR + movl $MEM_BUF,%ebx # from first + call read # sector + mov $0x1,%cx # Two passes +main.1: mov $MEM_BUF+PRT_OFF,%si # Partition table + movb $0x1,%dh # Partition +main.2: cmpb $PRT_BSD,0x4(%si) # Our partition type? + jne main.3 # No + jcxz main.5 # If second pass + testb $0x80,(%si) # Active? + jnz main.5 # Yes +main.3: add $0x10,%si # Next entry + incb %dh # Partition + cmpb $0x1+PRT_NUM,%dh # In table? + jb main.2 # Yes + dec %cx # Do two + jcxz main.1 # passes +/* + * If we get here, we didn't find any FreeBSD slices at all, so print an + * error message and die. + */ + mov $msg_part,%si # Message + jmp error # Error + +/* + * Ok, we have a slice and drive in %dx now, so use that to locate and + * load boot2. %si references the start of the slice we are looking + * for, so go ahead and load up the COPY_BLKS*COPY_BLK_SZ/SIZ_SEC sectors + * starting at sector 1024 (i.e. after the two vdev labels). We don't + * have do anything fancy here to allow for an extra copy of boot1 and + * a partition table (compare to this section of the UFS bootstrap) so we + * just load it all at 0x9000. The first part of boot2 is BTX, which wants + * to run at 0x9000. The boot2.bin binary starts right after the end of BTX, + * so we have to figure out where the start of it is and then move the + * binary to 0xc000. Normally, BTX clients start at MEM_USR, or 0xa000, + * but when we use btxld to create zfsboot2, we use an entry point of + * 0x2000. That entry point is relative to MEM_USR; thus boot2.bin + * starts at 0xc000. + * + * The load area and the target area for the client overlap so we have + * to use a decrementing string move. We also play segment register + * games with the destination address for the move so that the client + * can be larger than 16k (which would overflow the zero segment since + * the client starts at 0xc000). + */ +main.5: mov %dx,MEM_ARG # Save args + mov $NSECT,%cx # Sector count + movl $1024,%eax # Offset to boot2 + mov $MEM_BTX,%ebx # Destination buffer +main.6: pushal # Save params + call read # Read disk + popal # Restore + incl %eax # Advance to + add $SIZ_SEC,%ebx # next sector + loop main.6 # If not last, read another + + mov $MEM_BTX,%bx # BTX + mov 0xa(%bx),%si # Get BTX length and set + add %bx,%si # %si to start of boot2 + dec %si # Set %ds:%si to point at the + mov %si,%ax # last byte we want to copy + shr $4,%ax # from boot2, with %si made as + add $(COPY_BLKS*COPY_BLK_SZ/16),%ax # small as possible. + and $0xf,%si # + mov %ax,%ds # + mov $(MEM_USR+2*SIZ_PAG)/16,%ax # Set %es:(-1) to point at + add $(COPY_BLKS*COPY_BLK_SZ/16),%ax # the last byte we + mov %ax,%es # want to copy boot2 into. + mov $COPY_BLKS,%bx # Copy COPY_BLKS 32k blocks +copyloop: + add $COPY_BLK_SZ,%si # Adjust %ds:%si to point at + mov %ds,%ax # the end of the next 32k to + sub $COPY_BLK_SZ/16,%ax # copy from boot2 + mov %ax,%ds + mov $COPY_BLK_SZ-1,%di # Adjust %es:%di to point at + mov %es,%ax # the end of the next 32k into + sub $COPY_BLK_SZ/16,%ax # which we want boot2 copied + mov %ax,%es + mov $COPY_BLK_SZ,%cx # Copy 32k + std + rep movsb + dec %bx + jnz copyloop + mov %cx,%ds # Reset %ds and %es + mov %cx,%es + cld # Back to increment + +/* + * Enable A20 so we can access memory above 1 meg. + * Use the zero-valued %cx as a timeout for embedded hardware which do not + * have a keyboard controller. + */ +seta20: cli # Disable interrupts +seta20.1: dec %cx # Timeout? + jz seta20.3 # Yes + inb $0x64,%al # Get status + testb $0x2,%al # Busy? + jnz seta20.1 # Yes + movb $0xd1,%al # Command: Write + outb %al,$0x64 # output port +seta20.2: inb $0x64,%al # Get status + testb $0x2,%al # Busy? + jnz seta20.2 # Yes + movb $0xdf,%al # Enable + outb %al,$0x60 # A20 +seta20.3: sti # Enable interrupts + + jmp start+MEM_JMP-MEM_ORG # Start BTX + + +/* + * Read a sector from the disk. Sets up an EDD packet on the stack + * and passes it to read. We assume that the destination address is + * always segment-aligned. + * + * %eax - int - LBA to read in relative to partition start + * %ebx - ptr - destination address + * %dl - byte - drive to read from + * %si - ptr - MBR partition entry + */ +read: xor %ecx,%ecx # Get + addl 0x8(%si),%eax # LBA + adc $0,%ecx + pushl %ecx # Starting absolute block + pushl %eax # block number + shr $4,%ebx # Convert to segment + push %bx # Address of + push $0 # transfer buffer + push $0x1 # Read 1 sector + push $0x10 # Size of packet + mov %sp,%si # Packet pointer + mov $0x42,%ah # BIOS: Extended + int $0x13 # read + jc read.1 # If error, fail + lea 0x10(%si),%sp # Clear stack + ret # If success, return +read.1: mov %ah,%al # Format + mov $read_err,%di # error + call hex8 # code + mov $msg_read,%si # Set the error message and + # fall through to the error + # routine +/* + * Print out the error message pointed to by %ds:(%si) followed + * by a prompt, wait for a keypress, and then reboot the machine. + */ +error: callw putstr # Display message + mov $prompt,%si # Display + callw putstr # prompt + xorb %ah,%ah # BIOS: Get + int $0x16 # keypress + movw $0x1234, BDA_BOOT # Do a warm boot + ljmp $0xffff,$0x0 # reboot the machine +/* + * Display a null-terminated string using the BIOS output. + */ +putstr.0: mov $0x7,%bx # Page:attribute + movb $0xe,%ah # BIOS: Display + int $0x10 # character +putstr: lodsb # Get char + testb %al,%al # End of string? + jne putstr.0 # No + ret # To caller +/* + * Check to see if the disk supports EDD. zfsboot requires EDD and does not + * support older C/H/S disk I/O. + */ +check_edd: cmpb $0x80,%dl # Hard drive? + jb check_edd.1 # No, fail to boot + mov $0x55aa,%bx # Magic + push %dx # Save + movb $0x41,%ah # BIOS: Check + int $0x13 # extensions present + pop %dx # Restore + jc check_edd.1 # If error, fail + cmp $0xaa55,%bx # Magic? + jne check_edd.1 # No, so fail + testb $0x1,%cl # Packet interface? + jz check_edd.1 # No, so fail + ret # EDD ok, keep booting +check_edd.1: mov $msg_chs,%si # Warn that CHS is + jmp error # unsupported and fail +/* + * AL to hex, saving the result to [EDI]. + */ +hex8: push %ax # Save + shrb $0x4,%al # Do upper + call hex8.1 # 4 + pop %ax # Restore +hex8.1: andb $0xf,%al # Get lower 4 + cmpb $0xa,%al # Convert + sbbb $0x69,%al # to hex + das # digit + orb $0x20,%al # To lower case + stosb # Save char + ret # (Recursive) + +/* Messages */ + +msg_chs: .asciz "CHS not supported" +msg_read: .ascii "Read error: " +read_err: .asciz "XX" +msg_part: .asciz "Boot error" + +prompt: .asciz "\r\n" + + .org PRT_OFF,0x90 + +/* Partition table */ + + .fill 0x30,0x1,0x0 +part4: .byte 0x80, 0x00, 0x01, 0x00 + .byte 0xa5, 0xfe, 0xff, 0xff + .byte 0x00, 0x00, 0x00, 0x00 + .byte 0x50, 0xc3, 0x00, 0x00 # 50000 sectors long, bleh + + .word 0xaa55 # Magic number diff --git a/stand/i386/zfsloader/Makefile b/stand/i386/zfsloader/Makefile new file mode 100644 index 0000000..a9fa913 --- /dev/null +++ b/stand/i386/zfsloader/Makefile @@ -0,0 +1,7 @@ +# $FreeBSD$ + +LOADER= zfsloader +NEWVERSWHAT= "ZFS enabled bootstrap loader" x86 +HAVE_ZFS= yes + +.include "${.CURDIR}/../loader/Makefile" diff --git a/stand/i386/zfsloader/Makefile.depend b/stand/i386/zfsloader/Makefile.depend new file mode 100644 index 0000000..15b0c98 --- /dev/null +++ b/stand/i386/zfsloader/Makefile.depend @@ -0,0 +1,22 @@ +# $FreeBSD$ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + include \ + include/xlocale \ + lib/libstand \ + sys/boot/ficl32 \ + sys/boot/geli \ + sys/boot/i386/btx/btx \ + sys/boot/i386/btx/btxldr \ + sys/boot/i386/btx/lib \ + sys/boot/i386/libi386 \ + sys/boot/libstand32 \ + sys/boot/zfs \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif |