chain: Support GPT disks
authorShao Miller <shao.miller@yrdsb.edu.on.ca>
Thu, 24 Jun 2010 18:01:34 +0000 (14:01 -0400)
committerShao Miller <shao.miller@yrdsb.edu.on.ca>
Fri, 25 Jun 2010 02:16:49 +0000 (19:16 -0700)
A GPT disk partition iterator is introduced, so
we can chain-load partitions on such a disk.

Tested-by: Shao Miller <shao.miller@yrdsb.edu.on.ca>
Signed-off-by: Shao Miller <shao.miller@yrdsb.edu.on.ca>
com32/modules/chain.c

index 31129bf..8fe4522 100644 (file)
@@ -3,7 +3,7 @@
  *   Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
  *   Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
  *   Significant portions copyright (C) 2010 Shao Miller
- *                                     [partition iteration]
+ *                                     [partition iteration, GPT]
  *
  *   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
@@ -459,6 +459,15 @@ struct disk_part_iter {
            /* The parent extended partition index */
            int parent_index;
        } ebr;
+       /* GPT specifics */
+       struct {
+           /* Real (not effective) index in the partition table */
+           int index;
+           /* Count of entries in GPT */
+           int parts;
+           /* Partition record size */
+           uint32_t size;
+       } gpt;
     } private;
 };
 
@@ -592,8 +601,195 @@ err_alloc:
     return NULL;
 }
 
+/*
+ * GUID
+ * Be careful with endianness, you must adjust it yourself
+ * iff you are directly using the fourth data chunk
+ */
+struct guid {
+    uint32_t data1;
+    uint16_t data2;
+    uint16_t data3;
+    uint64_t data4;
+} __attribute__ ((packed));
+
+#if DEBUG
+/*
+ * Fill a buffer with a textual GUID representation.
+ * The buffer must be >= char[37] and will be populated
+ * with an ASCII NUL C string terminator.
+ * Example: 11111111-2222-3333-4444-444444444444
+ * Endian:  LLLLLLLL-LLLL-LLLL-BBBB-BBBBBBBBBBBB
+ */
+static void guid_to_str(char *buf, const struct guid *id)
+{
+    /*
+     * This walk-map effectively reverses the little-endian
+     * portions of the GUID in the output text
+     */
+    static const char le_walk_map[] = {
+       3, -1, -1, -1, 0,
+       5, -1, 0,
+       3, -1, 0,
+       2, 1, 0,
+       1, 1, 1, 1, 1, 1
+    };
+    unsigned int i = 0;
+    const char *walker = (const char *)id;
+
+    while (i < sizeof(le_walk_map)) {
+       walker += le_walk_map[i];
+       if (!le_walk_map[i])
+           *buf = '-';
+       else {
+           *buf = ((*walker & 0xF0) >> 4) + '0';
+           if (*buf > '9')
+               *buf += 'A' - '9' - 1;
+           buf++;
+           *buf = (*walker & 0x0F) + '0';
+           if (*buf > '9')
+               *buf += 'A' - '9' - 1;
+       }
+       buf++;
+       i++;
+    }
+    *buf = 0;
+}
+#endif
+
+/* A GPT partition */
+struct gpt_part {
+    struct guid type;
+    struct guid uid;
+    uint64_t lba_first;
+    uint64_t lba_last;
+    uint64_t attribs;
+    char name[72];
+} __attribute__ ((packed));
+
+#if DEBUG
+static void gpt_part_dump(const struct gpt_part *gpt_part)
+{
+    unsigned int i;
+    char guid_text[37];
+
+    printf("----------------------------------\n"
+          "GPT part. LBA first __ : 0x%.16llx\n"
+          "GPT part. LBA last ___ : 0x%.16llx\n"
+          "GPT part. attribs ____ : 0x%.16llx\n"
+          "GPT part. name _______ : '",
+          gpt_part->lba_first, gpt_part->lba_last, gpt_part->attribs);
+    for (i = 0; i < sizeof(gpt_part->name); i++) {
+       if (gpt_part->name[i])
+           printf("%c", gpt_part->name[i]);
+    }
+    puts("'");
+    guid_to_str(guid_text, &gpt_part->type);
+    printf("GPT part. type GUID __ : {%s}\n", guid_text);
+    guid_to_str(guid_text, &gpt_part->uid);
+    printf("GPT part. unique ID __ : {%s}\n", guid_text);
+}
+#endif
+
+/* A GPT header */
+struct gpt {
+    char sig[8];
+    union {
+       struct {
+           uint16_t minor;
+           uint16_t major;
+       } fields __attribute__ ((packed));
+       uint32_t uint32;
+       char raw[4];
+    } rev __attribute__ ((packed));
+    uint32_t hdr_size;
+    uint32_t chksum;
+    char reserved1[4];
+    uint64_t lba_cur;
+    uint64_t lba_alt;
+    uint64_t lba_first_usable;
+    uint64_t lba_last_usable;
+    struct guid disk_guid;
+    uint64_t lba_table;
+    uint32_t part_count;
+    uint32_t part_size;
+    uint32_t table_chksum;
+    char reserved2[1];
+} __attribute__ ((packed));
+static const char gpt_sig_magic[] = "EFI PART";
+
+#if DEBUG
+static void gpt_dump(const struct gpt *gpt)
+{
+    char guid_text[37];
+
+    printf("GPT sig ______________ : '%8.8s'\n"
+          "GPT major revision ___ : 0x%.4x\n"
+          "GPT minor revision ___ : 0x%.4x\n"
+          "GPT header size ______ : 0x%.8x\n"
+          "GPT header checksum __ : 0x%.8x\n"
+          "GPT reserved _________ : '%4.4s'\n"
+          "GPT LBA current ______ : 0x%.16llx\n"
+          "GPT LBA alternative __ : 0x%.16llx\n"
+          "GPT LBA first usable _ : 0x%.16llx\n"
+          "GPT LBA last usable __ : 0x%.16llx\n"
+          "GPT LBA part. table __ : 0x%.16llx\n"
+          "GPT partition count __ : 0x%.8x\n"
+          "GPT partition size ___ : 0x%.8x\n"
+          "GPT part. table chksum : 0x%.8x\n",
+          gpt->sig,
+          gpt->rev.fields.major,
+          gpt->rev.fields.minor,
+          gpt->hdr_size,
+          gpt->chksum,
+          gpt->reserved1,
+          gpt->lba_cur,
+          gpt->lba_alt,
+          gpt->lba_first_usable,
+          gpt->lba_last_usable,
+          gpt->lba_table, gpt->part_count, gpt->part_size, gpt->table_chksum);
+    guid_to_str(guid_text, &gpt->disk_guid);
+    printf("GPT disk GUID ________ : {%s}\n", guid_text);
+}
+#endif
+
+static disk_part_iter_func_decl(next_gpt_part)
+{
+    const struct gpt_part *gpt_part = NULL;
+
+    while (++part->private.gpt.index < part->private.gpt.parts) {
+       gpt_part =
+           (const struct gpt_part *)(part->block +
+                                     (part->private.gpt.index *
+                                      part->private.gpt.size));
+       if (!gpt_part->lba_first)
+           continue;
+       break;
+    }
+    /* Were we the last partition? */
+    if (part->private.gpt.index == part->private.gpt.parts) {
+       goto err_last;
+    }
+    part->lba_data = gpt_part->lba_first;
+    /* Update our index */
+    part->index++;
+#if DEBUG
+    gpt_part_dump(gpt_part);
+#endif
+    /* In a GPT scheme, we re-use the iterator */
+    return part;
+
+err_last:
+    free(part->block);
+    free(part);
+
+    return NULL;
+}
+
 static disk_part_iter_func_decl(get_first_partition)
 {
+    const struct gpt *gpt_candidate;
+
     /*
      * Ignore any passed partition iterator.  The caller should
      * have passed NULL.  Allocate a new partition iterator
@@ -619,9 +815,42 @@ static disk_part_iter_func_decl(get_first_partition)
     part->record = NULL;
     part->private.mbr_index = -1;
     part->next = next_mbr_part;
+    /* Check for a GPT disk */
+    gpt_candidate = (const struct gpt *)(part->block + SECTOR);
+    if (!memcmp(gpt_candidate->sig, gpt_sig_magic, sizeof(gpt_sig_magic))) {
+       /* LBA for partition table */
+       uint64_t lba_table;
+
+       /* It looks like one */
+       /* TODO: Check checksum.  Possibly try alternative GPT */
+#if DEBUG
+       puts("Looks like a GPT disk.");
+       gpt_dump(gpt_candidate);
+#endif
+       /* TODO: Check table checksum (maybe) */
+       /* Note relevant GPT details */
+       part->next = next_gpt_part;
+       part->private.gpt.index = -1;
+       part->private.gpt.parts = gpt_candidate->part_count;
+       part->private.gpt.size = gpt_candidate->part_size;
+       lba_table = gpt_candidate->lba_table;
+       gpt_candidate = NULL;
+       /* Load the partition table */
+       free(part->block);
+       part->block =
+           read_sectors(lba_table,
+                        ((part->private.gpt.size * part->private.gpt.parts) +
+                         SECTOR - 1) / SECTOR);
+       if (!part->block) {
+           error("Could not read GPT partition list!\n");
+           goto err_gpt_table;
+       }
+    }
     /* Return the pseudo-partition's next partition, which is real */
     return part->next(part);
 
+err_gpt_table:
+
 err_mbr:
 
     free(part->block);