vboot: Add ui for setting vendor data in VPD

BUG=b:124297157
TEST=make runtest
     test on device
BRANCH=none
CQ-DEPEND=CL:1466822

Change-Id: Ic3b1b502b1aff14a795397da3024f8a12eb04775
Reviewed-on: https://chromium-review.googlesource.com/1466290
Commit-Ready: Mathew King <mathewk@chromium.org>
Tested-by: Mathew King <mathewk@chromium.org>
Reviewed-by: Julius Werner <jwerner@chromium.org>
Reviewed-by: Mathew King <mathewk@chromium.org>
diff --git a/Makefile b/Makefile
index 4fa4423..5b6ea76 100644
--- a/Makefile
+++ b/Makefile
@@ -163,6 +163,7 @@
 # FIRMWARE_ARCH not defined; assuming local compile.
 CC ?= gcc
 CFLAGS += -DCHROMEOS_ENVIRONMENT -Wall ${WERROR} ${DEBUG_FLAGS}
+CHROMEOS_ENVIRONMENT = 1
 endif
 
 ifneq (${DEBUG},)
@@ -411,6 +412,14 @@
 	firmware/lib/tpm_lite/mocked_tlcl.c
 endif
 
+ifneq (${VENDOR_DATA_LENGTH},)
+CFLAGS += -DVENDOR_DATA_LENGTH=${VENDOR_DATA_LENGTH}
+else ifeq (${CHROMEOS_ENVIRONMENT},1)
+CFLAGS += -DVENDOR_DATA_LENGTH=4
+else
+CFLAGS += -DVENDOR_DATA_LENGTH=0
+endif
+
 ifeq (${FIRMWARE_ARCH},)
 # Include BIOS stubs in the firmware library when compiling for host
 # TODO: split out other stub funcs too
diff --git a/firmware/2lib/include/2api.h b/firmware/2lib/include/2api.h
index 90647d5..34eb779 100644
--- a/firmware/2lib/include/2api.h
+++ b/firmware/2lib/include/2api.h
@@ -177,6 +177,9 @@
 	 * data provided by the caller could be uninitialized.)
 	 */
 	VB2_CONTEXT_NVDATA_V2 = (1 << 18),
+
+	/* Allow vendor data to be set via the vendor data ui. */
+	VB2_CONTEXT_VENDOR_DATA_SETTABLE = (1 << 19),
 };
 
 /*
diff --git a/firmware/include/vboot_api.h b/firmware/include/vboot_api.h
index 0d3addf..860a5fc 100644
--- a/firmware/include/vboot_api.h
+++ b/firmware/include/vboot_api.h
@@ -138,6 +138,8 @@
 	VBERROR_PERIPHERAL_BUSY               = 0x10030,
 	/* Error reading or writing Alt OS flags to TPM */
 	VBERROR_TPM_ALT_OS                    = 0x10031,
+	/* Error writing VPD */
+	VBERROR_VPD_WRITE                     = 0x10032,
 
 	/* VbExEcGetExpectedRWHash() may return the following codes */
 	/* Compute expected RW hash from the EC image; BIOS doesn't have it */
@@ -274,7 +276,7 @@
 	void *kernel_buffer;
 	/* Size of kernel buffer in bytes */
 	uint32_t kernel_buffer_size;
-	/* input flags.  Currently used for detachables */
+	/* input flags. */
 	uint32_t inflags;
 
 	/*
@@ -306,6 +308,11 @@
  */
 #define VB_SALK_INFLAGS_ENABLE_DETACHABLE_UI (1 << 0)
 
+/* Flag to indicate that the vendor data is not set and the vendor data
+ * UI should be enabled.
+ */
+#define VB_SALK_INFLAGS_VENDOR_DATA_SETTABLE (1 << 1)
+
 /**
  * Select and loads the kernel.
  *
@@ -671,9 +678,32 @@
 	VB_SCREEN_ALT_FW_PICK = 0x212,
 	/* Alt firmware menu screen (for detachable UI ) */
 	VB_SCREEN_ALT_FW_MENU = 0x213,
+	/* Set vendor data menu screen */
+	VB_SCREEN_SET_VENDOR_DATA = 0x214,
+	/* Confirm vendor data menu screen */
+	VB_SCREEN_CONFIRM_VENDOR_DATA = 0x215,
 };
 
 /**
+ * Extra data needed when displaying vendor data screens
+ */
+typedef struct VbVendorData
+{
+	/* Current state of the the vendor data input */
+	const char *input_text;
+} VbVendorData;
+
+/**
+ * Extra data that may be used when displaying a screen
+ */
+typedef struct VbScreenData
+{
+	union {
+		VbVendorData vendor_data;
+	};
+} VbScreenData;
+
+/**
  * Display a predefined screen; see VB_SCREEN_* for valid screens.
  *
  * This is a backup method of screen display, intended for use if the GBB does
@@ -681,7 +711,8 @@
  * to be simple ASCII text such as "NO GOOD" or "INSERT"; these screens should
  * only be seen during development.
  */
-VbError_t VbExDisplayScreen(uint32_t screen_type, uint32_t locale);
+VbError_t VbExDisplayScreen(uint32_t screen_type, uint32_t locale,
+			    const VbScreenData *data);
 
 /**
  * Display a predefined menu screen; see VB_SCREEN_* for valid screens.
@@ -714,6 +745,17 @@
  */
 VbError_t VbExDisplayDebugInfo(const char *info_str);
 
+/**
+ * Write vendor data to read-only VPD
+ *
+ * @param vendor_data_value   The value of vendor data to write to VPD. The
+ *                            string length will be exactly VENDOR_DATA_LENGTH
+ *                            characters and null-terminated.
+ *
+ * @return VBERROR_SUCCESS or error code on error.
+ */
+VbError_t VbExSetVendorData(const char *vendor_data_value);
+
 /*****************************************************************************/
 /* Keyboard and switches */
 
@@ -724,6 +766,7 @@
 enum VbKeyCode_t {
 	VB_KEY_ENTER = '\r',
 	VB_KEY_ESC = 0x1b,
+	VB_KEY_BACKSPACE = 0x8,
 	VB_KEY_UP = 0x100,
 	VB_KEY_DOWN = 0x101,
 	VB_KEY_LEFT = 0x102,
diff --git a/firmware/lib/ec_sync_all.c b/firmware/lib/ec_sync_all.c
index 36d9e72..6c3fe74 100644
--- a/firmware/lib/ec_sync_all.c
+++ b/firmware/lib/ec_sync_all.c
@@ -79,7 +79,7 @@
 	/* Display the wait screen if we need it */
 	if (need_wait_screen) {
 		VB2_DEBUG("EC is slow. Show WAIT screen.\n");
-		VbDisplayScreen(ctx, VB_SCREEN_WAIT, 0);
+		VbDisplayScreen(ctx, VB_SCREEN_WAIT, 0, NULL);
 	}
 
 	/* Phase 2; Applies update and/or jumps to the correct EC image */
diff --git a/firmware/lib/include/sysincludes.h b/firmware/lib/include/sysincludes.h
index 7a9139c..28aa96c 100644
--- a/firmware/lib/include/sysincludes.h
+++ b/firmware/lib/include/sysincludes.h
@@ -15,6 +15,7 @@
 #ifndef VBOOT_REFERENCE_SYSINCLUDES_H_
 #define VBOOT_REFERENCE_SYSINCLUDES_H_
 
+#include <ctype.h>
 #include <inttypes.h>  /* For PRIu64 */
 #include <stddef.h>
 #include <stdint.h>
diff --git a/firmware/lib/include/vboot_display.h b/firmware/lib/include/vboot_display.h
index 20066df..4c8df0e 100644
--- a/firmware/lib/include/vboot_display.h
+++ b/firmware/lib/include/vboot_display.h
@@ -10,7 +10,8 @@
 
 struct vb2_context;
 
-VbError_t VbDisplayScreen(struct vb2_context *ctx, uint32_t screen, int force);
+VbError_t VbDisplayScreen(struct vb2_context *ctx, uint32_t screen, int force,
+			  const VbScreenData *data);
 VbError_t VbDisplayMenu(struct vb2_context *ctx,
 			uint32_t screen, int force, uint32_t selected_index,
 			uint32_t disabled_idx_mask);
diff --git a/firmware/lib/vboot_api_kernel.c b/firmware/lib/vboot_api_kernel.c
index 516b1e4..17b3b7b 100644
--- a/firmware/lib/vboot_api_kernel.c
+++ b/firmware/lib/vboot_api_kernel.c
@@ -432,6 +432,9 @@
 			retval = VbBootRecovery(&ctx);
 		VbExEcEnteringMode(0, VB_EC_RECOVERY);
 	} else if (ctx.flags & VB2_CONTEXT_DEVELOPER_MODE) {
+		if (kparams->inflags & VB_SALK_INFLAGS_VENDOR_DATA_SETTABLE)
+			ctx.flags |= VB2_CONTEXT_VENDOR_DATA_SETTABLE;
+
 		/* Developer boot.  This has UI. */
 		if (kparams->inflags & VB_SALK_INFLAGS_ENABLE_DETACHABLE_UI)
 			retval = VbBootDeveloperMenu(&ctx);
diff --git a/firmware/lib/vboot_display.c b/firmware/lib/vboot_display.c
index c760772..a17d6dd 100644
--- a/firmware/lib/vboot_display.c
+++ b/firmware/lib/vboot_display.c
@@ -35,7 +35,8 @@
 	return 0;
 }
 
-VbError_t VbDisplayScreen(struct vb2_context *ctx, uint32_t screen, int force)
+VbError_t VbDisplayScreen(struct vb2_context *ctx, uint32_t screen, int force,
+			  const VbScreenData *data)
 {
 	uint32_t locale;
 	VbError_t rv;
@@ -47,7 +48,7 @@
 	/* Read the locale last saved */
 	locale = vb2_nv_get(ctx, VB2_NV_LOCALIZATION_INDEX);
 
-	rv = VbExDisplayScreen(screen, locale);
+	rv = VbExDisplayScreen(screen, locale, data);
 
 	if (rv == VBERROR_SUCCESS)
 		/* Keep track of the currently displayed screen */
@@ -464,12 +465,13 @@
 #endif
 
 		/* Force redraw of current screen */
-		return VbDisplayScreen(ctx, disp_current_screen, 1);
+		return VbDisplayScreen(ctx, disp_current_screen, 1, NULL);
 	}
 
 	if (0 == memcmp(MagicBuffer, MAGIC_WORD, MAGIC_WORD_LEN)) {
 		if (VBEASTEREGG)
-			(void)VbDisplayScreen(ctx, disp_current_screen, 1);
+			(void)VbDisplayScreen(ctx, disp_current_screen, 1,
+					      NULL);
 	}
 
 	return VBERROR_SUCCESS;
diff --git a/firmware/lib/vboot_ui.c b/firmware/lib/vboot_ui.c
index 57e546a..dea6e03 100644
--- a/firmware/lib/vboot_ui.c
+++ b/firmware/lib/vboot_ui.c
@@ -176,7 +176,7 @@
 {
 	int active = 1;
 
-	VbDisplayScreen(ctx, VB_SCREEN_ALT_FW_PICK, 0);
+	VbDisplayScreen(ctx, VB_SCREEN_ALT_FW_PICK, 0, NULL);
 
 	/* We'll loop until the user decides what to do */
 	do {
@@ -217,11 +217,152 @@
 	} while (active);
 
 	/* Back to developer screen */
-	VbDisplayScreen(ctx, VB_SCREEN_DEVELOPER_WARNING, 0);
+	VbDisplayScreen(ctx, VB_SCREEN_DEVELOPER_WARNING, 0, NULL);
 
 	return 0;
 }
 
+/*
+ * Prompt the user to enter the vendor data
+ */
+VbError_t vb2_enter_vendor_data_ui(struct vb2_context *ctx, char *data_value)
+{
+	int len = 0;
+	VbScreenData data = {
+		.vendor_data = { data_value }
+	};
+
+	data_value[0] = '\0';
+	VbDisplayScreen(ctx, VB_SCREEN_SET_VENDOR_DATA, 1, &data);
+
+	/* We'll loop until the user decides what to do */
+	do {
+		uint32_t key = VbExKeyboardRead();
+
+		if (VbWantShutdown(ctx, key)) {
+			VB2_DEBUG("Vendor Data UI - shutdown requested!\n");
+			return VBERROR_SHUTDOWN_REQUESTED;
+		}
+		switch (key) {
+		case 0:
+			/* nothing pressed */
+			break;
+		case VB_KEY_ESC:
+			/* Escape pressed - return to developer screen */
+			VB2_DEBUG("Vendor Data UI - user pressed Esc: "
+				  "exit to Developer screen\n");
+			data_value[0] = '\0';
+			return VBERROR_SUCCESS;
+		case 'a'...'z':
+			key = toupper(key);
+		case '0'...'9':
+		case 'A'...'Z':
+			if (len < VENDOR_DATA_LENGTH) {
+				data_value[len++] = key;
+				data_value[len] = '\0';
+				VbDisplayScreen(ctx, VB_SCREEN_SET_VENDOR_DATA,
+						1, &data);
+			} else {
+				vb2_error_beep(VB_BEEP_NOT_ALLOWED);
+			}
+
+			VB2_DEBUG("Vendor Data UI - vendor_data: %s\n",
+				  data_value);
+			break;
+		case VB_KEY_BACKSPACE:
+			if (len > 0) {
+				data_value[--len] = '\0';
+				VbDisplayScreen(ctx, VB_SCREEN_SET_VENDOR_DATA,
+						1, &data);
+			}
+
+			VB2_DEBUG("Vendor Data UI - vendor_data: %s\n",
+				  data_value);
+			break;
+		case VB_KEY_ENTER:
+			if (len == VENDOR_DATA_LENGTH) {
+				/* Enter pressed - confirm input */
+				VB2_DEBUG("Vendor Data UI - user pressed "
+					  "Enter: confirm vendor data\n");
+				return VBERROR_SUCCESS;
+			} else {
+				vb2_error_beep(VB_BEEP_NOT_ALLOWED);
+			}
+			break;
+		default:
+			VB2_DEBUG("Vendor Data UI - pressed key %d\n", key);
+			break;
+		}
+		VbExSleepMs(DEV_KEY_DELAY);
+	} while (1);
+
+	return VBERROR_SUCCESS;
+}
+
+/*
+ * User interface for setting the vendor data in VPD
+ */
+VbError_t vb2_vendor_data_ui(struct vb2_context *ctx)
+{
+	char data_value[VENDOR_DATA_LENGTH + 1];
+	VbScreenData data = {
+		.vendor_data = { data_value }
+	};
+
+	VbError_t ret = vb2_enter_vendor_data_ui(ctx, data_value);
+
+	if (ret)
+		return ret;
+
+	/* Vendor data was not entered just return */
+	if (data_value[0] == '\0')
+		return VBERROR_SUCCESS;
+
+	VbDisplayScreen(ctx, VB_SCREEN_CONFIRM_VENDOR_DATA, 1, &data);
+	/* We'll loop until the user decides what to do */
+	do {
+		uint32_t key = VbExKeyboardRead();
+
+		if (VbWantShutdown(ctx, key)) {
+			VB2_DEBUG("Vendor Data UI - shutdown requested!\n");
+			return VBERROR_SHUTDOWN_REQUESTED;
+		}
+		switch (key) {
+		case 0:
+			/* nothing pressed */
+			break;
+		case VB_KEY_ESC:
+			/* Escape pressed - return to developer screen */
+			VB2_DEBUG("Vendor Data UI - user pressed Esc: "
+				  "exit to Developer screen\n");
+			return VBERROR_SUCCESS;
+		case VB_KEY_ENTER:
+			/* Enter pressed - write vendor data */
+			VB2_DEBUG("Vendor Data UI - user pressed Enter: "
+				  "write vendor data (%s) to VPD\n",
+				  data_value);
+			ret = VbExSetVendorData(data_value);
+
+			if (ret == VBERROR_SUCCESS) {
+				vb2_nv_set(ctx, VB2_NV_DISABLE_DEV_REQUEST, 1);
+				return VBERROR_REBOOT_REQUIRED;
+			} else {
+				/*
+				 * TODO(mathewk): If setting vendor data fails
+				 * we should give helpful feedback to the user
+				 */
+				return ret;
+			}
+		default:
+			VB2_DEBUG("Vendor Data UI - pressed key %d\n", key);
+			break;
+		}
+		VbExSleepMs(DEV_KEY_DELAY);
+	} while (1);
+
+	return VBERROR_SUCCESS;
+}
+
 static const char dev_disable_msg[] =
 	"Developer mode is disabled on this device by system policy.\n"
 	"For more information, see http://dev.chromium.org/chromium-os/fwmp\n"
@@ -279,7 +420,8 @@
 	/* If dev mode is disabled, only allow TONORM */
 	while (disable_dev_boot) {
 		VB2_DEBUG("dev_disable_boot is set\n");
-		VbDisplayScreen(ctx, VB_SCREEN_DEVELOPER_TO_NORM, 0);
+		VbDisplayScreen(ctx,
+				VB_SCREEN_DEVELOPER_TO_NORM, 0, NULL);
 		VbExDisplayDebugInfo(dev_disable_msg);
 
 		/* Ignore space in VbUserConfirms()... */
@@ -287,7 +429,8 @@
 		case 1:
 			VB2_DEBUG("leaving dev-mode\n");
 			vb2_nv_set(ctx, VB2_NV_DISABLE_DEV_REQUEST, 1);
-			VbDisplayScreen(ctx, VB_SCREEN_TO_NORM_CONFIRMED, 0);
+			VbDisplayScreen(ctx,
+				VB_SCREEN_TO_NORM_CONFIRMED, 0, NULL);
 			VbExSleepMs(5000);
 			return VBERROR_REBOOT_REQUIRED;
 		case -1:
@@ -300,7 +443,7 @@
 	}
 
 	/* Show the dev mode warning screen */
-	VbDisplayScreen(ctx, VB_SCREEN_DEVELOPER_WARNING, 0);
+	VbDisplayScreen(ctx, VB_SCREEN_DEVELOPER_WARNING, 0, NULL);
 
 	/* Initialize audio/delay context */
 	vb2_audio_start(ctx);
@@ -342,7 +485,8 @@
 					break;
 				}
 				VbDisplayScreen(ctx,
-						VB_SCREEN_DEVELOPER_TO_NORM, 0);
+					VB_SCREEN_DEVELOPER_TO_NORM,
+					0, NULL);
 				/* Ignore space in VbUserConfirms()... */
 				switch (VbUserConfirms(ctx, 0)) {
 				case 1:
@@ -350,7 +494,8 @@
 					vb2_nv_set(ctx, VB2_NV_DISABLE_DEV_REQUEST,
 						1);
 					VbDisplayScreen(ctx,
-						VB_SCREEN_TO_NORM_CONFIRMED, 0);
+						VB_SCREEN_TO_NORM_CONFIRMED,
+						0, NULL);
 					VbExSleepMs(5000);
 					return VBERROR_REBOOT_REQUIRED;
 				case -1:
@@ -360,7 +505,8 @@
 					/* Stay in dev-mode */
 					VB2_DEBUG("stay in dev-mode\n");
 					VbDisplayScreen(ctx,
-						VB_SCREEN_DEVELOPER_WARNING, 0);
+						VB_SCREEN_DEVELOPER_WARNING,
+						0, NULL);
 					/* Start new countdown */
 					vb2_audio_start(ctx);
 				}
@@ -395,6 +541,35 @@
 				vb2_error_no_altfw();
 			}
 			break;
+		case VB_KEY_CTRL('S'):
+			if (VENDOR_DATA_LENGTH == 0)
+				break;
+			/*
+			 * Only show the vendor data ui if it is tag is settable
+			 */
+			if (ctx->flags & VB2_CONTEXT_VENDOR_DATA_SETTABLE) {
+				int ret;
+
+				VB2_DEBUG("VbBootDeveloper() - user pressed "
+					  "Ctrl+S; Try set vendor data\n");
+
+				ret = vb2_vendor_data_ui(ctx);
+				if (ret) {
+					return ret;
+				} else {
+					/* Show dev mode warning screen again */
+					VbDisplayScreen(ctx,
+						VB_SCREEN_DEVELOPER_WARNING,
+						0, NULL);
+				}
+			} else {
+				vb2_error_notify(
+					"WARNING: Vendor data cannot be "
+					"changed because it is already set.\n",
+					NULL,
+					VB_BEEP_NOT_ALLOWED);
+			}
+			break;
 		case VB_KEY_CTRL_ENTER:
 			/*
 			 * The Ctrl-Enter is special for Lumpy test purpose;
@@ -418,13 +593,14 @@
 				 * Clear the screen to show we get the Ctrl+U
 				 * key press.
 				 */
-				VbDisplayScreen(ctx, VB_SCREEN_BLANK, 0);
+				VbDisplayScreen(ctx, VB_SCREEN_BLANK, 0, NULL);
 				if (VBERROR_SUCCESS == VbTryUsb(ctx)) {
 					return VBERROR_SUCCESS;
 				} else {
 					/* Show dev mode warning screen again */
 					VbDisplayScreen(ctx,
-						VB_SCREEN_DEVELOPER_WARNING, 0);
+						VB_SCREEN_DEVELOPER_WARNING,
+						0, NULL);
 				}
 			}
 			break;
@@ -467,7 +643,7 @@
 {
 	vb2_init_ui();
 	VbError_t retval = vb2_developer_ui(ctx);
-	VbDisplayScreen(ctx, VB_SCREEN_BLANK, 0);
+	VbDisplayScreen(ctx, VB_SCREEN_BLANK, 0, NULL);
 	return retval;
 }
 
@@ -509,7 +685,7 @@
 		 */
 		vb2_nv_commit(ctx);
 
-		VbDisplayScreen(ctx, VB_SCREEN_OS_BROKEN, 0);
+		VbDisplayScreen(ctx, VB_SCREEN_OS_BROKEN, 0, NULL);
 		VB2_DEBUG("VbBootRecovery() waiting for manual recovery\n");
 		while (1) {
 			key = VbExKeyboardRead();
@@ -541,7 +717,7 @@
 		VbDisplayScreen(ctx, VBERROR_NO_DISK_FOUND == retval ?
 				VB_SCREEN_RECOVERY_INSERT :
 				VB_SCREEN_RECOVERY_NO_GOOD,
-				0);
+				0, NULL);
 
 		/*
 		 * Scan keyboard more frequently than media, since x86
@@ -578,7 +754,8 @@
 
 				/* Ask the user to confirm entering dev-mode */
 				VbDisplayScreen(ctx,
-						VB_SCREEN_RECOVERY_TO_DEV, 0);
+						VB_SCREEN_RECOVERY_TO_DEV,
+						0, NULL);
 				/* SPACE means no... */
 				uint32_t vbc_flags =
 					VB_CONFIRM_SPACE_MEANS_NO |
@@ -622,6 +799,6 @@
 {
 	vb2_init_ui();
 	VbError_t retval = recovery_ui(ctx);
-	VbDisplayScreen(ctx, VB_SCREEN_BLANK, 0);
+	VbDisplayScreen(ctx, VB_SCREEN_BLANK, 0, NULL);
 	return retval;
 }
diff --git a/firmware/lib/vboot_ui_menu.c b/firmware/lib/vboot_ui_menu.c
index e269d23..380dd3e 100644
--- a/firmware/lib/vboot_ui_menu.c
+++ b/firmware/lib/vboot_ui_menu.c
@@ -71,7 +71,7 @@
 /* Flash the screen to black to catch user awareness, then redraw menu. */
 static void vb2_flash_screen(struct vb2_context *ctx)
 {
-	VbDisplayScreen(ctx, VB_SCREEN_BLANK, 0);
+	VbDisplayScreen(ctx, VB_SCREEN_BLANK, 0, NULL);
 	VbExSleepMs(50);
 	vb2_draw_current_screen(ctx);
 }
@@ -808,7 +808,7 @@
 	if (VBERROR_SUCCESS != retval)
 		return retval;
 	retval = vb2_developer_menu(ctx);
-	VbDisplayScreen(ctx, VB_SCREEN_BLANK, 0);
+	VbDisplayScreen(ctx, VB_SCREEN_BLANK, 0, NULL);
 	return retval;
 }
 
@@ -913,6 +913,6 @@
 		retval = recovery_ui(ctx);
 	else
 		retval = broken_ui(ctx);
-	VbDisplayScreen(ctx, VB_SCREEN_BLANK, 0);
+	VbDisplayScreen(ctx, VB_SCREEN_BLANK, 0, NULL);
 	return retval;
 }
diff --git a/firmware/stub/vboot_api_stub.c b/firmware/stub/vboot_api_stub.c
index effa237..599b80c 100644
--- a/firmware/stub/vboot_api_stub.c
+++ b/firmware/stub/vboot_api_stub.c
@@ -26,7 +26,8 @@
 	return VBERROR_SUCCESS;
 }
 
-VbError_t VbExDisplayScreen(uint32_t screen_type, uint32_t locale)
+VbError_t VbExDisplayScreen(uint32_t screen_type, uint32_t locale,
+			    const VbScreenData *data)
 {
 	return VBERROR_SUCCESS;
 }
@@ -173,3 +174,8 @@
 {
 	return 0;
 }
+
+VbError_t VbExSetVendorData(const char *vendor_data_value)
+{
+	return 0;
+}
diff --git a/tests/ec_sync_tests.c b/tests/ec_sync_tests.c
index 4b01b64..a971b96 100644
--- a/tests/ec_sync_tests.c
+++ b/tests/ec_sync_tests.c
@@ -202,7 +202,8 @@
 	return update_retval;
 }
 
-VbError_t VbDisplayScreen(struct vb2_context *ctx, uint32_t screen, int force)
+VbError_t VbDisplayScreen(struct vb2_context *ctx, uint32_t screen, int force,
+			  const VbScreenData *data)
 {
 	if (screens_count < ARRAY_SIZE(screens_displayed))
 		screens_displayed[screens_count++] = screen;
diff --git a/tests/vboot_api_devmode_tests.c b/tests/vboot_api_devmode_tests.c
index 31428a4..8aaa4b1 100644
--- a/tests/vboot_api_devmode_tests.c
+++ b/tests/vboot_api_devmode_tests.c
@@ -235,7 +235,8 @@
 	return beep_return;
 }
 
-VbError_t VbExDisplayScreen(uint32_t screen_type, uint32_t locale)
+VbError_t VbExDisplayScreen(uint32_t screen_type, uint32_t locale,
+			    const VbScreenData *data)
 {
 	switch(screen_type) {
 		case VB_SCREEN_BLANK:
diff --git a/tests/vboot_api_kernel2_tests.c b/tests/vboot_api_kernel2_tests.c
index 9b88759..9224725 100644
--- a/tests/vboot_api_kernel2_tests.c
+++ b/tests/vboot_api_kernel2_tests.c
@@ -41,7 +41,7 @@
 static int trust_ec;
 static int virtdev_set;
 static uint32_t virtdev_retval;
-static uint32_t mock_keypress[8];
+static uint32_t mock_keypress[16];
 static uint32_t mock_keyflags[8];
 static uint32_t mock_keypress_count;
 static uint32_t mock_switches[8];
@@ -52,6 +52,9 @@
 static uint32_t mock_num_disks[8];
 static uint32_t mock_num_disks_count;
 
+static char set_vendor_data[32];
+static int set_vendor_data_called;
+
 extern enum VbEcBootMode_t VbGetMode(void);
 extern struct RollbackSpaceFwmp *VbApiKernelGetFwmp(void);
 
@@ -83,6 +86,7 @@
 	trust_ec = 0;
 	virtdev_set = 0;
 	virtdev_retval = 0;
+	set_vendor_data_called = 0;
 
 	memset(screens_displayed, 0, sizeof(screens_displayed));
 	screens_count = 0;
@@ -193,7 +197,8 @@
 	return vbtlk_retval + get_info_flags;
 }
 
-VbError_t VbDisplayScreen(struct vb2_context *ctx, uint32_t screen, int force)
+VbError_t VbDisplayScreen(struct vb2_context *ctx, uint32_t screen, int force,
+			  const VbScreenData *data)
 {
 	if (screens_count < ARRAY_SIZE(screens_displayed))
 		screens_displayed[screens_count++] = screen;
@@ -207,6 +212,14 @@
 	return virtdev_retval;
 }
 
+VbError_t VbExSetVendorData(const char *vendor_data_value)
+{
+	set_vendor_data_called = 1;
+	strncpy(set_vendor_data, vendor_data_value, sizeof(set_vendor_data));
+
+	return VBERROR_SUCCESS;
+}
+
 /* Tests */
 
 static void VbUserConfirmsTest(void)
@@ -590,6 +603,174 @@
 	vbtlk_retval = VBERROR_SUCCESS - VB_DISK_FLAG_REMOVABLE;
 	TEST_EQ(VbBootDeveloper(&ctx), 0, "Ctrl+U force USB");
 
+	/* Ctrl+S set vendor data and reboot */
+	ResetMocks();
+	ctx.flags |= VB2_CONTEXT_VENDOR_DATA_SETTABLE;
+	mock_keypress[0] = VB_KEY_CTRL('S');
+	mock_keypress[1] = '4';
+	mock_keypress[2] = '3';
+	mock_keypress[3] = '2';
+	mock_keypress[4] = '1';
+	mock_keypress[5] = VB_KEY_ENTER; // Set vendor data
+	mock_keypress[6] = VB_KEY_ENTER; // Confirm vendor data
+	TEST_EQ(VbBootDeveloper(&ctx), VBERROR_REBOOT_REQUIRED,
+		"Ctrl+S set vendor data and reboot");
+	TEST_EQ(set_vendor_data_called, 1, "  VbExSetVendorData() called");
+	TEST_STR_EQ(set_vendor_data, "4321", "  Vendor data correct");
+
+	/* Ctrl+S extra keys ignored */
+	ResetMocks();
+	ctx.flags |= VB2_CONTEXT_VENDOR_DATA_SETTABLE;
+	mock_keypress[0] = VB_KEY_CTRL('S');
+	mock_keypress[1] = '4';
+	mock_keypress[2] = '3';
+	mock_keypress[3] = '2';
+	mock_keypress[4] = '1';
+	mock_keypress[5] = '5';
+	mock_keypress[6] = VB_KEY_ENTER; // Set vendor data
+	mock_keypress[7] = VB_KEY_ENTER; // Confirm vendor data
+	TEST_EQ(VbBootDeveloper(&ctx), VBERROR_REBOOT_REQUIRED,
+		"Ctrl+S extra keys ignored");
+	TEST_EQ(set_vendor_data_called, 1, "  VbExSetVendorData() called");
+	TEST_STR_EQ(set_vendor_data, "4321", "  Vendor data correct");
+
+	/* Ctrl+S converts case */
+	ResetMocks();
+	ctx.flags |= VB2_CONTEXT_VENDOR_DATA_SETTABLE;
+	mock_keypress[0] = VB_KEY_CTRL('S');
+	mock_keypress[1] = 'a';
+	mock_keypress[2] = 'B';
+	mock_keypress[3] = 'Y';
+	mock_keypress[4] = 'z';
+	mock_keypress[5] = VB_KEY_ENTER; // Set vendor data
+	mock_keypress[6] = VB_KEY_ENTER; // Confirm vendor data
+	TEST_EQ(VbBootDeveloper(&ctx), VBERROR_REBOOT_REQUIRED,
+		"Ctrl+S converts case");
+	TEST_EQ(set_vendor_data_called, 1, "  VbExSetVendorData() called");
+	TEST_STR_EQ(set_vendor_data, "ABYZ", "  Vendor data correct");
+
+	/* Ctrl+S backspace works */
+	ResetMocks();
+	ctx.flags |= VB2_CONTEXT_VENDOR_DATA_SETTABLE;
+	mock_keypress[0] = VB_KEY_CTRL('S');
+	mock_keypress[1] = 'A';
+	mock_keypress[2] = 'B';
+	mock_keypress[3] = 'C';
+	mock_keypress[4] = VB_KEY_BACKSPACE;
+	mock_keypress[5] = VB_KEY_BACKSPACE;
+	mock_keypress[6] = '3';
+	mock_keypress[7] = '2';
+	mock_keypress[8] = '1';
+	mock_keypress[9] = VB_KEY_ENTER; // Set vendor data
+	mock_keypress[10] = VB_KEY_ENTER; // Confirm vendor data
+	TEST_EQ(VbBootDeveloper(&ctx), VBERROR_REBOOT_REQUIRED,
+		"Ctrl+S backspace works");
+	TEST_EQ(set_vendor_data_called, 1, "  VbExSetVendorData() called");
+	TEST_STR_EQ(set_vendor_data, "A321", "  Vendor data correct");
+
+	/* Ctrl+S invalid chars don't print */
+	ResetMocks();
+	ctx.flags |= VB2_CONTEXT_VENDOR_DATA_SETTABLE;
+	mock_keypress[0] = VB_KEY_CTRL('S');
+	mock_keypress[1] = '4';
+	mock_keypress[2] = '-';
+	mock_keypress[3] = '^';
+	mock_keypress[4] = '&';
+	mock_keypress[5] = '$';
+	mock_keypress[6] = '.';
+	mock_keypress[7] = '3';
+	mock_keypress[8] = '2';
+	mock_keypress[9] = '1';
+	mock_keypress[10] = VB_KEY_ENTER; // Set vendor data
+	mock_keypress[11] = VB_KEY_ENTER; // Confirm vendor data
+	TEST_EQ(VbBootDeveloper(&ctx), VBERROR_REBOOT_REQUIRED,
+		"Ctrl+S invalid chars don't print");
+	TEST_EQ(set_vendor_data_called, 1, "  VbExSetVendorData() called");
+	TEST_STR_EQ(set_vendor_data, "4321", "  Vendor data correct");
+
+	/* Ctrl+S invalid chars don't print with backspace */
+	ResetMocks();
+	ctx.flags |= VB2_CONTEXT_VENDOR_DATA_SETTABLE;
+	mock_keypress[0] = VB_KEY_CTRL('S');
+	mock_keypress[1] = '4';
+	mock_keypress[2] = '-';
+	mock_keypress[3] = VB_KEY_BACKSPACE; // Should delete 4
+	mock_keypress[4] = '3';
+	mock_keypress[5] = '2';
+	mock_keypress[6] = '1';
+	mock_keypress[7] = '0';
+	mock_keypress[8] = VB_KEY_ENTER; // Set vendor data
+	mock_keypress[9] = VB_KEY_ENTER; // Confirm vendor data
+	TEST_EQ(VbBootDeveloper(&ctx), VBERROR_REBOOT_REQUIRED,
+		"Ctrl+S invalid chars don't print with backspace");
+	TEST_EQ(set_vendor_data_called, 1, "  VbExSetVendorData() called");
+	TEST_STR_EQ(set_vendor_data, "3210", "  Vendor data correct");
+
+	/* Ctrl+S backspace only doesn't underrun */
+	ResetMocks();
+	ctx.flags |= VB2_CONTEXT_VENDOR_DATA_SETTABLE;
+	mock_keypress[0] = VB_KEY_CTRL('S');
+	mock_keypress[1] = 'A';
+	mock_keypress[2] = VB_KEY_BACKSPACE;
+	mock_keypress[3] = VB_KEY_BACKSPACE;
+	mock_keypress[4] = '4';
+	mock_keypress[5] = '3';
+	mock_keypress[6] = '2';
+	mock_keypress[7] = '1';
+	mock_keypress[8] = VB_KEY_ENTER; // Set vendor data
+	mock_keypress[9] = VB_KEY_ENTER; // Confirm vendor data
+	TEST_EQ(VbBootDeveloper(&ctx), VBERROR_REBOOT_REQUIRED,
+		"Ctrl+S backspace only doesn't underrun");
+	TEST_EQ(set_vendor_data_called, 1, "  VbExSetVendorData() called");
+	TEST_STR_EQ(set_vendor_data, "4321", "  Vendor data correct");
+
+	/* Ctrl+S too short */
+	ResetMocks();
+	ctx.flags |= VB2_CONTEXT_VENDOR_DATA_SETTABLE;
+	mock_keypress[0] = VB_KEY_CTRL('S');
+	mock_keypress[1] = '1';
+	mock_keypress[2] = '2';
+	mock_keypress[3] = '3';
+	mock_keypress[4] = VB_KEY_ENTER; // Set vendor data (Nothing happens)
+	mock_keypress[5] = VB_KEY_ENTER; // Confirm vendor data (Nothing happens)
+	mock_keypress[6] = VB_KEY_ESC;
+	TEST_EQ(VbBootDeveloper(&ctx), 1002, "Ctrl+S too short");
+	TEST_EQ(set_vendor_data_called, 0, "  VbExSetVendorData() not called");
+
+	/* Ctrl+S esc from set screen */
+	ResetMocks();
+	ctx.flags |= VB2_CONTEXT_VENDOR_DATA_SETTABLE;
+	mock_keypress[0] = VB_KEY_CTRL('S');
+	mock_keypress[1] = VB_KEY_ESC;
+	TEST_EQ(VbBootDeveloper(&ctx), 1002, "Ctrl+S esc from set screen");
+	TEST_EQ(set_vendor_data_called, 0, "  VbExSetVendorData() not called");
+
+	/* Ctrl+S esc from set screen with tag */
+	ResetMocks();
+	ctx.flags |= VB2_CONTEXT_VENDOR_DATA_SETTABLE;
+	mock_keypress[0] = VB_KEY_CTRL('S');
+	mock_keypress[1] = '4';
+	mock_keypress[2] = '3';
+	mock_keypress[3] = '2';
+	mock_keypress[4] = '1';
+	mock_keypress[5] = VB_KEY_ESC;
+	TEST_EQ(VbBootDeveloper(&ctx), 1002,
+		"Ctrl+S esc from set screen with tag");
+	TEST_EQ(set_vendor_data_called, 0, "  VbExSetVendorData() not called");
+
+	/* Ctrl+S esc from confirm screen */
+	ResetMocks();
+	ctx.flags |= VB2_CONTEXT_VENDOR_DATA_SETTABLE;
+	mock_keypress[0] = VB_KEY_CTRL('S');
+	mock_keypress[1] = '4';
+	mock_keypress[2] = '3';
+	mock_keypress[3] = '2';
+	mock_keypress[4] = '1';
+	mock_keypress[5] = VB_KEY_ENTER; // Set vendor data
+	mock_keypress[6] = VB_KEY_ESC;
+	TEST_EQ(VbBootDeveloper(&ctx), 1002, "Ctrl+S esc from set screen");
+	TEST_EQ(set_vendor_data_called, 0, "  VbExSetVendorData() not called");
+
 	/* If no USB, eventually times out and tries fixed disk */
 	ResetMocks();
 	vb2_nv_set(&ctx, VB2_NV_DEV_BOOT_USB, 1);
diff --git a/tests/vboot_detach_menu_tests.c b/tests/vboot_detach_menu_tests.c
index 68a8a2c..4db4f3a 100644
--- a/tests/vboot_detach_menu_tests.c
+++ b/tests/vboot_detach_menu_tests.c
@@ -205,7 +205,8 @@
 	return vbtlk_last_retval + get_info_flags;
 }
 
-VbError_t VbDisplayScreen(struct vb2_context *ctx, uint32_t screen, int force)
+VbError_t VbDisplayScreen(struct vb2_context *ctx, uint32_t screen, int force,
+			  const VbScreenData *data)
 {
 	if (screens_count < ARRAY_SIZE(screens_displayed))
 		screens_displayed[screens_count++] = screen;