-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

with aim for inclusion in mainline Grub2.

Grub2 1.99 is base. Patches to gpt.c and msdos.c in /grub-core/partmap/.

Diffs attached with original code and patched code.

Goal: To make the disk bootable on: PCs using BIOS, PCs using UEFI,
Intel Macintoshes using BIOS, and Intel Macintoshes using EFI, as well
as having the (HybridData) partition readable by both Windows and MacOSX.

ExaExample partition layout:mple partition layout:

Physical: MBR GPT (HybridData) (EFI) (BIOS)
MBR:
1: 0C (FAT32) : FAT32 (HybridData)
2: EF (EFI) : FAT32 (EFI)
3: EE (GPT) : (Protective)
GPT:
1: 0700 (Data) : FAT32 (HybridData)
2: ef00 (EFI) : FAT32 (EFI)
3: 0f02 (BIOS) : Unformatted (BIOS)

Current behaviour:

Grub2 detects an MBR and checks if the first MBR primary partition is
GPT protective partition. The first MBR primary partition is not a GPT
protective partition, so it treats the disk as a pure MBR disk and so
refuses to install the boot loader kernel in the post-MBR gap because
there is no post-MBR gap because it is taken by GPT protective partition.

Proposed behaviour:

Grub2 detects an MBR and checks if any of the MBR primary partitions are
GPT protective partitions. It finds a GPT protective partition (MBR
primary partition 3 in the example), and concludes that the disk is a
GPT disk and looks for a BIOS boot partition (Hah!IdontNeedEFI) (GPT
partition 3 in the example) in which to install the boot loader kernel.

Questions, Comments, and Feedback welcome.

See also: http://perditauranus.dyndns.org/tdiary/?date=20110803
See also: http://www.rodsbooks.com/gdisk/hybrid.html#bootloaders

- -- 
David Joshua Geary
UNE Linux User Group: li...@une.edu.au <http://lugune.dyndns.org>
I don't care what software you use so long as
  we only exchange files in open data formats
Open-Document <http://en.wikipedia.org/wiki/Open_Document>
Ogg <http://en.wikipedia.org/wiki/Ogg>
PDF <http://en.wikipedia.org/wiki/Pdf>

- -----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1.4.10 (GNU/Linux)

mQINBE0JY54BEAD2D3ckM+FTElSG6w89L1PkeB+m8+8K5ZJZKfLJXWv7NNTRS5DC
uJd/OrlQwC4Ecwkp4HqmjF8jYzCeLEnC7SR6VU3j05l1vR7pZvphUx7lYBbhXbV/
ykBQBK9AWQwf1ve3G2+hGRAeYmhSzen3Px06aBq4/nr3adF1JFekV5CyCuHxDriT
p7HMqEwCvRExnaJD45VKzCQiCYLDYkgVc5u+yBWHd92Jk0DHtm6T/CuoLsoYPIgU
Jy8sNFdGE9cC3Nb6Ay/Je4FUlKqu9xNej7KGF0SNVjfD7+bMlB37uaDSk1vLGkmg
rhnR78YTdZ2iE2hcbae8l/N9aFWUHfxnuHEew/imTJgaBOMOp6k3FGIFG+1qznnG
UlXBMR7gY5jyPKTLe0iJo5MCylKeS56uyDgGv8FtGD73MRqGxktR5b8Zv4Wjv0cz
S4yBT8dvAYohw8UeRF4EKQlmxX7rusaipXCe/lGyuytfNFJ77EHMESzVkM/da0qk
Zt2sotf83XXE+h6lsUDee1whNDtNvaYmTziKZVHJ3olP3kg+FglgoWKG/y5QQHtW
ASa4tnlnNFVGJaOBVnVOq5RsrmQ+YSmYsm/OfjzAthc3QM5BhQhB381y4lKtFo1S
G7qChNSpA29ea1Muqgz3fDlP3m4iHo1AFvBTgpvEIh/Eb8MXSmeHxmEk0QARAQAB
tD9EYXZpZCBHZWFyeSAoR01haWwgRW1haWwgU2lnbmluZyBLZXkpIDxiaWFuY2Eu
dXJhbnVzQGdtYWlsLmNvbT6JAjgEEwECACIFAk0JY54CGyMGCwkIBwMCBhUIAgkK
CwQWAgMBAh4BAheAAAoJEMR6DgDuR4IfvsUQALCnZfpyz39BQT609n0m+wLZg8CX
uTnCf1m+oZj5W11oqqZd0XcHc1o4n6F6VNvSULHCgNYP6kOnJHliBX53Qdr7muJz
qQSwtywaS6oQUHWJk++youkvBqV/A4tiggEsXKzdQx/F+o2sIWQ4VGR3NlLkOJa4
xddVAGLuYHmOt1OenwfFXUFodmcPSWwnyVu9E8cm7iZxdg66u/fEraY4pC2yI2Zi
T0c1NRVA6ytqo/t4V2wmysZLj9r+O3q88ncUTg1qcVNS32eEVxZJS1R0y7MyM+Gr
lh6OI+VrhkfQ/DawvsAqHxxgMWMrzBQUa9ss7mPlawZWOSBiBLtckYHpPJje21ze
EPzhPub7EnXfenLWIHG2+O0FIkPIu5OSRwHtAgkTXBXtrzzyMIU/cFsuI0D2Da6m
w4QSlEAb8hWGS5LqaM/8i+eaTDL7XgZ9Z7OibS4lzcUnZZOW5dzHIqDVK77c57If
rG2cin/GChsYrlckN1XrvlulSXYu08D/xL4f39sUZexiyYZtLoWas+MvOboGgc8o
MfE7U92gXb1l4hm3iKXHzaYdBC1YqxMdgy9RBDaSbHq3n0pFpCGW8SURtuOlkVbq
kgwPq6v1obb0ZvpE3cLPN8PhqSWxbM4sEYYo6jDAaTRkv/Cy/faEQuCJ+wmVT2bu
deYspeWy73rGANavuQINBE0JY54BEADD+h4XyQmbyfNrsmPjHlqJtmZHojtiZZKZ
nKoawdLex/L7XJBXfNhTu1kwCV2fW1HibyUW/a5m41H9ked5gScR8bEn02liXc6c
Ad6rAMSY9H2/5+FXcGPJqV/9VJTNC8FTEADmJzHzENLDcpil1jXkYDwSn5wYYsmS
1GtT4jsRkGZYzXerPs3JfpxTFuVQWTsodyzfs+YwnNVIcAX4yBrg5WSRPlbHC0WP
VQxqFVdDDq0RN11+2sM6BYVvnL1fX69TN4MAPhBjbYalIt0Ls9dGjn/aiB0E1nEn
QfRcQb939gJQV5LLKxICc9Qa/GfMFjt6OYArZ8RzMvIs7WGdFXG0GuU/DjHRI65U
f7Ed7bGZw9S0ZAjawH8q1/JeUkNtbMCz5oRvvhYTXOw3WLXCeTPALJ6XU7fRTY48
rIDxpvoxyE4mMoNnn1ZE5rcIbCza0tDD3FUPae6i6Mixnk0hXGirwnwPuJyFi3Io
zibtMzuoju7l2gKzhZVcp4HlERhGTcEPfCaVIGy3j0k0nfc+hCySA59fXfs6UK5T
URhSKLb8+R/Ne0Fyiyg8ASdS8HPIs8Njw4CppfWX5Ds5NgwF5OALlYHkIKjn07Kc
+bGO+WWJvnZfo2fdjgPPs4ECaJL7L5RuVuy26QnPMLgAUNJLgGjugGoE0pOLSwmj
f3xOB4GlowARAQABiQIfBBgBAgAJBQJNCWOeAhsMAAoJEMR6DgDuR4If6LAQAIAx
WHngmPZ1vzraRVadFnQrYr4bpBT+Hznwm91HtAup8HgutSiulOY1Kbpra1avjmQi
MYCRQqDeOsFGirOomterbU18sXX7YBFbAT0c+rHoSdtFP2JTkSzN0Ocs1mZZuLfu
Dw/DSAaxlpkKQUJ5xn+qAOPr0fX2HbNcTc9kKYPNJJ9n7WtPhG6hC7ehjHis2jSz
Am3Ik+5gNZjaNkHsanaGTfx3mo/jf2AUGydB2quYPz8DFyTYN6q+VZ6+Vzy08RGz
MLWwM/SSMhT+GXrhSeuAiO26a70T0XTbhliccUDlG6ORPQ+ydzkffNQB35OfIkbA
+SoiDf1txduWDeJxblZ3feYqwBcjBBJU/ofEigH32z0ZOsFpWcJTsXSB2RyoJT+Y
Pb426uZYYTi2ZtWGcDtwRzK2lXM3WIHnoduFY5LIXq51c+cP2u8a461pmrlTvixE
y+IfX/zHfvSUnJDpnO4sA770oCHBUsbmU14oPdr+Kla67jqpjujap1ITRA96bcNm
K7O3vOKqbkAoy4w9ogCbBHrH1/8M+iRWA+0Iim+zhYSeEmjLszeecII9MH3u8CS0
DjLhpLrX6WG7F9fr0xAnc3Dui+qBm/q1dKenCKauUYWRgDPUXzP32oIiw/bXBp/4
qKbhUOJTXVVns4OnvPNzFoCvcNS6G/3ZGlX/6duU
=kv+y
- -----END PGP PUBLIC KEY BLOCK-----
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.11 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/

iQIcBAEBAgAGBQJOULD7AAoJEMR6DgDuR4If/vkP/3K3gczRr5LpChbtDKuDdu3/
SGCtRahsnwsR9hmyxwCRKjuiYFDFw+K7aybX/bI9doJxQfR3rL5rclgkuvxvl4nK
fhEF+FmIANVvAHVQU4rJNHu1xpxgwplJ2AhBpuNbMWyyVrA8yBwezEgd1YYDedMs
tf2G1e4HW+RYvF1oqDZARhJpfgXZJW1oqeVrNA8ZBdyj05nVDm8yB7Os3JawsFE9
E9fvypowlydLDzM1U//3a4CjJ8Q09QTa0GzGiE07pCOWENEehdW2pDJXnQB/BXUY
91FxIcLsMXuY5qg2PVaEOpYt8yQsAVzqNEYX/0nPHf5/++jOtLUiYgXRtd/vq+90
l7kYxo1rm+sRusmy2y0S5BKD8LsqB/I8Y/PP0vKwkKujzFKTcbYRKxyqTvgFLyia
HopvTZxEwLzvjsn9k2h2kDYVYOXnMiylpyyMlWBlT1J5xwS70RsVlGwfxYqebpLg
S6FZBN9SU20SX7LaRcGwedfjhrHAJk5MLvy3m8cuECZp7HZr3fzBe/a4hJfiD2dG
nqHNiDhAFSJLh6lRCELO0noMVLfuJ5nsdubIi0esWIBLrf0aGmkwUwWu+w+txfdU
T3YpKfPzCrnnVDV1jc81uuXrnKrKMyQTpd33fnd3Rc5DpnvgBhlkN8WyeljA/lqo
2XLjGbJyhTZjlIh3SJG0
=/0rL
-----END PGP SIGNATURE-----
/* gpt.c - Read GUID Partition Tables (GPT).  */
/*
 *  GRUB  --  GRand Unified Bootloader
 *  Copyright (C) 2002,2005,2006,2007,2008  Free Software Foundation, Inc.
 *
 *  GRUB 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
 *  (at your option) any later version.
 *
 *  GRUB is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <grub/disk.h>
#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/partition.h>
#include <grub/dl.h>
#include <grub/msdos_partition.h>
#include <grub/gpt_partition.h>

GRUB_MOD_LICENSE ("GPLv3+");

static grub_uint8_t grub_gpt_magic[8] =
  {
    0x45, 0x46, 0x49, 0x20, 0x50, 0x41, 0x52, 0x54
  };

static const grub_gpt_part_type_t grub_gpt_partition_type_empty = GRUB_GPT_PARTITION_TYPE_EMPTY;

#ifdef GRUB_UTIL
static const grub_gpt_part_type_t grub_gpt_partition_type_bios_boot = GRUB_GPT_PARTITION_TYPE_BIOS_BOOT;
#endif

/* 512 << 7 = 65536 byte sectors.  */
#define MAX_SECTOR_LOG 7

static struct grub_partition_map grub_gpt_partition_map;



static grub_err_t
gpt_partition_map_iterate (grub_disk_t disk,
			   int (*hook) (grub_disk_t disk,
					const grub_partition_t partition))
{
  struct grub_partition part;
  struct grub_gpt_header gpt;
  struct grub_gpt_partentry entry;
  struct grub_msdos_partition_mbr mbr;
  grub_uint64_t entries;
  unsigned int i;
  int last_offset = 0;
  int sector_log = 0;

  /* Read the protective MBR.  */
  if (grub_disk_read (disk, 0, 0, sizeof (mbr), &mbr))
    return grub_errno;

  /* Check if it is valid.  */
  if (mbr.signature != grub_cpu_to_le16 (GRUB_PC_PARTITION_SIGNATURE))
    return grub_error (GRUB_ERR_BAD_PART_TABLE, "no signature");

  /* Make sure the MBR is a protective or hybrid MBR and not a normal MBR.  */
  for (i = 0; i < 4; ++i)
    if (mbr.entries[i].type != GRUB_PC_PARTITION_TYPE_GPT_DISK) {
      break;
    }
  if (i == 4)
    return grub_error (GRUB_ERR_BAD_PART_TABLE, "no GPT partition map found");

  /* Read the GPT header.  */
  for (sector_log = 0; sector_log < MAX_SECTOR_LOG; sector_log++)
    {
      if (grub_disk_read (disk, 1 << sector_log, 0, sizeof (gpt), &gpt))
	return grub_errno;

      if (grub_memcmp (gpt.magic, grub_gpt_magic, sizeof (grub_gpt_magic)) == 0)
	break;
    }
  if (sector_log == MAX_SECTOR_LOG)
    return grub_error (GRUB_ERR_BAD_PART_TABLE, "no valid GPT header");

  grub_dprintf ("gpt", "Read a valid GPT header\n");

  entries = grub_le_to_cpu64 (gpt.partitions) << sector_log;
  for (i = 0; i < grub_le_to_cpu32 (gpt.maxpart); i++)
    {
      if (grub_disk_read (disk, entries, last_offset,
			  sizeof (entry), &entry))
	return grub_errno;

      if (grub_memcmp (&grub_gpt_partition_type_empty, &entry.type,
		       sizeof (grub_gpt_partition_type_empty)))
	{
	  /* Calculate the first block and the size of the partition.  */
	  part.start = grub_le_to_cpu64 (entry.start) << sector_log;
	  part.len = (grub_le_to_cpu64 (entry.end)
		      - grub_le_to_cpu64 (entry.start) + 1)  << sector_log;
	  part.offset = entries;
	  part.number = i;
	  part.index = last_offset;
	  part.partmap = &grub_gpt_partition_map;
	  part.parent = disk->partition;

	  grub_dprintf ("gpt", "GPT entry %d: start=%lld, length=%lld\n", i,
			(unsigned long long) part.start,
			(unsigned long long) part.len);

	  if (hook (disk, &part))
	    return grub_errno;
	}

      last_offset += grub_le_to_cpu32 (gpt.partentry_size);
      if (last_offset == GRUB_DISK_SECTOR_SIZE)
	{
	  last_offset = 0;
	  entries++;
	}
    }

  return GRUB_ERR_NONE;
}

#ifdef GRUB_UTIL
static grub_err_t
gpt_partition_map_embed (struct grub_disk *disk, unsigned int *nsectors,
			 grub_embed_type_t embed_type,
			 grub_disk_addr_t **sectors)
{
  grub_disk_addr_t start = 0, len = 0;
  unsigned i;
  grub_err_t err;

  auto int NESTED_FUNC_ATTR find_usable_region (grub_disk_t disk,
						const grub_partition_t p);
  int NESTED_FUNC_ATTR find_usable_region (grub_disk_t disk __attribute__ ((unused)),
					   const grub_partition_t p)
  {
    struct grub_gpt_partentry gptdata;

    disk->partition = p->parent;
    if (grub_disk_read (disk, p->offset, p->index,
			sizeof (gptdata), &gptdata))
      return 0;

    /* If there's an embed region, it is in a dedicated partition.  */
    if (! grub_memcmp (&gptdata.type, &grub_gpt_partition_type_bios_boot, 16))
      {
	start = p->start;
	len = p->len;
	return 1;
      }

      return 0;
    }

  if (embed_type != GRUB_EMBED_PCBIOS)
    return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
		       "GPT curently supports only PC-BIOS embedding");

  err = gpt_partition_map_iterate (disk, find_usable_region);
  if (err)
    return err;

  if (len == 0)
    return grub_error (GRUB_ERR_FILE_NOT_FOUND,
		       "This GPT partition label has no BIOS Boot Partition;"
		       " embedding won't be possible!");

  if (len < *nsectors)
    return grub_error (GRUB_ERR_OUT_OF_RANGE,
		       "Your BIOS Boot Partition is too small;"
		       " embedding won't be possible!");

  *nsectors = len;
  *sectors = grub_malloc (*nsectors * sizeof (**sectors));
  if (!*sectors)
    return grub_errno;
  for (i = 0; i < *nsectors; i++)
    (*sectors)[i] = start + i;

  return GRUB_ERR_NONE;
}
#endif


/* Partition map type.  */
static struct grub_partition_map grub_gpt_partition_map =
  {
    .name = "gpt",
    .iterate = gpt_partition_map_iterate,
#ifdef GRUB_UTIL
    .embed = gpt_partition_map_embed
#endif
  };

GRUB_MOD_INIT(part_gpt)
{
  grub_partition_map_register (&grub_gpt_partition_map);
}

GRUB_MOD_FINI(part_gpt)
{
  grub_partition_map_unregister (&grub_gpt_partition_map);
}
70,71c70,75
<   /* Make sure the MBR is a protective MBR and not a normal MBR.  */
<   if (mbr.entries[0].type != GRUB_PC_PARTITION_TYPE_GPT_DISK)
---
>   /* Make sure the MBR is a protective or hybrid MBR and not a normal MBR.  */
>   for (i = 0; i < 4; ++i)
>     if (mbr.entries[i].type != GRUB_PC_PARTITION_TYPE_GPT_DISK) {
>       break;
>     }
>   if (i == 4)
/* gpt.c - Read GUID Partition Tables (GPT).  */
/*
 *  GRUB  --  GRand Unified Bootloader
 *  Copyright (C) 2002,2005,2006,2007,2008  Free Software Foundation, Inc.
 *
 *  GRUB 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
 *  (at your option) any later version.
 *
 *  GRUB is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <grub/disk.h>
#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/partition.h>
#include <grub/dl.h>
#include <grub/msdos_partition.h>
#include <grub/gpt_partition.h>

GRUB_MOD_LICENSE ("GPLv3+");

static grub_uint8_t grub_gpt_magic[8] =
  {
    0x45, 0x46, 0x49, 0x20, 0x50, 0x41, 0x52, 0x54
  };

static const grub_gpt_part_type_t grub_gpt_partition_type_empty = 
GRUB_GPT_PARTITION_TYPE_EMPTY;

#ifdef GRUB_UTIL
static const grub_gpt_part_type_t grub_gpt_partition_type_bios_boot = 
GRUB_GPT_PARTITION_TYPE_BIOS_BOOT;
#endif

/* 512 << 7 = 65536 byte sectors.  */
#define MAX_SECTOR_LOG 7

static struct grub_partition_map grub_gpt_partition_map;



static grub_err_t
gpt_partition_map_iterate (grub_disk_t disk,
                           int (*hook) (grub_disk_t disk,
                                        const grub_partition_t partition))
{
  struct grub_partition part;
  struct grub_gpt_header gpt;
  struct grub_gpt_partentry entry;
  struct grub_msdos_partition_mbr mbr;
  grub_uint64_t entries;
  unsigned int i;
  int last_offset = 0;
  int sector_log = 0;

  /* Read the protective MBR.  */
  if (grub_disk_read (disk, 0, 0, sizeof (mbr), &mbr))
    return grub_errno;

  /* Check if it is valid.  */
  if (mbr.signature != grub_cpu_to_le16 (GRUB_PC_PARTITION_SIGNATURE))
    return grub_error (GRUB_ERR_BAD_PART_TABLE, "no signature");

  /* Make sure the MBR is a protective MBR and not a normal MBR.  */
  if (mbr.entries[0].type != GRUB_PC_PARTITION_TYPE_GPT_DISK)
    return grub_error (GRUB_ERR_BAD_PART_TABLE, "no GPT partition map found");

  /* Read the GPT header.  */
  for (sector_log = 0; sector_log < MAX_SECTOR_LOG; sector_log++)
    {
      if (grub_disk_read (disk, 1 << sector_log, 0, sizeof (gpt), &gpt))
        return grub_errno;

      if (grub_memcmp (gpt.magic, grub_gpt_magic, sizeof (grub_gpt_magic)) == 0)
        break;
    }
  if (sector_log == MAX_SECTOR_LOG)
    return grub_error (GRUB_ERR_BAD_PART_TABLE, "no valid GPT header");

  grub_dprintf ("gpt", "Read a valid GPT header\n");

  entries = grub_le_to_cpu64 (gpt.partitions) << sector_log;
  for (i = 0; i < grub_le_to_cpu32 (gpt.maxpart); i++)
    {
      if (grub_disk_read (disk, entries, last_offset,
                          sizeof (entry), &entry))
        return grub_errno;

      if (grub_memcmp (&grub_gpt_partition_type_empty, &entry.type,
                       sizeof (grub_gpt_partition_type_empty)))
        {
          /* Calculate the first block and the size of the partition.  */
          part.start = grub_le_to_cpu64 (entry.start) << sector_log;
          part.len = (grub_le_to_cpu64 (entry.end)
                      - grub_le_to_cpu64 (entry.start) + 1)  << sector_log;
          part.offset = entries;
          part.number = i;
          part.index = last_offset;
          part.partmap = &grub_gpt_partition_map;
          part.parent = disk->partition;

          grub_dprintf ("gpt", "GPT entry %d: start=%lld, length=%lld\n", i,
                        (unsigned long long) part.start,
                        (unsigned long long) part.len);

          if (hook (disk, &part))
            return grub_errno;
        }

      last_offset += grub_le_to_cpu32 (gpt.partentry_size);
      if (last_offset == GRUB_DISK_SECTOR_SIZE)
        {
          last_offset = 0;
          entries++;
        }
    }

  return GRUB_ERR_NONE;
}

#ifdef GRUB_UTIL
static grub_err_t
gpt_partition_map_embed (struct grub_disk *disk, unsigned int *nsectors,
                         grub_embed_type_t embed_type,
                         grub_disk_addr_t **sectors)
{
  grub_disk_addr_t start = 0, len = 0;
  unsigned i;
  grub_err_t err;

  auto int NESTED_FUNC_ATTR find_usable_region (grub_disk_t disk,
                                                const grub_partition_t p);
  int NESTED_FUNC_ATTR find_usable_region (grub_disk_t disk __attribute__ 
((unused)),
                                           const grub_partition_t p)
  {
    struct grub_gpt_partentry gptdata;

    disk->partition = p->parent;
    if (grub_disk_read (disk, p->offset, p->index,
                        sizeof (gptdata), &gptdata))
      return 0;

    /* If there's an embed region, it is in a dedicated partition.  */
    if (! grub_memcmp (&gptdata.type, &grub_gpt_partition_type_bios_boot, 16))
      {
        start = p->start;
        len = p->len;
        return 1;
      }

      return 0;
    }

  if (embed_type != GRUB_EMBED_PCBIOS)
    return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
                       "GPT curently supports only PC-BIOS embedding");

  err = gpt_partition_map_iterate (disk, find_usable_region);
  if (err)
    return err;

  if (len == 0)
    return grub_error (GRUB_ERR_FILE_NOT_FOUND,
                       "This GPT partition label has no BIOS Boot Partition;"
                       " embedding won't be possible!");

  if (len < *nsectors)
    return grub_error (GRUB_ERR_OUT_OF_RANGE,
                       "Your BIOS Boot Partition is too small;"
                       " embedding won't be possible!");

  *nsectors = len;
  *sectors = grub_malloc (*nsectors * sizeof (**sectors));
  if (!*sectors)
    return grub_errno;
  for (i = 0; i < *nsectors; i++)
    (*sectors)[i] = start + i;

  return GRUB_ERR_NONE;
}
#endif


/* Partition map type.  */
static struct grub_partition_map grub_gpt_partition_map =
  {
    .name = "gpt",
    .iterate = gpt_partition_map_iterate,
#ifdef GRUB_UTIL
    .embed = gpt_partition_map_embed
#endif
  };

GRUB_MOD_INIT(part_gpt)
{
  grub_partition_map_register (&grub_gpt_partition_map);
}

GRUB_MOD_FINI(part_gpt)
{
  grub_partition_map_unregister (&grub_gpt_partition_map);
}
/* pc.c - Read PC style partition tables.  */
/*
 *  GRUB  --  GRand Unified Bootloader
 *  Copyright (C) 2002,2004,2005,2006,2007,2008,2009  Free Software Foundation, Inc.
 *
 *  GRUB 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
 *  (at your option) any later version.
 *
 *  GRUB is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <grub/partition.h>
#include <grub/msdos_partition.h>
#include <grub/disk.h>
#include <grub/mm.h>
#include <grub/misc.h>
#include <grub/dl.h>

GRUB_MOD_LICENSE ("GPLv3+");

static struct grub_partition_map grub_msdos_partition_map;


grub_err_t
grub_partition_msdos_iterate (grub_disk_t disk,
			      int (*hook) (grub_disk_t disk,
					   const grub_partition_t partition))
{
  struct grub_partition p;
  struct grub_msdos_partition_mbr mbr;
  int labeln = 0;
  grub_disk_addr_t lastaddr;
  grub_disk_addr_t ext_offset;
  grub_disk_addr_t delta = 0;

  if (disk->partition && disk->partition->partmap == &grub_msdos_partition_map)
    {
      if (disk->partition->msdostype == GRUB_PC_PARTITION_TYPE_LINUX_MINIX)
	delta = disk->partition->start;
      else
	return grub_error (GRUB_ERR_BAD_PART_TABLE, "no embedding supported");
    }

  p.offset = 0;
  ext_offset = 0;
  p.number = -1;
  p.partmap = &grub_msdos_partition_map;

  /* Any value different than `p.offset' will satisfy the check during
     first loop.  */
  lastaddr = !p.offset;

  while (1)
    {
      int i;
      struct grub_msdos_partition_entry *e;

      /* Read the MBR.  */
      if (grub_disk_read (disk, p.offset, 0, sizeof (mbr), &mbr))
	goto finish;

      /* This is our loop-detection algorithm. It works the following way:
	 It saves last position which was a power of two. Then it compares the
	 saved value with a current one. This way it's guaranteed that the loop
	 will be broken by at most third walk.
       */
      if (labeln && lastaddr == p.offset)
	return grub_error (GRUB_ERR_BAD_PART_TABLE, "loop detected");

      labeln++;
      if ((labeln & (labeln - 1)) == 0)
	lastaddr = p.offset;

      /* Check if it is valid.  */
      if (mbr.signature != grub_cpu_to_le16 (GRUB_PC_PARTITION_SIGNATURE))
	return grub_error (GRUB_ERR_BAD_PART_TABLE, "no signature");

      for (i = 0; i < 4; i++)
	if (mbr.entries[i].flag & 0x7f)
	  return grub_error (GRUB_ERR_BAD_PART_TABLE, "bad boot flag");

      /* First pass to find GPT partitions.  */
      for (p.index = 0; p.index < 4; p.index++)
	{
	  e = mbr.entries + p.index;

	  /* If this is a GPT partition, this MBR is just a dummy.  */
	  if (e->type == GRUB_PC_PARTITION_TYPE_GPT_DISK)
	    return grub_error (GRUB_ERR_BAD_PART_TABLE, "dummy mbr");
	}

      /* Second pass to analyze DOS partitions.  */
      for (p.index = 0; p.index < 4; p.index++)
	{
	  e = mbr.entries + p.index;

	  p.start = p.offset + grub_le_to_cpu32 (e->start) - delta;
	  p.len = grub_le_to_cpu32 (e->length);
	  p.msdostype = e->type;

	  grub_dprintf ("partition",
			"partition %d: flag 0x%x, type 0x%x, start 0x%llx, len 0x%llx\n",
			p.index, e->flag, e->type,
			(unsigned long long) p.start,
			(unsigned long long) p.len);

	  /* If this partition is a normal one, call the hook.  */
	  if (! grub_msdos_partition_is_empty (e->type)
	      && ! grub_msdos_partition_is_extended (e->type))
	    {
	      p.number++;

	      if (hook (disk, &p))
		return grub_errno;
	    }
	  else if (p.number < 4)
	    /* If this partition is a logical one, shouldn't increase the
	       partition number.  */
	    p.number++;
	}

      /* Find an extended partition.  */
      for (i = 0; i < 4; i++)
	{
	  e = mbr.entries + i;

	  if (grub_msdos_partition_is_extended (e->type))
	    {
	      p.offset = ext_offset + grub_le_to_cpu32 (e->start);
	      if (! ext_offset)
		ext_offset = p.offset;

	      break;
	    }
	}

      /* If no extended partition, the end.  */
      if (i == 4)
	break;
    }

 finish:
  return grub_errno;
}

#ifdef GRUB_UTIL
static grub_err_t
pc_partition_map_embed (struct grub_disk *disk, unsigned int *nsectors,
			grub_embed_type_t embed_type,
			grub_disk_addr_t **sectors)
{
  grub_disk_addr_t end = ~0ULL;
  struct grub_msdos_partition_mbr mbr;
  int labeln = 0;
  /* Any value different than `p.offset' will satisfy the check during
     first loop.  */
  grub_disk_addr_t lastaddr = 1;
  grub_disk_addr_t ext_offset = 0;
  grub_disk_addr_t offset = 0;

  if (embed_type != GRUB_EMBED_PCBIOS)
    return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
		       "PC-style partitions curently support "
		       "only PC-BIOS embedding");

  if (disk->partition)
    return grub_error (GRUB_ERR_OUT_OF_RANGE,
		       "Embedding on MSDOS subpartition isn't supported");

  while (1)
    {
      int i;
      struct grub_msdos_partition_entry *e;
      grub_err_t err;

      /* Read the MBR.  */
      err = grub_disk_read (disk, offset, 0, sizeof (mbr), &mbr);
      if (err)
	return err;

      /* This is our loop-detection algorithm. It works the following way:
	 It saves last position which was a power of two. Then it compares the
	 saved value with a current one. This way it's guaranteed that the loop
	 will be broken by at most third walk.
       */
      if (labeln && lastaddr == offset)
	return grub_error (GRUB_ERR_BAD_PART_TABLE, "loop detected");

      labeln++;
      if ((labeln & (labeln - 1)) == 0)
	lastaddr = offset;

      /* Check if it is valid.  */
      if (mbr.signature != grub_cpu_to_le16 (GRUB_PC_PARTITION_SIGNATURE))
	return grub_error (GRUB_ERR_BAD_PART_TABLE, "no signature");

      for (i = 0; i < 4; i++)
	if (mbr.entries[i].flag & 0x7f)
	  return grub_error (GRUB_ERR_BAD_PART_TABLE, "bad boot flag");

      /* Analyze DOS partitions.  */
      for (i = 0; i < 4; i++)
	{
	  e = mbr.entries + i;

	  if (!grub_msdos_partition_is_empty (e->type)
	      && end > offset + grub_le_to_cpu32 (e->start))
	    end = offset + grub_le_to_cpu32 (e->start);

	  /* If this is a GPT partition, this MBR is just a dummy.  */
	  if (e->type == GRUB_PC_PARTITION_TYPE_GPT_DISK)
	    return grub_error (GRUB_ERR_BAD_PART_TABLE, "dummy mbr");
	}

      /* Find an extended partition.  */
      for (i = 0; i < 4; i++)
	{
	  e = mbr.entries + i;

	  if (grub_msdos_partition_is_extended (e->type))
	    {
	      offset = ext_offset + grub_le_to_cpu32 (e->start);
	      if (! ext_offset)
		ext_offset = offset;

	      break;
	    }
	}

      /* If no extended partition, the end.  */
      if (i == 4)
	break;
    }

  if (end >= *nsectors + 2)
    {
      unsigned i;
      *nsectors = end - 2;
      *sectors = grub_malloc (*nsectors * sizeof (**sectors));
      if (!*sectors)
	return grub_errno;
      for (i = 0; i < *nsectors; i++)
	(*sectors)[i] = 1 + i;
      return GRUB_ERR_NONE;
    }

  if (end <= 1)
    return grub_error (GRUB_ERR_FILE_NOT_FOUND,
		       "This msdos-style partition label has no "
		       "post-MBR gap; embedding won't be possible!");

  if (*nsectors > 62)
    return grub_error (GRUB_ERR_OUT_OF_RANGE,
		       "Your core.img is unusually large.  "
		       "It won't fit in the embedding area.");

  return grub_error (GRUB_ERR_OUT_OF_RANGE,
		     "Your embedding area is unusually small.  "
		     "core.img won't fit in it.");
}
#endif


/* Partition map type.  */
static struct grub_partition_map grub_msdos_partition_map =
  {
    .name = "msdos",
    .iterate = grub_partition_msdos_iterate,
#ifdef GRUB_UTIL
    .embed = pc_partition_map_embed
#endif
  };

GRUB_MOD_INIT(part_msdos)
{
  grub_partition_map_register (&grub_msdos_partition_map);
}

GRUB_MOD_FINI(part_msdos)
{
  grub_partition_map_unregister (&grub_msdos_partition_map);
}
90c90,100
<       /* Analyze DOS partitions.  */
---
>       /* First pass to find GPT partitions.  */
>       for (p.index = 0; p.index < 4; p.index++)
> 	{
> 	  e = mbr.entries + p.index;
> 
> 	  /* If this is a GPT partition, this MBR is just a dummy.  */
> 	  if (e->type == GRUB_PC_PARTITION_TYPE_GPT_DISK)
> 	    return grub_error (GRUB_ERR_BAD_PART_TABLE, "dummy mbr");
> 	}
> 
>       /* Second pass to analyze DOS partitions.  */
105,108d114
< 	  /* If this is a GPT partition, this MBR is just a dummy.  */
< 	  if (e->type == GRUB_PC_PARTITION_TYPE_GPT_DISK && p.index == 0)
< 	    return grub_error (GRUB_ERR_BAD_PART_TABLE, "dummy mbr");
< 
213c219
< 	  if (e->type == GRUB_PC_PARTITION_TYPE_GPT_DISK && i == 0)
---
> 	  if (e->type == GRUB_PC_PARTITION_TYPE_GPT_DISK)
/* pc.c - Read PC style partition tables.  */
/*
 *  GRUB  --  GRand Unified Bootloader
 *  Copyright (C) 2002,2004,2005,2006,2007,2008,2009  Free Software Foundation, 
Inc.
 *
 *  GRUB 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
 *  (at your option) any later version.
 *
 *  GRUB is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <grub/partition.h>
#include <grub/msdos_partition.h>
#include <grub/disk.h>
#include <grub/mm.h>
#include <grub/misc.h>
#include <grub/dl.h>

GRUB_MOD_LICENSE ("GPLv3+");

static struct grub_partition_map grub_msdos_partition_map;


grub_err_t
grub_partition_msdos_iterate (grub_disk_t disk,
                              int (*hook) (grub_disk_t disk,
                                           const grub_partition_t partition))
{
  struct grub_partition p;
  struct grub_msdos_partition_mbr mbr;
  int labeln = 0;
  grub_disk_addr_t lastaddr;
  grub_disk_addr_t ext_offset;
  grub_disk_addr_t delta = 0;

  if (disk->partition && disk->partition->partmap == &grub_msdos_partition_map)
    {
      if (disk->partition->msdostype == GRUB_PC_PARTITION_TYPE_LINUX_MINIX)
        delta = disk->partition->start;
      else
        return grub_error (GRUB_ERR_BAD_PART_TABLE, "no embedding supported");
    }

  p.offset = 0;
  ext_offset = 0;
  p.number = -1;
  p.partmap = &grub_msdos_partition_map;

  /* Any value different than `p.offset' will satisfy the check during
     first loop.  */
  lastaddr = !p.offset;

  while (1)
    {
      int i;
      struct grub_msdos_partition_entry *e;

      /* Read the MBR.  */
      if (grub_disk_read (disk, p.offset, 0, sizeof (mbr), &mbr))
        goto finish;

      /* This is our loop-detection algorithm. It works the following way:
         It saves last position which was a power of two. Then it compares the
         saved value with a current one. This way it's guaranteed that the loop
         will be broken by at most third walk.
       */
      if (labeln && lastaddr == p.offset)
        return grub_error (GRUB_ERR_BAD_PART_TABLE, "loop detected");

      labeln++;
      if ((labeln & (labeln - 1)) == 0)
        lastaddr = p.offset;

      /* Check if it is valid.  */
      if (mbr.signature != grub_cpu_to_le16 (GRUB_PC_PARTITION_SIGNATURE))
        return grub_error (GRUB_ERR_BAD_PART_TABLE, "no signature");

      for (i = 0; i < 4; i++)
        if (mbr.entries[i].flag & 0x7f)
          return grub_error (GRUB_ERR_BAD_PART_TABLE, "bad boot flag");

      /* Analyze DOS partitions.  */
      for (p.index = 0; p.index < 4; p.index++)
        {
          e = mbr.entries + p.index;

          p.start = p.offset + grub_le_to_cpu32 (e->start) - delta;
          p.len = grub_le_to_cpu32 (e->length);
          p.msdostype = e->type;

          grub_dprintf ("partition",
                        "partition %d: flag 0x%x, type 0x%x, start 0x%llx, len 
0x%llx\n",
                        p.index, e->flag, e->type,
                        (unsigned long long) p.start,
                        (unsigned long long) p.len);

          /* If this is a GPT partition, this MBR is just a dummy.  */
          if (e->type == GRUB_PC_PARTITION_TYPE_GPT_DISK && p.index == 0)
            return grub_error (GRUB_ERR_BAD_PART_TABLE, "dummy mbr");

          /* If this partition is a normal one, call the hook.  */
          if (! grub_msdos_partition_is_empty (e->type)
              && ! grub_msdos_partition_is_extended (e->type))
            {
              p.number++;

              if (hook (disk, &p))
                return grub_errno;
            }
          else if (p.number < 4)
            /* If this partition is a logical one, shouldn't increase the
               partition number.  */
            p.number++;
        }

      /* Find an extended partition.  */
      for (i = 0; i < 4; i++)
        {
          e = mbr.entries + i;

          if (grub_msdos_partition_is_extended (e->type))
            {
              p.offset = ext_offset + grub_le_to_cpu32 (e->start);
              if (! ext_offset)
                ext_offset = p.offset;

              break;
            }
        }

      /* If no extended partition, the end.  */
      if (i == 4)
        break;
    }

 finish:
  return grub_errno;
}

#ifdef GRUB_UTIL
static grub_err_t
pc_partition_map_embed (struct grub_disk *disk, unsigned int *nsectors,
                        grub_embed_type_t embed_type,
                        grub_disk_addr_t **sectors)
{
  grub_disk_addr_t end = ~0ULL;
  struct grub_msdos_partition_mbr mbr;
  int labeln = 0;
  /* Any value different than `p.offset' will satisfy the check during
     first loop.  */
  grub_disk_addr_t lastaddr = 1;
  grub_disk_addr_t ext_offset = 0;
  grub_disk_addr_t offset = 0;

  if (embed_type != GRUB_EMBED_PCBIOS)
    return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
                       "PC-style partitions curently support "
                       "only PC-BIOS embedding");

  if (disk->partition)
    return grub_error (GRUB_ERR_OUT_OF_RANGE,
                       "Embedding on MSDOS subpartition isn't supported");

  while (1)
    {
      int i;
      struct grub_msdos_partition_entry *e;
      grub_err_t err;

      /* Read the MBR.  */
      err = grub_disk_read (disk, offset, 0, sizeof (mbr), &mbr);
      if (err)
        return err;

      /* This is our loop-detection algorithm. It works the following way:
         It saves last position which was a power of two. Then it compares the
         saved value with a current one. This way it's guaranteed that the loop
         will be broken by at most third walk.
       */
      if (labeln && lastaddr == offset)
        return grub_error (GRUB_ERR_BAD_PART_TABLE, "loop detected");

      labeln++;
      if ((labeln & (labeln - 1)) == 0)
        lastaddr = offset;

      /* Check if it is valid.  */
      if (mbr.signature != grub_cpu_to_le16 (GRUB_PC_PARTITION_SIGNATURE))
        return grub_error (GRUB_ERR_BAD_PART_TABLE, "no signature");

      for (i = 0; i < 4; i++)
        if (mbr.entries[i].flag & 0x7f)
          return grub_error (GRUB_ERR_BAD_PART_TABLE, "bad boot flag");

      /* Analyze DOS partitions.  */
      for (i = 0; i < 4; i++)
        {
          e = mbr.entries + i;

          if (!grub_msdos_partition_is_empty (e->type)
              && end > offset + grub_le_to_cpu32 (e->start))
            end = offset + grub_le_to_cpu32 (e->start);

          /* If this is a GPT partition, this MBR is just a dummy.  */
          if (e->type == GRUB_PC_PARTITION_TYPE_GPT_DISK && i == 0)
            return grub_error (GRUB_ERR_BAD_PART_TABLE, "dummy mbr");
        }

      /* Find an extended partition.  */
      for (i = 0; i < 4; i++)
        {
          e = mbr.entries + i;

          if (grub_msdos_partition_is_extended (e->type))
            {
              offset = ext_offset + grub_le_to_cpu32 (e->start);
              if (! ext_offset)
                ext_offset = offset;

              break;
            }
        }

      /* If no extended partition, the end.  */
      if (i == 4)
        break;
    }

  if (end >= *nsectors + 2)
    {
      unsigned i;
      *nsectors = end - 2;
      *sectors = grub_malloc (*nsectors * sizeof (**sectors));
      if (!*sectors)
        return grub_errno;
      for (i = 0; i < *nsectors; i++)
        (*sectors)[i] = 1 + i;
      return GRUB_ERR_NONE;
    }

  if (end <= 1)
    return grub_error (GRUB_ERR_FILE_NOT_FOUND,
                       "This msdos-style partition label has no "
                       "post-MBR gap; embedding won't be possible!");

  if (*nsectors > 62)
    return grub_error (GRUB_ERR_OUT_OF_RANGE,
                       "Your core.img is unusually large.  "
                       "It won't fit in the embedding area.");

  return grub_error (GRUB_ERR_OUT_OF_RANGE,
                     "Your embedding area is unusually small.  "
                     "core.img won't fit in it.");
}
#endif


/* Partition map type.  */
static struct grub_partition_map grub_msdos_partition_map =
  {
    .name = "msdos",
    .iterate = grub_partition_msdos_iterate,
#ifdef GRUB_UTIL
    .embed = pc_partition_map_embed
#endif
  };

GRUB_MOD_INIT(part_msdos)
{
  grub_partition_map_register (&grub_msdos_partition_map);
}

GRUB_MOD_FINI(part_msdos)
{
  grub_partition_map_unregister (&grub_msdos_partition_map);
}

Attachment: gpt.c.sig
Description: PGP signature

Attachment: gpt.c.diff.sig
Description: PGP signature

Attachment: gpt.c.original.sig
Description: PGP signature

Attachment: msdos.c.sig
Description: PGP signature

Attachment: msdos.c.diff.sig
Description: PGP signature

Attachment: msdos.c.original.sig
Description: PGP signature

_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel

Reply via email to