Add CTRL-L in dev screen to support a "legacy boot option"

This option is disabled per default and can be enabled with
crossystem dev_boot_legacy=1
or by setting the GBB flag
GBB_FLAG_FORCE_DEV_BOOT_LEGACY      0x00000080

BUG=chrome-os-partner:6108
TEST=crossystem dev_boot_legacy=1
     boot to dev mode screen, press CTRL-L, see SeaBIOS start
     (other CLs needed)
BRANCH=link
Signed-off-by: Stefan Reinauer <reinauer@chromium.org>

Change-Id: I593d2be7cff5ca07b8d08012c4514a172bd75a38
Reviewed-on: https://gerrit.chromium.org/gerrit/31265
Reviewed-by: Stefan Reinauer <reinauer@chromium.org>
Tested-by: Stefan Reinauer <reinauer@chromium.org>
Reviewed-by: Randall Spangler <rspangler@chromium.org>
Commit-Ready: Stefan Reinauer <reinauer@chromium.org>
diff --git a/firmware/include/gbb_header.h b/firmware/include/gbb_header.h
index 57fb701..5031eeb 100644
--- a/firmware/include/gbb_header.h
+++ b/firmware/include/gbb_header.h
@@ -51,6 +51,8 @@
 #define GBB_FLAG_DISABLE_FW_ROLLBACK_CHECK  0x00000020
 /* Allow Enter key to trigger dev->tonorm screen transition */
 #define GBB_FLAG_ENTER_TRIGGERS_TONORM    0x00000040
+/* Allow booting Legacy OSes in dev mode even if dev_boot_legacy=0. */
+#define GBB_FLAG_FORCE_DEV_BOOT_LEGACY    0x00000080
 
 
 #ifdef __cplusplus
diff --git a/firmware/include/vboot_api.h b/firmware/include/vboot_api.h
index 12da955..5b55bf5 100644
--- a/firmware/include/vboot_api.h
+++ b/firmware/include/vboot_api.h
@@ -688,4 +688,7 @@
                          void *outbuf, uint32_t *out_size);
 
 
+/* Execute legacy boot option */
+int VbExLegacy(void);
+
 #endif  /* VBOOT_REFERENCE_VBOOT_API_H_ */
diff --git a/firmware/include/vboot_nvstorage.h b/firmware/include/vboot_nvstorage.h
index 8748a5c..e350459 100644
--- a/firmware/include/vboot_nvstorage.h
+++ b/firmware/include/vboot_nvstorage.h
@@ -49,6 +49,8 @@
   VBNV_KERNEL_FIELD,
   /* Allow booting from USB in developer mode.  0=no, 1=yes. */
   VBNV_DEV_BOOT_USB,
+  /* Allow booting of legacy OSes in developer mode.  0=no, 1=yes. */
+  VBNV_DEV_BOOT_LEGACY,
   /* Only boot Google-signed images in developer mode.  0=no, 1=yes. */
   VBNV_DEV_BOOT_SIGNED_ONLY,
   /* Set by userspace to request that RO firmware disable dev-mode on the next
diff --git a/firmware/lib/vboot_api_init.c b/firmware/lib/vboot_api_init.c
index 0a1ee43..9c6d80e 100644
--- a/firmware/lib/vboot_api_init.c
+++ b/firmware/lib/vboot_api_init.c
@@ -235,6 +235,7 @@
      * initially disabled if the user later transitions back into developer
      * mode. */
     VbNvSet(&vnc, VBNV_DEV_BOOT_USB, 0);
+    VbNvSet(&vnc, VBNV_DEV_BOOT_LEGACY, 0);
     VbNvSet(&vnc, VBNV_DEV_BOOT_SIGNED_ONLY, 0);
 
     /* If we don't need the VGA option ROM but got it anyway, stop asking for
diff --git a/firmware/lib/vboot_api_kernel.c b/firmware/lib/vboot_api_kernel.c
index 3ce5983..750edb2 100644
--- a/firmware/lib/vboot_api_kernel.c
+++ b/firmware/lib/vboot_api_kernel.c
@@ -156,16 +156,19 @@
 VbError_t VbBootDeveloper(VbCommonParams* cparams, LoadKernelParams* p) {
   GoogleBinaryBlockHeader* gbb = (GoogleBinaryBlockHeader*)cparams->gbb_data;
   VbSharedDataHeader* shared = (VbSharedDataHeader*)cparams->shared_data_blob;
-  uint32_t allow_usb = 0;
+  uint32_t allow_usb = 0, allow_legacy = 0;
   VbAudioContext* audio = 0;
 
   VBDEBUG(("Entering %s()\n", __func__));
 
   /* Check if USB booting is allowed */
   VbNvGet(&vnc, VBNV_DEV_BOOT_USB, &allow_usb);
+  VbNvGet(&vnc, VBNV_DEV_BOOT_LEGACY, &allow_legacy);
   /* Handle GBB flag override */
   if (gbb->flags & GBB_FLAG_FORCE_DEV_BOOT_USB)
     allow_usb = 1;
+  if (gbb->flags & GBB_FLAG_FORCE_DEV_BOOT_LEGACY)
+    allow_legacy = 1;
 
   /* Show the dev mode warning screen */
   VbDisplayScreen(cparams, VB_SCREEN_DEVELOPER_WARNING, 0, &vnc);
@@ -227,6 +230,20 @@
         VBDEBUG(("VbBootDeveloper() - user pressed Ctrl+D; skip delay\n"));
         goto fallout;
         break;
+      case 0x0c:
+        VBDEBUG(("VbBootDeveloper() - user pressed Ctrl+L; Try legacy boot\n"));
+        /* If VbExLegacy() succeeds, it will never return.
+         * If it returns, beep.
+         */
+        if (allow_legacy)
+          VbExLegacy();
+        else
+          VBDEBUG(("VbBootDeveloper() - Legacy boot is disabled\n"));
+
+        VbExBeep(120, 400);
+        VbExSleepMs(120);
+        VbExBeep(120, 400);
+        break;
       /* The Ctrl-Enter is special for Lumpy test purpose. */
       case VB_KEY_CTRL_ENTER:
       case 0x15:
diff --git a/firmware/lib/vboot_display.c b/firmware/lib/vboot_display.c
index ca3360b..065eee5 100644
--- a/firmware/lib/vboot_display.c
+++ b/firmware/lib/vboot_display.c
@@ -553,6 +553,11 @@
   used += Strncat(buf + used, "\ndev_boot_usb: ", DEBUG_INFO_SIZE - used);
   used += Uint64ToString(buf + used, DEBUG_INFO_SIZE - used, i, 10, 0);
 
+  /* Add dev_boot_legacy flag */
+  VbNvGet(vncptr, VBNV_DEV_BOOT_LEGACY, &i);
+  used += Strncat(buf + used, "\ndev_boot_legacy: ", DEBUG_INFO_SIZE - used);
+  used += Uint64ToString(buf + used, DEBUG_INFO_SIZE - used, i, 10, 0);
+
   /* Add dev_boot_signed_only flag */
   VbNvGet(vncptr, VBNV_DEV_BOOT_SIGNED_ONLY, &i);
   used += Strncat(buf + used, "\ndev_boot_signed_only: ",
diff --git a/firmware/lib/vboot_nvstorage.c b/firmware/lib/vboot_nvstorage.c
index 0f4633b..618ff52 100644
--- a/firmware/lib/vboot_nvstorage.c
+++ b/firmware/lib/vboot_nvstorage.c
@@ -32,6 +32,7 @@
 #define DEV_FLAGS_OFFSET             4
 #define DEV_BOOT_USB_MASK               0x01
 #define DEV_BOOT_SIGNED_ONLY_MASK       0x02
+#define DEV_BOOT_LEGACY_MASK            0x04
 
 #define TPM_FLAGS_OFFSET             5
 #define TPM_CLEAR_OWNER_REQUEST         0x01
@@ -116,6 +117,10 @@
       *dest = (raw[DEV_FLAGS_OFFSET] & DEV_BOOT_USB_MASK ? 1 : 0);
       return 0;
 
+    case VBNV_DEV_BOOT_LEGACY:
+      *dest = (raw[DEV_FLAGS_OFFSET] & DEV_BOOT_LEGACY_MASK ? 1 : 0);
+      return 0;
+
     case VBNV_DEV_BOOT_SIGNED_ONLY:
       *dest = (raw[DEV_FLAGS_OFFSET] & DEV_BOOT_SIGNED_ONLY_MASK ? 1 : 0);
       return 0;
@@ -210,6 +215,13 @@
         raw[DEV_FLAGS_OFFSET] &= ~DEV_BOOT_USB_MASK;
       break;
 
+    case VBNV_DEV_BOOT_LEGACY:
+      if (value)
+        raw[DEV_FLAGS_OFFSET] |= DEV_BOOT_LEGACY_MASK;
+      else
+        raw[DEV_FLAGS_OFFSET] &= ~DEV_BOOT_LEGACY_MASK;
+      break;
+
     case VBNV_DEV_BOOT_SIGNED_ONLY:
       if (value)
         raw[DEV_FLAGS_OFFSET] |= DEV_BOOT_SIGNED_ONLY_MASK;
diff --git a/firmware/stub/vboot_api_stub.c b/firmware/stub/vboot_api_stub.c
index 53a49d2..8521db0 100644
--- a/firmware/stub/vboot_api_stub.c
+++ b/firmware/stub/vboot_api_stub.c
@@ -188,3 +188,8 @@
 VbError_t VbExEcProtectRW(void) {
   return VBERROR_SUCCESS;
 }
+
+int VbExLegacy(void)
+{
+  return 1;
+}
diff --git a/host/lib/crossystem.c b/host/lib/crossystem.c
index a19384d..e1ee4fc 100644
--- a/host/lib/crossystem.c
+++ b/host/lib/crossystem.c
@@ -421,6 +421,8 @@
     value = VbGetNvStorage(VBNV_LOCALIZATION_INDEX);
   } else if (!strcasecmp(name,"dev_boot_usb")) {
     value = VbGetNvStorage(VBNV_DEV_BOOT_USB);
+  } else if (!strcasecmp(name,"dev_boot_legacy")) {
+    value = VbGetNvStorage(VBNV_DEV_BOOT_LEGACY);
   } else if (!strcasecmp(name,"dev_boot_signed_only")) {
     value = VbGetNvStorage(VBNV_DEV_BOOT_SIGNED_ONLY);
   } else if (!strcasecmp(name,"oprom_needed")) {
@@ -521,6 +523,8 @@
     return VbSetNvStorage(VBNV_LOCALIZATION_INDEX, value);
   } else if (!strcasecmp(name,"dev_boot_usb")) {
     return VbSetNvStorage(VBNV_DEV_BOOT_USB, value);
+  } else if (!strcasecmp(name,"dev_boot_legacy")) {
+    return VbSetNvStorage(VBNV_DEV_BOOT_LEGACY, value);
   } else if (!strcasecmp(name,"dev_boot_signed_only")) {
     return VbSetNvStorage(VBNV_DEV_BOOT_SIGNED_ONLY, value);
   } else if (!strcasecmp(name,"oprom_needed")) {
diff --git a/scripts/image_signing/set_gbb_flags.sh b/scripts/image_signing/set_gbb_flags.sh
index 47f3691..de17c83 100755
--- a/scripts/image_signing/set_gbb_flags.sh
+++ b/scripts/image_signing/set_gbb_flags.sh
@@ -29,6 +29,7 @@
   GBB_FLAG_FORCE_DEV_BOOT_USB         0x00000010
   GBB_FLAG_DISABLE_FW_ROLLBACK_CHECK  0x00000020
   GBB_FLAG_ENTER_TRIGGERS_TONORM      0x00000040
+  GBB_FLAG_FORCE_DEV_BOOT_LEGACY      0x00000080
 
   To get a developer-friendly device, try 0x11 (short_delay + boot_usb).
   For factory-related tests (always DEV), try 0x39.
diff --git a/tests/vboot_nvstorage_test.c b/tests/vboot_nvstorage_test.c
index a555ac7..19937d6 100644
--- a/tests/vboot_nvstorage_test.c
+++ b/tests/vboot_nvstorage_test.c
@@ -30,6 +30,7 @@
   {VBNV_LOCALIZATION_INDEX, 0, 0x69, 0xB0, "localization index"},
   {VBNV_KERNEL_FIELD, 0, 0x12345678, 0xFEDCBA98, "kernel field"},
   {VBNV_DEV_BOOT_USB, 0, 1, 0, "dev boot usb"},
+  {VBNV_DEV_BOOT_LEGACY, 0, 1, 0, "dev boot legacy"},
   {VBNV_DEV_BOOT_SIGNED_ONLY, 0, 1, 0, "dev boot custom"},
   {VBNV_DISABLE_DEV_REQUEST, 0, 1, 0, "disable dev request"},
   {VBNV_CLEAR_TPM_OWNER_REQUEST, 0, 1, 0, "clear tpm owner request"},
diff --git a/utility/crossystem_main.c b/utility/crossystem_main.c
index 7528f01..d819107 100644
--- a/utility/crossystem_main.c
+++ b/utility/crossystem_main.c
@@ -43,6 +43,8 @@
   {"disable_dev_request", CAN_WRITE, "Disable virtual dev-mode on next boot"},
   {"dev_boot_usb", CAN_WRITE,
    "Enable developer mode boot from USB/SD (writable)"},
+  {"dev_boot_legacy", CAN_WRITE,
+   "Enable developer mode boot Legacy OSes (writable)"},
   {"dev_boot_signed_only", CAN_WRITE,
    "Enable developer mode boot only from official kernels (writable)"},
   {"devsw_boot", 0, "Developer switch position at boot"},