vboot: Use kernel max rollforward NV storage field

Kernel verification will now roll forward the minimum allowable
version in the TPM no farther than the kernel_max_rollforward setting.

Note that CL:765573 changes chromeos-setgoodkernel so it always sets
kernel_max_rollforward to 0xfffffffe when marking a kernel as good.
That ensures that firmware with this setting will behave the same for
now as existing firmware.

BUG=chromium:783997
BRANCH=none
CQ-DEPEND=CL:765573
TEST=make runtests
     Manual testing:
     crossystem tpm_kernvel --> print current kernel version in TPM
     - Resign the kernel with a higher version
     - Reboot
     - Wait a minute for chromeos-setgoodkernel to run
     crossystem kernel_max_rollforward=0
     - Reboot
     crossystem tpm_kernvel --> has not changed
     - Wait a minute for chromeos-setgoodkernel to run
     crossystem kernel_max_rollforward -> 0xfffffffe
     - Reboot
     crossystem tpm_kernvel --> has changed to the higher version

Change-Id: Ia32ecb7fa4078548cd311541ccbe120570cf1bc5
Reviewed-on: https://chromium-review.googlesource.com/765574
Commit-Ready: Randall Spangler <rspangler@chromium.org>
Tested-by: Randall Spangler <rspangler@chromium.org>
Reviewed-by: Julius Werner <jwerner@chromium.org>
Reviewed-by: Stefan Reinauer <reinauer@google.com>
diff --git a/firmware/lib/vboot_api_kernel.c b/firmware/lib/vboot_api_kernel.c
index 2cc1a88..1879b84 100644
--- a/firmware/lib/vboot_api_kernel.c
+++ b/firmware/lib/vboot_api_kernel.c
@@ -188,6 +188,7 @@
 {
 	VbSharedDataHeader *shared =
 		(VbSharedDataHeader *)cparams->shared_data_blob;
+	uint32_t max_rollforward;
 
 	/* Boot from fixed disk only */
 	VB2_DEBUG("Entering\n");
@@ -224,6 +225,25 @@
 		return rv;
 	}
 
+	/* Limit kernel version rollforward if needed */
+	if (0 == VbNvGet(&vnc, VBNV_KERNEL_MAX_ROLLFORWARD, &max_rollforward)) {
+		/*
+		 * Can't limit kernel version to less than the version
+		 * currently in the TPM.  That is, we're limiting rollforward,
+		 * not allowing rollback.
+		 */
+		if (max_rollforward < shared->kernel_version_tpm_start)
+			max_rollforward = shared->kernel_version_tpm_start;
+
+		if (shared->kernel_version_tpm > max_rollforward) {
+			VB2_DEBUG("Limiting TPM kernel version roll-forward "
+				  "to 0x%x < 0x%x\n",
+				  max_rollforward, shared->kernel_version_tpm);
+
+			shared->kernel_version_tpm = max_rollforward;
+		}
+	}
+
 	if ((shared->kernel_version_tpm > shared->kernel_version_tpm_start) &&
 	    RollbackKernelWrite(shared->kernel_version_tpm)) {
 		VB2_DEBUG("Error writing kernel versions to TPM.\n");
diff --git a/tests/vboot_api_kernel4_tests.c b/tests/vboot_api_kernel4_tests.c
index 5965070..e00c928 100644
--- a/tests/vboot_api_kernel4_tests.c
+++ b/tests/vboot_api_kernel4_tests.c
@@ -53,6 +53,7 @@
 
 	memset(&vnc, 0, sizeof(vnc));
 	VbNvSetup(&vnc);
+	VbNvSet(&vnc, VBNV_KERNEL_MAX_ROLLFORWARD, 0xffffffff);
 	VbNvTeardown(&vnc);                   /* So CRC gets generated */
 
 	memset(&shared_data, 0, sizeof(shared_data));
@@ -88,7 +89,6 @@
 
 uint32_t RollbackKernelWrite(uint32_t version)
 {
-	TEST_EQ(version, new_version, "RollbackKernelWrite new version");
 	rkr_version = version;
 	return rkw_retval;
 }
@@ -150,6 +150,7 @@
 {
 	ResetMocks();
 	test_slk(0, 0, "Normal");
+	TEST_EQ(rkr_version, 0x10002, "  version");
 
 	/*
 	 * If shared->flags doesn't ask for software sync, we won't notice
@@ -183,6 +184,20 @@
 	TEST_EQ(rkr_version, 0x10002, "  version");
 
 	ResetMocks();
+	VbNvSet(&vnc, VBNV_KERNEL_MAX_ROLLFORWARD, 0x30005);
+	VbNvTeardown(&vnc);
+	new_version = 0x40006;
+	test_slk(0, 0, "Limit max roll forward");
+	TEST_EQ(rkr_version, 0x30005, "  version");
+
+	ResetMocks();
+	VbNvSet(&vnc, VBNV_KERNEL_MAX_ROLLFORWARD, 0x10001);
+	VbNvTeardown(&vnc);
+	new_version = 0x40006;
+	test_slk(0, 0, "Max roll forward can't rollback");
+	TEST_EQ(rkr_version, 0x10002, "  version");
+
+	ResetMocks();
 	vbboot_retval = VBERROR_INVALID_KERNEL_FOUND;
 	VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, 123);
 	VbNvTeardown(&vnc);