Support alternative GPT header signature

In order to dual boot Windows and ChromeOS, Windows must
not find a GPT partition table on the disk. So change
ChromeOS to cope with an alternative signature "CHROMEOS"
instead of the standard "EFI PART"

BUG=chrome-os-partner:6108
TEST=rebuild chromeos, install it,
     run cgpt legacy /dev/sda
     dd if=/dev/sda of=/tmp/x bs=1k
     hexdump -C /tmp/X
     see the string CHROMEOS
BRANCH=link
Signed-off-by: Stefan Reinauer <reinauer@chromium.org>

Change-Id: Ia88eff33b9880bd73a78c1b8e026c1f8298c4557
Reviewed-on: https://gerrit.chromium.org/gerrit/31264
Reviewed-by: Randall Spangler <rspangler@chromium.org>
Commit-Ready: Stefan Reinauer <reinauer@chromium.org>
Tested-by: Stefan Reinauer <reinauer@chromium.org>
diff --git a/cgpt/Makefile b/cgpt/Makefile
index 8e810d4..c22a680 100644
--- a/cgpt/Makefile
+++ b/cgpt/Makefile
@@ -24,6 +24,7 @@
 	cgpt_repair.c \
 	cgpt_prioritize.c \
 	cgpt_find.c \
+	cgpt_legacy.c \
 	cmd_show.c \
 	cmd_repair.c \
 	cmd_create.c \
@@ -31,6 +32,7 @@
 	cmd_boot.c \
 	cmd_find.c \
 	cmd_prioritize.c \
+	cmd_legacy.c \
 	cgpt_common.c
 
 LIB_CGPT_CC_SRCS = \
diff --git a/cgpt/cgpt.c b/cgpt/cgpt.c
index e963142..ac7aecd 100644
--- a/cgpt/cgpt.c
+++ b/cgpt/cgpt.c
@@ -30,6 +30,7 @@
   {"find", cmd_find, "Locate a partition by its GUID"},
   {"prioritize", cmd_prioritize,
    "Reorder the priority of all kernel partitions"},
+  {"legacy", cmd_legacy, "Switch between GPT and Legacy GPT"},
 };
 
 void Usage(void) {
diff --git a/cgpt/cgpt.h b/cgpt/cgpt.h
index 6851ded..b3f7ff3 100644
--- a/cgpt/cgpt.h
+++ b/cgpt/cgpt.h
@@ -154,6 +154,7 @@
 int cmd_boot(int argc, char *argv[]);
 int cmd_find(int argc, char *argv[]);
 int cmd_prioritize(int argc, char *argv[]);
+int cmd_legacy(int argc, char *argv[]);
 
 #define ARRAY_COUNT(array) (sizeof(array)/sizeof((array)[0]))
 const char *GptError(int errnum);
diff --git a/cgpt/cgpt_common.c b/cgpt/cgpt_common.c
index d69eb67..193f2e1 100644
--- a/cgpt/cgpt_common.c
+++ b/cgpt/cgpt_common.c
@@ -733,7 +733,9 @@
   primary_header = (GptHeader*)gpt->primary_header;
   secondary_header = (GptHeader*)gpt->secondary_header;
 
-  if (gpt->modified & GPT_MODIFIED_ENTRIES1) {
+  if (gpt->modified & GPT_MODIFIED_ENTRIES1 &&
+      memcmp(primary_header, GPT_HEADER_SIGNATURE2,
+             GPT_HEADER_SIGNATURE_SIZE)) {
     primary_header->entries_crc32 =
         Crc32(gpt->primary_entries, TOTAL_ENTRIES_SIZE);
   }
@@ -785,6 +787,14 @@
  * Note that CRC is NOT re-computed in this function.
  */
 uint8_t RepairEntries(GptData *gpt, const uint32_t valid_entries) {
+  /* If we have an alternate GPT header signature, don't overwrite
+   * the secondary GPT with the primary one as that might wipe the
+   * partition table. Also don't overwrite the primary one with the
+   * secondary one as that will stop Windows from booting. */
+  GptHeader* h = (GptHeader*)(gpt->primary_header);
+  if (!memcmp(h->signature, GPT_HEADER_SIGNATURE2, GPT_HEADER_SIGNATURE_SIZE))
+    return 0;
+
   if (valid_entries == MASK_BOTH) {
     if (memcmp(gpt->primary_entries, gpt->secondary_entries,
                TOTAL_ENTRIES_SIZE)) {
diff --git a/cgpt/cgpt_legacy.c b/cgpt/cgpt_legacy.c
new file mode 100644
index 0000000..7d1eeea
--- /dev/null
+++ b/cgpt/cgpt_legacy.c
@@ -0,0 +1,42 @@
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cgpt.h"
+
+#include <string.h>
+
+#include "cgptlib_internal.h"
+#include "cgpt_params.h"
+
+int cgpt_legacy(CgptLegacyParams *params) {
+  struct drive drive;
+  GptHeader *h1, *h2;
+
+  if (params == NULL)
+    return CGPT_FAILED;
+
+  if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDWR))
+    return CGPT_FAILED;
+
+  h1 = (GptHeader *)drive.gpt.primary_header;
+  h2 = (GptHeader *)drive.gpt.secondary_header;
+  if (params->efipart) {
+    memcpy(h1->signature, GPT_HEADER_SIGNATURE, GPT_HEADER_SIGNATURE_SIZE);
+    memcpy(h2->signature, GPT_HEADER_SIGNATURE, GPT_HEADER_SIGNATURE_SIZE);
+    RepairEntries(&drive.gpt, MASK_SECONDARY);
+    drive.gpt.modified |= (GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1 |
+                           GPT_MODIFIED_HEADER2);
+  } else {
+    memcpy(h1->signature, GPT_HEADER_SIGNATURE2, GPT_HEADER_SIGNATURE_SIZE);
+    memcpy(h2->signature, GPT_HEADER_SIGNATURE2, GPT_HEADER_SIGNATURE_SIZE);
+    memset(drive.gpt.primary_entries, 0, drive.gpt.sector_bytes);
+    drive.gpt.modified |= (GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1 |
+                           GPT_MODIFIED_HEADER2);
+  }
+
+  UpdateCrc(&drive.gpt);
+
+  // Write it all out
+  return DriveClose(&drive, 1);
+}
diff --git a/cgpt/cgpt_params.h b/cgpt/cgpt_params.h
index 22af962..fcee03c 100644
--- a/cgpt/cgpt_params.h
+++ b/cgpt/cgpt_params.h
@@ -92,6 +92,11 @@
   int match_partnum;           // 0 for no match, 1-N for match
 } CgptFindParams;
 
+typedef struct CgptLegacyParams {
+  char *drive_name;
+  int efipart;
+} CgptLegacyParams;
+
 // create related methods.
 int cgpt_create(CgptCreateParams *params);
 
@@ -116,4 +121,8 @@
 
 // find related methods.
 void cgpt_find(CgptFindParams *params);
+
+// legacy related methods.
+int cgpt_legacy(CgptLegacyParams *params);
+
 #endif  // VBOOT_REFERENCE_CGPT_CGPT_PARAMS_H_
diff --git a/cgpt/cmd_legacy.c b/cgpt/cmd_legacy.c
new file mode 100644
index 0000000..5dad01e
--- /dev/null
+++ b/cgpt/cmd_legacy.c
@@ -0,0 +1,67 @@
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cgpt.h"
+
+#include <getopt.h>
+#include <string.h>
+
+#include "cgpt_params.h"
+
+static void Usage(void)
+{
+  printf("\nUsage: %s legacy [OPTIONS] DRIVE\n\n"
+         "Switch GPT header signature to \"CHROMEOS\".\n\n"
+         "Options:\n"
+         "  -e           Switch GPT header signature back to \"EFI PART\"\n"
+         "\n", progname);
+}
+
+int cmd_legacy(int argc, char *argv[]) {
+  CgptLegacyParams params;
+  memset(&params, 0, sizeof(params));
+
+  int c;
+  int errorcnt = 0;
+
+  opterr = 0;                     // quiet, you
+  while ((c=getopt(argc, argv, ":he")) != -1)
+  {
+    switch (c)
+    {
+    case 'e':
+      params.efipart = 1;
+      break;
+
+    case 'h':
+      Usage();
+      return CGPT_OK;
+    case '?':
+      Error("unrecognized option: -%c\n", optopt);
+      errorcnt++;
+      break;
+    case ':':
+      Error("missing argument to -%c\n", optopt);
+      errorcnt++;
+      break;
+    default:
+      errorcnt++;
+      break;
+    }
+  }
+  if (errorcnt)
+  {
+    Usage();
+    return CGPT_FAILED;
+  }
+
+  if (optind >= argc) {
+    Usage();
+    return CGPT_FAILED;
+  }
+
+  params.drive_name = argv[optind];
+
+  return cgpt_legacy(&params);
+}
diff --git a/firmware/lib/cgptlib/cgptlib_internal.c b/firmware/lib/cgptlib/cgptlib_internal.c
index ee3c87f..a942b1b 100644
--- a/firmware/lib/cgptlib/cgptlib_internal.c
+++ b/firmware/lib/cgptlib/cgptlib_internal.c
@@ -45,7 +45,8 @@
 
   /* Make sure we're looking at a header of reasonable size before
    * attempting to calculate CRC. */
-  if (Memcmp(h->signature, GPT_HEADER_SIGNATURE, GPT_HEADER_SIGNATURE_SIZE))
+  if (Memcmp(h->signature, GPT_HEADER_SIGNATURE, GPT_HEADER_SIGNATURE_SIZE) &&
+      Memcmp(h->signature, GPT_HEADER_SIGNATURE2, GPT_HEADER_SIGNATURE_SIZE))
     return 1;
   if (h->revision != GPT_HEADER_REVISION)
     return 1;
diff --git a/firmware/lib/cgptlib/include/gpt.h b/firmware/lib/cgptlib/include/gpt.h
index 69d89d9..95b7714 100644
--- a/firmware/lib/cgptlib/include/gpt.h
+++ b/firmware/lib/cgptlib/include/gpt.h
@@ -14,7 +14,8 @@
 
 __pragma(pack(push,1)) /* Support packing for MSVC. */
 
-#define GPT_HEADER_SIGNATURE "EFI PART"
+#define GPT_HEADER_SIGNATURE  "EFI PART"
+#define GPT_HEADER_SIGNATURE2 "CHROMEOS"
 #define GPT_HEADER_SIGNATURE_SIZE sizeof(GPT_HEADER_SIGNATURE)
 #define GPT_HEADER_REVISION 0x00010000
 
diff --git a/firmware/lib/vboot_kernel.c b/firmware/lib/vboot_kernel.c
index d1f261d..d189e43 100644
--- a/firmware/lib/vboot_kernel.c
+++ b/firmware/lib/vboot_kernel.c
@@ -72,23 +72,35 @@
  * Returns 0 if successful, 1 if error. */
 int WriteAndFreeGptData(VbExDiskHandle_t disk_handle, GptData* gptdata) {
 
+  int legacy = 0;
   uint64_t entries_sectors = TOTAL_ENTRIES_SIZE / gptdata->sector_bytes;
 
   if (gptdata->primary_header) {
+    GptHeader* h = (GptHeader*)(gptdata->primary_header);
+    legacy = !Memcmp(h->signature, GPT_HEADER_SIGNATURE2,
+             GPT_HEADER_SIGNATURE_SIZE);
     if (gptdata->modified & GPT_MODIFIED_HEADER1) {
-      VBDEBUG(("Updating GPT header 1\n"));
-      if (0 != VbExDiskWrite(disk_handle, 1, 1, gptdata->primary_header))
-        return 1;
+      if (legacy) {
+        VBDEBUG(("Not updating GPT header 1: legacy mode is enabled.\n"));
+      } else {
+        VBDEBUG(("Updating GPT header 1\n"));
+        if (0 != VbExDiskWrite(disk_handle, 1, 1, gptdata->primary_header))
+          return 1;
+      }
     }
     VbExFree(gptdata->primary_header);
   }
 
   if (gptdata->primary_entries) {
     if (gptdata->modified & GPT_MODIFIED_ENTRIES1) {
-      VBDEBUG(("Updating GPT entries 1\n"));
-      if (0 != VbExDiskWrite(disk_handle, 2, entries_sectors,
-                             gptdata->primary_entries))
-        return 1;
+      if (legacy) {
+        VBDEBUG(("Not updating GPT entries 1: legacy mode is enabled.\n"));
+      } else {
+        VBDEBUG(("Updating GPT entries 1\n"));
+        if (0 != VbExDiskWrite(disk_handle, 2, entries_sectors,
+                               gptdata->primary_entries))
+          return 1;
+      }
     }
     VbExFree(gptdata->primary_entries);
   }