nvstorage: Add kernel max rollforward NV storage field

This just adds the kernel_max_rollforward field to the nvstorage
libraries and crossystem.  The firmware does not use it yet; that's
coming in a subsequent CL.

16 of the fields's 32 bits are taken from unused bytes of the kernel
field.  This has no effect on existing usage.

BUG=chromium:783997
BRANCH=none
TEST=make runtests
     Also manual testing.  In a root shell:
     	crossystem kernel_max_rollforward --> Should default to 0

	crossystem kernel_max_rollforward=0xfffffffe
	crossystem kernel_max_rollforward --> Should be 0xfffffffe

     (Note that setting it to 0xffffffff is indistinguishable from the
     -1 value that the crossystem library uses to indicate error, so
     0xffffffff isn't actually usable as a max rollforward limit.  But
     0xfffffffe is, and if we ever get so close to the limit that we
     need to use 0xffffffff, something has already gone horribly wrong
     with our versioning strategy...)

Change-Id: I008f412e6ed3c0b59beb9881268585af69d1ff2e
Signed-off-by: Randall Spangler <rspangler@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/765572
Reviewed-by: Julius Werner <jwerner@chromium.org>
diff --git a/firmware/2lib/2nvstorage.c b/firmware/2lib/2nvstorage.c
index b40bbe7..0836336 100644
--- a/firmware/2lib/2nvstorage.c
+++ b/firmware/2lib/2nvstorage.c
@@ -121,10 +121,7 @@
 		return p[VB2_NV_OFFS_LOCALIZATION];
 
 	case VB2_NV_KERNEL_FIELD:
-		return (p[VB2_NV_OFFS_KERNEL]
-			| (p[VB2_NV_OFFS_KERNEL + 1] << 8)
-			| (p[VB2_NV_OFFS_KERNEL + 2] << 16)
-			| (p[VB2_NV_OFFS_KERNEL + 3] << 24));
+		return p[VB2_NV_OFFS_KERNEL1] | (p[VB2_NV_OFFS_KERNEL2] << 8);
 
 	case VB2_NV_DEV_BOOT_USB:
 		return GETBIT(VB2_NV_OFFS_DEV, VB2_NV_DEV_FLAG_USB);
@@ -175,6 +172,12 @@
 
 	case VB2_NV_BATTERY_CUTOFF_REQUEST:
 		return GETBIT(VB2_NV_OFFS_MISC, VB2_NV_MISC_BATTERY_CUTOFF);
+
+	case VB2_NV_KERNEL_MAX_ROLLFORWARD:
+		return (p[VB2_NV_OFFS_KERNEL_MAX_ROLLFORWARD1]
+			| (p[VB2_NV_OFFS_KERNEL_MAX_ROLLFORWARD2] << 8)
+			| (p[VB2_NV_OFFS_KERNEL_MAX_ROLLFORWARD3] << 16)
+			| (p[VB2_NV_OFFS_KERNEL_MAX_ROLLFORWARD4] << 24));
 	}
 
 	/*
@@ -280,10 +283,8 @@
 		break;
 
 	case VB2_NV_KERNEL_FIELD:
-		p[VB2_NV_OFFS_KERNEL] = (uint8_t)(value);
-		p[VB2_NV_OFFS_KERNEL + 1] = (uint8_t)(value >> 8);
-		p[VB2_NV_OFFS_KERNEL + 2] = (uint8_t)(value >> 16);
-		p[VB2_NV_OFFS_KERNEL + 3] = (uint8_t)(value >> 24);
+		p[VB2_NV_OFFS_KERNEL1] = (uint8_t)(value);
+		p[VB2_NV_OFFS_KERNEL2] = (uint8_t)(value >> 8);
 		break;
 
 	case VB2_NV_DEV_BOOT_USB:
@@ -356,6 +357,13 @@
 	case VB2_NV_BATTERY_CUTOFF_REQUEST:
 		SETBIT(VB2_NV_OFFS_MISC, VB2_NV_MISC_BATTERY_CUTOFF);
 		break;
+
+	case VB2_NV_KERNEL_MAX_ROLLFORWARD:
+		p[VB2_NV_OFFS_KERNEL_MAX_ROLLFORWARD1] = (uint8_t)(value);
+		p[VB2_NV_OFFS_KERNEL_MAX_ROLLFORWARD2] = (uint8_t)(value >> 8);
+		p[VB2_NV_OFFS_KERNEL_MAX_ROLLFORWARD3] = (uint8_t)(value >> 16);
+		p[VB2_NV_OFFS_KERNEL_MAX_ROLLFORWARD4] = (uint8_t)(value >> 24);
+		break;
 	}
 
 	/*
diff --git a/firmware/2lib/include/2nvstorage.h b/firmware/2lib/include/2nvstorage.h
index 66a5fdf..e3226a5 100644
--- a/firmware/2lib/include/2nvstorage.h
+++ b/firmware/2lib/include/2nvstorage.h
@@ -42,7 +42,7 @@
 	 * 8-bit value.
 	 */
 	VB2_NV_LOCALIZATION_INDEX,
-	/* Field reserved for kernel/user-mode use; 32-bit value. */
+	/* Field reserved for kernel/user-mode use; 16-bit value. */
 	VB2_NV_KERNEL_FIELD,
 	/* Allow booting from USB in developer mode.  0=no, 1=yes. */
 	VB2_NV_DEV_BOOT_USB,
@@ -94,10 +94,15 @@
 	VB2_NV_FASTBOOT_UNLOCK_IN_FW,
 	/* Boot system when AC detected (0=no, 1=yes). */
 	VB2_NV_BOOT_ON_AC_DETECT,
-	/* Try to update the EC-RO image after updating the EC-RW image(0=no, 1=yes). */
+	/*
+	 * Try to update the EC-RO image after updating the EC-RW image
+	 * (0=no, 1=yes).
+	 */
 	VB2_NV_TRY_RO_SYNC,
         /* Cut off battery and shutdown on next boot. */
         VB2_NV_BATTERY_CUTOFF_REQUEST,
+	/* Maximum kernel version to roll forward to */
+	VB2_NV_KERNEL_MAX_ROLLFORWARD,
 };
 
 /* Set default boot in developer mode */
diff --git a/firmware/2lib/include/2nvstorage_fields.h b/firmware/2lib/include/2nvstorage_fields.h
index 018bdeb..0ed3325 100644
--- a/firmware/2lib/include/2nvstorage_fields.h
+++ b/firmware/2lib/include/2nvstorage_fields.h
@@ -29,8 +29,12 @@
 	VB2_NV_OFFS_RECOVERY_SUBCODE = 6,
 	VB2_NV_OFFS_BOOT2 = 7,
 	VB2_NV_OFFS_MISC = 8,
-	/* Offsets 9-10 are currently unused */
-	VB2_NV_OFFS_KERNEL = 11, /* 11-14; field is 32 bits */
+	VB2_NV_OFFS_KERNEL_MAX_ROLLFORWARD1 = 9, /* bits 0-7 of 32 */
+	VB2_NV_OFFS_KERNEL_MAX_ROLLFORWARD2 = 10, /* bits 8-15 of 32 */
+	VB2_NV_OFFS_KERNEL1 = 11, /* bits 0-7 of 16 */
+	VB2_NV_OFFS_KERNEL2 = 12, /* bits 8-15 of 16 */
+	VB2_NV_OFFS_KERNEL_MAX_ROLLFORWARD3 = 13, /* bits 16-23 of 32 */
+	VB2_NV_OFFS_KERNEL_MAX_ROLLFORWARD4 = 14, /* bits 24-31 of 32 */
 	/* CRC must be last field */
 	VB2_NV_OFFS_CRC = 15
  };
diff --git a/firmware/include/vboot_nvstorage.h b/firmware/include/vboot_nvstorage.h
index 8c7ff9d..ecb5d00 100644
--- a/firmware/include/vboot_nvstorage.h
+++ b/firmware/include/vboot_nvstorage.h
@@ -122,8 +122,11 @@
 	VBNV_TRY_RO_SYNC,
 	/*
 	 * Finish mode transition (if requested), perform battery cut-off and
-	 * shutdown in next boot. */
+	 * shutdown in next boot.
+	 */
 	VBNV_BATTERY_CUTOFF_REQUEST,
+	/* Maximum kernel version to roll forward to */
+	VBNV_KERNEL_MAX_ROLLFORWARD,
 } VbNvParam;
 
 /* Set default boot in developer mode */
diff --git a/firmware/lib/vboot_nvstorage.c b/firmware/lib/vboot_nvstorage.c
index 3d6a65d..9dc9dc3 100644
--- a/firmware/lib/vboot_nvstorage.c
+++ b/firmware/lib/vboot_nvstorage.c
@@ -66,7 +66,14 @@
 #define MISC_TRY_RO_SYNC		0x04
 #define MISC_BATTERY_CUTOFF_REQUEST	0x08
 
-#define KERNEL_FIELD_OFFSET         11
+#define KERNEL_MAX_ROLLFORWARD1_OFFSET     9 /* Low bits */
+#define KERNEL_MAX_ROLLFORWARD2_OFFSET    10
+#define KERNEL_MAX_ROLLFORWARD3_OFFSET    13
+#define KERNEL_MAX_ROLLFORWARD4_OFFSET    14 /* High bits */
+
+#define KERNEL_FIELD1_OFFSET         11 /* Low bits */
+#define KERNEL_FIELD2_OFFSET         12 /* Low bits */
+
 #define CRC_OFFSET                  15
 
 int VbNvSetup(VbNvContext *context)
@@ -141,10 +148,8 @@
 		return 0;
 
 	case VBNV_KERNEL_FIELD:
-		*dest = (raw[KERNEL_FIELD_OFFSET]
-			 | (raw[KERNEL_FIELD_OFFSET + 1] << 8)
-			 | (raw[KERNEL_FIELD_OFFSET + 2] << 16)
-			 | (raw[KERNEL_FIELD_OFFSET + 3] << 24));
+		*dest = (raw[KERNEL_FIELD1_OFFSET]
+			 | (raw[KERNEL_FIELD2_OFFSET] << 8));
 		return 0;
 
 	case VBNV_DEV_BOOT_USB:
@@ -237,6 +242,13 @@
 			 ?  1 : 0;
 		return 0;
 
+	case VBNV_KERNEL_MAX_ROLLFORWARD:
+		*dest = (raw[KERNEL_MAX_ROLLFORWARD1_OFFSET]
+			 | (raw[KERNEL_MAX_ROLLFORWARD2_OFFSET] << 8)
+			 | (raw[KERNEL_MAX_ROLLFORWARD3_OFFSET] << 16)
+			 | (raw[KERNEL_MAX_ROLLFORWARD4_OFFSET] << 24));
+		return 0;
+
 	default:
 		return 1;
 	}
@@ -306,10 +318,8 @@
 		break;
 
 	case VBNV_KERNEL_FIELD:
-		raw[KERNEL_FIELD_OFFSET] = (uint8_t)(value);
-		raw[KERNEL_FIELD_OFFSET + 1] = (uint8_t)(value >> 8);
-		raw[KERNEL_FIELD_OFFSET + 2] = (uint8_t)(value >> 16);
-		raw[KERNEL_FIELD_OFFSET + 3] = (uint8_t)(value >> 24);
+		raw[KERNEL_FIELD1_OFFSET] = (uint8_t)(value);
+		raw[KERNEL_FIELD2_OFFSET] = (uint8_t)(value >> 8);
 		break;
 
 	case VBNV_DEV_BOOT_USB:
@@ -469,6 +479,13 @@
 			raw[MISC_OFFSET] &= ~MISC_BATTERY_CUTOFF_REQUEST;
 		break;
 
+	case VBNV_KERNEL_MAX_ROLLFORWARD:
+		raw[KERNEL_MAX_ROLLFORWARD1_OFFSET] = (uint8_t)(value);
+		raw[KERNEL_MAX_ROLLFORWARD2_OFFSET] = (uint8_t)(value >> 8);
+		raw[KERNEL_MAX_ROLLFORWARD3_OFFSET] = (uint8_t)(value >> 16);
+		raw[KERNEL_MAX_ROLLFORWARD4_OFFSET] = (uint8_t)(value >> 24);
+		break;
+
 	default:
 		return 1;
 	}
diff --git a/host/lib/crossystem.c b/host/lib/crossystem.c
index 16ba211..216ff32 100644
--- a/host/lib/crossystem.c
+++ b/host/lib/crossystem.c
@@ -72,14 +72,14 @@
 static const char *default_boot[] = {"disk", "usb", "legacy"};
 
 /* Masks for kern_nv usage by kernel. */
-#define KERN_NV_FWUPDATE_TRIES_MASK 0x0000000F
-#define KERN_NV_BLOCK_DEVMODE_FLAG  0x00000010
-#define KERN_NV_TPM_ATTACK_FLAG     0x00000020
+#define KERN_NV_FWUPDATE_TRIES_MASK 0x000F
+#define KERN_NV_BLOCK_DEVMODE_FLAG  0x0010
+#define KERN_NV_TPM_ATTACK_FLAG     0x0020
 /* If you want to use the remaining currently-unused bits in kern_nv
  * for something kernel-y, define a new field (the way we did for
  * fwupdate_tries).  Don't just modify kern_nv directly, because that
  * makes it too easy to accidentally corrupt other sub-fields. */
-#define KERN_NV_CURRENTLY_UNUSED    0xFFFFFFC0
+#define KERN_NV_CURRENTLY_UNUSED    0xFFC0
 
 /* Return true if the FWID starts with the specified string. */
 int FwidStartsWith(const char *start)
@@ -523,6 +523,8 @@
 		value = VbGetNvStorage(VBNV_RECOVERY_SUBCODE);
 	} else if (!strcasecmp(name,"wipeout_request")) {
 		value = VbGetNvStorage(VBNV_FW_REQ_WIPEOUT);
+	} else if (!strcasecmp(name,"kernel_max_rollforward")) {
+		value = VbGetNvStorage(VBNV_KERNEL_MAX_ROLLFORWARD);
 	}
 	/* Other parameters */
 	else if (!strcasecmp(name,"cros_debug")) {
@@ -716,6 +718,8 @@
 		return VbSetNvStorage_WithBackup(VBNV_TRY_RO_SYNC, value);
 	} else if (!strcasecmp(name, "battery_cutoff_request")) {
 		return VbSetNvStorage(VBNV_BATTERY_CUTOFF_REQUEST, value);
+	} else if (!strcasecmp(name,"kernel_max_rollforward")) {
+		return VbSetNvStorage(VBNV_KERNEL_MAX_ROLLFORWARD, value);
 	}
 
 	return -1;
diff --git a/tests/vb2_nvstorage_tests.c b/tests/vb2_nvstorage_tests.c
index 2056f10..fe31a5e 100644
--- a/tests/vb2_nvstorage_tests.c
+++ b/tests/vb2_nvstorage_tests.c
@@ -41,7 +41,7 @@
 	{VB2_NV_RECOVERY_REQUEST, 0, 0x42, 0xED, "recovery request"},
 	{VB2_NV_RECOVERY_SUBCODE, 0, 0x56, 0xAC, "recovery subcode"},
 	{VB2_NV_LOCALIZATION_INDEX, 0, 0x69, 0xB0, "localization index"},
-	{VB2_NV_KERNEL_FIELD, 0, 0x12345678, 0xFEDCBA98, "kernel field"},
+	{VB2_NV_KERNEL_FIELD, 0, 0x1234, 0xFEDC, "kernel field"},
 	{VB2_NV_DEV_BOOT_USB, 0, 1, 0, "dev boot usb"},
 	{VB2_NV_DEV_BOOT_LEGACY, 0, 1, 0, "dev boot legacy"},
 	{VB2_NV_DEV_BOOT_SIGNED_ONLY, 0, 1, 0, "dev boot custom"},
@@ -56,6 +56,8 @@
 	{VB2_NV_FASTBOOT_UNLOCK_IN_FW, 0, 1, 0, "fastboot unlock in fw"},
 	{VB2_NV_BOOT_ON_AC_DETECT, 0, 1, 0, "boot on ac detect"},
 	{VB2_NV_TRY_RO_SYNC, 0, 1, 0, "try read only software sync"},
+	{VB2_NV_KERNEL_MAX_ROLLFORWARD, 0, 0x12345678, 0xFEDCBA98,
+	 "kernel max rollforward"},
 	{0, 0, 0, 0, NULL}
 };
 
diff --git a/tests/vboot_nvstorage_test.c b/tests/vboot_nvstorage_test.c
index 687048e..c461978 100644
--- a/tests/vboot_nvstorage_test.c
+++ b/tests/vboot_nvstorage_test.c
@@ -29,7 +29,7 @@
   {VBNV_TRY_B_COUNT, 0, 6, 15, "try B count"},
   {VBNV_RECOVERY_REQUEST, 0, 0x42, 0xED, "recovery request"},
   {VBNV_LOCALIZATION_INDEX, 0, 0x69, 0xB0, "localization index"},
-  {VBNV_KERNEL_FIELD, 0, 0x12345678, 0xFEDCBA98, "kernel field"},
+  {VBNV_KERNEL_FIELD, 0, 0x1234, 0xFEDC, "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"},
@@ -49,6 +49,8 @@
   {VBNV_FASTBOOT_UNLOCK_IN_FW, 0, 1, 0, "fastboot unlock in firmware"},
   {VBNV_BOOT_ON_AC_DETECT, 0, 1, 0, "boot on ac detect"},
   {VBNV_TRY_RO_SYNC, 0, 1, 0, "try read only software sync"},
+  {VBNV_KERNEL_MAX_ROLLFORWARD, 0, 0x12345678, 0xFEDCBA98,
+   "kernel max rollforward"},
   {0, 0, 0, 0, NULL}
 };
 
diff --git a/utility/crossystem.c b/utility/crossystem.c
index 62e8a92..911d585 100644
--- a/utility/crossystem.c
+++ b/utility/crossystem.c
@@ -70,7 +70,9 @@
   {"fw_prev_result", IS_STRING, "Firmware result of previous boot (vboot2)"},
   {"hwid", IS_STRING, "Hardware ID"},
   {"inside_vm", 0, "Running in a VM?"},
-  {"kern_nv", 0, "Non-volatile field for kernel use", "0x%08x"},
+  {"kern_nv", 0, "Non-volatile field for kernel use", "0x%04x"},
+  {"kernel_max_rollforward", CAN_WRITE, "Max kernel version to store into TPM",
+   "0x%08x"},
   {"kernkey_vfy", IS_STRING, "Type of verification done on kernel key block"},
   {"loc_idx", CAN_WRITE, "Localization index for firmware screens (writable)"},
   {"mainfw_act", IS_STRING, "Active main firmware"},