grub-devel.gnu.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] core/partmap: Add El Torito boot catalog parsing
@ 2015-06-07 10:24 Ross Lagerwall
  2015-06-08 16:51 ` Andrei Borzenkov
  0 siblings, 1 reply; 12+ messages in thread
From: Ross Lagerwall @ 2015-06-07 10:24 UTC (permalink / raw)
  To: grub-devel; +Cc: Ross Lagerwall

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 <rosslagerwall@gmail.com>
---
 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;
+}
+
+\f
+/* 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



^ permalink raw reply related	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2016-02-12 18:31 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-06-07 10:24 [PATCH] core/partmap: Add El Torito boot catalog parsing Ross Lagerwall
2015-06-08 16:51 ` Andrei Borzenkov
2015-06-08 17:31   ` Ross Lagerwall
2015-06-08 17:50     ` Andrei Borzenkov
2015-06-08 18:25       ` Ross Lagerwall
2015-06-08 19:01         ` Andrei Borzenkov
2015-06-20 10:16           ` Ross Lagerwall
2015-06-20 10:19             ` Vladimir 'phcoder' Serbinenko
2015-06-20 13:52               ` Thomas Schmitt
2015-06-21 11:28             ` Andrei Borzenkov
2016-02-12 18:31             ` Vladimir 'φ-coder/phcoder' Serbinenko
2015-06-08 19:31         ` Thomas Schmitt

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).