Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F147384634
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
85 KB
Referenced Files
None
Subscribers
None
View Options
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(¶ms, sizeof(params));
+ params.len = sizeof(params);
v86.ctl = V86_FLAGS;
v86.addr = 0x13;
v86.eax = 0x4800;
v86.edx = bd->bd_unit;
v86.ds = VTOPSEG(¶ms);
v86.esi = VTOPOFF(¶ms);
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
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Jul 5, 9:24 AM (21 h, 31 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
29011931
Default Alt Text
(85 KB)
Attached To
Mode
rS FreeBSD src repository - subversion
Attached
Detach File
Event Timeline
Log In to Comment