[parted-devel] [PATCH] Write synced MBR rather than protective MBR on Intel Mac systems

Colin Watson cjwatson at ubuntu.com
Mon Mar 30 21:26:46 UTC 2009


The attached patch is from Ubuntu's parted package, originally written
by Matthew Garrett and extended by me. It copes with the fact that
Intel-based Macs require a synced MBR, not a protective MBR (Apple broke
the spec). Since this is incompatible, IMO the best option is to do DMI
detection of the local system and act accordingly; yes, this does mean
that you can't partition a disk for an Intel Mac on some other system or
vice versa, but I don't think this is too bad a restriction.

Sorry, I have no idea of how to test this reasonably.

Thanks,

-- 
Colin Watson                                       [cjwatson at ubuntu.com]
-------------- next part --------------
#! /bin/sh /usr/share/dpatch/dpatch-run
## gptsync.dpatch by Matthew Garrett <mjg59 at srcf.ucam.org> and
## Colin Watson <cjwatson at ubuntu.com>
##
## All lines beginning with `## DP:' are a description of the patch.
## DP: On Intel Mac systems, write a synced MBR rather than a protective
## DP: MBR.

@DPATCH@
diff -urNad parted-1.8.8.git.2008.03.24~/libparted/labels/gpt.c parted-1.8.8.git.2008.03.24/libparted/labels/gpt.c
--- parted-1.8.8.git.2008.03.24~/libparted/labels/gpt.c	2009-03-27 17:11:11.000000000 +0000
+++ parted-1.8.8.git.2008.03.24/libparted/labels/gpt.c	2009-03-27 17:12:32.000000000 +0000
@@ -11,6 +11,11 @@
     Per Intel EFI Specification v1.02
     http://developer.intel.com/technology/efi/efi.htm
 
+    DMI handling from dmidecode:
+      (C) 2000-2002 Alan Cox <alan at redhat.com>
+      (C) 2002-2005 Jean Delvare <khali at linux-fr.org>
+      Reduced for Intel Mac detection by Colin Watson <cjwatson at ubuntu.com>
+
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
     the Free Software Foundation; either version 3 of the License, or
@@ -35,6 +40,8 @@
 #include <stdio.h>
 #include <sys/types.h>
 #include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
 #include <fcntl.h>
 #include <unistd.h>
 #include <uuid/uuid.h>
@@ -258,6 +265,202 @@
 static PedDiskType gpt_disk_type;
 
 
+#define WORD(x) (*(const uint16_t *)(x))
+#define DWORD(x) (*(const uint32_t *)(x))
+
+struct dmi_header
+{
+	uint8_t type;
+	uint8_t length;
+	uint16_t handle;
+};
+
+#define APPLE_DMI "Apple Computer, Inc."
+#define APPLE_DMI_2 "Apple Inc."
+static int is_apple = 0;
+
+
+static int
+checksum (const uint8_t* buf, size_t len)
+{
+	uint8_t sum = 0;
+	size_t a;
+
+	for (a = 0; a < len; a++)
+		sum += buf[a];
+	return (sum == 0);
+}
+
+/* Copy a physical memory chunk into a memory buffer.
+ * This function allocates memory.
+ */
+static void*
+mem_chunk (size_t base, size_t len)
+{
+	void* p;
+	int fd;
+	size_t mmoffset;
+	void* mmp;
+
+	fd = open ("/dev/mem", O_RDONLY);
+	if (fd == -1)
+		return NULL;
+
+	p = malloc (len);
+	if (p == NULL)
+	{
+		close (fd);
+		return NULL;
+	}
+
+#ifdef _SC_PAGESIZE
+	mmoffset = base % sysconf (_SC_PAGESIZE);
+#else
+	mmoffset = base % getpagesize ();
+#endif
+	/* Please note that we don't use mmap() for performance reasons here,
+	 * but to workaround problems many people encountered when trying
+	 * to read from /dev/mem using regular read() calls.
+	 */
+	mmp = mmap (0, mmoffset + len, PROT_READ, MAP_SHARED, fd,
+		    base - mmoffset);
+	if (mmp == MAP_FAILED) {
+		free (p);
+		close (fd);
+		return NULL;
+	}
+
+	memcpy (p, mmp + mmoffset, len);
+
+	munmap (mmp, mmoffset + len);
+
+	close (fd);
+
+	return p;
+}
+
+static const char*
+dmi_string (struct dmi_header* dm, uint8_t s)
+{
+	char* bp = (char*)dm;
+	size_t i, len;
+
+	if (s == 0)
+		return "Not Specified";
+
+	bp += dm->length;
+	while (s > 1 && *bp)
+	{
+		bp += strlen (bp);
+		bp++;
+		s--;
+	}
+
+	if (!*bp)
+		return "<BAD INDEX>";
+
+	/* ASCII filtering */
+	len = strlen (bp);
+	for (i = 0; i < len; i++)
+		if (bp[i] < 32 || bp[i] == 127)
+			bp[i] = '.';
+
+	return bp;
+}
+
+static char*
+dmi_table (uint32_t base, uint16_t len, uint16_t num)
+{
+	uint8_t* buf;
+	uint8_t* data;
+	int i = 0;
+	char* ret = NULL;
+
+	buf = mem_chunk (base, len);
+	if (buf == NULL)
+		return NULL;
+
+	data = buf;
+	while (i < num && data + sizeof (struct dmi_header) <= buf + len) {
+		uint8_t* next;
+		struct dmi_header* h = (struct dmi_header*)data;
+
+		/* Stop decoding at end of table marker */
+		if (h->type == 127)
+			break;
+
+		/* Look for the next handle */
+		next = data + h->length;
+		while (next - buf + 1 < len && (next[0] != 0 || next[1] != 0))
+			next++;
+		next += 2;
+		/* system-manufacturer */
+		if (h->type == 1 && h->length > 0x04) {
+			ret = strdup (dmi_string (h, data[0x04]));
+			break;
+		}
+
+		data = next;
+		i++;
+	}
+
+	free (buf);
+	return ret;
+}
+
+static char*
+smbios_decode (uint8_t* buf)
+{
+	if (checksum (buf, buf[0x05]) &&
+	    memcmp (buf + 0x10, "_DMI_", 5) == 0 &&
+	    checksum (buf + 0x10, 0x0F)) {
+		return dmi_table (DWORD (buf + 0x18), WORD (buf + 0x16),
+				  WORD (buf + 0x1C));
+	}
+
+	return NULL;
+}
+
+static char*
+legacy_decode (uint8_t* buf)
+{
+	if (checksum (buf, 0x0F)) {
+		return dmi_table (DWORD (buf + 0x08), WORD (buf + 0x06),
+				  WORD (buf + 0x0C));
+	}
+
+	return NULL;
+}
+
+static char*
+dmi_system_manufacturer (void)
+{
+	uint8_t* buf;
+	size_t fp;
+	char* ret = NULL;
+
+	buf = mem_chunk (0xF0000, 0x10000);
+	if (buf == NULL)
+		return NULL;
+
+	for (fp = 0; fp <= 0xFFF0; fp += 16) {
+		if (memcmp (buf + fp, "_SM_", 4) == 0 && fp <= 0xFFE0) {
+			ret = smbios_decode (buf + fp);
+			if (ret)
+				break;
+			fp += 16;
+		} else if (memcmp (buf + fp, "_DMI_", 5) == 0) {
+			ret = legacy_decode (buf + fp);
+			if (ret)
+				break;
+		}
+	}
+
+	free (buf);
+	return ret;
+}
+
+
 static inline uint32_t
 pth_get_size (const PedDevice* dev)
 {
@@ -450,7 +653,8 @@
 	if (!gpt_sig_found)
 		return 0;
 
-	if (ped_device_read(dev, &legacy_mbr, 0, GPT_HEADER_SECTORS)) {
+	if (!is_apple &&
+	    ped_device_read(dev, &legacy_mbr, 0, GPT_HEADER_SECTORS)) {
 		if (!_pmbr_is_valid (&legacy_mbr)) {
 			int ex_status = ped_exception_throw (
 				PED_EXCEPTION_WARNING,
@@ -801,6 +1005,10 @@
  *  warn if it's not there, and treat the disk as MSDOS, with a note
  *  for users to use Parted to "fix up" their disk if they
  *  really want it to be considered GPT.
+ *
+ *  Of course, this is incompatible with how Apple handle things. For
+ *  legacy BIOS compatibility on Apple machines, we need a valid legacy MBR
+ *  rather than a protective one. Aren't standards wonderful?
  ************************************************************/
 static int
 gpt_read (PedDisk * disk)
@@ -959,6 +1167,96 @@
 	return ped_device_write (dev, &pmbr, GPT_PMBR_LBA, GPT_PMBR_SECTORS);
 }
 
+static int
+fill_raw_part (PartitionRecord_t* raw_part, PedPartition *part, PedSector offset, int number)
+{
+	GPTPartitionData* gpt_part_data = part->disk_specific;
+
+	if (part->fs_type) {
+		if (strncmp (part->fs_type->name, "fat", 3) == 0)
+			raw_part->OSType = 0x0b;
+		else if (strncmp (part->fs_type->name, "ntfs", 4) == 0)
+			raw_part->OSType = 0x07;
+		else if (strncmp (part->fs_type->name, "hfs", 3) == 0)
+			raw_part->OSType = 0xaf;
+		else if (strncmp (part->fs_type->name, "ext3", 4) == 0 ||
+			 strncmp (part->fs_type->name, "ext4", 4) == 0)
+			raw_part->OSType = 0x83;
+		else if (strncmp (part->fs_type->name, "linux-swap", 10) == 0)
+			raw_part->OSType = 0x82;
+		else 
+			raw_part->OSType = 0xef;
+	}
+
+	/* EFI system partitions will have a FAT filesystem and 
+	   PARTITION_SYSTEM_GUID */
+
+	if (!guid_cmp (gpt_part_data->type, PARTITION_SYSTEM_GUID)) {
+		if (raw_part->OSType == 0x0b) {
+			raw_part->OSType = 0xee;
+		}
+	}
+
+	/* Apple's firmware appears to become unhappy if the second partition
+	   isn't bootable */
+
+	if (number == 2)
+		raw_part->BootIndicator = 0x80;
+
+	raw_part->StartingLBA = PED_CPU_TO_LE32 ((part->geom.start - offset)
+				  / (part->disk->dev->sector_size / 512));
+
+	raw_part->SizeInLBA = PED_CPU_TO_LE32 (part->geom.length
+				  / (part->disk->dev->sector_size / 512));
+
+	/* Apple's firmware also appears to be unhappy if the EFI system 
+	   partition doesn't extend all the way to the start of the disk */
+
+	if (raw_part->OSType == 0xee) {
+	  raw_part->SizeInLBA += raw_part->StartingLBA - 1;
+	  raw_part->StartingLBA = 1;
+	}
+
+	raw_part->StartHead = 0xfe;
+	raw_part->StartSector = 0xff;
+	raw_part->StartTrack = 0xff;
+	raw_part->EndHead = 0xfe;
+	raw_part->EndSector = 0xff;
+	raw_part->EndTrack = 0xff;
+	
+	return 1;
+}
+
+static int
+_gptsync (const PedDisk * disk)
+{
+	LegacyMBR_t pmbr;
+	PedPartition* part;
+	int i;
+
+	if (!ped_device_read (disk->dev, (void*) &pmbr, GPT_PMBR_LBA, 
+			       GPT_PMBR_SECTORS))
+		return 0;
+
+	memset(&pmbr.PartitionRecord, 0, sizeof(pmbr.PartitionRecord));
+	pmbr.Signature = PED_CPU_TO_LE16(MSDOS_MBR_SIGNATURE);
+
+	for (i=1; i<=4; i++) {
+		part = ped_disk_get_partition (disk, i);
+		if (!part)
+			continue;
+
+		if (!fill_raw_part (&pmbr.PartitionRecord [i - 1], part, 0, i))
+			return 0;
+	}
+
+	if (!ped_device_write (disk->dev, (void*) &pmbr, GPT_PMBR_LBA, 
+			       GPT_PMBR_SECTORS))
+		return 0;
+
+	return ped_device_sync (disk->dev);
+}
+
 static void
 _generate_header (const PedDisk* disk, int alternate, uint32_t ptes_crc,
 		  GuidPartitionTableHeader_t** gpt_p)
@@ -1057,9 +1355,15 @@
 
 	ptes_crc = efi_crc32 (ptes, ptes_size);
 
-	/* Write protective MBR */
-	if (!_write_pmbr (disk->dev))
-		goto error_free_ptes;
+	if (is_apple) {
+		/* Write synced MBR */
+		if (!_gptsync (disk))
+			goto error_free_ptes;
+	} else {
+		/* Write protective MBR */
+		if (!_write_pmbr (disk->dev))
+			goto error_free_ptes;
+	}
 
 	/* Write PTH and PTEs */
 	_generate_header (disk, 0, ptes_crc, &gpt);
@@ -1523,10 +1827,27 @@
 void
 ped_disk_gpt_init()
 {
+	const char* force_gpt_apple;
+
 	PED_ASSERT (sizeof (GuidPartitionEntryAttributes_t) == 8, return);
 	PED_ASSERT (sizeof (GuidPartitionEntry_t) == 128, return);
 
 	ped_disk_type_register (&gpt_disk_type);
+
+	force_gpt_apple = getenv ("PARTED_GPT_APPLE");
+	if (force_gpt_apple) {
+		if (strcmp (force_gpt_apple, "1") == 0)
+			is_apple = 1;
+	} else {
+		char* manufacturer = dmi_system_manufacturer ();
+		if (manufacturer &&
+		    (strncasecmp (APPLE_DMI, manufacturer,
+				  strlen (APPLE_DMI)) == 0 ||
+		     strncasecmp (APPLE_DMI_2, manufacturer,
+				  strlen (APPLE_DMI_2)) == 0))
+			is_apple = 1;
+		free (manufacturer);
+	}
 }
 
 void


More information about the parted-devel mailing list