Add NV flag to default boot legacy OS

In developer mode, this option will make the system try to boot into
a legacy OS first after the 30 second timeout. This removes the need to
press a key during boot to try legacy mode and the need to remove the
write protect screw to boot legacy as default.

BUG=chromium:310697
BRANCH=none
TEST=make runtests

Change-Id: I9a9f64c14ad015e21d08eec36e8fc187189cd2f2
Signed-off-by: Mary Ruthven <mruthven@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/304077
Reviewed-by: Randall Spangler <rspangler@chromium.org>
diff --git a/firmware/2lib/2misc.c b/firmware/2lib/2misc.c
index 4b30a5c..a856775 100644
--- a/firmware/2lib/2misc.c
+++ b/firmware/2lib/2misc.c
@@ -276,6 +276,7 @@
 		vb2_nv_set(ctx, VB2_NV_DEV_BOOT_LEGACY, 0);
 		vb2_nv_set(ctx, VB2_NV_DEV_BOOT_SIGNED_ONLY, 0);
 		vb2_nv_set(ctx, VB2_NV_DEV_BOOT_FASTBOOT_FULL_CAP, 0);
+		vb2_nv_set(ctx, VB2_NV_DEV_DEFAULT_BOOT, 0);
 		vb2_nv_set(ctx, VB2_NV_FASTBOOT_UNLOCK_IN_FW, 0);
 	}
 
diff --git a/firmware/2lib/2nvstorage.c b/firmware/2lib/2nvstorage.c
index 55f1d0d..851c387 100644
--- a/firmware/2lib/2nvstorage.c
+++ b/firmware/2lib/2nvstorage.c
@@ -139,6 +139,10 @@
 		return GETBIT(VB2_NV_OFFS_DEV,
 			      VB2_NV_DEV_FLAG_FASTBOOT_FULL_CAP);
 
+	case VB2_NV_DEV_DEFAULT_BOOT:
+		return (p[VB2_NV_OFFS_DEV] & VB2_NV_DEV_FLAG_DEFAULT_BOOT)
+			>> VB2_NV_DEV_DEFAULT_BOOT_SHIFT;
+
 	case VB2_NV_DISABLE_DEV_REQUEST:
 		return GETBIT(VB2_NV_OFFS_BOOT, VB2_NV_BOOT_DISABLE_DEV);
 
@@ -292,6 +296,17 @@
 		SETBIT(VB2_NV_OFFS_DEV, VB2_NV_DEV_FLAG_FASTBOOT_FULL_CAP);
 		break;
 
+	case VB2_NV_DEV_DEFAULT_BOOT:
+		/* Map out of range values to disk */
+		if (value > (VB2_NV_DEV_FLAG_DEFAULT_BOOT >>
+			     VB2_NV_DEV_DEFAULT_BOOT_SHIFT))
+			value = VB2_DEV_DEFAULT_BOOT_DISK;
+
+		p[VB2_NV_OFFS_DEV] &= ~VB2_NV_DEV_FLAG_DEFAULT_BOOT;
+		p[VB2_NV_OFFS_DEV] |=
+			(uint8_t)(value << VB2_NV_DEV_DEFAULT_BOOT_SHIFT);
+		break;
+
 	case VB2_NV_DISABLE_DEV_REQUEST:
 		SETBIT(VB2_NV_OFFS_BOOT, VB2_NV_BOOT_DISABLE_DEV);
 		break;
diff --git a/firmware/2lib/include/2nvstorage.h b/firmware/2lib/include/2nvstorage.h
index bfe9a3a..31dfc8d 100644
--- a/firmware/2lib/include/2nvstorage.h
+++ b/firmware/2lib/include/2nvstorage.h
@@ -55,6 +55,8 @@
 	 * 0=no, 1=yes.
 	 */
 	VB2_NV_DEV_BOOT_FASTBOOT_FULL_CAP,
+	/* Set default boot mode (see vb2_dev_default_boot) */
+	VB2_NV_DEV_DEFAULT_BOOT,
 	/*
 	 * Set by userspace to request that RO firmware disable dev-mode on the
 	 * next boot. This is likely only possible if the dev-switch is
@@ -94,6 +96,19 @@
 	VB2_NV_BOOT_ON_AC_DETECT,
 };
 
+/* Set default boot in developer mode */
+enum vb2_dev_default_boot {
+	/* Default to boot from disk*/
+	VB2_DEV_DEFAULT_BOOT_DISK = 0,
+
+	/* Default to boot from USB */
+	VB2_DEV_DEFAULT_BOOT_USB= 1,
+
+	/* Default to boot legacy OS */
+	VB2_DEV_DEFAULT_BOOT_LEGACY = 2,
+
+};
+
 /* Firmware result codes for VB2_NV_FW_RESULT and VB2_NV_FW_PREV_RESULT */
 enum vb2_fw_result {
 	/* Unknown */
diff --git a/firmware/2lib/include/2nvstorage_fields.h b/firmware/2lib/include/2nvstorage_fields.h
index 8ae21f8..fd625d0 100644
--- a/firmware/2lib/include/2nvstorage_fields.h
+++ b/firmware/2lib/include/2nvstorage_fields.h
@@ -57,11 +57,13 @@
 #define VB2_NV_BOOT2_PREV_RESULT_SHIFT 4  /* Number of bits to shift result */
 #define VB2_NV_BOOT2_PREV_TRIED                0x40
 
-/* Fields in VB2_NV_OFFS_DEV (unused = 0xf0) */
+/* Fields in VB2_NV_OFFS_DEV (unused = 0xc0) */
 #define VB2_NV_DEV_FLAG_USB                    0x01
 #define VB2_NV_DEV_FLAG_SIGNED_ONLY            0x02
 #define VB2_NV_DEV_FLAG_LEGACY                 0x04
 #define VB2_NV_DEV_FLAG_FASTBOOT_FULL_CAP      0x08
+#define VB2_NV_DEV_FLAG_DEFAULT_BOOT           0x30
+#define VB2_NV_DEV_DEFAULT_BOOT_SHIFT 4  /* Number of bits to shift */
 
 /* Fields in VB2_NV_OFFS_TPM (unused = 0xf8) */
 #define VB2_NV_TPM_CLEAR_OWNER_REQUEST         0x01
diff --git a/firmware/include/vboot_nvstorage.h b/firmware/include/vboot_nvstorage.h
index 5a44ae1..dc4ab50 100644
--- a/firmware/include/vboot_nvstorage.h
+++ b/firmware/include/vboot_nvstorage.h
@@ -76,6 +76,8 @@
 	 * 0=no, 1=yes.
 	 */
 	VBNV_DEV_BOOT_FASTBOOT_FULL_CAP,
+	/* Set default boot mode (see VbDevDefaultBoot) */
+	VBNV_DEV_DEFAULT_BOOT,
 	/*
 	 * Set by userspace to request that RO firmware disable dev-mode on the
 	 * next boot. This is likely only possible if the dev-switch is
@@ -119,6 +121,19 @@
 
 } VbNvParam;
 
+/* Set default boot in developer mode */
+typedef enum VbDevDefaultBoot {
+	/* Default to boot from disk*/
+	VBNV_DEV_DEFAULT_BOOT_DISK = 0,
+
+	/* Default to boot from USB */
+	VBNV_DEV_DEFAULT_BOOT_USB = 1,
+
+	/* Default to boot legacy OS */
+	VBNV_DEV_DEFAULT_BOOT_LEGACY = 2,
+
+} VbDevDefaultBoot;
+
 /* Result of trying the firmware in VBNV_FW_TRIED */
 typedef enum VbFwResult {
 	/* Unknown */
diff --git a/firmware/lib/vboot_api_init.c b/firmware/lib/vboot_api_init.c
index 419175c..2657fb5 100644
--- a/firmware/lib/vboot_api_init.c
+++ b/firmware/lib/vboot_api_init.c
@@ -323,6 +323,7 @@
 		VbNvSet(&vnc, VBNV_DEV_BOOT_LEGACY, 0);
 		VbNvSet(&vnc, VBNV_DEV_BOOT_SIGNED_ONLY, 0);
 		VbNvSet(&vnc, VBNV_DEV_BOOT_FASTBOOT_FULL_CAP, 0);
+		VbNvSet(&vnc, VBNV_DEV_DEFAULT_BOOT, 0);
 		VbNvSet(&vnc, VBNV_FASTBOOT_UNLOCK_IN_FW, 0);
 		/*
 		 * Back up any changes now, so these values can't be forgotten
diff --git a/firmware/lib/vboot_api_kernel.c b/firmware/lib/vboot_api_kernel.c
index 0456123..7b47dc6 100644
--- a/firmware/lib/vboot_api_kernel.c
+++ b/firmware/lib/vboot_api_kernel.c
@@ -170,6 +170,27 @@
 	return retval;
 }
 
+uint32_t VbTryUsb(VbCommonParams *cparams, LoadKernelParams *p)
+{
+	uint32_t retval = VbTryLoadKernel(cparams, p, VB_DISK_FLAG_REMOVABLE);
+	if (VBERROR_SUCCESS == retval) {
+		VBDEBUG(("VbBootDeveloper() - booting USB\n"));
+	} else {
+		VBDEBUG(("VbBootDeveloper() - no kernel found on USB\n"));
+		VbExBeep(250, 200);
+		VbExSleepMs(120);
+		/*
+		 * Clear recovery requests from failed
+		 * kernel loading, so that powering off
+		 * at this point doesn't put us into
+		 * recovery mode.
+		 */
+		VbSetRecoveryRequest(
+			VBNV_RECOVERY_NOT_REQUESTED);
+	}
+	return retval;
+}
+
 #define CONFIRM_KEY_DELAY 20  /* Check confirm screen keys every 20ms */
 
 int VbUserConfirms(VbCommonParams *cparams, uint32_t confirm_flags)
@@ -251,7 +272,14 @@
 	GoogleBinaryBlockHeader *gbb = cparams->gbb;
 	VbSharedDataHeader *shared =
 		(VbSharedDataHeader *)cparams->shared_data_blob;
-	uint32_t allow_usb = 0, allow_legacy = 0, ctrl_d_pressed = 0;
+
+	uint32_t allow_usb = 0;
+	uint32_t allow_legacy = 0;
+	uint32_t use_usb = 0;
+	uint32_t use_legacy = 0;
+	uint32_t default_boot = 0;
+	uint32_t ctrl_d_pressed = 0;
+
 	VbAudioContext *audio = 0;
 
 	VBDEBUG(("Entering %s()\n", __func__));
@@ -260,11 +288,23 @@
 	VbNvGet(&vnc, VBNV_DEV_BOOT_USB, &allow_usb);
 	VbNvGet(&vnc, VBNV_DEV_BOOT_LEGACY, &allow_legacy);
 
+	/* Check if the default is to boot using disk, usb, or legacy */
+	VbNvGet(&vnc, VBNV_DEV_DEFAULT_BOOT, &default_boot);
+
+	if(default_boot == VBNV_DEV_DEFAULT_BOOT_USB)
+		use_usb = 1;
+	if(default_boot == VBNV_DEV_DEFAULT_BOOT_LEGACY)
+		use_legacy = 1;
+
 	/* 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;
+	if (gbb->flags & GBB_FLAG_DEFAULT_DEV_BOOT_LEGACY) {
+		use_legacy = 1;
+		use_usb = 0;
+	}
 
 	/* Show the dev mode warning screen */
 	VbDisplayScreen(cparams, VB_SCREEN_DEVELOPER_WARNING, 0, &vnc);
@@ -397,26 +437,10 @@
 				 */
 				VbDisplayScreen(cparams, VB_SCREEN_BLANK, 0,
 						&vnc);
-				if (VBERROR_SUCCESS ==
-				    VbTryLoadKernel(cparams, p,
-						    VB_DISK_FLAG_REMOVABLE)) {
-					VBDEBUG(("VbBootDeveloper() - "
-						 "booting USB\n"));
+				if (VBERROR_SUCCESS == VbTryUsb(cparams, p)) {
 					VbAudioClose(audio);
 					return VBERROR_SUCCESS;
 				} else {
-					VBDEBUG(("VbBootDeveloper() - "
-						 "no kernel found on USB\n"));
-					VbExBeep(250, 200);
-					VbExSleepMs(120);
-					/*
-					 * Clear recovery requests from failed
-					 * kernel loading, so that powering off
-					 * at this point doesn't put us into
-					 * recovery mode.
-					 */
-					VbSetRecoveryRequest(
-						VBNV_RECOVERY_NOT_REQUESTED);
 					/* Show dev mode warning screen again */
 					VbDisplayScreen(
 						cparams,
@@ -435,12 +459,18 @@
  fallout:
 
 	/* If defaulting to legacy boot, try that unless Ctrl+D was pressed */
-	if ((gbb->flags & GBB_FLAG_DEFAULT_DEV_BOOT_LEGACY) &&
-	    !ctrl_d_pressed) {
+	if (use_legacy && !ctrl_d_pressed) {
 		VBDEBUG(("VbBootDeveloper() - defaulting to legacy\n"));
 		VbTryLegacy(allow_legacy);
 	}
 
+	if ((use_usb && !ctrl_d_pressed) && allow_usb) {
+		if (VBERROR_SUCCESS == VbTryUsb(cparams, p)) {
+			VbAudioClose(audio);
+			return VBERROR_SUCCESS;
+		}
+	}
+
 	/* Timeout or Ctrl+D; attempt loading from fixed disk */
 	VBDEBUG(("VbBootDeveloper() - trying fixed disk\n"));
 	VbAudioClose(audio);
diff --git a/firmware/lib/vboot_display.c b/firmware/lib/vboot_display.c
index 311bc8e..1ad3c86 100644
--- a/firmware/lib/vboot_display.c
+++ b/firmware/lib/vboot_display.c
@@ -569,6 +569,12 @@
 			"\ndev_boot_legacy: ", DEBUG_INFO_SIZE - used);
 	used += Uint64ToString(buf + used, DEBUG_INFO_SIZE - used, i, 10, 0);
 
+	/* Add dev_default_boot flag */
+	VbNvGet(vncptr, VBNV_DEV_DEFAULT_BOOT, &i);
+	used += StrnAppend(buf + used,
+			"\ndev_default_boot: ", 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 += StrnAppend(buf + used, "\ndev_boot_signed_only: ",
diff --git a/firmware/lib/vboot_nvstorage.c b/firmware/lib/vboot_nvstorage.c
index 6a21bfe..403ce30 100644
--- a/firmware/lib/vboot_nvstorage.c
+++ b/firmware/lib/vboot_nvstorage.c
@@ -42,6 +42,8 @@
 #define DEV_BOOT_SIGNED_ONLY_MASK       0x02
 #define DEV_BOOT_LEGACY_MASK            0x04
 #define DEV_BOOT_FASTBOOT_FULL_CAP_MASK 0x08
+#define DEV_DEFAULT_BOOT_MASK           0x30
+#define DEV_DEFAULT_BOOT_SHIFT 4  /* Number of bits to shift */
 
 #define TPM_FLAGS_OFFSET             5
 #define TPM_CLEAR_OWNER_REQUEST         0x01
@@ -151,6 +153,11 @@
 		*dest = (raw[DEV_FLAGS_OFFSET] & DEV_BOOT_LEGACY_MASK ? 1 : 0);
 		return 0;
 
+	case VBNV_DEV_DEFAULT_BOOT:
+		*dest = (raw[DEV_FLAGS_OFFSET] & DEV_DEFAULT_BOOT_MASK)
+			>> DEV_DEFAULT_BOOT_SHIFT;
+		return 0;
+
 	case VBNV_DEV_BOOT_SIGNED_ONLY:
 		*dest = (raw[DEV_FLAGS_OFFSET] & DEV_BOOT_SIGNED_ONLY_MASK ?
 			 1 : 0);
@@ -308,6 +315,17 @@
 			raw[DEV_FLAGS_OFFSET] &= ~DEV_BOOT_LEGACY_MASK;
 		break;
 
+	case VBNV_DEV_DEFAULT_BOOT:
+		/* Map out of range values to boot disk */
+		if (value > (DEV_DEFAULT_BOOT_MASK >>
+			     DEV_DEFAULT_BOOT_SHIFT))
+			value = VBNV_DEV_DEFAULT_BOOT_DISK;
+
+		raw[DEV_FLAGS_OFFSET] &= ~DEV_DEFAULT_BOOT_MASK;
+		raw[DEV_FLAGS_OFFSET] |= (uint8_t)value <<
+			DEV_DEFAULT_BOOT_SHIFT;
+		break;
+
 	case VBNV_DEV_BOOT_SIGNED_ONLY:
 		if (value)
 			raw[DEV_FLAGS_OFFSET] |= DEV_BOOT_SIGNED_ONLY_MASK;
diff --git a/firmware/lib/vboot_nvstorage_rollback.c b/firmware/lib/vboot_nvstorage_rollback.c
index b1ec9ae..a913243 100644
--- a/firmware/lib/vboot_nvstorage_rollback.c
+++ b/firmware/lib/vboot_nvstorage_rollback.c
@@ -21,6 +21,7 @@
 	VBNV_DEV_BOOT_LEGACY,
 	VBNV_DEV_BOOT_SIGNED_ONLY,
 	VBNV_DEV_BOOT_FASTBOOT_FULL_CAP,
+	VBNV_DEV_DEFAULT_BOOT,
 	VBNV_FASTBOOT_UNLOCK_IN_FW,
 };
 
diff --git a/host/lib/crossystem.c b/host/lib/crossystem.c
index 3080344..e2066ce 100644
--- a/host/lib/crossystem.c
+++ b/host/lib/crossystem.c
@@ -61,6 +61,7 @@
 } VbBuildOption;
 
 static const char *fw_results[] = {"unknown", "trying", "success", "failure"};
+static const char *default_boot[] = {"disk", "usb", "legacy"};
 
 /* Masks for kern_nv usage by kernel. */
 #define KERN_NV_FWUPDATE_TRIES_MASK 0x0000000F
@@ -586,6 +587,12 @@
       return fw_results[v];
     else
       return "unknown";
+  } else if (!strcasecmp(name,"dev_default_boot")) {
+    int v = VbGetNvStorage(VBNV_DEV_DEFAULT_BOOT);
+    if (v < ARRAY_SIZE(default_boot))
+      return default_boot[v];
+    else
+      return "unknown";
   }
 
   return NULL;
@@ -694,6 +701,14 @@
 	return VbSetNvStorage(VBNV_FW_RESULT, i);
     }
     return -1;
+  } else if (!strcasecmp(name, "dev_default_boot")) {
+    int i;
+
+    for (i = 0; i < ARRAY_SIZE(default_boot); i++) {
+      if (!strcasecmp(value, default_boot[i]))
+	return VbSetNvStorage(VBNV_DEV_DEFAULT_BOOT, i);
+    }
+    return -1;
   }
 
   return -1;
diff --git a/tests/vb2_misc_tests.c b/tests/vb2_misc_tests.c
index 8be5ae3..aa3b061 100644
--- a/tests/vb2_misc_tests.c
+++ b/tests/vb2_misc_tests.c
@@ -310,6 +310,7 @@
 	vb2_nv_set(&cc, VB2_NV_DEV_BOOT_LEGACY, 1);
 	vb2_nv_set(&cc, VB2_NV_DEV_BOOT_SIGNED_ONLY, 1);
 	vb2_nv_set(&cc, VB2_NV_DEV_BOOT_FASTBOOT_FULL_CAP, 1);
+	vb2_nv_set(&cc, VB2_NV_DEV_DEFAULT_BOOT, 1);
 	vb2_nv_set(&cc, VB2_NV_FASTBOOT_UNLOCK_IN_FW, 1);
 	TEST_SUCC(vb2_check_dev_switch(&cc), "dev mode off");
 	TEST_EQ(vb2_nv_get(&cc, VB2_NV_DEV_BOOT_USB),
@@ -320,6 +321,8 @@
 		0, "  cleared dev boot signed only");
 	TEST_EQ(vb2_nv_get(&cc, VB2_NV_DEV_BOOT_FASTBOOT_FULL_CAP),
 		0, "  cleared dev boot fastboot full cap");
+	TEST_EQ(vb2_nv_get(&cc, VB2_NV_DEV_DEFAULT_BOOT),
+		0, "  cleared dev default boot");
 	TEST_EQ(vb2_nv_get(&cc, VB2_NV_FASTBOOT_UNLOCK_IN_FW),
 		0, "  cleared dev boot fastboot unlock in fw");
 
diff --git a/tests/vb2_nvstorage_tests.c b/tests/vb2_nvstorage_tests.c
index 0bf58b6..7796a1b 100644
--- a/tests/vb2_nvstorage_tests.c
+++ b/tests/vb2_nvstorage_tests.c
@@ -44,6 +44,7 @@
 	{VB2_NV_DEV_BOOT_LEGACY, 0, 1, 0, "dev boot legacy"},
 	{VB2_NV_DEV_BOOT_SIGNED_ONLY, 0, 1, 0, "dev boot custom"},
 	{VB2_NV_DEV_BOOT_FASTBOOT_FULL_CAP, 0, 1, 0, "dev boot fb full cap"},
+	{VB2_NV_DEV_DEFAULT_BOOT, 0, 1, 2, "dev default boot"},
 	{VB2_NV_DISABLE_DEV_REQUEST, 0, 1, 0, "disable dev request"},
 	{VB2_NV_CLEAR_TPM_OWNER_REQUEST, 0, 1, 0, "clear tpm owner request"},
 	{VB2_NV_CLEAR_TPM_OWNER_DONE, 0, 1, 0, "clear tpm owner done"},
@@ -195,6 +196,10 @@
 	vb2_nv_set(&c, VB2_NV_FW_RESULT, VB2_FW_RESULT_UNKNOWN + 100);
 	TEST_EQ(vb2_nv_get(&c, VB2_NV_FW_RESULT),
 		VB2_FW_RESULT_UNKNOWN, "Firmware result out of range");
+
+	vb2_nv_set(&c, VB2_NV_DEV_DEFAULT_BOOT, VB2_DEV_DEFAULT_BOOT_DISK + 100);
+	TEST_EQ(vb2_nv_get(&c, VB2_NV_DEV_DEFAULT_BOOT),
+		VB2_DEV_DEFAULT_BOOT_DISK, "default to booting from disk");
 }
 
 int main(int argc, char* argv[])
diff --git a/tests/vboot_api_init_tests.c b/tests/vboot_api_init_tests.c
index bfa5d7d..366873b 100644
--- a/tests/vboot_api_init_tests.c
+++ b/tests/vboot_api_init_tests.c
@@ -556,6 +556,7 @@
 	VbNvSet(&vnc, VBNV_DEV_BOOT_LEGACY, 1);
 	VbNvSet(&vnc, VBNV_DEV_BOOT_SIGNED_ONLY, 1);
 	VbNvSet(&vnc, VBNV_DEV_BOOT_FASTBOOT_FULL_CAP, 1);
+	VbNvSet(&vnc, VBNV_DEV_DEFAULT_BOOT, 1);
 	VbNvSet(&vnc, VBNV_FASTBOOT_UNLOCK_IN_FW, 1);
 	/* and some that don't */
 	VbNvSet(&vnc, VBNV_OPROM_NEEDED, 1);
@@ -586,6 +587,8 @@
 	TEST_EQ(u, 0, "  NV dev_boot_signed_only");
 	VbNvGet(&vnc, VBNV_DEV_BOOT_FASTBOOT_FULL_CAP, &u);
 	TEST_EQ(u, 0, "  NV dev_boot_fastboot_full_cap");
+	VbNvGet(&vnc, VBNV_DEV_DEFAULT_BOOT, &u);
+	TEST_EQ(u, 0, "  NV dev_default_boot");
 	VbNvGet(&vnc, VBNV_FASTBOOT_UNLOCK_IN_FW, &u);
 	TEST_EQ(u, 0, "  NV_fastboot_unlock_in_fw ");
 	/* So we should have written the backup */
@@ -605,6 +608,8 @@
 	TEST_EQ(u, 0, "  BU dev_boot_signed_only");
 	VbNvGet(&tmp_vnc, VBNV_DEV_BOOT_FASTBOOT_FULL_CAP, &u);
 	TEST_EQ(u, 0, "  BU dev_boot_fastboot_full_cap");
+	VbNvGet(&tmp_vnc, VBNV_DEV_DEFAULT_BOOT, &u);
+	TEST_EQ(u, 0, "  BU dev_default_boot");
 	VbNvGet(&tmp_vnc, VBNV_FASTBOOT_UNLOCK_IN_FW, &u);
 	TEST_EQ(u, 0, "  BU fastboot_unlock_in_fw");
 	/* but not the others */
@@ -647,6 +652,7 @@
 	VbNvSet(&vnc, VBNV_DEV_BOOT_LEGACY, 1);
 	VbNvSet(&vnc, VBNV_DEV_BOOT_SIGNED_ONLY, 1);
 	VbNvSet(&vnc, VBNV_DEV_BOOT_FASTBOOT_FULL_CAP, 1);
+	VbNvSet(&vnc, VBNV_DEV_DEFAULT_BOOT, 1);
 	VbNvSet(&vnc, VBNV_FASTBOOT_UNLOCK_IN_FW, 1);
 	/* and some that don't */
 	VbNvSet(&vnc, VBNV_OPROM_NEEDED, 1);
@@ -697,6 +703,8 @@
 	TEST_EQ(u, 1, "  BU dev_boot_signed_only");
 	VbNvGet(&tmp_vnc, VBNV_DEV_BOOT_FASTBOOT_FULL_CAP, &u);
 	TEST_EQ(u, 1, "  BU dev_boot_fastboot_full_cap");
+	VbNvGet(&tmp_vnc, VBNV_DEV_DEFAULT_BOOT, &u);
+	TEST_EQ(u, 1, "  BU dev_default_boot");
 	VbNvGet(&tmp_vnc, VBNV_FASTBOOT_UNLOCK_IN_FW, &u);
 	TEST_EQ(u, 1, "  BU fastboot_unlock_in_fw");
 	/* but not the others */
@@ -738,6 +746,8 @@
 	TEST_EQ(u, 1, "  BU dev_boot_signed_only");
 	VbNvGet(&vnc, VBNV_DEV_BOOT_FASTBOOT_FULL_CAP, &u);
 	TEST_EQ(u, 1, "  BU dev_boot_fastboot_full_cap");
+	VbNvGet(&tmp_vnc, VBNV_DEV_DEFAULT_BOOT, &u);
+	TEST_EQ(u, 1, "  BU dev_default_boot");
 	VbNvGet(&vnc, VBNV_FASTBOOT_UNLOCK_IN_FW, &u);
 	TEST_EQ(u, 1, "  BU fastboot_unlock_in_fw");
 
@@ -776,6 +786,8 @@
 	TEST_EQ(u, 0, "  BU dev_boot_signed_only");
 	VbNvGet(&vnc, VBNV_DEV_BOOT_FASTBOOT_FULL_CAP, &u);
 	TEST_EQ(u, 0, "  BU dev_boot_fastboot_full_cap");
+	VbNvGet(&vnc, VBNV_DEV_DEFAULT_BOOT, &u);
+	TEST_EQ(u, 0, "  BU dev_default_boot");
 	VbNvGet(&vnc, VBNV_FASTBOOT_UNLOCK_IN_FW, &u);
 	TEST_EQ(u, 0, "  BU fastboot_unlock_in_fw");
 }
diff --git a/tests/vboot_api_kernel2_tests.c b/tests/vboot_api_kernel2_tests.c
index 6420d51..aebfab6 100644
--- a/tests/vboot_api_kernel2_tests.c
+++ b/tests/vboot_api_kernel2_tests.c
@@ -293,6 +293,45 @@
 	TEST_EQ(VbBootDeveloper(&cparams, &lkp), 1002, "Timeout");
 	TEST_EQ(vbexlegacy_called, 1, "  try legacy");
 
+	/* Proceed to legacy after timeout if boot legacy and default boot
+	 * legacy are set */
+	ResetMocks();
+	VbNvSet(VbApiKernelGetVnc(), VBNV_DEV_DEFAULT_BOOT,
+		VBNV_DEV_DEFAULT_BOOT_LEGACY);
+	VbNvSet(VbApiKernelGetVnc(), VBNV_DEV_BOOT_LEGACY, 1);
+	TEST_EQ(VbBootDeveloper(&cparams, &lkp), 1002, "Timeout");
+	TEST_EQ(vbexlegacy_called, 1, "  try legacy");
+
+	/* Proceed to legacy boot mode only if enabled */
+	ResetMocks();
+	VbNvSet(VbApiKernelGetVnc(), VBNV_DEV_DEFAULT_BOOT,
+		VBNV_DEV_DEFAULT_BOOT_LEGACY);
+	TEST_EQ(VbBootDeveloper(&cparams, &lkp), 1002, "Timeout");
+	TEST_EQ(vbexlegacy_called, 0, "  not legacy");
+
+	/* Proceed to usb after timeout if boot usb and default boot
+	 * usb are set */
+	ResetMocks();
+	VbNvSet(VbApiKernelGetVnc(), VBNV_DEV_DEFAULT_BOOT,
+		VBNV_DEV_DEFAULT_BOOT_USB);
+	VbNvSet(VbApiKernelGetVnc(), VBNV_DEV_BOOT_USB, 1);
+	vbtlk_retval = VBERROR_SUCCESS - VB_DISK_FLAG_REMOVABLE;
+	TEST_EQ(VbBootDeveloper(&cparams, &lkp), 0, "Ctrl+U USB");
+
+	/* Proceed to usb boot mode only if enabled */
+	ResetMocks();
+	VbNvSet(VbApiKernelGetVnc(), VBNV_DEV_DEFAULT_BOOT,
+		VBNV_DEV_DEFAULT_BOOT_USB);
+	TEST_EQ(VbBootDeveloper(&cparams, &lkp), 1002, "Timeout");
+
+	/* If no USB tries fixed disk */
+	ResetMocks();
+	VbNvSet(VbApiKernelGetVnc(), VBNV_DEV_BOOT_USB, 1);
+	VbNvSet(VbApiKernelGetVnc(), VBNV_DEV_DEFAULT_BOOT,
+		VBNV_DEV_DEFAULT_BOOT_USB);
+	TEST_EQ(VbBootDeveloper(&cparams, &lkp), 1002, "Ctrl+U enabled");
+	TEST_EQ(vbexlegacy_called, 0, "  not legacy");
+
 	/* Up arrow is uninteresting / passed to VbCheckDisplayKey() */
 	ResetMocks();
 	mock_keypress[0] = VB_KEY_UP;
@@ -402,7 +441,6 @@
 	TEST_EQ(vbexlegacy_called, 0, "  not legacy");
 
 	ResetMocks();
-
 	gbb.flags |= GBB_FLAG_FORCE_DEV_BOOT_LEGACY;
 	mock_keypress[0] = 0x0c;
 	TEST_EQ(VbBootDeveloper(&cparams, &lkp), 1002, "Ctrl+L force legacy");
diff --git a/tests/vboot_nvstorage_test.c b/tests/vboot_nvstorage_test.c
index a82a742..6a90ea4 100644
--- a/tests/vboot_nvstorage_test.c
+++ b/tests/vboot_nvstorage_test.c
@@ -34,6 +34,7 @@
   {VBNV_DEV_BOOT_LEGACY, 0, 1, 0, "dev boot legacy"},
   {VBNV_DEV_BOOT_SIGNED_ONLY, 0, 1, 0, "dev boot custom"},
   {VBNV_DEV_BOOT_FASTBOOT_FULL_CAP, 0, 1, 0, "dev boot fastboot full cap"},
+  {VBNV_DEV_DEFAULT_BOOT, 0, 1, 2, "dev default boot"},
   {VBNV_DISABLE_DEV_REQUEST, 0, 1, 0, "disable dev request"},
   {VBNV_CLEAR_TPM_OWNER_REQUEST, 0, 1, 0, "clear tpm owner request"},
   {VBNV_CLEAR_TPM_OWNER_DONE, 0, 1, 0, "clear tpm owner done"},
@@ -189,6 +190,9 @@
   VbNvSet(&c, VBNV_FW_RESULT, VBNV_FW_RESULT_UNKNOWN + 100);
   VbNvGet(&c, VBNV_FW_RESULT, &data);
   TEST_EQ(data, VBNV_FW_RESULT_UNKNOWN, "Firmware result out of range");
+  VbNvSet(&c, VBNV_DEV_DEFAULT_BOOT, VBNV_DEV_DEFAULT_BOOT_DISK + 100);
+  VbNvGet(&c, VBNV_DEV_DEFAULT_BOOT, &data);
+  TEST_EQ(data, VBNV_DEV_DEFAULT_BOOT_DISK, "Firmware result out of range");
   VbNvTeardown(&c);
 }
 
diff --git a/utility/crossystem.c b/utility/crossystem.c
index 2c017af..a1ef6e0 100644
--- a/utility/crossystem.c
+++ b/utility/crossystem.c
@@ -46,6 +46,8 @@
    "Enable developer mode boot Legacy OSes (writable)"},
   {"dev_boot_signed_only", CAN_WRITE,
    "Enable developer mode boot only from official kernels (writable)"},
+  {"dev_default_boot", IS_STRING|CAN_WRITE,
+   "default boot from legacy or usb (writable)"},
   {"devsw_boot", 0, "Developer switch position at boot"},
   {"devsw_cur",  0, "Developer switch current position"},
   {"disable_dev_request", CAN_WRITE, "Disable virtual dev-mode on next boot"},