Support doing battery cut-off in firmware stage.

Add a new crossystem value "battery_cutoff_request" to indicate that
next reboot should cut-off battery and shutdown during firmware stage.

This request is primarily for factories to ship devices in an safe
state. Previously we have done same thing by running "ectool battery-cutoff"
but that creates a problem which "ectool" (and the one to request for
cut-off) must live in developer mode while  the device must be shipped
in normal mode. The mode transition was solved by setting
"disable_dev_request=1", but that flag is may get lost on x86 systems
(having NV storage in CMOS) when the battery is cut-off .

From the experience from Ryu, such settings (dev mode transition and
battery cut-off) should be done together inside firmware execution so we
can create a new flag, battery_cutoff_request, to finalize device
properly.

BRANCH=none
BUG=chromium:601705
TEST=emerge-chell depthcharge vboot_reference chromeos-bootimage
     crossystem battery_cutoff_request=1
     # Unplug AC adapter
     reboot
     # See device rebooted and then shutdown immediately.
     # Press power button and system won't boot.
     # Attach AC adapter and now system boots.
CQ-DEPEND=CL:338288,CL:338399

Original-Change-Id: I73ccae15b337cd65786106646546c67c155b8fa6
Original-Reviewed-on: https://chromium-review.googlesource.com/337602
Commit-Ready: Hung-Te Lin <hungte@chromium.org>
Tested-by: Hung-Te Lin <hungte@chromium.org>
Reviewed-by: Duncan Laurie <dlaurie@chromium.org>
(cherry picked from commit aee6bd69fefac653cfc4a5679eb387d7c3280d14)

Change-Id: I563602ee8437f867720942df7f658a28378579aa
Reviewed-on: https://chromium-review.googlesource.com/338178
Reviewed-by: Duncan Laurie <dlaurie@chromium.org>
Tested-by: Duncan Laurie <dlaurie@chromium.org>
Commit-Queue: Duncan Laurie <dlaurie@chromium.org>
diff --git a/firmware/2lib/2nvstorage.c b/firmware/2lib/2nvstorage.c
index 3e2a874..b40bbe7 100644
--- a/firmware/2lib/2nvstorage.c
+++ b/firmware/2lib/2nvstorage.c
@@ -172,6 +172,9 @@
 
 	case VB2_NV_TRY_RO_SYNC:
 		return GETBIT(VB2_NV_OFFS_MISC, VB2_NV_MISC_TRY_RO_SYNC);
+
+	case VB2_NV_BATTERY_CUTOFF_REQUEST:
+		return GETBIT(VB2_NV_OFFS_MISC, VB2_NV_MISC_BATTERY_CUTOFF);
 	}
 
 	/*
@@ -350,6 +353,9 @@
 		SETBIT(VB2_NV_OFFS_MISC, VB2_NV_MISC_TRY_RO_SYNC);
 		break;
 
+	case VB2_NV_BATTERY_CUTOFF_REQUEST:
+		SETBIT(VB2_NV_OFFS_MISC, VB2_NV_MISC_BATTERY_CUTOFF);
+		break;
 	}
 
 	/*
diff --git a/firmware/2lib/include/2nvstorage.h b/firmware/2lib/include/2nvstorage.h
index 5c958c7..66a5fdf 100644
--- a/firmware/2lib/include/2nvstorage.h
+++ b/firmware/2lib/include/2nvstorage.h
@@ -96,6 +96,8 @@
 	VB2_NV_BOOT_ON_AC_DETECT,
 	/* 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,
 };
 
 /* Set default boot in developer mode */
diff --git a/firmware/2lib/include/2nvstorage_fields.h b/firmware/2lib/include/2nvstorage_fields.h
index 9ec3d88..018bdeb 100644
--- a/firmware/2lib/include/2nvstorage_fields.h
+++ b/firmware/2lib/include/2nvstorage_fields.h
@@ -70,9 +70,10 @@
 #define VB2_NV_TPM_CLEAR_OWNER_DONE            0x02
 #define VB2_NV_TPM_REBOOTED                    0x04
 
-/* Fields in VB2_NV_OFFS_MISC (unused = 0xf8) */
+/* Fields in VB2_NV_OFFS_MISC (unused = 0xf0) */
 #define VB2_NV_MISC_UNLOCK_FASTBOOT            0x01
 #define VB2_NV_MISC_BOOT_ON_AC_DETECT          0x02
 #define VB2_NV_MISC_TRY_RO_SYNC		       0x04
+#define VB2_NV_MISC_BATTERY_CUTOFF             0x08
 
 #endif  /* VBOOT_REFERENCE_VBOOT_2NVSTORAGE_FIELDS_H_ */
diff --git a/firmware/include/vboot_api.h b/firmware/include/vboot_api.h
index ddc8cc6..5046d2a 100644
--- a/firmware/include/vboot_api.h
+++ b/firmware/include/vboot_api.h
@@ -945,6 +945,11 @@
  */
 VbError_t VbExEcVbootDone(int in_recovery);
 
+/**
+ * Request EC to stop discharging and cut-off battery.
+ */
+VbError_t VbExEcBatteryCutOff(void);
+
 /*****************************************************************************/
 /* Misc */
 
diff --git a/firmware/include/vboot_nvstorage.h b/firmware/include/vboot_nvstorage.h
index 91f62cb..ef78e47 100644
--- a/firmware/include/vboot_nvstorage.h
+++ b/firmware/include/vboot_nvstorage.h
@@ -120,7 +120,10 @@
 	VBNV_BOOT_ON_AC_DETECT,
 	/* Try to update the EC-RO image (0=no, 1=yes). */
 	VBNV_TRY_RO_SYNC,
-
+	/*
+	 * Finish mode transition (if requested), perform battery cut-off and
+	 * shutdown in next boot. */
+	VBNV_BATTERY_CUTOFF_REQUEST,
 } VbNvParam;
 
 /* Set default boot in developer mode */
diff --git a/firmware/lib/vboot_api_kernel.c b/firmware/lib/vboot_api_kernel.c
index fff3056..c336278 100644
--- a/firmware/lib/vboot_api_kernel.c
+++ b/firmware/lib/vboot_api_kernel.c
@@ -1082,6 +1082,7 @@
 	VbError_t retval = VBERROR_SUCCESS;
 	LoadKernelParams p;
 	uint32_t tpm_status = 0;
+	uint32_t battery_cutoff = 0;
 
 	/* Start timer */
 	shared->timer_vb_select_and_load_kernel_enter = VbExGetTimer();
@@ -1138,6 +1139,17 @@
 	if (retval != VBERROR_SUCCESS)
 		goto VbSelectAndLoadKernel_exit;
 
+	/* Check if we need to cut-off battery. This must be done after EC
+         * firmware updating and before kernel started. */
+	VbNvGet(&vnc, VBNV_BATTERY_CUTOFF_REQUEST, &battery_cutoff);
+	if (battery_cutoff) {
+		VBDEBUG(("Request to cut-off battery\n"));
+		VbNvSet(&vnc, VBNV_BATTERY_CUTOFF_REQUEST, 0);
+		VbExEcBatteryCutOff();
+		retval = VBERROR_SHUTDOWN_REQUESTED;
+		goto VbSelectAndLoadKernel_exit;
+	}
+
 	/* Read kernel version from the TPM.  Ignore errors in recovery mode. */
 	tpm_status = RollbackKernelRead(&shared->kernel_version_tpm);
 	if (0 != tpm_status) {
diff --git a/firmware/lib/vboot_nvstorage.c b/firmware/lib/vboot_nvstorage.c
index d022c5d..c131f08 100644
--- a/firmware/lib/vboot_nvstorage.c
+++ b/firmware/lib/vboot_nvstorage.c
@@ -64,6 +64,7 @@
 #define MISC_UNLOCK_FASTBOOT            0x01
 #define MISC_BOOT_ON_AC_DETECT          0x02
 #define MISC_TRY_RO_SYNC		0x04
+#define MISC_BATTERY_CUTOFF_REQUEST	0x08
 
 #define KERNEL_FIELD_OFFSET         11
 #define CRC_OFFSET                  15
@@ -231,6 +232,11 @@
 		*dest = (raw[MISC_OFFSET] & MISC_TRY_RO_SYNC) ? 1 : 0;
 		return 0;
 
+	case VBNV_BATTERY_CUTOFF_REQUEST:
+		*dest = (raw[MISC_OFFSET] & MISC_BATTERY_CUTOFF_REQUEST)
+			 ?  1 : 0;
+		return 0;
+
 	default:
 		return 1;
 	}
@@ -456,6 +462,13 @@
 			raw[MISC_OFFSET] &= ~MISC_TRY_RO_SYNC;
 		break;
 
+	case VBNV_BATTERY_CUTOFF_REQUEST:
+		if (value)
+			raw[MISC_OFFSET] |= MISC_BATTERY_CUTOFF_REQUEST;
+		else
+			raw[MISC_OFFSET] &= ~MISC_BATTERY_CUTOFF_REQUEST;
+		break;
+
 	default:
 		return 1;
 	}
diff --git a/firmware/stub/vboot_api_stub.c b/firmware/stub/vboot_api_stub.c
index 2299a03..7bc4cb8 100644
--- a/firmware/stub/vboot_api_stub.c
+++ b/firmware/stub/vboot_api_stub.c
@@ -166,6 +166,11 @@
        return VBERROR_SUCCESS;
 }
 
+VbError_t VbExEcBatteryCutOff(void)
+{
+	return VBERROR_SUCCESS;
+}
+
 enum VbEcBootMode_t VbGetMode(void)
 {
 	return vboot_mode;
diff --git a/host/lib/crossystem.c b/host/lib/crossystem.c
index fd5cd9e..3d2a908 100644
--- a/host/lib/crossystem.c
+++ b/host/lib/crossystem.c
@@ -542,6 +542,8 @@
 	  value = VbGetNvStorage(VBNV_BOOT_ON_AC_DETECT);
   } else if (!strcasecmp(name, "try_ro_sync")) {
 	  value = VbGetNvStorage(VBNV_TRY_RO_SYNC);
+  } else if (!strcasecmp(name, "battery_cutoff_request")) {
+    value = VbGetNvStorage(VBNV_BATTERY_CUTOFF_REQUEST);
   }
 
   return value;
@@ -678,6 +680,8 @@
     return VbSetNvStorage_WithBackup(VBNV_BOOT_ON_AC_DETECT, value);
   } else if (!strcasecmp(name, "try_ro_sync")) {
     return VbSetNvStorage_WithBackup(VBNV_TRY_RO_SYNC, value);
+  } else if (!strcasecmp(name, "battery_cutoff_request")) {
+    return VbSetNvStorage(VBNV_BATTERY_CUTOFF_REQUEST, value);
   }
 
   return -1;
diff --git a/utility/crossystem.c b/utility/crossystem.c
index 330a3fd..c88a444 100644
--- a/utility/crossystem.c
+++ b/utility/crossystem.c
@@ -34,6 +34,8 @@
   {"arch", IS_STRING, "Platform architecture"},
   {"backup_nvram_request", CAN_WRITE,
    "Backup the nvram somewhere at the next boot. Cleared on success."},
+  {"battery_cutoff_request", CAN_WRITE,
+   "Cut off battery and shutdown on next boot."},
   {"block_devmode", CAN_WRITE, "Block all use of developer mode"},
   {"clear_tpm_owner_request", CAN_WRITE, "Clear TPM owner on next boot"},
   {"clear_tpm_owner_done", CAN_WRITE, "Clear TPM owner done"},