Autotest: firmware_UpdateKernelVersion and firmware_UpdateKernelDataKeyVersion

These two tests individually modify kernel version and kernel data key version.
On runtime, they modify kernel b directly, and reboot. Then, check the version,
and recovery.

BUG=chrome-os-partner:12442
TEST=run_remote_tests.sh --remote=$REMOTE_IP --board=$BOARD --servo
    firmware_UpdateKernelVersion(or firmware_UpdateKernelDataKeyVersion)

Change-Id: Ic3ad3caed47ed6cdffc41d6df6f4696b74d350b7
Reviewed-on: https://gerrit.chromium.org/gerrit/31208
Reviewed-by: Tom Wai-Hong Tam <waihong@chromium.org>
Commit-Ready: Chun-Ting Chang <ctchang@chromium.org>
Tested-by: Chun-Ting Chang <ctchang@chromium.org>
diff --git a/client/cros/faft_client.py b/client/cros/faft_client.py
index 12863a8..d3c88a9 100644
--- a/client/cros/faft_client.py
+++ b/client/cros/faft_client.py
@@ -472,6 +472,17 @@
         """Increase kernel version for the requested section."""
         self._modify_kernel_version(section, 1)
 
+
+    def retrieve_kernel_version(self, section):
+        """Return kernel version."""
+        return self._kernel_handler.get_version(section)
+
+
+    def retrieve_kernel_datakey_version(self, section):
+        """Return kernel datakey version."""
+        return self._kernel_handler.get_datakey_version(section)
+
+
     def diff_kernel_a_b(self):
         """Compare kernel A with B.
 
@@ -551,7 +562,7 @@
                                    'RW_FWID_A'))
 
         [fwid] = self.run_shell_command_get_output(
-                'eu-strings RW_FWID_A')
+                "cat RW_FWID_A | tr '\\0' '\\t' | cut -f1")
 
         return fwid
 
@@ -657,6 +668,16 @@
         return self._temp_path
 
 
+    def get_keys_path(self):
+        """Get keys path in temporary directory."""
+        return self._keys_path
+
+
+    def resign_kernel_with_keys(self, section, key_path=None):
+        """Resign kernel with temporary key."""
+        self._kernel_handler.resign_kernel(section, key_path)
+
+
     def cleanup(self):
         """Cleanup for the RPC server. Currently nothing."""
         pass
diff --git a/server/site_tests/firmware_UpdateKernelDataKeyVersion/control b/server/site_tests/firmware_UpdateKernelDataKeyVersion/control
new file mode 100644
index 0000000..3389cf4
--- /dev/null
+++ b/server/site_tests/firmware_UpdateKernelDataKeyVersion/control
@@ -0,0 +1,37 @@
+# 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_UpdateKernelDataKeyVersion"
+PURPOSE = """
+Servo based kernel update test which checks the kernel data key version.
+"""
+CRITERIA = """
+This test will fail if one of the following conditions is met:
+1. In Normal Mode.
+2. After update, device restart with KERNEL A.
+3. Kernel datakey version does not increase after update.
+4. After recovery, device can't successfully restart.
+5. Kernel datakey version does not recover to original version after recovery.
+"""
+TIME = "LONG"
+TEST_CATEGORY = "Functional"
+TEST_CLASS = "firmware"
+TEST_TYPE = "server"
+
+DOC = """
+This test should run in developer mode. On runtime, this test modifies the
+kernel data key version of kernel b and modifies cgpt to reboot with kernel b.
+Check kernel data key version after reboot, and then recover kernel b's data
+key version to original version. Here also tries to reboot with kernel b after
+recovery. If sccuess, reboot with kernel a.
+"""
+
+def run_updatekerneldatakeyversion(machine):
+    host = hosts.create_host(machine)
+    job.run_test("firmware_UpdateKernelDataKeyVersion", host=host,
+                 cmdline_args=args,use_faft=True, disable_sysinfo=True,
+                 dev_mode=True, tag="dev")
+
+parallel_simple(run_updatekerneldatakeyversion, machines)
diff --git a/server/site_tests/firmware_UpdateKernelDataKeyVersion/files/make_keys.sh b/server/site_tests/firmware_UpdateKernelDataKeyVersion/files/make_keys.sh
new file mode 100755
index 0000000..ae37ce1
--- /dev/null
+++ b/server/site_tests/firmware_UpdateKernelDataKeyVersion/files/make_keys.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+. "$(dirname "$0")/common.sh"
+
+KDATAKEY_VERSION=$1
+
+# TODO(ctchang) Modify this after adding dumpRSAPublicKey to image
+PATH=$PATH:/usr/local/sbin/firmware/saft
+export PATH
+
+pushd /var/tmp/faft/autest/keys
+
+make_pair "kernel_data_key" $KERNEL_DATAKEY_ALGOID $KDATAKEY_VERSION
+make_keyblock "kernel" $KERNEL_KEYBLOCK_MODE "kernel_data_key" "kernel_subkey"
+
+popd
diff --git a/server/site_tests/firmware_UpdateKernelDataKeyVersion/firmware_UpdateKernelDataKeyVersion.py b/server/site_tests/firmware_UpdateKernelDataKeyVersion/firmware_UpdateKernelDataKeyVersion.py
new file mode 100644
index 0000000..80e6f9f
--- /dev/null
+++ b/server/site_tests/firmware_UpdateKernelDataKeyVersion/firmware_UpdateKernelDataKeyVersion.py
@@ -0,0 +1,115 @@
+# 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, os, time
+from autotest_lib.server.cros.faftsequence import FAFTSequence
+from autotest_lib.client.common_lib import error
+
+
+class firmware_UpdateKernelDataKeyVersion(FAFTSequence):
+    """
+    This test should run in developer mode. On runtime, this test modifies the
+    kernel data key version of kernel b and modifies cgpt to reboot with kernel
+    b. Check kernel data key version after reboot, and then recover kernel b's
+    data key version to original version. Here also tries to reboot with kernel
+    b after recovery. If sccuess, reboot with kernel a.
+    """
+    version = 1
+
+    def check_kernel_datakey_version(self, expected_ver):
+        actual_ver = self.faft_client.retrieve_kernel_datakey_version('b')
+        if actual_ver != expected_ver:
+            raise error.TestFail(
+                'Kernel Version should be %s, but got %s.'
+                % (expected_ver, actual_ver))
+        else:
+            logging.info(
+                'Update success, now version is %s'
+                % actual_ver)
+
+
+    def resign_kernel_datakey_version(self, host):
+        host.send_file(os.path.join(
+                           '~/trunk/src/platform/vboot_reference/scripts',
+                           'keygeneration/common.sh'),
+                       os.path.join(self.faft_client.get_temp_path(),
+                                    'common.sh'))
+        host.send_file(os.path.join('~/trunk/src/third_party/autotest/files/',
+                                    'server/site_tests',
+                                    'firmware_UpdateKernelDataKeyVersion',
+                                    'files/make_keys.sh'),
+                       os.path.join(self.faft_client.get_temp_path(),
+                                    'make_keys.sh'))
+        # TODO(ctchang) Delete this after adding dumpRSAPublicKey to image
+        host.send_file(os.path.join('/usr/bin/dumpRSAPublicKey'),
+                       '/usr/local/sbin/firmware/saft/dumpRSAPublicKey')
+
+        self.faft_client.run_shell_command('/bin/bash %s %s' % (
+            os.path.join(self.faft_client.get_temp_path(), 'make_keys.sh'),
+            self._update_version))
+
+
+    def modify_kernel_b_and_set_cgpt_priority(self, delta, target_dev):
+        if delta == 1:
+            self.faft_client.resign_kernel_with_keys(
+                'b', self.faft_client.get_keys_path())
+        elif delta == -1:
+            self.check_kernel_datakey_version(self._update_version)
+            self.faft_client.resign_kernel_with_keys('b')
+
+        if target_dev == 'a':
+            self.reset_and_prioritize_kernel('a')
+        else:
+            self.reset_and_prioritize_kernel('b')
+
+
+    def setup(self, host=None, dev_mode=True):
+        super(firmware_UpdateKernelDataKeyVersion, self).setup()
+
+        self.setup_dev_mode(dev_mode)
+
+        actual_ver = self.faft_client.retrieve_kernel_datakey_version('b')
+        logging.info('Original Kernel Version of KERN-B is %s' % actual_ver)
+
+        self._update_version = actual_ver + 1
+        logging.info('KERN-B will update to version %s' % self._update_version)
+
+        self.setup_kernel('a')
+        self.faft_client.setup_firmwareupdate_temp_dir()
+        self.resign_kernel_datakey_version(host)
+
+
+    def cleanup(self):
+        self.faft_client.cleanup_firmwareupdate_temp_dir()
+        super(firmware_UpdateKernelDataKeyVersion, self).cleanup()
+
+
+    def run_once(self, host=None):
+        self.register_faft_sequence((
+            {   # Step 1, Update Kernel Data Key Version.
+                'state_checker': (self.check_root_part_on_non_recovery, 'a'),
+                'userspace_action': (
+                     self.modify_kernel_b_and_set_cgpt_priority,
+                     1, 'b')
+            },
+            {   # Step 2, Check kernel data key version and rollback.
+                'state_checker': (self.check_root_part_on_non_recovery, 'b'),
+                'userspace_action': (
+                    self.modify_kernel_b_and_set_cgpt_priority,
+                    -1, 'b')
+            },
+            {   # Step 3, Boot with rollback kernel and change boot priority.
+                'state_checker': (self.check_root_part_on_non_recovery, 'b'),
+                'userspace_action':(
+                    self.modify_kernel_b_and_set_cgpt_priority,
+                    0, 'a')
+            },
+            {   # Step 4, Check rollback version.
+                'state_checker': (self.check_root_part_on_non_recovery, 'a'),
+                'userspace_action': (self.check_kernel_datakey_version,
+                                     self._update_version - 1)
+            }
+        ))
+
+        self.run_faft_sequence()
diff --git a/server/site_tests/firmware_UpdateKernelVersion/control b/server/site_tests/firmware_UpdateKernelVersion/control
new file mode 100644
index 0000000..8f8f64f
--- /dev/null
+++ b/server/site_tests/firmware_UpdateKernelVersion/control
@@ -0,0 +1,35 @@
+# 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_UpdateKernelVersion"
+PURPOSE = "Servo based kernel update test which checks the kernel version."
+CRITERIA = """
+This test will fail if one of the following conditions is met:
+1. In Normal Mode.
+2. After update, device restart with KERNEL A.
+3. Kernel version does not increase after update.
+4. After recovery, device can't successfully restart.
+5. Kernel version does not recover to original version after recovery.
+"""
+TIME = "LONG"
+TEST_CATEGORY = "Functional"
+TEST_CLASS = "firmware"
+TEST_TYPE = "server"
+
+DOC = """
+This test should run in developer mode. On runtime, this test modifies the
+kernel version of kernel b and modifies cgpt to reboot with kernel b. Check
+kernel version after reboot, and then recover kernel b version to original
+version. Here also tries to reboot with kernel b after recovery. If sccuess,
+reboot with kernel a.
+"""
+
+def run_updatekernelversion(machine):
+    host = hosts.create_host(machine)
+    job.run_test("firmware_UpdateKernelVersion", host=host, cmdline_args=args,
+                 use_faft=True, disable_sysinfo=True,
+                 dev_mode=True, tag="dev")
+
+parallel_simple(run_updatekernelversion, machines)
diff --git a/server/site_tests/firmware_UpdateKernelVersion/firmware_UpdateKernelVersion.py b/server/site_tests/firmware_UpdateKernelVersion/firmware_UpdateKernelVersion.py
new file mode 100644
index 0000000..2c90d21
--- /dev/null
+++ b/server/site_tests/firmware_UpdateKernelVersion/firmware_UpdateKernelVersion.py
@@ -0,0 +1,91 @@
+# 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, time
+from autotest_lib.server.cros.faftsequence import FAFTSequence
+from autotest_lib.client.common_lib import error
+
+
+class firmware_UpdateKernelVersion(FAFTSequence):
+    """
+    This is a servod based kernel update test which should run in developer
+    mode. On runtime, this test modifies the kernel version of kernel b and
+    modifies cgpt to reboot with kernel b. Check kernel version after reboot,
+    and then recover kernel b version to original version. Here also tries to
+    reboot with kernel b after recovery. If sccuess, reboot with kernel a.
+    """
+    version = 1
+
+    def check_kernel_version(self, expected_ver):
+        actual_ver = self.faft_client.retrieve_kernel_version('b')
+        if actual_ver != expected_ver:
+            raise error.TestFail(
+                'Kernel Version should be %s, but got %s.'
+                % (expected_ver, actual_ver))
+        else:
+            logging.info(
+                'Update success, now version is %s'
+                % actual_ver)
+
+
+    def modify_kernel_b_and_set_cgpt_priority(self, delta, target_dev):
+        if delta == 1:
+            self.faft_client.move_kernel_forward('b')
+        elif delta == -1:
+            self.check_kernel_version(self._update_version)
+            self.faft_client.move_kernel_backward('b')
+
+        if target_dev == 'a':
+            self.reset_and_prioritize_kernel('a')
+        else:
+            self.reset_and_prioritize_kernel('b')
+
+
+    def setup(self, dev_mode=True):
+        super(firmware_UpdateKernelVersion, self).setup()
+
+        self.setup_dev_mode(dev_mode)
+
+        actual_ver = self.faft_client.retrieve_kernel_version('b')
+        logging.info('Original Kernel Version of KERN-B is %s' % actual_ver)
+
+        self._update_version = actual_ver + 1
+        logging.info('KERN-B will update to version %s' % self._update_version)
+
+        self.setup_kernel('a')
+
+
+    def cleanup(self):
+        super(firmware_UpdateKernelVersion, self).cleanup()
+
+
+    def run_once(self, host=None):
+        self.register_faft_sequence((
+            {   # Step 1, Update Kernel Version.
+                'state_checker': (self.check_root_part_on_non_recovery, 'a'),
+                'userspace_action': (
+                     self.modify_kernel_b_and_set_cgpt_priority,
+                     1, 'b'
+                )
+            },
+            {   # Step 2, Check kernel version and rollback.
+                'state_checker': (self.check_root_part_on_non_recovery, 'b'),
+                'userspace_action': (
+                    self.modify_kernel_b_and_set_cgpt_priority,
+                    -1, 'b')
+            },
+            {   # Step 3, Boot with rollback kernel and change boot priority.
+                'state_checker': (self.check_root_part_on_non_recovery, 'b'),
+                'userspace_action':(
+                    self.modify_kernel_b_and_set_cgpt_priority,
+                    0, 'a')
+            },
+            {   # Step 4, Check rollback version.
+                'state_checker': (self.check_root_part_on_non_recovery, 'a'),
+                'userspace_action': (self.check_kernel_version,
+                                     self._update_version - 1)
+            }
+        ))
+
+        self.run_faft_sequence()