diff mbox

[Branch,~linaro-validation/lava-dispatcher/trunk] Rev 575: Boot test system with UEFI image included in test image

Message ID 20130407134019.29759.73325.launchpad@ackee.canonical.com
State Accepted
Headers show

Commit Message

Antonio Terceiro April 7, 2013, 1:40 p.m. UTC
Merge authors:
  Antonio Terceiro (terceiro)
Related merge proposals:
  https://code.launchpad.net/~terceiro/lava-dispatcher/vexpress-bootloader-test/+merge/150409
  proposed by: Antonio Terceiro (terceiro)
------------------------------------------------------------
revno: 575 [merge]
committer: Antonio Terceiro <antonio.terceiro@linaro.org>
branch nick: trunk
timestamp: Sun 2013-04-07 10:39:08 -0300
message:
  Boot test system with UEFI image included in test image
added:
  lava_dispatcher/device/vexpress.py
modified:
  lava_dispatcher/config.py
  lava_dispatcher/default-config/lava-dispatcher/device-types/vexpress-tc2.conf
  lava_dispatcher/device/master.py


--
lp:lava-dispatcher
https://code.launchpad.net/~linaro-validation/lava-dispatcher/trunk

You are subscribed to branch lp:lava-dispatcher.
To unsubscribe from this branch go to https://code.launchpad.net/~linaro-validation/lava-dispatcher/trunk/+edit-subscription
diff mbox

Patch

=== modified file 'lava_dispatcher/config.py'
--- lava_dispatcher/config.py	2013-03-29 18:21:28 +0000
+++ lava_dispatcher/config.py	2013-04-05 17:19:57 +0000
@@ -97,6 +97,13 @@ 
     fastboot_command = schema.StringOption()
     shared_working_directory = schema.StringOption(default=None)
 
+    uefi_image_filename = schema.StringOption(default=None)
+    vexpress_uefi_path = schema.StringOption(default=None)
+    vexpress_uefi_backup_path = schema.StringOption(default=None)
+    vexpress_stop_autoboot_prompt = schema.StringOption(
+        default='Press Enter to stop auto boot...')
+    vexpress_usb_mass_storage_device = schema.StringOption(default=None)
+
 class OptionDescriptor(object):
     def __init__(self, name):
         self.name = name

=== modified file 'lava_dispatcher/default-config/lava-dispatcher/device-types/vexpress-tc2.conf'
--- lava_dispatcher/default-config/lava-dispatcher/device-types/vexpress-tc2.conf	2013-02-13 18:25:26 +0000
+++ lava_dispatcher/default-config/lava-dispatcher/device-types/vexpress-tc2.conf	2013-02-27 21:10:47 +0000
@@ -1,3 +1,5 @@ 
+client_type = vexpress
+
 boot_cmds = 2
 
 boot_cmds_android = 3
@@ -15,3 +17,11 @@ 
     fstab.partitions
     init.rc
     fstab.arm-versatileexpress
+
+uefi_image_filename = uefi_v2p-ca15-tc2.bin 
+
+vexpress_uefi_path = SOFTWARE/TC2/uefi.bin
+
+vexpress_uefi_backup_path = SOFTWARE/TC2/backup-uefi.bin
+
+vexpress_usb_mass_storage_device = /dev/disk/by-label/VEMSD

=== modified file 'lava_dispatcher/device/master.py'
--- lava_dispatcher/device/master.py	2013-04-02 16:16:18 +0000
+++ lava_dispatcher/device/master.py	2013-04-07 13:37:43 +0000
@@ -112,6 +112,17 @@ 
         system = download_image(system, self.context, sdir, decompress=False)
         data = download_image(userdata, self.context, sdir, decompress=False)
 
+        with self._as_master() as master:
+            self._format_testpartition(master, 'ext4')
+            self._deploy_android_tarballs(master, boot, system, data)
+
+            if master.has_partition_with_label('userdata') and \
+                   master.has_partition_with_label('sdcard'):
+                _purge_linaro_android_sdcard(master)
+
+        self.deployment_data = Target.android_deployment_data
+
+    def _deploy_android_tarballs(self, master, boot, system, data):
         tmpdir = self.context.config.lava_image_tmpdir
         url = self.context.config.lava_image_url
 
@@ -123,17 +134,9 @@ 
         system_url = '/'.join(u.strip('/') for u in [url, system])
         data_url = '/'.join(u.strip('/') for u in [url, data])
 
-        with self._as_master() as master:
-            self._format_testpartition(master, 'ext4')
-            _deploy_linaro_android_boot(master, boot_url, self)
-            _deploy_linaro_android_system(master, system_url)
-            _deploy_linaro_android_data(master, data_url)
-
-            if master.has_partition_with_label('userdata') and \
-                   master.has_partition_with_label('sdcard'):
-                _purge_linaro_android_sdcard(master)
-
-        self.deployment_data = Target.android_deployment_data
+        _deploy_linaro_android_boot(master, boot_url, self)
+        _deploy_linaro_android_system(master, system_url)
+        _deploy_linaro_android_data(master, data_url)
 
     def deploy_linaro_prebuilt(self, image):
         self.boot_master_image()
@@ -402,9 +405,9 @@ 
             self.proc.sendline("hardreset")
             self.proc.empty_buffer()
 
-    def _enter_uboot(self):
+    def _enter_bootloader(self):
         if self.proc.expect(self.config.interrupt_boot_prompt) != 0:
-            raise Exception("Failed to enter uboot")
+            raise Exception("Failed to enter bootloader")
         self.proc.sendline(self.config.interrupt_boot_command)
 
     def _boot_linaro_image(self):
@@ -421,11 +424,11 @@ 
     def _boot(self, boot_cmds):
         try:
             self._soft_reboot()
-            self._enter_uboot()
+            self._enter_bootloader()
         except:
-            logging.exception("_enter_uboot failed")
+            logging.exception("_enter_bootloader failed")
             self._hard_reboot()
-            self._enter_uboot()
+            self._enter_bootloader()
         self.proc.sendline(boot_cmds[0])
         for line in range(1, len(boot_cmds)):
             self.proc.expect(self.config.bootloader_prompt, timeout=300)

=== added file 'lava_dispatcher/device/vexpress.py'
--- lava_dispatcher/device/vexpress.py	1970-01-01 00:00:00 +0000
+++ lava_dispatcher/device/vexpress.py	2013-04-05 21:58:28 +0000
@@ -0,0 +1,193 @@ 
+# Copyright (C) 2013 Linaro Limited
+#
+# Author: Antonio Terceiro <antonio.terceiro@linaro.org>
+#
+# This file is part of LAVA Dispatcher.
+#
+# LAVA Dispatcher is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# LAVA Dispatcher is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.    See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along
+# with this program; if not, see <http://www.gnu.org/licenses>.
+
+import pexpect
+import os
+import logging
+from time import sleep
+from contextlib import contextmanager
+
+from lava_dispatcher.device.master import MasterImageTarget
+from lava_dispatcher.errors import CriticalError
+
+class VexpressTarget(MasterImageTarget):
+
+    def __init__(self, context, config):
+        super(VexpressTarget, self).__init__(context, config)
+
+        self.test_uefi = None
+
+        if (self.config.uefi_image_filename is None or
+            self.config.vexpress_uefi_path is None or
+            self.config.vexpress_uefi_backup_path is None or
+            self.config.vexpress_usb_mass_storage_device is None):
+
+            raise CriticalError(
+                "Versatile Express devices must specify all "
+                "of the following configuration variables: "
+                "uefi_image_filename, vexpress_uefi_path, "
+                "vexpress_uefi_backup_path, and "
+                "vexpress_usb_mass_storage_device")
+
+    ##################################################################
+    # methods inherited from MasterImageTarget and overriden here
+    ##################################################################
+
+    def _soft_reboot(self):
+        """
+        The Vexpress board only displays the prompt to interrupt the MCC when
+        it is power-cycled, so we must always do a hard reset in practice.
+
+        When a soft reboot is requested, though, at least we sync the disks
+        before sending the hard reset.
+        """
+        # Try to C-c the running process, if any
+        self.proc.sendcontrol('c')
+        # Flush file system buffers
+        self.proc.sendline('sync')
+
+        self._hard_reboot()
+
+    def _enter_bootloader(self):
+        with self._mcc_setup() as mount_point:
+            self._install_test_uefi(mount_point)
+
+        super(VexpressTarget, self)._enter_bootloader()
+
+    def _wait_for_master_boot(self):
+        with self._mcc_setup() as mount_point:
+            self._restore_uefi_backup(mount_point)
+
+        super(VexpressTarget, self)._wait_for_master_boot()
+
+    def _deploy_android_tarballs(self, master, boot, system, data):
+        super(VexpressTarget, self)._deploy_android_tarballs(master, boot,
+                                                             system, data)
+        # android images have boot files inside boot/ in the tarball
+        uefi_on_image = os.path.join('boot', self.config.uefi_image_filename)
+        self._extract_uefi_from_tarball(boot, uefi_on_image)
+
+    def _deploy_tarballs(self, boot_tgz, root_tgz):
+        super(VexpressTarget, self)._deploy_tarballs(boot_tgz, root_tgz)
+        uefi_on_image = self.config.uefi_image_filename
+        self._extract_uefi_from_tarball(boot_tgz, uefi_on_image)
+
+    ##################################################################
+    # implementation-specific methods
+    ##################################################################
+
+    @contextmanager
+    def _mcc_setup(self):
+        """
+        This method will manage the context for manipulating the USB mass
+        storage device, and pass the mount point where the USB MSD is mounted
+        to the inner block.
+
+        Example:
+
+            with self._mcc_setup() as mount_point:
+                do_stuff_with(mount_point)
+
+
+        This can be used for example to copy files from/to the USB MSD.
+        Mounting and unmounting is managed by this method, so the inner block
+        does not have to handle that.
+        """
+
+        mount_point = os.path.join(self.scratch_dir, 'vexpress-usb')
+        if not os.path.exists(mount_point):
+            os.makedirs(mount_point)
+
+        self._enter_mcc()
+        self._mount_usbmsd(mount_point)
+        try:
+            yield mount_point
+        finally:
+            self._umount_usbmsd(mount_point)
+            self._leave_mcc()
+
+    def _enter_mcc(self):
+        match_id = self.proc.expect([
+            self.config.vexpress_stop_autoboot_prompt,
+            pexpect.EOF, pexpect.TIMEOUT])
+        if match_id != 0:
+            msg = 'Unable to intercept MCC boot prompt'
+            logging.error(msg)
+            raise CriticalError(msg)
+        self.proc.sendline("")
+        self.proc.expect(['Cmd>'])
+
+    def _mount_usbmsd(self, mount_point):
+        self.proc.sendline("USB_ON")
+        self.proc.expect(['Cmd>'])
+
+        # wait a few seconds so that the kernel on the host detects the USB
+        # mass storage interface exposed by the Vexpress
+        sleep(5)
+
+        usb_device = self.config.vexpress_usb_mass_storage_device
+
+        self.context.run_command('mount %s %s' % (usb_device, mount_point))
+
+    def _umount_usbmsd(self, mount_point):
+        self.context.run_command('umount %s' % mount_point)
+
+    def _leave_mcc(self):
+        self.proc.sendline("reboot")
+
+    def _extract_uefi_from_tarball(self, tarball, uefi_on_image):
+        tmpdir = self.scratch_dir
+
+        # Android boot tarballs have the UEFI binary at boot/*.bin, while
+        # Ubuntu ones have it at ./*.bin
+        #
+        # --no-anchored matches the name inside any directory in the tarball.
+        self.context.run_command('tar --no-anchored -xaf %s -C %s %s' % (tarball, tmpdir,
+                                                               uefi_on_image))
+
+        uefi_on_image = os.path.join(tmpdir, uefi_on_image)
+        test_uefi = os.path.join(tmpdir, 'uefi.bin')
+        self.context.run_command('mv %s %s' % (uefi_on_image, test_uefi))
+
+        self.test_uefi = test_uefi
+
+    def _restore_uefi_backup(self, mount_point):
+        uefi_path = self.config.vexpress_uefi_path
+        uefi = os.path.join(mount_point, uefi_path)
+        uefi_backup_path = self.config.vexpress_uefi_backup_path
+        uefi_backup = os.path.join(mount_point, uefi_backup_path)
+
+        if os.path.exists(uefi_backup):
+            # restore the uefi backup
+            self.context.run_command('cp %s %s' % (uefi_backup, uefi))
+        else:
+            # no existing backup yet means that this is the first time ever;
+            # the uefi in there is the good one, and we backup it up.
+            self.context.run_command('cp %s %s' % (uefi, uefi_backup))
+
+    def _install_test_uefi(self, mount_point):
+        uefi_path = self.config.vexpress_uefi_path
+        uefi = os.path.join(mount_point, uefi_path)
+        # FIXME what if self.test_uefi is not set, or points to an unexisting
+        # file?
+        self.context.run_command('cp %s %s' % (self.test_uefi, uefi))
+
+
+target_class = VexpressTarget