int mbrinit(cdev_t dev, struct disk_info *info, struct diskslices **sspp) { struct buf *bp; u_char *cp; int dospart; struct dos_partition *dp; struct dos_partition *dp0; struct dos_partition dpcopy[NDOSPART]; int error; int max_ncyls; int max_nsectors; int max_ntracks; u_int64_t mbr_offset; char partname[2]; u_long secpercyl; char *sname = "tempname"; struct diskslice *sp; struct diskslices *ssp; cdev_t wdev; mbr_offset = DOSBBSECTOR; reread_mbr: /* * Don't bother if the block size is weird or the * media size is 0 (probably means no media present). */ if (info->d_media_blksize & DEV_BMASK) return (EIO); if (info->d_media_size == 0) return (EIO); /* * Read master boot record. */ wdev = dev; bp = geteblk((int)info->d_media_blksize); bp->b_bio1.bio_offset = (off_t)mbr_offset * info->d_media_blksize; bp->b_bio1.bio_done = biodone_sync; bp->b_bio1.bio_flags |= BIO_SYNC; bp->b_bcount = info->d_media_blksize; bp->b_cmd = BUF_CMD_READ; bp->b_flags |= B_FAILONDIS; dev_dstrategy(wdev, &bp->b_bio1); if (biowait(&bp->b_bio1, "mbrrd") != 0) { if ((info->d_dsflags & DSO_MBRQUIET) == 0) { diskerr(&bp->b_bio1, wdev, "reading primary partition table: error", LOG_PRINTF, 0); kprintf("\n"); } error = EIO; goto done; } /* Weakly verify it. */ cp = bp->b_data; sname = dsname(dev, 0, 0, 0, NULL); if (cp[0x1FE] != 0x55 || cp[0x1FF] != 0xAA) { if (bootverbose) kprintf("%s: invalid primary partition table: no magic\n", sname); error = EINVAL; goto done; } /* Make a copy of the partition table to avoid alignment problems. */ memcpy(&dpcopy[0], cp + DOSPARTOFF, sizeof(dpcopy)); dp0 = &dpcopy[0]; /* * Check for "Ontrack Diskmanager" or GPT. If a GPT is found in * the first dos partition, ignore the rest of the MBR and go * to GPT processing. */ for (dospart = 0, dp = dp0; dospart < NDOSPART; dospart++, dp++) { if (dospart == 0 && (dp->dp_typ == DOSPTYP_PMBR || dp->dp_typ == DOSPTYP_GPT)) { if (bootverbose) kprintf( "%s: Found GPT in slice #%d\n", sname, dospart + 1); error = gptinit(dev, info, sspp); goto done; } if (dp->dp_typ == DOSPTYP_ONTRACK) { if (bootverbose) kprintf( "%s: Found \"Ontrack Disk Manager\" on this disk.\n", sname); bp->b_flags |= B_INVAL | B_AGE; brelse(bp); mbr_offset = 63; goto reread_mbr; } } if (bcmp(dp0, historical_bogus_partition_table, sizeof historical_bogus_partition_table) == 0 || bcmp(dp0, historical_bogus_partition_table_fixed, sizeof historical_bogus_partition_table_fixed) == 0) { #if 0 TRACE(("%s: invalid primary partition table: historical\n", sname)); #endif /* 0 */ if (bootverbose) kprintf( "%s: invalid primary partition table: Dangerously Dedicated (ignored)\n", sname); error = EINVAL; goto done; } /* Guess the geometry. */ /* * TODO: * Perhaps skip entries with 0 size. * Perhaps only look at entries of type DOSPTYP_386BSD. */ max_ncyls = 0; max_nsectors = 0; max_ntracks = 0; for (dospart = 0, dp = dp0; dospart < NDOSPART; dospart++, dp++) { int ncyls; int nsectors; int ntracks; ncyls = DPCYL(dp->dp_ecyl, dp->dp_esect) + 1; if (max_ncyls < ncyls) max_ncyls = ncyls; nsectors = DPSECT(dp->dp_esect); if (max_nsectors < nsectors) max_nsectors = nsectors; ntracks = dp->dp_ehd + 1; if (max_ntracks < ntracks) max_ntracks = ntracks; } /* * Check that we have guessed the geometry right by checking the * partition entries. */ /* * TODO: * As above. * Check for overlaps. * Check against d_secperunit if the latter is reliable. */ error = 0; for (dospart = 0, dp = dp0; dospart < NDOSPART; dospart++, dp++) { if (dp->dp_scyl == 0 && dp->dp_shd == 0 && dp->dp_ssect == 0 && dp->dp_start == 0 && dp->dp_size == 0) continue; //sname = dsname(dev, dkunit(dev), BASE_SLICE + dospart, // WHOLE_SLICE_PART, partname); /* * Temporarily ignore errors from this check. We could * simplify things by accepting the table eariler if we * always ignore errors here. Perhaps we should always * accept the table if the magic is right but not let * bad entries affect the geometry. */ check_part(sname, dp, mbr_offset, max_nsectors, max_ntracks, mbr_offset); } if (error != 0) goto done; /* * Accept the DOS partition table. * * Adjust the disk information structure with updated CHS * conversion parameters, but only use values extracted from * the primary partition table. * * NOTE! Regardless of our having to deal with this old cruft, * we do not screw around with the info->d_media* parameters. */ secpercyl = (u_long)max_nsectors * max_ntracks; if (secpercyl != 0 && mbr_offset == DOSBBSECTOR) { info->d_secpertrack = max_nsectors; info->d_nheads = max_ntracks; info->d_secpercyl = secpercyl; info->d_ncylinders = info->d_media_blocks / secpercyl; } /* * We are passed a pointer to a suitably initialized minimal * slices "struct" with no dangling pointers in it. Replace it * by a maximal one. This usually oversizes the "struct", but * enlarging it while searching for logical drives would be * inconvenient. */ kfree(*sspp, M_DEVBUF); ssp = dsmakeslicestruct(MAX_SLICES, info); *sspp = ssp; /* Initialize normal slices. */ sp = &ssp->dss_slices[BASE_SLICE]; for (dospart = 0, dp = dp0; dospart < NDOSPART; dospart++, dp++, sp++) { sname = dsname(dev, dkunit(dev), BASE_SLICE + dospart, WHOLE_SLICE_PART, partname); (void)mbr_setslice(sname, info, sp, dp, mbr_offset); } ssp->dss_nslices = BASE_SLICE + NDOSPART; /* Handle extended partitions. */ sp -= NDOSPART; for (dospart = 0; dospart < NDOSPART; dospart++, sp++) { if (sp->ds_type == DOSPTYP_EXTENDED || sp->ds_type == DOSPTYP_EXTENDEDX) { mbr_extended(wdev, info, ssp, sp->ds_offset, sp->ds_size, sp->ds_offset, max_nsectors, max_ntracks, mbr_offset, 1); } } /* * mbr_extended() abuses ssp->dss_nslices for the number of slices * that would be found if there were no limit on the number of slices * in *ssp. Cut it back now. */ if (ssp->dss_nslices > MAX_SLICES) ssp->dss_nslices = MAX_SLICES; done: bp->b_flags |= B_INVAL | B_AGE; brelse(bp); if (error == EINVAL) error = 0; return (error); }
/* * Handle GPT on raw disk. Note that GPTs are not recursive. The MBR is * ignored once a GPT has been detected. * * GPTs always start at block #1, regardless of how the MBR has been set up. * In fact, the MBR's starting block might be pointing to the boot partition * in the GPT rather then to the start of the GPT. * * This routine is called from mbrinit() when a GPT has been detected. */ int gptinit(cdev_t dev, struct disk_info *info, struct diskslices **sspp) { struct buf *bp1 = NULL; struct buf *bp2 = NULL; struct gpt_hdr *gpt; struct gpt_ent *ent; struct diskslice *sp; struct diskslices *ssp; cdev_t wdev; int error; uint32_t len; uint32_t entries; uint32_t entsz; uint32_t crc; uint32_t table_lba; uint32_t table_blocks; int i = 0, j; const char *dname; /* * The GPT starts in sector 1. */ wdev = dev; dname = dev_dname(wdev); bp1 = geteblk((int)info->d_media_blksize); bp1->b_bio1.bio_offset = info->d_media_blksize; bp1->b_bio1.bio_done = biodone_sync; bp1->b_bio1.bio_flags |= BIO_SYNC; bp1->b_bcount = info->d_media_blksize; bp1->b_cmd = BUF_CMD_READ; dev_dstrategy(wdev, &bp1->b_bio1); if (biowait(&bp1->b_bio1, "gptrd") != 0) { kprintf("%s: reading GPT @ block 1: error %d\n", dname, bp1->b_error); error = EIO; goto done; } /* * Header sanity check */ gpt = (void *)bp1->b_data; len = le32toh(gpt->hdr_size); if (len < GPT_MIN_HDR_SIZE || len > info->d_media_blksize) { kprintf("%s: Illegal GPT header size %d\n", dname, len); error = EINVAL; goto done; } crc = le32toh(gpt->hdr_crc_self); gpt->hdr_crc_self = 0; if (crc32(gpt, len) != crc) { kprintf("%s: GPT CRC32 did not match\n", dname); error = EINVAL; goto done; } /* * Validate the partition table and its location, then read it * into a buffer. */ entries = le32toh(gpt->hdr_entries); entsz = le32toh(gpt->hdr_entsz); table_lba = le32toh(gpt->hdr_lba_table); table_blocks = (entries * entsz + info->d_media_blksize - 1) / info->d_media_blksize; if (entries < 1 || entries > 128 || entsz < 128 || (entsz & 7) || entsz > MAXBSIZE / entries || table_lba < 2 || table_lba + table_blocks > info->d_media_blocks) { kprintf("%s: GPT partition table is out of bounds\n", dname); error = EINVAL; goto done; } /* * XXX subject to device dma size limitations */ bp2 = geteblk((int)(table_blocks * info->d_media_blksize)); bp2->b_bio1.bio_offset = (off_t)table_lba * info->d_media_blksize; bp2->b_bio1.bio_done = biodone_sync; bp2->b_bio1.bio_flags |= BIO_SYNC; bp2->b_bcount = table_blocks * info->d_media_blksize; bp2->b_cmd = BUF_CMD_READ; dev_dstrategy(wdev, &bp2->b_bio1); if (biowait(&bp2->b_bio1, "gptrd") != 0) { kprintf("%s: reading GPT partition table @ %lld: error %d\n", dname, (long long)bp2->b_bio1.bio_offset, bp2->b_error); error = EIO; goto done; } /* * We are passed a pointer to a minimal slices struct. Replace * it with a maximal one (128 slices + special slices). Well, * really there is only one special slice (the WHOLE_DISK_SLICE) * since we use the compatibility slice for s0, but don't quibble. * */ kfree(*sspp, M_DEVBUF); ssp = *sspp = dsmakeslicestruct(BASE_SLICE+128, info); /* * Create a slice for each partition. */ for (i = 0; i < (int)entries && i < 128; ++i) { struct gpt_ent sent; char partname[2]; char *sname; ent = (void *)((char *)bp2->b_data + i * entsz); le_uuid_dec(&ent->ent_type, &sent.ent_type); le_uuid_dec(&ent->ent_uuid, &sent.ent_uuid); sent.ent_lba_start = le64toh(ent->ent_lba_start); sent.ent_lba_end = le64toh(ent->ent_lba_end); sent.ent_attr = le64toh(ent->ent_attr); for (j = 0; j < NELEM(ent->ent_name); ++j) sent.ent_name[j] = le16toh(ent->ent_name[j]); /* * The COMPATIBILITY_SLICE is actually slice 0 (s0). This * is a bit weird becaue the whole-disk slice is #1, so * slice 1 (s1) starts at BASE_SLICE. */ if (i == 0) sp = &ssp->dss_slices[COMPATIBILITY_SLICE]; else sp = &ssp->dss_slices[BASE_SLICE+i-1]; sname = dsname(dev, dkunit(dev), WHOLE_DISK_SLICE, WHOLE_SLICE_PART, partname); if (kuuid_is_nil(&sent.ent_type)) continue; if (sent.ent_lba_start < table_lba + table_blocks || sent.ent_lba_end >= info->d_media_blocks || sent.ent_lba_start >= sent.ent_lba_end) { kprintf("%s part %d: unavailable, bad start or " "ending lba\n", sname, i); } else { gpt_setslice(sname, info, sp, &sent); } } ssp->dss_nslices = BASE_SLICE + i; error = 0; done: if (bp1) { bp1->b_flags |= B_INVAL | B_AGE; brelse(bp1); } if (bp2) { bp2->b_flags |= B_INVAL | B_AGE; brelse(bp2); } if (error == EINVAL) error = 0; return (error); }