loader: create separate lists for fd, cd and hd, merge bioscd with biosdisk

Create unified block IO implementation in BIOS version, like it is done in UEFI
side. Implement fd, disk and cd device lists, this will split floppy devices
from disks and will allow us to have consistent, predictable device naming
(modulo BIOS issues).

Differential Revision:	https://reviews.freebsd.org/D17888
This commit is contained in:
Toomas Soome 2018-11-30 08:01:11 +00:00
parent ad72d03040
commit cdff10360e
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=341328
8 changed files with 508 additions and 628 deletions

View file

@ -4,7 +4,7 @@
LIB= i386
SRCS= biosacpi.c bioscd.c biosdisk.c biosmem.c biospnp.c \
SRCS= biosacpi.c biosdisk.c biosmem.c biospnp.c \
biospci.c biossmap.c bootinfo.c bootinfo32.c bootinfo64.c \
comconsole.c devicename.c elf32_freebsd.c \
elf64_freebsd.c multiboot.c multiboot_tramp.S relocater_tramp.S \

View file

@ -1,432 +0,0 @@
/*-
* Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
* Copyright (c) 2001 John H. Baldwin <jhb@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
/*
* BIOS CD device handling for CD's that have been booted off of via no
* emulation booting as defined in the El Torito standard.
*
* Ideas and algorithms from:
*
* - FreeBSD libi386/biosdisk.c
*
*/
#include <stand.h>
#include <sys/param.h>
#include <machine/bootinfo.h>
#include <stdarg.h>
#include <bootstrap.h>
#include <btxv86.h>
#include <edd.h>
#include "libi386.h"
#define BIOSCD_SECSIZE 2048
#define BUFSIZE (1 * BIOSCD_SECSIZE)
#define MAXBCDEV 1
/* Major numbers for devices we frontend for. */
#define ACDMAJOR 117
#define CDMAJOR 15
#ifdef DISK_DEBUG
# define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args)
#else
# define DEBUG(fmt, args...)
#endif
struct specification_packet {
u_char sp_size;
u_char sp_bootmedia;
u_char sp_drive;
u_char sp_controller;
u_int sp_lba;
u_short sp_devicespec;
u_short sp_buffersegment;
u_short sp_loadsegment;
u_short sp_sectorcount;
u_short sp_cylsec;
u_char sp_head;
};
/*
* List of BIOS devices, translation from disk unit number to
* BIOS unit number.
*/
static struct bcinfo {
int bc_unit; /* BIOS unit number */
struct specification_packet bc_sp;
int bc_open; /* reference counter */
void *bc_bcache; /* buffer cache data */
} bcinfo [MAXBCDEV];
static int nbcinfo = 0;
#define BC(dev) (bcinfo[(dev)->dd.d_unit])
static int bc_read(int unit, daddr_t dblk, int blks, caddr_t dest);
static int bc_init(void);
static int bc_strategy(void *devdata, int flag, daddr_t dblk,
size_t size, char *buf, size_t *rsize);
static int bc_realstrategy(void *devdata, int flag, daddr_t dblk,
size_t size, char *buf, size_t *rsize);
static int bc_open(struct open_file *f, ...);
static int bc_close(struct open_file *f);
static int bc_print(int verbose);
struct devsw bioscd = {
"cd",
DEVT_CD,
bc_init,
bc_strategy,
bc_open,
bc_close,
noioctl,
bc_print,
NULL
};
/*
* Translate between BIOS device numbers and our private unit numbers.
*/
int
bc_bios2unit(int biosdev)
{
int i;
DEBUG("looking for bios device 0x%x", biosdev);
for (i = 0; i < nbcinfo; i++) {
DEBUG("bc unit %d is BIOS device 0x%x", i, bcinfo[i].bc_unit);
if (bcinfo[i].bc_unit == biosdev)
return(i);
}
return(-1);
}
int
bc_unit2bios(int unit)
{
if ((unit >= 0) && (unit < nbcinfo))
return(bcinfo[unit].bc_unit);
return(-1);
}
/*
* We can't quiz, we have to be told what device to use, so this functoin
* doesn't do anything. Instead, the loader calls bc_add() with the BIOS
* device number to add.
*/
static int
bc_init(void)
{
return (0);
}
int
bc_add(int biosdev)
{
if (nbcinfo >= MAXBCDEV)
return (-1);
bcinfo[nbcinfo].bc_unit = biosdev;
v86.ctl = V86_FLAGS;
v86.addr = 0x13;
v86.eax = 0x4b01;
v86.edx = biosdev;
v86.ds = VTOPSEG(&bcinfo[nbcinfo].bc_sp);
v86.esi = VTOPOFF(&bcinfo[nbcinfo].bc_sp);
v86int();
if ((v86.eax & 0xff00) != 0)
return (-1);
printf("BIOS CD is cd%d\n", nbcinfo);
nbcinfo++;
bcache_add_dev(nbcinfo); /* register cd device in bcache */
return(0);
}
/*
* Print information about disks
*/
static int
bc_print(int verbose)
{
char line[80];
int i, ret = 0;
if (nbcinfo == 0)
return (0);
printf("%s devices:", bioscd.dv_name);
if ((ret = pager_output("\n")) != 0)
return (ret);
for (i = 0; i < nbcinfo; i++) {
snprintf(line, sizeof(line), " cd%d: Device 0x%x\n", i,
bcinfo[i].bc_sp.sp_devicespec);
if ((ret = pager_output(line)) != 0)
break;
}
return (ret);
}
/*
* Attempt to open the disk described by (dev) for use by (f).
*/
static int
bc_open(struct open_file *f, ...)
{
va_list ap;
struct i386_devdesc *dev;
va_start(ap, f);
dev = va_arg(ap, struct i386_devdesc *);
va_end(ap);
if (dev->dd.d_unit >= nbcinfo) {
DEBUG("attempt to open nonexistent disk");
return(ENXIO);
}
BC(dev).bc_open++;
if (BC(dev).bc_bcache == NULL)
BC(dev).bc_bcache = bcache_allocate();
return(0);
}
static int
bc_close(struct open_file *f)
{
struct i386_devdesc *dev;
dev = (struct i386_devdesc *)f->f_devdata;
BC(dev).bc_open--;
if (BC(dev).bc_open == 0) {
bcache_free(BC(dev).bc_bcache);
BC(dev).bc_bcache = NULL;
}
return(0);
}
static int
bc_strategy(void *devdata, int rw, daddr_t dblk, size_t size,
char *buf, size_t *rsize)
{
struct bcache_devdata bcd;
struct i386_devdesc *dev;
dev = (struct i386_devdesc *)devdata;
bcd.dv_strategy = bc_realstrategy;
bcd.dv_devdata = devdata;
bcd.dv_cache = BC(dev).bc_bcache;
return (bcache_strategy(&bcd, rw, dblk, size, buf, rsize));
}
static int
bc_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size,
char *buf, size_t *rsize)
{
struct i386_devdesc *dev;
int unit;
int blks;
if (size % BIOSCD_SECSIZE)
return (EINVAL);
if ((rw & F_MASK) != F_READ)
return(EROFS);
dev = (struct i386_devdesc *)devdata;
unit = dev->dd.d_unit;
blks = size / BIOSCD_SECSIZE;
if (dblk % (BIOSCD_SECSIZE / DEV_BSIZE) != 0)
return (EINVAL);
dblk /= (BIOSCD_SECSIZE / DEV_BSIZE);
DEBUG("read %d from %lld to %p", blks, dblk, buf);
if (rsize)
*rsize = 0;
if ((blks = bc_read(unit, dblk, blks, buf)) < 0) {
DEBUG("read error");
return (EIO);
} else {
if (size / BIOSCD_SECSIZE > blks) {
if (rsize)
*rsize = blks * BIOSCD_SECSIZE;
return (0);
}
}
if (rsize)
*rsize = size;
return (0);
}
/* return negative value for an error, otherwise blocks read */
static int
bc_read(int unit, daddr_t dblk, int blks, caddr_t dest)
{
u_int maxfer, resid, result, retry, x;
caddr_t bbuf, p, xp;
static struct edd_packet packet;
int biosdev;
#ifdef DISK_DEBUG
int error;
#endif
/* Just in case some idiot actually tries to read -1 blocks... */
if (blks < 0)
return (-1);
/* If nothing to do, just return succcess. */
if (blks == 0)
return (0);
/* Decide whether we have to bounce */
if (VTOP(dest) >> 20 != 0) {
/*
* The destination buffer is above first 1MB of
* physical memory so we have to arrange a suitable
* bounce buffer.
*/
x = V86_IO_BUFFER_SIZE / BIOSCD_SECSIZE;
x = min(x, (unsigned)blks);
bbuf = PTOV(V86_IO_BUFFER);
maxfer = x;
} else {
bbuf = NULL;
maxfer = 0;
}
biosdev = bc_unit2bios(unit);
resid = blks;
p = dest;
while (resid > 0) {
if (bbuf)
xp = bbuf;
else
xp = p;
x = resid;
if (maxfer > 0)
x = min(x, maxfer);
/*
* Loop retrying the operation a couple of times. The BIOS
* may also retry.
*/
for (retry = 0; retry < 3; retry++) {
/* If retrying, reset the drive */
if (retry > 0) {
v86.ctl = V86_FLAGS;
v86.addr = 0x13;
v86.eax = 0;
v86.edx = biosdev;
v86int();
}
packet.len = sizeof(struct edd_packet);
packet.count = x;
packet.off = VTOPOFF(xp);
packet.seg = VTOPSEG(xp);
packet.lba = dblk;
v86.ctl = V86_FLAGS;
v86.addr = 0x13;
v86.eax = 0x4200;
v86.edx = biosdev;
v86.ds = VTOPSEG(&packet);
v86.esi = VTOPOFF(&packet);
v86int();
result = V86_CY(v86.efl);
if (result == 0)
break;
/* fall back to 1 sector read */
x = 1;
}
#ifdef DISK_DEBUG
error = (v86.eax >> 8) & 0xff;
#endif
DEBUG("%d sectors from %lld to %p (0x%x) %s", x, dblk, p,
VTOP(p), result ? "failed" : "ok");
DEBUG("unit %d status 0x%x", unit, error);
/* still an error? break off */
if (result != 0)
break;
if (bbuf != NULL)
bcopy(bbuf, p, x * BIOSCD_SECSIZE);
p += (x * BIOSCD_SECSIZE);
dblk += x;
resid -= x;
}
/* hexdump(dest, (blks * BIOSCD_SECSIZE)); */
if (blks - resid == 0)
return (-1); /* read failed */
return (blks - resid);
}
/*
* Return a suitable dev_t value for (dev).
*/
int
bc_getdev(struct i386_devdesc *dev)
{
int biosdev, unit;
int major;
int rootdev;
unit = dev->dd.d_unit;
biosdev = bc_unit2bios(unit);
DEBUG("unit %d BIOS device %d", unit, biosdev);
if (biosdev == -1) /* not a BIOS device */
return(-1);
/*
* XXX: Need to examine device spec here to figure out if SCSI or
* ATAPI. No idea on how to figure out device number. All we can
* really pass to the kernel is what bus and device on which bus we
* were booted from, which dev_t isn't well suited to since those
* number don't match to unit numbers very well. We may just need
* to engage in a hack where we pass -C to the boot args if we are
* the boot device.
*/
major = ACDMAJOR;
unit = 0; /* XXX */
/* XXX: Assume partition 'a'. */
rootdev = MAKEBOOTDEV(major, 0, unit, 0);
DEBUG("dev is 0x%x\n", rootdev);
return(rootdev);
}

File diff suppressed because it is too large Load diff

View file

@ -176,14 +176,9 @@ bi_load32(char *args, int *howtop, int *bootdevp, vm_offset_t *bip, vm_offset_t
switch(rootdev->dd.d_dev->dv_type) {
case DEVT_CD:
/* Pass in BIOS device number. */
bi.bi_bios_dev = bc_unit2bios(rootdev->dd.d_unit);
bootdevnr = bc_getdev(rootdev);
break;
case DEVT_DISK:
/* pass in the BIOS device number of the current disk */
bi.bi_bios_dev = bd_unit2bios(rootdev->dd.d_unit);
bi.bi_bios_dev = bd_unit2bios(rootdev);
bootdevnr = bd_getdev(rootdev);
break;

View file

@ -91,17 +91,15 @@ extern struct devdesc currdev; /* our current device */
/* exported devices XXX rename? */
extern struct devsw bioscd;
extern struct devsw biosdisk;
extern struct devsw biosfd;
extern struct devsw bioshd;
extern struct devsw pxedisk;
extern struct fs_ops pxe_fsops;
int bc_add(int biosdev); /* Register CD booted from. */
int bc_getdev(struct i386_devdesc *dev); /* return dev_t for (dev) */
int bc_bios2unit(int biosdev); /* xlate BIOS device -> bioscd unit */
int bc_unit2bios(int unit); /* xlate bioscd unit -> BIOS device */
uint32_t bd_getbigeom(int bunit); /* return geometry in bootinfo format */
int bd_bios2unit(int biosdev); /* xlate BIOS device -> biosdisk unit */
int bd_unit2bios(int unit); /* xlate biosdisk unit -> BIOS device */
int bd_unit2bios(struct i386_devdesc *); /* xlate biosdisk -> 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);

View file

@ -113,7 +113,7 @@ command_chain(int argc, char *argv[])
relocater_data[0].dest = 0x7C00;
relocater_data[0].size = size;
relocator_edx = bd_unit2bios(rootdev->dd.d_unit);
relocator_edx = bd_unit2bios(rootdev);
relocator_esi = relocater_size;
relocator_ds = 0;
relocator_es = 0;

View file

@ -51,8 +51,9 @@ extern struct devsw fwohci;
/* Exported for libstand */
struct devsw *devsw[] = {
&biosfd,
&bioscd,
&biosdisk,
&bioshd,
#if defined(LOADER_NFS_SUPPORT) || defined(LOADER_TFTP_SUPPORT)
&pxedisk,
#endif

View file

@ -251,14 +251,14 @@ extract_currdev(void)
int biosdev = -1;
/* Assume we are booting from a BIOS disk by default */
new_currdev.dd.d_dev = &biosdisk;
new_currdev.dd.d_dev = &bioshd;
/* new-style boot loaders such as pxeldr and cdldr */
if (kargs->bootinfo == 0) {
if ((kargs->bootflags & KARGS_FLAGS_CD) != 0) {
/* we are booting from a CD with cdboot */
new_currdev.dd.d_dev = &bioscd;
new_currdev.dd.d_unit = bc_bios2unit(initial_bootdev);
new_currdev.dd.d_unit = bd_bios2unit(initial_bootdev);
} else if ((kargs->bootflags & KARGS_FLAGS_PXE) != 0) {
/* we are booting from pxeldr */
new_currdev.dd.d_dev = &pxedisk;
@ -318,7 +318,7 @@ extract_currdev(void)
* If we are booting off of a BIOS disk and we didn't succeed in determining
* which one we booted off of, just use disk0: as a reasonable default.
*/
if ((new_currdev.dd.d_dev->dv_type == biosdisk.dv_type) &&
if ((new_currdev.dd.d_dev->dv_type == bioshd.dv_type) &&
((new_currdev.dd.d_unit = bd_bios2unit(biosdev)) == -1)) {
printf("Can't work out which disk we are booting from.\n"
"Guessed BIOS device 0x%x not found by probes, defaulting to disk0:\n", biosdev);
@ -390,18 +390,16 @@ static void
i386_zfs_probe(void)
{
char devname[32];
int unit;
struct i386_devdesc dev;
/*
* Open all the disks we can find and see if we can reconstruct
* ZFS pools from them.
*/
for (unit = 0; unit < MAXBDDEV; unit++) {
if (bd_unit2bios(unit) == -1)
break;
if (bd_unit2bios(unit) < 0x80)
continue;
sprintf(devname, "disk%d:", unit);
dev.dd.d_dev = &bioshd;
for (dev.dd.d_unit = 0; bd_unit2bios(&dev) >= 0; dev.dd.d_unit++) {
snprintf(devname, sizeof(devname), "%s%d:", bioshd.dv_name,
dev.dd.d_unit);
zfs_probe_dev(devname, NULL);
}
}