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

