Introduce new API to work with disks from the loader's drivers.

It uses new API from the part.c to work with partition tables.

Update userboot's disk driver to use new API. Note that struct
loader_callbacks_v1 has changed.
This commit is contained in:
Andrey V. Elsukov 2012-08-05 12:15:15 +00:00
parent 9cef000008
commit 2c6f04dc0a
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=239058
8 changed files with 343 additions and 818 deletions

View file

@ -1,6 +1,6 @@
# $FreeBSD$
SRCS+= boot.c commands.c console.c devopen.c disk.c interp.c
SRCS+= boot.c commands.c console.c devopen.c interp.c
SRCS+= interp_backslash.c interp_parse.c ls.c misc.c
SRCS+= module.c panic.c
@ -24,6 +24,18 @@ SRCS+= load_elf64.c reloc_elf64.c
SRCS+= dev_net.c
.endif
.if !defined(LOADER_NO_DISK_SUPPORT)
SRCS+= disk.c part.c
CFLAGS+= -DLOADER_DISK_SUPPORT
.if !defined(LOADER_NO_GPT_SUPPORT)
SRCS+= crc32.c
CFLAGS+= -DLOADER_GPT_SUPPORT
.endif
.if !defined(LOADER_NO_MBR_SUPPORT)
CFLAGS+= -DLOADER_MBR_SUPPORT
.endif
.endif
.if defined(HAVE_BCACHE)
SRCS+= bcache.c
.endif

View file

@ -1,5 +1,6 @@
/*-
* 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
@ -27,26 +28,11 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
/*
* MBR/GPT partitioned disk device handling.
*
* Ideas and algorithms from:
*
* - NetBSD libi386/biosdisk.c
* - FreeBSD biosboot/disk.c
*
*/
#include <sys/disk.h>
#include <stand.h>
#include <sys/diskmbr.h>
#include <sys/disklabel.h>
#include <sys/gpt.h>
#include <stdarg.h>
#include <uuid.h>
#include <bootstrap.h>
#include <part.h>
#include "disk.h"
@ -56,53 +42,26 @@ __FBSDID("$FreeBSD$");
# define DEBUG(fmt, args...)
#endif
/*
* 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
#ifdef LOADER_GPT_SUPPORT
struct gpt_part {
int gp_index;
uuid_t gp_type;
uint64_t gp_start;
uint64_t gp_end;
struct open_disk {
struct ptable *table;
off_t mediasize;
u_int sectorsize;
};
static uuid_t efi = GPT_ENT_TYPE_EFI;
static uuid_t freebsd_boot = GPT_ENT_TYPE_FREEBSD_BOOT;
static uuid_t freebsd_ufs = GPT_ENT_TYPE_FREEBSD_UFS;
static uuid_t freebsd_swap = GPT_ENT_TYPE_FREEBSD_SWAP;
static uuid_t freebsd_zfs = GPT_ENT_TYPE_FREEBSD_ZFS;
static uuid_t ms_basic_data = GPT_ENT_TYPE_MS_BASIC_DATA;
struct print_args {
struct disk_devdesc *dev;
const char *prefix;
int verbose;
};
#endif
#if defined(LOADER_GPT_SUPPORT) || defined(LOADER_MBR_SUPPORT)
/* Given a size in 512 byte sectors, convert it to a human-readable number. */
/* Convert size to a human-readable number. */
static char *
display_size(uint64_t size)
display_size(uint64_t size, u_int sectorsize)
{
static char buf[80];
char unit;
size /= 2;
size = size * sectorsize / 1024;
unit = 'K';
if (size >= 10485760000LL) {
size /= 1073741824;
@ -114,687 +73,230 @@ display_size(uint64_t size)
size /= 1024;
unit = 'M';
}
sprintf(buf, "%.6ld%cB", (long)size, unit);
sprintf(buf, "%ld%cB", (long)size, unit);
return (buf);
}
#endif
#ifdef LOADER_MBR_SUPPORT
static void
disk_checkextended(struct disk_devdesc *dev,
struct dos_partition *slicetab, int slicenum, int *nslicesp)
{
uint8_t buf[DISK_SECSIZE];
struct dos_partition *dp;
uint32_t base;
int rc, i, start, end;
dp = &slicetab[slicenum];
start = *nslicesp;
if (dp->dp_size == 0)
goto done;
if (dp->dp_typ != DOSPTYP_EXT)
goto done;
rc = dev->d_dev->dv_strategy(dev, F_READ, dp->dp_start, DISK_SECSIZE,
(char *) buf, NULL);
if (rc)
goto done;
if (buf[0x1fe] != 0x55 || buf[0x1ff] != 0xaa) {
DEBUG("no magic in extended table");
goto done;
}
base = dp->dp_start;
dp = (struct dos_partition *) &buf[DOSPARTOFF];
for (i = 0; i < NDOSPART; i++, dp++) {
if (dp->dp_size == 0)
continue;
if (*nslicesp == NEXTDOSPART)
goto done;
dp->dp_start += base;
bcopy(dp, &slicetab[*nslicesp], sizeof(*dp));
(*nslicesp)++;
}
end = *nslicesp;
/*
* now, recursively check the slices we just added
*/
for (i = start; i < end; i++)
disk_checkextended(dev, slicetab, i, nslicesp);
done:
return;
}
static int
disk_readslicetab(struct disk_devdesc *dev,
struct dos_partition **slicetabp, int *nslicesp)
ptblread(void *d, void *buf, size_t blocks, off_t offset)
{
struct dos_partition *slicetab = NULL;
int nslices, i;
int rc;
uint8_t buf[DISK_SECSIZE];
struct disk_devdesc *dev;
struct open_disk *od;
/*
* Find the slice in the DOS slice table.
*/
rc = dev->d_dev->dv_strategy(dev, F_READ, 0, DISK_SECSIZE,
(char *) buf, NULL);
if (rc) {
DEBUG("error reading MBR");
return (rc);
}
/*
* Check the slice table magic.
*/
if (buf[0x1fe] != 0x55 || buf[0x1ff] != 0xaa) {
DEBUG("no slice table/MBR (no magic)");
return (rc);
}
/*
* copy the partition table, then pick up any extended partitions.
*/
slicetab = malloc(NEXTDOSPART * sizeof(struct dos_partition));
bcopy(buf + DOSPARTOFF, slicetab,
sizeof(struct dos_partition) * NDOSPART);
nslices = NDOSPART; /* extended slices start here */
for (i = 0; i < NDOSPART; i++)
disk_checkextended(dev, slicetab, i, &nslices);
*slicetabp = slicetab;
*nslicesp = nslices;
return (0);
dev = (struct disk_devdesc *)d;
od = (struct open_disk *)dev->d_opendata;
return (dev->d_dev->dv_strategy(dev, F_READ, offset,
blocks * od->sectorsize, (char *)buf, NULL));
}
/*
* Search for the best MBR slice (typically the first FreeBSD slice).
*/
static int
disk_bestslice(struct dos_partition *slicetab, int nslices)
{
struct dos_partition *dp;
int pref, preflevel;
int i, prefslice;
prefslice = 0;
preflevel = PREF_NONE;
dp = &slicetab[0];
for (i = 0; i < nslices; i++, dp++) {
switch (dp->dp_typ) {
case DOSPTYP_386BSD: /* FreeBSD */
pref = dp->dp_flag & 0x80 ? PREF_FBSD_ACT : PREF_FBSD;
break;
case DOSPTYP_LINUX:
pref = dp->dp_flag & 0x80 ? PREF_LINUX_ACT : PREF_LINUX;
break;
case 0x01: /* DOS/Windows */
case 0x04:
case 0x06:
case 0x0b:
case 0x0c:
case 0x0e:
pref = dp->dp_flag & 0x80 ? PREF_DOS_ACT : PREF_DOS;
break;
default:
pref = PREF_NONE;
}
if (pref < preflevel) {
preflevel = pref;
prefslice = i + 1;
}
}
return (prefslice);
}
static int
disk_openmbr(struct disk_devdesc *dev)
{
struct dos_partition *slicetab = NULL, *dptr;
int nslices, sector, slice;
int rc;
uint8_t buf[DISK_SECSIZE];
struct disklabel *lp;
/*
* Following calculations attempt to determine the correct value
* for dev->d_offset by looking for the slice and partition specified,
* or searching for reasonable defaults.
*/
rc = disk_readslicetab(dev, &slicetab, &nslices);
if (rc)
return (rc);
/*
* if a slice number was supplied but not found, this is an error.
*/
if (dev->d_slice > 0) {
slice = dev->d_slice - 1;
if (slice >= nslices) {
DEBUG("slice %d not found", slice);
rc = EPART;
goto out;
}
}
/*
* Check for the historically bogus MBR found on true dedicated disks
*/
if (slicetab[3].dp_typ == DOSPTYP_386BSD &&
slicetab[3].dp_start == 0 && slicetab[3].dp_size == 50000) {
sector = 0;
goto unsliced;
}
/*
* Try to auto-detect the best slice; this should always give
* a slice number
*/
if (dev->d_slice == 0) {
slice = disk_bestslice(slicetab, nslices);
if (slice == -1) {
rc = ENOENT;
goto out;
}
dev->d_slice = slice;
}
/*
* Accept the supplied slice number unequivocally (we may be looking
* at a DOS partition).
* Note: we number 1-4, offsets are 0-3
*/
dptr = &slicetab[dev->d_slice - 1];
sector = dptr->dp_start;
DEBUG("slice entry %d at %d, %d sectors",
dev->d_slice - 1, sector, dptr->dp_size);
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_partition < 0) {
/* no partition, must be after the slice */
DEBUG("opening raw slice");
dev->d_offset = sector;
rc = 0;
goto out;
}
rc = dev->d_dev->dv_strategy(dev, F_READ, sector + LABELSECTOR,
DISK_SECSIZE, (char *) buf, NULL);
if (rc) {
DEBUG("error reading disklabel");
goto out;
}
lp = (struct disklabel *) buf;
if (lp->d_magic != DISKMAGIC) {
DEBUG("no disklabel");
rc = ENOENT;
goto out;
}
if (dev->d_partition >= lp->d_npartitions) {
DEBUG("partition '%c' exceeds partitions in table (a-'%c')",
'a' + dev->d_partition,
'a' + lp->d_npartitions);
rc = EPART;
goto out;
}
dev->d_offset =
lp->d_partitions[dev->d_partition].p_offset -
lp->d_partitions[RAW_PART].p_offset +
sector;
rc = 0;
out:
if (slicetab)
free(slicetab);
return (rc);
}
/*
* Print out each valid partition in the disklabel of a FreeBSD slice.
* For size calculations, we assume a 512 byte sector size.
*/
#define PWIDTH 35
static void
disk_printbsdslice(struct disk_devdesc *dev, daddr_t offset,
char *prefix, int verbose)
ptable_print(void *arg, const char *pname, const struct ptable_entry *part)
{
char line[80];
char buf[DISK_SECSIZE];
struct disklabel *lp;
int i, rc, fstype;
/* read disklabel */
rc = dev->d_dev->dv_strategy(dev, F_READ, offset + LABELSECTOR,
DISK_SECSIZE, (char *) buf, NULL);
if (rc)
return;
lp =(struct disklabel *)(&buf[0]);
if (lp->d_magic != DISKMAGIC) {
sprintf(line, "%s: FFS bad disklabel\n", prefix);
pager_output(line);
return;
}
/* Print partitions */
for (i = 0; i < lp->d_npartitions; i++) {
/*
* For each partition, make sure we know what type of fs it
* is. If not, then skip it.
*/
fstype = lp->d_partitions[i].p_fstype;
if (fstype != FS_BSDFFS &&
fstype != FS_SWAP &&
fstype != FS_VINUM)
continue;
/* Only print out statistics in verbose mode */
if (verbose)
sprintf(line, " %s%c: %s %s (%d - %d)\n",
prefix, 'a' + i,
(fstype == FS_SWAP) ? "swap " :
(fstype == FS_VINUM) ? "vinum" :
"FFS ",
display_size(lp->d_partitions[i].p_size),
lp->d_partitions[i].p_offset,
(lp->d_partitions[i].p_offset
+ lp->d_partitions[i].p_size));
else
sprintf(line, " %s%c: %s\n", prefix, 'a' + i,
(fstype == FS_SWAP) ? "swap" :
(fstype == FS_VINUM) ? "vinum" :
"FFS");
pager_output(line);
}
}
static void
disk_printslice(struct disk_devdesc *dev, int slice,
struct dos_partition *dp, char *prefix, int verbose)
{
char stats[80];
struct print_args *pa, bsd;
struct open_disk *od;
struct ptable *table;
char line[80];
if (verbose)
sprintf(stats, " %s (%d - %d)", display_size(dp->dp_size),
dp->dp_start, dp->dp_start + dp->dp_size);
else
stats[0] = '\0';
switch (dp->dp_typ) {
case DOSPTYP_386BSD:
disk_printbsdslice(dev, (daddr_t)dp->dp_start,
prefix, verbose);
return;
case DOSPTYP_LINSWP:
sprintf(line, "%s: Linux swap%s\n", prefix, stats);
break;
case DOSPTYP_LINUX:
/*
* XXX
* read the superblock to confirm this is an ext2fs partition?
*/
sprintf(line, "%s: ext2fs%s\n", prefix, stats);
break;
case 0x00: /* unused partition */
case DOSPTYP_EXT:
return;
case 0x01:
sprintf(line, "%s: FAT-12%s\n", prefix, stats);
break;
case 0x04:
case 0x06:
case 0x0e:
sprintf(line, "%s: FAT-16%s\n", prefix, stats);
break;
case 0x07:
sprintf(line, "%s: NTFS/HPFS%s\n", prefix, stats);
break;
case 0x0b:
case 0x0c:
sprintf(line, "%s: FAT-32%s\n", prefix, stats);
break;
default:
sprintf(line, "%s: Unknown fs: 0x%x %s\n", prefix, dp->dp_typ,
stats);
}
pa = (struct print_args *)arg;
od = (struct open_disk *)pa->dev->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");
pager_output(line);
if (part->type == PART_FREEBSD) {
/* Open slice with BSD label */
pa->dev->d_offset = part->start;
table = ptable_open(pa->dev, part->end - part->start + 1,
od->sectorsize, ptblread);
if (table == NULL)
return;
sprintf(line, " %s%s", pa->prefix, pname);
bsd.dev = pa->dev;
bsd.prefix = line;
bsd.verbose = pa->verbose;
ptable_iterate(table, &bsd, ptable_print);
ptable_close(table);
}
}
static int
disk_printmbr(struct disk_devdesc *dev, char *prefix, int verbose)
{
struct dos_partition *slicetab;
int nslices, i;
int rc;
char line[80];
rc = disk_readslicetab(dev, &slicetab, &nslices);
if (rc)
return (rc);
for (i = 0; i < nslices; i++) {
sprintf(line, "%ss%d", prefix, i + 1);
disk_printslice(dev, i, &slicetab[i], line, verbose);
}
free(slicetab);
return (0);
}
#endif
#ifdef LOADER_GPT_SUPPORT
static int
disk_readgpt(struct disk_devdesc *dev, struct gpt_part **gptp, int *ngptp)
{
struct dos_partition *dp;
struct gpt_hdr *hdr;
struct gpt_ent *ent;
struct gpt_part *gptab = NULL;
int entries_per_sec, rc, i, part;
daddr_t lba, elba;
uint8_t gpt[DISK_SECSIZE], tbl[DISK_SECSIZE];
/*
* Following calculations attempt to determine the correct value
* for dev->d_offset by looking for the slice and partition specified,
* or searching for reasonable defaults.
*/
rc = 0;
/* First, read the MBR and see if we have a PMBR. */
rc = dev->d_dev->dv_strategy(dev, F_READ, 0, DISK_SECSIZE,
(char *) tbl, NULL);
if (rc) {
DEBUG("error reading MBR");
return (EIO);
}
/* Check the slice table magic. */
if (tbl[0x1fe] != 0x55 || tbl[0x1ff] != 0xaa)
return (ENXIO);
/* Check for GPT slice. */
part = 0;
dp = (struct dos_partition *)(tbl + DOSPARTOFF);
for (i = 0; i < NDOSPART; i++) {
if (dp[i].dp_typ == 0xee)
part++;
else if ((part != 1) && (dp[i].dp_typ != 0x00))
return (EINVAL);
}
if (part != 1)
return (EINVAL);
/* Read primary GPT table header. */
rc = dev->d_dev->dv_strategy(dev, F_READ, 1, DISK_SECSIZE,
(char *) gpt, NULL);
if (rc) {
DEBUG("error reading GPT header");
return (EIO);
}
hdr = (struct gpt_hdr *)gpt;
if (bcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)) != 0 ||
hdr->hdr_lba_self != 1 || hdr->hdr_revision < 0x00010000 ||
hdr->hdr_entsz < sizeof(*ent) ||
DISK_SECSIZE % hdr->hdr_entsz != 0) {
DEBUG("Invalid GPT header\n");
return (EINVAL);
}
/* Walk the partition table to count valid partitions. */
part = 0;
entries_per_sec = DISK_SECSIZE / hdr->hdr_entsz;
elba = hdr->hdr_lba_table + hdr->hdr_entries / entries_per_sec;
for (lba = hdr->hdr_lba_table; lba < elba; lba++) {
rc = dev->d_dev->dv_strategy(dev, F_READ, lba, DISK_SECSIZE,
(char *) tbl, NULL);
if (rc) {
DEBUG("error reading GPT table");
return (EIO);
}
for (i = 0; i < entries_per_sec; i++) {
ent = (struct gpt_ent *)(tbl + i * hdr->hdr_entsz);
if (uuid_is_nil(&ent->ent_type, NULL) ||
ent->ent_lba_start == 0 ||
ent->ent_lba_end < ent->ent_lba_start)
continue;
part++;
}
}
/* Save the important information about all the valid partitions. */
if (part != 0) {
gptab = malloc(part * sizeof(struct gpt_part));
part = 0;
for (lba = hdr->hdr_lba_table; lba < elba; lba++) {
rc = dev->d_dev->dv_strategy(dev, F_READ, lba, DISK_SECSIZE,
(char *) tbl, NULL);
if (rc) {
DEBUG("error reading GPT table");
free(gptab);
return (EIO);
}
for (i = 0; i < entries_per_sec; i++) {
ent = (struct gpt_ent *)(tbl + i * hdr->hdr_entsz);
if (uuid_is_nil(&ent->ent_type, NULL) ||
ent->ent_lba_start == 0 ||
ent->ent_lba_end < ent->ent_lba_start)
continue;
gptab[part].gp_index = (lba - hdr->hdr_lba_table) *
entries_per_sec + i + 1;
gptab[part].gp_type = ent->ent_type;
gptab[part].gp_start = ent->ent_lba_start;
gptab[part].gp_end = ent->ent_lba_end;
part++;
}
}
}
*gptp = gptab;
*ngptp = part;
return (0);
}
static struct gpt_part *
disk_bestgpt(struct gpt_part *gpt, int ngpt)
{
struct gpt_part *gp, *prefpart;
int i, pref, preflevel;
prefpart = NULL;
preflevel = PREF_NONE;
gp = gpt;
for (i = 0; i < ngpt; i++, gp++) {
/* Windows. XXX: Also Linux. */
if (uuid_equal(&gp->gp_type, &ms_basic_data, NULL))
pref = PREF_DOS;
/* FreeBSD */
else if (uuid_equal(&gp->gp_type, &freebsd_ufs, NULL) ||
uuid_equal(&gp->gp_type, &freebsd_zfs, NULL))
pref = PREF_FBSD;
else
pref = PREF_NONE;
if (pref < preflevel) {
preflevel = pref;
prefpart = gp;
}
}
return (prefpart);
}
static int
disk_opengpt(struct disk_devdesc *dev)
{
struct gpt_part *gpt = NULL, *gp;
int rc, ngpt, i;
rc = disk_readgpt(dev, &gpt, &ngpt);
if (rc)
return (rc);
/* Is this a request for the whole disk? */
if (dev->d_slice < 0) {
dev->d_offset = 0;
rc = 0;
goto out;
}
/*
* If a partition number was supplied, then the user is trying to use
* an MBR address rather than a GPT address, so fail.
*/
if (dev->d_partition != 0xff) {
rc = ENOENT;
goto out;
}
/* If a slice number was supplied but not found, this is an error. */
gp = NULL;
if (dev->d_slice > 0) {
for (i = 0; i < ngpt; i++) {
if (gpt[i].gp_index == dev->d_slice) {
gp = &gpt[i];
break;
}
}
if (gp == NULL) {
DEBUG("partition %d not found", dev->d_slice);
rc = ENOENT;
goto out;
}
}
/* Try to auto-detect the best partition. */
if (dev->d_slice == 0) {
gp = disk_bestgpt(gpt, ngpt);
if (gp == NULL) {
rc = ENOENT;
goto out;
}
dev->d_slice = gp->gp_index;
}
dev->d_offset = gp->gp_start;
rc = 0;
out:
if (gpt)
free(gpt);
return (rc);
}
static void
disk_printgptpart(struct disk_devdesc *dev, struct gpt_part *gp,
char *prefix, int verbose)
{
char stats[80];
char line[96];
if (verbose)
sprintf(stats, " %s",
display_size(gp->gp_end + 1 - gp->gp_start));
else
stats[0] = '\0';
if (uuid_equal(&gp->gp_type, &efi, NULL))
sprintf(line, "%s: EFI %s\n", prefix, stats);
else if (uuid_equal(&gp->gp_type, &ms_basic_data, NULL))
sprintf(line, "%s: FAT/NTFS %s\n", prefix, stats);
else if (uuid_equal(&gp->gp_type, &freebsd_boot, NULL))
sprintf(line, "%s: FreeBSD boot%s\n", prefix, stats);
else if (uuid_equal(&gp->gp_type, &freebsd_ufs, NULL))
sprintf(line, "%s: FreeBSD UFS %s\n", prefix, stats);
else if (uuid_equal(&gp->gp_type, &freebsd_zfs, NULL))
sprintf(line, "%s: FreeBSD ZFS %s\n", prefix, stats);
else if (uuid_equal(&gp->gp_type, &freebsd_swap, NULL))
sprintf(line, "%s: FreeBSD swap%s\n", prefix, stats);
else
sprintf(line,
"%s: %08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x%s\n",
prefix,
gp->gp_type.time_low, gp->gp_type.time_mid,
gp->gp_type.time_hi_and_version,
gp->gp_type.clock_seq_hi_and_reserved,
gp->gp_type.clock_seq_low,
gp->gp_type.node[0],
gp->gp_type.node[1],
gp->gp_type.node[2],
gp->gp_type.node[3],
gp->gp_type.node[4],
gp->gp_type.node[5],
stats);
pager_output(line);
}
static int
disk_printgpt(struct disk_devdesc *dev, char *prefix, int verbose)
{
struct gpt_part *gpt = NULL;
int rc, ngpt, i;
char line[80];
rc = disk_readgpt(dev, &gpt, &ngpt);
if (rc)
return (rc);
for (i = 0; i < ngpt; i++) {
sprintf(line, "%sp%d", prefix, i + 1);
disk_printgptpart(dev, &gpt[i], line, verbose);
}
free(gpt);
return (0);
}
#endif
int
disk_open(struct disk_devdesc *dev)
{
int rc;
rc = 0;
/*
* While we are reading disk metadata, make sure we do it relative
* to the start of the disk
*/
dev->d_offset = 0;
#ifdef LOADER_GPT_SUPPORT
rc = disk_opengpt(dev);
if (rc == 0)
return (0);
#endif
#ifdef LOADER_MBR_SUPPORT
rc = disk_openmbr(dev);
#endif
return (rc);
}
#undef PWIDTH
void
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->d_opendata;
pa.dev = dev;
pa.prefix = prefix;
pa.verbose = verbose;
ptable_iterate(od->table, &pa, ptable_print);
}
int
disk_open(struct disk_devdesc *dev, off_t mediasize, u_int sectorsize)
{
struct open_disk *od;
struct ptable *table;
struct ptable_entry part;
int rc;
od = (struct open_disk *)malloc(sizeof(struct open_disk));
if (od == NULL) {
DEBUG("no memory");
return (ENOMEM);
}
/*
* While we are reading disk metadata, make sure we do it relative
* to the start of the disk
*/
rc = 0;
table = NULL;
dev->d_offset = 0;
dev->d_opendata = od;
od->mediasize = mediasize;
od->sectorsize = sectorsize;
DEBUG("open '%s', unit %d slice %d partition %d",
disk_fmtdev(dev), dev->d_unit, dev->d_slice, dev->d_partition);
/* Determine disk layout. */
od->table = ptable_open(dev, mediasize / sectorsize, sectorsize,
ptblread);
if (od->table == NULL) {
DEBUG("Can't read partition table");
rc = ENXIO;
goto out;
}
if (dev->d_slice > 0) {
/* Try to get information about partition */
rc = ptable_getpart(od->table, &part, dev->d_slice);
if (rc != 0) /* Partition doesn't exist */
goto out;
dev->d_offset = part.start;
if (dev->d_partition == -1 ||
dev->d_partition == 255)
goto out; /* Nothing more to do */
/* 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;
}
rc = ptable_getpart(table, &part, dev->d_partition);
if (rc != 0)
goto out;
dev->d_offset += part.start;
} else if (dev->d_slice == 0) {
rc = ptable_getbestpart(od->table, &part);
if (rc != 0)
goto out;
/* Save the slice number of best partition to dev */
dev->d_slice = part.index;
dev->d_offset = part.start;
}
out:
if (table != NULL)
ptable_close(table);
if (rc != 0) {
if (od->table != NULL)
ptable_close(od->table);
free(od);
}
return (rc);
}
int
disk_close(struct disk_devdesc *dev)
{
struct open_disk *od;
od = (struct open_disk *)dev->d_opendata;
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->d_dev->dv_name, dev->d_unit);
if (dev->d_slice > 0) {
#ifdef LOADER_GPT_SUPPORT
if (disk_printgpt(dev, prefix, verbose) == 0)
return;
if (dev->d_partition == 255) {
sprintf(cp, "p%d:", dev->d_slice);
return (buf);
} else
#endif
#ifdef LOADER_MBR_SUPPORT
disk_printmbr(dev, prefix, verbose);
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->d_unit = unit;
dev->d_slice = slice;
dev->d_partition = partition;
if (path != NULL)
*path = (*cp == '\0') ? cp: cp + 1;
return (0);
}

View file

@ -27,8 +27,7 @@
*/
/*
* Device descriptor for partitioned disks. We assume that all disk addresses
* are 512 byte block offsets from the start of the disk. To use, set the
* Device descriptor for partitioned disks. To use, set the
* d_slice and d_partition variables as follows:
*
* Whole disk access:
@ -74,8 +73,6 @@
* the device's strategy method.
*/
#define DISK_SECSIZE 512
struct disk_devdesc
{
struct devsw *d_dev;
@ -84,16 +81,21 @@ struct disk_devdesc
void *d_opendata;
int d_slice;
int d_partition;
int d_offset;
off_t d_offset;
};
/*
* Parse disk metadata and initialise dev->d_offset.
*/
extern int disk_open(struct disk_devdesc * dev);
extern int disk_open(struct disk_devdesc *dev, off_t mediasize,
u_int sectorsize);
extern int disk_close(struct disk_devdesc *dev);
/*
* Print information about slices on a disk. For the size calculations we
* assume a 512 byte sector.
* Print information about slices on a disk.
*/
extern void disk_print(struct disk_devdesc *dev, char *prefix, int verbose);
extern char* disk_fmtdev(struct disk_devdesc *dev);
extern int disk_parsedev(struct disk_devdesc *dev, const char *devspec,
const char **path);

View file

@ -26,6 +26,8 @@
* $FreeBSD$
*/
#include <sys/types.h>
#include <sys/disk.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <dirent.h>
@ -251,6 +253,29 @@ test_diskread(void *arg, int unit, uint64_t offset, void *dst, size_t size,
return (0);
}
int
test_diskioctl(void *arg, int unit, u_long cmd, void *data)
{
struct stat sb;
if (unit != 0 || disk_fd == -1)
return (EBADF);
switch (cmd) {
case DIOCGSECTORSIZE:
*(u_int *)data = 512;
break;
case DIOCGMEDIASIZE:
if (fstat(disk_fd, &sb) == 0)
*(off_t *)data = sb.st_size;
else
return (ENOTTY);
break;
default:
return (ENOTTY);
};
return (0);
}
/*
* Guest virtual machine i/o
*
@ -353,6 +378,7 @@ struct loader_callbacks_v1 cb = {
.stat = test_stat,
.diskread = test_diskread,
.diskioctl = test_diskioctl,
.copyin = test_copyin,
.copyout = test_copyout,

View file

@ -175,4 +175,9 @@ struct loader_callbacks_v1 {
*/
void (*getmem)(void *arg, uint64_t *lowmem,
uint64_t *highmem);
/*
* ioctl interface to the disk device
*/
int (*diskioctl)(void *arg, int unit, u_long cmd,
void *data);
};

View file

@ -32,7 +32,6 @@ CFLAGS+= -I${.CURDIR}/../../common
CFLAGS+= -I${.CURDIR}/../../..
CFLAGS+= -I${.CURDIR}/../../../../lib/libstand
CFLAGS+= -ffreestanding -I.
CFLAGS+= -DLOADER_GPT_SUPPORT -DLOADER_MBR_SUPPORT
LDFLAGS+= -nostdlib -Wl,-Bsymbolic

View file

@ -29,7 +29,6 @@ __FBSDID("$FreeBSD$");
#include <stand.h>
#include <string.h>
#include <sys/disklabel.h>
#include "bootstrap.h"
#include "disk.h"
@ -87,7 +86,7 @@ userboot_parsedev(struct disk_devdesc **dev, const char *devspec, const char **p
{
struct disk_devdesc *idev;
struct devsw *dv;
int i, unit, slice, partition, err;
int i, unit, err;
char *cp;
const char *np;
@ -113,62 +112,9 @@ userboot_parsedev(struct disk_devdesc **dev, const char *devspec, const char **p
break;
case DEVT_DISK:
unit = -1;
slice = -1;
partition = -1;
if (*np && (*np != ':')) {
unit = strtol(np, &cp, 10); /* next comes the unit number */
if (cp == np) {
err = EUNIT;
goto fail;
}
#ifdef LOADER_GPT_SUPPORT
if (*cp == 'p') { /* got a GPT partition */
np = cp + 1;
slice = strtol(np, &cp, 10);
if (cp == np) {
err = ESLICE;
goto fail;
}
if (*cp && (*cp != ':')) {
err = EINVAL;
goto fail;
}
partition = 0xff;
} else {
#endif
if (*cp == 's') { /* got a slice number */
np = cp + 1;
slice = strtol(np, &cp, 10);
if (cp == np) {
err = ESLICE;
goto fail;
}
}
if (*cp && (*cp != ':')) {
partition = *cp - 'a'; /* got a partition number */
if ((partition < 0) || (partition >= MAXPARTITIONS)) {
err = EPART;
goto fail;
}
cp++;
}
#ifdef LOADER_GPT_SUPPORT
}
#endif
} else {
cp = np;
}
if (*cp && (*cp != ':')) {
err = EINVAL;
err = disk_parsedev(idev, np, path);
if (err != 0)
goto fail;
}
idev->d_unit = unit;
idev->d_slice = slice;
idev->d_partition = partition;
if (path != NULL)
*path = (*cp == 0) ? cp : cp + 1;
break;
case DEVT_CD:
@ -219,8 +165,7 @@ userboot_fmtdev(void *vdev)
{
struct disk_devdesc *dev = (struct disk_devdesc *)vdev;
static char buf[128]; /* XXX device length constant? */
char *cp;
switch(dev->d_type) {
case DEVT_NONE:
strcpy(buf, "(no device)");
@ -231,22 +176,7 @@ userboot_fmtdev(void *vdev)
break;
case DEVT_DISK:
cp = buf;
cp += sprintf(cp, "%s%d", dev->d_dev->dv_name, dev->d_unit);
#ifdef LOADER_GPT_SUPPORT
if (dev->d_partition == 0xff) {
cp += sprintf(cp, "p%d", dev->d_slice);
} else {
#endif
if (dev->d_slice > 0)
cp += sprintf(cp, "s%d", dev->d_slice);
if (dev->d_partition >= 0)
cp += sprintf(cp, "%c", dev->d_partition + 'a');
#ifdef LOADER_GPT_SUPPORT
}
#endif
strcat(cp, ":");
break;
return (disk_fmtdev(vdev));
case DEVT_NET:
case DEVT_ZFS:

View file

@ -31,23 +31,31 @@ __FBSDID("$FreeBSD$");
* Userboot disk image handling.
*/
#include <sys/disk.h>
#include <stand.h>
#include <stdarg.h>
#include <uuid.h>
#include <bootstrap.h>
#include "disk.h"
#include "libuserboot.h"
struct userdisk_info {
uint64_t mediasize;
uint16_t sectorsize;
};
int userboot_disk_maxunit = 0;
static int userdisk_maxunit = 0;
static struct userdisk_info *ud_info;
static int userdisk_init(void);
static void userdisk_cleanup(void);
static int userdisk_strategy(void *devdata, int flag, daddr_t dblk,
size_t size, char *buf, size_t *rsize);
static int userdisk_open(struct open_file *f, ...);
static int userdisk_close(struct open_file *f);
static int userdisk_ioctl(struct open_file *f, u_long cmd, void *data);
static void userdisk_print(int verbose);
struct devsw userboot_disk = {
@ -57,41 +65,70 @@ struct devsw userboot_disk = {
userdisk_strategy,
userdisk_open,
userdisk_close,
noioctl,
userdisk_ioctl,
userdisk_print,
NULL
userdisk_cleanup
};
/*
* Nothing to do here.
* Initialize userdisk_info structure for each disk.
*/
static int
userdisk_init(void)
{
off_t mediasize;
u_int sectorsize;
int i;
userdisk_maxunit = userboot_disk_maxunit;
if (userdisk_maxunit > 0) {
ud_info = malloc(sizeof(*ud_info) * userdisk_maxunit);
if (ud_info == NULL)
return (ENOMEM);
for (i = 0; i < userdisk_maxunit; i++) {
if (CALLBACK(diskioctl, i, DIOCGSECTORSIZE,
&sectorsize) != 0 || CALLBACK(diskioctl, i,
DIOCGMEDIASIZE, &mediasize) != 0)
return (ENXIO);
ud_info[i].mediasize = mediasize;
ud_info[i].sectorsize = sectorsize;
}
}
return(0);
}
static void
userdisk_cleanup(void)
{
if (userdisk_maxunit > 0)
free(ud_info);
}
/*
* Print information about disks
*/
static void
userdisk_print(int verbose)
{
int i;
char line[80];
struct disk_devdesc dev;
struct disk_devdesc dev;
char line[80];
int i;
for (i = 0; i < userboot_disk_maxunit; i++) {
for (i = 0; i < userdisk_maxunit; i++) {
sprintf(line, " disk%d: Guest drive image\n", i);
pager_output(line);
dev.d_dev = &userboot_disk;
dev.d_unit = i;
dev.d_slice = -1;
dev.d_partition = -1;
dev.d_offset = 0;
sprintf(line, " disk%d", i);
disk_print(&dev, line, verbose);
if (disk_open(&dev, ud_info[i].mediasize,
ud_info[i].sectorsize) == 0) {
sprintf(line, " disk%d", i);
disk_print(&dev, line, verbose);
disk_close(&dev);
}
}
}
@ -108,17 +145,20 @@ userdisk_open(struct open_file *f, ...)
dev = va_arg(ap, struct disk_devdesc *);
va_end(ap);
if (dev->d_unit < 0 || dev->d_unit >= userboot_disk_maxunit)
if (dev->d_unit < 0 || dev->d_unit >= userdisk_maxunit)
return (EIO);
return (disk_open(dev));
return (disk_open(dev, ud_info[dev->d_unit].mediasize,
ud_info[dev->d_unit].sectorsize));
}
static int
userdisk_close(struct open_file *f)
{
struct disk_devdesc *dev;
return(0);
dev = (struct disk_devdesc *)f->f_devdata;
return (disk_close(dev));
}
static int
@ -136,7 +176,7 @@ userdisk_strategy(void *devdata, int rw, daddr_t dblk, size_t size,
return (EINVAL);
if (rsize)
*rsize = 0;
off = (dblk + dev->d_offset) * DISK_SECSIZE;
off = (dblk + dev->d_offset) * ud_info[dev->d_unit].sectorsize;
rc = CALLBACK(diskread, dev->d_unit, off, buf, size, &resid);
if (rc)
return (rc);
@ -144,3 +184,12 @@ userdisk_strategy(void *devdata, int rw, daddr_t dblk, size_t size,
*rsize = size - resid;
return (0);
}
static int
userdisk_ioctl(struct open_file *f, u_long cmd, void *data)
{
struct disk_devdesc *dev;
dev = (struct disk_devdesc *)f->f_devdata;
return (CALLBACK(diskioctl, dev->d_unit, cmd, data));
}