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                                       [[email protected]]
#! /bin/sh /usr/share/dpatch/dpatch-run
## gptsync.dpatch by Matthew Garrett <[email protected]> and
## Colin Watson <[email protected]>
##
## 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 <[email protected]>
+      (C) 2002-2005 Jean Delvare <[email protected]>
+      Reduced for Intel Mac detection by Colin Watson <[email protected]>
+
     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
_______________________________________________
parted-devel mailing list
[email protected]
http://lists.alioth.debian.org/mailman/listinfo/parted-devel

Reply via email to