cgpt: add support for managing the legacy boot gpt bit

Bit 2 in the GPT partition attributes has been allocated as the legacy
bios boot (equivalent to the "active" or "boot" flag in MBR).  If we
try to boot images on newer x86 systems, syslinux dies because it can't
find any GPT partition marked bootable.

Update the various parts of cgpt add & show to manage this bit.  Now we
can run:
	cgpt add -i 12 -B 1 chromiumos_image.bin
And the EFI partition will be marked bootable.

BUG=chromium:644845
TEST=vboot_reference unittests pass
TEST=booted an amd64-generic disk image via USB on a generic laptop
BRANCH=None

Change-Id: I78e17b8df5b0c61e9e2d8a3c703e6d5ad230fe92
Reviewed-on: https://chromium-review.googlesource.com/382411
Commit-Ready: Mike Frysinger <vapier@chromium.org>
Tested-by: Mike Frysinger <vapier@chromium.org>
Reviewed-by: Randall Spangler <rspangler@chromium.org>
diff --git a/cgpt/cgpt.h b/cgpt/cgpt.h
index c4edd95..1216a72 100644
--- a/cgpt/cgpt.h
+++ b/cgpt/cgpt.h
@@ -149,6 +149,9 @@
 uint32_t GetNumberOfEntries(const struct drive *drive);
 GptEntry *GetEntry(GptData *gpt, int secondary, uint32_t entry_index);
 
+void SetLegacyBoot(struct drive *drive, int secondary, uint32_t entry_index,
+                   int legacy_boot);
+int GetLegacyBoot(struct drive *drive, int secondary, uint32_t entry_index);
 void SetPriority(struct drive *drive, int secondary, uint32_t entry_index,
                  int priority);
 int GetPriority(struct drive *drive, int secondary, uint32_t entry_index);
diff --git a/cgpt/cgpt_add.c b/cgpt/cgpt_add.c
index d6b3cab..3808255 100644
--- a/cgpt/cgpt_add.c
+++ b/cgpt/cgpt_add.c
@@ -55,6 +55,10 @@
     snprintf(tmp, sizeof(tmp), "-P %d ", params->priority);
     StrnAppend(buf, tmp, sizeof(buf));
   }
+  if (params->set_legacy_boot) {
+    snprintf(tmp, sizeof(tmp), "-B %d ", params->legacy_boot);
+    StrnAppend(buf, tmp, sizeof(buf));
+  }
   if (params->set_raw) {
     snprintf(tmp, sizeof(tmp), "-A 0x%x ", params->raw_value);
     StrnAppend(buf, tmp, sizeof(buf));
@@ -109,6 +113,8 @@
       SetTries(drive, PRIMARY, index, params->tries);
     if (params->set_priority)
       SetPriority(drive, PRIMARY, index, params->priority);
+    if (params->set_legacy_boot)
+      SetLegacyBoot(drive, PRIMARY, index, params->legacy_boot);
   }
 
   // New partitions must specify type, begin, and size.
diff --git a/cgpt/cgpt_common.c b/cgpt/cgpt_common.c
index 29906d4..1a6bd3c 100644
--- a/cgpt/cgpt_common.c
+++ b/cgpt/cgpt_common.c
@@ -763,6 +763,20 @@
   return (GptEntry*)(&entries[stride * entry_index]);
 }
 
+void SetLegacyBoot(struct drive *drive, int secondary, uint32_t entry_index,
+                   int legacy_boot) {
+  require(legacy_boot >= 0 && legacy_boot <= CGPT_ATTRIBUTE_MAX_LEGACY_BOOT);
+  GptEntry *entry;
+  entry = GetEntry(&drive->gpt, secondary, entry_index);
+  SetEntryLegacyBoot(entry, legacy_boot);
+}
+
+int GetLegacyBoot(struct drive *drive, int secondary, uint32_t entry_index) {
+  GptEntry *entry;
+  entry = GetEntry(&drive->gpt, secondary, entry_index);
+  return GetEntryLegacyBoot(entry);
+}
+
 void SetPriority(struct drive *drive, int secondary, uint32_t entry_index,
                  int priority) {
   require(priority >= 0 && priority <= CGPT_ATTRIBUTE_MAX_PRIORITY);
diff --git a/cgpt/cgpt_show.c b/cgpt/cgpt_show.c
index 638655b..81b499c 100644
--- a/cgpt/cgpt_show.c
+++ b/cgpt/cgpt_show.c
@@ -100,6 +100,7 @@
   char contents[256];                   // scratch buffer for formatting output
   uint8_t label[GPT_PARTNAME_LEN];
   char type[GUID_STRLEN], unique[GUID_STRLEN];
+  int clen;
 
   UTF16ToUTF8(entry->name, sizeof(entry->name) / sizeof(entry->name[0]),
               label, sizeof(label));
@@ -118,6 +119,7 @@
   GuidToStr(&entry->unique, unique, GUID_STRLEN);
   printf(PARTITION_MORE, "UUID: ", unique);
 
+  clen = 0;
   if (!raw) {
     if (GuidEqual(&guid_chromeos_kernel, &entry->type)) {
       int tries = (entry->attrs.fields.gpt_att &
@@ -129,16 +131,35 @@
       int priority = (entry->attrs.fields.gpt_att &
                       CGPT_ATTRIBUTE_PRIORITY_MASK) >>
           CGPT_ATTRIBUTE_PRIORITY_OFFSET;
-      require(snprintf(contents, sizeof(contents),
-                       "priority=%d tries=%d successful=%d",
-                       priority, tries, successful) < sizeof(contents));
-      printf(PARTITION_MORE, "Attr: ", contents);
+      clen = snprintf(contents, sizeof(contents),
+                      "priority=%d tries=%d successful=%d ",
+                      priority, tries, successful);
+    }
+
+    if (entry->attrs.fields.system) {
+      clen += snprintf(contents + clen, sizeof(contents) - clen,
+                       "system=%d ", entry->attrs.fields.system);
+      require(clen < sizeof(contents));
+    }
+
+    if (entry->attrs.fields.efi_ignore) {
+      clen += snprintf(contents + clen, sizeof(contents) - clen,
+                       "efi_ignore=%d ", entry->attrs.fields.efi_ignore);
+      require(clen < sizeof(contents));
+    }
+
+    if (entry->attrs.fields.legacy_boot) {
+      clen += snprintf(contents + clen, sizeof(contents) - clen,
+                       "legacy_boot=%d ", entry->attrs.fields.legacy_boot);
+      require(clen < sizeof(contents));
     }
   } else {
-    require(snprintf(contents, sizeof(contents),
-                     "[%x]", entry->attrs.fields.gpt_att) < sizeof(contents));
-    printf(PARTITION_MORE, "Attr: ", contents);
+    clen = snprintf(contents, sizeof(contents),
+                    "[%x]", entry->attrs.fields.gpt_att);
   }
+  require(clen < sizeof(contents));
+  if (clen)
+    printf(PARTITION_MORE, "Attr: ", contents);
 }
 
 void EntriesDetails(struct drive *drive, const int secondary, int raw) {
@@ -209,6 +230,9 @@
       case 'P':
         printf("%d\n", GetPriority(drive, ANY_VALID, index));
         break;
+      case 'B':
+        printf("%d\n", GetLegacyBoot(drive, ANY_VALID, index));
+        break;
       case 'A':
         printf("0x%x\n", entry->attrs.fields.gpt_att);
         break;
diff --git a/cgpt/cmd_add.c b/cgpt/cmd_add.c
index ca379a2..3c7741b 100644
--- a/cgpt/cmd_add.c
+++ b/cgpt/cmd_add.c
@@ -27,6 +27,7 @@
          "  -S NUM       set Successful flag (0|1)\n"
          "  -T NUM       set Tries flag (0-15)\n"
          "  -P NUM       set Priority flag (0-15)\n"
+         "  -B NUM       set Legacy Boot flag (0|1)\n"
          "  -A NUM       set raw 16-bit attribute value (bits 48-63)\n"
          "\n"
          "Use the -i option to modify an existing partition.\n"
@@ -45,7 +46,7 @@
   char *e = 0;
 
   opterr = 0;                     // quiet, you
-  while ((c=getopt(argc, argv, ":hi:b:s:t:u:l:S:T:P:A:D:")) != -1)
+  while ((c=getopt(argc, argv, ":hi:b:s:t:u:l:S:T:P:B:A:D:")) != -1)
   {
     switch (c)
     {
@@ -103,6 +104,12 @@
       errorcnt += check_int_parse(c, e);
       errorcnt += check_int_limit(c, params.priority, 0, 15);
       break;
+    case 'B':
+      params.set_legacy_boot = 1;
+      params.legacy_boot = (uint32_t)strtoul(optarg, &e, 0);
+      errorcnt += check_int_parse(c, e);
+      errorcnt += check_int_limit(c, params.legacy_boot, 0, 1);
+      break;
     case 'A':
       params.set_raw = 1;
       params.raw_value = strtoull(optarg, &e, 0);
diff --git a/cgpt/cmd_show.c b/cgpt/cmd_show.c
index 860d80e..b9e6481 100644
--- a/cgpt/cmd_show.c
+++ b/cgpt/cmd_show.c
@@ -32,6 +32,7 @@
          "               -S  Successful flag\n"
          "               -T  Tries flag\n"
          "               -P  Priority flag\n"
+         "               -B  Legacy Boot flag\n"
          "               -A  raw 16-bit attribute value (bits 48-63)\n"
          "  -d           Debug output (including invalid headers)\n"
          "\n", progname);
@@ -46,7 +47,7 @@
   char *e = 0;
 
   opterr = 0;                     // quiet, you
-  while ((c=getopt(argc, argv, ":hnvqi:bstulSTPAdD:")) != -1)
+  while ((c=getopt(argc, argv, ":hnvqi:bstulSTPBAdD:")) != -1)
   {
     switch (c)
     {
@@ -75,6 +76,7 @@
     case 'S':
     case 'T':
     case 'P':
+    case 'B':
     case 'A':
       params.single_item = c;
       break;
diff --git a/firmware/include/gpt.h b/firmware/include/gpt.h
index 275b3ae..ae957cf 100644
--- a/firmware/include/gpt.h
+++ b/firmware/include/gpt.h
@@ -108,7 +108,12 @@
 	uint64_t ending_lba;
 	union {
 		struct {
-			uint16_t reserved[3];
+			uint8_t system:1;
+			uint8_t efi_ignore:1;
+			uint8_t legacy_boot:1;
+			uint8_t reserved1:5;
+			uint8_t reserved2;
+			uint16_t reserved[2];
 			uint16_t gpt_att;
 		} __attribute__((packed)) fields;
 		uint64_t whole;
diff --git a/firmware/include/gpt_misc.h b/firmware/include/gpt_misc.h
index 0ecbba4..58ce83f 100644
--- a/firmware/include/gpt_misc.h
+++ b/firmware/include/gpt_misc.h
@@ -198,9 +198,11 @@
 
 /* Getters and setters for partition attribute fields. */
 
+int GetEntryLegacyBoot(const GptEntry *e);
 int GetEntrySuccessful(const GptEntry *e);
 int GetEntryPriority(const GptEntry *e);
 int GetEntryTries(const GptEntry *e);
+void SetEntryLegacyBoot(GptEntry *e, int legacy_boot);
 void SetEntrySuccessful(GptEntry *e, int successful);
 void SetEntryPriority(GptEntry *e, int priority);
 void SetEntryTries(GptEntry *e, int tries);
diff --git a/firmware/lib/cgptlib/cgptlib_internal.c b/firmware/lib/cgptlib/cgptlib_internal.c
index 7d402df..24b7ba1 100644
--- a/firmware/lib/cgptlib/cgptlib_internal.c
+++ b/firmware/lib/cgptlib/cgptlib_internal.c
@@ -378,6 +378,11 @@
 	gpt->valid_entries = MASK_BOTH;
 }
 
+int GetEntryLegacyBoot(const GptEntry *e)
+{
+	return e->attrs.fields.legacy_boot;
+}
+
 int GetEntrySuccessful(const GptEntry *e)
 {
 	return (e->attrs.fields.gpt_att & CGPT_ATTRIBUTE_SUCCESSFUL_MASK) >>
@@ -402,6 +407,11 @@
 		CGPT_ATTRIBUTE_TRIES_OFFSET;
 }
 
+void SetEntryLegacyBoot(GptEntry *e, int legacy_boot)
+{
+	e->attrs.fields.legacy_boot = legacy_boot;
+}
+
 void SetEntrySuccessful(GptEntry *e, int successful)
 {
 	if (successful)
diff --git a/firmware/lib/cgptlib/include/cgptlib_internal.h b/firmware/lib/cgptlib/include/cgptlib_internal.h
index b13c491..cd616e5 100644
--- a/firmware/lib/cgptlib/include/cgptlib_internal.h
+++ b/firmware/lib/cgptlib/include/cgptlib_internal.h
@@ -26,7 +26,8 @@
  *     56  -- success
  *  55-52  -- tries
  *  51-48  -- priority
- *   47-2  -- UEFI: reserved for future use
+ *   47-3  -- UEFI: reserved for future use
+ *      2  -- UEFI: Legacy BIOS bootable
  *      1  -- UEFI: partition is not mapped
  *      0  -- UEFI: partition is required
  */
@@ -45,6 +46,9 @@
 #define CGPT_ATTRIBUTE_PRIORITY_MASK (CGPT_ATTRIBUTE_MAX_PRIORITY << \
                                       CGPT_ATTRIBUTE_PRIORITY_OFFSET)
 
+#define CGPT_ATTRIBUTE_LEGACY_BOOT_OFFSET (2)
+#define CGPT_ATTRIBUTE_MAX_LEGACY_BOOT (1ULL)
+
 /* Defines ChromeOS-specific limitation on GPT */
 #define MIN_SIZE_OF_HEADER 92
 #define MAX_SIZE_OF_HEADER 512
diff --git a/host/include/cgpt_params.h b/host/include/cgpt_params.h
index 3f0a71b..92f0d3a 100644
--- a/host/include/cgpt_params.h
+++ b/host/include/cgpt_params.h
@@ -32,6 +32,7 @@
   int successful;
   int tries;
   int priority;
+  int legacy_boot;
   uint32_t raw_value;
   int set_begin;
   int set_size;
@@ -40,6 +41,7 @@
   int set_successful;
   int set_tries;
   int set_priority;
+  int set_legacy_boot;
   int set_raw;
 } CgptAddParams;
 
diff --git a/tests/cgptlib_test.c b/tests/cgptlib_test.c
index 1e8df25..f736fd9 100644
--- a/tests/cgptlib_test.c
+++ b/tests/cgptlib_test.c
@@ -1131,6 +1131,15 @@
 	GptEntry *e = (GptEntry *)(gpt->primary_entries);
 
 	e->attrs.whole = 0x0000000000000000ULL;
+	SetEntryLegacyBoot(e, 1);
+	EXPECT(0x0000000000000004ULL == e->attrs.whole);
+	EXPECT(1 == GetEntryLegacyBoot(e));
+	e->attrs.whole = 0xFFFFFFFFFFFFFFFFULL;
+	SetEntryLegacyBoot(e, 0);
+	EXPECT(0xFFFFFFFFFFFFFFFBULL == e->attrs.whole);
+	EXPECT(0 == GetEntryLegacyBoot(e));
+
+	e->attrs.whole = 0x0000000000000000ULL;
 	SetEntrySuccessful(e, 1);
 	EXPECT(0x0100000000000000ULL == e->attrs.whole);
 	EXPECT(1 == GetEntrySuccessful(e));
@@ -1169,24 +1178,28 @@
 	override_priority = 0;
 
 	e->attrs.whole = 0xFFFFFFFFFFFFFFFFULL;
+	EXPECT(1 == GetEntryLegacyBoot(e));
 	EXPECT(1 == GetEntrySuccessful(e));
 	EXPECT(15 == GetEntryPriority(e));
 	EXPECT(15 == GetEntryTries(e));
 
 	override_priority = 10;
 	e->attrs.whole = 0xFFFFFFFFFFFFFFFFULL;
+	EXPECT(1 == GetEntryLegacyBoot(e));
 	EXPECT(1 == GetEntrySuccessful(e));
 	EXPECT(10 == GetEntryPriority(e));
 	EXPECT(15 == GetEntryTries(e));
 	override_priority = 0;
 
-	e->attrs.whole = 0x0123000000000000ULL;
+	e->attrs.whole = 0x0123000000000004ULL;
+	EXPECT(1 == GetEntryLegacyBoot(e));
 	EXPECT(1 == GetEntrySuccessful(e));
 	EXPECT(2 == GetEntryTries(e));
 	EXPECT(3 == GetEntryPriority(e));
 
 	override_priority = 10;
-	e->attrs.whole = 0x0123000000000000ULL;
+	e->attrs.whole = 0x0123000000000004ULL;
+	EXPECT(1 == GetEntryLegacyBoot(e));
 	EXPECT(1 == GetEntrySuccessful(e));
 	EXPECT(2 == GetEntryTries(e));
 	EXPECT(10 == GetEntryPriority(e));
@@ -1194,7 +1207,8 @@
 
 	/* Invalid priority */
 	override_priority = 100;
-	e->attrs.whole = 0x0123000000000000ULL;
+	e->attrs.whole = 0x0123000000000004ULL;
+	EXPECT(1 == GetEntryLegacyBoot(e));
 	EXPECT(1 == GetEntrySuccessful(e));
 	EXPECT(2 == GetEntryTries(e));
 	EXPECT(3 == GetEntryPriority(e));