summaryrefslogtreecommitdiffstats
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
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.
-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