faft: New test for verifying firmware RO normal boot and disabling it

This test only runs on the firmware on which its firmware preamble flags
have USE_RO_NORMAL enabled. Since we always build and pack a workable
RW firmware in the RW firmware body section, although it is not used when
the USE_RO_NORMAL flag is enabled.

On runtime, the test disables the RO normal boot flag in the current
firmware and checks its next boot result.

BUG=chromium-os:19710
TEST=run_remote_tests.sh --board=$BOARD --remote=$REMOTE_IP --servo \
     firmware_RONormalBoot/control.both

Change-Id: I9feddf3e2761d352f33deadf66430940b667723d
Reviewed-on: https://gerrit.chromium.org/gerrit/29591
Reviewed-by: Randall Spangler <rspangler@chromium.org>
Commit-Ready: Tom Wai-Hong Tam <waihong@chromium.org>
Tested-by: Tom Wai-Hong Tam <waihong@chromium.org>
diff --git a/server/site_tests/firmware_RONormalBoot/control b/server/site_tests/firmware_RONormalBoot/control
new file mode 120000
index 0000000..65441e6
--- /dev/null
+++ b/server/site_tests/firmware_RONormalBoot/control
@@ -0,0 +1 @@
+control.both
\ No newline at end of file
diff --git a/server/site_tests/firmware_RONormalBoot/control.both b/server/site_tests/firmware_RONormalBoot/control.both
new file mode 100644
index 0000000..6649896
--- /dev/null
+++ b/server/site_tests/firmware_RONormalBoot/control.both
@@ -0,0 +1,28 @@
+# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+AUTHOR = "Chrome OS Team"
+NAME = "firmware_RONormalBoot"
+PURPOSE = "Servo based firmware RO normal boot test"
+CRITERIA = "This test will fail if disabling RO normal boot flag boots failed"
+SUITE = "faft"
+TIME = "LONG"
+TEST_CATEGORY = "Functional"
+TEST_CLASS = "firmware"
+TEST_TYPE = "server"
+
+DOC = """
+This test disables the RO normal boot flag and checks the next boot result.
+"""
+
+def run_ronormalboot(machine):
+    host = hosts.create_host(machine)
+    job.run_test("firmware_RONormalBoot", host=host, cmdline_args=args,
+                 use_faft=True, disable_sysinfo=True,
+                 dev_mode=False, tag="normal")
+    job.run_test("firmware_RONormalBoot", host=host, cmdline_args=args,
+                 use_faft=True, disable_sysinfo=True,
+                 dev_mode=True, tag="dev")
+
+parallel_simple(run_ronormalboot, machines)
diff --git a/server/site_tests/firmware_RONormalBoot/control.dev b/server/site_tests/firmware_RONormalBoot/control.dev
new file mode 100644
index 0000000..198fb94
--- /dev/null
+++ b/server/site_tests/firmware_RONormalBoot/control.dev
@@ -0,0 +1,25 @@
+# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+AUTHOR = "Chrome OS Team"
+NAME = "firmware_RONormalBoot"
+PURPOSE = "Servo based firmware RO normal boot test"
+CRITERIA = "This test will fail if disabling RO normal boot flag boots failed"
+SUITE = "faft"
+TIME = "LONG"
+TEST_CATEGORY = "Functional"
+TEST_CLASS = "firmware"
+TEST_TYPE = "server"
+
+DOC = """
+This test disables the RO normal boot flag and checks the next boot result.
+"""
+
+def run_ronormalboot(machine):
+    host = hosts.create_host(machine)
+    job.run_test("firmware_RONormalBoot", host=host, cmdline_args=args,
+                 use_faft=True, disable_sysinfo=True,
+                 dev_mode=True, tag="dev")
+
+parallel_simple(run_ronormalboot, machines)
diff --git a/server/site_tests/firmware_RONormalBoot/control.normal b/server/site_tests/firmware_RONormalBoot/control.normal
new file mode 100644
index 0000000..8121828
--- /dev/null
+++ b/server/site_tests/firmware_RONormalBoot/control.normal
@@ -0,0 +1,25 @@
+# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+AUTHOR = "Chrome OS Team"
+NAME = "firmware_RONormalBoot"
+PURPOSE = "Servo based firmware RO normal boot test"
+CRITERIA = "This test will fail if disabling RO normal boot flag boots failed"
+SUITE = "faft"
+TIME = "LONG"
+TEST_CATEGORY = "Functional"
+TEST_CLASS = "firmware"
+TEST_TYPE = "server"
+
+DOC = """
+This test disables the RO normal boot flag and checks the next boot result.
+"""
+
+def run_ronormalboot(machine):
+    host = hosts.create_host(machine)
+    job.run_test("firmware_RONormalBoot", host=host, cmdline_args=args,
+                 use_faft=True, disable_sysinfo=True,
+                 dev_mode=False, tag="normal")
+
+parallel_simple(run_ronormalboot, machines)
diff --git a/server/site_tests/firmware_RONormalBoot/firmware_RONormalBoot.py b/server/site_tests/firmware_RONormalBoot/firmware_RONormalBoot.py
new file mode 100644
index 0000000..e35415b
--- /dev/null
+++ b/server/site_tests/firmware_RONormalBoot/firmware_RONormalBoot.py
@@ -0,0 +1,78 @@
+# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+
+from autotest_lib.server.cros.faftsequence import FAFTSequence
+
+
+class firmware_RONormalBoot(FAFTSequence):
+    """
+    Servo based firmware RO normal boot test.
+
+    This test only runs on the firmware on which its firmware preamble flags
+    have USE_RO_NORMAL enabled. Since we always build and pack a workable
+    RW firmware in the RW firmware body section, although it is not used when
+    the USE_RO_NORMAL flag is enabled.
+
+    On runtime, the test disables the RO normal boot flag in the current
+    firmware and checks its next boot result.
+    """
+    version = 1
+
+    PREAMBLE_USE_RO_NORMAL = 1
+
+
+    def ensure_fw_a_boot(self):
+        """Ensure firmware A boot this time."""
+        if not self.crossystem_checker({'mainfw_act': 'A', 'tried_fwb': '0'}):
+            self.run_faft_step({
+                'userspace_action': (self.faft_client.run_shell_command,
+                    'chromeos-firmwareupdate --mode recovery')
+            })
+
+
+    def setup(self, dev_mode=False):
+        super(firmware_RONormalBoot, self).setup()
+        self.setup_dev_mode(dev_mode)
+        self.ensure_fw_a_boot()
+
+
+    def cleanup(self):
+        self.ensure_fw_a_boot()
+        super(firmware_RONormalBoot, self).cleanup()
+
+
+    def run_once(self, host=None):
+        flags = self.faft_client.get_firmware_flags('a')
+        if flags & self.PREAMBLE_USE_RO_NORMAL:
+            self.register_faft_sequence((
+                {   # Step 1, disable the RO normal boot flag
+                    'state_checker': (self.crossystem_checker, {
+                        'mainfw_act': 'A',
+                        'tried_fwb': '0',
+                    }),
+                    'userspace_action': (self.faft_client.set_firmware_flags,
+                                         'a',
+                                         flags ^ self.PREAMBLE_USE_RO_NORMAL),
+                },
+                {   # Step 2, expected boot ok, restore the original flags
+                    'state_checker': (self.crossystem_checker, {
+                        'mainfw_act': 'A',
+                        'tried_fwb': '0',
+                    }),
+                    'userspace_action': (self.faft_client.set_firmware_flags,
+                                         'a',
+                                         flags),
+                },
+                {   # Step 3, done
+                    'state_checker': (self.crossystem_checker, {
+                        'mainfw_act': 'A',
+                        'tried_fwb': '0',
+                    }),
+                },
+            ))
+            self.run_faft_sequence()
+        else:
+            logging.info('The firmware USE_RO_NORMAL flag is disabled.')