diff --git a/sys/boot/i386/libi386/Makefile b/sys/boot/i386/libi386/Makefile index 6bcf8dcc4410..7026cd07f12a 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 000000000000..5a9605efe5c2 --- /dev/null +++ b/sys/boot/i386/libi386/bioscd.c @@ -0,0 +1,358 @@ +/*- + * Copyright (c) 1998 Michael Smith + * Copyright (c) 2001 John H. Baldwin + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * 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 + +#include +#include +#include + +#include + +#include +#include +#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 9b1368c08ace..8e5c752fe4c3 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 9b1368c08ace..8e5c752fe4c3 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 9b1368c08ace..8e5c752fe4c3 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 dfa685eb1b87..31259c1955be 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 3f83bdcfd9a7..cabe735b7bcf 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 */