From 2c6f04dc0a1fc38959acb378f6d48203cd351f60 Mon Sep 17 00:00:00 2001 From: "Andrey V. Elsukov" Date: Sun, 5 Aug 2012 12:15:15 +0000 Subject: [PATCH] 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. --- sys/boot/common/Makefile.inc | 14 +- sys/boot/common/disk.c | 934 +++++---------------- sys/boot/common/disk.h | 18 +- sys/boot/userboot/test/test.c | 26 + sys/boot/userboot/userboot.h | 5 + sys/boot/userboot/userboot/Makefile | 1 - sys/boot/userboot/userboot/devicename.c | 80 +- sys/boot/userboot/userboot/userboot_disk.c | 83 +- 8 files changed, 343 insertions(+), 818 deletions(-) diff --git a/sys/boot/common/Makefile.inc b/sys/boot/common/Makefile.inc index ad9535cf136a..935519c9e702 100644 --- a/sys/boot/common/Makefile.inc +++ b/sys/boot/common/Makefile.inc @@ -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 diff --git a/sys/boot/common/disk.c b/sys/boot/common/disk.c index 81902c0f79aa..f039e1d7c259 100644 --- a/sys/boot/common/disk.c +++ b/sys/boot/common/disk.c @@ -1,5 +1,6 @@ /*- * Copyright (c) 1998 Michael Smith + * Copyright (c) 2012 Andrey V. Elsukov * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -27,26 +28,11 @@ #include __FBSDID("$FreeBSD$"); -/* - * MBR/GPT partitioned disk device handling. - * - * Ideas and algorithms from: - * - * - NetBSD libi386/biosdisk.c - * - FreeBSD biosboot/disk.c - * - */ - +#include #include - -#include -#include -#include - #include -#include - #include +#include #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); } diff --git a/sys/boot/common/disk.h b/sys/boot/common/disk.h index 0fc7e9189d54..dc2c3c1658e6 100644 --- a/sys/boot/common/disk.h +++ b/sys/boot/common/disk.h @@ -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); + diff --git a/sys/boot/userboot/test/test.c b/sys/boot/userboot/test/test.c index a752a8032ef8..8745d315cbf5 100644 --- a/sys/boot/userboot/test/test.c +++ b/sys/boot/userboot/test/test.c @@ -26,6 +26,8 @@ * $FreeBSD$ */ +#include +#include #include #include #include @@ -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, diff --git a/sys/boot/userboot/userboot.h b/sys/boot/userboot/userboot.h index 7d8263ee0104..9b0572e5ab00 100644 --- a/sys/boot/userboot/userboot.h +++ b/sys/boot/userboot/userboot.h @@ -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); }; diff --git a/sys/boot/userboot/userboot/Makefile b/sys/boot/userboot/userboot/Makefile index 28ddc129f99c..bc0a30982737 100644 --- a/sys/boot/userboot/userboot/Makefile +++ b/sys/boot/userboot/userboot/Makefile @@ -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 diff --git a/sys/boot/userboot/userboot/devicename.c b/sys/boot/userboot/userboot/devicename.c index 24c5179f8593..d54d0230d7b9 100644 --- a/sys/boot/userboot/userboot/devicename.c +++ b/sys/boot/userboot/userboot/devicename.c @@ -29,7 +29,6 @@ __FBSDID("$FreeBSD$"); #include #include -#include #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: diff --git a/sys/boot/userboot/userboot/userboot_disk.c b/sys/boot/userboot/userboot/userboot_disk.c index 074d3e1620be..84c4038ee750 100644 --- a/sys/boot/userboot/userboot/userboot_disk.c +++ b/sys/boot/userboot/userboot/userboot_disk.c @@ -31,23 +31,31 @@ __FBSDID("$FreeBSD$"); * Userboot disk image handling. */ +#include #include - #include -#include - #include #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, + §orsize) != 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)); +}