diff options
-rw-r--r-- | sys/boot/common/Makefile.inc | 14 | ||||
-rw-r--r-- | sys/boot/common/disk.c | 874 | ||||
-rw-r--r-- | sys/boot/common/disk.h | 18 | ||||
-rw-r--r-- | sys/boot/userboot/test/test.c | 26 | ||||
-rw-r--r-- | sys/boot/userboot/userboot.h | 5 | ||||
-rw-r--r-- | sys/boot/userboot/userboot/Makefile | 1 | ||||
-rw-r--r-- | sys/boot/userboot/userboot/devicename.c | 80 | ||||
-rw-r--r-- | sys/boot/userboot/userboot/userboot_disk.c | 83 |
8 files changed, 313 insertions, 788 deletions
diff --git a/sys/boot/common/Makefile.inc b/sys/boot/common/Makefile.inc index ad9535c..935519c 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 81902c0..f039e1d 100644 --- a/sys/boot/common/disk.c +++ b/sys/boot/common/disk.c @@ -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; - -#endif - -#if defined(LOADER_GPT_SUPPORT) || defined(LOADER_MBR_SUPPORT) +struct print_args { + struct disk_devdesc *dev; + const char *prefix; + int verbose; +}; -/* 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) -{ - struct dos_partition *slicetab = NULL; - int nslices, i; - int rc; - uint8_t buf[DISK_SECSIZE]; - - /* - * 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); -} - -/* - * 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) +ptblread(void *d, void *buf, size_t blocks, off_t offset) { - 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; - } + struct disk_devdesc *dev; + struct open_disk *od; - 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); + 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)); } -/* - * 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); -} - -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); + 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); } - free(slicetab); - return (0); } +#undef PWIDTH -#endif - -#ifdef LOADER_GPT_SUPPORT - -static int -disk_readgpt(struct disk_devdesc *dev, struct gpt_part **gptp, int *ngptp) +void +disk_print(struct disk_devdesc *dev, char *prefix, int verbose) { - 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); + 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); } -static struct gpt_part * -disk_bestgpt(struct gpt_part *gpt, int ngpt) +int +disk_open(struct disk_devdesc *dev, off_t mediasize, u_int sectorsize) { - struct gpt_part *gp, *prefpart; - int i, pref, preflevel; - - prefpart = NULL; - preflevel = PREF_NONE; + struct open_disk *od; + struct ptable *table; + struct ptable_entry part; + int rc; - 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; - } + od = (struct open_disk *)malloc(sizeof(struct open_disk)); + if (od == NULL) { + DEBUG("no memory"); + return (ENOMEM); } - 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. + * While we are reading disk metadata, make sure we do it relative + * to the start of the disk */ - if (dev->d_partition != 0xff) { - rc = ENOENT; + 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 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; + /* Try to get information about partition */ + rc = ptable_getpart(od->table, &part, dev->d_slice); + if (rc != 0) /* Partition doesn't exist */ goto out; - } - } - - /* Try to auto-detect the best partition. */ - if (dev->d_slice == 0) { - gp = disk_bestgpt(gpt, ngpt); - if (gp == NULL) { - rc = ENOENT; + 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; } - dev->d_slice = gp->gp_index; + 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; } - - dev->d_offset = gp->gp_start; - rc = 0; - out: - if (gpt) - free(gpt); + if (table != NULL) + ptable_close(table); + if (rc != 0) { + if (od->table != NULL) + ptable_close(od->table); + free(od); + } 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) +int +disk_close(struct disk_devdesc *dev) { - struct gpt_part *gpt = NULL; - int rc, ngpt, i; - char line[80]; + struct open_disk *od; - 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); + od = (struct open_disk *)dev->d_opendata; + ptable_close(od->table); + free(od); return (0); } -#endif - -int -disk_open(struct disk_devdesc *dev) +char* +disk_fmtdev(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; + 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 - rc = disk_opengpt(dev); - if (rc == 0) - return (0); + if (dev->d_partition == 255) { + sprintf(cp, "p%d:", dev->d_slice); + return (buf); + } else #endif #ifdef LOADER_MBR_SUPPORT - rc = disk_openmbr(dev); + cp += sprintf(cp, "s%d", dev->d_slice); #endif - - return (rc); + if (dev->d_partition >= 0) + cp += sprintf(cp, "%c", dev->d_partition + 'a'); + } + strcat(cp, ":"); + return (buf); } -void -disk_print(struct disk_devdesc *dev, char *prefix, int verbose) +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 (disk_printgpt(dev, prefix, verbose) == 0) - return; + 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 - disk_printmbr(dev, prefix, verbose); + 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 0fc7e91..dc2c3c1 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 a752a80..8745d31 100644 --- a/sys/boot/userboot/test/test.c +++ b/sys/boot/userboot/test/test.c @@ -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, diff --git a/sys/boot/userboot/userboot.h b/sys/boot/userboot/userboot.h index 7d8263e..9b0572e 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 28ddc12..bc0a309 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 24c5179..d54d023 100644 --- a/sys/boot/userboot/userboot/devicename.c +++ b/sys/boot/userboot/userboot/devicename.c @@ -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: diff --git a/sys/boot/userboot/userboot/userboot_disk.c b/sys/boot/userboot/userboot/userboot_disk.c index 074d3e1..84c4038 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 <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, + §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)); +} |