trunks_send: do not update pre 0.0.19 versions unless forced

The way the CR50 handles updates changed in version RW 0.0.19. In
general trunks_send should not be updating earlier versions, because
this would cause a TPM restart on resumes after suspend, the user
should explicitly express the need to update earlier versions.

This patch implements the new policy. In case an older Cr50 is
encountered and the --force command line option is not used, the
utility returns exit code of 2.

BUG=b:35580805
TEST=verified that attempts to update cr50 running 0.0.18 get
      processed as expected:

  localhost ~ # trunks_send --update /opt/google/cr50/firmware/cr50.bin.prod
  protocol version: 6
  offsets: backup RO at 0x40000, backup RW at 0x4000
  Not updating from RW 0.0.18, use --force if necessary
  localhost ~ # echo $?
  2
  localhost ~ #

     also verified that upload with --force succeeds.

Change-Id: I69553382888ea77f8084399e5eb9cbb0173abff5
Signed-off-by: Vadim Bendebury <vbendeb@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/505251
Reviewed-by: Andrey Pronin <apronin@chromium.org>
diff --git a/trunks/trunks_send.cc b/trunks/trunks_send.cc
index 5d05ac0..37609d0 100644
--- a/trunks/trunks_send.cc
+++ b/trunks/trunks_send.cc
@@ -483,11 +483,18 @@
   return num_txed_sections;
 }
 
+enum UpdateStatus {
+  UpdateSuccess = 0,
+  UpdateError = 1,
+  UpdateCancelled = 2
+};
+
 // Updathe the Cr50 image on the device.
-static int HandleUpdate(TrunksDBusProxy* proxy, base::CommandLine* cl) {
+static UpdateStatus HandleUpdate(TrunksDBusProxy* proxy,
+                                 base::CommandLine* cl) {
   if (cl->GetArgs().size() != 1) {
     LOG(ERROR) << "A single image file name must be provided.";
-    return 1;
+    return UpdateError;
   }
 
   base::FilePath filename(cl->GetArgs()[0]);
@@ -495,38 +502,48 @@
   std::string update_image;
   if (!base::ReadFileToString(filename, &update_image)) {
     LOG(ERROR) << "Failed to read " << filename.value();
-    return 1;
+    return UpdateError;
   }
 
   FirstResponsePdu rpdu;
   if (!SetupConnection(proxy, &rpdu)) {
-    return 1;
+    return UpdateError;
+  }
+
+  // Cr50 images with RW versoin below 0.0.19 process updates differently,
+  // and as such require special treatment.
+  bool running_pre_19 =
+    rpdu.shv[1].minor < 19 &&
+    rpdu.shv[1].major == 0 &&
+    rpdu.shv[1].epoch == 0;
+
+  if (running_pre_19 && !cl->HasSwitch(kForce)) {
+    printf("Not updating from RW 0.0.%d, use --force if necessary\n",
+           rpdu.shv[1].minor);
+    return UpdateCancelled;
   }
 
   int rv = TransferImage(proxy, update_image, rpdu, cl->HasSwitch(kForce));
 
   if (rv < 0) {
-    return 1;
+    return UpdateError;
   }
 
   // Positive rv indicates that some sections were transferred and a Cr50
   // reboot is required. RW Cr50 versions below 0.0.19 require a posted reset
   // to switch to the new image.
-  if (rv > 0 &&
-      rpdu.shv[1].minor < 19 &&
-      rpdu.shv[1].major == 0 &&
-      rpdu.shv[1].epoch == 0) {
+  if (rv > 0 && running_pre_19) {
     std::string dummy;
 
     LOG(INFO) << "Will post a reset request.";
 
     if (VendorCommand(proxy, VENDOR_CC_POST_RESET, dummy, &dummy, true)) {
       LOG(ERROR) << "Failed to post a reset request.";
-      return 1;
+      return UpdateError;
     }
   }
 
-  return 0;
+  return UpdateSuccess;
 }
 
 // Vendor command to get the console lock state