Correctly report firmware update failure if RO needs update.

If the RO Firmware needs updating but can't be because it's
really RO, then report a new erro code (43) back to Omaha.

This normally only happens during board bringup when the RO
firmware is locked prematurely. We want to know these devices
exist so we can replace them.

BUG=chromium:218555
TEST=Build/unittests. Did NOT test on hardware with bug.

Change-Id: I383a999f1b175c378bef936c55d87565d8113336
Reviewed-on: https://gerrit.chromium.org/gerrit/63841
Commit-Queue: Don Garrett <dgarrett@chromium.org>
Reviewed-by: Don Garrett <dgarrett@chromium.org>
Tested-by: Don Garrett <dgarrett@chromium.org>
diff --git a/error_code.h b/error_code.h
index aab0917..65a5533 100644
--- a/error_code.h
+++ b/error_code.h
@@ -52,6 +52,7 @@
   kErrorCodeOmahaUpdateDeferredForBackoff = 40,
   kErrorCodePostinstallPowerwashError = 41,
   kErrorCodeUpdateCanceledByChannelChange = 42,
+  kErrorCodePostinstallFirmwareRONotUpdatable = 43,
 
   // Note: When adding new error codes, please remember to add the
   // error into one of the buckets in PayloadState::UpdateFailed method so
diff --git a/payload_state.cc b/payload_state.cc
index f7c75da..6d6791f 100644
--- a/payload_state.cc
+++ b/payload_state.cc
@@ -246,6 +246,7 @@
     case kErrorCodeNewRootfsVerificationError:
     case kErrorCodeNewKernelVerificationError:
     case kErrorCodePostinstallBootedFromFirmwareB:
+    case kErrorCodePostinstallFirmwareRONotUpdatable:
     case kErrorCodeOmahaRequestEmptyResponseError:
     case kErrorCodeOmahaRequestXMLParseError:
     case kErrorCodeOmahaResponseInvalid:
diff --git a/payload_state_unittest.cc b/payload_state_unittest.cc
index eb66628..0eb1875 100644
--- a/payload_state_unittest.cc
+++ b/payload_state_unittest.cc
@@ -86,7 +86,7 @@
 class PayloadStateTest : public ::testing::Test { };
 
 TEST(PayloadStateTest, DidYouAddANewErrorCode) {
-  if (kErrorCodeUmaReportedMax != 43) {
+  if (kErrorCodeUmaReportedMax != 44) {
     LOG(ERROR) << "The following failure is intentional. If you added a new "
                << "ErrorCode enum value, make sure to add it to the "
                << "PayloadState::UpdateFailed method and then update this test "
diff --git a/postinstall_runner_action.cc b/postinstall_runner_action.cc
index 0f8059e..6be13e1 100644
--- a/postinstall_runner_action.cc
+++ b/postinstall_runner_action.cc
@@ -92,6 +92,14 @@
       // to get back to FW A.
       completer.set_code(kErrorCodePostinstallBootedFromFirmwareB);
     }
+
+    if (return_code == 4) {
+      // This special return code means that we tried to update firmware,
+      // but couldn't because we booted from FW B, and we need to reboot
+      // to get back to FW A.
+      completer.set_code(kErrorCodePostinstallFirmwareRONotUpdatable);
+    }
+
     return;
   }
 
diff --git a/postinstall_runner_action_unittest.cc b/postinstall_runner_action_unittest.cc
index 0deb859..128610f 100644
--- a/postinstall_runner_action_unittest.cc
+++ b/postinstall_runner_action_unittest.cc
@@ -92,6 +92,11 @@
   DoTest(true, 3, false);
 }
 
+TEST_F(PostinstallRunnerActionTest, RunAsRootFirmwareROErrScriptTest) {
+  ASSERT_EQ(0, getuid());
+  DoTest(true, 4, false);
+}
+
 const char* PostinstallRunnerActionTest::kImageMountPointTemplate =
     "au_destination-XXXXXX";
 
diff --git a/utils.cc b/utils.cc
index 05e5758..248c665 100644
--- a/utils.cc
+++ b/utils.cc
@@ -966,6 +966,8 @@
       return "TestOmahaUrl";
     case kErrorCodeSpecialFlags:
       return "kErrorCodeSpecialFlags";
+    case kErrorCodePostinstallFirmwareRONotUpdatable:
+      return "kErrorCodePostinstallFirmwareRONotUpdatable";
     // Don't add a default case to let the compiler warn about newly added
     // error codes which should be added here.
   }