Page MenuHomeFreeBSD

No OneTemporary

Size
85 KB
Referenced Files
None
Subscribers
None
Index: stable/11/stand/common/disk.c
===================================================================
--- stable/11/stand/common/disk.c (revision 346474)
+++ stable/11/stand/common/disk.c (revision 346475)
@@ -1,442 +1,440 @@
/*-
* Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
* Copyright (c) 2012 Andrey V. Elsukov <ae@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/disk.h>
#include <sys/queue.h>
#include <stand.h>
#include <stdarg.h>
#include <bootstrap.h>
#include <part.h>
#include "disk.h"
#ifdef DISK_DEBUG
# define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args)
#else
# define DEBUG(fmt, args...)
#endif
struct open_disk {
struct ptable *table;
uint64_t mediasize;
uint64_t entrysize;
u_int sectorsize;
};
struct print_args {
struct disk_devdesc *dev;
const char *prefix;
int verbose;
};
/* Convert size to a human-readable number. */
static char *
display_size(uint64_t size, u_int sectorsize)
{
static char buf[80];
char unit;
size = size * sectorsize / 1024;
unit = 'K';
if (size >= 10485760000LL) {
size /= 1073741824;
unit = 'T';
} else if (size >= 10240000) {
size /= 1048576;
unit = 'G';
} else if (size >= 10000) {
size /= 1024;
unit = 'M';
}
sprintf(buf, "%ld%cB", (long)size, unit);
return (buf);
}
int
ptblread(void *d, void *buf, size_t blocks, uint64_t offset)
{
struct disk_devdesc *dev;
struct open_disk *od;
dev = (struct disk_devdesc *)d;
od = (struct open_disk *)dev->dd.d_opendata;
/*
* The strategy function assumes the offset is in units of 512 byte
* sectors. For larger sector sizes, we need to adjust the offset to
* match the actual sector size.
*/
offset *= (od->sectorsize / 512);
/*
* As the GPT backup partition is located at the end of the disk,
* to avoid reading past disk end, flag bcache not to use RA.
*/
return (dev->dd.d_dev->dv_strategy(dev, F_READ | F_NORA, offset,
blocks * od->sectorsize, (char *)buf, NULL));
}
#define PWIDTH 35
static int
ptable_print(void *arg, const char *pname, const struct ptable_entry *part)
{
struct disk_devdesc dev;
struct print_args *pa, bsd;
struct open_disk *od;
struct ptable *table;
char line[80];
int res;
pa = (struct print_args *)arg;
od = (struct open_disk *)pa->dev->dd.d_opendata;
sprintf(line, " %s%s: %s", pa->prefix, pname,
parttype2str(part->type));
if (pa->verbose)
sprintf(line, "%-*s%s", PWIDTH, line,
display_size(part->end - part->start + 1,
od->sectorsize));
strcat(line, "\n");
if (pager_output(line))
return 1;
res = 0;
if (part->type == PART_FREEBSD) {
/* Open slice with BSD label */
dev.dd.d_dev = pa->dev->dd.d_dev;
dev.dd.d_unit = pa->dev->dd.d_unit;
dev.d_slice = part->index;
dev.d_partition = -1;
if (disk_open(&dev, part->end - part->start + 1,
od->sectorsize) == 0) {
table = ptable_open(&dev, part->end - part->start + 1,
od->sectorsize, ptblread);
if (table != NULL) {
sprintf(line, " %s%s", pa->prefix, pname);
bsd.dev = pa->dev;
bsd.prefix = line;
bsd.verbose = pa->verbose;
res = ptable_iterate(table, &bsd, ptable_print);
ptable_close(table);
}
disk_close(&dev);
}
}
return (res);
}
#undef PWIDTH
int
disk_print(struct disk_devdesc *dev, char *prefix, int verbose)
{
struct open_disk *od;
struct print_args pa;
/* Disk should be opened */
od = (struct open_disk *)dev->dd.d_opendata;
pa.dev = dev;
pa.prefix = prefix;
pa.verbose = verbose;
return (ptable_iterate(od->table, &pa, ptable_print));
}
int
disk_read(struct disk_devdesc *dev, void *buf, uint64_t offset, u_int blocks)
{
struct open_disk *od;
int ret;
od = (struct open_disk *)dev->dd.d_opendata;
ret = dev->dd.d_dev->dv_strategy(dev, F_READ, dev->d_offset + offset,
blocks * od->sectorsize, buf, NULL);
return (ret);
}
int
disk_write(struct disk_devdesc *dev, void *buf, uint64_t offset, u_int blocks)
{
struct open_disk *od;
int ret;
od = (struct open_disk *)dev->dd.d_opendata;
ret = dev->dd.d_dev->dv_strategy(dev, F_WRITE, dev->d_offset + offset,
blocks * od->sectorsize, buf, NULL);
return (ret);
}
int
disk_ioctl(struct disk_devdesc *dev, u_long cmd, void *data)
{
struct open_disk *od = dev->dd.d_opendata;
if (od == NULL)
return (ENOTTY);
switch (cmd) {
case DIOCGSECTORSIZE:
*(u_int *)data = od->sectorsize;
break;
case DIOCGMEDIASIZE:
if (dev->d_offset == 0)
*(uint64_t *)data = od->mediasize;
else
*(uint64_t *)data = od->entrysize * od->sectorsize;
break;
default:
return (ENOTTY);
}
return (0);
}
int
disk_open(struct disk_devdesc *dev, uint64_t mediasize, u_int sectorsize)
{
struct disk_devdesc partdev;
struct open_disk *od;
struct ptable *table;
struct ptable_entry part;
int rc, slice, partition;
rc = 0;
od = (struct open_disk *)malloc(sizeof(struct open_disk));
if (od == NULL) {
DEBUG("no memory");
return (ENOMEM);
}
dev->dd.d_opendata = od;
od->entrysize = 0;
od->mediasize = mediasize;
od->sectorsize = sectorsize;
/*
* While we are reading disk metadata, make sure we do it relative
* to the start of the disk
*/
memcpy(&partdev, dev, sizeof(partdev));
partdev.d_offset = 0;
partdev.d_slice = -1;
partdev.d_partition = -1;
dev->d_offset = 0;
table = NULL;
slice = dev->d_slice;
partition = dev->d_partition;
DEBUG("%s unit %d, slice %d, partition %d => %p",
disk_fmtdev(dev), dev->dd.d_unit, dev->d_slice, dev->d_partition, od);
/* Determine disk layout. */
od->table = ptable_open(&partdev, mediasize / sectorsize, sectorsize,
ptblread);
if (od->table == NULL) {
DEBUG("Can't read partition table");
rc = ENXIO;
goto out;
}
if (ptable_getsize(od->table, &mediasize) != 0) {
rc = ENXIO;
goto out;
}
- if (mediasize > od->mediasize) {
- od->mediasize = mediasize;
- }
+ od->mediasize = mediasize;
if (ptable_gettype(od->table) == PTABLE_BSD &&
partition >= 0) {
/* It doesn't matter what value has d_slice */
rc = ptable_getpart(od->table, &part, partition);
if (rc == 0) {
dev->d_offset = part.start;
od->entrysize = part.end - part.start + 1;
}
} else if (ptable_gettype(od->table) == PTABLE_ISO9660) {
dev->d_offset = 0;
od->entrysize = mediasize;
} else if (slice >= 0) {
/* Try to get information about partition */
if (slice == 0)
rc = ptable_getbestpart(od->table, &part);
else
rc = ptable_getpart(od->table, &part, slice);
if (rc != 0) /* Partition doesn't exist */
goto out;
dev->d_offset = part.start;
od->entrysize = part.end - part.start + 1;
slice = part.index;
if (ptable_gettype(od->table) == PTABLE_GPT) {
partition = 255;
goto out; /* Nothing more to do */
} else if (partition == 255) {
/*
* When we try to open GPT partition, but partition
* table isn't GPT, reset d_partition value to -1
* and try to autodetect appropriate value.
*/
partition = -1;
}
/*
* If d_partition < 0 and we are looking at a BSD slice,
* then try to read BSD label, otherwise return the
* whole MBR slice.
*/
if (partition == -1 &&
part.type != PART_FREEBSD)
goto out;
/* Try to read BSD label */
table = ptable_open(dev, part.end - part.start + 1,
od->sectorsize, ptblread);
if (table == NULL) {
DEBUG("Can't read BSD label");
rc = ENXIO;
goto out;
}
/*
* If slice contains BSD label and d_partition < 0, then
* assume the 'a' partition. Otherwise just return the
* whole MBR slice, because it can contain ZFS.
*/
if (partition < 0) {
if (ptable_gettype(table) != PTABLE_BSD)
goto out;
partition = 0;
}
rc = ptable_getpart(table, &part, partition);
if (rc != 0)
goto out;
dev->d_offset += part.start;
od->entrysize = part.end - part.start + 1;
}
out:
if (table != NULL)
ptable_close(table);
if (rc != 0) {
if (od->table != NULL)
ptable_close(od->table);
free(od);
DEBUG("%s could not open", disk_fmtdev(dev));
} else {
/* Save the slice and partition number to the dev */
dev->d_slice = slice;
dev->d_partition = partition;
DEBUG("%s offset %lld => %p", disk_fmtdev(dev),
(long long)dev->d_offset, od);
}
return (rc);
}
int
disk_close(struct disk_devdesc *dev)
{
struct open_disk *od;
od = (struct open_disk *)dev->dd.d_opendata;
DEBUG("%s closed => %p", disk_fmtdev(dev), od);
ptable_close(od->table);
free(od);
return (0);
}
char*
disk_fmtdev(struct disk_devdesc *dev)
{
static char buf[128];
char *cp;
cp = buf + sprintf(buf, "%s%d", dev->dd.d_dev->dv_name, dev->dd.d_unit);
if (dev->d_slice >= 0) {
#ifdef LOADER_GPT_SUPPORT
if (dev->d_partition == 255) {
sprintf(cp, "p%d:", dev->d_slice);
return (buf);
} else
#endif
#ifdef LOADER_MBR_SUPPORT
cp += sprintf(cp, "s%d", dev->d_slice);
#endif
}
if (dev->d_partition >= 0)
cp += sprintf(cp, "%c", dev->d_partition + 'a');
strcat(cp, ":");
return (buf);
}
int
disk_parsedev(struct disk_devdesc *dev, const char *devspec, const char **path)
{
int unit, slice, partition;
const char *np;
char *cp;
np = devspec;
unit = slice = partition = -1;
if (*np != '\0' && *np != ':') {
unit = strtol(np, &cp, 10);
if (cp == np)
return (EUNIT);
#ifdef LOADER_GPT_SUPPORT
if (*cp == 'p') {
np = cp + 1;
slice = strtol(np, &cp, 10);
if (np == cp)
return (ESLICE);
/* we don't support nested partitions on GPT */
if (*cp != '\0' && *cp != ':')
return (EINVAL);
partition = 255;
} else
#endif
#ifdef LOADER_MBR_SUPPORT
if (*cp == 's') {
np = cp + 1;
slice = strtol(np, &cp, 10);
if (np == cp)
return (ESLICE);
}
#endif
if (*cp != '\0' && *cp != ':') {
partition = *cp - 'a';
if (partition < 0)
return (EPART);
cp++;
}
} else
return (EINVAL);
if (*cp != '\0' && *cp != ':')
return (EINVAL);
dev->dd.d_unit = unit;
dev->d_slice = slice;
dev->d_partition = partition;
if (path != NULL)
*path = (*cp == '\0') ? cp: cp + 1;
return (0);
}
Index: stable/11/stand/common/part.c
===================================================================
--- stable/11/stand/common/part.c (revision 346474)
+++ stable/11/stand/common/part.c (revision 346475)
@@ -1,1136 +1,1135 @@
/*-
* Copyright (c) 2012 Andrey V. Elsukov <ae@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 AUTHORS 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 AUTHORS 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/diskmbr.h>
#include <sys/disklabel.h>
#include <sys/endian.h>
#include <sys/gpt.h>
#include <sys/stddef.h>
#include <sys/queue.h>
#include <sys/vtoc.h>
#include <fs/cd9660/iso.h>
#include <crc32.h>
#include <part.h>
#include <uuid.h>
#ifdef PART_DEBUG
#define DEBUG(fmt, args...) printf("%s: " fmt "\n", __func__, ## args)
#else
#define DEBUG(fmt, args...)
#endif
#ifdef LOADER_GPT_SUPPORT
#define MAXTBLSZ 64
static const uuid_t gpt_uuid_unused = GPT_ENT_TYPE_UNUSED;
static const uuid_t gpt_uuid_ms_basic_data = GPT_ENT_TYPE_MS_BASIC_DATA;
static const uuid_t gpt_uuid_freebsd_ufs = GPT_ENT_TYPE_FREEBSD_UFS;
static const uuid_t gpt_uuid_efi = GPT_ENT_TYPE_EFI;
static const uuid_t gpt_uuid_freebsd = GPT_ENT_TYPE_FREEBSD;
static const uuid_t gpt_uuid_freebsd_boot = GPT_ENT_TYPE_FREEBSD_BOOT;
static const uuid_t gpt_uuid_freebsd_nandfs = GPT_ENT_TYPE_FREEBSD_NANDFS;
static const uuid_t gpt_uuid_freebsd_swap = GPT_ENT_TYPE_FREEBSD_SWAP;
static const uuid_t gpt_uuid_freebsd_zfs = GPT_ENT_TYPE_FREEBSD_ZFS;
static const uuid_t gpt_uuid_freebsd_vinum = GPT_ENT_TYPE_FREEBSD_VINUM;
#endif
struct pentry {
struct ptable_entry part;
uint64_t flags;
union {
uint8_t bsd;
uint8_t mbr;
uuid_t gpt;
uint16_t vtoc8;
} type;
STAILQ_ENTRY(pentry) entry;
};
struct ptable {
enum ptable_type type;
uint16_t sectorsize;
uint64_t sectors;
STAILQ_HEAD(, pentry) entries;
};
static struct parttypes {
enum partition_type type;
const char *desc;
} ptypes[] = {
{ PART_UNKNOWN, "Unknown" },
{ PART_EFI, "EFI" },
{ PART_FREEBSD, "FreeBSD" },
{ PART_FREEBSD_BOOT, "FreeBSD boot" },
{ PART_FREEBSD_NANDFS, "FreeBSD nandfs" },
{ PART_FREEBSD_UFS, "FreeBSD UFS" },
{ PART_FREEBSD_ZFS, "FreeBSD ZFS" },
{ PART_FREEBSD_SWAP, "FreeBSD swap" },
{ PART_FREEBSD_VINUM, "FreeBSD vinum" },
{ PART_LINUX, "Linux" },
{ PART_LINUX_SWAP, "Linux swap" },
{ PART_DOS, "DOS/Windows" },
{ PART_ISO9660, "ISO9660" },
};
const char *
parttype2str(enum partition_type type)
{
size_t i;
for (i = 0; i < nitems(ptypes); i++)
if (ptypes[i].type == type)
return (ptypes[i].desc);
return (ptypes[0].desc);
}
#ifdef LOADER_GPT_SUPPORT
static void
uuid_letoh(uuid_t *uuid)
{
uuid->time_low = le32toh(uuid->time_low);
uuid->time_mid = le16toh(uuid->time_mid);
uuid->time_hi_and_version = le16toh(uuid->time_hi_and_version);
}
static enum partition_type
gpt_parttype(uuid_t type)
{
if (uuid_equal(&type, &gpt_uuid_efi, NULL))
return (PART_EFI);
else if (uuid_equal(&type, &gpt_uuid_ms_basic_data, NULL))
return (PART_DOS);
else if (uuid_equal(&type, &gpt_uuid_freebsd_boot, NULL))
return (PART_FREEBSD_BOOT);
else if (uuid_equal(&type, &gpt_uuid_freebsd_ufs, NULL))
return (PART_FREEBSD_UFS);
else if (uuid_equal(&type, &gpt_uuid_freebsd_zfs, NULL))
return (PART_FREEBSD_ZFS);
else if (uuid_equal(&type, &gpt_uuid_freebsd_swap, NULL))
return (PART_FREEBSD_SWAP);
else if (uuid_equal(&type, &gpt_uuid_freebsd_vinum, NULL))
return (PART_FREEBSD_VINUM);
else if (uuid_equal(&type, &gpt_uuid_freebsd_nandfs, NULL))
return (PART_FREEBSD_NANDFS);
else if (uuid_equal(&type, &gpt_uuid_freebsd, NULL))
return (PART_FREEBSD);
return (PART_UNKNOWN);
}
static struct gpt_hdr *
gpt_checkhdr(struct gpt_hdr *hdr, uint64_t lba_self, uint64_t lba_last,
uint16_t sectorsize)
{
uint32_t sz, crc;
if (memcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)) != 0) {
DEBUG("no GPT signature");
return (NULL);
}
sz = le32toh(hdr->hdr_size);
if (sz < 92 || sz > sectorsize) {
DEBUG("invalid GPT header size: %d", sz);
return (NULL);
}
crc = le32toh(hdr->hdr_crc_self);
hdr->hdr_crc_self = 0;
if (crc32(hdr, sz) != crc) {
DEBUG("GPT header's CRC doesn't match");
return (NULL);
}
hdr->hdr_crc_self = crc;
hdr->hdr_revision = le32toh(hdr->hdr_revision);
if (hdr->hdr_revision < GPT_HDR_REVISION) {
DEBUG("unsupported GPT revision %d", hdr->hdr_revision);
return (NULL);
}
hdr->hdr_lba_self = le64toh(hdr->hdr_lba_self);
if (hdr->hdr_lba_self != lba_self) {
DEBUG("self LBA doesn't match");
return (NULL);
}
hdr->hdr_lba_alt = le64toh(hdr->hdr_lba_alt);
if (hdr->hdr_lba_alt == hdr->hdr_lba_self) {
DEBUG("invalid alternate LBA");
return (NULL);
}
hdr->hdr_entries = le32toh(hdr->hdr_entries);
hdr->hdr_entsz = le32toh(hdr->hdr_entsz);
if (hdr->hdr_entries == 0 ||
hdr->hdr_entsz < sizeof(struct gpt_ent) ||
sectorsize % hdr->hdr_entsz != 0) {
DEBUG("invalid entry size or number of entries");
return (NULL);
}
hdr->hdr_lba_start = le64toh(hdr->hdr_lba_start);
hdr->hdr_lba_end = le64toh(hdr->hdr_lba_end);
hdr->hdr_lba_table = le64toh(hdr->hdr_lba_table);
hdr->hdr_crc_table = le32toh(hdr->hdr_crc_table);
uuid_letoh(&hdr->hdr_uuid);
return (hdr);
}
static int
gpt_checktbl(const struct gpt_hdr *hdr, uint8_t *tbl, size_t size,
uint64_t lba_last)
{
struct gpt_ent *ent;
uint32_t i, cnt;
cnt = size / hdr->hdr_entsz;
if (hdr->hdr_entries <= cnt) {
cnt = hdr->hdr_entries;
/* Check CRC only when buffer size is enough for table. */
if (hdr->hdr_crc_table !=
crc32(tbl, hdr->hdr_entries * hdr->hdr_entsz)) {
DEBUG("GPT table's CRC doesn't match");
return (-1);
}
}
for (i = 0; i < cnt; i++) {
ent = (struct gpt_ent *)(tbl + i * hdr->hdr_entsz);
uuid_letoh(&ent->ent_type);
if (uuid_equal(&ent->ent_type, &gpt_uuid_unused, NULL))
continue;
ent->ent_lba_start = le64toh(ent->ent_lba_start);
ent->ent_lba_end = le64toh(ent->ent_lba_end);
}
return (0);
}
static struct ptable *
ptable_gptread(struct ptable *table, void *dev, diskread_t dread)
{
struct pentry *entry;
struct gpt_hdr *phdr, hdr;
struct gpt_ent *ent;
uint8_t *buf, *tbl;
uint64_t offset;
int pri, sec;
size_t size, i;
buf = malloc(table->sectorsize);
if (buf == NULL)
return (NULL);
tbl = malloc(table->sectorsize * MAXTBLSZ);
if (tbl == NULL) {
free(buf);
return (NULL);
}
/* Read the primary GPT header. */
if (dread(dev, buf, 1, 1) != 0) {
ptable_close(table);
table = NULL;
goto out;
}
pri = sec = 0;
/* Check the primary GPT header. */
phdr = gpt_checkhdr((struct gpt_hdr *)buf, 1, table->sectors - 1,
table->sectorsize);
if (phdr != NULL) {
/* Read the primary GPT table. */
size = MIN(MAXTBLSZ,
howmany(phdr->hdr_entries * phdr->hdr_entsz,
table->sectorsize));
if (dread(dev, tbl, size, phdr->hdr_lba_table) == 0 &&
gpt_checktbl(phdr, tbl, size * table->sectorsize,
table->sectors - 1) == 0) {
memcpy(&hdr, phdr, sizeof(hdr));
pri = 1;
}
}
offset = pri ? hdr.hdr_lba_alt: table->sectors - 1;
/* Read the backup GPT header. */
if (dread(dev, buf, 1, offset) != 0)
phdr = NULL;
else
phdr = gpt_checkhdr((struct gpt_hdr *)buf, offset,
table->sectors - 1, table->sectorsize);
if (phdr != NULL) {
/*
* Compare primary and backup headers.
* If they are equal, then we do not need to read backup
* table. If they are different, then prefer backup header
* and try to read backup table.
*/
if (pri == 0 ||
uuid_equal(&hdr.hdr_uuid, &phdr->hdr_uuid, NULL) == 0 ||
hdr.hdr_revision != phdr->hdr_revision ||
hdr.hdr_size != phdr->hdr_size ||
hdr.hdr_lba_start != phdr->hdr_lba_start ||
hdr.hdr_lba_end != phdr->hdr_lba_end ||
hdr.hdr_entries != phdr->hdr_entries ||
hdr.hdr_entsz != phdr->hdr_entsz ||
hdr.hdr_crc_table != phdr->hdr_crc_table) {
/* Read the backup GPT table. */
size = MIN(MAXTBLSZ,
howmany(phdr->hdr_entries * phdr->hdr_entsz,
table->sectorsize));
if (dread(dev, tbl, size, phdr->hdr_lba_table) == 0 &&
gpt_checktbl(phdr, tbl, size * table->sectorsize,
table->sectors - 1) == 0) {
memcpy(&hdr, phdr, sizeof(hdr));
sec = 1;
}
}
}
if (pri == 0 && sec == 0) {
/* Both primary and backup tables are invalid. */
table->type = PTABLE_NONE;
goto out;
}
DEBUG("GPT detected");
size = MIN(hdr.hdr_entries * hdr.hdr_entsz,
MAXTBLSZ * table->sectorsize);
/*
* If the disk's sector count is smaller than the sector count recorded
* in the disk's GPT table header, set the table->sectors to the value
* recorded in GPT tables. This is done to work around buggy firmware
* that returns truncated disk sizes.
*
* Note, this is still not a foolproof way to get disk's size. For
* example, an image file can be truncated when copied to smaller media.
*/
- if (hdr.hdr_lba_alt + 1 > table->sectors)
- table->sectors = hdr.hdr_lba_alt + 1;
+ table->sectors = hdr.hdr_lba_alt + 1;
for (i = 0; i < size / hdr.hdr_entsz; i++) {
ent = (struct gpt_ent *)(tbl + i * hdr.hdr_entsz);
if (uuid_equal(&ent->ent_type, &gpt_uuid_unused, NULL))
continue;
/* Simple sanity checks. */
if (ent->ent_lba_start < hdr.hdr_lba_start ||
ent->ent_lba_end > hdr.hdr_lba_end ||
ent->ent_lba_start > ent->ent_lba_end)
continue;
entry = malloc(sizeof(*entry));
if (entry == NULL)
break;
entry->part.start = ent->ent_lba_start;
entry->part.end = ent->ent_lba_end;
entry->part.index = i + 1;
entry->part.type = gpt_parttype(ent->ent_type);
entry->flags = le64toh(ent->ent_attr);
memcpy(&entry->type.gpt, &ent->ent_type, sizeof(uuid_t));
STAILQ_INSERT_TAIL(&table->entries, entry, entry);
DEBUG("new GPT partition added");
}
out:
free(buf);
free(tbl);
return (table);
}
#endif /* LOADER_GPT_SUPPORT */
#ifdef LOADER_MBR_SUPPORT
/* We do not need to support too many EBR partitions in the loader */
#define MAXEBRENTRIES 8
static enum partition_type
mbr_parttype(uint8_t type)
{
switch (type) {
case DOSPTYP_386BSD:
return (PART_FREEBSD);
case DOSPTYP_LINSWP:
return (PART_LINUX_SWAP);
case DOSPTYP_LINUX:
return (PART_LINUX);
case 0x01:
case 0x04:
case 0x06:
case 0x07:
case 0x0b:
case 0x0c:
case 0x0e:
return (PART_DOS);
}
return (PART_UNKNOWN);
}
static struct ptable *
ptable_ebrread(struct ptable *table, void *dev, diskread_t dread)
{
struct dos_partition *dp;
struct pentry *e1, *entry;
uint32_t start, end, offset;
u_char *buf;
int i, index;
STAILQ_FOREACH(e1, &table->entries, entry) {
if (e1->type.mbr == DOSPTYP_EXT ||
e1->type.mbr == DOSPTYP_EXTLBA)
break;
}
if (e1 == NULL)
return (table);
index = 5;
offset = e1->part.start;
buf = malloc(table->sectorsize);
if (buf == NULL)
return (table);
DEBUG("EBR detected");
for (i = 0; i < MAXEBRENTRIES; i++) {
#if 0 /* Some BIOSes return an incorrect number of sectors */
if (offset >= table->sectors)
break;
#endif
if (dread(dev, buf, 1, offset) != 0)
break;
dp = (struct dos_partition *)(buf + DOSPARTOFF);
if (dp[0].dp_typ == 0)
break;
start = le32toh(dp[0].dp_start);
if (dp[0].dp_typ == DOSPTYP_EXT &&
dp[1].dp_typ == 0) {
offset = e1->part.start + start;
continue;
}
end = le32toh(dp[0].dp_size);
entry = malloc(sizeof(*entry));
if (entry == NULL)
break;
entry->part.start = offset + start;
entry->part.end = entry->part.start + end - 1;
entry->part.index = index++;
entry->part.type = mbr_parttype(dp[0].dp_typ);
entry->flags = dp[0].dp_flag;
entry->type.mbr = dp[0].dp_typ;
STAILQ_INSERT_TAIL(&table->entries, entry, entry);
DEBUG("new EBR partition added");
if (dp[1].dp_typ == 0)
break;
offset = e1->part.start + le32toh(dp[1].dp_start);
}
free(buf);
return (table);
}
#endif /* LOADER_MBR_SUPPORT */
static enum partition_type
bsd_parttype(uint8_t type)
{
switch (type) {
case FS_NANDFS:
return (PART_FREEBSD_NANDFS);
case FS_SWAP:
return (PART_FREEBSD_SWAP);
case FS_BSDFFS:
return (PART_FREEBSD_UFS);
case FS_VINUM:
return (PART_FREEBSD_VINUM);
case FS_ZFS:
return (PART_FREEBSD_ZFS);
}
return (PART_UNKNOWN);
}
static struct ptable *
ptable_bsdread(struct ptable *table, void *dev, diskread_t dread)
{
struct disklabel *dl;
struct partition *part;
struct pentry *entry;
uint8_t *buf;
uint32_t raw_offset;
int i;
if (table->sectorsize < sizeof(struct disklabel)) {
DEBUG("Too small sectorsize");
return (table);
}
buf = malloc(table->sectorsize);
if (buf == NULL)
return (table);
if (dread(dev, buf, 1, 1) != 0) {
DEBUG("read failed");
ptable_close(table);
table = NULL;
goto out;
}
dl = (struct disklabel *)buf;
if (le32toh(dl->d_magic) != DISKMAGIC &&
le32toh(dl->d_magic2) != DISKMAGIC)
goto out;
if (le32toh(dl->d_secsize) != table->sectorsize) {
DEBUG("unsupported sector size");
goto out;
}
dl->d_npartitions = le16toh(dl->d_npartitions);
if (dl->d_npartitions > 20 || dl->d_npartitions < 8) {
DEBUG("invalid number of partitions");
goto out;
}
DEBUG("BSD detected");
part = &dl->d_partitions[0];
raw_offset = le32toh(part[RAW_PART].p_offset);
for (i = 0; i < dl->d_npartitions; i++, part++) {
if (i == RAW_PART)
continue;
if (part->p_size == 0)
continue;
entry = malloc(sizeof(*entry));
if (entry == NULL)
break;
entry->part.start = le32toh(part->p_offset) - raw_offset;
entry->part.end = entry->part.start +
le32toh(part->p_size) - 1;
entry->part.type = bsd_parttype(part->p_fstype);
entry->part.index = i; /* starts from zero */
entry->type.bsd = part->p_fstype;
STAILQ_INSERT_TAIL(&table->entries, entry, entry);
DEBUG("new BSD partition added");
}
table->type = PTABLE_BSD;
out:
free(buf);
return (table);
}
#ifdef LOADER_VTOC8_SUPPORT
static enum partition_type
vtoc8_parttype(uint16_t type)
{
switch (type) {
case VTOC_TAG_FREEBSD_NANDFS:
return (PART_FREEBSD_NANDFS);
case VTOC_TAG_FREEBSD_SWAP:
return (PART_FREEBSD_SWAP);
case VTOC_TAG_FREEBSD_UFS:
return (PART_FREEBSD_UFS);
case VTOC_TAG_FREEBSD_VINUM:
return (PART_FREEBSD_VINUM);
case VTOC_TAG_FREEBSD_ZFS:
return (PART_FREEBSD_ZFS);
}
return (PART_UNKNOWN);
}
static struct ptable *
ptable_vtoc8read(struct ptable *table, void *dev, diskread_t dread)
{
struct pentry *entry;
struct vtoc8 *dl;
uint8_t *buf;
uint16_t sum, heads, sectors;
int i;
if (table->sectorsize != sizeof(struct vtoc8))
return (table);
buf = malloc(table->sectorsize);
if (buf == NULL)
return (table);
if (dread(dev, buf, 1, 0) != 0) {
DEBUG("read failed");
ptable_close(table);
table = NULL;
goto out;
}
dl = (struct vtoc8 *)buf;
/* Check the sum */
for (i = sum = 0; i < sizeof(struct vtoc8); i += sizeof(sum))
sum ^= be16dec(buf + i);
if (sum != 0) {
DEBUG("incorrect checksum");
goto out;
}
if (be16toh(dl->nparts) != VTOC8_NPARTS) {
DEBUG("invalid number of entries");
goto out;
}
sectors = be16toh(dl->nsecs);
heads = be16toh(dl->nheads);
if (sectors * heads == 0) {
DEBUG("invalid geometry");
goto out;
}
DEBUG("VTOC8 detected");
for (i = 0; i < VTOC8_NPARTS; i++) {
dl->part[i].tag = be16toh(dl->part[i].tag);
if (i == VTOC_RAW_PART ||
dl->part[i].tag == VTOC_TAG_UNASSIGNED)
continue;
entry = malloc(sizeof(*entry));
if (entry == NULL)
break;
entry->part.start = be32toh(dl->map[i].cyl) * heads * sectors;
entry->part.end = be32toh(dl->map[i].nblks) +
entry->part.start - 1;
entry->part.type = vtoc8_parttype(dl->part[i].tag);
entry->part.index = i; /* starts from zero */
entry->type.vtoc8 = dl->part[i].tag;
STAILQ_INSERT_TAIL(&table->entries, entry, entry);
DEBUG("new VTOC8 partition added");
}
table->type = PTABLE_VTOC8;
out:
free(buf);
return (table);
}
#endif /* LOADER_VTOC8_SUPPORT */
#define cdb2devb(bno) ((bno) * ISO_DEFAULT_BLOCK_SIZE / table->sectorsize)
static struct ptable *
ptable_iso9660read(struct ptable *table, void *dev, diskread_t dread)
{
uint8_t *buf;
struct iso_primary_descriptor *vd;
struct pentry *entry;
buf = malloc(table->sectorsize);
if (buf == NULL)
return (table);
if (dread(dev, buf, 1, cdb2devb(16)) != 0) {
DEBUG("read failed");
ptable_close(table);
table = NULL;
goto out;
}
vd = (struct iso_primary_descriptor *)buf;
if (bcmp(vd->id, ISO_STANDARD_ID, sizeof vd->id) != 0)
goto out;
entry = malloc(sizeof(*entry));
if (entry == NULL)
goto out;
entry->part.start = 0;
entry->part.end = table->sectors;
entry->part.type = PART_ISO9660;
entry->part.index = 0;
STAILQ_INSERT_TAIL(&table->entries, entry, entry);
table->type = PTABLE_ISO9660;
out:
free(buf);
return (table);
}
struct ptable *
ptable_open(void *dev, uint64_t sectors, uint16_t sectorsize,
diskread_t *dread)
{
struct dos_partition *dp;
struct ptable *table;
uint8_t *buf;
int i, count;
#ifdef LOADER_MBR_SUPPORT
struct pentry *entry;
uint32_t start, end;
int has_ext;
#endif
table = NULL;
buf = malloc(sectorsize);
if (buf == NULL)
return (NULL);
/* First, read the MBR. */
if (dread(dev, buf, 1, DOSBBSECTOR) != 0) {
DEBUG("read failed");
goto out;
}
table = malloc(sizeof(*table));
if (table == NULL)
goto out;
table->sectors = sectors;
table->sectorsize = sectorsize;
table->type = PTABLE_NONE;
STAILQ_INIT(&table->entries);
if (ptable_iso9660read(table, dev, dread) != NULL) {
if (table->type == PTABLE_ISO9660)
goto out;
}
#ifdef LOADER_VTOC8_SUPPORT
if (be16dec(buf + offsetof(struct vtoc8, magic)) == VTOC_MAGIC) {
if (ptable_vtoc8read(table, dev, dread) == NULL) {
/* Read error. */
table = NULL;
goto out;
} else if (table->type == PTABLE_VTOC8)
goto out;
}
#endif
/* Check the BSD label. */
if (ptable_bsdread(table, dev, dread) == NULL) { /* Read error. */
table = NULL;
goto out;
} else if (table->type == PTABLE_BSD)
goto out;
#if defined(LOADER_GPT_SUPPORT) || defined(LOADER_MBR_SUPPORT)
/* Check the MBR magic. */
if (buf[DOSMAGICOFFSET] != 0x55 ||
buf[DOSMAGICOFFSET + 1] != 0xaa) {
DEBUG("magic sequence not found");
#if defined(LOADER_GPT_SUPPORT)
/* There is no PMBR, check that we have backup GPT */
table->type = PTABLE_GPT;
table = ptable_gptread(table, dev, dread);
#endif
goto out;
}
/* Check that we have PMBR. Also do some validation. */
dp = (struct dos_partition *)(buf + DOSPARTOFF);
for (i = 0, count = 0; i < NDOSPART; i++) {
if (dp[i].dp_flag != 0 && dp[i].dp_flag != 0x80) {
DEBUG("invalid partition flag %x", dp[i].dp_flag);
goto out;
}
#ifdef LOADER_GPT_SUPPORT
if (dp[i].dp_typ == DOSPTYP_PMBR) {
table->type = PTABLE_GPT;
DEBUG("PMBR detected");
}
#endif
if (dp[i].dp_typ != 0)
count++;
}
/* Do we have some invalid values? */
if (table->type == PTABLE_GPT && count > 1) {
if (dp[1].dp_typ != DOSPTYP_HFS) {
table->type = PTABLE_NONE;
DEBUG("Incorrect PMBR, ignore it");
} else {
DEBUG("Bootcamp detected");
}
}
#ifdef LOADER_GPT_SUPPORT
if (table->type == PTABLE_GPT) {
table = ptable_gptread(table, dev, dread);
goto out;
}
#endif
#ifdef LOADER_MBR_SUPPORT
/* Read MBR. */
DEBUG("MBR detected");
table->type = PTABLE_MBR;
for (i = has_ext = 0; i < NDOSPART; i++) {
if (dp[i].dp_typ == 0)
continue;
start = le32dec(&(dp[i].dp_start));
end = le32dec(&(dp[i].dp_size));
if (start == 0 || end == 0)
continue;
#if 0 /* Some BIOSes return an incorrect number of sectors */
if (start + end - 1 >= sectors)
continue; /* XXX: ignore */
#endif
if (dp[i].dp_typ == DOSPTYP_EXT ||
dp[i].dp_typ == DOSPTYP_EXTLBA)
has_ext = 1;
entry = malloc(sizeof(*entry));
if (entry == NULL)
break;
entry->part.start = start;
entry->part.end = start + end - 1;
entry->part.index = i + 1;
entry->part.type = mbr_parttype(dp[i].dp_typ);
entry->flags = dp[i].dp_flag;
entry->type.mbr = dp[i].dp_typ;
STAILQ_INSERT_TAIL(&table->entries, entry, entry);
DEBUG("new MBR partition added");
}
if (has_ext) {
table = ptable_ebrread(table, dev, dread);
/* FALLTHROUGH */
}
#endif /* LOADER_MBR_SUPPORT */
#endif /* LOADER_MBR_SUPPORT || LOADER_GPT_SUPPORT */
out:
free(buf);
return (table);
}
void
ptable_close(struct ptable *table)
{
struct pentry *entry;
while (!STAILQ_EMPTY(&table->entries)) {
entry = STAILQ_FIRST(&table->entries);
STAILQ_REMOVE_HEAD(&table->entries, entry);
free(entry);
}
free(table);
}
enum ptable_type
ptable_gettype(const struct ptable *table)
{
return (table->type);
}
int
ptable_getsize(const struct ptable *table, uint64_t *sizep)
{
uint64_t tmp = table->sectors * table->sectorsize;
if (tmp < table->sectors)
return (EOVERFLOW);
if (sizep != NULL)
*sizep = tmp;
return (0);
}
int
ptable_getpart(const struct ptable *table, struct ptable_entry *part, int index)
{
struct pentry *entry;
if (part == NULL || table == NULL)
return (EINVAL);
STAILQ_FOREACH(entry, &table->entries, entry) {
if (entry->part.index != index)
continue;
memcpy(part, &entry->part, sizeof(*part));
return (0);
}
return (ENOENT);
}
/*
* 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
int
ptable_getbestpart(const struct ptable *table, struct ptable_entry *part)
{
struct pentry *entry, *best;
int pref, preflevel;
if (part == NULL || table == NULL)
return (EINVAL);
best = NULL;
preflevel = pref = PREF_NONE;
STAILQ_FOREACH(entry, &table->entries, entry) {
#ifdef LOADER_MBR_SUPPORT
if (table->type == PTABLE_MBR) {
switch (entry->type.mbr) {
case DOSPTYP_386BSD:
pref = entry->flags & 0x80 ? PREF_FBSD_ACT:
PREF_FBSD;
break;
case DOSPTYP_LINUX:
pref = entry->flags & 0x80 ? PREF_LINUX_ACT:
PREF_LINUX;
break;
case 0x01: /* DOS/Windows */
case 0x04:
case 0x06:
case 0x0c:
case 0x0e:
case DOSPTYP_FAT32:
pref = entry->flags & 0x80 ? PREF_DOS_ACT:
PREF_DOS;
break;
default:
pref = PREF_NONE;
}
}
#endif /* LOADER_MBR_SUPPORT */
#ifdef LOADER_GPT_SUPPORT
if (table->type == PTABLE_GPT) {
if (entry->part.type == PART_DOS)
pref = PREF_DOS;
else if (entry->part.type == PART_FREEBSD_UFS ||
entry->part.type == PART_FREEBSD_ZFS)
pref = PREF_FBSD;
else
pref = PREF_NONE;
}
#endif /* LOADER_GPT_SUPPORT */
#ifdef LOADER_PC98_SUPPORT
if (table->type == PTABLE_PC98) {
switch(entry->part.type & PC98_MID_MASK) {
case PC98_MID_386BSD: /* FreeBSD */
if ((entry->part.type & PC98_MID_BOOTABLE) &&
(preflevel > PREF_FBSD_ACT)) {
pref = i;
preflevel = PREF_FBSD_ACT;
} else if (preflevel > PREF_FBSD) {
pref = i;
preflevel = PREF_FBSD;
}
break;
case 0x11: /* DOS/Windows */
case 0x20:
case 0x21:
case 0x22:
case 0x23:
case 0x63:
if ((entry->part.type & PC98_MID_BOOTABLE) &&
(preflevel > PREF_DOS_ACT)) {
pref = i;
preflevel = PREF_DOS_ACT;
} else if (preflevel > PREF_DOS) {
pref = i;
preflevel = PREF_DOS;
}
break;
}
}
#endif /* LOADER_PC98_SUPPORT */
if (pref < preflevel) {
preflevel = pref;
best = entry;
}
}
if (best != NULL) {
memcpy(part, &best->part, sizeof(*part));
return (0);
}
return (ENOENT);
}
int
ptable_iterate(const struct ptable *table, void *arg, ptable_iterate_t *iter)
{
struct pentry *entry;
char name[32];
int ret = 0;
name[0] = '\0';
STAILQ_FOREACH(entry, &table->entries, entry) {
#ifdef LOADER_MBR_SUPPORT
if (table->type == PTABLE_MBR)
sprintf(name, "s%d", entry->part.index);
else
#endif
#ifdef LOADER_GPT_SUPPORT
if (table->type == PTABLE_GPT)
sprintf(name, "p%d", entry->part.index);
else
#endif
#ifdef LOADER_VTOC8_SUPPORT
if (table->type == PTABLE_VTOC8)
sprintf(name, "%c", (uint8_t) 'a' +
entry->part.index);
else
#endif
if (table->type == PTABLE_BSD)
sprintf(name, "%c", (uint8_t) 'a' +
entry->part.index);
if ((ret = iter(arg, name, &entry->part)) != 0)
return (ret);
}
return (ret);
}
#ifdef LOADER_PC98_SUPPORT
static int
bd_open_pc98(struct open_disk *od, struct i386_devdesc *dev)
{
struct pc98_partition *dptr;
struct disklabel *lp;
int sector, slice, i;
char buf[BUFSIZE];
/*
* 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.
*/
od->od_nslices = 0;
if (od->od_flags & BD_FLOPPY) {
sector = 0;
goto unsliced;
}
if (bd_read(od, 0, 1, buf)) {
DEBUG("error reading MBR");
return (EIO);
}
/*
* 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)");
return (ENOENT);
}
sector = 0;
goto unsliced; /* may be a floppy */
}
if (bd_read(od, 1, 1, buf)) {
DEBUG("error reading MBR");
return (EIO);
}
/*
* copy the partition table, then pick up any extended partitions.
*/
bcopy(buf + PC98_PARTOFF, &od->od_slicetab,
sizeof(struct pc98_partition) * PC98_NPARTS);
od->od_nslices = PC98_NPARTS; /* extended slices start here */
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);
return (ENOENT);
}
}
/* 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) {
return (ENOENT);
}
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_scyl * od->od_hds * od->od_sec +
dptr->dp_shd * od->od_sec + dptr->dp_ssect;
{
int end = dptr->dp_ecyl * od->od_hds * od->od_sec +
dptr->dp_ehd * od->od_sec + dptr->dp_esect;
DEBUG("slice entry %d at %d, %d sectors",
dev->d_kind.biosdisk.slice - 1, sector, end-sector);
}
/*
* If we are looking at a BSD slice, and the partition is < 0, assume the 'a' partition
*/
if ((dptr->dp_mid == DOSMID_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");
return (EIO);
}
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");
return (ENOENT);
}
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);
return (EPART);
}
#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;
}
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);
}
#endif /* LOADER_PC98_SUPPORT */
Index: stable/11/stand/i386/libi386/biosdisk.c
===================================================================
--- stable/11/stand/i386/libi386/biosdisk.c (revision 346474)
+++ stable/11/stand/i386/libi386/biosdisk.c (revision 346475)
@@ -1,800 +1,940 @@
/*-
* Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
* Copyright (c) 2012 Andrey V. Elsukov <ae@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 <sys/disk.h>
#include <sys/limits.h>
#include <stand.h>
#include <machine/bootinfo.h>
#include <stdarg.h>
#include <bootstrap.h>
#include <btxv86.h>
#include <edd.h>
#include "disk.h"
#include "libi386.h"
#define BIOS_NUMDRIVES 0x475
#define BIOSDISK_SECSIZE 512
#define BUFSIZE (1 * BIOSDISK_SECSIZE)
#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
/*
* List of BIOS devices, translation from disk unit number to
* BIOS unit number.
*/
static struct bdinfo
{
int bd_unit; /* BIOS unit number */
int bd_cyl; /* BIOS geometry */
int bd_hds;
int bd_sec;
int bd_flags;
#define BD_MODEINT13 0x0000
#define BD_MODEEDD1 0x0001
#define BD_MODEEDD3 0x0002
+#define BD_MODEEDD (BD_MODEEDD1 | BD_MODEEDD3)
#define BD_MODEMASK 0x0003
#define BD_FLOPPY 0x0004
+#define BD_NO_MEDIA 0x0008
int bd_type; /* BIOS 'drive type' (floppy only) */
uint16_t bd_sectorsize; /* Sector size */
uint64_t bd_sectors; /* Disk size */
int bd_open; /* reference counter */
void *bd_bcache; /* buffer cache data */
} bdinfo [MAXBDDEV];
static int nbdinfo = 0;
#define BD(dev) (bdinfo[(dev)->dd.d_unit])
#define BD_RD 0
#define BD_WR 1
static void bd_io_workaround(struct disk_devdesc *dev);
static int bd_io(struct disk_devdesc *, daddr_t, int, caddr_t, int);
static int bd_int13probe(struct bdinfo *bd);
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 int bd_ioctl(struct open_file *f, u_long cmd, void *data);
static int bd_print(int verbose);
struct devsw biosdisk = {
"disk",
DEVT_DISK,
bd_init,
bd_strategy,
bd_open,
bd_close,
bd_ioctl,
bd_print,
NULL
};
/*
* 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++) {
#ifndef VIRTUALBOX
/*
* Check the BIOS equipment list for number
* of fixed disks.
*/
if (base == 0x80 &&
(nfd >= *(unsigned char *)PTOV(BIOS_NUMDRIVES)))
break;
#endif
bdinfo[nbdinfo].bd_open = 0;
bdinfo[nbdinfo].bd_bcache = NULL;
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++;
}
}
bcache_add_dev(nbdinfo);
return (0);
}
/*
- * Try to detect a device supported by the legacy int13 BIOS
+ * Return EDD version or 0 if EDD is not supported on this drive.
*/
static int
-bd_int13probe(struct bdinfo *bd)
+bd_check_extensions(int unit)
{
- struct edd_params params;
- int ret = 1; /* assume success */
+ /* Determine if we can use EDD with this device. */
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x13;
+ v86.eax = 0x4100;
+ v86.edx = unit;
+ v86.ebx = 0x55aa;
+ v86int();
+ if (V86_CY(v86.efl) || /* carry set */
+ (v86.ebx & 0xffff) != 0xaa55) /* signature */
+ return (0);
+
+ /* extended disk access functions (AH=42h-44h,47h,48h) supported */
+ if ((v86.ecx & EDD_INTERFACE_FIXED_DISK) == 0)
+ return (0);
+
+ return ((v86.eax >> 8) & 0xff);
+}
+
+static void
+bd_reset_disk(int unit)
+{
+ /* reset disk */
v86.ctl = V86_FLAGS;
v86.addr = 0x13;
+ v86.eax = 0;
+ v86.edx = unit;
+ v86int();
+}
+
+/*
+ * Read CHS info. Return 0 on success, error otherwise.
+ */
+static int
+bd_get_diskinfo_std(struct bdinfo *bd)
+{
+ bzero(&v86, sizeof(v86));
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x13;
v86.eax = 0x800;
v86.edx = bd->bd_unit;
v86int();
- /* Don't error out if we get bad sector number, try EDD as well */
- if (V86_CY(v86.efl) || /* carry set */
- (v86.edx & 0xff) <= (unsigned)(bd->bd_unit & 0x7f)) /* unit # bad */
- return (0); /* skip device */
+ if (V86_CY(v86.efl) && ((v86.eax & 0xff00) != 0))
+ return ((v86.eax & 0xff00) >> 8);
- if ((v86.ecx & 0x3f) == 0) /* absurd sector number */
- ret = 0; /* set error */
+ /* return custom error on absurd sector number */
+ if ((v86.ecx & 0x3f) == 0)
+ return (0x60);
- /* Convert max cyl # -> # of cylinders */
bd->bd_cyl = ((v86.ecx & 0xc0) << 2) + ((v86.ecx & 0xff00) >> 8) + 1;
/* Convert max head # -> # of heads */
bd->bd_hds = ((v86.edx & 0xff00) >> 8) + 1;
bd->bd_sec = v86.ecx & 0x3f;
- bd->bd_type = v86.ebx & 0xff;
- bd->bd_flags |= BD_MODEINT13;
+ bd->bd_type = v86.ebx;
+ bd->bd_sectors = (uint64_t)bd->bd_cyl * bd->bd_hds * bd->bd_sec;
- /* Calculate sectors count from the geometry */
- bd->bd_sectors = bd->bd_cyl * bd->bd_hds * bd->bd_sec;
- bd->bd_sectorsize = BIOSDISK_SECSIZE;
- DEBUG("unit 0x%x geometry %d/%d/%d", bd->bd_unit, bd->bd_cyl,
- bd->bd_hds, bd->bd_sec);
+ return (0);
+}
- /* Determine if we can use EDD with this device. */
- v86.ctl = V86_FLAGS;
- v86.addr = 0x13;
- v86.eax = 0x4100;
- v86.edx = bd->bd_unit;
- v86.ebx = 0x55aa;
- v86int();
- if (V86_CY(v86.efl) || /* carry set */
- (v86.ebx & 0xffff) != 0xaa55 || /* signature */
- (v86.ecx & EDD_INTERFACE_FIXED_DISK) == 0)
- return (ret); /* return code from int13 AH=08 */
+/*
+ * Read EDD info. Return 0 on success, error otherwise.
+ */
+static int
+bd_get_diskinfo_ext(struct bdinfo *bd)
+{
+ struct edd_params params;
+ uint64_t total;
- /* EDD supported */
- bd->bd_flags |= BD_MODEEDD1;
- if ((v86.eax & 0xff00) >= 0x3000)
- bd->bd_flags |= BD_MODEEDD3;
/* Get disk params */
- params.len = sizeof(struct edd_params);
+ bzero(&params, sizeof(params));
+ params.len = sizeof(params);
v86.ctl = V86_FLAGS;
v86.addr = 0x13;
v86.eax = 0x4800;
v86.edx = bd->bd_unit;
v86.ds = VTOPSEG(&params);
v86.esi = VTOPOFF(&params);
v86int();
- if (!V86_CY(v86.efl)) {
- uint64_t total;
- /*
- * Sector size must be a multiple of 512 bytes.
- * An alternate test would be to check power of 2,
- * powerof2(params.sector_size).
- */
- if (params.sector_size % BIOSDISK_SECSIZE)
- bd->bd_sectorsize = BIOSDISK_SECSIZE;
- else
- bd->bd_sectorsize = params.sector_size;
+ if (V86_CY(v86.efl) && ((v86.eax & 0xff00) != 0))
+ return ((v86.eax & 0xff00) >> 8);
- total = bd->bd_sectorsize * params.sectors;
- if (params.sectors != 0) {
- /* Only update if we did not overflow. */
- if (total > params.sectors)
- bd->bd_sectors = params.sectors;
- }
+ /*
+ * Sector size must be a multiple of 512 bytes.
+ * An alternate test would be to check power of 2,
+ * powerof2(params.sector_size).
+ * 4K is largest read buffer we can use at this time.
+ */
+ if (params.sector_size >= 512 &&
+ params.sector_size <= 4096 &&
+ (params.sector_size % BIOSDISK_SECSIZE) == 0)
+ bd->bd_sectorsize = params.sector_size;
+ bd->bd_cyl = params.cylinders;
+ bd->bd_hds = params.heads;
+ bd->bd_sec = params.sectors_per_track;
+
+ if (params.sectors != 0) {
+ total = params.sectors;
+ } else {
total = (uint64_t)params.cylinders *
params.heads * params.sectors_per_track;
- if (total > 0 && bd->bd_sectors > total)
- bd->bd_sectors = total;
+ }
+ bd->bd_sectors = total;
- ret = 1;
+ return (0);
+}
+
+/*
+ * Try to detect a device supported by the legacy int13 BIOS
+ */
+static int
+bd_int13probe(struct bdinfo *bd)
+{
+ int edd;
+ int ret;
+
+ bd->bd_flags &= ~BD_NO_MEDIA;
+
+ edd = bd_check_extensions(bd->bd_unit);
+ if (edd == 0)
+ bd->bd_flags |= BD_MODEINT13;
+ else if (edd < 0x30)
+ bd->bd_flags |= BD_MODEEDD1;
+ else
+ bd->bd_flags |= BD_MODEEDD3;
+
+ /* Default sector size */
+ bd->bd_sectorsize = BIOSDISK_SECSIZE;
+
+ /*
+ * Test if the floppy device is present, so we can avoid receiving
+ * bogus information from bd_get_diskinfo_std().
+ */
+ if (bd->bd_unit < 0x80) {
+ /* reset disk */
+ bd_reset_disk(bd->bd_unit);
+
+ /* Get disk type */
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x13;
+ v86.eax = 0x1500;
+ v86.edx = bd->bd_unit;
+ v86int();
+ if (V86_CY(v86.efl) || (v86.eax & 0x300) == 0)
+ return (0);
}
- DEBUG("unit 0x%x flags %x, sectors %llu, sectorsize %u",
- bd->bd_unit, bd->bd_flags, bd->bd_sectors, bd->bd_sectorsize);
- return (ret);
+
+ ret = 1;
+ if (edd != 0)
+ ret = bd_get_diskinfo_ext(bd);
+ if (ret != 0 || bd->bd_sectors == 0)
+ ret = bd_get_diskinfo_std(bd);
+
+ if (ret != 0 && bd->bd_unit < 0x80) {
+ /* Set defaults for 1.44 floppy */
+ bd->bd_cyl = 80;
+ bd->bd_hds = 2;
+ bd->bd_sec = 18;
+ bd->bd_type = 4;
+ bd->bd_sectors = 2880;
+ /* Since we are there, there most likely is no media */
+ bd->bd_flags |= BD_NO_MEDIA;
+ ret = 0;
+ }
+
+ if (ret != 0) {
+ if (bd->bd_sectors != 0 && edd != 0) {
+ bd->bd_sec = 63;
+ bd->bd_hds = 255;
+ bd->bd_cyl =
+ (bd->bd_sectors + bd->bd_sec * bd->bd_hds - 1) /
+ bd->bd_sec * bd->bd_hds;
+ } else {
+ printf("Can not get information about %s unit %#x\n",
+ biosdisk.dv_name, bd->bd_unit);
+ return (0);
+ }
+ }
+
+ if (bd->bd_sec == 0)
+ bd->bd_sec = 63;
+ if (bd->bd_hds == 0)
+ bd->bd_hds = 255;
+
+ if (bd->bd_sectors == 0)
+ bd->bd_sectors = (uint64_t)bd->bd_cyl * bd->bd_hds * bd->bd_sec;
+
+ DEBUG("unit 0x%x geometry %d/%d/%d", bd->bd_unit, bd->bd_cyl,
+ bd->bd_hds, bd->bd_sec);
+
+ return (1);
}
/*
* Print information about disks
*/
static int
bd_print(int verbose)
{
static char line[80];
struct disk_devdesc dev;
int i, ret = 0;
if (nbdinfo == 0)
return (0);
printf("%s devices:", biosdisk.dv_name);
if ((ret = pager_output("\n")) != 0)
return (ret);
for (i = 0; i < nbdinfo; i++) {
snprintf(line, sizeof(line),
- " disk%d: BIOS drive %c (%ju X %u):\n", i,
+ " disk%d: BIOS drive %c (%s%ju X %u):\n", i,
(bdinfo[i].bd_unit < 0x80) ? ('A' + bdinfo[i].bd_unit):
('C' + bdinfo[i].bd_unit - 0x80),
+ (bdinfo[i].bd_flags & BD_NO_MEDIA) == BD_NO_MEDIA ?
+ "no media, " : "",
(uintmax_t)bdinfo[i].bd_sectors,
bdinfo[i].bd_sectorsize);
if ((ret = pager_output(line)) != 0)
break;
+ if ((bdinfo[i].bd_flags & BD_NO_MEDIA) == BD_NO_MEDIA)
+ continue;
+
dev.dd.d_dev = &biosdisk;
dev.dd.d_unit = i;
dev.d_slice = -1;
dev.d_partition = -1;
if (disk_open(&dev,
bdinfo[i].bd_sectorsize * bdinfo[i].bd_sectors,
bdinfo[i].bd_sectorsize) == 0) {
snprintf(line, sizeof(line), " disk%d", i);
ret = disk_print(&dev, line, verbose);
disk_close(&dev);
if (ret != 0)
break;
}
}
return (ret);
}
/*
+ * Read disk size from partition.
+ * This is needed to work around buggy BIOS systems returning
+ * wrong (truncated) disk media size.
+ * During bd_probe() we tested if the multiplication of bd_sectors
+ * would overflow so it should be safe to perform here.
+ */
+static uint64_t
+bd_disk_get_sectors(struct disk_devdesc *dev)
+{
+ struct disk_devdesc disk;
+ uint64_t size;
+
+ disk.dd.d_dev = dev->dd.d_dev;
+ disk.dd.d_unit = dev->dd.d_unit;
+ disk.d_slice = -1;
+ disk.d_partition = -1;
+ disk.d_offset = 0;
+
+ size = BD(dev).bd_sectors * BD(dev).bd_sectorsize;
+ if (disk_open(&disk, size, BD(dev).bd_sectorsize) == 0) {
+ (void) disk_ioctl(&disk, DIOCGMEDIASIZE, &size);
+ disk_close(&disk);
+ }
+ return (size / BD(dev).bd_sectorsize);
+}
+
+/*
* 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, ...)
{
struct disk_devdesc *dev;
- struct disk_devdesc disk;
va_list ap;
- uint64_t size;
int rc;
va_start(ap, f);
dev = va_arg(ap, struct disk_devdesc *);
va_end(ap);
if (dev->dd.d_unit < 0 || dev->dd.d_unit >= nbdinfo)
return (EIO);
- BD(dev).bd_open++;
+
+ if ((BD(dev).bd_flags & BD_NO_MEDIA) == BD_NO_MEDIA) {
+ if (!bd_int13probe(&BD(dev)))
+ return (EIO);
+ if ((BD(dev).bd_flags & BD_NO_MEDIA) == BD_NO_MEDIA)
+ return (EIO);
+ }
if (BD(dev).bd_bcache == NULL)
BD(dev).bd_bcache = bcache_allocate();
- /*
- * Read disk size from partition.
- * This is needed to work around buggy BIOS systems returning
- * wrong (truncated) disk media size.
- * During bd_probe() we tested if the mulitplication of bd_sectors
- * would overflow so it should be safe to perform here.
- */
- disk.dd.d_dev = dev->dd.d_dev;
- disk.dd.d_unit = dev->dd.d_unit;
- disk.d_slice = -1;
- disk.d_partition = -1;
- disk.d_offset = 0;
+ if (BD(dev).bd_open == 0)
+ BD(dev).bd_sectors = bd_disk_get_sectors(dev);
+ BD(dev).bd_open++;
- if (disk_open(&disk, BD(dev).bd_sectors * BD(dev).bd_sectorsize,
- BD(dev).bd_sectorsize) == 0) {
-
- if (disk_ioctl(&disk, DIOCGMEDIASIZE, &size) == 0) {
- size /= BD(dev).bd_sectorsize;
- if (size > BD(dev).bd_sectors)
- BD(dev).bd_sectors = size;
- }
- disk_close(&disk);
- }
-
rc = disk_open(dev, BD(dev).bd_sectors * BD(dev).bd_sectorsize,
BD(dev).bd_sectorsize);
if (rc != 0) {
BD(dev).bd_open--;
if (BD(dev).bd_open == 0) {
bcache_free(BD(dev).bd_bcache);
BD(dev).bd_bcache = NULL;
}
}
return (rc);
}
static int
bd_close(struct open_file *f)
{
struct disk_devdesc *dev;
dev = (struct disk_devdesc *)f->f_devdata;
BD(dev).bd_open--;
if (BD(dev).bd_open == 0) {
bcache_free(BD(dev).bd_bcache);
BD(dev).bd_bcache = NULL;
}
return (disk_close(dev));
}
static int
bd_ioctl(struct open_file *f, u_long cmd, void *data)
{
struct disk_devdesc *dev;
int rc;
dev = (struct disk_devdesc *)f->f_devdata;
rc = disk_ioctl(dev, cmd, data);
if (rc != ENOTTY)
return (rc);
switch (cmd) {
case DIOCGSECTORSIZE:
*(uint32_t *)data = BD(dev).bd_sectorsize;
break;
case DIOCGMEDIASIZE:
*(uint64_t *)data = BD(dev).bd_sectors * BD(dev).bd_sectorsize;
break;
default:
return (ENOTTY);
}
return (0);
}
static int
bd_strategy(void *devdata, int rw, daddr_t dblk, size_t size,
char *buf, size_t *rsize)
{
struct bcache_devdata bcd;
struct disk_devdesc *dev;
dev = (struct disk_devdesc *)devdata;
bcd.dv_strategy = bd_realstrategy;
bcd.dv_devdata = devdata;
bcd.dv_cache = BD(dev).bd_bcache;
return (bcache_strategy(&bcd, rw, dblk + dev->d_offset, size,
buf, rsize));
}
static int
bd_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size,
char *buf, size_t *rsize)
{
struct disk_devdesc *dev = (struct disk_devdesc *)devdata;
- uint64_t disk_blocks;
- int blks, rc;
+ uint64_t disk_blocks, offset;
+ size_t blks, blkoff, bsize, rest;
+ caddr_t bbuf;
+ int rc;
- if (size % BD(dev).bd_sectorsize) {
- panic("bd_strategy: %d bytes I/O not multiple of block size",
- size);
+ if ((BD(dev).bd_flags & BD_NO_MEDIA) == BD_NO_MEDIA)
+ return (EIO);
+
+ /*
+ * First make sure the IO size is a multiple of 512 bytes. While we do
+ * process partial reads below, the strategy mechanism is built
+ * assuming IO is a multiple of 512B blocks. If the request is not
+ * a multiple of 512B blocks, it has to be some sort of bug.
+ */
+ if (size == 0 || (size % BIOSDISK_SECSIZE) != 0) {
+ printf("bd_strategy: %d bytes I/O not multiple of %d\n",
+ size, BIOSDISK_SECSIZE);
+ return (EIO);
}
DEBUG("open_disk %p", dev);
+ offset = dblk * BIOSDISK_SECSIZE;
+ dblk = offset / BD(dev).bd_sectorsize;
+ blkoff = offset % BD(dev).bd_sectorsize;
+
/*
* Check the value of the size argument. We do have quite small
* heap (64MB), but we do not know good upper limit, so we check against
* INT_MAX here. This will also protect us against possible overflows
* while translating block count to bytes.
*/
if (size > INT_MAX) {
- DEBUG("too large read: %zu bytes", size);
+ DEBUG("too large I/O: %zu bytes", size);
return (EIO);
}
blks = size / BD(dev).bd_sectorsize;
+ if (blks == 0 || (size % BD(dev).bd_sectorsize) != 0)
+ blks++;
+
if (dblk > dblk + blks)
return (EIO);
if (rsize)
*rsize = 0;
/*
* Get disk blocks, this value is either for whole disk or for
* partition.
*/
if (disk_ioctl(dev, DIOCGMEDIASIZE, &disk_blocks) == 0) {
- /* DIOCGMEDIASIZE returns bytes. */
+ /* DIOCGMEDIASIZE does return bytes. */
disk_blocks /= BD(dev).bd_sectorsize;
} else {
/* We should not get here. Just try to survive. */
disk_blocks = BD(dev).bd_sectors - dev->d_offset;
}
/* Validate source block address. */
if (dblk < dev->d_offset || dblk >= dev->d_offset + disk_blocks)
return (EIO);
/*
* Truncate if we are crossing disk or partition end.
*/
if (dblk + blks >= dev->d_offset + disk_blocks) {
blks = dev->d_offset + disk_blocks - dblk;
size = blks * BD(dev).bd_sectorsize;
- DEBUG("short read %d", blks);
+ DEBUG("short I/O %d", blks);
}
- switch (rw & F_MASK) {
- case F_READ:
- DEBUG("read %d from %lld to %p", blks, dblk, buf);
+ if (V86_IO_BUFFER_SIZE / BD(dev).bd_sectorsize == 0)
+ panic("BUG: Real mode buffer is too small\n");
- if (blks && (rc = bd_io(dev, dblk, blks, buf, BD_RD))) {
- /* Filter out floppy controller errors */
- if (BD(dev).bd_flags != BD_FLOPPY || rc != 0x20) {
- printf("read %d from %lld to %p, error: 0x%x\n",
- blks, dblk, buf, rc);
+ bbuf = PTOV(V86_IO_BUFFER);
+ rest = size;
+
+ while (blks > 0) {
+ int x = min(blks, V86_IO_BUFFER_SIZE / BD(dev).bd_sectorsize);
+
+ switch (rw & F_MASK) {
+ case F_READ:
+ DEBUG("read %d from %lld to %p", x, dblk, buf);
+ bsize = BD(dev).bd_sectorsize * x - blkoff;
+ if (rest < bsize)
+ bsize = rest;
+
+ if ((rc = bd_io(dev, dblk, x, bbuf, BD_RD)) != 0)
+ return (EIO);
+
+ bcopy(bbuf + blkoff, buf, bsize);
+ break;
+ case F_WRITE :
+ DEBUG("write %d from %lld to %p", x, dblk, buf);
+ if (blkoff != 0) {
+ /*
+ * We got offset to sector, read 1 sector to
+ * bbuf.
+ */
+ x = 1;
+ bsize = BD(dev).bd_sectorsize - blkoff;
+ bsize = min(bsize, rest);
+ rc = bd_io(dev, dblk, x, bbuf, BD_RD);
+ } else if (rest < BD(dev).bd_sectorsize) {
+ /*
+ * The remaining block is not full
+ * sector. Read 1 sector to bbuf.
+ */
+ x = 1;
+ bsize = rest;
+ rc = bd_io(dev, dblk, x, bbuf, BD_RD);
+ } else {
+ /* We can write full sector(s). */
+ bsize = BD(dev).bd_sectorsize * x;
}
- return (EIO);
- }
- break;
- case F_WRITE :
- DEBUG("write %d from %lld to %p", blks, dblk, buf);
+ /*
+ * Put your Data In, Put your Data out,
+ * Put your Data In, and shake it all about
+ */
+ bcopy(buf, bbuf + blkoff, bsize);
+ if ((rc = bd_io(dev, dblk, x, bbuf, BD_WR)) != 0)
+ return (EIO);
- if (blks && bd_io(dev, dblk, blks, buf, BD_WR)) {
- DEBUG("write error");
- return (EIO);
+ break;
+ default:
+ /* DO NOTHING */
+ return (EROFS);
}
- break;
- default:
- /* DO NOTHING */
- return (EROFS);
+
+ blkoff = 0;
+ buf += bsize;
+ rest -= bsize;
+ blks -= x;
+ dblk += x;
}
- if (rsize)
+ if (rsize != NULL)
*rsize = size;
return (0);
}
static int
bd_edd_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest,
int dowrite)
{
static struct edd_packet packet;
packet.len = sizeof(struct edd_packet);
packet.count = blks;
packet.off = VTOPOFF(dest);
packet.seg = VTOPSEG(dest);
packet.lba = dblk;
v86.ctl = V86_FLAGS;
v86.addr = 0x13;
/* Should we Write with verify ?? 0x4302 ? */
if (dowrite == BD_WR)
v86.eax = 0x4300;
else
v86.eax = 0x4200;
v86.edx = BD(dev).bd_unit;
v86.ds = VTOPSEG(&packet);
v86.esi = VTOPOFF(&packet);
v86int();
if (V86_CY(v86.efl))
return (v86.eax >> 8);
return (0);
}
static int
bd_chs_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest,
int dowrite)
{
uint32_t x, bpc, cyl, hd, sec;
bpc = BD(dev).bd_sec * BD(dev).bd_hds; /* blocks per cylinder */
x = dblk;
cyl = x / bpc; /* block # / blocks per cylinder */
x %= bpc; /* block offset into cylinder */
hd = x / BD(dev).bd_sec; /* offset / blocks per track */
sec = x % BD(dev).bd_sec; /* offset into track */
/* correct sector number for 1-based BIOS numbering */
sec++;
if (cyl > 1023) {
/* CHS doesn't support cylinders > 1023. */
return (1);
}
v86.ctl = V86_FLAGS;
v86.addr = 0x13;
if (dowrite == BD_WR)
v86.eax = 0x300 | blks;
else
v86.eax = 0x200 | blks;
v86.ecx = ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec;
v86.edx = (hd << 8) | BD(dev).bd_unit;
v86.es = VTOPSEG(dest);
v86.ebx = VTOPOFF(dest);
v86int();
if (V86_CY(v86.efl))
return (v86.eax >> 8);
return (0);
}
static void
bd_io_workaround(struct disk_devdesc *dev)
{
uint8_t buf[8 * 1024];
bd_edd_io(dev, 0xffffffff, 1, (caddr_t)buf, BD_RD);
}
-
static int
bd_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest,
int dowrite)
{
- u_int x, sec, result, resid, retry, maxfer;
- caddr_t p, xp, bbuf;
-
+ int result, retry;
+
/* Just in case some idiot actually tries to read/write -1 blocks... */
if (blks < 0)
return (-1);
- resid = blks;
- p = dest;
-
/*
* Workaround for a problem with some HP ProLiant BIOS failing to work
* out the boot disk after installation. hrs and kuriyama discovered
* this problem with an HP ProLiant DL320e Gen 8 with a 3TB HDD, and
* discovered that an int13h call seems to cause a buffer overrun in
* the bios. The problem is alleviated by doing an extra read before
* the buggy read. It is not immediately known whether other models
* are similarly affected.
+ * Loop retrying the operation a couple of times. The BIOS
+ * may also retry.
*/
if (dowrite == BD_RD && dblk >= 0x100000000)
bd_io_workaround(dev);
+ for (retry = 0; retry < 3; retry++) {
+ if (BD(dev).bd_flags & BD_MODEEDD)
+ result = bd_edd_io(dev, dblk, blks, dest, dowrite);
+ else
+ result = bd_chs_io(dev, dblk, blks, dest, dowrite);
- /* Decide whether we have to bounce */
- if (VTOP(dest) >> 20 != 0 || (BD(dev).bd_unit < 0x80 &&
- (VTOP(dest) >> 16) !=
- (VTOP(dest + blks * BD(dev).bd_sectorsize) >> 16))) {
+ if (result == 0) {
+ if (BD(dev).bd_flags & BD_NO_MEDIA)
+ BD(dev).bd_flags &= ~BD_NO_MEDIA;
+ break;
+ }
- /*
- * There is a 64k physical boundary somewhere in the
- * destination buffer, or the destination buffer is above
- * first 1MB of physical memory 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 = V86_IO_BUFFER_SIZE / BD(dev).bd_sectorsize;
- x = min(x, (unsigned)blks);
- bbuf = PTOV(V86_IO_BUFFER);
- maxfer = x; /* limit transfers to bounce region size */
- } else {
- bbuf = NULL;
- maxfer = 0;
- }
-
- while (resid > 0) {
- /*
- * Play it safe and don't cross track boundaries.
- * (XXX this is probably unnecessary)
- */
- sec = dblk % BD(dev).bd_sec; /* offset into track */
- x = min(BD(dev).bd_sec - sec, resid);
- if (maxfer > 0)
- x = min(x, maxfer); /* fit bounce buffer */
+ bd_reset_disk(BD(dev).bd_unit);
- /* where do we transfer to? */
- xp = bbuf == NULL ? p : bbuf;
-
/*
- * Put your Data In, Put your Data out,
- * Put your Data In, and shake it all about
+ * Error codes:
+ * 20h controller failure
+ * 31h no media in drive (IBM/MS INT 13 extensions)
+ * 80h no media in drive, VMWare (Fusion)
+ * There is no reason to repeat the IO with errors above.
*/
- if (dowrite == BD_WR && bbuf != NULL)
- bcopy(p, bbuf, x * BD(dev).bd_sectorsize);
-
- /*
- * 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 = BD(dev).bd_unit;
- v86int();
- }
-
- if (BD(dev).bd_flags & BD_MODEEDD1)
- result = bd_edd_io(dev, dblk, x, xp, dowrite);
- else
- result = bd_chs_io(dev, dblk, x, xp, dowrite);
- if (result == 0)
- break;
+ if (result == 0x20 || result == 0x31 || result == 0x80) {
+ BD(dev).bd_flags |= BD_NO_MEDIA;
+ break;
}
+ }
- if (dowrite == BD_WR)
- DEBUG("Write %d sector(s) from %p (0x%x) to %lld %s", x,
- p, VTOP(p), dblk, result ? "failed" : "ok");
- else
- DEBUG("Read %d sector(s) from %lld to %p (0x%x) %s", x,
- dblk, p, VTOP(p), result ? "failed" : "ok");
- if (result) {
- return (result);
+ if (result != 0 && (BD(dev).bd_flags & BD_NO_MEDIA) == 0) {
+ if (dowrite == BD_WR) {
+ printf("%s%d: Write %d sector(s) from %p (0x%x) "
+ "to %lld: 0x%x\n", dev->dd.d_dev->dv_name,
+ dev->dd.d_unit, blks, dest, VTOP(dest), dblk,
+ result);
+ } else {
+ printf("%s%d: Read %d sector(s) from %lld to %p "
+ "(0x%x): 0x%x\n", dev->dd.d_dev->dv_name,
+ dev->dd.d_unit, blks, dblk, dest, VTOP(dest),
+ result);
}
- if (dowrite == BD_RD && bbuf != NULL)
- bcopy(bbuf, p, x * BD(dev).bd_sectorsize);
- p += (x * BD(dev).bd_sectorsize);
- dblk += x;
- resid -= x;
}
- return (0);
+ return (result);
}
/*
* 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.
*/
uint32_t
bd_getbigeom(int bunit)
{
v86.ctl = V86_FLAGS;
v86.addr = 0x13;
v86.eax = 0x800;
v86.edx = 0x80 + bunit;
v86int();
if (V86_CY(v86.efl))
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 *d)
{
struct disk_devdesc *dev;
int biosdev;
int major;
int rootdev;
char *nip, *cp;
int i, unit;
dev = (struct disk_devdesc *)d;
biosdev = bd_unit2bios(dev->dd.d_unit);
DEBUG("unit %d BIOS device %d", dev->dd.d_unit, biosdev);
if (biosdev == -1) /* not a BIOS device */
return (-1);
if (disk_open(dev, BD(dev).bd_sectors * BD(dev).bd_sectorsize,
BD(dev).bd_sectorsize) != 0) /* oops, not a viable device */
return (-1);
else
disk_close(dev);
if (biosdev < 0x80) {
/* floppy (or emulated floppy) or ATAPI device */
if (bdinfo[dev->dd.d_unit].bd_type == DT_ATAPI) {
/* is an ATAPI disk */
major = WFDMAJOR;
} else {
/* is a floppy disk */
major = FDMAJOR;
}
} else {
/* assume an IDE disk */
major = WDMAJOR;
}
/* default root disk unit number */
unit = biosdev & 0x7f;
/* 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_slice + 1, unit, dev->d_partition);
DEBUG("dev is 0x%x\n", rootdev);
return (rootdev);
}
Index: stable/11/stand/i386/loader/main.c
===================================================================
--- stable/11/stand/i386/loader/main.c (revision 346474)
+++ stable/11/stand/i386/loader/main.c (revision 346475)
@@ -1,415 +1,417 @@
/*-
* 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 bootstrap main() and assorted miscellaneous
* commands.
*/
#include <stand.h>
#include <stddef.h>
#include <string.h>
#include <machine/bootinfo.h>
#include <machine/cpufunc.h>
#include <machine/psl.h>
#include <sys/disk.h>
#include <sys/reboot.h>
#include <common/drv.h>
#include "bootstrap.h"
#include "common/bootargs.h"
#include "libi386/libi386.h"
#include "libi386/smbios.h"
#include "btxv86.h"
#ifdef LOADER_ZFS_SUPPORT
#include "libzfs.h"
#endif
CTASSERT(sizeof(struct bootargs) == BOOTARGS_SIZE);
CTASSERT(offsetof(struct bootargs, bootinfo) == BA_BOOTINFO);
CTASSERT(offsetof(struct bootargs, bootflags) == BA_BOOTFLAGS);
CTASSERT(offsetof(struct bootinfo, bi_size) == BI_SIZE);
/* Arguments passed in from the boot1/boot2 loader */
static struct bootargs *kargs;
static uint32_t initial_howto;
static uint32_t initial_bootdev;
static struct bootinfo *initial_bootinfo;
struct arch_switch archsw; /* MI/MD interface boundary */
static void extract_currdev(void);
static int isa_inb(int port);
static void isa_outb(int port, int value);
void exit(int code);
#ifdef LOADER_GELI_SUPPORT
#include "geliboot.h"
struct geli_boot_args *gargs;
struct geli_boot_data *gbdata;
#endif
#ifdef LOADER_ZFS_SUPPORT
struct zfs_boot_args *zargs;
static void i386_zfs_probe(void);
#endif
/* XXX debugging */
extern char end[];
static void *heap_top;
static void *heap_bottom;
int
main(void)
{
int i;
/* Pick up arguments */
kargs = (void *)__args;
initial_howto = kargs->howto;
initial_bootdev = kargs->bootdev;
initial_bootinfo = kargs->bootinfo ? (struct bootinfo *)PTOV(kargs->bootinfo) : NULL;
/* Initialize the v86 register set to a known-good state. */
bzero(&v86, sizeof(v86));
v86.efl = PSL_RESERVED_DEFAULT | PSL_I;
/*
* Initialise the heap as early as possible. Once this is done, malloc() is usable.
*/
bios_getmem();
#if defined(LOADER_BZIP2_SUPPORT) || defined(LOADER_FIREWIRE_SUPPORT) || \
defined(LOADER_GPT_SUPPORT) || defined(LOADER_ZFS_SUPPORT)
if (high_heap_size > 0) {
heap_top = PTOV(high_heap_base + high_heap_size);
heap_bottom = PTOV(high_heap_base);
if (high_heap_base < memtop_copyin)
memtop_copyin = high_heap_base;
} else
#endif
{
heap_top = (void *)PTOV(bios_basemem);
heap_bottom = (void *)end;
}
setheap(heap_bottom, heap_top);
/*
* XXX Chicken-and-egg problem; we want to have console output early, but some
* console attributes may depend on reading from eg. the boot device, which we
* can't do yet.
*
* We can use printf() etc. once this is done.
* If the previous boot stage has requested a serial console, prefer that.
*/
bi_setboothowto(initial_howto);
if (initial_howto & RB_MULTIPLE) {
if (initial_howto & RB_SERIAL)
setenv("console", "comconsole vidconsole", 1);
else
setenv("console", "vidconsole comconsole", 1);
} else if (initial_howto & RB_SERIAL)
setenv("console", "comconsole", 1);
else if (initial_howto & RB_MUTE)
setenv("console", "nullconsole", 1);
cons_probe();
/*
* Initialise the block cache. Set the upper limit.
*/
bcache_init(32768, 512);
/*
* Special handling for PXE and CD booting.
*/
if (kargs->bootinfo == 0) {
/*
* We only want the PXE disk to try to init itself in the below
* walk through devsw if we actually booted off of PXE.
*/
if (kargs->bootflags & KARGS_FLAGS_PXE)
pxe_enable(kargs->pxeinfo ? PTOV(kargs->pxeinfo) : NULL);
else if (kargs->bootflags & KARGS_FLAGS_CD)
bc_add(initial_bootdev);
}
archsw.arch_autoload = i386_autoload;
archsw.arch_getdev = i386_getdev;
archsw.arch_copyin = i386_copyin;
archsw.arch_copyout = i386_copyout;
archsw.arch_readin = i386_readin;
archsw.arch_isainb = isa_inb;
archsw.arch_isaoutb = isa_outb;
#ifdef LOADER_ZFS_SUPPORT
archsw.arch_zfs_probe = i386_zfs_probe;
/*
* zfsboot and gptzfsboot have always passed KARGS_FLAGS_ZFS, so if that is
* set along with KARGS_FLAGS_EXTARG we know we can interpret the extarg
* data as a struct zfs_boot_args.
*/
#define KARGS_EXTARGS_ZFS (KARGS_FLAGS_EXTARG | KARGS_FLAGS_ZFS)
if ((kargs->bootflags & KARGS_EXTARGS_ZFS) == KARGS_EXTARGS_ZFS) {
zargs = (struct zfs_boot_args *)(kargs + 1);
}
#endif /* LOADER_ZFS_SUPPORT */
#ifdef LOADER_GELI_SUPPORT
/*
* If we decided earlier that we have zfs_boot_args extarg data, and it is
* big enough to contain the embedded geli data (the early zfs_boot_args
* structs weren't), then init the gbdata pointer accordingly. If there is
* extarg data which isn't zfs_boot_args data, determine whether it is
* geli_boot_args data. Recent versions of gptboot set KARGS_FLAGS_GELI to
* indicate that. Earlier versions didn't, but we presume that's what we
* have if the extarg size exactly matches the size of the geli_boot_args
* struct during that pre-flag era.
*/
#define LEGACY_GELI_ARGS_SIZE 260 /* This can never change */
#ifdef LOADER_ZFS_SUPPORT
if (zargs != NULL) {
if (zargs->size > offsetof(struct zfs_boot_args, gelidata)) {
gbdata = &zargs->gelidata;
}
} else
#endif /* LOADER_ZFS_SUPPORT */
if ((kargs->bootflags & KARGS_FLAGS_EXTARG) != 0) {
gargs = (struct geli_boot_args *)(kargs + 1);
if ((kargs->bootflags & KARGS_FLAGS_GELI) ||
gargs->size == LEGACY_GELI_ARGS_SIZE) {
gbdata = &gargs->gelidata;
}
}
if (gbdata != NULL)
import_geli_boot_data(gbdata);
#endif /* LOADER_GELI_SUPPORT */
/*
* March through the device switch probing for things.
*/
for (i = 0; devsw[i] != NULL; i++)
if (devsw[i]->dv_init != NULL)
(devsw[i]->dv_init)();
printf("BIOS %dkB/%dkB available memory\n", bios_basemem / 1024, bios_extmem / 1024);
if (initial_bootinfo != NULL) {
initial_bootinfo->bi_basemem = bios_basemem / 1024;
initial_bootinfo->bi_extmem = bios_extmem / 1024;
}
/* detect ACPI for future reference */
biosacpi_detect();
/* detect SMBIOS for future reference */
smbios_detect(NULL);
/* detect PCI BIOS for future reference */
biospci_detect();
printf("\n%s", bootprog_info);
extract_currdev(); /* set $currdev and $loaddev */
setenv("LINES", "24", 1); /* optional */
bios_getsmap();
interact();
/* if we ever get here, it is an error */
return (1);
}
/*
* Set the 'current device' by (if possible) recovering the boot device as
* supplied by the initial bootstrap.
*
* XXX should be extended for netbooting.
*/
static void
extract_currdev(void)
{
struct i386_devdesc new_currdev;
#ifdef LOADER_ZFS_SUPPORT
char buf[20];
#endif
int biosdev = -1;
/* Assume we are booting from a BIOS disk by default */
new_currdev.dd.d_dev = &biosdisk;
/* 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);
} else if ((kargs->bootflags & KARGS_FLAGS_PXE) != 0) {
/* we are booting from pxeldr */
new_currdev.dd.d_dev = &pxedisk;
new_currdev.dd.d_unit = 0;
} else {
/* we don't know what our boot device is */
new_currdev.d_kind.biosdisk.slice = -1;
new_currdev.d_kind.biosdisk.partition = 0;
biosdev = -1;
}
#ifdef LOADER_ZFS_SUPPORT
} else if ((kargs->bootflags & KARGS_FLAGS_ZFS) != 0) {
/* zargs was set in main() if we have new style extended argument */
if (zargs != NULL &&
zargs->size >= offsetof(struct zfs_boot_args, primary_pool)) {
/* sufficient data is provided */
new_currdev.d_kind.zfs.pool_guid = zargs->pool;
new_currdev.d_kind.zfs.root_guid = zargs->root;
if (zargs->size >= sizeof(*zargs) && zargs->primary_vdev != 0) {
sprintf(buf, "%llu", zargs->primary_pool);
setenv("vfs.zfs.boot.primary_pool", buf, 1);
sprintf(buf, "%llu", zargs->primary_vdev);
setenv("vfs.zfs.boot.primary_vdev", buf, 1);
}
} else {
/* old style zfsboot block */
new_currdev.d_kind.zfs.pool_guid = kargs->zfspool;
new_currdev.d_kind.zfs.root_guid = 0;
}
new_currdev.dd.d_dev = &zfs_dev;
#endif
} else if ((initial_bootdev & B_MAGICMASK) != B_DEVMAGIC) {
/* The passed-in boot device is bad */
new_currdev.d_kind.biosdisk.slice = -1;
new_currdev.d_kind.biosdisk.partition = 0;
biosdev = -1;
} else {
new_currdev.d_kind.biosdisk.slice = B_SLICE(initial_bootdev) - 1;
new_currdev.d_kind.biosdisk.partition = B_PARTITION(initial_bootdev);
biosdev = initial_bootinfo->bi_bios_dev;
/*
* If we are booted by an old bootstrap, we have to guess at the BIOS
* unit number. We will lose if there is more than one disk type
* and we are not booting from the lowest-numbered disk type
* (ie. SCSI when IDE also exists).
*/
if ((biosdev == 0) && (B_TYPE(initial_bootdev) != 2)) /* biosdev doesn't match major */
biosdev = 0x80 + B_UNIT(initial_bootdev); /* assume harddisk */
}
/*
* 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) &&
((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);
new_currdev.dd.d_unit = 0;
}
#ifdef LOADER_ZFS_SUPPORT
if (new_currdev.dd.d_dev->dv_type == DEVT_ZFS)
init_zfs_bootenv(zfs_fmtdev(&new_currdev));
#endif
env_setenv("currdev", EV_VOLATILE, i386_fmtdev(&new_currdev),
i386_setcurrdev, env_nounset);
env_setenv("loaddev", EV_VOLATILE, i386_fmtdev(&new_currdev), env_noset,
env_nounset);
}
COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot);
static int
command_reboot(int argc, char *argv[])
{
int i;
for (i = 0; devsw[i] != NULL; ++i)
if (devsw[i]->dv_cleanup != NULL)
(devsw[i]->dv_cleanup)();
printf("Rebooting...\n");
delay(1000000);
__exit(0);
}
/* provide this for panic, as it's not in the startup code */
void
exit(int code)
{
__exit(code);
}
COMMAND_SET(heap, "heap", "show heap usage", command_heap);
static int
command_heap(int argc, char *argv[])
{
mallocstats();
printf("heap base at %p, top at %p, upper limit at %p\n", heap_bottom,
sbrk(0), heap_top);
return(CMD_OK);
}
/* ISA bus access functions for PnP. */
static int
isa_inb(int port)
{
return (inb(port));
}
static void
isa_outb(int port, int value)
{
outb(port, value);
}
#ifdef LOADER_ZFS_SUPPORT
static void
i386_zfs_probe(void)
{
char devname[32];
int unit;
/*
* 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);
zfs_probe_dev(devname, NULL);
}
}
#endif
Index: stable/11
===================================================================
--- stable/11 (revision 346474)
+++ stable/11 (revision 346475)
Property changes on: stable/11
___________________________________________________________________
Modified: svn:mergeinfo
## -0,0 +0,1 ##
Merged /head:r339658,339959,340047,340049,340215

File Metadata

Mime Type
text/x-diff
Expires
Sun, Jul 5, 9:24 AM (1 d, 35 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
29011931
Default Alt Text
(85 KB)

Event Timeline