[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