summaryrefslogtreecommitdiffstats
path: root/sys/boot/i386
diff options
context:
space:
mode:
authorjhb <jhb@FreeBSD.org>2001-11-05 18:58:33 +0000
committerjhb <jhb@FreeBSD.org>2001-11-05 18:58:33 +0000
commit094cb29b1151595b1d62c786f4f942d3a11fe157 (patch)
tree0052d165cb914858205c5e81032180dff27ccb6e /sys/boot/i386
parentc077e35a66d15f941c73fdf96aeb87b7a6387c51 (diff)
downloadFreeBSD-src-094cb29b1151595b1d62c786f4f942d3a11fe157.zip
FreeBSD-src-094cb29b1151595b1d62c786f4f942d3a11fe157.tar.gz
Add a device driver for the BIOS device for CD-ROM's booted via El Torito
no emulation mode. Unlike other BIOS devices, this device uses 2048 byte sectors. Also, the bioscd driver does not have to worry about slices or partitions.
Diffstat (limited to 'sys/boot/i386')
-rw-r--r--sys/boot/i386/libi386/Makefile4
-rw-r--r--sys/boot/i386/libi386/bioscd.c358
-rw-r--r--sys/boot/i386/libi386/bootinfo.c19
-rw-r--r--sys/boot/i386/libi386/bootinfo32.c19
-rw-r--r--sys/boot/i386/libi386/bootinfo64.c19
-rw-r--r--sys/boot/i386/libi386/devicename.c16
-rw-r--r--sys/boot/i386/libi386/libi386.h10
7 files changed, 424 insertions, 21 deletions
diff --git a/sys/boot/i386/libi386/Makefile b/sys/boot/i386/libi386/Makefile
index 6bcf8dc..7026cd0 100644
--- a/sys/boot/i386/libi386/Makefile
+++ b/sys/boot/i386/libi386/Makefile
@@ -6,8 +6,8 @@ NOPROFILE=
INTERNALLIB= true
INTERNALSTATICLIB= true
-SRCS= aout_freebsd.c biosacpi.c biosdisk.c biosmem.c biospnp.c biospci.c \
- bootinfo.c comconsole.c devicename.c elf_freebsd.c gatea20.c \
+SRCS= aout_freebsd.c biosacpi.c bioscd.c biosdisk.c biosmem.c biospnp.c \
+ biospci.c bootinfo.c comconsole.c devicename.c elf_freebsd.c gatea20.c \
i386_copy.c i386_module.c nullconsole.c pxe.c pxetramp.s \
time.c vidconsole.c
diff --git a/sys/boot/i386/libi386/bioscd.c b/sys/boot/i386/libi386/bioscd.c
new file mode 100644
index 0000000..5a9605e
--- /dev/null
+++ b/sys/boot/i386/libi386/bioscd.c
@@ -0,0 +1,358 @@
+/*-
+ * 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.
+ *
+ * $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/reboot.h>
+#include <machine/param.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" , __FUNCTION__ , ## 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/bootinfo.c b/sys/boot/i386/libi386/bootinfo.c
index 9b1368c..8e5c752 100644
--- a/sys/boot/i386/libi386/bootinfo.c
+++ b/sys/boot/i386/libi386/bootinfo.c
@@ -277,21 +277,30 @@ bi_load(char *args, int *howtop, int *bootdevp, vm_offset_t *bip)
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);
+ if (bootdevnr != -1)
+ *howtop |= RB_CDROM;
+ 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);
- if (bootdevnr != -1)
- break;
- printf("root device %s invalid\n", i386_fmtdev(rootdev));
- return(EINVAL);
-
+ 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);
*bootdevp = bootdevnr;
diff --git a/sys/boot/i386/libi386/bootinfo32.c b/sys/boot/i386/libi386/bootinfo32.c
index 9b1368c..8e5c752 100644
--- a/sys/boot/i386/libi386/bootinfo32.c
+++ b/sys/boot/i386/libi386/bootinfo32.c
@@ -277,21 +277,30 @@ bi_load(char *args, int *howtop, int *bootdevp, vm_offset_t *bip)
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);
+ if (bootdevnr != -1)
+ *howtop |= RB_CDROM;
+ 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);
- if (bootdevnr != -1)
- break;
- printf("root device %s invalid\n", i386_fmtdev(rootdev));
- return(EINVAL);
-
+ 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);
*bootdevp = bootdevnr;
diff --git a/sys/boot/i386/libi386/bootinfo64.c b/sys/boot/i386/libi386/bootinfo64.c
index 9b1368c..8e5c752 100644
--- a/sys/boot/i386/libi386/bootinfo64.c
+++ b/sys/boot/i386/libi386/bootinfo64.c
@@ -277,21 +277,30 @@ bi_load(char *args, int *howtop, int *bootdevp, vm_offset_t *bip)
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);
+ if (bootdevnr != -1)
+ *howtop |= RB_CDROM;
+ 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);
- if (bootdevnr != -1)
- break;
- printf("root device %s invalid\n", i386_fmtdev(rootdev));
- return(EINVAL);
-
+ 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);
*bootdevp = bootdevnr;
diff --git a/sys/boot/i386/libi386/devicename.c b/sys/boot/i386/libi386/devicename.c
index dfa685e..31259c1 100644
--- a/sys/boot/i386/libi386/devicename.c
+++ b/sys/boot/i386/libi386/devicename.c
@@ -147,10 +147,11 @@ i386_parsedev(struct i386_devdesc **dev, const char *devspec, const char **path)
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) {
@@ -162,8 +163,11 @@ i386_parsedev(struct i386_devdesc **dev, const char *devspec, const char **path)
err = EINVAL;
goto fail;
}
-
- idev->d_kind.netif.unit = unit;
+
+ 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;
@@ -199,6 +203,10 @@ i386_fmtdev(void *vdev)
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);
diff --git a/sys/boot/i386/libi386/libi386.h b/sys/boot/i386/libi386/libi386.h
index 3f83bdc..cabe735 100644
--- a/sys/boot/i386/libi386/libi386.h
+++ b/sys/boot/i386/libi386/libi386.h
@@ -45,6 +45,11 @@ struct i386_devdesc
int partition;
void *data;
} biosdisk;
+ struct
+ {
+ int unit;
+ void *data;
+ } bioscd;
struct
{
int unit; /* XXX net layer lives over these? */
@@ -61,10 +66,15 @@ 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 */
OpenPOWER on IntegriCloud