diff options
Diffstat (limited to 'sys/boot/i386/libi386')
27 files changed, 6246 insertions, 0 deletions
diff --git a/sys/boot/i386/libi386/Makefile b/sys/boot/i386/libi386/Makefile new file mode 100644 index 0000000..3bdb791 --- /dev/null +++ b/sys/boot/i386/libi386/Makefile @@ -0,0 +1,48 @@ +# $FreeBSD$ +# +LIB= i386 +INTERNALLIB= true + +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 gatea20.c \ + i386_copy.c i386_module.c nullconsole.c pxe.c pxetramp.s \ + time.c vidconsole.c amd64_tramp.S + +CFLAGS+= -ffreestanding +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 + +# Include simple terminal emulation (cons25-compatible) +CFLAGS+= -DTERM_EMU + +CFLAGS+= -I${.CURDIR}/../../common -I${.CURDIR}/../btx/lib \ + -I${.CURDIR}/../../../contrib/dev/acpica \ + -I${.CURDIR}/../../.. -I. +# the location of libstand +CFLAGS+= -I${.CURDIR}/../../../../lib/libstand/ + +# Make "machine" required for all objects +# (based on the more complete case in sys/i386/boot/Makefile.inc) +${SRCS:M*.c:R:S/$/.o/g}: machine + +# If it's not there, don't consider it a target +.if exists(${.CURDIR}/../../../i386/include) +beforedepend ${OBJS}: machine + +CLEANFILES+= machine +machine: + ln -sf ${.CURDIR}/../../../i386/include machine + +.endif + +.include <bsd.lib.mk> diff --git a/sys/boot/i386/libi386/amd64_tramp.S b/sys/boot/i386/libi386/amd64_tramp.S new file mode 100644 index 0000000..ff12c66 --- /dev/null +++ b/sys/boot/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 | CR4_PSE), %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 compatability 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/sys/boot/i386/libi386/biosacpi.c b/sys/boot/i386/libi386/biosacpi.c new file mode 100644 index 0000000..b4080bd --- /dev/null +++ b/sys/boot/i386/libi386/biosacpi.c @@ -0,0 +1,130 @@ +/*- + * 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 "acfreebsd.h" +#include "acconfig.h" +#define ACPI_SYSTEM_XFACE +#include "actypes.h" +#include "actbl.h" + +/* + * Detect ACPI and export information about the APCI BIOS into the + * environment. + */ + +static RSDP_DESCRIPTOR *biosacpi_find_rsdp(void); +static RSDP_DESCRIPTOR *biosacpi_search_rsdp(char *base, int length); + +#define RSDP_CHECKSUM_LENGTH 20 + +void +biosacpi_detect(void) +{ + RSDP_DESCRIPTOR *rsdp; + char buf[16]; + int revision; + + /* XXX check the BIOS datestamp */ + + /* locate and validate the RSDP */ + if ((rsdp = biosacpi_find_rsdp()) == NULL) + return; + + /* export values from the RSDP */ + revision = rsdp->Revision; + if (revision == 0) + revision = 1; + sprintf(buf, "%d", revision); + setenv("hint.acpi.0.revision", buf, 1); + sprintf(buf, "%6s", rsdp->OemId); + buf[6] = '\0'; + setenv("hint.acpi.0.oem", buf, 1); + sprintf(buf, "0x%08x", rsdp->RsdtPhysicalAddress); + setenv("hint.acpi.0.rsdt", buf, 1); + if (revision >= 2) { + /* XXX extended checksum? */ + sprintf(buf, "0x%016llx", rsdp->XsdtPhysicalAddress); + setenv("hint.acpi.0.xsdt", buf, 1); + sprintf(buf, "%d", rsdp->Length); + setenv("hint.acpi.0.xsdt_length", buf, 1); + } + /* XXX other tables? */ + + setenv("acpi_load", "YES", 1); +} + +/* + * Find the RSDP in low memory. + */ +static RSDP_DESCRIPTOR * +biosacpi_find_rsdp(void) +{ + RSDP_DESCRIPTOR *rsdp; + + /* search the EBDA */ + if ((rsdp = biosacpi_search_rsdp((char *)0, 0x400)) != NULL) + return(rsdp); + + /* search the BIOS space */ + if ((rsdp = biosacpi_search_rsdp((char *)0xe0000, 0x20000)) != NULL) + return(rsdp); + + return(NULL); +} + +static RSDP_DESCRIPTOR * +biosacpi_search_rsdp(char *base, int length) +{ + RSDP_DESCRIPTOR *rsdp; + u_int8_t *cp, sum; + int ofs, idx; + + /* search on 16-byte boundaries */ + for (ofs = 0; ofs < length; ofs += 16) { + rsdp = (RSDP_DESCRIPTOR *)(base + ofs); + + /* compare signature, validate checksum */ + if (!strncmp(rsdp->Signature, RSDP_SIG, strlen(RSDP_SIG))) { + cp = (u_int8_t *)rsdp; + sum = 0; + for (idx = 0; idx < RSDP_CHECKSUM_LENGTH; idx++) + sum += *(cp + idx); + if (sum != 0) { + printf("acpi: bad RSDP checksum (%d)\n", sum); + continue; + } + return(rsdp); + } + } + return(NULL); +} diff --git a/sys/boot/i386/libi386/bioscd.c b/sys/boot/i386/libi386/bioscd.c new file mode 100644 index 0000000..7259ff5 --- /dev/null +++ b/sys/boot/i386/libi386/bioscd.c @@ -0,0 +1,359 @@ +/*- + * 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 <machine/psl.h> + +#include <stdarg.h> + +#include <bootstrap.h> +#include <btxv86.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; +} bcinfo [MAXBCDEV]; +static int nbcinfo = 0; + +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 void 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++; + return(0); +} + +/* + * Print information about disks + */ +static void +bc_print(int verbose) +{ + int i; + char line[80]; + + for (i = 0; i < nbcinfo; i++) { + sprintf(line, " cd%d: Device 0x%x\n", i, + bcinfo[i].bc_sp.sp_devicespec); + pager_output(line); + } +} + +/* + * 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; + int error; + + va_start(ap, f); + dev = va_arg(ap, struct i386_devdesc *); + va_end(ap); + if (dev->d_kind.bioscd.unit >= nbcinfo) { + DEBUG("attempt to open nonexistent disk"); + return(ENXIO); + } + + return(0); +} + +static int +bc_close(struct open_file *f) +{ + + return(0); +} + +static int +bc_strategy(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_READ) + return(EROFS); + dev = (struct i386_devdesc *)devdata; + unit = dev->d_kind.bioscd.unit; + blks = size / BIOSCD_SECSIZE; + if (dblk % (BIOSCD_SECSIZE / DEV_BSIZE) != 0) + return (EINVAL); + dblk /= (BIOSCD_SECSIZE / DEV_BSIZE); + DEBUG("read %d from %d to %p", blks, dblk, buf); + + if (rsize) + *rsize = 0; + if (blks && bc_read(unit, dblk, blks, buf)) { + DEBUG("read error"); + return (EIO); + } +#ifdef BD_SUPPORT_FRAGS + DEBUG("bc_strategy: frag read %d from %d+%d to %p", + fragsize, dblk, blks, buf + (blks * BIOSCD_SECSIZE)); + if (fragsize && bc_read(unit, dblk + blks, 1, fragsize)) { + DEBUG("frag read error"); + return(EIO); + } + bcopy(fragbuf, buf + (blks * BIOSCD_SECSIZE), fragsize); +#endif + if (rsize) + *rsize = size; + return (0); +} + +static int +bc_read(int unit, daddr_t dblk, int blks, caddr_t dest) +{ + u_int result, resid, retry; + static unsigned short packet[8]; + 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); + + biosdev = bc_unit2bios(unit); + /* + * 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[0] = 0x10; + packet[1] = blks; + packet[2] = VTOPOFF(dest); + packet[3] = VTOPSEG(dest); + packet[4] = dblk & 0xffff; + packet[5] = dblk >> 16; + packet[6] = 0; + packet[7] = 0; + v86.ctl = V86_FLAGS; + v86.addr = 0x13; + v86.eax = 0x4200; + v86.edx = biosdev; + v86.ds = VTOPSEG(packet); + v86.esi = VTOPOFF(packet); + v86int(); + result = (v86.efl & PSL_C); + if (result == 0) + break; + } + +#ifdef DISK_DEBUG + error = (v86.eax >> 8) & 0xff; +#endif + DEBUG("%d sectors from %ld to %p (0x%x) %s", blks, dblk, dest, + VTOP(dest), result ? "failed" : "ok"); + DEBUG("unit %d status 0x%x", unit, error); + +/* hexdump(dest, (blks * BIOSCD_SECSIZE)); */ + return(0); +} + +/* + * 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_kind.bioscd.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, 0, unit, 0); + DEBUG("dev is 0x%x\n", rootdev); + return(rootdev); +} diff --git a/sys/boot/i386/libi386/biosdisk.c b/sys/boot/i386/libi386/biosdisk.c new file mode 100644 index 0000000..6d381d4 --- /dev/null +++ b/sys/boot/i386/libi386/biosdisk.c @@ -0,0 +1,1233 @@ +/*- + * 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$"); + +/* + * BIOS disk device handling. + * + * Ideas and algorithms from: + * + * - NetBSD libi386/biosdisk.c + * - FreeBSD biosboot/disk.c + * + */ + +#include <stand.h> + +#include <sys/disklabel.h> +#include <sys/diskmbr.h> +#include <machine/bootinfo.h> + +#include <stdarg.h> + +#include <bootstrap.h> +#include <btxv86.h> +#include "libi386.h" + +#define BIOS_NUMDRIVES 0x475 +#define BIOSDISK_SECSIZE 512 +#define BUFSIZE (1 * BIOSDISK_SECSIZE) +#define MAXBDDEV MAXDEV + +#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 + +struct open_disk { + int od_dkunit; /* disk unit number */ + int od_unit; /* BIOS unit number */ + int od_cyl; /* BIOS geometry */ + int od_hds; + int od_sec; + int od_boff; /* block offset from beginning of BIOS disk */ + int od_flags; +#define BD_MODEINT13 0x0000 +#define BD_MODEEDD1 0x0001 +#define BD_MODEEDD3 0x0002 +#define BD_MODEMASK 0x0003 +#define BD_FLOPPY 0x0004 +#define BD_LABELOK 0x0008 +#define BD_PARTTABOK 0x0010 + struct disklabel od_disklabel; + int od_nslices; /* slice count */ + struct dos_partition od_slicetab[NEXTDOSPART]; +}; + +/* + * List of BIOS devices, translation from disk unit number to + * BIOS unit number. + */ +static struct bdinfo +{ + int bd_unit; /* BIOS unit number */ + int bd_flags; + int bd_type; /* BIOS 'drive type' (floppy only) */ +} bdinfo [MAXBDDEV]; +static int nbdinfo = 0; + +static int bd_getgeom(struct open_disk *od); +static int bd_read(struct open_disk *od, daddr_t dblk, int blks, + caddr_t dest); +static int bd_write(struct open_disk *od, daddr_t dblk, int blks, + caddr_t dest); + +static int bd_int13probe(struct bdinfo *bd); + +static void bd_printslice(struct open_disk *od, struct dos_partition *dp, + char *prefix, int verbose); +static void bd_printbsdslice(struct open_disk *od, daddr_t offset, + char *prefix, int verbose); + +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 void bd_print(int verbose); + +struct devsw biosdisk = { + "disk", + DEVT_DISK, + bd_init, + bd_strategy, + bd_open, + bd_close, + noioctl, + bd_print, + NULL +}; + +static int bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev); +static void bd_closedisk(struct open_disk *od); +static int bd_bestslice(struct open_disk *od); +static void bd_checkextended(struct open_disk *od, int slicenum); + +/* + * 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; + + /* sequence 0, 0x80 */ + for (base = 0; base <= 0x80; base += 0x80) { + for (unit = base; (nbdinfo < MAXBDDEV); unit++) { + /* check the BIOS equipment list for number of fixed disks */ + if((base == 0x80) && + (nfd >= *(unsigned char *)PTOV(BIOS_NUMDRIVES))) + break; + + 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++; + } + } + return(0); +} + +/* + * Try to detect a device supported by the legacy int13 BIOS + */ +static int +bd_int13probe(struct bdinfo *bd) +{ + v86.ctl = V86_FLAGS; + v86.addr = 0x13; + v86.eax = 0x800; + v86.edx = bd->bd_unit; + v86int(); + + if (!(v86.efl & 0x1) && /* carry clear */ + ((v86.edx & 0xff) > ((unsigned)bd->bd_unit & 0x7f))) { /* unit # OK */ + bd->bd_flags |= BD_MODEINT13; + bd->bd_type = v86.ebx & 0xff; + + /* Determine if we can use EDD with this device. */ + v86.eax = 0x4100; + v86.edx = bd->bd_unit; + v86.ebx = 0x55aa; + v86int(); + if (!(v86.efl & 0x1) && /* carry clear */ + ((v86.ebx & 0xffff) == 0xaa55) && /* signature */ + (v86.ecx & 0x1)) { /* packets mode ok */ + bd->bd_flags |= BD_MODEEDD1; + if((v86.eax & 0xff00) > 0x300) + bd->bd_flags |= BD_MODEEDD3; + } + return(1); + } + return(0); +} + +/* + * Print information about disks + */ +static void +bd_print(int verbose) +{ + int i, j; + char line[80]; + struct i386_devdesc dev; + struct open_disk *od; + struct dos_partition *dptr; + + for (i = 0; i < nbdinfo; i++) { + sprintf(line, " disk%d: BIOS drive %c:\n", i, + (bdinfo[i].bd_unit < 0x80) ? ('A' + bdinfo[i].bd_unit) : ('C' + bdinfo[i].bd_unit - 0x80)); + pager_output(line); + + /* try to open the whole disk */ + dev.d_kind.biosdisk.unit = i; + dev.d_kind.biosdisk.slice = -1; + dev.d_kind.biosdisk.partition = -1; + + if (!bd_opendisk(&od, &dev)) { + + /* Do we have a partition table? */ + if (od->od_flags & BD_PARTTABOK) { + dptr = &od->od_slicetab[0]; + + /* Check for a "dedicated" disk */ + if ((dptr[3].dp_typ == DOSPTYP_386BSD) && + (dptr[3].dp_start == 0) && + (dptr[3].dp_size == 50000)) { + sprintf(line, " disk%d", i); + bd_printbsdslice(od, 0, line, verbose); + } else { + for (j = 0; j < od->od_nslices; j++) { + sprintf(line, " disk%ds%d", i, j + 1); + bd_printslice(od, &dptr[j], line, verbose); + } + } + } + bd_closedisk(od); + } + } +} + +/* + * Print information about slices on a disk. For the size calculations we + * assume a 512 byte sector. + */ +static void +bd_printslice(struct open_disk *od, struct dos_partition *dp, char *prefix, + int verbose) +{ + char line[80]; + + switch (dp->dp_typ) { + case DOSPTYP_386BSD: + bd_printbsdslice(od, (daddr_t)dp->dp_start, prefix, verbose); + return; + case DOSPTYP_LINSWP: + if (verbose) + sprintf(line, "%s: Linux swap %.6dMB (%d - %d)\n", + prefix, dp->dp_size / 2048, + dp->dp_start, dp->dp_start + dp->dp_size); + else + sprintf(line, "%s: Linux swap\n", prefix); + break; + case DOSPTYP_LINUX: + /* + * XXX + * read the superblock to confirm this is an ext2fs partition? + */ + if (verbose) + sprintf(line, "%s: ext2fs %.6dMB (%d - %d)\n", prefix, + dp->dp_size / 2048, dp->dp_start, + dp->dp_start + dp->dp_size); + else + sprintf(line, "%s: ext2fs\n", prefix); + break; + case 0x00: /* unused partition */ + case DOSPTYP_EXT: + return; + case 0x01: + if (verbose) + sprintf(line, "%s: FAT-12 %.6dMB (%d - %d)\n", prefix, + dp->dp_size / 2048, dp->dp_start, + dp->dp_start + dp->dp_size); + else + sprintf(line, "%s: FAT-12\n", prefix); + break; + case 0x04: + case 0x06: + case 0x0e: + if (verbose) + sprintf(line, "%s: FAT-16 %.6dMB (%d - %d)\n", prefix, + dp->dp_size / 2048, dp->dp_start, + dp->dp_start + dp->dp_size); + else + sprintf(line, "%s: FAT-16\n", prefix); + break; + case 0x0b: + case 0x0c: + if (verbose) + sprintf(line, "%s: FAT-32 %.6dMB (%d - %d)\n", prefix, + dp->dp_size / 2048, dp->dp_start, + dp->dp_start + dp->dp_size); + else + sprintf(line, "%s: FAT-32\n", prefix); + break; + default: + if (verbose) + sprintf(line, "%s: Unknown fs: 0x%x %.6dMB (%d - %d)\n", + prefix, dp->dp_typ, dp->dp_size / 2048, + dp->dp_start, dp->dp_start + dp->dp_size); + else + sprintf(line, "%s: Unknown fs: 0x%x\n", prefix, + dp->dp_typ); + } + pager_output(line); +} + +/* + * Print out each valid partition in the disklabel of a FreeBSD slice. + * For size calculations, we assume a 512 byte sector size. + */ +static void +bd_printbsdslice(struct open_disk *od, daddr_t offset, char *prefix, + int verbose) +{ + char line[80]; + char buf[BIOSDISK_SECSIZE]; + struct disklabel *lp; + int i; + + /* read disklabel */ + if (bd_read(od, offset + LABELSECTOR, 1, buf)) + return; + lp =(struct disklabel *)(&buf[0]); + if (lp->d_magic != DISKMAGIC) { + sprintf(line, "%s: FFS bad disklabel\n", prefix); + pager_output(line); + return; + } + + /* Print partitions */ + for (i = 0; i < lp->d_npartitions; i++) { + /* + * For each partition, make sure we know what type of fs it is. If + * not, then skip it. However, since floppies often have bogus + * fstypes, print the 'a' partition on a floppy even if it is marked + * unused. + */ + if ((lp->d_partitions[i].p_fstype == FS_BSDFFS) || + (lp->d_partitions[i].p_fstype == FS_SWAP) || + (lp->d_partitions[i].p_fstype == FS_VINUM) || + ((lp->d_partitions[i].p_fstype == FS_UNUSED) && + (od->od_flags & BD_FLOPPY) && (i == 0))) { + + /* Only print out statistics in verbose mode */ + if (verbose) + sprintf(line, " %s%c: %s %.6dMB (%d - %d)\n", prefix, 'a' + i, + (lp->d_partitions[i].p_fstype == FS_SWAP) ? "swap" : + (lp->d_partitions[i].p_fstype == FS_VINUM) ? "vinum" : + "FFS", + lp->d_partitions[i].p_size / 2048, + lp->d_partitions[i].p_offset, + lp->d_partitions[i].p_offset + lp->d_partitions[i].p_size); + else + sprintf(line, " %s%c: %s\n", prefix, 'a' + i, + (lp->d_partitions[i].p_fstype == FS_SWAP) ? "swap" : + (lp->d_partitions[i].p_fstype == FS_VINUM) ? "vinum" : + "FFS"); + pager_output(line); + } + } +} + + +/* + * 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, ...) +{ + 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); + if ((error = bd_opendisk(&od, dev))) + return(error); + + /* + * Save our context + */ + ((struct i386_devdesc *)(f->f_devdata))->d_kind.biosdisk.data = od; + DEBUG("open_disk %p, partition at 0x%x", od, od->od_boff); + return(0); +} + +static int +bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev) +{ + struct dos_partition *dptr; + struct disklabel *lp; + struct open_disk *od; + int sector, slice, i; + int error; + char buf[BUFSIZE]; + + if (dev->d_kind.biosdisk.unit >= nbdinfo) { + DEBUG("attempt to open nonexistent disk"); + return(ENXIO); + } + + od = (struct open_disk *)malloc(sizeof(struct open_disk)); + if (!od) { + DEBUG("no memory"); + return (ENOMEM); + } + + /* Look up BIOS unit number, intialise open_disk structure */ + od->od_dkunit = dev->d_kind.biosdisk.unit; + od->od_unit = bdinfo[od->od_dkunit].bd_unit; + od->od_flags = bdinfo[od->od_dkunit].bd_flags; + od->od_boff = 0; + od->od_nslices = 0; + error = 0; + DEBUG("open '%s', unit 0x%x slice %d partition %c", + i386_fmtdev(dev), dev->d_kind.biosdisk.unit, + dev->d_kind.biosdisk.slice, dev->d_kind.biosdisk.partition + 'a'); + + /* Get geometry for this open (removable device may have changed) */ + if (bd_getgeom(od)) { + DEBUG("can't get geometry"); + error = ENXIO; + goto out; + } + + /* + * Following calculations attempt to determine the correct value + * for d->od_boff by looking for the slice and partition specified, + * or searching for reasonable defaults. + */ + + /* + * Find the slice in the DOS slice table. + */ + if (bd_read(od, 0, 1, buf)) { + DEBUG("error reading MBR"); + error = EIO; + goto out; + } + + /* + * Check the slice table magic. + */ + if (((u_char)buf[0x1fe] != 0x55) || ((u_char)buf[0x1ff] != 0xaa)) { + /* If a slice number was explicitly supplied, this is an error */ + if (dev->d_kind.biosdisk.slice > 0) { + DEBUG("no slice table/MBR (no magic)"); + error = ENOENT; + goto out; + } + sector = 0; + goto unsliced; /* may be a floppy */ + } + + /* + * copy the partition table, then pick up any extended partitions. + */ + bcopy(buf + DOSPARTOFF, &od->od_slicetab, + sizeof(struct dos_partition) * NDOSPART); + od->od_nslices = 4; /* extended slices start here */ + for (i = 0; i < NDOSPART; i++) + bd_checkextended(od, i); + od->od_flags |= BD_PARTTABOK; + dptr = &od->od_slicetab[0]; + + /* Is this a request for the whole disk? */ + if (dev->d_kind.biosdisk.slice == -1) { + sector = 0; + goto unsliced; + } + + /* + * if a slice number was supplied but not found, this is an error. + */ + if (dev->d_kind.biosdisk.slice > 0) { + slice = dev->d_kind.biosdisk.slice - 1; + if (slice >= od->od_nslices) { + DEBUG("slice %d not found", slice); + error = ENOENT; + goto out; + } + } + + /* + * Check for the historically bogus MBR found on true dedicated disks + */ + if ((dptr[3].dp_typ == DOSPTYP_386BSD) && + (dptr[3].dp_start == 0) && + (dptr[3].dp_size == 50000)) { + sector = 0; + goto unsliced; + } + + /* Try to auto-detect the best slice; this should always give a slice number */ + if (dev->d_kind.biosdisk.slice == 0) { + slice = bd_bestslice(od); + if (slice == -1) { + error = ENOENT; + goto out; + } + dev->d_kind.biosdisk.slice = slice; + } + + dptr = &od->od_slicetab[0]; + /* + * Accept the supplied slice number unequivocally (we may be looking + * at a DOS partition). + */ + dptr += (dev->d_kind.biosdisk.slice - 1); /* we number 1-4, offsets are 0-3 */ + sector = dptr->dp_start; + DEBUG("slice entry %d at %d, %d sectors", dev->d_kind.biosdisk.slice - 1, sector, dptr->dp_size); + + /* + * If we are looking at a BSD slice, and the partition is < 0, assume the 'a' partition + */ + if ((dptr->dp_typ == DOSPTYP_386BSD) && (dev->d_kind.biosdisk.partition < 0)) + dev->d_kind.biosdisk.partition = 0; + + unsliced: + /* + * Now we have the slice offset, look for the partition in the disklabel if we have + * a partition to start with. + * + * XXX we might want to check the label checksum. + */ + if (dev->d_kind.biosdisk.partition < 0) { + od->od_boff = sector; /* no partition, must be after the slice */ + DEBUG("opening raw slice"); + } else { + + if (bd_read(od, sector + LABELSECTOR, 1, buf)) { + DEBUG("error reading disklabel"); + error = EIO; + goto out; + } + DEBUG("copy %d bytes of label from %p to %p", sizeof(struct disklabel), buf + LABELOFFSET, &od->od_disklabel); + bcopy(buf + LABELOFFSET, &od->od_disklabel, sizeof(struct disklabel)); + lp = &od->od_disklabel; + od->od_flags |= BD_LABELOK; + + if (lp->d_magic != DISKMAGIC) { + DEBUG("no disklabel"); + error = ENOENT; + goto out; + } + if (dev->d_kind.biosdisk.partition >= lp->d_npartitions) { + DEBUG("partition '%c' exceeds partitions in table (a-'%c')", + 'a' + dev->d_kind.biosdisk.partition, 'a' + lp->d_npartitions); + error = EPART; + goto out; + + } + +#ifdef DISK_DEBUG + /* Complain if the partition is unused unless this is a floppy. */ + if ((lp->d_partitions[dev->d_kind.biosdisk.partition].p_fstype == FS_UNUSED) && + !(od->od_flags & BD_FLOPPY)) + DEBUG("warning, partition marked as unused"); +#endif + + od->od_boff = + lp->d_partitions[dev->d_kind.biosdisk.partition].p_offset - + lp->d_partitions[RAW_PART].p_offset + + sector; + } + + out: + if (error) { + free(od); + } else { + *odp = od; /* return the open disk */ + } + return(error); +} + +static void +bd_checkextended(struct open_disk *od, int slicenum) +{ + char buf[BIOSDISK_SECSIZE]; + struct dos_partition *dp; + u_int base; + int i, start, end; + + dp = &od->od_slicetab[slicenum]; + start = od->od_nslices; + + if (dp->dp_size == 0) + goto done; + if (dp->dp_typ != DOSPTYP_EXT) + goto done; + if (bd_read(od, (daddr_t)dp->dp_start, 1, buf)) + goto done; + if (((u_char)buf[0x1fe] != 0x55) || ((u_char)buf[0x1ff] != 0xaa)) { + DEBUG("no magic in extended table"); + goto done; + } + base = dp->dp_start; + dp = (struct dos_partition *)(&buf[DOSPARTOFF]); + for (i = 0; i < NDOSPART; i++, dp++) { + if (dp->dp_size == 0) + continue; + if (od->od_nslices == NEXTDOSPART) + goto done; + dp->dp_start += base; + bcopy(dp, &od->od_slicetab[od->od_nslices], sizeof(*dp)); + od->od_nslices++; + } + end = od->od_nslices; + + /* + * now, recursively check the slices we just added + */ + for (i = start; i < end; i++) + bd_checkextended(od, i); +done: + return; +} + +/* + * Search for a slice with the following preferences: + * + * 1: Active FreeBSD slice + * 2: Non-active FreeBSD slice + * 3: Active Linux slice + * 4: non-active Linux slice + * 5: Active FAT/FAT32 slice + * 6: non-active FAT/FAT32 slice + */ +#define PREF_RAWDISK 0 +#define PREF_FBSD_ACT 1 +#define PREF_FBSD 2 +#define PREF_LINUX_ACT 3 +#define PREF_LINUX 4 +#define PREF_DOS_ACT 5 +#define PREF_DOS 6 +#define PREF_NONE 7 + +/* + * slicelimit is in the range 0 .. NDOSPART + */ +static int +bd_bestslice(struct open_disk *od) +{ + struct dos_partition *dp; + int pref, preflevel; + int i, prefslice; + + prefslice = 0; + preflevel = PREF_NONE; + + dp = &od->od_slicetab[0]; + for (i = 0; i < od->od_nslices; i++, dp++) { + + switch (dp->dp_typ) { + case DOSPTYP_386BSD: /* FreeBSD */ + pref = dp->dp_flag & 0x80 ? PREF_FBSD_ACT : PREF_FBSD; + break; + + case DOSPTYP_LINUX: + pref = dp->dp_flag & 0x80 ? PREF_LINUX_ACT : PREF_LINUX; + break; + + case 0x01: /* DOS/Windows */ + case 0x04: + case 0x06: + case 0x0b: + case 0x0c: + case 0x0e: + pref = dp->dp_flag & 0x80 ? PREF_DOS_ACT : PREF_DOS; + break; + + default: + pref = PREF_NONE; + } + if (pref < preflevel) { + preflevel = pref; + prefslice = i + 1; + } + } + return (prefslice); +} + +static int +bd_close(struct open_file *f) +{ + struct open_disk *od = (struct open_disk *)(((struct i386_devdesc *)(f->f_devdata))->d_kind.biosdisk.data); + + bd_closedisk(od); + return(0); +} + +static void +bd_closedisk(struct open_disk *od) +{ + DEBUG("open_disk %p", od); +#if 0 + /* XXX is this required? (especially if disk already open...) */ + if (od->od_flags & BD_FLOPPY) + delay(3000000); +#endif + free(od); +} + +static int +bd_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, size_t *rsize) +{ + struct bcache_devdata bcd; + struct open_disk *od = (struct open_disk *)(((struct i386_devdesc *)devdata)->d_kind.biosdisk.data); + + bcd.dv_strategy = bd_realstrategy; + bcd.dv_devdata = devdata; + return(bcache_strategy(&bcd, od->od_unit, rw, dblk+od->od_boff, size, buf, rsize)); +} + +static int +bd_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, size_t *rsize) +{ + struct open_disk *od = (struct open_disk *)(((struct i386_devdesc *)devdata)->d_kind.biosdisk.data); + int blks; +#ifdef BD_SUPPORT_FRAGS + char fragbuf[BIOSDISK_SECSIZE]; + size_t fragsize; + + fragsize = size % BIOSDISK_SECSIZE; +#else + if (size % BIOSDISK_SECSIZE) + panic("bd_strategy: %d bytes I/O not multiple of block size", size); +#endif + + DEBUG("open_disk %p", od); + + + switch(rw){ + case F_READ: + blks = size / BIOSDISK_SECSIZE; + DEBUG("read %d from %d to %p", blks, dblk, buf); + + if (rsize) + *rsize = 0; + if (blks && bd_read(od, dblk, blks, buf)) { + DEBUG("read error"); + return (EIO); + } +#ifdef BD_SUPPORT_FRAGS + 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 + if (rsize) + *rsize = size; + return (0); + break; + + case F_WRITE : + blks = size / BIOSDISK_SECSIZE; + DEBUG("write %d from %d to %p", blks, dblk, buf); + + if (rsize) + *rsize = 0; + if (blks && bd_write(od, dblk, blks, buf)) { + DEBUG("write error"); + return (EIO); + } +#ifdef BD_SUPPORT_FRAGS + if(fragsize) { + DEBUG("Attempted to write a frag"); + return (EIO); + } +#endif + + if (rsize) + *rsize = size; + return (0); + default: + /* DO NOTHING */ + } + + return EROFS; +} + +/* Max number of sectors to bounce-buffer if the request crosses a 64k boundary */ +#define FLOPPY_BOUNCEBUF 18 + +static int +bd_read(struct open_disk *od, daddr_t dblk, int blks, caddr_t dest) +{ + u_int x, bpc, cyl, hd, sec, result, resid, retry, maxfer; + caddr_t p, xp, bbuf, breg; + + /* Just in case some idiot actually tries to read -1 blocks... */ + if (blks < 0) + return (-1); + + bpc = (od->od_sec * od->od_hds); /* blocks per cylinder */ + resid = blks; + p = dest; + + /* Decide whether we have to bounce */ + if ((od->od_unit < 0x80) && + ((VTOP(dest) >> 16) != (VTOP(dest + blks * BIOSDISK_SECSIZE) >> 16))) { + + /* + * There is a 64k physical boundary somewhere in the destination buffer, 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 = min(FLOPPY_BOUNCEBUF, (unsigned)blks); + bbuf = malloc(x * 2 * BIOSDISK_SECSIZE); + if (((u_int32_t)VTOP(bbuf) & 0xffff0000) == ((u_int32_t)VTOP(dest + x * BIOSDISK_SECSIZE) & 0xffff0000)) { + breg = bbuf; + } else { + breg = bbuf + x * BIOSDISK_SECSIZE; + } + maxfer = x; /* limit transfers to bounce region size */ + } else { + breg = bbuf = NULL; + maxfer = 0; + } + + while (resid > 0) { + x = dblk; + cyl = x / bpc; /* block # / blocks per cylinder */ + x %= bpc; /* block offset into cylinder */ + hd = x / od->od_sec; /* offset / blocks per track */ + sec = x % od->od_sec; /* offset into track */ + + /* play it safe and don't cross track boundaries (XXX this is probably unnecessary) */ + x = min(od->od_sec - sec, resid); + if (maxfer > 0) + x = min(x, maxfer); /* fit bounce buffer */ + + /* where do we transfer to? */ + xp = bbuf == NULL ? p : breg; + + /* correct sector number for 1-based BIOS numbering */ + sec++; + + /* 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 = od->od_unit; + v86int(); + } + + if(cyl > 1023) { + /* use EDD if the disk supports it, otherwise, return error */ + if(od->od_flags & BD_MODEEDD1) { + static unsigned short packet[8]; + + packet[0] = 0x10; + packet[1] = x; + packet[2] = VTOPOFF(xp); + packet[3] = VTOPSEG(xp); + packet[4] = dblk & 0xffff; + packet[5] = dblk >> 16; + packet[6] = 0; + packet[7] = 0; + v86.ctl = V86_FLAGS; + v86.addr = 0x13; + v86.eax = 0x4200; + v86.edx = od->od_unit; + v86.ds = VTOPSEG(packet); + v86.esi = VTOPOFF(packet); + v86int(); + result = (v86.efl & 0x1); + if(result == 0) + break; + } else { + result = 1; + break; + } + } else { + /* Use normal CHS addressing */ + v86.ctl = V86_FLAGS; + v86.addr = 0x13; + v86.eax = 0x200 | x; + v86.ecx = ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec; + v86.edx = (hd << 8) | od->od_unit; + v86.es = VTOPSEG(xp); + v86.ebx = VTOPOFF(xp); + v86int(); + result = (v86.efl & 0x1); + if (result == 0) + break; + } + } + + DEBUG("%d sectors from %d/%d/%d to %p (0x%x) %s", x, cyl, hd, sec - 1, p, VTOP(p), result ? "failed" : "ok"); + /* BUG here, cannot use v86 in printf because putchar uses it too */ + DEBUG("ax = 0x%04x cx = 0x%04x dx = 0x%04x status 0x%x", + 0x200 | x, ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec, (hd << 8) | od->od_unit, (v86.eax >> 8) & 0xff); + if (result) { + if (bbuf != NULL) + free(bbuf); + return(-1); + } + if (bbuf != NULL) + bcopy(breg, p, x * BIOSDISK_SECSIZE); + p += (x * BIOSDISK_SECSIZE); + dblk += x; + resid -= x; + } + +/* hexdump(dest, (blks * BIOSDISK_SECSIZE)); */ + if (bbuf != NULL) + free(bbuf); + return(0); +} + + +static int +bd_write(struct open_disk *od, daddr_t dblk, int blks, caddr_t dest) +{ + u_int x, bpc, cyl, hd, sec, result, resid, retry, maxfer; + caddr_t p, xp, bbuf, breg; + + /* Just in case some idiot actually tries to read -1 blocks... */ + if (blks < 0) + return (-1); + + bpc = (od->od_sec * od->od_hds); /* blocks per cylinder */ + resid = blks; + p = dest; + + /* Decide whether we have to bounce */ + if ((od->od_unit < 0x80) && + ((VTOP(dest) >> 16) != (VTOP(dest + blks * BIOSDISK_SECSIZE) >> 16))) { + + /* + * There is a 64k physical boundary somewhere in the destination buffer, 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 = min(FLOPPY_BOUNCEBUF, (unsigned)blks); + bbuf = malloc(x * 2 * BIOSDISK_SECSIZE); + if (((u_int32_t)VTOP(bbuf) & 0xffff0000) == ((u_int32_t)VTOP(dest + x * BIOSDISK_SECSIZE) & 0xffff0000)) { + breg = bbuf; + } else { + breg = bbuf + x * BIOSDISK_SECSIZE; + } + maxfer = x; /* limit transfers to bounce region size */ + } else { + breg = bbuf = NULL; + maxfer = 0; + } + + while (resid > 0) { + x = dblk; + cyl = x / bpc; /* block # / blocks per cylinder */ + x %= bpc; /* block offset into cylinder */ + hd = x / od->od_sec; /* offset / blocks per track */ + sec = x % od->od_sec; /* offset into track */ + + /* play it safe and don't cross track boundaries (XXX this is probably unnecessary) */ + x = min(od->od_sec - sec, resid); + if (maxfer > 0) + x = min(x, maxfer); /* fit bounce buffer */ + + /* where do we transfer to? */ + xp = bbuf == NULL ? p : breg; + + /* correct sector number for 1-based BIOS numbering */ + sec++; + + + /* Put your Data In, Put your Data out, + Put your Data In, and shake it all about + */ + if (bbuf != NULL) + bcopy(p, breg, x * BIOSDISK_SECSIZE); + p += (x * BIOSDISK_SECSIZE); + dblk += x; + resid -= x; + + /* 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 = od->od_unit; + v86int(); + } + + if(cyl > 1023) { + /* use EDD if the disk supports it, otherwise, return error */ + if(od->od_flags & BD_MODEEDD1) { + static unsigned short packet[8]; + + packet[0] = 0x10; + packet[1] = x; + packet[2] = VTOPOFF(xp); + packet[3] = VTOPSEG(xp); + packet[4] = dblk & 0xffff; + packet[5] = dblk >> 16; + packet[6] = 0; + packet[7] = 0; + v86.ctl = V86_FLAGS; + v86.addr = 0x13; + /* Should we Write with verify ?? 0x4302 ? */ + v86.eax = 0x4300; + v86.edx = od->od_unit; + v86.ds = VTOPSEG(packet); + v86.esi = VTOPOFF(packet); + v86int(); + result = (v86.efl & 0x1); + if(result == 0) + break; + } else { + result = 1; + break; + } + } else { + /* Use normal CHS addressing */ + v86.ctl = V86_FLAGS; + v86.addr = 0x13; + v86.eax = 0x300 | x; + v86.ecx = ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec; + v86.edx = (hd << 8) | od->od_unit; + v86.es = VTOPSEG(xp); + v86.ebx = VTOPOFF(xp); + v86int(); + result = (v86.efl & 0x1); + if (result == 0) + break; + } + } + + DEBUG("%d sectors from %d/%d/%d to %p (0x%x) %s", x, cyl, hd, sec - 1, p, VTOP(p), result ? "failed" : "ok"); + /* BUG here, cannot use v86 in printf because putchar uses it too */ + DEBUG("ax = 0x%04x cx = 0x%04x dx = 0x%04x status 0x%x", + 0x200 | x, ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec, (hd << 8) | od->od_unit, (v86.eax >> 8) & 0xff); + if (result) { + if (bbuf != NULL) + free(bbuf); + return(-1); + } + } + +/* hexdump(dest, (blks * BIOSDISK_SECSIZE)); */ + if (bbuf != NULL) + free(bbuf); + return(0); +} +static int +bd_getgeom(struct open_disk *od) +{ + + v86.ctl = V86_FLAGS; + v86.addr = 0x13; + v86.eax = 0x800; + v86.edx = od->od_unit; + v86int(); + + if ((v86.efl & 0x1) || /* carry set */ + ((v86.edx & 0xff) <= (unsigned)(od->od_unit & 0x7f))) /* unit # bad */ + return(1); + + /* convert max cyl # -> # of cylinders */ + od->od_cyl = ((v86.ecx & 0xc0) << 2) + ((v86.ecx & 0xff00) >> 8) + 1; + /* convert max head # -> # of heads */ + od->od_hds = ((v86.edx & 0xff00) >> 8) + 1; + od->od_sec = v86.ecx & 0x3f; + + DEBUG("unit 0x%x geometry %d/%d/%d", od->od_unit, od->od_cyl, od->od_hds, od->od_sec); + return(0); +} + +/* + * 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.efl & 0x1) + 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 *dev) +{ + struct open_disk *od; + int biosdev; + int major; + int rootdev; + char *nip, *cp; + int unitofs = 0, i, unit; + + biosdev = bd_unit2bios(dev->d_kind.biosdisk.unit); + DEBUG("unit %d BIOS device %d", dev->d_kind.biosdisk.unit, biosdev); + if (biosdev == -1) /* not a BIOS device */ + return(-1); + if (bd_opendisk(&od, dev) != 0) /* oops, not a viable device */ + return(-1); + + if (biosdev < 0x80) { + /* floppy (or emulated floppy) or ATAPI device */ + if (bdinfo[dev->d_kind.biosdisk.unit].bd_type == DT_ATAPI) { + /* is an ATAPI disk */ + major = WFDMAJOR; + } else { + /* is a floppy disk */ + major = FDMAJOR; + } + } else { + /* harddisk */ + if ((od->od_flags & BD_LABELOK) && (od->od_disklabel.d_type == DTYPE_SCSI)) { + /* label OK, disk labelled as SCSI */ + major = DAMAJOR; + /* check for unit number correction hint, now deprecated */ + if ((nip = getenv("num_ide_disks")) != NULL) { + i = strtol(nip, &cp, 0); + /* check for parse error */ + if ((cp != nip) && (*cp == 0)) + unitofs = i; + } + } else { + /* assume an IDE disk */ + major = WDMAJOR; + } + } + /* default root disk unit number */ + unit = (biosdev & 0x7f) - unitofs; + + /* 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_kind.biosdisk.slice + 1) >> 4, /* XXX slices may be wrong here */ + (dev->d_kind.biosdisk.slice + 1) & 0xf, + unit, + dev->d_kind.biosdisk.partition); + DEBUG("dev is 0x%x\n", rootdev); + return(rootdev); +} diff --git a/sys/boot/i386/libi386/biosmem.c b/sys/boot/i386/libi386/biosmem.c new file mode 100644 index 0000000..019cf90 --- /dev/null +++ b/sys/boot/i386/libi386/biosmem.c @@ -0,0 +1,107 @@ +/*- + * 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 "libi386.h" +#include "btxv86.h" + +vm_offset_t memtop; +u_int32_t bios_basemem, bios_extmem; + +#define SMAPSIG 0x534D4150 + +struct smap { + u_int64_t base; + u_int64_t length; + u_int32_t type; +} __packed; + +static struct smap smap; + +void +bios_getmem(void) +{ + + /* 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 smap); + v86.edx = SMAPSIG; + v86.es = VTOPSEG(&smap); + v86.edi = VTOPOFF(&smap); + v86int(); + if ((v86.efl & 1) || (v86.eax != SMAPSIG)) + break; + /* look for a low-memory segment that's large enough */ + if ((smap.type == 1) && (smap.base == 0) && (smap.length >= (512 * 1024))) + bios_basemem = smap.length; + /* look for the first segment in 'extended' memory */ + if ((smap.type == 1) && (smap.base == 0x100000)) { + bios_extmem = smap.length; + } + } 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; + } + + /* Set memtop to actual top of memory */ + memtop = 0x100000 + bios_extmem; + +} + diff --git a/sys/boot/i386/libi386/biospci.c b/sys/boot/i386/libi386/biospci.c new file mode 100644 index 0000000..5717bdc --- /dev/null +++ b/sys/boot/i386/libi386/biospci.c @@ -0,0 +1,294 @@ +/*- + * 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 enumerator using the PCI BIOS. + */ + +#include <stand.h> +#include <machine/stdarg.h> +#include <bootstrap.h> +#include <isapnp.h> +#include <btxv86.h> + +/* + * 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} +}; + + +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_null}, + {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); + +static int biospci_version; +static int biospci_hwcap; + +struct pnphandler biospcihandler = +{ + "PCI BIOS", + biospci_enumerate +}; + +static void +biospci_enumerate(void) +{ + int device_index, locator, devid; + struct pci_class *pc; + struct pci_subclass *psc; + struct pci_progif *ppi; + + /* Find the PCI BIOS */ + v86.ctl = V86_FLAGS; + v86.addr = 0x1a; + v86.eax = 0xb101; + v86.edi = 0x0; + v86int(); + + /* Check for OK response */ + if ((v86.efl & 1) || ((v86.eax & 0xff00) != 0) || (v86.edx != 0x20494350)) + return; + + biospci_version = v86.ebx & 0xffff; + biospci_hwcap = v86.eax & 0xff; +#if 0 + printf("PCI BIOS %d.%d%s%s\n", + bcd2bin((biospci_version >> 8) & 0xf), bcd2bin(biospci_version & 0xf), + (biospci_hwcap & 1) ? " config1" : "", (biospci_hwcap & 2) ? " config2" : ""); +#endif + /* 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 */ + v86.ctl = V86_FLAGS; + v86.addr = 0x1a; + v86.eax = 0xb103; + v86.ecx = (pc->pc_class << 16) + (psc->ps_subclass << 8) + ppi->pi_code; + v86.esi = device_index; + v86int(); + /* error/end of matches */ + if ((v86.efl & 1) || (v86.eax & 0xff00)) + break; + + /* Got something */ + locator = v86.ebx; + + /* Read the device identifier from the nominated device */ + v86.ctl = V86_FLAGS; + v86.addr = 0x1a; + v86.eax = 0xb10a; + v86.ebx = locator; + v86.edi = 0x0; + v86int(); + /* error */ + if ((v86.efl & 1) || (v86.eax & 0xff00)) + break; + + /* We have the device ID, create a PnP object and save everything */ + devid = v86.ecx; + 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); +} diff --git a/sys/boot/i386/libi386/biospnp.c b/sys/boot/i386/libi386/biospnp.c new file mode 100644 index 0000000..b6e765a --- /dev/null +++ b/sys/boot/i386/libi386/biospnp.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$"); + +/* + * 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] __packed; + u_int8_t pnp_version __packed; + u_int8_t pnp_length __packed; + u_int16_t pnp_BIOScontrol __packed; + u_int8_t pnp_checksum __packed; + u_int32_t pnp_eventflag __packed; + u_int16_t pnp_rmip __packed; + u_int16_t pnp_rmcs __packed; + u_int16_t pnp_pmip __packed; + u_int32_t pnp_pmcs __packed; + u_int8_t pnp_OEMdev[4] __packed; + u_int16_t pnp_rmds __packed; + u_int32_t pnp_pmds __packed; +}; + +struct pnp_devNode +{ + u_int16_t dn_size __packed; + u_int8_t dn_handle __packed; + u_int8_t dn_id[4] __packed; + u_int8_t dn_type[3] __packed; + u_int16_t dn_attrib __packed; + u_int8_t dn_data[1] __packed; +}; + +struct pnp_isaConfiguration +{ + u_int8_t ic_revision __packed; + u_int8_t ic_nCSN __packed; + u_int16_t ic_rdport __packed; + 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 *)malloc(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; + } + } + + /* 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/sys/boot/i386/libi386/biossmap.c b/sys/boot/i386/libi386/biossmap.c new file mode 100644 index 0000000..2859a56 --- /dev/null +++ b/sys/boot/i386/libi386/biossmap.c @@ -0,0 +1,108 @@ +/*- + * 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 <machine/metadata.h> +#include "bootstrap.h" +#include "libi386.h" +#include "btxv86.h" + +#define SMAPSIG 0x534D4150 + +struct smap { + u_int64_t base; + u_int64_t length; + u_int32_t type; +} __packed; + +static struct smap smap; + +static struct smap *smapbase; +static int smaplen; + +void +bios_getsmap(void) +{ + int n; + + n = 0; + smaplen = 0; + /* Count up segments in 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 smap); + v86.edx = SMAPSIG; + v86.es = VTOPSEG(&smap); + v86.edi = VTOPOFF(&smap); + v86int(); + if ((v86.efl & 1) || (v86.eax != SMAPSIG)) + break; + n++; + } while (v86.ebx != 0); + if (n == 0) + return; + n += 10; /* spare room */ + smapbase = malloc(n * sizeof(*smapbase)); + + /* Save 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 smap); + v86.edx = SMAPSIG; + v86.es = VTOPSEG(&smapbase[smaplen]); + v86.edi = VTOPOFF(&smapbase[smaplen]); + v86int(); + smaplen++; + if ((v86.efl & 1) || (v86.eax != SMAPSIG)) + break; + } while (v86.ebx != 0 && smaplen < n); +} +void +bios_addsmapdata(struct preloaded_file *kfp) +{ + int len; + + if (smapbase == 0 || smaplen == 0) + return; + len = smaplen * sizeof(*smapbase); + file_addmetadata(kfp, MODINFOMD_SMAP, len, smapbase); + /* Temporary compatability with older development kernels */ + file_addmetadata(kfp, 0x0009, len, smapbase); +} diff --git a/sys/boot/i386/libi386/bootinfo.c b/sys/boot/i386/libi386/bootinfo.c new file mode 100644 index 0000000..5095281 --- /dev/null +++ b/sys/boot/i386/libi386/bootinfo.c @@ -0,0 +1,157 @@ +/*- + * 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 "bootstrap.h" +#include "libi386.h" +#include "btxv86.h" + +/* + * Return a 'boothowto' value corresponding to the kernel arguments in + * (kargs) and any relevant environment variables. + */ +static struct +{ + const char *ev; + int mask; +} howto_names[] = { + {"boot_askname", RB_ASKNAME}, + {"boot_cdrom", RB_CDROM}, + {"boot_userconfig", RB_CONFIG}, + {"boot_ddb", RB_KDB}, + {"boot_gdb", RB_GDB}, + {"boot_single", RB_SINGLE}, + {"boot_verbose", RB_VERBOSE}, + {"boot_multicons", RB_MULTIPLE}, + {"boot_serial", RB_SERIAL}, + {NULL, 0} +}; + +int +bi_getboothowto(char *kargs) +{ + char *cp; + int howto; + int active; + int i; + + /* 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_CONFIG; + 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; + if (!strcmp(getenv("console"), "comconsole")) + howto |= RB_SERIAL; + if (!strcmp(getenv("console"), "nullconsole")) + howto |= RB_MUTE; + return(howto); +} + +/* + * 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/sys/boot/i386/libi386/bootinfo32.c b/sys/boot/i386/libi386/bootinfo32.c new file mode 100644 index 0000000..ceb254c --- /dev/null +++ b/sys/boot/i386/libi386/bootinfo32.c @@ -0,0 +1,273 @@ +/*- + * 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 "bootstrap.h" +#include "libi386.h" +#include "btxv86.h" + +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; + + 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_kind.bioscd.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_kind.biosdisk.unit); + bootdevnr = bd_getdev(rootdev); + break; + + case DEVT_NET: + 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); + + /* 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/sys/boot/i386/libi386/bootinfo64.c b/sys/boot/i386/libi386/bootinfo64.c new file mode 100644 index 0000000..58b4e3c --- /dev/null +++ b/sys/boot/i386/libi386/bootinfo64.c @@ -0,0 +1,208 @@ +/*- + * 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 "bootstrap.h" +#include "libi386.h" +#include "btxv86.h" + +/* + * 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); +} + +/* + * 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_load64(char *args, 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; + u_int64_t kernend; + u_int64_t envp; + vm_offset_t size; + char *rootdevname; + int i, howto; + char *kernelname; + const char *kernelpath; + + 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)); + + /* 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, "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); + bios_addsmapdata(kfp); + + /* Figure out the size and location of the metadata */ + *modulep = addr; + size = bi_copymodules64(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_copymodules64(addr); + + return(0); +} diff --git a/sys/boot/i386/libi386/comconsole.c b/sys/boot/i386/libi386/comconsole.c new file mode 100644 index 0000000..1b08d9b --- /dev/null +++ b/sys/boot/i386/libi386/comconsole.c @@ -0,0 +1,114 @@ +/*- + * 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 "libi386.h" + +#define COMC_FMT 0x3 /* 8N1 */ +#define COMC_TXWAIT 0x40000 /* transmit timeout */ +#define COMC_BPS(x) (115200 / (x)) /* speed to DLAB divisor */ + +#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_ischar(void); + +static int comc_started; + +struct console comconsole = { + "comconsole", + "serial port", + 0, + comc_probe, + comc_init, + comc_putchar, + comc_getchar, + comc_ischar +}; + +static void +comc_probe(struct console *cp) +{ + /* XXX check the BIOS equipment list? */ + cp->c_flags |= (C_PRESENTIN | C_PRESENTOUT); +} + +static int +comc_init(int arg) +{ + if (comc_started && arg == 0) + return 0; + comc_started = 1; + + outb(COMPORT + com_cfcr, CFCR_DLAB | COMC_FMT); + outb(COMPORT + com_dlbl, COMC_BPS(COMSPEED) & 0xff); + outb(COMPORT + com_dlbh, COMC_BPS(COMSPEED) >> 8); + outb(COMPORT + com_cfcr, COMC_FMT); + outb(COMPORT + com_mcr, MCR_RTS | MCR_DTR); + + do + inb(COMPORT + com_data); + while (inb(COMPORT + com_lsr) & LSR_RXRDY); + + return(0); +} + +static void +comc_putchar(int c) +{ + int wait; + + for (wait = COMC_TXWAIT; wait > 0; wait--) + if (inb(COMPORT + com_lsr) & LSR_TXRDY) { + outb(COMPORT + com_data, (u_char)c); + break; + } +} + +static int +comc_getchar(void) +{ + return(comc_ischar() ? inb(COMPORT + com_data) : -1); +} + +static int +comc_ischar(void) +{ + return(inb(COMPORT + com_lsr) & LSR_RXRDY); +} diff --git a/sys/boot/i386/libi386/devicename.c b/sys/boot/i386/libi386/devicename.c new file mode 100644 index 0000000..7ae45b1 --- /dev/null +++ b/sys/boot/i386/libi386/devicename.c @@ -0,0 +1,244 @@ +/*- + * 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 <sys/disklabel.h> +#include "bootstrap.h" +#include "libi386.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, slice, partition, 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: + unit = -1; + slice = -1; + partition = -1; + if (*np && (*np != ':')) { + unit = strtol(np, &cp, 10); /* next comes the unit number */ + if (cp == np) { + err = EUNIT; + goto fail; + } + if (*cp == 's') { /* got a slice number */ + np = cp + 1; + slice = strtol(np, &cp, 10); + if (cp == np) { + err = ESLICE; + goto fail; + } + } + if (*cp && (*cp != ':')) { + partition = *cp - 'a'; /* get a partition number */ + if ((partition < 0) || (partition >= MAXPARTITIONS)) { + err = EPART; + goto fail; + } + cp++; + } + } + if (*cp && (*cp != ':')) { + err = EINVAL; + goto fail; + } + + idev->d_kind.biosdisk.unit = unit; + idev->d_kind.biosdisk.slice = slice; + idev->d_kind.biosdisk.partition = partition; + if (path != NULL) + *path = (*cp == 0) ? cp : cp + 1; + 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; + } + } + if (*cp && (*cp != ':')) { + err = EINVAL; + goto fail; + } + + if (dv->dv_type == DEVT_NET) + idev->d_kind.netif.unit = unit; + else + idev->d_kind.bioscd.unit = unit; + if (path != NULL) + *path = (*cp == 0) ? cp : cp + 1; + 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? */ + char *cp; + + switch(dev->d_type) { + case DEVT_NONE: + strcpy(buf, "(no device)"); + break; + + case DEVT_CD: + sprintf(buf, "%s%d:", dev->d_dev->dv_name, dev->d_kind.bioscd.unit); + break; + + case DEVT_DISK: + cp = buf; + cp += sprintf(cp, "%s%d", dev->d_dev->dv_name, dev->d_kind.biosdisk.unit); + if (dev->d_kind.biosdisk.slice > 0) + cp += sprintf(cp, "s%d", dev->d_kind.biosdisk.slice); + if (dev->d_kind.biosdisk.partition >= 0) + cp += sprintf(cp, "%c", dev->d_kind.biosdisk.partition + 'a'); + strcat(cp, ":"); + break; + + case DEVT_NET: + sprintf(buf, "%s%d:", dev->d_dev->dv_name, dev->d_kind.netif.unit); + break; + } + return(buf); +} + + +/* + * Set currdev to suit the value being supplied in (value) + */ +int +i386_setcurrdev(struct env_var *ev, int flags, 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/sys/boot/i386/libi386/elf32_freebsd.c b/sys/boot/i386/libi386/elf32_freebsd.c new file mode 100644 index 0000000..a508201 --- /dev/null +++ b/sys/boot/i386/libi386/elf32_freebsd.c @@ -0,0 +1,76 @@ +/*- + * 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); + +struct file_format i386_elf = { elf32_loadfile, elf32_exec }; + +/* + * There is an a.out kernel and one or more a.out 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); /* XXX actually EFUCKUP */ + 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"); +} diff --git a/sys/boot/i386/libi386/elf64_freebsd.c b/sys/boot/i386/libi386/elf64_freebsd.c new file mode 100644 index 0000000..229c259 --- /dev/null +++ b/sys/boot/i386/libi386/elf64_freebsd.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$"); + +#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); + +struct file_format amd64_elf = { elf64_loadfile, elf64_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 amd64_tramp(); + +/* + * There is an a.out kernel and one or more a.out 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); /* XXX actually EFUCKUP */ + ehdr = (Elf_Ehdr *)&(md->md_data); + + err = bi_load64(fp->f_args, &modulep, &kernend); + 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"); +} diff --git a/sys/boot/i386/libi386/gatea20.c b/sys/boot/i386/libi386/gatea20.c new file mode 100644 index 0000000..1c5f0f0 --- /dev/null +++ b/sys/boot/i386/libi386/gatea20.c @@ -0,0 +1,54 @@ +/* + * $NetBSD: gatea20.c,v 1.2 1997/10/29 00:32:49 fvdl Exp $ + */ + +/* extracted from freebsd:sys/i386/boot/biosboot/io.c */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stand.h> +#include <machine/cpufunc.h> + +#include <bootstrap.h> + +#include "libi386.h" + +#define K_RDWR 0x60 /* keyboard data & cmds (read/write) */ +#define K_STATUS 0x64 /* keyboard status */ +#define K_CMD 0x64 /* keybd ctlr command (write-only) */ + +#define K_OBUF_FUL 0x01 /* output buffer full */ +#define K_IBUF_FUL 0x02 /* input buffer full */ + +#define KC_CMD_WIN 0xd0 /* read output port */ +#define KC_CMD_WOUT 0xd1 /* write output port */ +#define KB_A20 0x9f /* enable A20, + reset (!), + enable output buffer full interrupt + enable data line + disable clock line */ + +/* + * Gate A20 for high memory + */ +static unsigned char x_20 = KB_A20; +void gateA20() +{ + __asm("pushfl ; cli"); +#ifdef IBM_L40 + outb(0x92, 0x2); +#else /* !IBM_L40 */ + while (inb(K_STATUS) & K_IBUF_FUL); + while (inb(K_STATUS) & K_OBUF_FUL) + (void)inb(K_RDWR); + + outb(K_CMD, KC_CMD_WOUT); + delay(100); + while (inb(K_STATUS) & K_IBUF_FUL); + outb(K_RDWR, x_20); + delay(100); + while (inb(K_STATUS) & K_IBUF_FUL); +#endif /* IBM_L40 */ + __asm("popfl"); +} diff --git a/sys/boot/i386/libi386/i386_copy.c b/sys/boot/i386/libi386/i386_copy.c new file mode 100644 index 0000000..2dce4f1 --- /dev/null +++ b/sys/boot/i386/libi386/i386_copy.c @@ -0,0 +1,91 @@ +/*- + * 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" + +#define READIN_BUF (16 * 1024) + +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) +{ + void *buf; + size_t resid, chunk, get; + ssize_t got; + + if (dest + len >= memtop) + return(0); + + chunk = min(READIN_BUF, len); + buf = malloc(chunk); + if (buf == NULL) + return(0); + + for (resid = len; resid > 0; resid -= got, dest += got) { + get = min(chunk, resid); + got = read(fd, buf, get); + if (got <= 0) + break; + bcopy(buf, PTOV(dest), (size_t)got); + } + free(buf); + return(len - resid); +} diff --git a/sys/boot/i386/libi386/i386_module.c b/sys/boot/i386/libi386/i386_module.c new file mode 100644 index 0000000..91993df --- /dev/null +++ b/sys/boot/i386/libi386/i386_module.c @@ -0,0 +1,68 @@ +/*- + * 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. + * + */ + +#include <stand.h> +#include <string.h> + +#include "bootstrap.h" +#include "libi386.h" + +/* + * Use voodoo to load modules required by current hardware. + */ +int +i386_autoload(void) +{ + int error; + int disabled; + char *rv; + + /* XXX use PnP to locate stuff here */ + + /* autoload ACPI support */ + /* XXX should be in 4th keyed off acpi_load */ + disabled = 0; + rv = getenv("hint.acpi.0.disabled"); + if (rv != NULL && strncmp(rv, "0", 1) != 0) { + disabled = 1; + } + + if (getenv("acpi_load") && (!disabled)) { + error = mod_load("acpi", NULL, 0, NULL); + if (error != 0) + printf("ACPI autoload failed - %s\n", strerror(error)); + } + + return(0); +} diff --git a/sys/boot/i386/libi386/libi386.h b/sys/boot/i386/libi386/libi386.h new file mode 100644 index 0000000..f2696e0 --- /dev/null +++ b/sys/boot/i386/libi386/libi386.h @@ -0,0 +1,108 @@ +/*- + * 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. + */ +struct i386_devdesc +{ + struct devsw *d_dev; + int d_type; + union + { + struct + { + int unit; + int slice; + int partition; + void *data; + } biosdisk; + struct + { + int unit; + void *data; + } bioscd; + struct + { + int unit; /* XXX net layer lives over these? */ + } netif; + } d_kind; +}; + +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, void *value); + +extern struct devdesc currdev; /* our current device */ + +#define MAXDEV 31 /* maximum number of distinct devices */ + +/* 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 */ +u_int32_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 u_int32_t bios_basemem; /* base memory in bytes */ +extern u_int32_t bios_extmem; /* extended memory in bytes */ +extern vm_offset_t memtop; + +void biosacpi_detect(); + +void gateA20(void); + +int i386_autoload(void); + +int bi_getboothowto(char *kargs); +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 *modulep, vm_offset_t *kernend); + +void pxe_enable(void *pxeinfo); diff --git a/sys/boot/i386/libi386/nullconsole.c b/sys/boot/i386/libi386/nullconsole.c new file mode 100644 index 0000000..ebb1e7e --- /dev/null +++ b/sys/boot/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/sys/boot/i386/libi386/pread.c b/sys/boot/i386/libi386/pread.c new file mode 100644 index 0000000..870e254 --- /dev/null +++ b/sys/boot/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/sys/boot/i386/libi386/pxe.c b/sys/boot/i386/libi386/pxe.c new file mode 100644 index 0000000..b78508a --- /dev/null +++ b/sys/boot/i386/libi386/pxe.c @@ -0,0 +1,609 @@ +/*- + * 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 <string.h> +#include <stdarg.h> + +#include <netinet/in_systm.h> +#include <netinet/in.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 +#define PXE_TFTP_BUFFER_SIZE 512 +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 */ +static BOOTPLAYER bootplayer; /* PXE Cached information. */ + +static int pxe_debug = 0; +static int pxe_sock = -1; +static int pxe_opens = 0; + +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_strategy(void *devdata, int flag, daddr_t dblk, + size_t size, char *buf, size_t *rsize); +static int pxe_open(struct open_file *f, ...); +static int pxe_close(struct open_file *f); +static void pxe_print(int verbose); +static void pxe_cleanup(void); +static void pxe_setnfshandle(char *rootpath); + +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 int pxe_netif_get(struct iodesc *desc, void *pkt, size_t len, + time_t timeout); +static int 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[NENTS(pxe_ifs)]; + +struct netif_driver pxenetif = { + "pxenet", + pxe_netif_match, + pxe_netif_probe, + pxe_netif_init, + pxe_netif_get, + pxe_netif_put, + pxe_netif_end, + pxe_ifs, + NENTS(pxe_ifs) +}; + +struct netif_driver *netif_drivers[] = { + &pxenetif, + NULL +}; + +struct devsw pxedisk = { + "pxe", + DEVT_NET, + pxe_init, + pxe_strategy, + pxe_open, + pxe_close, + noioctl, + pxe_print, + 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; + + 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; + } + } + + 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); + } + bcopy(PTOV((gci_p->Buffer.segment << 4) + gci_p->Buffer.offset), + &bootplayer, gci_p->BufferSize); + return (1); +} + + +static int +pxe_strategy(void *devdata, int flag, daddr_t dblk, size_t size, + char *buf, size_t *rsize) +{ + return (EIO); +} + +static int +pxe_open(struct open_file *f, ...) +{ + va_list args; + char *devname; /* Device part of file name (or NULL). */ + char temp[FNAME_SIZE]; + int error = 0; + int i; + + va_start(args, f); + devname = va_arg(args, char*); + va_end(args); + + /* On first open, do netif open, mount, etc. */ + if (pxe_opens == 0) { + /* Find network interface. */ + if (pxe_sock < 0) { + pxe_sock = netif_open(devname); + if (pxe_sock < 0) { + printf("pxe_open: netif_open() failed\n"); + return (ENXIO); + } + if (pxe_debug) + printf("pxe_open: netif_open() succeeded\n"); + } + if (rootip.s_addr == 0) { + /* + * Do a bootp/dhcp request to find out where our + * NFS/TFTP server is. Even if we dont get back + * the proper information, fall back to the server + * which brought us to life and a default rootpath. + */ + bootp(pxe_sock, BOOTP_PXE); + if (rootip.s_addr == 0) + rootip.s_addr = bootplayer.sip; + if (!rootpath[1]) + strcpy(rootpath, PXENFSROOTPATH); + + for (i = 0; rootpath[i] != '\0' && i < FNAME_SIZE; i++) + if (rootpath[i] == ':') + break; + if (i && i != FNAME_SIZE && rootpath[i] == ':') { + rootpath[i++] = '\0'; + if (inet_addr(&rootpath[0]) != INADDR_NONE) + rootip.s_addr = inet_addr(&rootpath[0]); + bcopy(&rootpath[i], &temp[0], strlen(&rootpath[i])+1); + bcopy(&temp[0], &rootpath[0], strlen(&rootpath[i])+1); + } + printf("pxe_open: server addr: %s\n", inet_ntoa(rootip)); + printf("pxe_open: server path: %s\n", rootpath); + printf("pxe_open: gateway ip: %s\n", inet_ntoa(gateip)); + + setenv("boot.netif.ip", inet_ntoa(myip), 1); + setenv("boot.netif.netmask", intoa(netmask), 1); + setenv("boot.netif.gateway", inet_ntoa(gateip), 1); + if (bootplayer.Hardware == ETHER_TYPE) { + sprintf(temp, "%6D", bootplayer.CAddr, ":"); + setenv("boot.netif.hwaddr", temp, 1); + } + setenv("boot.nfsroot.server", inet_ntoa(rootip), 1); + setenv("boot.nfsroot.path", rootpath, 1); + } + } + pxe_opens++; + f->f_devdata = &pxe_sock; + return (error); +} + +static int +pxe_close(struct open_file *f) +{ + +#ifdef PXE_DEBUG + if (pxe_debug) + printf("pxe_close: opens=%d\n", pxe_opens); +#endif + + /* On last close, do netif close, etc. */ + f->f_devdata = NULL; + /* Extra close call? */ + if (pxe_opens <= 0) + return (0); + pxe_opens--; + /* Not last close? */ + if (pxe_opens > 0) + return(0); + + /* get an NFS filehandle for our root filesystem */ + pxe_setnfshandle(rootpath); + + if (pxe_sock >= 0) { + +#ifdef PXE_DEBUG + if (pxe_debug) + printf("pxe_close: calling netif_close()\n"); +#endif + netif_close(pxe_sock); + pxe_sock = -1; + } + return (0); +} + +static void +pxe_print(int verbose) +{ + if (pxe_call != NULL) { + if (*bootplayer.Sname == '\0') { + printf(" "IP_STR":%s\n", + IP_ARGS(htonl(bootplayer.sip)), + bootplayer.bootfile); + } else { + printf(" %s:%s\n", bootplayer.Sname, + bootplayer.bootfile); + } + } + + return; +} + +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; +} + +/* + * Reach inside the libstand NFS code and dig out an NFS handle + * for the root filesystem. + */ +struct nfs_iodesc { + struct iodesc *iodesc; + off_t off; + u_char fh[NFS_FHSIZE]; + /* structure truncated here */ +}; +extern struct nfs_iodesc nfs_root_node; + +static void +pxe_setnfshandle(char *rootpath) +{ + int i; + u_char *fh; + char buf[2 * NFS_FHSIZE + 3], *cp; + + fh = &nfs_root_node.fh[0]; + buf[0] = 'X'; + cp = &buf[1]; + for (i = 0; i < NFS_FHSIZE; i++, cp += 2) + sprintf(cp, "%02x", fh[i]); + sprintf(cp, "X"); + setenv("boot.nfsroot.nfshandle", buf, 1); +} + +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; +} + + +time_t +getsecs() +{ + time_t n = 0; + time(&n); + return n; +} + +static int +pxe_netif_match(struct netif *nif, void *machdep_hint) +{ + return 1; +} + + +static int +pxe_netif_probe(struct netif *nif, void *machdep_hint) +{ + t_PXENV_UDP_OPEN *udpopen_p = (t_PXENV_UDP_OPEN *)scratch_buffer; + + if (pxe_call == NULL) + return -1; + + bzero(udpopen_p, sizeof(*udpopen_p)); + udpopen_p->src_ip = bootplayer.yip; + pxe_call(PXENV_UDP_OPEN); + + if (udpopen_p->status != 0) { + printf("pxe_netif_probe: failed %x\n", udpopen_p->status); + return -1; + } + return 0; +} + +static void +pxe_netif_end(struct netif *nif) +{ + t_PXENV_UDP_CLOSE *udpclose_p = (t_PXENV_UDP_CLOSE *)scratch_buffer; + bzero(udpclose_p, sizeof(*udpclose_p)); + + pxe_call(PXENV_UDP_CLOSE); + if (udpclose_p->status != 0) + printf("pxe_end failed %x\n", udpclose_p->status); +} + +static void +pxe_netif_init(struct iodesc *desc, void *machdep_hint) +{ + int i; + for (i = 0; i < 6; ++i) + desc->myea[i] = bootplayer.CAddr[i]; + desc->xid = bootplayer.ident; +} + +static int +pxe_netif_get(struct iodesc *desc, void *pkt, size_t len, time_t timeout) +{ + return len; +} + +static int +pxe_netif_put(struct iodesc *desc, void *pkt, size_t len) +{ + return len; +} + +ssize_t +sendudp(struct iodesc *h, void *pkt, size_t len) +{ + t_PXENV_UDP_WRITE *udpwrite_p = (t_PXENV_UDP_WRITE *)scratch_buffer; + bzero(udpwrite_p, sizeof(*udpwrite_p)); + + udpwrite_p->ip = h->destip.s_addr; + udpwrite_p->dst_port = h->destport; + udpwrite_p->src_port = h->myport; + udpwrite_p->buffer_size = len; + udpwrite_p->buffer.segment = VTOPSEG(pkt); + udpwrite_p->buffer.offset = VTOPOFF(pkt); + + if (netmask == 0 || SAMENET(myip, h->destip, netmask)) + udpwrite_p->gw = 0; + else + udpwrite_p->gw = gateip.s_addr; + + pxe_call(PXENV_UDP_WRITE); + +#if 0 + /* XXX - I dont know why we need this. */ + delay(1000); +#endif + if (udpwrite_p->status != 0) { + /* XXX: This happens a lot. It shouldn't. */ + if (udpwrite_p->status != 1) + printf("sendudp failed %x\n", udpwrite_p->status); + return -1; + } + return len; +} + +ssize_t +readudp(struct iodesc *h, void *pkt, size_t len, time_t timeout) +{ + t_PXENV_UDP_READ *udpread_p = (t_PXENV_UDP_READ *)scratch_buffer; + struct udphdr *uh = NULL; + + uh = (struct udphdr *) pkt - 1; + bzero(udpread_p, sizeof(*udpread_p)); + + udpread_p->dest_ip = h->myip.s_addr; + udpread_p->d_port = h->myport; + udpread_p->buffer_size = len; + udpread_p->buffer.segment = VTOPSEG(data_buffer); + udpread_p->buffer.offset = VTOPOFF(data_buffer); + + pxe_call(PXENV_UDP_READ); + +#if 0 + /* XXX - I dont know why we need this. */ + delay(1000); +#endif + if (udpread_p->status != 0) { + /* XXX: This happens a lot. It shouldn't. */ + if (udpread_p->status != 1) + printf("readudp failed %x\n", udpread_p->status); + return -1; + } + bcopy(data_buffer, pkt, udpread_p->buffer_size); + uh->uh_sport = udpread_p->s_port; + return udpread_p->buffer_size; +} diff --git a/sys/boot/i386/libi386/pxe.h b/sys/boot/i386/libi386/pxe.h new file mode 100644 index 0000000..f1f22aa --- /dev/null +++ b/sys/boot/i386/libi386/pxe.h @@ -0,0 +1,522 @@ +/* + * 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 IP_STR "%d.%d.%d.%d" +#define IP_ARGS(ip) \ + (int)(ip >> 24) & 0xff, (int)(ip >> 16) & 0xff, \ + (int)(ip >> 8) & 0xff, (int)ip & 0xff + +#define MAC_STR "%02x:%02x:%02x:%02x:%02x:%02x" +#define MAC_ARGS(mac) \ + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5] + +#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 0x0003 +# define FLTR_SRC_RTG 0x0004 + + 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 addres 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 reciever 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 returnd 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_RECIEVE 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/sys/boot/i386/libi386/pxetramp.s b/sys/boot/i386/libi386/pxetramp.s new file mode 100644 index 0000000..dcf1441 --- /dev/null +++ b/sys/boot/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/sys/boot/i386/libi386/time.c b/sys/boot/i386/libi386/time.c new file mode 100644 index 0000000..1db9a4a --- /dev/null +++ b/sys/boot/i386/libi386/time.c @@ -0,0 +1,83 @@ +/*- + * 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" + +/* + * Return the time in seconds since the beginning of the day. + * + * If we pass midnight, don't wrap back to 0. + * + * XXX uses undocumented BCD support from libstand. + */ + +time_t +time(time_t *t) +{ + static time_t lasttime, now; + 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 */ + + now = hr * 3600 + minute * 60 + sec; + if (now < lasttime) + now += 24 * 3600; + lasttime = now; + + if (t != NULL) + *t = now; + return(now); +} + +/* + * 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/sys/boot/i386/libi386/vidconsole.c b/sys/boot/i386/libi386/vidconsole.c new file mode 100644 index 0000000..bea6b1e --- /dev/null +++ b/sys/boot/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(void); +void curs_move(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(); + curs_move(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? */ +} + +static 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); + return; + case '\n': + cury++; + if (cury > 24) { + scroll_up(1, fg_c, bg_c); + cury--; + } else { + curs_move(curx, cury); + } + return; + case '\b': + if (curx > 0) { + curx--; + curs_move(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); +#endif + } +} + +#ifdef TERM_EMU + +/* Get cursor position on the screen. Result is in edx. Sets + * curx and cury appropriately. + */ +void +get_pos(void) +{ + + v86.ctl = 0; + v86.addr = 0x10; + v86.eax = 0x0300; + v86.ebx = 0x0; + v86int(); + curx = v86.edx & 0x00ff; + cury = (v86.edx & 0xff00) >> 8; +} + +/* Move cursor to x rows and y cols (0-based). */ +void +curs_move(int x, int y) +{ + + v86.ctl = 0; + v86.addr = 0x10; + v86.eax = 0x0200; + v86.ebx = 0x0; + v86.edx = ((0x00ff & y) << 8) + (0x00ff & x); + v86int(); + curx = x; + cury = 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(); + 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(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(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.efl & PSL_Z)); +} + +#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 (0); + } + } + + return (1); +} +#endif /* KEYBOARD_PROBE */ |