Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 31 Oct 2011 03:47:23 +0000 (UTC)
From:      Marcel Moolenaar <marcel@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-9@freebsd.org
Subject:   svn commit: r226956 - stable/9/sys/geom/part
Message-ID:  <201110310347.p9V3lNTZ097586@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: marcel
Date: Mon Oct 31 03:47:23 2011
New Revision: 226956
URL: http://svn.freebsd.org/changeset/base/226956

Log:
  MFC r226647:
  Add support for Boot Camp. The support is defined as follows:
  o   Detect when Boot Camp is enabled (i.e. the MBR mirrors the GPT).
  o   When Boot Camp is enabled, update the MBR whenever we write the GPT.
  o   Creation of a Boot Camp enabled GPT is not supported.
  o   Automatically disable Boot Camp when the GPT has been changed so that
      there's either no EFI partition or no HFS+ partition.
  o   The first 4 partitions (by index) get mirrored in the MBR.
  
  Requested by, discussed with and tested by: kris@pcbsd.org
  Approved by: re (bz@, kib@)

Modified:
  stable/9/sys/geom/part/g_part_gpt.c
Directory Properties:
  stable/9/sys/   (props changed)
  stable/9/sys/amd64/include/xen/   (props changed)
  stable/9/sys/boot/   (props changed)
  stable/9/sys/boot/i386/efi/   (props changed)
  stable/9/sys/boot/ia64/efi/   (props changed)
  stable/9/sys/boot/ia64/ski/   (props changed)
  stable/9/sys/boot/powerpc/boot1.chrp/   (props changed)
  stable/9/sys/boot/powerpc/ofw/   (props changed)
  stable/9/sys/cddl/contrib/opensolaris/   (props changed)
  stable/9/sys/conf/   (props changed)
  stable/9/sys/contrib/dev/acpica/   (props changed)
  stable/9/sys/contrib/octeon-sdk/   (props changed)
  stable/9/sys/contrib/pf/   (props changed)
  stable/9/sys/contrib/x86emu/   (props changed)

Modified: stable/9/sys/geom/part/g_part_gpt.c
==============================================================================
--- stable/9/sys/geom/part/g_part_gpt.c	Mon Oct 31 02:43:36 2011	(r226955)
+++ stable/9/sys/geom/part/g_part_gpt.c	Mon Oct 31 03:47:23 2011	(r226956)
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2002, 2005, 2006, 2007 Marcel Moolenaar
+ * Copyright (c) 2002, 2005-2007, 2011 Marcel Moolenaar
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -79,6 +79,7 @@ struct g_part_gpt_table {
 	struct gpt_hdr		*hdr;
 	quad_t			lba[GPT_ELT_COUNT];
 	enum gpt_state		state[GPT_ELT_COUNT];
+	int			bootcamp;
 };
 
 struct g_part_gpt_entry {
@@ -178,41 +179,165 @@ static struct uuid gpt_uuid_unused = GPT
 static struct g_part_uuid_alias {
 	struct uuid *uuid;
 	int alias;
+	int mbrtype;
 } gpt_uuid_alias_match[] = {
-	{ &gpt_uuid_apple_boot,		G_PART_ALIAS_APPLE_BOOT },
-	{ &gpt_uuid_apple_hfs,		G_PART_ALIAS_APPLE_HFS },
-	{ &gpt_uuid_apple_label,	G_PART_ALIAS_APPLE_LABEL },
-	{ &gpt_uuid_apple_raid,		G_PART_ALIAS_APPLE_RAID },
-	{ &gpt_uuid_apple_raid_offline,	G_PART_ALIAS_APPLE_RAID_OFFLINE },
-	{ &gpt_uuid_apple_tv_recovery,	G_PART_ALIAS_APPLE_TV_RECOVERY },
-	{ &gpt_uuid_apple_ufs,		G_PART_ALIAS_APPLE_UFS },
-	{ &gpt_uuid_bios_boot,		G_PART_ALIAS_BIOS_BOOT },
-	{ &gpt_uuid_efi, 		G_PART_ALIAS_EFI },
-	{ &gpt_uuid_freebsd,		G_PART_ALIAS_FREEBSD },
-	{ &gpt_uuid_freebsd_boot, 	G_PART_ALIAS_FREEBSD_BOOT },
-	{ &gpt_uuid_freebsd_swap,	G_PART_ALIAS_FREEBSD_SWAP },
-	{ &gpt_uuid_freebsd_ufs,	G_PART_ALIAS_FREEBSD_UFS },
-	{ &gpt_uuid_freebsd_vinum,	G_PART_ALIAS_FREEBSD_VINUM },
-	{ &gpt_uuid_freebsd_zfs,	G_PART_ALIAS_FREEBSD_ZFS },
-	{ &gpt_uuid_linux_data,		G_PART_ALIAS_LINUX_DATA },
-	{ &gpt_uuid_linux_lvm,		G_PART_ALIAS_LINUX_LVM },
-	{ &gpt_uuid_linux_raid,		G_PART_ALIAS_LINUX_RAID },
-	{ &gpt_uuid_linux_swap,		G_PART_ALIAS_LINUX_SWAP },
-	{ &gpt_uuid_mbr,		G_PART_ALIAS_MBR },
-	{ &gpt_uuid_ms_basic_data,	G_PART_ALIAS_MS_BASIC_DATA },
-	{ &gpt_uuid_ms_ldm_data,	G_PART_ALIAS_MS_LDM_DATA },
-	{ &gpt_uuid_ms_ldm_metadata,	G_PART_ALIAS_MS_LDM_METADATA },
-	{ &gpt_uuid_ms_reserved,	G_PART_ALIAS_MS_RESERVED },
-	{ &gpt_uuid_netbsd_ccd,		G_PART_ALIAS_NETBSD_CCD },
-	{ &gpt_uuid_netbsd_cgd,		G_PART_ALIAS_NETBSD_CGD },
-	{ &gpt_uuid_netbsd_ffs,		G_PART_ALIAS_NETBSD_FFS },
-	{ &gpt_uuid_netbsd_lfs,		G_PART_ALIAS_NETBSD_LFS },
-	{ &gpt_uuid_netbsd_raid,	G_PART_ALIAS_NETBSD_RAID },
-	{ &gpt_uuid_netbsd_swap,	G_PART_ALIAS_NETBSD_SWAP },
-
-	{ NULL, 0 }
+	{ &gpt_uuid_apple_boot,		G_PART_ALIAS_APPLE_BOOT,	 0xab },
+	{ &gpt_uuid_apple_hfs,		G_PART_ALIAS_APPLE_HFS,		 0xaf },
+	{ &gpt_uuid_apple_label,	G_PART_ALIAS_APPLE_LABEL,	 0 },
+	{ &gpt_uuid_apple_raid,		G_PART_ALIAS_APPLE_RAID,	 0 },
+	{ &gpt_uuid_apple_raid_offline,	G_PART_ALIAS_APPLE_RAID_OFFLINE, 0 },
+	{ &gpt_uuid_apple_tv_recovery,	G_PART_ALIAS_APPLE_TV_RECOVERY,	 0 },
+	{ &gpt_uuid_apple_ufs,		G_PART_ALIAS_APPLE_UFS,		 0 },
+	{ &gpt_uuid_bios_boot,		G_PART_ALIAS_BIOS_BOOT,		 0 },
+	{ &gpt_uuid_efi, 		G_PART_ALIAS_EFI,		 0xee },
+	{ &gpt_uuid_freebsd,		G_PART_ALIAS_FREEBSD,		 0xa5 },
+	{ &gpt_uuid_freebsd_boot, 	G_PART_ALIAS_FREEBSD_BOOT,	 0 },
+	{ &gpt_uuid_freebsd_swap,	G_PART_ALIAS_FREEBSD_SWAP,	 0 },
+	{ &gpt_uuid_freebsd_ufs,	G_PART_ALIAS_FREEBSD_UFS,	 0 },
+	{ &gpt_uuid_freebsd_vinum,	G_PART_ALIAS_FREEBSD_VINUM,	 0 },
+	{ &gpt_uuid_freebsd_zfs,	G_PART_ALIAS_FREEBSD_ZFS,	 0 },
+	{ &gpt_uuid_linux_data,		G_PART_ALIAS_LINUX_DATA,	 0x0b },
+	{ &gpt_uuid_linux_lvm,		G_PART_ALIAS_LINUX_LVM,		 0 },
+	{ &gpt_uuid_linux_raid,		G_PART_ALIAS_LINUX_RAID,	 0 },
+	{ &gpt_uuid_linux_swap,		G_PART_ALIAS_LINUX_SWAP,	 0 },
+	{ &gpt_uuid_mbr,		G_PART_ALIAS_MBR,		 0 },
+	{ &gpt_uuid_ms_basic_data,	G_PART_ALIAS_MS_BASIC_DATA,	 0x0b },
+	{ &gpt_uuid_ms_ldm_data,	G_PART_ALIAS_MS_LDM_DATA,	 0 },
+	{ &gpt_uuid_ms_ldm_metadata,	G_PART_ALIAS_MS_LDM_METADATA,	 0 },
+	{ &gpt_uuid_ms_reserved,	G_PART_ALIAS_MS_RESERVED,	 0 },
+	{ &gpt_uuid_netbsd_ccd,		G_PART_ALIAS_NETBSD_CCD,	 0 },
+	{ &gpt_uuid_netbsd_cgd,		G_PART_ALIAS_NETBSD_CGD,	 0 },
+	{ &gpt_uuid_netbsd_ffs,		G_PART_ALIAS_NETBSD_FFS,	 0 },
+	{ &gpt_uuid_netbsd_lfs,		G_PART_ALIAS_NETBSD_LFS,	 0 },
+	{ &gpt_uuid_netbsd_raid,	G_PART_ALIAS_NETBSD_RAID,	 0 },
+	{ &gpt_uuid_netbsd_swap,	G_PART_ALIAS_NETBSD_SWAP,	 0 },
+	{ NULL, 0, 0 }
 };
 
+static int
+gpt_write_mbr_entry(u_char *mbr, int idx, int typ, quad_t start,
+    quad_t end)
+{
+
+	if (typ == 0 || start > UINT32_MAX || end > UINT32_MAX)
+		return (EINVAL);
+
+	mbr += DOSPARTOFF + idx * DOSPARTSIZE;
+	mbr[0] = 0;
+	if (start == 1) {
+		/*
+		 * Treat the PMBR partition specially to maximize
+		 * interoperability with BIOSes.
+		 */
+		mbr[1] = mbr[3] = 0;
+		mbr[2] = 2;
+	} else
+		mbr[1] = mbr[2] = mbr[3] = 0xff;
+	mbr[4] = typ;
+	mbr[5] = mbr[6] = mbr[7] = 0xff;
+	le32enc(mbr + 8, (uint32_t)start);
+	le32enc(mbr + 12, (uint32_t)(end - start + 1));
+	return (0);
+}
+
+static int
+gpt_map_type(struct uuid *t)
+{
+	struct g_part_uuid_alias *uap;
+
+	for (uap = &gpt_uuid_alias_match[0]; uap->uuid; uap++) {
+		if (EQUUID(t, uap->uuid))
+			return (uap->mbrtype);
+	}
+	return (0);
+}
+
+/*
+ * Under Boot Camp the PMBR partition (type 0xEE) doesn't cover the
+ * whole disk anymore. Rather, it covers the GPT table and the EFI
+ * system partition only. This way the HFS+ partition and any FAT
+ * partitions can be added to the MBR without creating an overlap.
+ */
+static int
+gpt_is_bootcamp(struct g_part_gpt_table *table, const char *provname)
+{
+	uint8_t *p;
+
+	p = table->mbr + DOSPARTOFF;
+	if (p[4] != 0xee || le32dec(p + 8) != 1)
+		return (0);
+
+	p += DOSPARTSIZE;
+	if (p[4] != 0xaf)
+		return (0);
+
+	printf("GEOM: %s: enabling Boot Camp\n", provname);
+	return (1);
+}
+
+static void
+gpt_update_bootcamp(struct g_part_table *basetable)
+{
+	struct g_part_entry *baseentry;
+	struct g_part_gpt_entry *entry;
+	struct g_part_gpt_table *table;
+	int bootable, error, index, slices, typ;
+
+	table = (struct g_part_gpt_table *)basetable;
+
+	bootable = -1;
+	for (index = 0; index < NDOSPART; index++) {
+		if (table->mbr[DOSPARTOFF + DOSPARTSIZE * index])
+			bootable = index;
+	}
+
+	bzero(table->mbr + DOSPARTOFF, DOSPARTSIZE * NDOSPART);
+	slices = 0;
+	LIST_FOREACH(baseentry, &basetable->gpt_entry, gpe_entry) {
+		if (baseentry->gpe_deleted)
+			continue;
+		index = baseentry->gpe_index - 1;
+		if (index >= NDOSPART)
+			continue;
+
+		entry = (struct g_part_gpt_entry *)baseentry;
+
+		switch (index) {
+		case 0:	/* This must be the EFI system partition. */
+			if (!EQUUID(&entry->ent.ent_type, &gpt_uuid_efi))
+				goto disable;
+			error = gpt_write_mbr_entry(table->mbr, index, 0xee,
+			    1ull, entry->ent.ent_lba_end);
+			break;
+		case 1:	/* This must be the HFS+ partition. */
+			if (!EQUUID(&entry->ent.ent_type, &gpt_uuid_apple_hfs))
+				goto disable;
+			error = gpt_write_mbr_entry(table->mbr, index, 0xaf,
+			    entry->ent.ent_lba_start, entry->ent.ent_lba_end);
+			break;
+		default:
+			typ = gpt_map_type(&entry->ent.ent_type);
+			error = gpt_write_mbr_entry(table->mbr, index, typ,
+			    entry->ent.ent_lba_start, entry->ent.ent_lba_end);
+			break;
+		}
+		if (error)
+			continue;
+
+		if (index == bootable)
+			table->mbr[DOSPARTOFF + DOSPARTSIZE * index] = 0x80;
+		slices |= 1 << index;
+	}
+	if ((slices & 3) == 3)
+		return;
+
+ disable:
+	table->bootcamp = 0;
+	bzero(table->mbr + DOSPARTOFF, DOSPARTSIZE * NDOSPART);
+	gpt_write_mbr_entry(table->mbr, 0, 0xee, 1ull,
+	    MIN(table->lba[GPT_ELT_SECHDR], UINT32_MAX));
+}
+
 static struct gpt_hdr *
 gpt_read_hdr(struct g_part_gpt_table *table, struct g_consumer *cp,
     enum gpt_elt elt)
@@ -457,8 +582,9 @@ g_part_gpt_bootcode(struct g_part_table 
 	if (codesz > 0)
 		bcopy(gpp->gpp_codeptr, table->mbr, codesz);
 
-	/* Mark the PMBR active since some BIOS require it */
-	table->mbr[DOSPARTOFF] = 0x80;		/* status */
+	/* Mark the PMBR active since some BIOS require it. */
+	if (!table->bootcamp)
+		table->mbr[DOSPARTOFF] = 0x80;		/* status */
 	return (0);
 }
 
@@ -486,15 +612,7 @@ g_part_gpt_create(struct g_part_table *b
 	last = (pp->mediasize / pp->sectorsize) - 1;
 
 	le16enc(table->mbr + DOSMAGICOFFSET, DOSMAGIC);
-	table->mbr[DOSPARTOFF + 1] = 0x01;		/* shd */
-	table->mbr[DOSPARTOFF + 2] = 0x01;		/* ssect */
-	table->mbr[DOSPARTOFF + 3] = 0x00;		/* scyl */
-	table->mbr[DOSPARTOFF + 4] = 0xee;		/* typ */
-	table->mbr[DOSPARTOFF + 5] = 0xff;		/* ehd */
-	table->mbr[DOSPARTOFF + 6] = 0xff;		/* esect */
-	table->mbr[DOSPARTOFF + 7] = 0xff;		/* ecyl */
-	le32enc(table->mbr + DOSPARTOFF + 8, 1);	/* start */
-	le32enc(table->mbr + DOSPARTOFF + 12, MIN(last, UINT32_MAX));
+	gpt_write_mbr_entry(table->mbr, 0, 0xee, 1, MIN(last, UINT32_MAX));
 
 	/* Allocate space for the header */
 	table->hdr = g_malloc(sizeof(struct gpt_hdr), M_WAITOK | M_ZERO);
@@ -802,6 +920,21 @@ g_part_gpt_read(struct g_part_table *bas
 	}
 
 	g_free(tbl);
+
+	/*
+	 * Under Mac OS X, the MBR mirrors the first 4 GPT partitions
+	 * if (and only if) any FAT32 or FAT16 partitions have been
+	 * created. This happens irrespective of whether Boot Camp is
+	 * used/enabled, though it's generally understood to be done
+	 * to support legacy Windows under Boot Camp. We refer to this
+	 * mirroring simply as Boot Camp. We try to detect Boot Camp
+	 * so that we can update the MBR if and when GPT changes have
+	 * been made. Note that we do not enable Boot Camp if not
+	 * previously enabled because we can't assume that we're on a
+	 * Mac alongside Mac OS X.
+	 */
+	table->bootcamp = gpt_is_bootcamp(table, pp->name);
+
 	return (0);
 }
 
@@ -816,73 +949,52 @@ g_part_gpt_recover(struct g_part_table *
 }
 
 static int
-g_part_gpt_setunset(struct g_part_table *table, struct g_part_entry *baseentry,
-    const char *attrib, unsigned int set)
+g_part_gpt_setunset(struct g_part_table *basetable,
+    struct g_part_entry *baseentry, const char *attrib, unsigned int set)
 {
-	struct g_part_entry *iter;
 	struct g_part_gpt_entry *entry;
-	int changed, bootme, bootonce, bootfailed;
+	struct g_part_gpt_table *table;
+	uint64_t attr;
+	int i;
 
-	bootme = bootonce = bootfailed = 0;
+	table = (struct g_part_gpt_table *)basetable;
+	entry = (struct g_part_gpt_entry *)baseentry;
+
+	if (strcasecmp(attrib, "active") == 0) {
+		if (!table->bootcamp || baseentry->gpe_index > NDOSPART)
+			return (EINVAL);
+		for (i = 0; i < NDOSPART; i++) {
+			table->mbr[DOSPARTOFF + i * DOSPARTSIZE] =
+			    (i == baseentry->gpe_index - 1) ? 0x80 : 0;
+		}
+		return (0);
+	}
+
+	attr = 0;
 	if (strcasecmp(attrib, "bootme") == 0) {
-		bootme = 1;
+		attr |= GPT_ENT_ATTR_BOOTME;
 	} else if (strcasecmp(attrib, "bootonce") == 0) {
-		/* BOOTME is set automatically with BOOTONCE, but not unset. */
-		bootonce = 1;
+		attr |= GPT_ENT_ATTR_BOOTONCE;
 		if (set)
-			bootme = 1;
+			attr |= GPT_ENT_ATTR_BOOTME;
 	} else if (strcasecmp(attrib, "bootfailed") == 0) {
 		/*
 		 * It should only be possible to unset BOOTFAILED, but it might
 		 * be useful for test purposes to also be able to set it.
 		 */
-		bootfailed = 1;
+		attr |= GPT_ENT_ATTR_BOOTFAILED;
 	}
-	if (!bootme && !bootonce && !bootfailed)
+	if (attr == 0)
 		return (EINVAL);
 
-	LIST_FOREACH(iter, &table->gpt_entry, gpe_entry) {
-		if (iter->gpe_deleted)
-			continue;
-		if (iter != baseentry)
-			continue;
-		changed = 0;
-		entry = (struct g_part_gpt_entry *)iter;
-		if (set) {
-			if (bootme &&
-			    !(entry->ent.ent_attr & GPT_ENT_ATTR_BOOTME)) {
-				entry->ent.ent_attr |= GPT_ENT_ATTR_BOOTME;
-				changed = 1;
-			}
-			if (bootonce &&
-			    !(entry->ent.ent_attr & GPT_ENT_ATTR_BOOTONCE)) {
-				entry->ent.ent_attr |= GPT_ENT_ATTR_BOOTONCE;
-				changed = 1;
-			}
-			if (bootfailed &&
-			    !(entry->ent.ent_attr & GPT_ENT_ATTR_BOOTFAILED)) {
-				entry->ent.ent_attr |= GPT_ENT_ATTR_BOOTFAILED;
-				changed = 1;
-			}
-		} else {
-			if (bootme &&
-			    (entry->ent.ent_attr & GPT_ENT_ATTR_BOOTME)) {
-				entry->ent.ent_attr &= ~GPT_ENT_ATTR_BOOTME;
-				changed = 1;
-			}
-			if (bootonce &&
-			    (entry->ent.ent_attr & GPT_ENT_ATTR_BOOTONCE)) {
-				entry->ent.ent_attr &= ~GPT_ENT_ATTR_BOOTONCE;
-				changed = 1;
-			}
-			if (bootfailed &&
-			    (entry->ent.ent_attr & GPT_ENT_ATTR_BOOTFAILED)) {
-				entry->ent.ent_attr &= ~GPT_ENT_ATTR_BOOTFAILED;
-				changed = 1;
-			}
-		}
-		if (changed && !iter->gpe_created)
-			iter->gpe_modified = 1;
+	if (set)
+		attr = entry->ent.ent_attr | attr;
+	else
+		attr = entry->ent.ent_attr & ~attr;
+	if (attr != entry->ent.ent_attr) {
+		entry->ent.ent_attr = attr;
+		if (!baseentry->gpe_created)
+			baseentry->gpe_modified = 1;
 	}
 	return (0);
 }
@@ -923,6 +1035,10 @@ g_part_gpt_write(struct g_part_table *ba
 	tblsz = (table->hdr->hdr_entries * table->hdr->hdr_entsz +
 	    pp->sectorsize - 1) / pp->sectorsize;
 
+	/* Reconstruct the MBR from the GPT if under Boot Camp. */
+	if (table->bootcamp)
+		gpt_update_bootcamp(basetable);
+
 	/* Write the PMBR */
 	buf = g_malloc(pp->sectorsize, M_WAITOK | M_ZERO);
 	bcopy(table->mbr, buf, MBRSIZE);



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201110310347.p9V3lNTZ097586>