grub-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH] core/partmap: Add El Torito boot catalog parsing


From: Ross Lagerwall
Subject: [PATCH] core/partmap: Add El Torito boot catalog parsing
Date: Sun, 7 Jun 2015 11:24:46 +0100

Add a module, part_eltorito, to allow parsing of the El Torito boot
catalog into partitions. This follows the El Torito Bootable CD-ROM
Format Specification Version 1.0 and the UEFI Specification 2.5.
In cases where the specification is unclear, the code follows the UEFI
reference implementation.

This is useful when booting CDs in UEFI mode. Before, GRUB would not be
able to use the embedded ESP from which it was executed, so it would
have a root and prefix set to the top level of the CD. This could result
in subtle configuration bugs, because the same ISO booted from a USB
disk (using isohybrid) would have its root and prefix set to the
embedded ESP because it can find it through the MBR.
With this change, GRUB is able to access the embedded ESP and set its
root and prefix correctly when booting a CD in UEFI mode. It also
allows GRUB to access embedded floppy and HDD images.

Tested with OVMF and a couple of real machines.

Signed-off-by: Ross Lagerwall <address@hidden>
---
 grub-core/Makefile.core.def  |   5 ++
 grub-core/partmap/eltorito.c | 199 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 204 insertions(+)
 create mode 100644 grub-core/partmap/eltorito.c

diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index a6101de..f07572b 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -1785,6 +1785,11 @@ module = {
 };
 
 module = {
+  name = part_eltorito;
+  common = partmap/eltorito.c;
+};
+
+module = {
   name = part_msdos;
   common = partmap/msdos.c;
 };
diff --git a/grub-core/partmap/eltorito.c b/grub-core/partmap/eltorito.c
new file mode 100644
index 0000000..dee6250
--- /dev/null
+++ b/grub-core/partmap/eltorito.c
@@ -0,0 +1,199 @@
+/* eltorito.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/i18n.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+#define ISO9660_LOG2_BLKSZ          2
+#define BOOT_RECORD_LBA             (0x11 << ISO9660_LOG2_BLKSZ)
+#define PRIMARY_VOLUME_LBA          (0x10 << ISO9660_LOG2_BLKSZ)
+#define VOLUME_SPACE_SIZE_OFFSET    0x50
+#define ELTORITO_MAGIC              (grub_cpu_to_le16_compile_time (0xAA55))
+#define ELTORITO_BOOTABLE           0x88
+#define ELTORITO_NOT_BOOTABLE       0x00
+#define ELTORITO_MEDIA_TYPE_MASK    0x03
+#define ELTORITO_TYPE_NO_EMUL       0x00
+#define ELTORITO_TYPE_FLOPPY_1_2    0x01
+#define ELTORITO_TYPE_FLOPPY_1_4    0x02
+#define ELTORITO_TYPE_FLOPPY_2_8    0x03
+#define ELTORITO_TYPE_HDD           0x04
+
+static const char *IDENT = "CD001";
+static const char SYSTEM_ID[32] = "EL TORITO SPECIFICATION";
+
+struct boot_record {
+  grub_uint8_t indicator;
+  char identifier[5];
+  grub_uint8_t version;
+  char system_id[32];
+  grub_uint8_t pad[32];
+  grub_uint32_t catalog_sector;
+} GRUB_PACKED;
+
+union et_entry {
+  /* The validation entry. */
+  struct {
+    grub_uint8_t header;
+    grub_uint8_t platform;
+    grub_uint16_t reserved;
+    char identifier[24];
+    grub_uint16_t checksum;
+    grub_uint16_t magic;
+  } validation GRUB_PACKED;
+
+  /* All other entries. */
+  struct {
+    grub_uint8_t boot_indicator;
+    grub_uint8_t media_type;
+    grub_uint16_t load_segment;
+    grub_uint8_t system_type;
+    grub_uint8_t unused;
+    grub_uint16_t sector_count;
+    grub_uint32_t load_lba;
+    char pad[20];
+  } e GRUB_PACKED;
+
+  /* Used for calculating the checksum. */
+  grub_uint16_t bin[16];
+};
+
+static struct grub_partition_map grub_eltorito_partition_map;
+
+static grub_err_t
+grub_eltorito_partition_map_iterate (grub_disk_t disk,
+                                     grub_partition_iterate_hook_t hook,
+                                     void *hook_data)
+{
+  struct grub_partition part;
+  struct boot_record br;
+  union et_entry entries[64];
+  unsigned int i;
+  int found = 0;
+  grub_uint16_t checksum = 0, sector_count;
+  grub_uint32_t total_sectors;
+
+  /* Find boot catalog. */
+  if (grub_disk_read (disk, BOOT_RECORD_LBA, 0, sizeof br, &br))
+    return grub_errno;
+
+  if (br.indicator != 0 || br.version != 1 ||
+      grub_memcmp (br.identifier, IDENT, sizeof br.identifier) != 0 ||
+      grub_memcmp (br.system_id, SYSTEM_ID, sizeof SYSTEM_ID) != 0)
+    return grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid boot descriptor");
+
+  part.offset = grub_le_to_cpu32 (br.catalog_sector) << ISO9660_LOG2_BLKSZ;
+
+  /* Load boot catalog and verify the validation entry. */
+  if (grub_disk_read (disk, part.offset, 0, sizeof entries, entries))
+    return grub_errno;
+
+  if (entries[0].validation.header != 1 ||
+      entries[0].validation.reserved != 0 ||
+      entries[0].validation.magic != ELTORITO_MAGIC)
+    return grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid validation entry");
+
+  for (i = 0; i < ARRAY_SIZE (entries[0].bin); i++)
+    checksum += grub_le_to_cpu16 (entries[0].bin[i]);
+  if (checksum != 0)
+    return grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid validation entry");
+
+  /* Calculate the number of sectors, in case the ISO9660 filesystem is smaller
+     than the disk. */
+  if (grub_disk_read (disk, PRIMARY_VOLUME_LBA, VOLUME_SPACE_SIZE_OFFSET,
+                      sizeof total_sectors, &total_sectors))
+    return grub_errno;
+  total_sectors = grub_le_to_cpu32 (total_sectors) << ISO9660_LOG2_BLKSZ;
+  total_sectors = grub_min (total_sectors,
+                            disk->total_sectors << (disk->log_sector_size -
+                                                    GRUB_DISK_SECTOR_BITS));
+
+  part.partmap = &grub_eltorito_partition_map;
+  part.parent = disk->partition;
+
+  /* Add a partition for each valid entry. */
+  for (i = 1; i < ARRAY_SIZE (entries); i++)
+    {
+      if ((entries[i].e.boot_indicator == ELTORITO_BOOTABLE ||
+           entries[i].e.boot_indicator == ELTORITO_NOT_BOOTABLE) &&
+          entries[i].e.load_lba != 0)
+        {
+         part.number = found++;
+         part.index = i;
+         part.start = entries[i].e.load_lba << ISO9660_LOG2_BLKSZ;
+          sector_count = grub_le_to_cpu16 (entries[i].e.sector_count);
+
+          /* Calculate the sector size as it's done in the UEFI reference
+             implementation so that partitions are matched correctly. */
+          switch (entries[i].e.media_type & ELTORITO_MEDIA_TYPE_MASK)
+            {
+            case ELTORITO_TYPE_NO_EMUL:
+            default:
+              if (sector_count < 2)
+                part.len = total_sectors - part.start;
+              else
+                part.len = sector_count << ISO9660_LOG2_BLKSZ;
+              break;
+            case ELTORITO_TYPE_HDD:
+              if (sector_count < 2)
+                part.len = total_sectors - part.start;
+              else
+                part.len = sector_count;
+              break;
+            case ELTORITO_TYPE_FLOPPY_1_2:
+              part.len = 2400;
+              break;
+            case ELTORITO_TYPE_FLOPPY_1_4:
+              part.len = 2880;
+              break;
+            case ELTORITO_TYPE_FLOPPY_2_8:
+              part.len = 5760;
+              break;
+            }
+
+         if (hook (disk, &part, hook_data))
+           return grub_errno;
+        }
+    }
+
+  return GRUB_ERR_NONE;
+}
+
+
+/* Partition map type.  */
+static struct grub_partition_map grub_eltorito_partition_map =
+  {
+    .name = "eltorito",
+    .iterate = grub_eltorito_partition_map_iterate,
+  };
+
+GRUB_MOD_INIT(part_eltorito)
+{
+  grub_partition_map_register (&grub_eltorito_partition_map);
+}
+
+GRUB_MOD_FINI(part_eltorito)
+{
+  grub_partition_map_unregister (&grub_eltorito_partition_map);
+}
-- 
2.4.2




reply via email to

[Prev in Thread] Current Thread [Next in Thread]