mirror of
				https://github.com/ARM-software/workload-automation.git
				synced 2025-11-04 00:52:08 +00:00 
			
		
		
		
	Devices: Removed Devices
They are now superseded by DeviceManagers
This commit is contained in:
		@@ -14,7 +14,6 @@
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
from wlauto.core.bootstrap import settings  # NOQA
 | 
			
		||||
from wlauto.core.device import Device  # NOQA
 | 
			
		||||
from wlauto.core.device_manager import DeviceManager, RuntimeParameter, CoreParameter  # NOQA
 | 
			
		||||
from wlauto.core.command import Command  # NOQA
 | 
			
		||||
from wlauto.core.workload import Workload  # NOQA
 | 
			
		||||
@@ -26,8 +25,6 @@ from wlauto.core.resource import ResourceGetter, Resource, GetterPriority, NO_ON
 | 
			
		||||
from wlauto.core.exttype import get_extension_type  # NOQA Note: MUST be imported after other core imports.
 | 
			
		||||
 | 
			
		||||
from wlauto.common.resources import File, ExtensionAsset, Executable
 | 
			
		||||
from wlauto.common.linux.device import LinuxDevice  # NOQA
 | 
			
		||||
from wlauto.common.android.device import AndroidDevice, BigLittleDevice   # NOQA
 | 
			
		||||
from wlauto.common.android.resources import ApkFile, JarFile
 | 
			
		||||
from wlauto.common.android.workload import (UiAutomatorWorkload, ApkWorkload, AndroidBenchmark,  # NOQA
 | 
			
		||||
                                    AndroidUiAutoBenchmark, GameWorkload)  # NOQA
 | 
			
		||||
 
 | 
			
		||||
@@ -1,765 +0,0 @@
 | 
			
		||||
#    Copyright 2013-2015 ARM Limited
 | 
			
		||||
#
 | 
			
		||||
# Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
# you may not use this file except in compliance with the License.
 | 
			
		||||
# You may obtain a copy of the License at
 | 
			
		||||
#
 | 
			
		||||
#     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
#
 | 
			
		||||
# Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
# distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
# See the License for the specific language governing permissions and
 | 
			
		||||
# limitations under the License.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
# pylint: disable=E1101
 | 
			
		||||
import os
 | 
			
		||||
import sys
 | 
			
		||||
import re
 | 
			
		||||
import time
 | 
			
		||||
import tempfile
 | 
			
		||||
import shutil
 | 
			
		||||
import threading
 | 
			
		||||
from subprocess import CalledProcessError
 | 
			
		||||
 | 
			
		||||
from wlauto.core.extension import Parameter
 | 
			
		||||
from wlauto.common.linux.device import BaseLinuxDevice, PsEntry
 | 
			
		||||
from wlauto.exceptions import DeviceError, WorkerThreadError, TimeoutError, DeviceNotRespondingError
 | 
			
		||||
from wlauto.utils.misc import convert_new_lines
 | 
			
		||||
from wlauto.utils.types import boolean, regex
 | 
			
		||||
from wlauto.utils.android import (adb_shell, adb_background_shell, adb_list_devices,
 | 
			
		||||
                                  adb_command, AndroidProperties, ANDROID_VERSION_MAP)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
SCREEN_STATE_REGEX = re.compile('(?:mPowerState|mScreenOn|Display Power: state)=([0-9]+|true|false|ON|OFF)', re.I)
 | 
			
		||||
SCREEN_SIZE_REGEX = re.compile(r'mUnrestrictedScreen=\(\d+,\d+\)\s+(?P<width>\d+)x(?P<height>\d+)')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AndroidDevice(BaseLinuxDevice):  # pylint: disable=W0223
 | 
			
		||||
    """
 | 
			
		||||
    Device running Android OS.
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    platform = 'android'
 | 
			
		||||
 | 
			
		||||
    parameters = [
 | 
			
		||||
        Parameter('adb_name',
 | 
			
		||||
                  description='The unique ID of the device as output by "adb devices".'),
 | 
			
		||||
        Parameter('android_prompt', kind=regex, default=re.compile('^.*(shell|root)@.*:/\S* [#$] ', re.MULTILINE),
 | 
			
		||||
                  description='The format  of matching the shell prompt in Android.'),
 | 
			
		||||
        Parameter('working_directory', default='/sdcard/wa-working',
 | 
			
		||||
                  description='Directory that will be used WA on the device for output files etc.'),
 | 
			
		||||
        Parameter('binaries_directory', default='/data/local/tmp', override=True,
 | 
			
		||||
                  description='Location of binaries on the device.'),
 | 
			
		||||
        Parameter('package_data_directory', default='/data/data',
 | 
			
		||||
                  description='Location of of data for an installed package (APK).'),
 | 
			
		||||
        Parameter('external_storage_directory', default='/sdcard',
 | 
			
		||||
                  description='Mount point for external storage.'),
 | 
			
		||||
        Parameter('connection', default='usb', allowed_values=['usb', 'ethernet'],
 | 
			
		||||
                  description='Specified the nature of adb connection.'),
 | 
			
		||||
        Parameter('logcat_poll_period', kind=int,
 | 
			
		||||
                  description="""
 | 
			
		||||
                  If specified and is not ``0``, logcat will be polled every
 | 
			
		||||
                  ``logcat_poll_period`` seconds, and buffered on the host. This
 | 
			
		||||
                  can be used if a lot of output is expected in logcat and the fixed
 | 
			
		||||
                  logcat buffer on the device is not big enough. The trade off is that
 | 
			
		||||
                  this introduces some minor runtime overhead. Not set by default.
 | 
			
		||||
                  """),
 | 
			
		||||
        Parameter('enable_screen_check', kind=boolean, default=False,
 | 
			
		||||
                  description="""
 | 
			
		||||
                  Specified whether the device should make sure that the screen is on
 | 
			
		||||
                  during initialization.
 | 
			
		||||
                  """),
 | 
			
		||||
        Parameter('swipe_to_unlock', kind=str, default=None,
 | 
			
		||||
                  allowed_values=[None, "horizontal", "vertical"],
 | 
			
		||||
                  description="""
 | 
			
		||||
                  If set a swipe of the specified direction will be performed.
 | 
			
		||||
                  This should unlock the screen.
 | 
			
		||||
                  """),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    default_timeout = 30
 | 
			
		||||
    delay = 2
 | 
			
		||||
    long_delay = 3 * delay
 | 
			
		||||
    ready_timeout = 60
 | 
			
		||||
 | 
			
		||||
    # Overwritten from Device. For documentation, see corresponding method in
 | 
			
		||||
    # Device.
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def is_rooted(self):
 | 
			
		||||
        if self._is_rooted is None:
 | 
			
		||||
            try:
 | 
			
		||||
                result = adb_shell(self.adb_name, 'su', timeout=1)
 | 
			
		||||
                if 'not found' in result:
 | 
			
		||||
                    self._is_rooted = False
 | 
			
		||||
                else:
 | 
			
		||||
                    self._is_rooted = True
 | 
			
		||||
            except TimeoutError:
 | 
			
		||||
                self._is_rooted = True
 | 
			
		||||
            except DeviceError:
 | 
			
		||||
                self._is_rooted = False
 | 
			
		||||
        return self._is_rooted
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def abi(self):
 | 
			
		||||
        return self.getprop()['ro.product.cpu.abi'].split('-')[0]
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def supported_eabi(self):
 | 
			
		||||
        props = self.getprop()
 | 
			
		||||
        result = [props['ro.product.cpu.abi']]
 | 
			
		||||
        if 'ro.product.cpu.abi2' in props:
 | 
			
		||||
            result.append(props['ro.product.cpu.abi2'])
 | 
			
		||||
        if 'ro.product.cpu.abilist' in props:
 | 
			
		||||
            for eabi in props['ro.product.cpu.abilist'].split(','):
 | 
			
		||||
                if eabi not in result:
 | 
			
		||||
                    result.append(eabi)
 | 
			
		||||
        return result
 | 
			
		||||
 | 
			
		||||
    def __init__(self, **kwargs):
 | 
			
		||||
        super(AndroidDevice, self).__init__(**kwargs)
 | 
			
		||||
        self._logcat_poller = None
 | 
			
		||||
 | 
			
		||||
    def reset(self):
 | 
			
		||||
        self._is_ready = False
 | 
			
		||||
        self._just_rebooted = True
 | 
			
		||||
        adb_command(self.adb_name, 'reboot', timeout=self.default_timeout)
 | 
			
		||||
 | 
			
		||||
    def hard_reset(self):
 | 
			
		||||
        super(AndroidDevice, self).hard_reset()
 | 
			
		||||
        self._is_ready = False
 | 
			
		||||
        self._just_rebooted = True
 | 
			
		||||
 | 
			
		||||
    def boot(self, hard=False, **kwargs):
 | 
			
		||||
        if hard:
 | 
			
		||||
            self.hard_reset()
 | 
			
		||||
        else:
 | 
			
		||||
            self.reset()
 | 
			
		||||
 | 
			
		||||
    def connect(self):  # NOQA pylint: disable=R0912
 | 
			
		||||
        iteration_number = 0
 | 
			
		||||
        max_iterations = self.ready_timeout / self.delay
 | 
			
		||||
        available = False
 | 
			
		||||
        self.logger.debug('Polling for device {}...'.format(self.adb_name))
 | 
			
		||||
        while iteration_number < max_iterations:
 | 
			
		||||
            devices = adb_list_devices()
 | 
			
		||||
            if self.adb_name:
 | 
			
		||||
                for device in devices:
 | 
			
		||||
                    if device.name == self.adb_name and device.status != 'offline':
 | 
			
		||||
                        available = True
 | 
			
		||||
            else:  # adb_name not set
 | 
			
		||||
                if len(devices) == 1:
 | 
			
		||||
                    available = True
 | 
			
		||||
                elif len(devices) > 1:
 | 
			
		||||
                    raise DeviceError('More than one device is connected and adb_name is not set.')
 | 
			
		||||
 | 
			
		||||
            if available:
 | 
			
		||||
                break
 | 
			
		||||
            else:
 | 
			
		||||
                time.sleep(self.delay)
 | 
			
		||||
                iteration_number += 1
 | 
			
		||||
        else:
 | 
			
		||||
            raise DeviceError('Could not boot {} ({}).'.format(self.name, self.adb_name))
 | 
			
		||||
 | 
			
		||||
        while iteration_number < max_iterations:
 | 
			
		||||
            available = (int('0' + (adb_shell(self.adb_name, 'getprop sys.boot_completed', timeout=self.default_timeout))) == 1)
 | 
			
		||||
            if available:
 | 
			
		||||
                break
 | 
			
		||||
            else:
 | 
			
		||||
                time.sleep(self.delay)
 | 
			
		||||
                iteration_number += 1
 | 
			
		||||
        else:
 | 
			
		||||
            raise DeviceError('Could not boot {} ({}).'.format(self.name, self.adb_name))
 | 
			
		||||
 | 
			
		||||
        if self._just_rebooted:
 | 
			
		||||
            self.logger.debug('Waiting for boot to complete...')
 | 
			
		||||
            # On some devices, adb connection gets reset some time after booting.
 | 
			
		||||
            # This  causes errors during execution. To prevent this, open a shell
 | 
			
		||||
            # session and wait for it to be killed. Once its killed, give adb
 | 
			
		||||
            # enough time to restart, and then the device should be ready.
 | 
			
		||||
            # TODO: This is more of a work-around rather than an actual solution.
 | 
			
		||||
            #       Need to figure out what is going on the "proper" way of handling it.
 | 
			
		||||
            try:
 | 
			
		||||
                adb_shell(self.adb_name, '', timeout=20)
 | 
			
		||||
                time.sleep(5)  # give adb time to re-initialize
 | 
			
		||||
            except TimeoutError:
 | 
			
		||||
                pass  # timed out waiting for the session to be killed -- assume not going to be.
 | 
			
		||||
 | 
			
		||||
            self.logger.debug('Boot completed.')
 | 
			
		||||
            self._just_rebooted = False
 | 
			
		||||
        self._is_ready = True
 | 
			
		||||
 | 
			
		||||
    def initialize(self, context):
 | 
			
		||||
        if self.is_rooted:
 | 
			
		||||
            self.disable_screen_lock()
 | 
			
		||||
            self.disable_selinux()
 | 
			
		||||
        if self.enable_screen_check:
 | 
			
		||||
            self.ensure_screen_is_on()
 | 
			
		||||
 | 
			
		||||
    def disconnect(self):
 | 
			
		||||
        if self._logcat_poller:
 | 
			
		||||
            self._logcat_poller.close()
 | 
			
		||||
 | 
			
		||||
    def ping(self):
 | 
			
		||||
        try:
 | 
			
		||||
            # May be triggered inside initialize()
 | 
			
		||||
            adb_shell(self.adb_name, 'ls /', timeout=10)
 | 
			
		||||
        except (TimeoutError, CalledProcessError):
 | 
			
		||||
            raise DeviceNotRespondingError(self.adb_name or self.name)
 | 
			
		||||
 | 
			
		||||
    def start(self):
 | 
			
		||||
        if self.logcat_poll_period:
 | 
			
		||||
            if self._logcat_poller:
 | 
			
		||||
                self._logcat_poller.close()
 | 
			
		||||
            self._logcat_poller = _LogcatPoller(self, self.logcat_poll_period, timeout=self.default_timeout)
 | 
			
		||||
            self._logcat_poller.start()
 | 
			
		||||
 | 
			
		||||
    def stop(self):
 | 
			
		||||
        if self._logcat_poller:
 | 
			
		||||
            self._logcat_poller.stop()
 | 
			
		||||
 | 
			
		||||
    def get_android_version(self):
 | 
			
		||||
        return ANDROID_VERSION_MAP.get(self.get_sdk_version(), None)
 | 
			
		||||
 | 
			
		||||
    def get_android_id(self):
 | 
			
		||||
        """
 | 
			
		||||
        Get the device's ANDROID_ID. Which is
 | 
			
		||||
 | 
			
		||||
            "A 64-bit number (as a hex string) that is randomly generated when the user
 | 
			
		||||
            first sets up the device and should remain constant for the lifetime of the
 | 
			
		||||
            user's device."
 | 
			
		||||
 | 
			
		||||
        .. note:: This will get reset on userdata erasure.
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        output = self.execute('content query --uri content://settings/secure --projection value --where "name=\'android_id\'"').strip()
 | 
			
		||||
        return output.split('value=')[-1]
 | 
			
		||||
 | 
			
		||||
    def get_sdk_version(self):
 | 
			
		||||
        try:
 | 
			
		||||
            return int(self.getprop('ro.build.version.sdk'))
 | 
			
		||||
        except (ValueError, TypeError):
 | 
			
		||||
            return None
 | 
			
		||||
 | 
			
		||||
    def get_installed_package_version(self, package):
 | 
			
		||||
        """
 | 
			
		||||
        Returns the version (versionName) of the specified package if it is installed
 | 
			
		||||
        on the device, or ``None`` otherwise.
 | 
			
		||||
 | 
			
		||||
        Added in version 2.1.4
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        output = self.execute('dumpsys package {}'.format(package))
 | 
			
		||||
        for line in convert_new_lines(output).split('\n'):
 | 
			
		||||
            if 'versionName' in line:
 | 
			
		||||
                return line.split('=', 1)[1]
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
    def list_packages(self):
 | 
			
		||||
        """
 | 
			
		||||
        List packages installed on the device.
 | 
			
		||||
 | 
			
		||||
        Added in version 2.1.4
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        output = self.execute('pm list packages')
 | 
			
		||||
        output = output.replace('package:', '')
 | 
			
		||||
        return output.split()
 | 
			
		||||
 | 
			
		||||
    def package_is_installed(self, package_name):
 | 
			
		||||
        """
 | 
			
		||||
        Returns ``True`` the if a package with the specified name is installed on
 | 
			
		||||
        the device, and ``False`` otherwise.
 | 
			
		||||
 | 
			
		||||
        Added in version 2.1.4
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        return package_name in self.list_packages()
 | 
			
		||||
 | 
			
		||||
    def executable_is_installed(self, executable_name):  # pylint: disable=unused-argument,no-self-use
 | 
			
		||||
        raise AttributeError("""Instead of using is_installed, please use
 | 
			
		||||
            ``get_binary_path`` or ``install_if_needed`` instead. You should
 | 
			
		||||
            use the path returned by these functions to then invoke the binary
 | 
			
		||||
 | 
			
		||||
            please see: https://pythonhosted.org/wlauto/writing_extensions.html""")
 | 
			
		||||
 | 
			
		||||
    def is_installed(self, name):
 | 
			
		||||
        if self.package_is_installed(name):
 | 
			
		||||
            return True
 | 
			
		||||
        elif "." in name:  # assumes android packages have a . in their name and binaries documentation
 | 
			
		||||
            return False
 | 
			
		||||
        else:
 | 
			
		||||
            raise AttributeError("""Instead of using is_installed, please use
 | 
			
		||||
                ``get_binary_path`` or ``install_if_needed`` instead. You should
 | 
			
		||||
                use the path returned by these functions to then invoke the binary
 | 
			
		||||
 | 
			
		||||
                please see: https://pythonhosted.org/wlauto/writing_extensions.html""")
 | 
			
		||||
 | 
			
		||||
    def listdir(self, path, as_root=False, **kwargs):
 | 
			
		||||
        contents = self.execute('ls {}'.format(path), as_root=as_root)
 | 
			
		||||
        return [x.strip() for x in contents.split()]
 | 
			
		||||
 | 
			
		||||
    def push_file(self, source, dest, as_root=False, timeout=default_timeout):  # pylint: disable=W0221
 | 
			
		||||
        """
 | 
			
		||||
        Modified in version 2.1.4: added  ``as_root`` parameter.
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        self._check_ready()
 | 
			
		||||
        try:
 | 
			
		||||
            if not as_root:
 | 
			
		||||
                adb_command(self.adb_name, "push '{}' '{}'".format(source, dest), timeout=timeout)
 | 
			
		||||
            else:
 | 
			
		||||
                device_tempfile = self.path.join(self.file_transfer_cache, source.lstrip(self.path.sep))
 | 
			
		||||
                self.execute('mkdir -p {}'.format(self.path.dirname(device_tempfile)))
 | 
			
		||||
                adb_command(self.adb_name, "push '{}' '{}'".format(source, device_tempfile), timeout=timeout)
 | 
			
		||||
                self.execute('cp {} {}'.format(device_tempfile, dest), as_root=True)
 | 
			
		||||
        except CalledProcessError as e:
 | 
			
		||||
            raise DeviceError(e)
 | 
			
		||||
 | 
			
		||||
    def pull_file(self, source, dest, as_root=False, timeout=default_timeout):  # pylint: disable=W0221
 | 
			
		||||
        """
 | 
			
		||||
        Modified in version 2.1.4: added  ``as_root`` parameter.
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        self._check_ready()
 | 
			
		||||
        try:
 | 
			
		||||
            if not as_root:
 | 
			
		||||
                adb_command(self.adb_name, "pull '{}' '{}'".format(source, dest), timeout=timeout)
 | 
			
		||||
            else:
 | 
			
		||||
                device_tempfile = self.path.join(self.file_transfer_cache, source.lstrip(self.path.sep))
 | 
			
		||||
                self.execute('mkdir -p {}'.format(self.path.dirname(device_tempfile)))
 | 
			
		||||
                self.execute('cp {} {}'.format(source, device_tempfile), as_root=True)
 | 
			
		||||
                adb_command(self.adb_name, "pull '{}' '{}'".format(device_tempfile, dest), timeout=timeout)
 | 
			
		||||
        except CalledProcessError as e:
 | 
			
		||||
            raise DeviceError(e)
 | 
			
		||||
 | 
			
		||||
    def delete_file(self, filepath, as_root=False):  # pylint: disable=W0221
 | 
			
		||||
        self._check_ready()
 | 
			
		||||
        adb_shell(self.adb_name, "rm '{}'".format(filepath), as_root=as_root, timeout=self.default_timeout)
 | 
			
		||||
 | 
			
		||||
    def file_exists(self, filepath):
 | 
			
		||||
        self._check_ready()
 | 
			
		||||
        output = adb_shell(self.adb_name, 'if [ -e \'{}\' ]; then echo 1; else echo 0; fi'.format(filepath),
 | 
			
		||||
                           timeout=self.default_timeout)
 | 
			
		||||
        return bool(int(output))
 | 
			
		||||
 | 
			
		||||
    def install(self, filepath, timeout=default_timeout, with_name=None):  # pylint: disable=W0221
 | 
			
		||||
        ext = os.path.splitext(filepath)[1].lower()
 | 
			
		||||
        if ext == '.apk':
 | 
			
		||||
            return self.install_apk(filepath, timeout)
 | 
			
		||||
        else:
 | 
			
		||||
            return self.install_executable(filepath, with_name)
 | 
			
		||||
 | 
			
		||||
    def install_apk(self, filepath, timeout=default_timeout):  # pylint: disable=W0221
 | 
			
		||||
        self._check_ready()
 | 
			
		||||
        ext = os.path.splitext(filepath)[1].lower()
 | 
			
		||||
        if ext == '.apk':
 | 
			
		||||
            return adb_command(self.adb_name, "install {}".format(filepath), timeout=timeout)
 | 
			
		||||
        else:
 | 
			
		||||
            raise DeviceError('Can\'t install {}: unsupported format.'.format(filepath))
 | 
			
		||||
 | 
			
		||||
    def install_executable(self, filepath, with_name=None):
 | 
			
		||||
        """
 | 
			
		||||
        Installs a binary executable on device. Returns
 | 
			
		||||
        the path to the installed binary, or ``None`` if the installation has failed.
 | 
			
		||||
        Optionally, ``with_name`` parameter may be used to specify a different name under
 | 
			
		||||
        which the executable will be installed.
 | 
			
		||||
 | 
			
		||||
        Added in version 2.1.3.
 | 
			
		||||
        Updated in version 2.1.5 with ``with_name`` parameter.
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        self._ensure_binaries_directory_is_writable()
 | 
			
		||||
        executable_name = with_name or os.path.basename(filepath)
 | 
			
		||||
        on_device_file = self.path.join(self.working_directory, executable_name)
 | 
			
		||||
        on_device_executable = self.path.join(self.binaries_directory, executable_name)
 | 
			
		||||
        self.push_file(filepath, on_device_file)
 | 
			
		||||
        self.execute('cp {} {}'.format(on_device_file, on_device_executable), as_root=self.is_rooted)
 | 
			
		||||
        self.execute('chmod 0777 {}'.format(on_device_executable), as_root=self.is_rooted)
 | 
			
		||||
        return on_device_executable
 | 
			
		||||
 | 
			
		||||
    def uninstall(self, package):
 | 
			
		||||
        self._check_ready()
 | 
			
		||||
        adb_command(self.adb_name, "uninstall {}".format(package), timeout=self.default_timeout)
 | 
			
		||||
 | 
			
		||||
    def uninstall_executable(self, executable_name):
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        Added in version 2.1.3.
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        on_device_executable = self.get_binary_path(executable_name, search_system_binaries=False)
 | 
			
		||||
        if not on_device_executable:
 | 
			
		||||
            raise DeviceError("Could not uninstall {}, binary not found".format(on_device_executable))
 | 
			
		||||
        self._ensure_binaries_directory_is_writable()
 | 
			
		||||
        self.delete_file(on_device_executable, as_root=self.is_rooted)
 | 
			
		||||
 | 
			
		||||
    def execute(self, command, timeout=default_timeout, check_exit_code=True, background=False,
 | 
			
		||||
                as_root=False, busybox=False, **kwargs):
 | 
			
		||||
        """
 | 
			
		||||
        Execute the specified command on the device using adb.
 | 
			
		||||
 | 
			
		||||
        Parameters:
 | 
			
		||||
 | 
			
		||||
            :param command: The command to be executed. It should appear exactly
 | 
			
		||||
                            as if you were typing it into a shell.
 | 
			
		||||
            :param timeout: Time, in seconds, to wait for adb to return before aborting
 | 
			
		||||
                            and raising an error. Defaults to ``AndroidDevice.default_timeout``.
 | 
			
		||||
            :param check_exit_code: If ``True``, the return code of the command on the Device will
 | 
			
		||||
                                    be check and exception will be raised if it is not 0.
 | 
			
		||||
                                    Defaults to ``True``.
 | 
			
		||||
            :param background: If ``True``, will execute adb in a subprocess, and will return
 | 
			
		||||
                               immediately, not waiting for adb to return. Defaults to ``False``
 | 
			
		||||
            :param busybox: If ``True``, will use busybox to execute the command. Defaults to ``False``.
 | 
			
		||||
 | 
			
		||||
                            Added in version 2.1.3
 | 
			
		||||
 | 
			
		||||
                            .. note:: The device must be rooted to be able to use some busybox features.
 | 
			
		||||
 | 
			
		||||
            :param as_root: If ``True``, will attempt to execute command in privileged mode. The device
 | 
			
		||||
                            must be rooted, otherwise an error will be raised. Defaults to ``False``.
 | 
			
		||||
 | 
			
		||||
                            Added in version 2.1.3
 | 
			
		||||
 | 
			
		||||
        :returns: If ``background`` parameter is set to ``True``, the subprocess object will
 | 
			
		||||
                  be returned; otherwise, the contents of STDOUT from the device will be returned.
 | 
			
		||||
 | 
			
		||||
        :raises: DeviceError if adb timed out  or if the command returned non-zero exit
 | 
			
		||||
                 code on the device, or if attempting to execute a command in privileged mode on an
 | 
			
		||||
                 unrooted device.
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        self._check_ready()
 | 
			
		||||
        if as_root and not self.is_rooted:
 | 
			
		||||
            raise DeviceError('Attempting to execute "{}" as root on unrooted device.'.format(command))
 | 
			
		||||
        if busybox:
 | 
			
		||||
            command = ' '.join([self.busybox, command])
 | 
			
		||||
        if background:
 | 
			
		||||
            return adb_background_shell(self.adb_name, command, as_root=as_root)
 | 
			
		||||
        else:
 | 
			
		||||
            return adb_shell(self.adb_name, command, timeout, check_exit_code, as_root)
 | 
			
		||||
 | 
			
		||||
    def kick_off(self, command):
 | 
			
		||||
        """
 | 
			
		||||
        Like execute but closes adb session and returns immediately, leaving the command running on the
 | 
			
		||||
        device (this is different from execute(background=True) which keeps adb connection open and returns
 | 
			
		||||
        a subprocess object).
 | 
			
		||||
 | 
			
		||||
        .. note:: This relies on busybox's nohup applet and so won't work on unrooted devices.
 | 
			
		||||
 | 
			
		||||
        Added in version 2.1.4
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        if not self.is_rooted:
 | 
			
		||||
            raise DeviceError('kick_off uses busybox\'s nohup applet and so can only be run a rooted device.')
 | 
			
		||||
        try:
 | 
			
		||||
            command = 'cd {} && busybox nohup {}'.format(self.working_directory, command)
 | 
			
		||||
            output = self.execute(command, timeout=1, as_root=True)
 | 
			
		||||
        except TimeoutError:
 | 
			
		||||
            pass
 | 
			
		||||
        else:
 | 
			
		||||
            raise ValueError('Background command exited before timeout; got "{}"'.format(output))
 | 
			
		||||
 | 
			
		||||
    def get_pids_of(self, process_name):
 | 
			
		||||
        """Returns a list of PIDs of all processes with the specified name."""
 | 
			
		||||
        result = self.execute('ps | {} grep {}'.format(self.busybox, process_name),
 | 
			
		||||
                              check_exit_code=False).strip()
 | 
			
		||||
        if result and 'not found' not in result:
 | 
			
		||||
            return [int(x.split()[1]) for x in result.split('\n')]
 | 
			
		||||
        else:
 | 
			
		||||
            return []
 | 
			
		||||
 | 
			
		||||
    def ps(self, **kwargs):
 | 
			
		||||
        """
 | 
			
		||||
        Returns the list of running processes on the device. Keyword arguments may
 | 
			
		||||
        be used to specify simple filters for columns.
 | 
			
		||||
 | 
			
		||||
        Added in version 2.1.4
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        lines = iter(convert_new_lines(self.execute('ps')).split('\n'))
 | 
			
		||||
        lines.next()  # header
 | 
			
		||||
        result = []
 | 
			
		||||
        for line in lines:
 | 
			
		||||
            parts = line.split()
 | 
			
		||||
            if parts:
 | 
			
		||||
                result.append(PsEntry(*(parts[0:1] + map(int, parts[1:5]) + parts[5:])))
 | 
			
		||||
        if not kwargs:
 | 
			
		||||
            return result
 | 
			
		||||
        else:
 | 
			
		||||
            filtered_result = []
 | 
			
		||||
            for entry in result:
 | 
			
		||||
                if all(getattr(entry, k) == v for k, v in kwargs.iteritems()):
 | 
			
		||||
                    filtered_result.append(entry)
 | 
			
		||||
            return filtered_result
 | 
			
		||||
 | 
			
		||||
    def get_properties(self, context):
 | 
			
		||||
        """Captures and saves the information from /system/build.prop and /proc/version"""
 | 
			
		||||
        props = super(AndroidDevice, self).get_properties(context)
 | 
			
		||||
        props.update(self._get_android_properties(context))
 | 
			
		||||
        return props
 | 
			
		||||
 | 
			
		||||
    def _get_android_properties(self, context):
 | 
			
		||||
        props = {}
 | 
			
		||||
        props['android_id'] = self.get_android_id()
 | 
			
		||||
        buildprop_file = os.path.join(context.host_working_directory, 'build.prop')
 | 
			
		||||
        if not os.path.isfile(buildprop_file):
 | 
			
		||||
            self.pull_file('/system/build.prop', context.host_working_directory)
 | 
			
		||||
        self._update_build_properties(buildprop_file, props)
 | 
			
		||||
        context.add_run_artifact('build_properties', buildprop_file, 'export')
 | 
			
		||||
 | 
			
		||||
        dumpsys_target_file = self.path.join(self.working_directory, 'window.dumpsys')
 | 
			
		||||
        dumpsys_host_file = os.path.join(context.host_working_directory, 'window.dumpsys')
 | 
			
		||||
        self.execute('{} > {}'.format('dumpsys window', dumpsys_target_file))
 | 
			
		||||
        self.pull_file(dumpsys_target_file, dumpsys_host_file)
 | 
			
		||||
        context.add_run_artifact('dumpsys_window', dumpsys_host_file, 'meta')
 | 
			
		||||
        return props
 | 
			
		||||
 | 
			
		||||
    def getprop(self, prop=None):
 | 
			
		||||
        """Returns parsed output of Android getprop command. If a property is
 | 
			
		||||
        specified, only the value for that property will be returned (with
 | 
			
		||||
        ``None`` returned if the property doesn't exist. Otherwise,
 | 
			
		||||
        ``wlauto.utils.android.AndroidProperties`` will be returned, which is
 | 
			
		||||
        a dict-like object."""
 | 
			
		||||
        props = AndroidProperties(self.execute('getprop'))
 | 
			
		||||
        if prop:
 | 
			
		||||
            return props[prop]
 | 
			
		||||
        return props
 | 
			
		||||
 | 
			
		||||
    # Android-specific methods. These either rely on specifics of adb or other
 | 
			
		||||
    # Android-only concepts in their interface and/or implementation.
 | 
			
		||||
 | 
			
		||||
    def forward_port(self, from_port, to_port):
 | 
			
		||||
        """
 | 
			
		||||
        Forward a port on the device to a port on localhost.
 | 
			
		||||
 | 
			
		||||
        :param from_port: Port on the device which to forward.
 | 
			
		||||
        :param to_port: Port on the localhost to which the device port will be forwarded.
 | 
			
		||||
 | 
			
		||||
        Ports should be specified using adb spec. See the "adb forward" section in "adb help".
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        adb_command(self.adb_name, 'forward {} {}'.format(from_port, to_port), timeout=self.default_timeout)
 | 
			
		||||
 | 
			
		||||
    def dump_logcat(self, outfile, filter_spec=None):
 | 
			
		||||
        """
 | 
			
		||||
        Dump the contents of logcat, for the specified filter spec to the
 | 
			
		||||
        specified output file.
 | 
			
		||||
        See http://developer.android.com/tools/help/logcat.html
 | 
			
		||||
 | 
			
		||||
        :param outfile: Output file on the host into which the contents of the
 | 
			
		||||
                        log will be written.
 | 
			
		||||
        :param filter_spec: Logcat filter specification.
 | 
			
		||||
                            see http://developer.android.com/tools/debugging/debugging-log.html#filteringOutput
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        if self._logcat_poller:
 | 
			
		||||
            return self._logcat_poller.write_log(outfile)
 | 
			
		||||
        else:
 | 
			
		||||
            if filter_spec:
 | 
			
		||||
                command = 'logcat -d -s {} > {}'.format(filter_spec, outfile)
 | 
			
		||||
            else:
 | 
			
		||||
                command = 'logcat -d > {}'.format(outfile)
 | 
			
		||||
            return adb_command(self.adb_name, command, timeout=self.default_timeout)
 | 
			
		||||
 | 
			
		||||
    def clear_logcat(self):
 | 
			
		||||
        """Clear (flush) logcat log."""
 | 
			
		||||
        if self._logcat_poller:
 | 
			
		||||
            return self._logcat_poller.clear_buffer()
 | 
			
		||||
        else:
 | 
			
		||||
            return adb_shell(self.adb_name, 'logcat -c', timeout=self.default_timeout)
 | 
			
		||||
 | 
			
		||||
    def get_screen_size(self):
 | 
			
		||||
        output = self.execute('dumpsys window')
 | 
			
		||||
        match = SCREEN_SIZE_REGEX.search(output)
 | 
			
		||||
        if match:
 | 
			
		||||
            return (int(match.group('width')),
 | 
			
		||||
                    int(match.group('height')))
 | 
			
		||||
        else:
 | 
			
		||||
            return (0, 0)
 | 
			
		||||
 | 
			
		||||
    def perform_unlock_swipe(self):
 | 
			
		||||
        width, height = self.get_screen_size()
 | 
			
		||||
        command = 'input swipe {} {} {} {}'
 | 
			
		||||
        if self.swipe_to_unlock == "horizontal":
 | 
			
		||||
            swipe_heigh = height * 2 // 3
 | 
			
		||||
            start = 100
 | 
			
		||||
            stop = width - start
 | 
			
		||||
            self.execute(command.format(start, swipe_heigh, stop, swipe_heigh))
 | 
			
		||||
        if self.swipe_to_unlock == "vertical":
 | 
			
		||||
            swipe_middle = height / 2
 | 
			
		||||
            swipe_heigh = height * 2 // 3
 | 
			
		||||
            self.execute(command.format(swipe_middle, swipe_heigh, swipe_middle, 0))
 | 
			
		||||
        else:  # Should never reach here
 | 
			
		||||
            raise DeviceError("Invalid swipe direction: {}".format(self.swipe_to_unlock))
 | 
			
		||||
 | 
			
		||||
    def capture_screen(self, filepath):
 | 
			
		||||
        """Caputers the current device screen into the specified file in a PNG format."""
 | 
			
		||||
        on_device_file = self.path.join(self.working_directory, 'screen_capture.png')
 | 
			
		||||
        self.execute('screencap -p  {}'.format(on_device_file))
 | 
			
		||||
        self.pull_file(on_device_file, filepath)
 | 
			
		||||
        self.delete_file(on_device_file)
 | 
			
		||||
 | 
			
		||||
    def is_screen_on(self):
 | 
			
		||||
        """Returns ``True`` if the device screen is currently on, ``False`` otherwise."""
 | 
			
		||||
        output = self.execute('dumpsys power')
 | 
			
		||||
        match = SCREEN_STATE_REGEX.search(output)
 | 
			
		||||
        if match:
 | 
			
		||||
            return boolean(match.group(1))
 | 
			
		||||
        else:
 | 
			
		||||
            raise DeviceError('Could not establish screen state.')
 | 
			
		||||
 | 
			
		||||
    def ensure_screen_is_on(self):
 | 
			
		||||
        if not self.is_screen_on():
 | 
			
		||||
            self.execute('input keyevent 26')
 | 
			
		||||
            if self.swipe_to_unlock:
 | 
			
		||||
                self.perform_unlock_swipe()
 | 
			
		||||
 | 
			
		||||
    def disable_screen_lock(self):
 | 
			
		||||
        """
 | 
			
		||||
        Attempts to disable he screen lock on the device.
 | 
			
		||||
 | 
			
		||||
        .. note:: This does not always work...
 | 
			
		||||
 | 
			
		||||
        Added inversion 2.1.4
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        lockdb = '/data/system/locksettings.db'
 | 
			
		||||
        sqlcommand = "update locksettings set value='0' where name='screenlock.disabled';"
 | 
			
		||||
        self.execute('sqlite3 {} "{}"'.format(lockdb, sqlcommand), as_root=True)
 | 
			
		||||
 | 
			
		||||
    def disable_selinux(self):
 | 
			
		||||
        # This may be invoked from intialize() so we can't use execute() or the
 | 
			
		||||
        # standard API for doing this.
 | 
			
		||||
        api_level = int(adb_shell(self.adb_name, 'getprop ro.build.version.sdk',
 | 
			
		||||
                                  timeout=self.default_timeout).strip())
 | 
			
		||||
        # SELinux was added in Android 4.3 (API level 18). Trying to
 | 
			
		||||
        # 'getenforce' in earlier versions will produce an error.
 | 
			
		||||
        if api_level >= 18:
 | 
			
		||||
            se_status = self.execute('getenforce', as_root=True).strip()
 | 
			
		||||
            if se_status == 'Enforcing':
 | 
			
		||||
                self.execute('setenforce 0', as_root=True)
 | 
			
		||||
 | 
			
		||||
    def get_device_model(self):
 | 
			
		||||
        try:
 | 
			
		||||
            return self.getprop(prop='ro.product.device')
 | 
			
		||||
        except KeyError:
 | 
			
		||||
            return None
 | 
			
		||||
 | 
			
		||||
    # Internal methods: do not use outside of the class.
 | 
			
		||||
 | 
			
		||||
    def _update_build_properties(self, filepath, props):
 | 
			
		||||
        try:
 | 
			
		||||
            with open(filepath) as fh:
 | 
			
		||||
                for line in fh:
 | 
			
		||||
                    line = re.sub(r'#.*', '', line).strip()
 | 
			
		||||
                    if not line:
 | 
			
		||||
                        continue
 | 
			
		||||
                    key, value = line.split('=', 1)
 | 
			
		||||
                    props[key] = value
 | 
			
		||||
        except ValueError:
 | 
			
		||||
            self.logger.warning('Could not parse build.prop.')
 | 
			
		||||
 | 
			
		||||
    def _update_versions(self, filepath, props):
 | 
			
		||||
        with open(filepath) as fh:
 | 
			
		||||
            text = fh.read()
 | 
			
		||||
            props['version'] = text
 | 
			
		||||
            text = re.sub(r'#.*', '', text).strip()
 | 
			
		||||
            match = re.search(r'^(Linux version .*?)\s*\((gcc version .*)\)$', text)
 | 
			
		||||
            if match:
 | 
			
		||||
                props['linux_version'] = match.group(1).strip()
 | 
			
		||||
                props['gcc_version'] = match.group(2).strip()
 | 
			
		||||
            else:
 | 
			
		||||
                self.logger.warning('Could not parse version string.')
 | 
			
		||||
 | 
			
		||||
    def _ensure_binaries_directory_is_writable(self):
 | 
			
		||||
        matched = []
 | 
			
		||||
        for entry in self.list_file_systems():
 | 
			
		||||
            if self.binaries_directory.rstrip('/').startswith(entry.mount_point):
 | 
			
		||||
                matched.append(entry)
 | 
			
		||||
        if matched:
 | 
			
		||||
            entry = sorted(matched, key=lambda x: len(x.mount_point))[-1]
 | 
			
		||||
            if 'rw' not in entry.options:
 | 
			
		||||
                self.execute('mount -o rw,remount {} {}'.format(entry.device, entry.mount_point), as_root=True)
 | 
			
		||||
        else:
 | 
			
		||||
            raise DeviceError('Could not find mount point for binaries directory {}'.format(self.binaries_directory))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class _LogcatPoller(threading.Thread):
 | 
			
		||||
 | 
			
		||||
    join_timeout = 5
 | 
			
		||||
 | 
			
		||||
    def __init__(self, device, period, timeout=None):
 | 
			
		||||
        super(_LogcatPoller, self).__init__()
 | 
			
		||||
        self.adb_device = device.adb_name
 | 
			
		||||
        self.logger = device.logger
 | 
			
		||||
        self.period = period
 | 
			
		||||
        self.timeout = timeout
 | 
			
		||||
        self.stop_signal = threading.Event()
 | 
			
		||||
        self.lock = threading.RLock()
 | 
			
		||||
        self.buffer_file = tempfile.mktemp()
 | 
			
		||||
        self.last_poll = 0
 | 
			
		||||
        self.daemon = True
 | 
			
		||||
        self.exc = None
 | 
			
		||||
 | 
			
		||||
    def run(self):
 | 
			
		||||
        self.logger.debug('Starting logcat polling.')
 | 
			
		||||
        try:
 | 
			
		||||
            while True:
 | 
			
		||||
                if self.stop_signal.is_set():
 | 
			
		||||
                    break
 | 
			
		||||
                with self.lock:
 | 
			
		||||
                    current_time = time.time()
 | 
			
		||||
                    if (current_time - self.last_poll) >= self.period:
 | 
			
		||||
                        self._poll()
 | 
			
		||||
                time.sleep(0.5)
 | 
			
		||||
        except Exception:  # pylint: disable=W0703
 | 
			
		||||
            self.exc = WorkerThreadError(self.name, sys.exc_info())
 | 
			
		||||
        self.logger.debug('Logcat polling stopped.')
 | 
			
		||||
 | 
			
		||||
    def stop(self):
 | 
			
		||||
        self.logger.debug('Stopping logcat polling.')
 | 
			
		||||
        self.stop_signal.set()
 | 
			
		||||
        self.join(self.join_timeout)
 | 
			
		||||
        if self.is_alive():
 | 
			
		||||
            self.logger.error('Could not join logcat poller thread.')
 | 
			
		||||
        if self.exc:
 | 
			
		||||
            raise self.exc  # pylint: disable=E0702
 | 
			
		||||
 | 
			
		||||
    def clear_buffer(self):
 | 
			
		||||
        self.logger.debug('Clearing logcat buffer.')
 | 
			
		||||
        with self.lock:
 | 
			
		||||
            adb_shell(self.adb_device, 'logcat -c', timeout=self.timeout)
 | 
			
		||||
            with open(self.buffer_file, 'w') as _:  # NOQA
 | 
			
		||||
                pass
 | 
			
		||||
 | 
			
		||||
    def write_log(self, outfile):
 | 
			
		||||
        self.logger.debug('Writing logbuffer to {}.'.format(outfile))
 | 
			
		||||
        with self.lock:
 | 
			
		||||
            self._poll()
 | 
			
		||||
            if os.path.isfile(self.buffer_file):
 | 
			
		||||
                shutil.copy(self.buffer_file, outfile)
 | 
			
		||||
            else:  # there was no logcat trace at this time
 | 
			
		||||
                with open(outfile, 'w') as _:  # NOQA
 | 
			
		||||
                    pass
 | 
			
		||||
 | 
			
		||||
    def close(self):
 | 
			
		||||
        self.logger.debug('Closing logcat poller.')
 | 
			
		||||
        if os.path.isfile(self.buffer_file):
 | 
			
		||||
            os.remove(self.buffer_file)
 | 
			
		||||
 | 
			
		||||
    def _poll(self):
 | 
			
		||||
        with self.lock:
 | 
			
		||||
            self.last_poll = time.time()
 | 
			
		||||
            adb_command(self.adb_device, 'logcat -d >> {}'.format(self.buffer_file), timeout=self.timeout)
 | 
			
		||||
            adb_command(self.adb_device, 'logcat -c', timeout=self.timeout)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BigLittleDevice(AndroidDevice):  # pylint: disable=W0223
 | 
			
		||||
 | 
			
		||||
    parameters = [
 | 
			
		||||
        Parameter('scheduler', default='hmp', override=True),
 | 
			
		||||
    ]
 | 
			
		||||
@@ -1,684 +0,0 @@
 | 
			
		||||
#    Copyright 2014-2015 ARM Limited
 | 
			
		||||
#
 | 
			
		||||
# Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
# you may not use this file except in compliance with the License.
 | 
			
		||||
# You may obtain a copy of the License at
 | 
			
		||||
#
 | 
			
		||||
#     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
#
 | 
			
		||||
# Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
# distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
# See the License for the specific language governing permissions and
 | 
			
		||||
# limitations under the License.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
# Original implementation by Rene de Jong. Updated by Sascha Bischoff.
 | 
			
		||||
 | 
			
		||||
# pylint: disable=E1101
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
import os
 | 
			
		||||
import re
 | 
			
		||||
import shutil
 | 
			
		||||
import socket
 | 
			
		||||
import subprocess
 | 
			
		||||
import sys
 | 
			
		||||
import tarfile
 | 
			
		||||
import time
 | 
			
		||||
from pexpect import EOF, TIMEOUT, pxssh
 | 
			
		||||
 | 
			
		||||
from wlauto import settings, Parameter
 | 
			
		||||
from wlauto.core.resource import NO_ONE
 | 
			
		||||
from wlauto.common.resources import Executable
 | 
			
		||||
from wlauto.core import signal as sig
 | 
			
		||||
from wlauto.exceptions import DeviceError
 | 
			
		||||
from wlauto.utils import ssh, types
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BaseGem5Device(object):
 | 
			
		||||
    """
 | 
			
		||||
    Base implementation for a gem5-based device
 | 
			
		||||
 | 
			
		||||
    This class is used as the base class for OS-specific devices such as the
 | 
			
		||||
    G3m5LinuxDevice and the Gem5AndroidDevice. The majority of the gem5-specific
 | 
			
		||||
    functionality is included here.
 | 
			
		||||
 | 
			
		||||
    Note: When inheriting from this class, make sure to inherit from this class
 | 
			
		||||
    prior to inheriting from the OS-specific class, i.e. LinuxDevice, to ensure
 | 
			
		||||
    that the methods are correctly overridden.
 | 
			
		||||
    """
 | 
			
		||||
    # gem5 can be very slow. Hence, we use some very long timeouts!
 | 
			
		||||
    delay = 3600
 | 
			
		||||
    long_delay = 3 * delay
 | 
			
		||||
    ready_timeout = long_delay
 | 
			
		||||
    default_timeout = delay
 | 
			
		||||
 | 
			
		||||
    platform = None
 | 
			
		||||
    path_module = 'posixpath'
 | 
			
		||||
 | 
			
		||||
    parameters = [
 | 
			
		||||
        Parameter('gem5_binary', kind=str, default='./build/ARM/gem5.fast',
 | 
			
		||||
                  mandatory=False, description="Command used to execute gem5. "
 | 
			
		||||
                  "Adjust according to needs."),
 | 
			
		||||
        Parameter('gem5_args', kind=types.arguments, mandatory=True,
 | 
			
		||||
                  description="Command line passed to the gem5 simulation. This"
 | 
			
		||||
                  " command line is used to set up the simulated system, and "
 | 
			
		||||
                  "should be the same as used for a standard gem5 simulation "
 | 
			
		||||
                  "without workload automation. Note that this is simulation "
 | 
			
		||||
                  "script specific and will hence need to be tailored to each "
 | 
			
		||||
                  "particular use case."),
 | 
			
		||||
        Parameter('gem5_vio_args', kind=types.arguments, mandatory=True,
 | 
			
		||||
                  constraint=lambda x: "{}" in str(x),
 | 
			
		||||
                  description="gem5 VirtIO command line used to enable the "
 | 
			
		||||
                  "VirtIO device in the simulated system. At the very least, "
 | 
			
		||||
                  "the root parameter of the VirtIO9PDiod device must be "
 | 
			
		||||
                  "exposed on the command line. Please set this root mount to "
 | 
			
		||||
                  "{}, as it will be replaced with the directory used by "
 | 
			
		||||
                  "Workload Automation at runtime."),
 | 
			
		||||
        Parameter('temp_dir', kind=str, default='/tmp',
 | 
			
		||||
                  description="Temporary directory used to pass files into the "
 | 
			
		||||
                  "gem5 simulation. Workload Automation will automatically "
 | 
			
		||||
                  "create a directory in this folder, and will remove it again "
 | 
			
		||||
                  "once the simulation completes."),
 | 
			
		||||
        Parameter('checkpoint', kind=bool, default=False,
 | 
			
		||||
                  mandatory=False, description="This parameter "
 | 
			
		||||
                  "tells Workload Automation to create a checkpoint of the "
 | 
			
		||||
                  "simulated system once the guest system has finished booting."
 | 
			
		||||
                  " This checkpoint can then be used at a later stage by other "
 | 
			
		||||
                  "WA runs to avoid booting the guest system a second time. Set"
 | 
			
		||||
                  " to True to take a checkpoint of the simulated system post "
 | 
			
		||||
                  "boot."),
 | 
			
		||||
        Parameter('run_delay', kind=int, default=0, mandatory=False,
 | 
			
		||||
                  constraint=lambda x: x >= 0,
 | 
			
		||||
                  description="This sets the time that the "
 | 
			
		||||
                  "system should sleep in the simulated system prior to "
 | 
			
		||||
                  "running and workloads or taking checkpoints. This allows "
 | 
			
		||||
                  "the system to quieten down prior to running the workloads. "
 | 
			
		||||
                  "When this is combined with the checkpoint_post_boot"
 | 
			
		||||
                  " option, it allows the checkpoint to be created post-sleep,"
 | 
			
		||||
                  " and therefore the set of workloads resuming from this "
 | 
			
		||||
                  "checkpoint will not be required to sleep.")
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def is_rooted(self):  # pylint: disable=R0201
 | 
			
		||||
        # gem5 is always rooted
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
    # pylint: disable=E0203
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        self.logger = logging.getLogger('gem5Device')
 | 
			
		||||
 | 
			
		||||
        # The gem5 subprocess
 | 
			
		||||
        self.gem5 = None
 | 
			
		||||
        self.gem5_port = -1
 | 
			
		||||
        self.gem5outdir = os.path.join(settings.output_directory, "gem5")
 | 
			
		||||
        self.m5_path = 'm5'
 | 
			
		||||
        self.stdout_file = None
 | 
			
		||||
        self.stderr_file = None
 | 
			
		||||
        self.stderr_filename = None
 | 
			
		||||
        self.sckt = None
 | 
			
		||||
 | 
			
		||||
        # Find the first one that does not exist. Ensures that we do not re-use
 | 
			
		||||
        # the directory used by someone else.
 | 
			
		||||
        for i in xrange(sys.maxint):
 | 
			
		||||
            directory = os.path.join(self.temp_dir, "wa_{}".format(i))
 | 
			
		||||
            try:
 | 
			
		||||
                os.stat(directory)
 | 
			
		||||
                continue
 | 
			
		||||
            except OSError:
 | 
			
		||||
                break
 | 
			
		||||
        self.temp_dir = directory
 | 
			
		||||
        self.logger.debug("Using {} as the temporary directory.".format(self.temp_dir))
 | 
			
		||||
 | 
			
		||||
        # Start the gem5 simulation when WA starts a run using a signal.
 | 
			
		||||
        sig.connect(self.init_gem5, sig.RUN_START)
 | 
			
		||||
 | 
			
		||||
    def validate(self):
 | 
			
		||||
        # Assemble the virtio args
 | 
			
		||||
        self.gem5_vio_args = str(self.gem5_vio_args).format(self.temp_dir)  # pylint: disable=W0201
 | 
			
		||||
        self.logger.debug("gem5 VirtIO command: {}".format(self.gem5_vio_args))
 | 
			
		||||
 | 
			
		||||
    def init_gem5(self, _):
 | 
			
		||||
        """
 | 
			
		||||
        Start gem5, find out the telnet port and connect to the simulation.
 | 
			
		||||
 | 
			
		||||
        We first create the temporary directory used by VirtIO to pass files
 | 
			
		||||
        into the simulation, as well as the gem5 output directory.We then create
 | 
			
		||||
        files for the standard output and error for the gem5 process. The gem5
 | 
			
		||||
        process then is started.
 | 
			
		||||
        """
 | 
			
		||||
        self.logger.info("Creating temporary directory: {}".format(self.temp_dir))
 | 
			
		||||
        os.mkdir(self.temp_dir)
 | 
			
		||||
        os.mkdir(self.gem5outdir)
 | 
			
		||||
 | 
			
		||||
        # We need to redirect the standard output and standard error for the
 | 
			
		||||
        # gem5 process to a file so that we can debug when things go wrong.
 | 
			
		||||
        f = os.path.join(self.gem5outdir, 'stdout')
 | 
			
		||||
        self.stdout_file = open(f, 'w')
 | 
			
		||||
        f = os.path.join(self.gem5outdir, 'stderr')
 | 
			
		||||
        self.stderr_file = open(f, 'w')
 | 
			
		||||
        # We need to keep this so we can check which port to use for the telnet
 | 
			
		||||
        # connection.
 | 
			
		||||
        self.stderr_filename = f
 | 
			
		||||
 | 
			
		||||
        self.start_gem5()
 | 
			
		||||
 | 
			
		||||
    def start_gem5(self):
 | 
			
		||||
        """
 | 
			
		||||
        Starts the gem5 simulator, and parses the output to get the telnet port.
 | 
			
		||||
        """
 | 
			
		||||
        self.logger.info("Starting the gem5 simulator")
 | 
			
		||||
 | 
			
		||||
        command_line = "{} --outdir={}/gem5 {} {}".format(self.gem5_binary,
 | 
			
		||||
                                                          settings.output_directory,
 | 
			
		||||
                                                          self.gem5_args,
 | 
			
		||||
                                                          self.gem5_vio_args)
 | 
			
		||||
        self.logger.debug("gem5 command line: {}".format(command_line))
 | 
			
		||||
        self.gem5 = subprocess.Popen(command_line.split(),
 | 
			
		||||
                                     stdout=self.stdout_file,
 | 
			
		||||
                                     stderr=self.stderr_file)
 | 
			
		||||
 | 
			
		||||
        while self.gem5_port == -1:
 | 
			
		||||
            # Check that gem5 is running!
 | 
			
		||||
            if self.gem5.poll():
 | 
			
		||||
                raise DeviceError("The gem5 process has crashed with error code {}!".format(self.gem5.poll()))
 | 
			
		||||
 | 
			
		||||
            # Open the stderr file
 | 
			
		||||
            f = open(self.stderr_filename, 'r')
 | 
			
		||||
            for line in f:
 | 
			
		||||
                m = re.search(r"Listening\ for\ system\ connection\ on\ port\ (?P<port>\d+)", line)
 | 
			
		||||
                if m:
 | 
			
		||||
                    port = int(m.group('port'))
 | 
			
		||||
                    if port >= 3456 and port < 5900:
 | 
			
		||||
                        self.gem5_port = port
 | 
			
		||||
                        f.close()
 | 
			
		||||
                        break
 | 
			
		||||
            else:
 | 
			
		||||
                time.sleep(1)
 | 
			
		||||
            f.close()
 | 
			
		||||
 | 
			
		||||
    def connect(self):  # pylint: disable=R0912,W0201
 | 
			
		||||
        """
 | 
			
		||||
        Connect to the gem5 simulation and wait for Android to boot. Then,
 | 
			
		||||
        create checkpoints, and mount the VirtIO device.
 | 
			
		||||
        """
 | 
			
		||||
        self.connect_gem5()
 | 
			
		||||
 | 
			
		||||
        self.wait_for_boot()
 | 
			
		||||
 | 
			
		||||
        if self.run_delay:
 | 
			
		||||
            self.logger.info("Sleeping for {} seconds in the guest".format(self.run_delay))
 | 
			
		||||
            self.gem5_shell("sleep {}".format(self.run_delay))
 | 
			
		||||
 | 
			
		||||
        if self.checkpoint:
 | 
			
		||||
            self.checkpoint_gem5()
 | 
			
		||||
 | 
			
		||||
        self.mount_virtio()
 | 
			
		||||
        self.logger.info("Creating the working directory in the simulated system")
 | 
			
		||||
        self.gem5_shell('mkdir -p {}'.format(self.working_directory))
 | 
			
		||||
        self._is_ready = True  # pylint: disable=W0201
 | 
			
		||||
 | 
			
		||||
    def wait_for_boot(self):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def connect_gem5(self):  # pylint: disable=R0912
 | 
			
		||||
        """
 | 
			
		||||
        Connect to the telnet port of the gem5 simulation.
 | 
			
		||||
 | 
			
		||||
        We connect, and wait for the prompt to be found. We do not use a timeout
 | 
			
		||||
        for this, and wait for the prompt in a while loop as the gem5 simulation
 | 
			
		||||
        can take many hours to reach a prompt when booting the system. We also
 | 
			
		||||
        inject some newlines periodically to try and force gem5 to show a
 | 
			
		||||
        prompt. Once the prompt has been found, we replace it with a unique
 | 
			
		||||
        prompt to ensure that we are able to match it properly. We also disable
 | 
			
		||||
        the echo as this simplifies parsing the output when executing commands
 | 
			
		||||
        on the device.
 | 
			
		||||
        """
 | 
			
		||||
        self.logger.info("Connecting to the gem5 simulation on port {}".format(self.gem5_port))
 | 
			
		||||
        host = socket.gethostname()
 | 
			
		||||
        port = self.gem5_port
 | 
			
		||||
 | 
			
		||||
        # Connect to the gem5 telnet port. Use a short timeout here.
 | 
			
		||||
        attempts = 0
 | 
			
		||||
        while attempts < 10:
 | 
			
		||||
            attempts += 1
 | 
			
		||||
            try:
 | 
			
		||||
                self.sckt = ssh.TelnetConnection()
 | 
			
		||||
                self.sckt.login(host, 'None', port=port, auto_prompt_reset=False,
 | 
			
		||||
                                login_timeout=10)
 | 
			
		||||
                break
 | 
			
		||||
            except pxssh.ExceptionPxssh:
 | 
			
		||||
                pass
 | 
			
		||||
        else:
 | 
			
		||||
            self.gem5.kill()
 | 
			
		||||
            raise DeviceError("Failed to connect to the gem5 telnet session.")
 | 
			
		||||
 | 
			
		||||
        self.logger.info("Connected! Waiting for prompt...")
 | 
			
		||||
 | 
			
		||||
        # We need to find the prompt. It might be different if we are resuming
 | 
			
		||||
        # from a checkpoint. Therefore, we test multiple options here.
 | 
			
		||||
        prompt_found = False
 | 
			
		||||
        while not prompt_found:
 | 
			
		||||
            try:
 | 
			
		||||
                self.login_to_device()
 | 
			
		||||
            except TIMEOUT:
 | 
			
		||||
                pass
 | 
			
		||||
            try:
 | 
			
		||||
                # Try and force a prompt to be shown
 | 
			
		||||
                self.sckt.send('\n')
 | 
			
		||||
                self.sckt.expect([r'# ', self.sckt.UNIQUE_PROMPT, r'\[PEXPECT\][\\\$\#]+ '], timeout=60)
 | 
			
		||||
                prompt_found = True
 | 
			
		||||
            except TIMEOUT:
 | 
			
		||||
                pass
 | 
			
		||||
 | 
			
		||||
        self.logger.info("Setting unique prompt...")
 | 
			
		||||
 | 
			
		||||
        self.sckt.set_unique_prompt()
 | 
			
		||||
        self.sckt.prompt()
 | 
			
		||||
        self.logger.info("Prompt found and replaced with a unique string")
 | 
			
		||||
 | 
			
		||||
        # We check that the prompt is what we think it should be. If not, we
 | 
			
		||||
        # need to update the regex we use to match.
 | 
			
		||||
        self.find_prompt()
 | 
			
		||||
 | 
			
		||||
        self.sckt.setecho(False)
 | 
			
		||||
        self.sync_gem5_shell()
 | 
			
		||||
        self.resize_shell()
 | 
			
		||||
 | 
			
		||||
    def get_properties(self, context):  # pylint: disable=R0801
 | 
			
		||||
        """ Get the property files from the device """
 | 
			
		||||
        for propfile in self.property_files:
 | 
			
		||||
            try:
 | 
			
		||||
                normname = propfile.lstrip(self.path.sep).replace(self.path.sep, '.')
 | 
			
		||||
                outfile = os.path.join(context.host_working_directory, normname)
 | 
			
		||||
                if self.is_file(propfile):
 | 
			
		||||
                    self.execute('cat {} > {}'.format(propfile, normname))
 | 
			
		||||
                    self.pull_file(normname, outfile)
 | 
			
		||||
                elif self.is_directory(propfile):
 | 
			
		||||
                    self.get_directory(context, propfile)
 | 
			
		||||
                    continue
 | 
			
		||||
                else:
 | 
			
		||||
                    continue
 | 
			
		||||
            except DeviceError:
 | 
			
		||||
                # We pull these files "opportunistically", so if a pull fails
 | 
			
		||||
                # (e.g. we don't have permissions to read the file), just note
 | 
			
		||||
                # it quietly (not as an error/warning) and move on.
 | 
			
		||||
                self.logger.debug('Could not pull property file "{}"'.format(propfile))
 | 
			
		||||
        return {}
 | 
			
		||||
 | 
			
		||||
    def get_directory(self, context, directory):
 | 
			
		||||
        """ Pull a directory from the device """
 | 
			
		||||
        normname = directory.lstrip(self.path.sep).replace(self.path.sep, '.')
 | 
			
		||||
        outdir = os.path.join(context.host_working_directory, normname)
 | 
			
		||||
        temp_file = os.path.join(context.host_working_directory, "{}.tar".format(normname))
 | 
			
		||||
        # Check that the folder exists
 | 
			
		||||
        self.gem5_shell("ls -la {}".format(directory))
 | 
			
		||||
        # Compress the folder
 | 
			
		||||
        try:
 | 
			
		||||
            self.gem5_shell("{} tar -cvf {}.tar {}".format(self.busybox, normname, directory))
 | 
			
		||||
        except DeviceError:
 | 
			
		||||
            self.logger.debug("Failed to run tar command on device! Not pulling {}".format(directory))
 | 
			
		||||
            return
 | 
			
		||||
        self.pull_file(normname, temp_file)
 | 
			
		||||
        f = tarfile.open(temp_file, 'r')
 | 
			
		||||
        os.mkdir(outdir)
 | 
			
		||||
        f.extractall(outdir)
 | 
			
		||||
        os.remove(temp_file)
 | 
			
		||||
 | 
			
		||||
    def get_pids_of(self, process_name):
 | 
			
		||||
        """ Returns a list of PIDs of all processes with the specified name. """
 | 
			
		||||
        result = self.gem5_shell('ps | {} grep {}'.format(self.busybox, process_name),
 | 
			
		||||
                                 check_exit_code=False).strip()
 | 
			
		||||
        if result and 'not found' not in result and len(result.split('\n')) > 2:
 | 
			
		||||
            return [int(x.split()[1]) for x in result.split('\n')]
 | 
			
		||||
        else:
 | 
			
		||||
            return []
 | 
			
		||||
 | 
			
		||||
    def find_prompt(self):
 | 
			
		||||
        prompt = r'\[PEXPECT\][\\\$\#]+ '
 | 
			
		||||
        synced = False
 | 
			
		||||
        while not synced:
 | 
			
		||||
            self.sckt.send('\n')
 | 
			
		||||
            i = self.sckt.expect([prompt, self.sckt.UNIQUE_PROMPT, r'[\$\#] '], timeout=self.delay)
 | 
			
		||||
            if i == 0:
 | 
			
		||||
                synced = True
 | 
			
		||||
            elif i == 1:
 | 
			
		||||
                prompt = self.sckt.UNIQUE_PROMPT
 | 
			
		||||
                synced = True
 | 
			
		||||
            else:
 | 
			
		||||
                prompt = re.sub(r'\$', r'\\\$', self.sckt.before.strip() + self.sckt.after.strip())
 | 
			
		||||
                prompt = re.sub(r'\#', r'\\\#', prompt)
 | 
			
		||||
                prompt = re.sub(r'\[', r'\[', prompt)
 | 
			
		||||
                prompt = re.sub(r'\]', r'\]', prompt)
 | 
			
		||||
 | 
			
		||||
        self.sckt.PROMPT = prompt
 | 
			
		||||
 | 
			
		||||
    def close(self):
 | 
			
		||||
        if self._logcat_poller:
 | 
			
		||||
            self._logcat_poller.stop()
 | 
			
		||||
 | 
			
		||||
    def reset(self):
 | 
			
		||||
        self.logger.warn("Attempt to restart the gem5 device. This is not "
 | 
			
		||||
                         "supported!")
 | 
			
		||||
 | 
			
		||||
    # pylint: disable=unused-argument
 | 
			
		||||
    def push_file(self, source, dest, **kwargs):
 | 
			
		||||
        """
 | 
			
		||||
        Push a file to the gem5 device using VirtIO
 | 
			
		||||
 | 
			
		||||
        The file to push to the device is copied to the temporary directory on
 | 
			
		||||
        the host, before being copied within the simulation to the destination.
 | 
			
		||||
        Checks, in the form of 'ls' with error code checking, are performed to
 | 
			
		||||
        ensure that the file is copied to the destination.
 | 
			
		||||
        """
 | 
			
		||||
        filename = os.path.basename(source)
 | 
			
		||||
        self.logger.debug("Pushing {} to device.".format(source))
 | 
			
		||||
        self.logger.debug("temp_dir: {}".format(self.temp_dir))
 | 
			
		||||
        self.logger.debug("dest: {}".format(dest))
 | 
			
		||||
        self.logger.debug("filename: {}".format(filename))
 | 
			
		||||
 | 
			
		||||
        # We need to copy the file to copy to the temporary directory
 | 
			
		||||
        self.move_to_temp_dir(source)
 | 
			
		||||
 | 
			
		||||
        # Back to the gem5 world
 | 
			
		||||
        self.gem5_shell("ls -al /mnt/obb/{}".format(filename))
 | 
			
		||||
        if self.busybox:
 | 
			
		||||
            self.gem5_shell("{} cp /mnt/obb/{} {}".format(self.busybox, filename, dest))
 | 
			
		||||
        else:
 | 
			
		||||
            self.gem5_shell("cat /mnt/obb/{} > {}".format(filename, dest))
 | 
			
		||||
        self.gem5_shell("sync")
 | 
			
		||||
        self.gem5_shell("ls -al {}".format(dest))
 | 
			
		||||
        self.gem5_shell("ls -al /mnt/obb/")
 | 
			
		||||
        self.logger.debug("Push complete.")
 | 
			
		||||
 | 
			
		||||
    # pylint: disable=unused-argument
 | 
			
		||||
    def pull_file(self, source, dest, **kwargs):
 | 
			
		||||
        """
 | 
			
		||||
        Pull a file from the gem5 device using m5 writefile
 | 
			
		||||
 | 
			
		||||
        The file is copied to the local directory within the guest as the m5
 | 
			
		||||
        writefile command assumes that the file is local. The file is then
 | 
			
		||||
        written out to the host system using writefile, prior to being moved to
 | 
			
		||||
        the destination on the host.
 | 
			
		||||
        """
 | 
			
		||||
        filename = os.path.basename(source)
 | 
			
		||||
 | 
			
		||||
        self.logger.debug("pull_file {} {}".format(source, filename))
 | 
			
		||||
        # We don't check the exit code here because it is non-zero if the source
 | 
			
		||||
        # and destination are the same. The ls below will cause an error if the
 | 
			
		||||
        # file was not where we expected it to be.
 | 
			
		||||
        self.gem5_shell("{} cp {} {}".format(self.busybox, source, filename),
 | 
			
		||||
                        check_exit_code=False)
 | 
			
		||||
        self.gem5_shell("sync")
 | 
			
		||||
        self.gem5_shell("ls -la {}".format(filename))
 | 
			
		||||
        self.logger.debug('Finished the copy in the simulator')
 | 
			
		||||
        self.gem5_util("writefile {}".format(filename))
 | 
			
		||||
 | 
			
		||||
        if 'cpu' not in filename:
 | 
			
		||||
            while not os.path.exists(os.path.join(self.gem5outdir, filename)):
 | 
			
		||||
                time.sleep(1)
 | 
			
		||||
 | 
			
		||||
        # Perform the local move
 | 
			
		||||
        shutil.move(os.path.join(self.gem5outdir, filename), dest)
 | 
			
		||||
        self.logger.debug("Pull complete.")
 | 
			
		||||
 | 
			
		||||
    # pylint: disable=unused-argument
 | 
			
		||||
    def delete_file(self, filepath, **kwargs):
 | 
			
		||||
        """ Delete a file on the device """
 | 
			
		||||
        self._check_ready()
 | 
			
		||||
        self.gem5_shell("rm '{}'".format(filepath))
 | 
			
		||||
 | 
			
		||||
    def file_exists(self, filepath):
 | 
			
		||||
        """ Check if a file exists """
 | 
			
		||||
        self._check_ready()
 | 
			
		||||
        output = self.gem5_shell('if [ -e \'{}\' ]; then echo 1; else echo 0; fi'.format(filepath))
 | 
			
		||||
        try:
 | 
			
		||||
            if int(output):
 | 
			
		||||
                return True
 | 
			
		||||
        except ValueError:
 | 
			
		||||
            # If we cannot process the output, assume that there is no file
 | 
			
		||||
            pass
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
    def disconnect(self):
 | 
			
		||||
        """
 | 
			
		||||
        Close and disconnect from the gem5 simulation. Additionally, we remove
 | 
			
		||||
        the temporary directory used to pass files into the simulation.
 | 
			
		||||
        """
 | 
			
		||||
        self.logger.info("Gracefully terminating the gem5 simulation.")
 | 
			
		||||
        try:
 | 
			
		||||
            self.gem5_util("exit")
 | 
			
		||||
            self.gem5.wait()
 | 
			
		||||
        except EOF:
 | 
			
		||||
            pass
 | 
			
		||||
        self.logger.info("Removing the temporary directory")
 | 
			
		||||
        try:
 | 
			
		||||
            shutil.rmtree(self.temp_dir)
 | 
			
		||||
        except OSError:
 | 
			
		||||
            self.logger.warn("Failed to remove the temporary directory!")
 | 
			
		||||
 | 
			
		||||
    # gem5 might be slow. Hence, we need to make the ping timeout very long.
 | 
			
		||||
    def ping(self):
 | 
			
		||||
        self.logger.debug("Pinging gem5 to see if it is still alive")
 | 
			
		||||
        self.gem5_shell('ls /', timeout=self.longdelay)
 | 
			
		||||
 | 
			
		||||
    # Additional Android-specific methods.
 | 
			
		||||
    def forward_port(self, _):  # pylint: disable=R0201
 | 
			
		||||
        raise DeviceError('we do not need forwarding')
 | 
			
		||||
 | 
			
		||||
    # gem5 should dump out a framebuffer. We can use this if it exists. Failing
 | 
			
		||||
    # that, fall back to the parent class implementation.
 | 
			
		||||
    def capture_screen(self, filepath):
 | 
			
		||||
        file_list = os.listdir(self.gem5outdir)
 | 
			
		||||
        screen_caps = []
 | 
			
		||||
        for f in file_list:
 | 
			
		||||
            if '.bmp' in f:
 | 
			
		||||
                screen_caps.append(f)
 | 
			
		||||
 | 
			
		||||
        if len(screen_caps) == 1:
 | 
			
		||||
            # Bail out if we do not have image, and resort to the slower, built
 | 
			
		||||
            # in method.
 | 
			
		||||
            try:
 | 
			
		||||
                import Image
 | 
			
		||||
                gem5_image = os.path.join(self.gem5outdir, screen_caps[0])
 | 
			
		||||
                temp_image = os.path.join(self.gem5outdir, "file.png")
 | 
			
		||||
                im = Image.open(gem5_image)
 | 
			
		||||
                im.save(temp_image, "PNG")
 | 
			
		||||
                shutil.copy(temp_image, filepath)
 | 
			
		||||
                os.remove(temp_image)
 | 
			
		||||
                self.logger.debug("capture_screen: using gem5 screencap")
 | 
			
		||||
                return True
 | 
			
		||||
            except (shutil.Error, ImportError, IOError):
 | 
			
		||||
                pass
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
    # pylint: disable=W0613
 | 
			
		||||
    def execute(self, command, timeout=1000, check_exit_code=True, background=False,
 | 
			
		||||
                as_root=False, busybox=False, **kwargs):
 | 
			
		||||
        self._check_ready()
 | 
			
		||||
        if as_root and not self.is_rooted:
 | 
			
		||||
            raise DeviceError('Attempting to execute "{}" as root on unrooted device.'.format(command))
 | 
			
		||||
        if busybox:
 | 
			
		||||
            if not self.is_rooted:
 | 
			
		||||
                raise DeviceError('Attempting to execute "{}" with busybox. '.format(command) +
 | 
			
		||||
                                  'Busybox can only be deployed to rooted devices.')
 | 
			
		||||
            command = ' '.join([self.busybox, command])
 | 
			
		||||
        if background:
 | 
			
		||||
            self.logger.debug("Attempt to execute in background. Not supported "
 | 
			
		||||
                              "in gem5, hence ignored.")
 | 
			
		||||
        return self.gem5_shell(command, as_root=as_root)
 | 
			
		||||
 | 
			
		||||
    # Internal methods: do not use outside of the class.
 | 
			
		||||
 | 
			
		||||
    def _check_ready(self):
 | 
			
		||||
        """
 | 
			
		||||
        Check if the device is ready.
 | 
			
		||||
 | 
			
		||||
        As this is gem5, we just assume that the device is ready once we have
 | 
			
		||||
        connected to the gem5 simulation, and updated the prompt.
 | 
			
		||||
        """
 | 
			
		||||
        if not self._is_ready:
 | 
			
		||||
            raise DeviceError('Device not ready.')
 | 
			
		||||
 | 
			
		||||
    def gem5_shell(self, command, as_root=False, timeout=None, check_exit_code=True, sync=True):  # pylint: disable=R0912
 | 
			
		||||
        """
 | 
			
		||||
        Execute a command in the gem5 shell
 | 
			
		||||
 | 
			
		||||
        This wraps the telnet connection to gem5 and processes the raw output.
 | 
			
		||||
 | 
			
		||||
        This method waits for the shell to return, and then will try and
 | 
			
		||||
        separate the output from the command from the command itself. If this
 | 
			
		||||
        fails, warn, but continue with the potentially wrong output.
 | 
			
		||||
 | 
			
		||||
        The exit code is also checked by default, and non-zero exit codes will
 | 
			
		||||
        raise a DeviceError.
 | 
			
		||||
        """
 | 
			
		||||
        conn = self.sckt
 | 
			
		||||
        if sync:
 | 
			
		||||
            self.sync_gem5_shell()
 | 
			
		||||
 | 
			
		||||
        self.logger.debug("gem5_shell command: {}".format(command))
 | 
			
		||||
 | 
			
		||||
        # Send the actual command
 | 
			
		||||
        conn.send("{}\n".format(command))
 | 
			
		||||
 | 
			
		||||
        # Wait for the response. We just sit here and wait for the prompt to
 | 
			
		||||
        # appear, as gem5 might take a long time to provide the output. This
 | 
			
		||||
        # avoids timeout issues.
 | 
			
		||||
        command_index = -1
 | 
			
		||||
        while command_index == -1:
 | 
			
		||||
            if conn.prompt():
 | 
			
		||||
                output = re.sub(r' \r([^\n])', r'\1', conn.before)
 | 
			
		||||
                output = re.sub(r'[\b]', r'', output)
 | 
			
		||||
                # Deal with line wrapping
 | 
			
		||||
                output = re.sub(r'[\r].+?<', r'', output)
 | 
			
		||||
                command_index = output.find(command)
 | 
			
		||||
 | 
			
		||||
                # If we have -1, then we cannot match the command, but the
 | 
			
		||||
                # prompt has returned. Hence, we have a bit of an issue. We
 | 
			
		||||
                # warn, and return the whole output.
 | 
			
		||||
                if command_index == -1:
 | 
			
		||||
                    self.logger.warn("gem5_shell: Unable to match command in "
 | 
			
		||||
                                     "command output. Expect parsing errors!")
 | 
			
		||||
                    command_index = 0
 | 
			
		||||
 | 
			
		||||
        output = output[command_index + len(command):].strip()
 | 
			
		||||
 | 
			
		||||
        # It is possible that gem5 will echo the command. Therefore, we need to
 | 
			
		||||
        # remove that too!
 | 
			
		||||
        command_index = output.find(command)
 | 
			
		||||
        if command_index != -1:
 | 
			
		||||
            output = output[command_index + len(command):].strip()
 | 
			
		||||
 | 
			
		||||
        self.logger.debug("gem5_shell output: {}".format(output))
 | 
			
		||||
 | 
			
		||||
        # We get a second prompt. Hence, we need to eat one to make sure that we
 | 
			
		||||
        # stay in sync. If we do not do this, we risk getting out of sync for
 | 
			
		||||
        # slower simulations.
 | 
			
		||||
        self.sckt.expect([self.sckt.UNIQUE_PROMPT, self.sckt.PROMPT], timeout=self.delay)
 | 
			
		||||
 | 
			
		||||
        if check_exit_code:
 | 
			
		||||
            exit_code_text = self.gem5_shell('echo $?', as_root=as_root,
 | 
			
		||||
                                             timeout=timeout, check_exit_code=False,
 | 
			
		||||
                                             sync=False)
 | 
			
		||||
            try:
 | 
			
		||||
                exit_code = int(exit_code_text.split()[0])
 | 
			
		||||
                if exit_code:
 | 
			
		||||
                    message = 'Got exit code {}\nfrom: {}\nOUTPUT: {}'
 | 
			
		||||
                    raise DeviceError(message.format(exit_code, command, output))
 | 
			
		||||
            except (ValueError, IndexError):
 | 
			
		||||
                self.logger.warning('Could not get exit code for "{}",\ngot: "{}"'.format(command, exit_code_text))
 | 
			
		||||
 | 
			
		||||
        return output
 | 
			
		||||
 | 
			
		||||
    def gem5_util(self, command):
 | 
			
		||||
        """ Execute a gem5 utility command using the m5 binary on the device """
 | 
			
		||||
        self.gem5_shell('{} {}'.format(self.m5_path, command))
 | 
			
		||||
 | 
			
		||||
    def sync_gem5_shell(self):
 | 
			
		||||
        """
 | 
			
		||||
        Synchronise with the gem5 shell.
 | 
			
		||||
 | 
			
		||||
        Write some unique text to the gem5 device to allow us to synchronise
 | 
			
		||||
        with the shell output. We actually get two prompts so we need to match
 | 
			
		||||
        both of these.
 | 
			
		||||
        """
 | 
			
		||||
        self.logger.debug("Sending Sync")
 | 
			
		||||
        self.sckt.send("echo \*\*sync\*\*\n")
 | 
			
		||||
        self.sckt.expect(r"\*\*sync\*\*", timeout=self.delay)
 | 
			
		||||
        self.sckt.expect([self.sckt.UNIQUE_PROMPT, self.sckt.PROMPT], timeout=self.delay)
 | 
			
		||||
        self.sckt.expect([self.sckt.UNIQUE_PROMPT, self.sckt.PROMPT], timeout=self.delay)
 | 
			
		||||
 | 
			
		||||
    def resize_shell(self):
 | 
			
		||||
        """
 | 
			
		||||
        Resize the shell to avoid line wrapping issues.
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        # Try and avoid line wrapping as much as possible. Don't check the error
 | 
			
		||||
        # codes from these command because some of them WILL fail.
 | 
			
		||||
        self.gem5_shell('stty columns 1024', check_exit_code=False)
 | 
			
		||||
        self.gem5_shell('{} stty columns 1024'.format(self.busybox), check_exit_code=False)
 | 
			
		||||
        self.gem5_shell('stty cols 1024', check_exit_code=False)
 | 
			
		||||
        self.gem5_shell('{} stty cols 1024'.format(self.busybox), check_exit_code=False)
 | 
			
		||||
        self.gem5_shell('reset', check_exit_code=False)
 | 
			
		||||
 | 
			
		||||
    def move_to_temp_dir(self, source):
 | 
			
		||||
        """
 | 
			
		||||
        Move a file to the temporary directory on the host for copying to the
 | 
			
		||||
        gem5 device
 | 
			
		||||
        """
 | 
			
		||||
        command = "cp {} {}".format(source, self.temp_dir)
 | 
			
		||||
        self.logger.debug("Local copy command: {}".format(command))
 | 
			
		||||
        subprocess.call(command.split())
 | 
			
		||||
        subprocess.call("sync".split())
 | 
			
		||||
 | 
			
		||||
    def checkpoint_gem5(self, end_simulation=False):
 | 
			
		||||
        """ Checkpoint the gem5 simulation, storing all system state """
 | 
			
		||||
        self.logger.info("Taking a post-boot checkpoint")
 | 
			
		||||
        self.gem5_util("checkpoint")
 | 
			
		||||
        if end_simulation:
 | 
			
		||||
            self.disconnect()
 | 
			
		||||
 | 
			
		||||
    def mount_virtio(self):
 | 
			
		||||
        """
 | 
			
		||||
        Mount the VirtIO device in the simulated system.
 | 
			
		||||
        """
 | 
			
		||||
        self.logger.info("Mounting VirtIO device in simulated system")
 | 
			
		||||
 | 
			
		||||
        self.gem5_shell('mkdir -p /mnt/obb')
 | 
			
		||||
 | 
			
		||||
        mount_command = "mount -t 9p -o trans=virtio,version=9p2000.L,aname={} gem5 /mnt/obb".format(self.temp_dir)
 | 
			
		||||
        self.gem5_shell(mount_command)
 | 
			
		||||
 | 
			
		||||
    def deploy_m5(self, context, force=False):
 | 
			
		||||
        """
 | 
			
		||||
        Deploys the m5 binary to the device and returns the path to the binary
 | 
			
		||||
        on the device.
 | 
			
		||||
 | 
			
		||||
        :param force: by default, if the binary is already present on the
 | 
			
		||||
                    device, it will not be deployed again. Setting force to
 | 
			
		||||
                    ``True`` overrides that behaviour and ensures that the
 | 
			
		||||
                    binary is always copied. Defaults to ``False``.
 | 
			
		||||
 | 
			
		||||
        :returns: The on-device path to the m5 binary.
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        on_device_executable = self.path.join(self.binaries_directory, 'm5')
 | 
			
		||||
        if not force and self.file_exists(on_device_executable):
 | 
			
		||||
            # We want to check the version of the binary. We cannot directly
 | 
			
		||||
            # check this because the m5 binary itself is unversioned. We also
 | 
			
		||||
            # need to make sure not to check the error code as "m5 --help"
 | 
			
		||||
            # returns a non-zero error code.
 | 
			
		||||
            output = self.gem5_shell('m5 --help', check_exit_code=False)
 | 
			
		||||
            if "writefile" in output:
 | 
			
		||||
                self.logger.debug("Using the m5 binary on the device...")
 | 
			
		||||
                self.m5_path = on_device_executable
 | 
			
		||||
                return on_device_executable
 | 
			
		||||
            else:
 | 
			
		||||
                self.logger.debug("m5 on device does not support writefile!")
 | 
			
		||||
        host_file = context.resolver.get(Executable(NO_ONE, self.abi, 'm5'))
 | 
			
		||||
        self.logger.info("Installing the m5 binary to the device...")
 | 
			
		||||
        self.m5_path = self.install(host_file)
 | 
			
		||||
        return self.m5_path
 | 
			
		||||
@@ -1,16 +0,0 @@
 | 
			
		||||
#    Copyright 2014-2015 ARM Limited
 | 
			
		||||
#
 | 
			
		||||
# Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
# you may not use this file except in compliance with the License.
 | 
			
		||||
# You may obtain a copy of the License at
 | 
			
		||||
#
 | 
			
		||||
#     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
#
 | 
			
		||||
# Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
# distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
# See the License for the specific language governing permissions and
 | 
			
		||||
# limitations under the License.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -1,875 +0,0 @@
 | 
			
		||||
#    Copyright 2014-2015 ARM Limited
 | 
			
		||||
#
 | 
			
		||||
# Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
# you may not use this file except in compliance with the License.
 | 
			
		||||
# You may obtain a copy of the License at
 | 
			
		||||
#
 | 
			
		||||
#     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
#
 | 
			
		||||
# Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
# distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
# See the License for the specific language governing permissions and
 | 
			
		||||
# limitations under the License.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
# pylint: disable=E1101
 | 
			
		||||
import os
 | 
			
		||||
import re
 | 
			
		||||
import time
 | 
			
		||||
import socket
 | 
			
		||||
from collections import namedtuple
 | 
			
		||||
from subprocess import CalledProcessError
 | 
			
		||||
 | 
			
		||||
from wlauto.core.extension import Parameter
 | 
			
		||||
from wlauto.core.device import Device, RuntimeParameter, CoreParameter
 | 
			
		||||
from wlauto.core.resource import NO_ONE
 | 
			
		||||
from wlauto.exceptions import ConfigError, DeviceError, TimeoutError, DeviceNotRespondingError
 | 
			
		||||
from wlauto.common.resources import Executable
 | 
			
		||||
from wlauto.utils.cpuinfo import Cpuinfo
 | 
			
		||||
from wlauto.utils.misc import convert_new_lines, escape_double_quotes, ranges_to_list, ABI_MAP
 | 
			
		||||
from wlauto.utils.misc import isiterable, list_to_mask
 | 
			
		||||
from wlauto.utils.ssh import SshShell
 | 
			
		||||
from wlauto.utils.types import boolean, list_of_strings
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
FSTAB_ENTRY_REGEX = re.compile(r'(\S+) on (\S+) type (\S+) \((\S+)\)')
 | 
			
		||||
 | 
			
		||||
FstabEntry = namedtuple('FstabEntry', ['device', 'mount_point', 'fs_type', 'options', 'dump_freq', 'pass_num'])
 | 
			
		||||
PsEntry = namedtuple('PsEntry', 'user pid ppid vsize rss wchan pc state name')
 | 
			
		||||
LsmodEntry = namedtuple('LsmodEntry', ['name', 'size', 'use_count', 'used_by'])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BaseLinuxDevice(Device):  # pylint: disable=abstract-method
 | 
			
		||||
 | 
			
		||||
    path_module = 'posixpath'
 | 
			
		||||
    has_gpu = True
 | 
			
		||||
 | 
			
		||||
    parameters = [
 | 
			
		||||
        Parameter('scheduler', kind=str, default='unknown',
 | 
			
		||||
                  allowed_values=['unknown', 'smp', 'hmp', 'iks', 'ea', 'other'],
 | 
			
		||||
                  description="""
 | 
			
		||||
                  Specifies the type of multi-core scheduling model utilized in the device. The value
 | 
			
		||||
                  must be one of the following:
 | 
			
		||||
 | 
			
		||||
                  :unknown: A generic Device interface is used to interact with the underlying device
 | 
			
		||||
                            and the underlying scheduling model is unkown.
 | 
			
		||||
                  :smp: A standard single-core or Symmetric Multi-Processing system.
 | 
			
		||||
                  :hmp: ARM Heterogeneous Multi-Processing system.
 | 
			
		||||
                  :iks: Linaro In-Kernel Switcher.
 | 
			
		||||
                  :ea: ARM Energy-Aware scheduler.
 | 
			
		||||
                  :other: Any other system not covered by the above.
 | 
			
		||||
 | 
			
		||||
                          .. note:: most currently-available systems would fall under ``smp`` rather than
 | 
			
		||||
                                    this value. ``other`` is there to future-proof against new schemes
 | 
			
		||||
                                    not yet covered by WA.
 | 
			
		||||
 | 
			
		||||
                  """),
 | 
			
		||||
        Parameter('iks_switch_frequency', kind=int, default=None,
 | 
			
		||||
                  description="""
 | 
			
		||||
                 This is the switching frequency, in kilohertz, of IKS devices. This parameter *MUST NOT*
 | 
			
		||||
                 be set for non-IKS device (i.e. ``scheduler != 'iks'``). If left unset for IKS devices,
 | 
			
		||||
                 it will default to ``800000``, i.e. 800MHz.
 | 
			
		||||
                 """),
 | 
			
		||||
        Parameter('property_files', kind=list_of_strings,
 | 
			
		||||
                  default=[
 | 
			
		||||
                      '/etc/arch-release',
 | 
			
		||||
                      '/etc/debian_version',
 | 
			
		||||
                      '/etc/lsb-release',
 | 
			
		||||
                      '/proc/config.gz',
 | 
			
		||||
                      '/proc/cmdline',
 | 
			
		||||
                      '/proc/cpuinfo',
 | 
			
		||||
                      '/proc/version',
 | 
			
		||||
                      '/proc/zconfig',
 | 
			
		||||
                      '/sys/kernel/debug/sched_features',
 | 
			
		||||
                      '/sys/kernel/hmp',
 | 
			
		||||
                  ],
 | 
			
		||||
                  description='''
 | 
			
		||||
                  A list of paths to files containing static OS properties. These will be pulled into the
 | 
			
		||||
                  __meta directory in output for each run in order to provide information about the platfrom.
 | 
			
		||||
                  These paths do not have to exist and will be ignored if the path is not present on a
 | 
			
		||||
                  particular device.
 | 
			
		||||
                  '''),
 | 
			
		||||
        Parameter('binaries_directory',
 | 
			
		||||
                  description='Location of executable binaries on this device (must be in PATH).'),
 | 
			
		||||
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    runtime_parameters = [
 | 
			
		||||
        RuntimeParameter('sysfile_values', 'get_sysfile_values', 'set_sysfile_values', value_name='params'),
 | 
			
		||||
        CoreParameter('${core}_cores', 'get_number_of_online_cpus', 'set_number_of_online_cpus',
 | 
			
		||||
                      value_name='number'),
 | 
			
		||||
        CoreParameter('${core}_min_frequency', 'get_core_min_frequency', 'set_core_min_frequency',
 | 
			
		||||
                      value_name='freq'),
 | 
			
		||||
        CoreParameter('${core}_max_frequency', 'get_core_max_frequency', 'set_core_max_frequency',
 | 
			
		||||
                      value_name='freq'),
 | 
			
		||||
        CoreParameter('${core}_frequency', 'get_core_cur_frequency', 'set_core_cur_frequency',
 | 
			
		||||
                      value_name='freq'),
 | 
			
		||||
        CoreParameter('${core}_governor', 'get_core_governor', 'set_core_governor',
 | 
			
		||||
                      value_name='governor'),
 | 
			
		||||
        CoreParameter('${core}_governor_tunables', 'get_core_governor_tunables', 'set_core_governor_tunables',
 | 
			
		||||
                      value_name='tunables'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    dynamic_modules = [
 | 
			
		||||
        'devcpufreq',
 | 
			
		||||
        'cpuidle',
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def abi(self):
 | 
			
		||||
        if not self._abi:
 | 
			
		||||
            val = self.execute('uname -m').strip()
 | 
			
		||||
            for abi, architectures in ABI_MAP.iteritems():
 | 
			
		||||
                if val in architectures:
 | 
			
		||||
                    self._abi = abi
 | 
			
		||||
                    break
 | 
			
		||||
            else:
 | 
			
		||||
                self._abi = val
 | 
			
		||||
        return self._abi
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def online_cpus(self):
 | 
			
		||||
        val = self.get_sysfile_value('/sys/devices/system/cpu/online')
 | 
			
		||||
        return ranges_to_list(val)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def number_of_cores(self):
 | 
			
		||||
        """
 | 
			
		||||
        Added in version 2.1.4.
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        if self._number_of_cores is None:
 | 
			
		||||
            corere = re.compile(r'^\s*cpu\d+\s*$')
 | 
			
		||||
            output = self.execute('ls /sys/devices/system/cpu')
 | 
			
		||||
            self._number_of_cores = 0
 | 
			
		||||
            for entry in output.split():
 | 
			
		||||
                if corere.match(entry):
 | 
			
		||||
                    self._number_of_cores += 1
 | 
			
		||||
        return self._number_of_cores
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def resource_cache(self):
 | 
			
		||||
        return self.path.join(self.working_directory, '.cache')
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def file_transfer_cache(self):
 | 
			
		||||
        return self.path.join(self.working_directory, '.transfer')
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def cpuinfo(self):
 | 
			
		||||
        if not self._cpuinfo:
 | 
			
		||||
            self._cpuinfo = Cpuinfo(self.execute('cat /proc/cpuinfo'))
 | 
			
		||||
        return self._cpuinfo
 | 
			
		||||
 | 
			
		||||
    def __init__(self, **kwargs):
 | 
			
		||||
        super(BaseLinuxDevice, self).__init__(**kwargs)
 | 
			
		||||
        self.busybox = None
 | 
			
		||||
        self._is_initialized = False
 | 
			
		||||
        self._is_ready = False
 | 
			
		||||
        self._just_rebooted = False
 | 
			
		||||
        self._is_rooted = None
 | 
			
		||||
        self._is_root_user = False
 | 
			
		||||
        self._available_frequencies = {}
 | 
			
		||||
        self._available_governors = {}
 | 
			
		||||
        self._available_governor_tunables = {}
 | 
			
		||||
        self._number_of_cores = None
 | 
			
		||||
        self._written_sysfiles = []
 | 
			
		||||
        self._cpuinfo = None
 | 
			
		||||
        self._abi = None
 | 
			
		||||
 | 
			
		||||
    def validate(self):
 | 
			
		||||
        if self.iks_switch_frequency is not None and self.scheduler != 'iks':  # pylint: disable=E0203
 | 
			
		||||
            raise ConfigError('iks_switch_frequency must NOT be set for non-IKS devices.')
 | 
			
		||||
        if self.iks_switch_frequency is None and self.scheduler == 'iks':  # pylint: disable=E0203
 | 
			
		||||
            self.iks_switch_frequency = 800000  # pylint: disable=W0201
 | 
			
		||||
 | 
			
		||||
    def initialize(self, context):
 | 
			
		||||
        self.execute('mkdir -p {}'.format(self.working_directory))
 | 
			
		||||
        if not self.binaries_directory:
 | 
			
		||||
            self._set_binaries_dir()
 | 
			
		||||
        self.execute('mkdir -p {}'.format(self.binaries_directory))
 | 
			
		||||
        self.busybox = self.deploy_busybox(context)
 | 
			
		||||
 | 
			
		||||
    def _set_binaries_dir(self):
 | 
			
		||||
        # pylint: disable=attribute-defined-outside-init
 | 
			
		||||
        self.binaries_directory = self.path.join(self.working_directory, "bin")
 | 
			
		||||
 | 
			
		||||
    def is_file(self, filepath):
 | 
			
		||||
        output = self.execute('if [ -f \'{}\' ]; then echo 1; else echo 0; fi'.format(filepath))
 | 
			
		||||
        # output from ssh my contain part of the expression in the buffer,
 | 
			
		||||
        # split out everything except the last word.
 | 
			
		||||
        return boolean(output.split()[-1])  # pylint: disable=maybe-no-member
 | 
			
		||||
 | 
			
		||||
    def is_directory(self, filepath):
 | 
			
		||||
        output = self.execute('if [ -d \'{}\' ]; then echo 1; else echo 0; fi'.format(filepath))
 | 
			
		||||
        # output from ssh my contain part of the expression in the buffer,
 | 
			
		||||
        # split out everything except the last word.
 | 
			
		||||
        return boolean(output.split()[-1])  # pylint: disable=maybe-no-member
 | 
			
		||||
 | 
			
		||||
    def get_properties(self, context):
 | 
			
		||||
        for propfile in self.property_files:
 | 
			
		||||
            try:
 | 
			
		||||
                normname = propfile.lstrip(self.path.sep).replace(self.path.sep, '.')
 | 
			
		||||
                outfile = os.path.join(context.host_working_directory, normname)
 | 
			
		||||
                if self.is_file(propfile):
 | 
			
		||||
                    with open(outfile, 'w') as wfh:
 | 
			
		||||
                        wfh.write(self.execute('cat {}'.format(propfile)))
 | 
			
		||||
                elif self.is_directory(propfile):
 | 
			
		||||
                    self.pull_file(propfile, outfile)
 | 
			
		||||
                else:
 | 
			
		||||
                    continue
 | 
			
		||||
            except DeviceError:
 | 
			
		||||
                # We pull these files "opportunistically", so if a pull fails
 | 
			
		||||
                # (e.g. we don't have permissions to read the file), just note
 | 
			
		||||
                # it quietly (not as an error/warning) and move on.
 | 
			
		||||
                self.logger.debug('Could not pull property file "{}"'.format(propfile))
 | 
			
		||||
        return {}
 | 
			
		||||
 | 
			
		||||
    def get_sysfile_value(self, sysfile, kind=None):
 | 
			
		||||
        """
 | 
			
		||||
        Get the contents of the specified sysfile.
 | 
			
		||||
 | 
			
		||||
        :param sysfile: The file who's contents will be returned.
 | 
			
		||||
 | 
			
		||||
        :param kind: The type of value to be expected in the sysfile. This can
 | 
			
		||||
                     be any Python callable that takes a single str argument.
 | 
			
		||||
                     If not specified or is None, the contents will be returned
 | 
			
		||||
                     as a string.
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        output = self.execute('cat \'{}\''.format(sysfile), as_root=self.is_rooted).strip()  # pylint: disable=E1103
 | 
			
		||||
        if kind:
 | 
			
		||||
            return kind(output)
 | 
			
		||||
        else:
 | 
			
		||||
            return output
 | 
			
		||||
 | 
			
		||||
    def set_sysfile_value(self, sysfile, value, verify=True):
 | 
			
		||||
        """
 | 
			
		||||
        Set the value of the specified sysfile. By default, the value will be checked afterwards.
 | 
			
		||||
        Can be overridden by setting ``verify`` parameter to ``False``.
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        value = str(value)
 | 
			
		||||
        self.execute('echo {} > \'{}\''.format(value, sysfile), check_exit_code=False, as_root=True)
 | 
			
		||||
        if verify:
 | 
			
		||||
            output = self.get_sysfile_value(sysfile)
 | 
			
		||||
            if output.strip() != value:  # pylint: disable=E1103
 | 
			
		||||
                message = 'Could not set the value of {} to {}'.format(sysfile, value)
 | 
			
		||||
                raise DeviceError(message)
 | 
			
		||||
        self._written_sysfiles.append(sysfile)
 | 
			
		||||
 | 
			
		||||
    def get_sysfile_values(self):
 | 
			
		||||
        """
 | 
			
		||||
        Returns a dict mapping paths of sysfiles that were previously set to their
 | 
			
		||||
        current values.
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        values = {}
 | 
			
		||||
        for sysfile in self._written_sysfiles:
 | 
			
		||||
            values[sysfile] = self.get_sysfile_value(sysfile)
 | 
			
		||||
        return values
 | 
			
		||||
 | 
			
		||||
    def set_sysfile_values(self, params):
 | 
			
		||||
        """
 | 
			
		||||
        The plural version of ``set_sysfile_value``. Takes a single parameter which is a mapping of
 | 
			
		||||
        file paths to values to be set. By default, every value written will be verified. The can
 | 
			
		||||
        be disabled for individual paths by appending ``'!'`` to them.
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        for sysfile, value in params.iteritems():
 | 
			
		||||
            verify = not sysfile.endswith('!')
 | 
			
		||||
            sysfile = sysfile.rstrip('!')
 | 
			
		||||
            self.set_sysfile_value(sysfile, value, verify=verify)
 | 
			
		||||
 | 
			
		||||
    def deploy_busybox(self, context, force=False):
 | 
			
		||||
        """
 | 
			
		||||
        Deploys the busybox binary to the specified device and returns
 | 
			
		||||
        the path to the binary on the device.
 | 
			
		||||
 | 
			
		||||
        :param context: an instance of ExecutionContext
 | 
			
		||||
        :param force: by default, if the binary is already present on the
 | 
			
		||||
                    device, it will not be deployed again. Setting force
 | 
			
		||||
                    to ``True`` overrides that behavior and ensures that the
 | 
			
		||||
                    binary is always copied. Defaults to ``False``.
 | 
			
		||||
 | 
			
		||||
        :returns: The on-device path to the busybox binary.
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        on_device_executable = self.get_binary_path("busybox", search_system_binaries=False)
 | 
			
		||||
        if force or not on_device_executable:
 | 
			
		||||
            host_file = context.resolver.get(Executable(NO_ONE, self.abi, 'busybox'))
 | 
			
		||||
            return self.install(host_file)
 | 
			
		||||
        return on_device_executable
 | 
			
		||||
 | 
			
		||||
    def is_installed(self, name):  # pylint: disable=unused-argument,no-self-use
 | 
			
		||||
        raise AttributeError("""Instead of using is_installed, please use
 | 
			
		||||
            ``get_binary_path`` or ``install_if_needed`` instead. You should
 | 
			
		||||
            use the path returned by these functions to then invoke the binary
 | 
			
		||||
 | 
			
		||||
            please see: https://pythonhosted.org/wlauto/writing_extensions.html""")
 | 
			
		||||
 | 
			
		||||
    def get_binary_path(self, name, search_system_binaries=True):
 | 
			
		||||
        """
 | 
			
		||||
        Searches the devices ``binary_directory`` for the given binary,
 | 
			
		||||
        if it cant find it there it tries using which to find it.
 | 
			
		||||
 | 
			
		||||
        :param name: The name of the binary
 | 
			
		||||
        :param search_system_binaries: By default this function will try using
 | 
			
		||||
                                       which to find the binary if it isn't in
 | 
			
		||||
                                       ``binary_directory``. When this is set
 | 
			
		||||
                                       to ``False`` it will not try this.
 | 
			
		||||
 | 
			
		||||
        :returns: The on-device path to the binary.
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        wa_binary = self.path.join(self.binaries_directory, name)
 | 
			
		||||
        if self.file_exists(wa_binary):
 | 
			
		||||
            return wa_binary
 | 
			
		||||
        if search_system_binaries:
 | 
			
		||||
            try:
 | 
			
		||||
                return self.execute('{} which {}'.format(self.busybox, name)).strip()
 | 
			
		||||
            except DeviceError:
 | 
			
		||||
                pass
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
    def install_if_needed(self, host_path, search_system_binaries=True):
 | 
			
		||||
        """
 | 
			
		||||
        Similar to get_binary_path but will install the binary if not found.
 | 
			
		||||
 | 
			
		||||
        :param host_path: The path to the binary on the host
 | 
			
		||||
        :param search_system_binaries: By default this function will try using
 | 
			
		||||
                                       which to find the binary if it isn't in
 | 
			
		||||
                                       ``binary_directory``. When this is set
 | 
			
		||||
                                       to ``False`` it will not try this.
 | 
			
		||||
 | 
			
		||||
        :returns: The on-device path to the binary.
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        binary_path = self.get_binary_path(os.path.split(host_path)[1],
 | 
			
		||||
                                           search_system_binaries=search_system_binaries)
 | 
			
		||||
        if not binary_path:
 | 
			
		||||
            binary_path = self.install(host_path)
 | 
			
		||||
        return binary_path
 | 
			
		||||
 | 
			
		||||
    def list_file_systems(self):
 | 
			
		||||
        output = self.execute('mount')
 | 
			
		||||
        fstab = []
 | 
			
		||||
        for line in output.split('\n'):
 | 
			
		||||
            line = line.strip()
 | 
			
		||||
            if not line:
 | 
			
		||||
                continue
 | 
			
		||||
            match = FSTAB_ENTRY_REGEX.search(line)
 | 
			
		||||
            if match:
 | 
			
		||||
                fstab.append(FstabEntry(match.group(1), match.group(2),
 | 
			
		||||
                                        match.group(3), match.group(4),
 | 
			
		||||
                                        None, None))
 | 
			
		||||
            else:  # assume pre-M Android
 | 
			
		||||
                fstab.append(FstabEntry(*line.split()))
 | 
			
		||||
        return fstab
 | 
			
		||||
 | 
			
		||||
    # Process query and control
 | 
			
		||||
 | 
			
		||||
    def get_pids_of(self, process_name):
 | 
			
		||||
        raise NotImplementedError()
 | 
			
		||||
 | 
			
		||||
    def ps(self, **kwargs):
 | 
			
		||||
        raise NotImplementedError()
 | 
			
		||||
 | 
			
		||||
    def kill(self, pid, signal=None, as_root=False):  # pylint: disable=W0221
 | 
			
		||||
        """
 | 
			
		||||
        Kill the specified process.
 | 
			
		||||
 | 
			
		||||
            :param pid: PID of the process to kill.
 | 
			
		||||
            :param signal: Specify which singal to send to the process. This must
 | 
			
		||||
                           be a valid value for -s option of kill. Defaults to ``None``.
 | 
			
		||||
 | 
			
		||||
        Modified in version 2.1.4: added ``signal`` parameter.
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        signal_string = '-s {}'.format(signal) if signal else ''
 | 
			
		||||
        self.execute('kill {} {}'.format(signal_string, pid), as_root=as_root)
 | 
			
		||||
 | 
			
		||||
    def killall(self, process_name, signal=None, as_root=False):  # pylint: disable=W0221
 | 
			
		||||
        """
 | 
			
		||||
        Kill all processes with the specified name.
 | 
			
		||||
 | 
			
		||||
            :param process_name: The name of the process(es) to kill.
 | 
			
		||||
            :param signal: Specify which singal to send to the process. This must
 | 
			
		||||
                           be a valid value for -s option of kill. Defaults to ``None``.
 | 
			
		||||
 | 
			
		||||
        Modified in version 2.1.5: added ``as_root`` parameter.
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        for pid in self.get_pids_of(process_name):
 | 
			
		||||
            self.kill(pid, signal=signal, as_root=as_root)
 | 
			
		||||
 | 
			
		||||
    def get_online_cpus(self, c):
 | 
			
		||||
        if isinstance(c, int):  # assume c == cluster
 | 
			
		||||
            return [i for i in self.online_cpus if self.core_clusters[i] == c]
 | 
			
		||||
        elif isinstance(c, basestring):  # assume c == core
 | 
			
		||||
            return [i for i in self.online_cpus if self.core_names[i] == c]
 | 
			
		||||
        else:
 | 
			
		||||
            raise ValueError(c)
 | 
			
		||||
 | 
			
		||||
    def get_number_of_online_cpus(self, c):
 | 
			
		||||
        return len(self.get_online_cpus(c))
 | 
			
		||||
 | 
			
		||||
    def set_number_of_online_cpus(self, core, number):
 | 
			
		||||
        core_ids = [i for i, c in enumerate(self.core_names) if c == core]
 | 
			
		||||
        max_cores = len(core_ids)
 | 
			
		||||
        if number > max_cores:
 | 
			
		||||
            message = 'Attempting to set the number of active {} to {}; maximum is {}'
 | 
			
		||||
            raise ValueError(message.format(core, number, max_cores))
 | 
			
		||||
        for i in xrange(0, number):
 | 
			
		||||
            self.enable_cpu(core_ids[i])
 | 
			
		||||
        for i in xrange(number, max_cores):
 | 
			
		||||
            self.disable_cpu(core_ids[i])
 | 
			
		||||
 | 
			
		||||
    # hotplug
 | 
			
		||||
 | 
			
		||||
    def enable_cpu(self, cpu):
 | 
			
		||||
        """
 | 
			
		||||
        Enable the specified core.
 | 
			
		||||
 | 
			
		||||
        :param cpu: CPU core to enable. This must be the full name as it
 | 
			
		||||
                    appears in sysfs, e.g. "cpu0".
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        self.hotplug_cpu(cpu, online=True)
 | 
			
		||||
 | 
			
		||||
    def disable_cpu(self, cpu):
 | 
			
		||||
        """
 | 
			
		||||
        Disable the specified core.
 | 
			
		||||
 | 
			
		||||
        :param cpu: CPU core to disable. This must be the full name as it
 | 
			
		||||
                    appears in sysfs, e.g. "cpu0".
 | 
			
		||||
        """
 | 
			
		||||
        self.hotplug_cpu(cpu, online=False)
 | 
			
		||||
 | 
			
		||||
    def hotplug_cpu(self, cpu, online):
 | 
			
		||||
        """
 | 
			
		||||
        Hotplug the specified CPU either on or off.
 | 
			
		||||
        See https://www.kernel.org/doc/Documentation/cpu-hotplug.txt
 | 
			
		||||
 | 
			
		||||
        :param cpu: The CPU for which the governor is to be set. This must be
 | 
			
		||||
                    the full name as it appears in sysfs, e.g. "cpu0".
 | 
			
		||||
        :param online: CPU will be enabled if this value bool()'s to True, and
 | 
			
		||||
                       will be disabled otherwise.
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        if isinstance(cpu, int):
 | 
			
		||||
            cpu = 'cpu{}'.format(cpu)
 | 
			
		||||
        status = 1 if online else 0
 | 
			
		||||
        sysfile = '/sys/devices/system/cpu/{}/online'.format(cpu)
 | 
			
		||||
        self.set_sysfile_value(sysfile, status)
 | 
			
		||||
 | 
			
		||||
    def get_number_of_active_cores(self, core):
 | 
			
		||||
        if core not in self.core_names:
 | 
			
		||||
            raise ValueError('Unexpected core: {}; must be in {}'.format(core, list(set(self.core_names))))
 | 
			
		||||
        active_cpus = self.active_cpus
 | 
			
		||||
        num_active_cores = 0
 | 
			
		||||
        for i, c in enumerate(self.core_names):
 | 
			
		||||
            if c == core and i in active_cpus:
 | 
			
		||||
                num_active_cores += 1
 | 
			
		||||
        return num_active_cores
 | 
			
		||||
 | 
			
		||||
    def set_number_of_active_cores(self, core, number):  # NOQA
 | 
			
		||||
        if core not in self.core_names:
 | 
			
		||||
            raise ValueError('Unexpected core: {}; must be in {}'.format(core, list(set(self.core_names))))
 | 
			
		||||
        core_ids = [i for i, c in enumerate(self.core_names) if c == core]
 | 
			
		||||
        max_cores = len(core_ids)
 | 
			
		||||
        if number > max_cores:
 | 
			
		||||
            message = 'Attempting to set the number of active {} to {}; maximum is {}'
 | 
			
		||||
            raise ValueError(message.format(core, number, max_cores))
 | 
			
		||||
 | 
			
		||||
        if not number:
 | 
			
		||||
            # make sure at least one other core is enabled to avoid trying to
 | 
			
		||||
            # hotplug everything.
 | 
			
		||||
            for i, c in enumerate(self.core_names):
 | 
			
		||||
                if c != core:
 | 
			
		||||
                    self.enable_cpu(i)
 | 
			
		||||
                    break
 | 
			
		||||
            else:  # did not find one
 | 
			
		||||
                raise ValueError('Cannot hotplug all cpus on the device!')
 | 
			
		||||
 | 
			
		||||
        for i in xrange(0, number):
 | 
			
		||||
            self.enable_cpu(core_ids[i])
 | 
			
		||||
        for i in xrange(number, max_cores):
 | 
			
		||||
            self.disable_cpu(core_ids[i])
 | 
			
		||||
 | 
			
		||||
    def invoke(self, binary, args=None, in_directory=None, on_cpus=None,
 | 
			
		||||
               background=False, as_root=False, timeout=30):
 | 
			
		||||
        """
 | 
			
		||||
        Executes the specified binary under the specified conditions.
 | 
			
		||||
 | 
			
		||||
        :binary: binary to execute. Must be present and executable on the device.
 | 
			
		||||
        :args: arguments to be passed to the binary. The can be either a list or
 | 
			
		||||
               a string.
 | 
			
		||||
        :in_directory:  execute the binary in the  specified directory. This must
 | 
			
		||||
                        be an absolute path.
 | 
			
		||||
        :on_cpus:  taskset the binary to these CPUs. This may be a single ``int`` (in which
 | 
			
		||||
                   case, it will be interpreted as the mask), a list of ``ints``, in which
 | 
			
		||||
                   case this will be interpreted as the list of cpus, or string, which
 | 
			
		||||
                   will be interpreted as a comma-separated list of cpu ranges, e.g.
 | 
			
		||||
                   ``"0,4-7"``.
 | 
			
		||||
        :background: If ``True``, a ``subprocess.Popen`` object will be returned straight
 | 
			
		||||
                     away. If ``False`` (the default), this will wait for the command to
 | 
			
		||||
                     terminate and return the STDOUT output
 | 
			
		||||
        :as_root: Specify whether the command should be run as root
 | 
			
		||||
        :timeout: If the invocation does not terminate within this number of seconds,
 | 
			
		||||
                  a ``TimeoutError`` exception will be raised. Set to ``None`` if the
 | 
			
		||||
                  invocation should not timeout.
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        command = binary
 | 
			
		||||
        if args:
 | 
			
		||||
            if isiterable(args):
 | 
			
		||||
                args = ' '.join(args)
 | 
			
		||||
            command = '{} {}'.format(command, args)
 | 
			
		||||
        if on_cpus:
 | 
			
		||||
            if isinstance(on_cpus, basestring):
 | 
			
		||||
                on_cpus = ranges_to_list(on_cpus)
 | 
			
		||||
            if isiterable(on_cpus):
 | 
			
		||||
                on_cpus = list_to_mask(on_cpus)  # pylint: disable=redefined-variable-type
 | 
			
		||||
            command = '{} taskset 0x{:x} {}'.format(self.busybox, on_cpus, command)
 | 
			
		||||
        if in_directory:
 | 
			
		||||
            command = 'cd {} && {}'.format(in_directory, command)
 | 
			
		||||
        return self.execute(command, background=background, as_root=as_root, timeout=timeout)
 | 
			
		||||
 | 
			
		||||
    def get_device_model(self):
 | 
			
		||||
        if self.file_exists("/proc/device-tree/model"):
 | 
			
		||||
            raw_model = self.execute("cat /proc/device-tree/model")
 | 
			
		||||
            return '_'.join(raw_model.split()[:2])
 | 
			
		||||
        # Right now we don't know any other way to get device model
 | 
			
		||||
        # info in linux on arm platforms
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
    # internal methods
 | 
			
		||||
 | 
			
		||||
    def _check_ready(self):
 | 
			
		||||
        if not self._is_ready:
 | 
			
		||||
            raise AttributeError('Device not ready.')
 | 
			
		||||
 | 
			
		||||
    def _get_core_cluster(self, core):
 | 
			
		||||
        """Returns the first cluster that has cores of the specified type. Raises
 | 
			
		||||
        value error if no cluster for the specified type has been found"""
 | 
			
		||||
        core_indexes = [i for i, c in enumerate(self.core_names) if c == core]
 | 
			
		||||
        core_clusters = set(self.core_clusters[i] for i in core_indexes)
 | 
			
		||||
        if not core_clusters:
 | 
			
		||||
            raise ValueError('No cluster found for core {}'.format(core))
 | 
			
		||||
        return sorted(list(core_clusters))[0]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class LinuxDevice(BaseLinuxDevice):
 | 
			
		||||
 | 
			
		||||
    platform = 'linux'
 | 
			
		||||
 | 
			
		||||
    default_timeout = 30
 | 
			
		||||
    delay = 2
 | 
			
		||||
    long_delay = 3 * delay
 | 
			
		||||
    ready_timeout = 60
 | 
			
		||||
 | 
			
		||||
    parameters = [
 | 
			
		||||
        Parameter('host', mandatory=True, description='Host name or IP address for the device.'),
 | 
			
		||||
        Parameter('username', mandatory=True, description='User name for the account on the device.'),
 | 
			
		||||
        Parameter('password', description='Password for the account on the device (for password-based auth).'),
 | 
			
		||||
        Parameter('keyfile', description='Keyfile to be used for key-based authentication.'),
 | 
			
		||||
        Parameter('port', kind=int, default=22, description='SSH port number on the device.'),
 | 
			
		||||
        Parameter('password_prompt', default='[sudo] password',
 | 
			
		||||
                  description='Prompt presented by sudo when requesting the password.'),
 | 
			
		||||
 | 
			
		||||
        Parameter('use_telnet', kind=boolean, default=False,
 | 
			
		||||
                  description='Optionally, telnet may be used instead of ssh, though this is discouraged.'),
 | 
			
		||||
        Parameter('boot_timeout', kind=int, default=120,
 | 
			
		||||
                  description='How long to try to connect to the device after a reboot.'),
 | 
			
		||||
 | 
			
		||||
        Parameter('working_directory', default=None,
 | 
			
		||||
                  description='''
 | 
			
		||||
                  Working directory to be used by WA. This must be in a location where the specified user
 | 
			
		||||
                  has write permissions. This will default to /home/<username>/wa (or to /root/wa, if
 | 
			
		||||
                  username is 'root').
 | 
			
		||||
                  '''),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def is_rooted(self):
 | 
			
		||||
        if self._is_rooted is None:
 | 
			
		||||
            # First check if the user is root
 | 
			
		||||
            try:
 | 
			
		||||
                self.execute('test $(id -u) = 0')
 | 
			
		||||
                self._is_root_user = True
 | 
			
		||||
                self._is_rooted = True
 | 
			
		||||
                return self._is_rooted
 | 
			
		||||
            except DeviceError:
 | 
			
		||||
                self._is_root_user = False
 | 
			
		||||
 | 
			
		||||
            # Otherwise, check if the user has sudo rights
 | 
			
		||||
            try:
 | 
			
		||||
                self.execute('ls /', as_root=True)
 | 
			
		||||
                self._is_rooted = True
 | 
			
		||||
            except DeviceError:
 | 
			
		||||
                self._is_rooted = False
 | 
			
		||||
        return self._is_rooted
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super(LinuxDevice, self).__init__(*args, **kwargs)
 | 
			
		||||
        self.shell = None
 | 
			
		||||
        self._is_rooted = None
 | 
			
		||||
 | 
			
		||||
    def validate(self):
 | 
			
		||||
        if self.working_directory is None:  # pylint: disable=access-member-before-definition
 | 
			
		||||
            if self.username == 'root':
 | 
			
		||||
                self.working_directory = '/root/wa'  # pylint: disable=attribute-defined-outside-init
 | 
			
		||||
            else:
 | 
			
		||||
                self.working_directory = '/home/{}/wa'.format(self.username)  # pylint: disable=attribute-defined-outside-init
 | 
			
		||||
 | 
			
		||||
    def initialize(self, context, *args, **kwargs):
 | 
			
		||||
        self.execute('mkdir -p {}'.format(self.binaries_directory))
 | 
			
		||||
        self.execute('export PATH={}:$PATH'.format(self.binaries_directory))
 | 
			
		||||
        super(LinuxDevice, self).initialize(context, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    # Power control
 | 
			
		||||
 | 
			
		||||
    def reset(self):
 | 
			
		||||
        self.execute('reboot', as_root=True)
 | 
			
		||||
        self._is_ready = False
 | 
			
		||||
 | 
			
		||||
    def hard_reset(self):
 | 
			
		||||
        self._is_ready = False
 | 
			
		||||
 | 
			
		||||
    def boot(self, hard=False, **kwargs):
 | 
			
		||||
        if hard:
 | 
			
		||||
            self.hard_reset()
 | 
			
		||||
        else:
 | 
			
		||||
            self.reset()
 | 
			
		||||
        self.logger.debug('Waiting for device...')
 | 
			
		||||
        start_time = time.time()
 | 
			
		||||
        while (time.time() - start_time) < self.boot_timeout:
 | 
			
		||||
            try:
 | 
			
		||||
                s = socket.create_connection((self.host, self.port), timeout=5)
 | 
			
		||||
                s.close()
 | 
			
		||||
                break
 | 
			
		||||
            except socket.timeout:
 | 
			
		||||
                pass
 | 
			
		||||
            except socket.error:
 | 
			
		||||
                time.sleep(5)
 | 
			
		||||
        else:
 | 
			
		||||
            raise DeviceError('Could not connect to {} after reboot'.format(self.host))
 | 
			
		||||
 | 
			
		||||
    def connect(self):  # NOQA pylint: disable=R0912
 | 
			
		||||
        self.shell = SshShell(password_prompt=self.password_prompt,
 | 
			
		||||
                              timeout=self.default_timeout, telnet=self.use_telnet)
 | 
			
		||||
        self.shell.login(self.host, self.username, self.password, self.keyfile, self.port)
 | 
			
		||||
        self._is_ready = True
 | 
			
		||||
 | 
			
		||||
    def disconnect(self):  # NOQA pylint: disable=R0912
 | 
			
		||||
        self.shell.logout()
 | 
			
		||||
        self._is_ready = False
 | 
			
		||||
 | 
			
		||||
    # Execution
 | 
			
		||||
 | 
			
		||||
    def has_root(self):
 | 
			
		||||
        try:
 | 
			
		||||
            self.execute('ls /', as_root=True)
 | 
			
		||||
            return True
 | 
			
		||||
        except DeviceError as e:
 | 
			
		||||
            if 'not in the sudoers file' not in e.message:
 | 
			
		||||
                raise e
 | 
			
		||||
            return False
 | 
			
		||||
 | 
			
		||||
    def execute(self, command, timeout=default_timeout, check_exit_code=True, background=False,
 | 
			
		||||
                as_root=False, strip_colors=True, **kwargs):
 | 
			
		||||
        """
 | 
			
		||||
        Execute the specified command on the device using adb.
 | 
			
		||||
 | 
			
		||||
        Parameters:
 | 
			
		||||
 | 
			
		||||
            :param command: The command to be executed. It should appear exactly
 | 
			
		||||
                            as if you were typing it into a shell.
 | 
			
		||||
            :param timeout: Time, in seconds, to wait for adb to return before aborting
 | 
			
		||||
                            and raising an error. Defaults to ``AndroidDevice.default_timeout``.
 | 
			
		||||
            :param check_exit_code: If ``True``, the return code of the command on the Device will
 | 
			
		||||
                                    be check and exception will be raised if it is not 0.
 | 
			
		||||
                                    Defaults to ``True``.
 | 
			
		||||
            :param background: If ``True``, will execute create a new ssh shell rather than using
 | 
			
		||||
                               the default session and will return it immediately. If this is ``True``,
 | 
			
		||||
                               ``timeout``, ``strip_colors`` and (obvisously) ``check_exit_code`` will
 | 
			
		||||
                               be ignored; also, with this, ``as_root=True``  is only valid if ``username``
 | 
			
		||||
                               for the device was set to ``root``.
 | 
			
		||||
            :param as_root: If ``True``, will attempt to execute command in privileged mode. The device
 | 
			
		||||
                            must be rooted, otherwise an error will be raised. Defaults to ``False``.
 | 
			
		||||
 | 
			
		||||
                            Added in version 2.1.3
 | 
			
		||||
 | 
			
		||||
        :returns: If ``background`` parameter is set to ``True``, the subprocess object will
 | 
			
		||||
                  be returned; otherwise, the contents of STDOUT from the device will be returned.
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        self._check_ready()
 | 
			
		||||
        try:
 | 
			
		||||
            if background:
 | 
			
		||||
                if as_root and self.username != 'root':
 | 
			
		||||
                    raise DeviceError('Cannot execute in background with as_root=True unless user is root.')
 | 
			
		||||
                return self.shell.background(command)
 | 
			
		||||
            else:
 | 
			
		||||
                # If we're already the root user, don't bother with sudo
 | 
			
		||||
                if self._is_root_user:
 | 
			
		||||
                    as_root = False
 | 
			
		||||
                return self.shell.execute(command, timeout, check_exit_code, as_root, strip_colors)
 | 
			
		||||
        except CalledProcessError as e:
 | 
			
		||||
            raise DeviceError(e)
 | 
			
		||||
 | 
			
		||||
    def kick_off(self, command, as_root=False):
 | 
			
		||||
        """
 | 
			
		||||
        Like execute but closes adb session and returns immediately, leaving the command running on the
 | 
			
		||||
        device (this is different from execute(background=True) which keeps adb connection open and returns
 | 
			
		||||
        a subprocess object).
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        self._check_ready()
 | 
			
		||||
        command = 'sh -c "{}" 1>/dev/null 2>/dev/null &'.format(escape_double_quotes(command))
 | 
			
		||||
        return self.shell.execute(command, as_root=as_root)
 | 
			
		||||
 | 
			
		||||
    def get_pids_of(self, process_name):
 | 
			
		||||
        """Returns a list of PIDs of all processes with the specified name."""
 | 
			
		||||
        # result should be a column of PIDs with the first row as "PID" header
 | 
			
		||||
        result = self.execute('ps -C {} -o pid'.format(process_name),  # NOQA
 | 
			
		||||
                              check_exit_code=False).strip().split()
 | 
			
		||||
        if len(result) >= 2:  # at least one row besides the header
 | 
			
		||||
            return map(int, result[1:])
 | 
			
		||||
        else:
 | 
			
		||||
            return []
 | 
			
		||||
 | 
			
		||||
    def ps(self, **kwargs):
 | 
			
		||||
        command = 'ps -eo user,pid,ppid,vsize,rss,wchan,pcpu,state,fname'
 | 
			
		||||
        lines = iter(convert_new_lines(self.execute(command)).split('\n'))
 | 
			
		||||
        lines.next()  # header
 | 
			
		||||
 | 
			
		||||
        result = []
 | 
			
		||||
        for line in lines:
 | 
			
		||||
            parts = re.split(r'\s+', line, maxsplit=8)
 | 
			
		||||
            if parts:
 | 
			
		||||
                result.append(PsEntry(*(parts[0:1] + map(int, parts[1:5]) + parts[5:])))
 | 
			
		||||
 | 
			
		||||
        if not kwargs:
 | 
			
		||||
            return result
 | 
			
		||||
        else:
 | 
			
		||||
            filtered_result = []
 | 
			
		||||
            for entry in result:
 | 
			
		||||
                if all(getattr(entry, k) == v for k, v in kwargs.iteritems()):
 | 
			
		||||
                    filtered_result.append(entry)
 | 
			
		||||
            return filtered_result
 | 
			
		||||
 | 
			
		||||
    # File management
 | 
			
		||||
 | 
			
		||||
    def push_file(self, source, dest, as_root=False, timeout=default_timeout):  # pylint: disable=W0221
 | 
			
		||||
        self._check_ready()
 | 
			
		||||
        try:
 | 
			
		||||
            if not as_root or self.username == 'root':
 | 
			
		||||
                self.shell.push_file(source, dest, timeout=timeout)
 | 
			
		||||
            else:
 | 
			
		||||
                tempfile = self.path.join(self.working_directory, self.path.basename(dest))
 | 
			
		||||
                self.shell.push_file(source, tempfile, timeout=timeout)
 | 
			
		||||
                self.shell.execute('cp -r {} {}'.format(tempfile, dest), timeout=timeout, as_root=True)
 | 
			
		||||
        except CalledProcessError as e:
 | 
			
		||||
            raise DeviceError(e)
 | 
			
		||||
 | 
			
		||||
    def pull_file(self, source, dest, as_root=False, timeout=default_timeout):  # pylint: disable=W0221
 | 
			
		||||
        self._check_ready()
 | 
			
		||||
        try:
 | 
			
		||||
            if not as_root or self.username == 'root':
 | 
			
		||||
                self.shell.pull_file(source, dest, timeout=timeout)
 | 
			
		||||
            else:
 | 
			
		||||
                tempfile = self.path.join(self.working_directory, self.path.basename(source))
 | 
			
		||||
                self.shell.execute('cp -r {} {}'.format(source, tempfile), timeout=timeout, as_root=True)
 | 
			
		||||
                self.shell.execute('chown -R {} {}'.format(self.username, tempfile), timeout=timeout, as_root=True)
 | 
			
		||||
                self.shell.pull_file(tempfile, dest, timeout=timeout)
 | 
			
		||||
        except CalledProcessError as e:
 | 
			
		||||
            raise DeviceError(e)
 | 
			
		||||
 | 
			
		||||
    def delete_file(self, filepath, as_root=False):  # pylint: disable=W0221
 | 
			
		||||
        self.execute('rm -rf {}'.format(filepath), as_root=as_root)
 | 
			
		||||
 | 
			
		||||
    def file_exists(self, filepath):
 | 
			
		||||
        output = self.execute('if [ -e \'{}\' ]; then echo 1; else echo 0; fi'.format(filepath))
 | 
			
		||||
        # output from ssh my contain part of the expression in the buffer,
 | 
			
		||||
        # split out everything except the last word.
 | 
			
		||||
        return boolean(output.split()[-1])  # pylint: disable=maybe-no-member
 | 
			
		||||
 | 
			
		||||
    def listdir(self, path, as_root=False, **kwargs):
 | 
			
		||||
        contents = self.execute('ls -1 {}'.format(path), as_root=as_root).strip()
 | 
			
		||||
        if not contents:
 | 
			
		||||
            return []
 | 
			
		||||
        return [x.strip() for x in contents.split('\n')]  # pylint: disable=maybe-no-member
 | 
			
		||||
 | 
			
		||||
    def install(self, filepath, timeout=default_timeout, with_name=None):  # pylint: disable=W0221
 | 
			
		||||
        destpath = self.path.join(self.binaries_directory,
 | 
			
		||||
                                  with_name or self.path.basename(filepath))
 | 
			
		||||
        self.push_file(filepath, destpath, as_root=True)
 | 
			
		||||
        self.execute('chmod a+x {}'.format(destpath), timeout=timeout, as_root=True)
 | 
			
		||||
        return destpath
 | 
			
		||||
 | 
			
		||||
    install_executable = install  # compatibility
 | 
			
		||||
 | 
			
		||||
    def uninstall(self, executable_name):
 | 
			
		||||
        on_device_executable = self.get_binary_path(executable_name, search_system_binaries=False)
 | 
			
		||||
        if not on_device_executable:
 | 
			
		||||
            raise DeviceError("Could not uninstall {}, binary not found".format(on_device_executable))
 | 
			
		||||
        self.delete_file(on_device_executable, as_root=self.is_rooted)
 | 
			
		||||
 | 
			
		||||
    uninstall_executable = uninstall  # compatibility
 | 
			
		||||
 | 
			
		||||
    # misc
 | 
			
		||||
 | 
			
		||||
    def lsmod(self):
 | 
			
		||||
        """List loaded kernel modules."""
 | 
			
		||||
        lines = self.execute('lsmod').splitlines()
 | 
			
		||||
        entries = []
 | 
			
		||||
        for line in lines[1:]:  # first line is the header
 | 
			
		||||
            if not line.strip():
 | 
			
		||||
                continue
 | 
			
		||||
            parts = line.split()
 | 
			
		||||
            name = parts[0]
 | 
			
		||||
            size = int(parts[1])
 | 
			
		||||
            use_count = int(parts[2])
 | 
			
		||||
            if len(parts) > 3:
 | 
			
		||||
                used_by = ''.join(parts[3:]).split(',')
 | 
			
		||||
            else:
 | 
			
		||||
                used_by = []
 | 
			
		||||
            entries.append(LsmodEntry(name, size, use_count, used_by))
 | 
			
		||||
        return entries
 | 
			
		||||
 | 
			
		||||
    def insmod(self, path):
 | 
			
		||||
        """Install a kernel module located on the host on the target device."""
 | 
			
		||||
        target_path = self.path.join(self.working_directory, os.path.basename(path))
 | 
			
		||||
        self.push_file(path, target_path)
 | 
			
		||||
        self.execute('insmod {}'.format(target_path), as_root=True)
 | 
			
		||||
 | 
			
		||||
    def ping(self):
 | 
			
		||||
        try:
 | 
			
		||||
            # May be triggered inside initialize()
 | 
			
		||||
            self.shell.execute('ls /', timeout=5)
 | 
			
		||||
        except (TimeoutError, CalledProcessError):
 | 
			
		||||
            raise DeviceNotRespondingError(self.host)
 | 
			
		||||
 | 
			
		||||
    def capture_screen(self, filepath):
 | 
			
		||||
        if not self.get_binary_path('scrot'):
 | 
			
		||||
            self.logger.debug('Could not take screenshot as scrot is not installed.')
 | 
			
		||||
            return
 | 
			
		||||
        try:
 | 
			
		||||
            tempfile = self.path.join(self.working_directory, os.path.basename(filepath))
 | 
			
		||||
            self.execute('DISPLAY=:0.0 scrot {}'.format(tempfile))
 | 
			
		||||
            self.pull_file(tempfile, filepath)
 | 
			
		||||
            self.delete_file(tempfile)
 | 
			
		||||
        except DeviceError as e:
 | 
			
		||||
            if "Can't open X dispay." not in e.message:
 | 
			
		||||
                raise e
 | 
			
		||||
            message = e.message.split('OUTPUT:', 1)[1].strip()
 | 
			
		||||
            self.logger.debug('Could not take screenshot: {}'.format(message))
 | 
			
		||||
 | 
			
		||||
    def is_screen_on(self):
 | 
			
		||||
        pass  # TODO
 | 
			
		||||
 | 
			
		||||
    def ensure_screen_is_on(self):
 | 
			
		||||
        pass  # TODO
 | 
			
		||||
@@ -55,7 +55,6 @@ sys.path.insert(0, os.path.join(_this_dir, '..', 'external'))
 | 
			
		||||
_EXTENSION_TYPE_TABLE = [
 | 
			
		||||
    # name,               class,                                    default package,            default path
 | 
			
		||||
    ('command',           'wlauto.core.command.Command',            'wlauto.commands',          'commands'),
 | 
			
		||||
#    ('device',            'wlauto.core.device.Device',              'wlauto.devices',           'devices'),
 | 
			
		||||
    ('device_manager',    'wlauto.core.device.DeviceManager',       'wlauto.managers',          'devices'),
 | 
			
		||||
    ('instrument',        'wlauto.core.instrumentation.Instrument', 'wlauto.instrumentation',   'instruments'),
 | 
			
		||||
    ('module',            'wlauto.core.extension.Module',           'wlauto.modules',           'modules'),
 | 
			
		||||
 
 | 
			
		||||
@@ -1,449 +0,0 @@
 | 
			
		||||
#    Copyright 2013-2015 ARM Limited
 | 
			
		||||
#
 | 
			
		||||
# Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
# you may not use this file except in compliance with the License.
 | 
			
		||||
# You may obtain a copy of the License at
 | 
			
		||||
#
 | 
			
		||||
#     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
#
 | 
			
		||||
# Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
# distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
# See the License for the specific language governing permissions and
 | 
			
		||||
# limitations under the License.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
Base classes for device interfaces.
 | 
			
		||||
 | 
			
		||||
    :Device: The base class for all devices. This defines the interface that must be
 | 
			
		||||
             implemented by all devices and therefore any workload and instrumentation
 | 
			
		||||
             can always rely on.
 | 
			
		||||
    :AndroidDevice: Implements most of the :class:`Device` interface, and extends it
 | 
			
		||||
                    with a number of Android-specific methods.
 | 
			
		||||
    :BigLittleDevice: Subclasses :class:`AndroidDevice` to implement big.LITTLE-specific
 | 
			
		||||
                      runtime parameters.
 | 
			
		||||
    :SimpleMulticoreDevice: Subclasses :class:`AndroidDevice` to implement homogeneous cores
 | 
			
		||||
                          device runtime parameters.
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import os
 | 
			
		||||
import imp
 | 
			
		||||
import string
 | 
			
		||||
from collections import OrderedDict
 | 
			
		||||
from contextlib import contextmanager
 | 
			
		||||
 | 
			
		||||
from wlauto.core.extension import Extension, ExtensionMeta, AttributeCollection, Parameter
 | 
			
		||||
from wlauto.core.extension_loader import ExtensionLoader
 | 
			
		||||
from wlauto.exceptions import DeviceError, ConfigError
 | 
			
		||||
from wlauto.utils.types import list_of_integers, list_of, caseless_string
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
__all__ = ['RuntimeParameter', 'CoreParameter', 'Device', 'DeviceMeta']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RuntimeParameter(object):
 | 
			
		||||
    """
 | 
			
		||||
    A runtime parameter which has its getter and setter methods associated it
 | 
			
		||||
    with it.
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, name, getter, setter,
 | 
			
		||||
                 getter_args=None, setter_args=None,
 | 
			
		||||
                 value_name='value', override=False):
 | 
			
		||||
        """
 | 
			
		||||
        :param name: the name of the parameter.
 | 
			
		||||
        :param getter: the getter method which returns the value of this parameter.
 | 
			
		||||
        :param setter: the setter method which sets the value of this parameter. The setter
 | 
			
		||||
                       always expects to be passed one argument when it is called.
 | 
			
		||||
        :param getter_args: keyword arguments to be used when invoking the getter.
 | 
			
		||||
        :param setter_args: keyword arguments to be used when invoking the setter.
 | 
			
		||||
        :param override: A ``bool`` that specifies whether a parameter of the same name further up the
 | 
			
		||||
                            hierarchy should be overridden. If this is ``False`` (the default), an exception
 | 
			
		||||
                            will be raised by the ``AttributeCollection`` instead.
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        self.name = name
 | 
			
		||||
        self.getter = getter
 | 
			
		||||
        self.setter = setter
 | 
			
		||||
        self.getter_args = getter_args or {}
 | 
			
		||||
        self.setter_args = setter_args or {}
 | 
			
		||||
        self.value_name = value_name
 | 
			
		||||
        self.override = override
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return self.name
 | 
			
		||||
 | 
			
		||||
    __repr__ = __str__
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CoreParameter(RuntimeParameter):
 | 
			
		||||
    """A runtime parameter that will get expanded into a RuntimeParameter for each core type."""
 | 
			
		||||
 | 
			
		||||
    def get_runtime_parameters(self, core_names):
 | 
			
		||||
        params = []
 | 
			
		||||
        for core in set(core_names):
 | 
			
		||||
            name = string.Template(self.name).substitute(core=core)
 | 
			
		||||
            getter = string.Template(self.getter).substitute(core=core)
 | 
			
		||||
            setter = string.Template(self.setter).substitute(core=core)
 | 
			
		||||
            getargs = dict(self.getter_args.items() + [('core', core)])
 | 
			
		||||
            setargs = dict(self.setter_args.items() + [('core', core)])
 | 
			
		||||
            params.append(RuntimeParameter(name, getter, setter, getargs, setargs, self.value_name, self.override))
 | 
			
		||||
        return params
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DynamicModuleSpec(dict):
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def name(self):
 | 
			
		||||
        return self.keys()[0]
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        dict.__init__(self)
 | 
			
		||||
        if args:
 | 
			
		||||
            if len(args) > 1:
 | 
			
		||||
                raise ValueError(args)
 | 
			
		||||
            value = args[0]
 | 
			
		||||
        else:
 | 
			
		||||
            value = kwargs
 | 
			
		||||
        if isinstance(value, basestring):
 | 
			
		||||
            self[value] = {}
 | 
			
		||||
        elif isinstance(value, dict) and len(value) == 1:
 | 
			
		||||
            for k, v in value.iteritems():
 | 
			
		||||
                self[k] = v
 | 
			
		||||
        else:
 | 
			
		||||
            raise ValueError(value)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DeviceMeta(ExtensionMeta):
 | 
			
		||||
 | 
			
		||||
    to_propagate = ExtensionMeta.to_propagate + [
 | 
			
		||||
        ('runtime_parameters', RuntimeParameter, AttributeCollection),
 | 
			
		||||
        ('dynamic_modules', DynamicModuleSpec, AttributeCollection),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Device(Extension):
 | 
			
		||||
    """
 | 
			
		||||
    Base class for all devices supported by Workload Automation. Defines
 | 
			
		||||
    the interface the rest of WA uses to interact with devices.
 | 
			
		||||
 | 
			
		||||
        :name: Unique name used to identify the device.
 | 
			
		||||
        :platform: The name of the device's platform (e.g. ``Android``) this may
 | 
			
		||||
                   be used by workloads and instrumentation to assess whether they
 | 
			
		||||
                   can run on the device.
 | 
			
		||||
        :working_directory: a string of the directory which is
 | 
			
		||||
                            going to be used by the workloads on the device.
 | 
			
		||||
        :binaries_directory: a string of the binary directory for
 | 
			
		||||
                             the device.
 | 
			
		||||
        :has_gpu:     Should be ``True`` if the device as a separate GPU, and
 | 
			
		||||
                    ``False`` if graphics processing is done on a CPU.
 | 
			
		||||
 | 
			
		||||
                    .. note:: Pretty much all devices currently on the market
 | 
			
		||||
                                have GPUs, however this may not be the case for some
 | 
			
		||||
                                development boards.
 | 
			
		||||
 | 
			
		||||
        :path_module: The name of one of the modules implementing the os.path
 | 
			
		||||
                      interface, e.g. ``posixpath`` or ``ntpath``. You can provide
 | 
			
		||||
                      your own implementation rather than relying on one of the
 | 
			
		||||
                      standard library modules, in which case you need to specify
 | 
			
		||||
                      the *full* path to you module. e.g. '/home/joebloggs/mypathimp.py'
 | 
			
		||||
        :parameters: A list of RuntimeParameter objects. The order of the objects
 | 
			
		||||
                     is very important as the setters and getters will be called
 | 
			
		||||
                     in the order the RuntimeParameter objects inserted.
 | 
			
		||||
        :active_cores: This should be a list of all the currently active cpus in
 | 
			
		||||
                      the device in ``'/sys/devices/system/cpu/online'``. The
 | 
			
		||||
                      returned list should be read from the device at the time
 | 
			
		||||
                      of read request.
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    __metaclass__ = DeviceMeta
 | 
			
		||||
 | 
			
		||||
    parameters = [
 | 
			
		||||
        Parameter('core_names', kind=list_of(caseless_string), mandatory=True, default=None,
 | 
			
		||||
                  description="""
 | 
			
		||||
                  This is a list of all cpu cores on the device with each
 | 
			
		||||
                  element being the core type, e.g. ``['a7', 'a7', 'a15']``. The
 | 
			
		||||
                  order of the cores must match the order they are listed in
 | 
			
		||||
                  ``'/sys/devices/system/cpu'``. So in this case, ``'cpu0'`` must
 | 
			
		||||
                  be an A7 core, and ``'cpu2'`` an A15.'
 | 
			
		||||
                  """),
 | 
			
		||||
        Parameter('core_clusters', kind=list_of_integers, mandatory=True, default=None,
 | 
			
		||||
                  description="""
 | 
			
		||||
                  This is a list indicating the cluster affinity of the CPU cores,
 | 
			
		||||
                  each element correponding to the cluster ID of the core coresponding
 | 
			
		||||
                  to its index. E.g. ``[0, 0, 1]`` indicates that cpu0 and cpu1 are on
 | 
			
		||||
                  cluster 0, while cpu2 is on cluster 1. If this is not specified, this
 | 
			
		||||
                  will be inferred from ``core_names`` if possible (assuming all cores with
 | 
			
		||||
                  the same name are on the same cluster).
 | 
			
		||||
                  """),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    runtime_parameters = []
 | 
			
		||||
    # dynamic modules are loaded or not based on whether the device supports
 | 
			
		||||
    # them (established at runtime by module probling the device).
 | 
			
		||||
    dynamic_modules = []
 | 
			
		||||
 | 
			
		||||
    # These must be overwritten by subclasses.
 | 
			
		||||
    name = None
 | 
			
		||||
    platform = None
 | 
			
		||||
    default_working_directory = None
 | 
			
		||||
    has_gpu = None
 | 
			
		||||
    path_module = None
 | 
			
		||||
    active_cores = None
 | 
			
		||||
 | 
			
		||||
    def __init__(self, **kwargs):  # pylint: disable=W0613
 | 
			
		||||
        super(Device, self).__init__(**kwargs)
 | 
			
		||||
        if not self.path_module:
 | 
			
		||||
            raise NotImplementedError('path_module must be specified by the deriving classes.')
 | 
			
		||||
        libpath = os.path.dirname(os.__file__)
 | 
			
		||||
        modpath = os.path.join(libpath, self.path_module)
 | 
			
		||||
        if not modpath.lower().endswith('.py'):
 | 
			
		||||
            modpath += '.py'
 | 
			
		||||
        try:
 | 
			
		||||
            self.path = imp.load_source('device_path', modpath)
 | 
			
		||||
        except IOError:
 | 
			
		||||
            raise DeviceError('Unsupported path module: {}'.format(self.path_module))
 | 
			
		||||
 | 
			
		||||
    def validate(self):
 | 
			
		||||
        # pylint: disable=access-member-before-definition,attribute-defined-outside-init
 | 
			
		||||
        if self.core_names and not self.core_clusters:
 | 
			
		||||
            self.core_clusters = []
 | 
			
		||||
            clusters = []
 | 
			
		||||
            for cn in self.core_names:
 | 
			
		||||
                if cn not in clusters:
 | 
			
		||||
                    clusters.append(cn)
 | 
			
		||||
                self.core_clusters.append(clusters.index(cn))
 | 
			
		||||
        if len(self.core_names) != len(self.core_clusters):
 | 
			
		||||
            raise ConfigError('core_names and core_clusters are of different lengths.')
 | 
			
		||||
 | 
			
		||||
    def initialize(self, context):
 | 
			
		||||
        """
 | 
			
		||||
        Initialization that is performed at the begining of the run (after the device has
 | 
			
		||||
        been connecte).
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        loader = ExtensionLoader()
 | 
			
		||||
        for module_spec in self.dynamic_modules:
 | 
			
		||||
            module = self._load_module(loader, module_spec)
 | 
			
		||||
            if not hasattr(module, 'probe'):
 | 
			
		||||
                message = 'Module {} does not have "probe" attribute; cannot be loaded dynamically'
 | 
			
		||||
                raise ValueError(message.format(module.name))
 | 
			
		||||
            if module.probe(self):
 | 
			
		||||
                self.logger.debug('Installing module "{}"'.format(module.name))
 | 
			
		||||
                self._install_module(module)
 | 
			
		||||
            else:
 | 
			
		||||
                self.logger.debug('Module "{}" is not supported by the device'.format(module.name))
 | 
			
		||||
 | 
			
		||||
    def reset(self):
 | 
			
		||||
        """
 | 
			
		||||
        Initiate rebooting of the device.
 | 
			
		||||
 | 
			
		||||
        Added in version 2.1.3.
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        raise NotImplementedError()
 | 
			
		||||
 | 
			
		||||
    def boot(self, *args, **kwargs):
 | 
			
		||||
        """
 | 
			
		||||
        Perform the seteps necessary to boot the device to the point where it is ready
 | 
			
		||||
        to accept other commands.
 | 
			
		||||
 | 
			
		||||
        Changed in version 2.1.3: no longer expected to wait until boot completes.
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        raise NotImplementedError()
 | 
			
		||||
 | 
			
		||||
    def connect(self, *args, **kwargs):
 | 
			
		||||
        """
 | 
			
		||||
        Establish a connection to the device that will be used for subsequent commands.
 | 
			
		||||
 | 
			
		||||
        Added in version 2.1.3.
 | 
			
		||||
        """
 | 
			
		||||
        raise NotImplementedError()
 | 
			
		||||
 | 
			
		||||
    def disconnect(self):
 | 
			
		||||
        """ Close the established connection to the device. """
 | 
			
		||||
        raise NotImplementedError()
 | 
			
		||||
 | 
			
		||||
    def ping(self):
 | 
			
		||||
        """
 | 
			
		||||
        This must return successfully if the device is able to receive commands, or must
 | 
			
		||||
        raise :class:`wlauto.exceptions.DeviceUnresponsiveError` if the device cannot respond.
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        raise NotImplementedError()
 | 
			
		||||
 | 
			
		||||
    def get_runtime_parameter_names(self):
 | 
			
		||||
        return [p.name for p in self._expand_runtime_parameters()]
 | 
			
		||||
 | 
			
		||||
    def get_runtime_parameters(self):
 | 
			
		||||
        """ returns the runtime parameters that have been set. """
 | 
			
		||||
        # pylint: disable=cell-var-from-loop
 | 
			
		||||
        runtime_parameters = OrderedDict()
 | 
			
		||||
        for rtp in self._expand_runtime_parameters():
 | 
			
		||||
            if not rtp.getter:
 | 
			
		||||
                continue
 | 
			
		||||
            getter = getattr(self, rtp.getter)
 | 
			
		||||
            rtp_value = getter(**rtp.getter_args)
 | 
			
		||||
            runtime_parameters[rtp.name] = rtp_value
 | 
			
		||||
        return runtime_parameters
 | 
			
		||||
 | 
			
		||||
    def set_runtime_parameters(self, params):
 | 
			
		||||
        """
 | 
			
		||||
        The parameters are taken from the keyword arguments and are specific to
 | 
			
		||||
        a particular device. See the device documentation.
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        runtime_parameters = self._expand_runtime_parameters()
 | 
			
		||||
        rtp_map = {rtp.name.lower(): rtp for rtp in runtime_parameters}
 | 
			
		||||
 | 
			
		||||
        params = OrderedDict((k.lower(), v) for k, v in params.iteritems() if v is not None)
 | 
			
		||||
 | 
			
		||||
        expected_keys = rtp_map.keys()
 | 
			
		||||
        if not set(params.keys()).issubset(set(expected_keys)):
 | 
			
		||||
            unknown_params = list(set(params.keys()).difference(set(expected_keys)))
 | 
			
		||||
            raise ConfigError('Unknown runtime parameter(s): {}'.format(unknown_params))
 | 
			
		||||
 | 
			
		||||
        for param in params:
 | 
			
		||||
            self.logger.debug('Setting runtime parameter "{}"'.format(param))
 | 
			
		||||
            rtp = rtp_map[param]
 | 
			
		||||
            setter = getattr(self, rtp.setter)
 | 
			
		||||
            args = dict(rtp.setter_args.items() + [(rtp.value_name, params[rtp.name.lower()])])
 | 
			
		||||
            setter(**args)
 | 
			
		||||
 | 
			
		||||
    def capture_screen(self, filepath):
 | 
			
		||||
        """Captures the current device screen into the specified file in a PNG format."""
 | 
			
		||||
        raise NotImplementedError()
 | 
			
		||||
 | 
			
		||||
    def get_properties(self, output_path):
 | 
			
		||||
        """Captures and saves the device configuration properties version and
 | 
			
		||||
         any other relevant information. Return them in a dict"""
 | 
			
		||||
        raise NotImplementedError()
 | 
			
		||||
 | 
			
		||||
    def listdir(self, path, **kwargs):
 | 
			
		||||
        """ List the contents of the specified directory. """
 | 
			
		||||
        raise NotImplementedError()
 | 
			
		||||
 | 
			
		||||
    def push_file(self, source, dest):
 | 
			
		||||
        """ Push a file from the host file system onto the device. """
 | 
			
		||||
        raise NotImplementedError()
 | 
			
		||||
 | 
			
		||||
    def pull_file(self, source, dest):
 | 
			
		||||
        """ Pull a file from device system onto the host file system. """
 | 
			
		||||
        raise NotImplementedError()
 | 
			
		||||
 | 
			
		||||
    def delete_file(self, filepath):
 | 
			
		||||
        """ Delete the specified file on the device. """
 | 
			
		||||
        raise NotImplementedError()
 | 
			
		||||
 | 
			
		||||
    def file_exists(self, filepath):
 | 
			
		||||
        """ Check if the specified file or directory exist on the device. """
 | 
			
		||||
        raise NotImplementedError()
 | 
			
		||||
 | 
			
		||||
    def get_pids_of(self, process_name):
 | 
			
		||||
        """ Returns a list of PIDs of the specified process name. """
 | 
			
		||||
        raise NotImplementedError()
 | 
			
		||||
 | 
			
		||||
    def kill(self, pid, as_root=False):
 | 
			
		||||
        """ Kill the  process with the specified PID. """
 | 
			
		||||
        raise NotImplementedError()
 | 
			
		||||
 | 
			
		||||
    def killall(self, process_name, as_root=False):
 | 
			
		||||
        """ Kill all running processes with the specified name. """
 | 
			
		||||
        raise NotImplementedError()
 | 
			
		||||
 | 
			
		||||
    def install(self, filepath, **kwargs):
 | 
			
		||||
        """ Install the specified file on the device. What "install" means is device-specific
 | 
			
		||||
        and may possibly also depend on the type of file."""
 | 
			
		||||
        raise NotImplementedError()
 | 
			
		||||
 | 
			
		||||
    def uninstall(self, filepath):
 | 
			
		||||
        """ Uninstall the specified file on the device. What "uninstall" means is device-specific
 | 
			
		||||
        and may possibly also depend on the type of file."""
 | 
			
		||||
        raise NotImplementedError()
 | 
			
		||||
 | 
			
		||||
    def execute(self, command, timeout=None, **kwargs):
 | 
			
		||||
        """
 | 
			
		||||
        Execute the specified command command on the device and return the output.
 | 
			
		||||
 | 
			
		||||
        :param command: Command to be executed on the device.
 | 
			
		||||
        :param timeout: If the command does not return after the specified time,
 | 
			
		||||
                        execute() will abort with an error. If there is no timeout for
 | 
			
		||||
                        the command, this should be set to 0 or None.
 | 
			
		||||
 | 
			
		||||
        Other device-specific keyword arguments may also be specified.
 | 
			
		||||
 | 
			
		||||
        :returns: The stdout output from the command.
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        raise NotImplementedError()
 | 
			
		||||
 | 
			
		||||
    def set_sysfile_value(self, filepath, value, verify=True):
 | 
			
		||||
        """
 | 
			
		||||
        Write the specified value to the specified file on the device
 | 
			
		||||
        and verify that the value has actually been written.
 | 
			
		||||
 | 
			
		||||
        :param file: The file to be modified.
 | 
			
		||||
        :param value: The value to be written to the file. Must be
 | 
			
		||||
                      an int or a string convertable to an int.
 | 
			
		||||
        :param verify: Specifies whether the value should be verified, once written.
 | 
			
		||||
 | 
			
		||||
        Should raise DeviceError if could write value.
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        raise NotImplementedError()
 | 
			
		||||
 | 
			
		||||
    def get_sysfile_value(self, sysfile, kind=None):
 | 
			
		||||
        """
 | 
			
		||||
        Get the contents of the specified sysfile.
 | 
			
		||||
 | 
			
		||||
        :param sysfile: The file who's contents will be returned.
 | 
			
		||||
 | 
			
		||||
        :param kind: The type of value to be expected in the sysfile. This can
 | 
			
		||||
                     be any Python callable that takes a single str argument.
 | 
			
		||||
                     If not specified or is None, the contents will be returned
 | 
			
		||||
                     as a string.
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        raise NotImplementedError()
 | 
			
		||||
 | 
			
		||||
    def start(self):
 | 
			
		||||
        """
 | 
			
		||||
        This gets invoked before an iteration is started and is endented to help the
 | 
			
		||||
        device manange any internal supporting functions.
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def stop(self):
 | 
			
		||||
        """
 | 
			
		||||
        This gets invoked after iteration execution has completed and is endented to help the
 | 
			
		||||
        device manange any internal supporting functions.
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return 'Device<{}>'.format(self.name)
 | 
			
		||||
 | 
			
		||||
    __repr__ = __str__
 | 
			
		||||
 | 
			
		||||
    def _expand_runtime_parameters(self):
 | 
			
		||||
        expanded_params = []
 | 
			
		||||
        for param in self.runtime_parameters:
 | 
			
		||||
            if isinstance(param, CoreParameter):
 | 
			
		||||
                expanded_params.extend(param.get_runtime_parameters(self.core_names))  # pylint: disable=no-member
 | 
			
		||||
            else:
 | 
			
		||||
                expanded_params.append(param)
 | 
			
		||||
        return expanded_params
 | 
			
		||||
 | 
			
		||||
    @contextmanager
 | 
			
		||||
    def _check_alive(self):
 | 
			
		||||
        try:
 | 
			
		||||
            yield
 | 
			
		||||
        except Exception as e:
 | 
			
		||||
            self.ping()
 | 
			
		||||
            raise e
 | 
			
		||||
@@ -1,16 +0,0 @@
 | 
			
		||||
#    Copyright 2014-2015 ARM Limited
 | 
			
		||||
#
 | 
			
		||||
# Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
# you may not use this file except in compliance with the License.
 | 
			
		||||
# You may obtain a copy of the License at
 | 
			
		||||
#
 | 
			
		||||
#     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
#
 | 
			
		||||
# Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
# distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
# See the License for the specific language governing permissions and
 | 
			
		||||
# limitations under the License.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -1,16 +0,0 @@
 | 
			
		||||
#    Copyright 2013-2015 ARM Limited
 | 
			
		||||
#
 | 
			
		||||
# Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
# you may not use this file except in compliance with the License.
 | 
			
		||||
# You may obtain a copy of the License at
 | 
			
		||||
#
 | 
			
		||||
#     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
#
 | 
			
		||||
# Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
# distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
# See the License for the specific language governing permissions and
 | 
			
		||||
# limitations under the License.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -1,222 +0,0 @@
 | 
			
		||||
#    Copyright 2014-2015 ARM Limited
 | 
			
		||||
#
 | 
			
		||||
# Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
# you may not use this file except in compliance with the License.
 | 
			
		||||
# You may obtain a copy of the License at
 | 
			
		||||
#
 | 
			
		||||
#     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
#
 | 
			
		||||
# Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
# distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
# See the License for the specific language governing permissions and
 | 
			
		||||
# limitations under the License.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
# Original implementation by Rene de Jong. Updated by Sascha Bischoff.
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
import os
 | 
			
		||||
import time
 | 
			
		||||
 | 
			
		||||
from wlauto import AndroidDevice, Parameter
 | 
			
		||||
from wlauto.common.gem5.device import BaseGem5Device
 | 
			
		||||
from wlauto.exceptions import DeviceError
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Gem5AndroidDevice(BaseGem5Device, AndroidDevice):
 | 
			
		||||
    """
 | 
			
		||||
    Implements gem5 Android device.
 | 
			
		||||
 | 
			
		||||
    This class allows a user to connect WA to a simulation using gem5. The
 | 
			
		||||
    connection to the device is made using the telnet connection of the
 | 
			
		||||
    simulator, and is used for all commands. The simulator does not have ADB
 | 
			
		||||
    support, and therefore we need to fall back to using standard shell
 | 
			
		||||
    commands.
 | 
			
		||||
 | 
			
		||||
    Files are copied into the simulation using a VirtIO 9P device in gem5. Files
 | 
			
		||||
    are copied out of the simulated environment using the m5 writefile command
 | 
			
		||||
    within the simulated system.
 | 
			
		||||
 | 
			
		||||
    When starting the workload run, the simulator is automatically started by
 | 
			
		||||
    Workload Automation, and a connection to the simulator is established. WA
 | 
			
		||||
    will then wait for Android to boot on the simulated system (which can take
 | 
			
		||||
    hours), prior to executing any other commands on the device. It is also
 | 
			
		||||
    possible to resume from a checkpoint when starting the simulation. To do
 | 
			
		||||
    this, please append the relevant checkpoint commands from the gem5
 | 
			
		||||
    simulation script to the gem5_discription argument in the agenda.
 | 
			
		||||
 | 
			
		||||
    Host system requirements:
 | 
			
		||||
        * VirtIO support. We rely on diod on the host system. This can be
 | 
			
		||||
          installed on ubuntu using the following command:
 | 
			
		||||
 | 
			
		||||
                sudo apt-get install diod
 | 
			
		||||
 | 
			
		||||
    Guest requirements:
 | 
			
		||||
        * VirtIO support. We rely on VirtIO to move files into the simulation.
 | 
			
		||||
          Please make sure that the following are set in the kernel
 | 
			
		||||
          configuration:
 | 
			
		||||
 | 
			
		||||
                CONFIG_NET_9P=y
 | 
			
		||||
 | 
			
		||||
                CONFIG_NET_9P_VIRTIO=y
 | 
			
		||||
 | 
			
		||||
                CONFIG_9P_FS=y
 | 
			
		||||
 | 
			
		||||
                CONFIG_9P_FS_POSIX_ACL=y
 | 
			
		||||
 | 
			
		||||
                CONFIG_9P_FS_SECURITY=y
 | 
			
		||||
 | 
			
		||||
                CONFIG_VIRTIO_BLK=y
 | 
			
		||||
 | 
			
		||||
        * m5 binary. Please make sure that the m5 binary is on the device and
 | 
			
		||||
          can by found in the path.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    name = 'gem5_android'
 | 
			
		||||
    platform = 'android'
 | 
			
		||||
 | 
			
		||||
    parameters = [
 | 
			
		||||
        Parameter('core_names', default=[], override=True),
 | 
			
		||||
        Parameter('core_clusters', default=[], override=True),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    # Overwritten from Device. For documentation, see corresponding method in
 | 
			
		||||
    # Device.
 | 
			
		||||
 | 
			
		||||
    def __init__(self, **kwargs):
 | 
			
		||||
        self.logger = logging.getLogger('Gem5AndroidDevice')
 | 
			
		||||
        AndroidDevice.__init__(self, **kwargs)
 | 
			
		||||
        BaseGem5Device.__init__(self)
 | 
			
		||||
 | 
			
		||||
    def login_to_device(self):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def wait_for_boot(self):
 | 
			
		||||
        """
 | 
			
		||||
        Wait for the system to boot
 | 
			
		||||
 | 
			
		||||
        We monitor the sys.boot_completed and service.bootanim.exit system
 | 
			
		||||
        properties to determine when the system has finished booting. In the
 | 
			
		||||
        event that we cannot coerce the result of service.bootanim.exit to an
 | 
			
		||||
        integer, we assume that the boot animation was disabled and do not wait
 | 
			
		||||
        for it to finish.
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        self.logger.info("Waiting for Android to boot...")
 | 
			
		||||
        while True:
 | 
			
		||||
            booted = False
 | 
			
		||||
            anim_finished = True  # Assume boot animation was disabled on except
 | 
			
		||||
            try:
 | 
			
		||||
                booted = (int('0' + self.gem5_shell('getprop sys.boot_completed', check_exit_code=False).strip()) == 1)
 | 
			
		||||
                anim_finished = (int(self.gem5_shell('getprop service.bootanim.exit', check_exit_code=False).strip()) == 1)
 | 
			
		||||
            except ValueError:
 | 
			
		||||
                pass
 | 
			
		||||
            if booted and anim_finished:
 | 
			
		||||
                break
 | 
			
		||||
            time.sleep(60)
 | 
			
		||||
 | 
			
		||||
        self.logger.info("Android booted")
 | 
			
		||||
 | 
			
		||||
    def install(self, filepath, timeout=3 * 3600):  # pylint: disable=W0221
 | 
			
		||||
        """ Install an APK or a normal executable """
 | 
			
		||||
        ext = os.path.splitext(filepath)[1].lower()
 | 
			
		||||
        if ext == '.apk':
 | 
			
		||||
            return self.install_apk(filepath, timeout)
 | 
			
		||||
        else:
 | 
			
		||||
            return self.install_executable(filepath)
 | 
			
		||||
 | 
			
		||||
    def install_apk(self, filepath, timeout=3 * 3600):  # pylint: disable=W0221
 | 
			
		||||
        """
 | 
			
		||||
        Install an APK on the gem5 device
 | 
			
		||||
 | 
			
		||||
        The APK is pushed to the device. Then the file and folder permissions
 | 
			
		||||
        are changed to ensure that the APK can be correctly installed. The APK
 | 
			
		||||
        is then installed on the device using 'pm'.
 | 
			
		||||
        """
 | 
			
		||||
        self._check_ready()
 | 
			
		||||
        self.logger.info("Installing {}".format(filepath))
 | 
			
		||||
        ext = os.path.splitext(filepath)[1].lower()
 | 
			
		||||
        if ext == '.apk':
 | 
			
		||||
            filename = os.path.basename(filepath)
 | 
			
		||||
            on_device_path = os.path.join('/data/local/tmp', filename)
 | 
			
		||||
            self.push_file(filepath, on_device_path)
 | 
			
		||||
            # We need to make sure that the folder permissions are set
 | 
			
		||||
            # correctly, else the APK does not install correctly.
 | 
			
		||||
            self.gem5_shell('chmod 775 /data/local/tmp')
 | 
			
		||||
            self.gem5_shell('chmod 774 {}'.format(on_device_path))
 | 
			
		||||
            self.logger.debug("Actually installing the APK: {}".format(on_device_path))
 | 
			
		||||
            return self.gem5_shell("pm install {}".format(on_device_path))
 | 
			
		||||
        else:
 | 
			
		||||
            raise DeviceError('Can\'t install {}: unsupported format.'.format(filepath))
 | 
			
		||||
 | 
			
		||||
    def install_executable(self, filepath, with_name=None):
 | 
			
		||||
        """ Install an executable """
 | 
			
		||||
        executable_name = os.path.basename(filepath)
 | 
			
		||||
        on_device_file = self.path.join(self.working_directory, executable_name)
 | 
			
		||||
        on_device_executable = self.path.join(self.binaries_directory, executable_name)
 | 
			
		||||
        self.push_file(filepath, on_device_file)
 | 
			
		||||
        if self.busybox:
 | 
			
		||||
            self.execute('{} cp {} {}'.format(self.busybox, on_device_file, on_device_executable))
 | 
			
		||||
        else:
 | 
			
		||||
            self.execute('cat {} > {}'.format(on_device_file, on_device_executable))
 | 
			
		||||
        self.execute('chmod 0777 {}'.format(on_device_executable))
 | 
			
		||||
        return on_device_executable
 | 
			
		||||
 | 
			
		||||
    def uninstall(self, package):
 | 
			
		||||
        self._check_ready()
 | 
			
		||||
        self.gem5_shell("pm uninstall {}".format(package))
 | 
			
		||||
 | 
			
		||||
    def dump_logcat(self, outfile, filter_spec=None):
 | 
			
		||||
        """ Extract logcat from simulation """
 | 
			
		||||
        self.logger.info("Extracting logcat from the simulated system")
 | 
			
		||||
        filename = outfile.split('/')[-1]
 | 
			
		||||
        command = 'logcat -d > {}'.format(filename)
 | 
			
		||||
        self.gem5_shell(command)
 | 
			
		||||
        self.pull_file("{}".format(filename), outfile)
 | 
			
		||||
 | 
			
		||||
    def clear_logcat(self):
 | 
			
		||||
        """Clear (flush) logcat log."""
 | 
			
		||||
        if self._logcat_poller:
 | 
			
		||||
            return self._logcat_poller.clear_buffer()
 | 
			
		||||
        else:
 | 
			
		||||
            return self.gem5_shell('logcat -c')
 | 
			
		||||
 | 
			
		||||
    def disable_selinux(self):
 | 
			
		||||
        """ Disable SELinux. Overridden as parent implementation uses ADB """
 | 
			
		||||
        api_level = int(self.gem5_shell('getprop ro.build.version.sdk').strip())
 | 
			
		||||
 | 
			
		||||
        # SELinux was added in Android 4.3 (API level 18). Trying to
 | 
			
		||||
        # 'getenforce' in earlier versions will produce an error.
 | 
			
		||||
        if api_level >= 18:
 | 
			
		||||
            se_status = self.execute('getenforce', as_root=True).strip()
 | 
			
		||||
            if se_status == 'Enforcing':
 | 
			
		||||
                self.execute('setenforce 0', as_root=True)
 | 
			
		||||
 | 
			
		||||
    def get_properties(self, context):  # pylint: disable=R0801
 | 
			
		||||
        """ Get the property files from the device """
 | 
			
		||||
        BaseGem5Device.get_properties(self, context)
 | 
			
		||||
        props = self._get_android_properties(context)
 | 
			
		||||
        return props
 | 
			
		||||
 | 
			
		||||
    def disable_screen_lock(self):
 | 
			
		||||
        """
 | 
			
		||||
        Attempts to disable he screen lock on the device.
 | 
			
		||||
 | 
			
		||||
        Overridden here as otherwise we have issues with too many backslashes.
 | 
			
		||||
        """
 | 
			
		||||
        lockdb = '/data/system/locksettings.db'
 | 
			
		||||
        sqlcommand = "update locksettings set value=\'0\' where name=\'screenlock.disabled\';"
 | 
			
		||||
        self.execute('sqlite3 {} "{}"'.format(lockdb, sqlcommand), as_root=True)
 | 
			
		||||
 | 
			
		||||
    def capture_screen(self, filepath):
 | 
			
		||||
        if BaseGem5Device.capture_screen(self, filepath):
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        # If we didn't manage to do the above, call the parent class.
 | 
			
		||||
        self.logger.warning("capture_screen: falling back to parent class implementation")
 | 
			
		||||
        AndroidDevice.capture_screen(self, filepath)
 | 
			
		||||
 | 
			
		||||
    def initialize(self, context):
 | 
			
		||||
        self.resize_shell()
 | 
			
		||||
        self.deploy_m5(context, force=False)
 | 
			
		||||
@@ -1,38 +0,0 @@
 | 
			
		||||
#    Copyright 2013-2015 ARM Limited
 | 
			
		||||
#
 | 
			
		||||
# Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
# you may not use this file except in compliance with the License.
 | 
			
		||||
# You may obtain a copy of the License at
 | 
			
		||||
#
 | 
			
		||||
#     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
#
 | 
			
		||||
# Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
# distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
# See the License for the specific language governing permissions and
 | 
			
		||||
# limitations under the License.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
from wlauto import AndroidDevice, Parameter
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class GenericDevice(AndroidDevice):
 | 
			
		||||
    name = 'generic_android'
 | 
			
		||||
    description = """
 | 
			
		||||
    A generic Android device interface. Use this if you do not have an interface
 | 
			
		||||
    for your device.
 | 
			
		||||
 | 
			
		||||
    This should allow basic WA functionality on most Android devices using adb over
 | 
			
		||||
    USB. Some additional configuration may be required for some WA extensions
 | 
			
		||||
    (e.g. configuring ``core_names`` and ``core_clusters``).
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    default_working_directory = '/storage/sdcard0/working'
 | 
			
		||||
    has_gpu = True
 | 
			
		||||
 | 
			
		||||
    parameters = [
 | 
			
		||||
        Parameter('core_names', default=[], override=True),
 | 
			
		||||
        Parameter('core_clusters', default=[], override=True),
 | 
			
		||||
    ]
 | 
			
		||||
@@ -1,223 +0,0 @@
 | 
			
		||||
#    Copyright 2014-2015 ARM Limited
 | 
			
		||||
#
 | 
			
		||||
# Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
# you may not use this file except in compliance with the License.
 | 
			
		||||
# You may obtain a copy of the License at
 | 
			
		||||
#
 | 
			
		||||
#     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
#
 | 
			
		||||
# Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
# distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
# See the License for the specific language governing permissions and
 | 
			
		||||
# limitations under the License.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# pylint: disable=E1101
 | 
			
		||||
import os
 | 
			
		||||
import re
 | 
			
		||||
import time
 | 
			
		||||
 | 
			
		||||
import pexpect
 | 
			
		||||
 | 
			
		||||
from wlauto import BigLittleDevice, Parameter
 | 
			
		||||
from wlauto.exceptions import DeviceError
 | 
			
		||||
from wlauto.utils.serial_port import open_serial_connection, pulse_dtr
 | 
			
		||||
from wlauto.utils.android import adb_connect, adb_disconnect, adb_list_devices
 | 
			
		||||
from wlauto.utils.uefi import UefiMenu, UefiConfig
 | 
			
		||||
from wlauto.utils.uboot import UbootMenu
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
AUTOSTART_MESSAGE = 'Press Enter to stop auto boot...'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Juno(BigLittleDevice):
 | 
			
		||||
 | 
			
		||||
    name = 'juno'
 | 
			
		||||
    description = """
 | 
			
		||||
    ARM Juno next generation big.LITTLE development platform.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    capabilities = ['reset_power']
 | 
			
		||||
 | 
			
		||||
    has_gpu = True
 | 
			
		||||
 | 
			
		||||
    core_modules = [
 | 
			
		||||
        'vexpress',
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    parameters = [
 | 
			
		||||
        Parameter('retries', kind=int, default=2,
 | 
			
		||||
                  description="""Specifies the number of times the device will attempt to recover
 | 
			
		||||
                                 (normally, with a hard reset) if it detects that something went wrong."""),
 | 
			
		||||
 | 
			
		||||
        Parameter('microsd_mount_point', default='/media/JUNO',
 | 
			
		||||
                  description='Location at which the device\'s MicroSD card will be mounted.'),
 | 
			
		||||
        Parameter('port', default='/dev/ttyS0', description='Serial port on which the device is connected.'),
 | 
			
		||||
        Parameter('baudrate', kind=int, default=115200, description='Serial connection baud.'),
 | 
			
		||||
        Parameter('timeout', kind=int, default=300, description='Serial connection timeout.'),
 | 
			
		||||
        Parameter('core_names', default=['a53', 'a53', 'a53', 'a53', 'a57', 'a57'], override=True),
 | 
			
		||||
        Parameter('core_clusters', default=[0, 0, 0, 0, 1, 1], override=True),
 | 
			
		||||
 | 
			
		||||
        Parameter('bootloader', default='uefi', allowed_values=['uefi', 'u-boot'],
 | 
			
		||||
                  description="""Bootloader used on the device."""),
 | 
			
		||||
        Parameter('actually_disconnect', kind=bool, default=False,
 | 
			
		||||
                  description="""
 | 
			
		||||
                  Actually perfom "adb disconnect" on closing the connection to the device.
 | 
			
		||||
                  """),
 | 
			
		||||
 | 
			
		||||
        # VExpress flasher expects a device to have these:
 | 
			
		||||
        Parameter('uefi_entry', default='WA',
 | 
			
		||||
                  description='The name of the entry to use (will be created if does not exist).'),
 | 
			
		||||
        Parameter('uefi_config', kind=UefiConfig,
 | 
			
		||||
                  description='''Specifies the configuration for the UEFI entry for his device. In an
 | 
			
		||||
                                 entry specified by ``uefi_entry`` parameter doesn't exist in UEFI menu,
 | 
			
		||||
                                 it will be created using this config. This configuration will also be
 | 
			
		||||
                                 used, when flashing new images.''',
 | 
			
		||||
                  default={
 | 
			
		||||
                      'image_name': 'Image',
 | 
			
		||||
                      'image_args': None,  # populated from bootargs if not specified
 | 
			
		||||
                      'fdt_support': True,
 | 
			
		||||
                  }
 | 
			
		||||
                  ),
 | 
			
		||||
        Parameter('bootargs', default='console=ttyAMA0,115200 earlyprintk=pl011,0x7ff80000 '
 | 
			
		||||
                                      'verbose debug init=/init root=/dev/sda1 rw ip=dhcp '
 | 
			
		||||
                                      'rootwait video=DVI-D-1:1920x1080R@60',
 | 
			
		||||
                  description='''Default boot arguments to use when boot_arguments were not.'''),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    short_delay = 1
 | 
			
		||||
    firmware_prompt = 'Cmd>'
 | 
			
		||||
 | 
			
		||||
    def validate(self):
 | 
			
		||||
        if not self.uefi_config.image_args:
 | 
			
		||||
            self.uefi_config.image_args = self.bootargs
 | 
			
		||||
 | 
			
		||||
    def boot(self, hard=False, **kwargs):
 | 
			
		||||
        if kwargs:
 | 
			
		||||
            self.bootargs = kwargs  # pylint: disable=attribute-defined-outside-init
 | 
			
		||||
        if hard:
 | 
			
		||||
            self.logger.debug('Performing a hard reset.')
 | 
			
		||||
            self.hard_reset()
 | 
			
		||||
        else:
 | 
			
		||||
            self.logger.debug('Resetting the device.')
 | 
			
		||||
            self.reset()
 | 
			
		||||
        if self.bootloader == 'uefi':
 | 
			
		||||
            self._boot_via_uefi()
 | 
			
		||||
        else:
 | 
			
		||||
            self._boot_via_uboot(bootargs=self.bootargs)
 | 
			
		||||
 | 
			
		||||
    def _boot_via_uboot(self, **kwargs):
 | 
			
		||||
        if not kwargs:
 | 
			
		||||
            # Standard linaro configuration will proceed directly to the kernel
 | 
			
		||||
            return
 | 
			
		||||
        with open_serial_connection(port=self.port,
 | 
			
		||||
                                    baudrate=self.baudrate,
 | 
			
		||||
                                    timeout=self.timeout,
 | 
			
		||||
                                    init_dtr=0) as target:
 | 
			
		||||
            menu = UbootMenu(target)
 | 
			
		||||
            self.logger.debug('Waiting for U-Boot prompt...')
 | 
			
		||||
            menu.open(timeout=120)
 | 
			
		||||
            for var, value in kwargs.iteritems():
 | 
			
		||||
                menu.setenv(var, value)
 | 
			
		||||
            menu.boot()
 | 
			
		||||
 | 
			
		||||
    def _boot_via_uefi(self):
 | 
			
		||||
        with open_serial_connection(port=self.port,
 | 
			
		||||
                                    baudrate=self.baudrate,
 | 
			
		||||
                                    timeout=self.timeout,
 | 
			
		||||
                                    init_dtr=0) as target:
 | 
			
		||||
            menu = UefiMenu(target)
 | 
			
		||||
            self.logger.debug('Waiting for UEFI menu...')
 | 
			
		||||
            menu.open(timeout=120)
 | 
			
		||||
            try:
 | 
			
		||||
                menu.select(self.uefi_entry)
 | 
			
		||||
            except LookupError:
 | 
			
		||||
                self.logger.debug('{} UEFI entry not found.'.format(self.uefi_entry))
 | 
			
		||||
                self.logger.debug('Attempting to create one using default flasher configuration.')
 | 
			
		||||
                menu.create_entry(self.uefi_entry, self.uefi_config)
 | 
			
		||||
                menu.select(self.uefi_entry)
 | 
			
		||||
            self.logger.debug('Waiting for the Android prompt.')
 | 
			
		||||
            target.expect(self.android_prompt, timeout=self.timeout)
 | 
			
		||||
 | 
			
		||||
    def connect(self):
 | 
			
		||||
        if not self._is_ready:
 | 
			
		||||
            if not self.adb_name:  # pylint: disable=E0203
 | 
			
		||||
                with open_serial_connection(timeout=self.timeout,
 | 
			
		||||
                                            port=self.port,
 | 
			
		||||
                                            baudrate=self.baudrate,
 | 
			
		||||
                                            init_dtr=0) as target:
 | 
			
		||||
                    target.sendline('')
 | 
			
		||||
                    self.logger.debug('Waiting for the Android prompt.')
 | 
			
		||||
                    target.expect(self.android_prompt)
 | 
			
		||||
 | 
			
		||||
                    self.logger.debug('Waiting for IP address...')
 | 
			
		||||
                    wait_start_time = time.time()
 | 
			
		||||
                    while True:
 | 
			
		||||
                        target.sendline('ip addr list eth0')
 | 
			
		||||
                        time.sleep(1)
 | 
			
		||||
                        try:
 | 
			
		||||
                            target.expect('inet ([1-9]\d*.\d+.\d+.\d+)', timeout=10)
 | 
			
		||||
                            self.adb_name = target.match.group(1) + ':5555'  # pylint: disable=W0201
 | 
			
		||||
                            break
 | 
			
		||||
                        except pexpect.TIMEOUT:
 | 
			
		||||
                            pass  # We have our own timeout -- see below.
 | 
			
		||||
                        if (time.time() - wait_start_time) > self.ready_timeout:
 | 
			
		||||
                            raise DeviceError('Could not acquire IP address.')
 | 
			
		||||
 | 
			
		||||
            if self.adb_name in adb_list_devices():
 | 
			
		||||
                adb_disconnect(self.adb_name)
 | 
			
		||||
            adb_connect(self.adb_name, timeout=self.timeout)
 | 
			
		||||
            super(Juno, self).connect()  # wait for boot to complete etc.
 | 
			
		||||
            self._is_ready = True
 | 
			
		||||
 | 
			
		||||
    def disconnect(self):
 | 
			
		||||
        if self._is_ready:
 | 
			
		||||
            super(Juno, self).disconnect()
 | 
			
		||||
            if self.actually_disconnect:
 | 
			
		||||
                adb_disconnect(self.adb_name)
 | 
			
		||||
            self._is_ready = False
 | 
			
		||||
 | 
			
		||||
    def reset(self):
 | 
			
		||||
        # Currently, reboot is not working in Android on Juno, so
 | 
			
		||||
        # perfrom a ahard reset instead
 | 
			
		||||
        self.hard_reset()
 | 
			
		||||
 | 
			
		||||
    def hard_reset(self):
 | 
			
		||||
        self.disconnect()
 | 
			
		||||
        self.adb_name = None  # Force re-acquire IP address on reboot. pylint: disable=attribute-defined-outside-init
 | 
			
		||||
        with open_serial_connection(port=self.port,
 | 
			
		||||
                                    baudrate=self.baudrate,
 | 
			
		||||
                                    timeout=300,
 | 
			
		||||
                                    init_dtr=0,
 | 
			
		||||
                                    get_conn=True) as (target, conn):
 | 
			
		||||
            pulse_dtr(conn, state=True, duration=0.1)  # TRM specifies a pulse of >=100ms
 | 
			
		||||
 | 
			
		||||
            i = target.expect([AUTOSTART_MESSAGE, self.firmware_prompt])
 | 
			
		||||
            if i:
 | 
			
		||||
                self.logger.debug('Saw firmware prompt.')
 | 
			
		||||
                time.sleep(self.short_delay)
 | 
			
		||||
                target.sendline('reboot')
 | 
			
		||||
            else:
 | 
			
		||||
                self.logger.debug('Saw auto boot message.')
 | 
			
		||||
 | 
			
		||||
    def wait_for_microsd_mount_point(self, target, timeout=100):
 | 
			
		||||
        attempts = 1 + self.retries
 | 
			
		||||
        if os.path.exists(os.path.join(self.microsd_mount_point, 'config.txt')):
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        self.logger.debug('Waiting for VExpress MicroSD to mount...')
 | 
			
		||||
        for i in xrange(attempts):
 | 
			
		||||
            if i:  # Do not reboot on the first attempt.
 | 
			
		||||
                target.sendline('reboot')
 | 
			
		||||
            for _ in xrange(timeout):
 | 
			
		||||
                time.sleep(self.short_delay)
 | 
			
		||||
                if os.path.exists(os.path.join(self.microsd_mount_point, 'config.txt')):
 | 
			
		||||
                    return
 | 
			
		||||
        raise DeviceError('Did not detect MicroSD mount on {}'.format(self.microsd_mount_point))
 | 
			
		||||
 | 
			
		||||
    def get_android_id(self):
 | 
			
		||||
        # Android ID currenlty not set properly in Juno Android builds.
 | 
			
		||||
        return 'abad1deadeadbeef'
 | 
			
		||||
 | 
			
		||||
@@ -1,48 +0,0 @@
 | 
			
		||||
#    Copyright 2013-2015 ARM Limited
 | 
			
		||||
#
 | 
			
		||||
# Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
# you may not use this file except in compliance with the License.
 | 
			
		||||
# You may obtain a copy of the License at
 | 
			
		||||
#
 | 
			
		||||
#     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
#
 | 
			
		||||
# Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
# distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
# See the License for the specific language governing permissions and
 | 
			
		||||
# limitations under the License.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
import time
 | 
			
		||||
 | 
			
		||||
from wlauto import AndroidDevice, Parameter
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Nexus10Device(AndroidDevice):
 | 
			
		||||
 | 
			
		||||
    name = 'Nexus10'
 | 
			
		||||
    description = """
 | 
			
		||||
    Nexus10 is a 10 inch tablet device, which has dual-core A15.
 | 
			
		||||
 | 
			
		||||
    To be able to use Nexus10 in WA, the following must be true:
 | 
			
		||||
 | 
			
		||||
        - USB Debugging Mode is enabled.
 | 
			
		||||
        - Generate USB debugging authorisation for the host machine
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    default_working_directory = '/sdcard/working'
 | 
			
		||||
    has_gpu = True
 | 
			
		||||
    max_cores = 2
 | 
			
		||||
 | 
			
		||||
    parameters = [
 | 
			
		||||
        Parameter('core_names', default=['A15', 'A15'], override=True),
 | 
			
		||||
        Parameter('core_clusters', default=[0, 0], override=True),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    def initialize(self, context):
 | 
			
		||||
        time.sleep(self.long_delay)
 | 
			
		||||
        self.execute('svc power stayon true', check_exit_code=False)
 | 
			
		||||
        time.sleep(self.long_delay)
 | 
			
		||||
        self.execute('input keyevent 82')
 | 
			
		||||
@@ -1,40 +0,0 @@
 | 
			
		||||
#    Copyright 2013-2015 ARM Limited
 | 
			
		||||
#
 | 
			
		||||
# Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
# you may not use this file except in compliance with the License.
 | 
			
		||||
# You may obtain a copy of the License at
 | 
			
		||||
#
 | 
			
		||||
#     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
#
 | 
			
		||||
# Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
# distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
# See the License for the specific language governing permissions and
 | 
			
		||||
# limitations under the License.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
from wlauto import AndroidDevice, Parameter
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Nexus5Device(AndroidDevice):
 | 
			
		||||
 | 
			
		||||
    name = 'Nexus5'
 | 
			
		||||
    description = """
 | 
			
		||||
    Adapter for Nexus 5.
 | 
			
		||||
 | 
			
		||||
    To be able to use Nexus5 in WA, the following must be true:
 | 
			
		||||
 | 
			
		||||
        - USB Debugging Mode is enabled.
 | 
			
		||||
        - Generate USB debugging authorisation for the host machine
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    default_working_directory = '/storage/sdcard0/working'
 | 
			
		||||
    has_gpu = True
 | 
			
		||||
    max_cores = 4
 | 
			
		||||
 | 
			
		||||
    parameters = [
 | 
			
		||||
        Parameter('core_names', default=['krait400', 'krait400', 'krait400', 'krait400'], override=True),
 | 
			
		||||
        Parameter('core_clusters', default=[0, 0, 0, 0], override=True),
 | 
			
		||||
    ]
 | 
			
		||||
@@ -1,76 +0,0 @@
 | 
			
		||||
#    Copyright 2013-2015 ARM Limited
 | 
			
		||||
#
 | 
			
		||||
# Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
# you may not use this file except in compliance with the License.
 | 
			
		||||
# You may obtain a copy of the License at
 | 
			
		||||
#
 | 
			
		||||
#     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
#
 | 
			
		||||
# Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
# distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
# See the License for the specific language governing permissions and
 | 
			
		||||
# limitations under the License.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
import time
 | 
			
		||||
 | 
			
		||||
from wlauto import AndroidDevice, Parameter
 | 
			
		||||
from wlauto.exceptions import TimeoutError
 | 
			
		||||
from wlauto.utils.android import adb_shell
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Note3Device(AndroidDevice):
 | 
			
		||||
 | 
			
		||||
    name = 'Note3'
 | 
			
		||||
    description = """
 | 
			
		||||
    Adapter for Galaxy Note 3.
 | 
			
		||||
 | 
			
		||||
    To be able to use Note3 in WA, the following must be true:
 | 
			
		||||
 | 
			
		||||
        - USB Debugging Mode is enabled.
 | 
			
		||||
        - Generate USB debugging authorisation for the host machine
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    parameters = [
 | 
			
		||||
        Parameter('core_names', default=['A15', 'A15', 'A15', 'A15'], override=True),
 | 
			
		||||
        Parameter('core_clusters', default=[0, 0, 0, 0], override=True),
 | 
			
		||||
        Parameter('working_directory', default='/storage/sdcard0/wa-working', override=True),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    def __init__(self, **kwargs):
 | 
			
		||||
        super(Note3Device, self).__init__(**kwargs)
 | 
			
		||||
        self._just_rebooted = False
 | 
			
		||||
 | 
			
		||||
    def initialize(self, context):
 | 
			
		||||
        self.execute('svc power stayon true', check_exit_code=False)
 | 
			
		||||
 | 
			
		||||
    def reset(self):
 | 
			
		||||
        super(Note3Device, self).reset()
 | 
			
		||||
        self._just_rebooted = True
 | 
			
		||||
 | 
			
		||||
    def hard_reset(self):
 | 
			
		||||
        super(Note3Device, self).hard_reset()
 | 
			
		||||
        self._just_rebooted = True
 | 
			
		||||
 | 
			
		||||
    def connect(self):  # NOQA pylint: disable=R0912
 | 
			
		||||
        super(Note3Device, self).connect()
 | 
			
		||||
        if self._just_rebooted:
 | 
			
		||||
            self.logger.debug('Waiting for boot to complete...')
 | 
			
		||||
            # On the Note 3, adb connection gets reset some time after booting.
 | 
			
		||||
            # This  causes errors during execution. To prevent this, open a shell
 | 
			
		||||
            # session and wait for it to be killed. Once its killed, give adb
 | 
			
		||||
            # enough time to restart, and then the device should be ready.
 | 
			
		||||
            try:
 | 
			
		||||
                adb_shell(self.adb_name, '', timeout=20)  # pylint: disable=no-member
 | 
			
		||||
                time.sleep(5)  # give adb time to re-initialize
 | 
			
		||||
            except TimeoutError:
 | 
			
		||||
                pass  # timed out waiting for the session to be killed -- assume not going to be.
 | 
			
		||||
 | 
			
		||||
            self.logger.debug('Boot completed.')
 | 
			
		||||
            self._just_rebooted = False
 | 
			
		||||
        # Swipe upwards to unlock the screen.
 | 
			
		||||
        time.sleep(self.long_delay)
 | 
			
		||||
        self.execute('input touchscreen swipe 540 1600 560 800 ')
 | 
			
		||||
@@ -1,38 +0,0 @@
 | 
			
		||||
#    Copyright 2013-2015 ARM Limited
 | 
			
		||||
#
 | 
			
		||||
# Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
# you may not use this file except in compliance with the License.
 | 
			
		||||
# You may obtain a copy of the License at
 | 
			
		||||
#
 | 
			
		||||
#     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
#
 | 
			
		||||
# Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
# distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
# See the License for the specific language governing permissions and
 | 
			
		||||
# limitations under the License.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
from wlauto import AndroidDevice, Parameter
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class OdroidXU3(AndroidDevice):
 | 
			
		||||
 | 
			
		||||
    name = "odroidxu3"
 | 
			
		||||
    description = 'HardKernel Odroid XU3 development board.'
 | 
			
		||||
 | 
			
		||||
    core_modules = [
 | 
			
		||||
        'odroidxu3-fan',
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    parameters = [
 | 
			
		||||
        Parameter('adb_name', default='BABABEEFBABABEEF', override=True),
 | 
			
		||||
        Parameter('working_directory', default='/data/local/wa-working', override=True),
 | 
			
		||||
        Parameter('core_names', default=['a7', 'a7', 'a7', 'a7', 'a15', 'a15', 'a15', 'a15'], override=True),
 | 
			
		||||
        Parameter('core_clusters', default=[0, 0, 0, 0, 1, 1, 1, 1], override=True),
 | 
			
		||||
        Parameter('port', default='/dev/ttyUSB0', kind=str,
 | 
			
		||||
                  description='Serial port on which the device is connected'),
 | 
			
		||||
        Parameter('baudrate', default=115200, kind=int, description='Serial connection baud rate'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
@@ -1,850 +0,0 @@
 | 
			
		||||
#    Copyright 2013-2015 ARM Limited
 | 
			
		||||
#
 | 
			
		||||
# Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
# you may not use this file except in compliance with the License.
 | 
			
		||||
# You may obtain a copy of the License at
 | 
			
		||||
#
 | 
			
		||||
#     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
#
 | 
			
		||||
# Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
# distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
# See the License for the specific language governing permissions and
 | 
			
		||||
# limitations under the License.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
import os
 | 
			
		||||
import sys
 | 
			
		||||
import re
 | 
			
		||||
import string
 | 
			
		||||
import shutil
 | 
			
		||||
import time
 | 
			
		||||
from collections import Counter
 | 
			
		||||
 | 
			
		||||
import pexpect
 | 
			
		||||
 | 
			
		||||
from wlauto import BigLittleDevice, RuntimeParameter, Parameter, settings
 | 
			
		||||
from wlauto.exceptions import ConfigError, DeviceError
 | 
			
		||||
from wlauto.utils.android import adb_connect, adb_disconnect, adb_list_devices
 | 
			
		||||
from wlauto.utils.serial_port import open_serial_connection
 | 
			
		||||
from wlauto.utils.misc import merge_dicts
 | 
			
		||||
from wlauto.utils.types import boolean
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
BOOT_FIRMWARE = {
 | 
			
		||||
    'uefi': {
 | 
			
		||||
        'SCC_0x010': '0x000003E0',
 | 
			
		||||
        'reboot_attempts': 0,
 | 
			
		||||
    },
 | 
			
		||||
    'bootmon': {
 | 
			
		||||
        'SCC_0x010': '0x000003D0',
 | 
			
		||||
        'reboot_attempts': 2,
 | 
			
		||||
    },
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MODES = {
 | 
			
		||||
    'mp_a7_only': {
 | 
			
		||||
        'images_file': 'images_mp.txt',
 | 
			
		||||
        'dtb': 'mp_a7',
 | 
			
		||||
        'initrd': 'init_mp',
 | 
			
		||||
        'kernel': 'kern_mp',
 | 
			
		||||
        'SCC_0x700': '0x1032F003',
 | 
			
		||||
        'cpus': ['a7', 'a7', 'a7'],
 | 
			
		||||
    },
 | 
			
		||||
    'mp_a7_bootcluster': {
 | 
			
		||||
        'images_file': 'images_mp.txt',
 | 
			
		||||
        'dtb': 'mp_a7bc',
 | 
			
		||||
        'initrd': 'init_mp',
 | 
			
		||||
        'kernel': 'kern_mp',
 | 
			
		||||
        'SCC_0x700': '0x1032F003',
 | 
			
		||||
        'cpus': ['a7', 'a7', 'a7', 'a15', 'a15'],
 | 
			
		||||
    },
 | 
			
		||||
    'mp_a15_only': {
 | 
			
		||||
        'images_file': 'images_mp.txt',
 | 
			
		||||
        'dtb': 'mp_a15',
 | 
			
		||||
        'initrd': 'init_mp',
 | 
			
		||||
        'kernel': 'kern_mp',
 | 
			
		||||
        'SCC_0x700': '0x0032F003',
 | 
			
		||||
        'cpus': ['a15', 'a15'],
 | 
			
		||||
    },
 | 
			
		||||
    'mp_a15_bootcluster': {
 | 
			
		||||
        'images_file': 'images_mp.txt',
 | 
			
		||||
        'dtb': 'mp_a15bc',
 | 
			
		||||
        'initrd': 'init_mp',
 | 
			
		||||
        'kernel': 'kern_mp',
 | 
			
		||||
        'SCC_0x700': '0x0032F003',
 | 
			
		||||
        'cpus': ['a15', 'a15', 'a7', 'a7', 'a7'],
 | 
			
		||||
    },
 | 
			
		||||
    'iks_cpu': {
 | 
			
		||||
        'images_file': 'images_iks.txt',
 | 
			
		||||
        'dtb': 'iks',
 | 
			
		||||
        'initrd': 'init_iks',
 | 
			
		||||
        'kernel': 'kern_iks',
 | 
			
		||||
        'SCC_0x700': '0x1032F003',
 | 
			
		||||
        'cpus': ['a7', 'a7'],
 | 
			
		||||
    },
 | 
			
		||||
    'iks_a15': {
 | 
			
		||||
        'images_file': 'images_iks.txt',
 | 
			
		||||
        'dtb': 'iks',
 | 
			
		||||
        'initrd': 'init_iks',
 | 
			
		||||
        'kernel': 'kern_iks',
 | 
			
		||||
        'SCC_0x700': '0x0032F003',
 | 
			
		||||
        'cpus': ['a15', 'a15'],
 | 
			
		||||
    },
 | 
			
		||||
    'iks_a7': {
 | 
			
		||||
        'images_file': 'images_iks.txt',
 | 
			
		||||
        'dtb': 'iks',
 | 
			
		||||
        'initrd': 'init_iks',
 | 
			
		||||
        'kernel': 'kern_iks',
 | 
			
		||||
        'SCC_0x700': '0x0032F003',
 | 
			
		||||
        'cpus': ['a7', 'a7'],
 | 
			
		||||
    },
 | 
			
		||||
    'iks_ns_a15': {
 | 
			
		||||
        'images_file': 'images_iks.txt',
 | 
			
		||||
        'dtb': 'iks',
 | 
			
		||||
        'initrd': 'init_iks',
 | 
			
		||||
        'kernel': 'kern_iks',
 | 
			
		||||
        'SCC_0x700': '0x0032F003',
 | 
			
		||||
        'cpus': ['a7', 'a7', 'a7', 'a15', 'a15'],
 | 
			
		||||
    },
 | 
			
		||||
    'iks_ns_a7': {
 | 
			
		||||
        'images_file': 'images_iks.txt',
 | 
			
		||||
        'dtb': 'iks',
 | 
			
		||||
        'initrd': 'init_iks',
 | 
			
		||||
        'kernel': 'kern_iks',
 | 
			
		||||
        'SCC_0x700': '0x0032F003',
 | 
			
		||||
        'cpus': ['a7', 'a7', 'a7', 'a15', 'a15'],
 | 
			
		||||
    },
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
A7_ONLY_MODES = ['mp_a7_only', 'iks_a7', 'iks_cpu']
 | 
			
		||||
A15_ONLY_MODES = ['mp_a15_only', 'iks_a15']
 | 
			
		||||
 | 
			
		||||
DEFAULT_A7_GOVERNOR_TUNABLES = {
 | 
			
		||||
    'interactive': {
 | 
			
		||||
        'above_hispeed_delay': 80000,
 | 
			
		||||
        'go_hispeed_load': 85,
 | 
			
		||||
        'hispeed_freq': 800000,
 | 
			
		||||
        'min_sample_time': 80000,
 | 
			
		||||
        'timer_rate': 20000,
 | 
			
		||||
    },
 | 
			
		||||
    'ondemand': {
 | 
			
		||||
        'sampling_rate': 50000,
 | 
			
		||||
    },
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFAULT_A15_GOVERNOR_TUNABLES = {
 | 
			
		||||
    'interactive': {
 | 
			
		||||
        'above_hispeed_delay': 80000,
 | 
			
		||||
        'go_hispeed_load': 85,
 | 
			
		||||
        'hispeed_freq': 1000000,
 | 
			
		||||
        'min_sample_time': 80000,
 | 
			
		||||
        'timer_rate': 20000,
 | 
			
		||||
    },
 | 
			
		||||
    'ondemand': {
 | 
			
		||||
        'sampling_rate': 50000,
 | 
			
		||||
    },
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ADB_SHELL_TIMEOUT = 30
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class _TC2DeviceConfig(object):
 | 
			
		||||
 | 
			
		||||
    name = 'TC2 Configuration'
 | 
			
		||||
    device_name = 'TC2'
 | 
			
		||||
 | 
			
		||||
    def __init__(self,  # pylint: disable=R0914,W0613
 | 
			
		||||
                 root_mount='/media/VEMSD',
 | 
			
		||||
 | 
			
		||||
                 disable_boot_configuration=False,
 | 
			
		||||
                 boot_firmware=None,
 | 
			
		||||
                 mode=None,
 | 
			
		||||
 | 
			
		||||
                 fs_medium='usb',
 | 
			
		||||
 | 
			
		||||
                 device_working_directory='/data/local/usecase',
 | 
			
		||||
 | 
			
		||||
                 bm_image='bm_v519r.axf',
 | 
			
		||||
 | 
			
		||||
                 serial_device='/dev/ttyS0',
 | 
			
		||||
                 serial_baud=38400,
 | 
			
		||||
                 serial_max_timeout=600,
 | 
			
		||||
                 serial_log=sys.stdout,
 | 
			
		||||
 | 
			
		||||
                 init_timeout=120,
 | 
			
		||||
 | 
			
		||||
                 always_delete_uefi_entry=True,
 | 
			
		||||
                 psci_enable=True,
 | 
			
		||||
 | 
			
		||||
                 host_working_directory=None,
 | 
			
		||||
 | 
			
		||||
                 a7_governor_tunables=None,
 | 
			
		||||
                 a15_governor_tunables=None,
 | 
			
		||||
 | 
			
		||||
                 adb_name=None,
 | 
			
		||||
                 # Compatibility with other android devices.
 | 
			
		||||
                 enable_screen_check=None,  # pylint: disable=W0613
 | 
			
		||||
                 **kwargs
 | 
			
		||||
                 ):
 | 
			
		||||
        self.root_mount = root_mount
 | 
			
		||||
        self.disable_boot_configuration = disable_boot_configuration
 | 
			
		||||
        if not disable_boot_configuration:
 | 
			
		||||
            self.boot_firmware = boot_firmware or 'uefi'
 | 
			
		||||
            self.default_mode = mode or 'mp_a7_bootcluster'
 | 
			
		||||
        elif boot_firmware or mode:
 | 
			
		||||
            raise ConfigError('boot_firmware and/or mode cannot be specified when disable_boot_configuration is enabled.')
 | 
			
		||||
 | 
			
		||||
        self.mode = self.default_mode
 | 
			
		||||
        self.working_directory = device_working_directory
 | 
			
		||||
        self.serial_device = serial_device
 | 
			
		||||
        self.serial_baud = serial_baud
 | 
			
		||||
        self.serial_max_timeout = serial_max_timeout
 | 
			
		||||
        self.serial_log = serial_log
 | 
			
		||||
        self.bootmon_prompt = re.compile('^([KLM]:\\\)?>', re.MULTILINE)
 | 
			
		||||
 | 
			
		||||
        self.fs_medium = fs_medium.lower()
 | 
			
		||||
 | 
			
		||||
        self.bm_image = bm_image
 | 
			
		||||
 | 
			
		||||
        self.init_timeout = init_timeout
 | 
			
		||||
 | 
			
		||||
        self.always_delete_uefi_entry = always_delete_uefi_entry
 | 
			
		||||
        self.psci_enable = psci_enable
 | 
			
		||||
 | 
			
		||||
        self.resource_dir = os.path.join(os.path.dirname(__file__), 'resources')
 | 
			
		||||
        self.board_dir = os.path.join(self.root_mount, 'SITE1', 'HBI0249A')
 | 
			
		||||
        self.board_file = 'board.txt'
 | 
			
		||||
        self.board_file_bak = 'board.bak'
 | 
			
		||||
        self.images_file = 'images.txt'
 | 
			
		||||
 | 
			
		||||
        self.host_working_directory = host_working_directory or settings.meta_directory
 | 
			
		||||
 | 
			
		||||
        if not a7_governor_tunables:
 | 
			
		||||
            self.a7_governor_tunables = DEFAULT_A7_GOVERNOR_TUNABLES
 | 
			
		||||
        else:
 | 
			
		||||
            self.a7_governor_tunables = merge_dicts(DEFAULT_A7_GOVERNOR_TUNABLES, a7_governor_tunables)
 | 
			
		||||
 | 
			
		||||
        if not a15_governor_tunables:
 | 
			
		||||
            self.a15_governor_tunables = DEFAULT_A15_GOVERNOR_TUNABLES
 | 
			
		||||
        else:
 | 
			
		||||
            self.a15_governor_tunables = merge_dicts(DEFAULT_A15_GOVERNOR_TUNABLES, a15_governor_tunables)
 | 
			
		||||
 | 
			
		||||
        self.adb_name = adb_name
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def src_images_template_file(self):
 | 
			
		||||
        return os.path.join(self.resource_dir, MODES[self.mode]['images_file'])
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def src_images_file(self):
 | 
			
		||||
        return os.path.join(self.host_working_directory, 'images.txt')
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def src_board_template_file(self):
 | 
			
		||||
        return os.path.join(self.resource_dir, 'board_template.txt')
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def src_board_file(self):
 | 
			
		||||
        return os.path.join(self.host_working_directory, 'board.txt')
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def kernel_arguments(self):
 | 
			
		||||
        kernel_args = ' console=ttyAMA0,38400 androidboot.console=ttyAMA0 selinux=0'
 | 
			
		||||
        if self.fs_medium == 'usb':
 | 
			
		||||
            kernel_args += ' androidboot.hardware=arm-versatileexpress-usb'
 | 
			
		||||
        if 'iks' in self.mode:
 | 
			
		||||
            kernel_args += ' no_bL_switcher=0'
 | 
			
		||||
        return kernel_args
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def kernel(self):
 | 
			
		||||
        return MODES[self.mode]['kernel']
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def initrd(self):
 | 
			
		||||
        return MODES[self.mode]['initrd']
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def dtb(self):
 | 
			
		||||
        return MODES[self.mode]['dtb']
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def SCC_0x700(self):
 | 
			
		||||
        return MODES[self.mode]['SCC_0x700']
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def SCC_0x010(self):
 | 
			
		||||
        return BOOT_FIRMWARE[self.boot_firmware]['SCC_0x010']
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def reboot_attempts(self):
 | 
			
		||||
        return BOOT_FIRMWARE[self.boot_firmware]['reboot_attempts']
 | 
			
		||||
 | 
			
		||||
    def validate(self):
 | 
			
		||||
        valid_modes = MODES.keys()
 | 
			
		||||
        if self.mode not in valid_modes:
 | 
			
		||||
            message = 'Invalid mode: {}; must be in {}'.format(
 | 
			
		||||
                self.mode, valid_modes)
 | 
			
		||||
            raise ConfigError(message)
 | 
			
		||||
 | 
			
		||||
        valid_boot_firmware = BOOT_FIRMWARE.keys()
 | 
			
		||||
        if self.boot_firmware not in valid_boot_firmware:
 | 
			
		||||
            message = 'Invalid boot_firmware: {}; must be in {}'.format(
 | 
			
		||||
                self.boot_firmware,
 | 
			
		||||
                valid_boot_firmware)
 | 
			
		||||
            raise ConfigError(message)
 | 
			
		||||
 | 
			
		||||
        if self.fs_medium not in ['usb', 'sdcard']:
 | 
			
		||||
            message = 'Invalid filesystem medium: {}  allowed values : usb, sdcard '.format(self.fs_medium)
 | 
			
		||||
            raise ConfigError(message)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TC2Device(BigLittleDevice):
 | 
			
		||||
 | 
			
		||||
    name = 'TC2'
 | 
			
		||||
    description = """
 | 
			
		||||
    TC2 is a development board, which has three A7 cores and two A15 cores.
 | 
			
		||||
 | 
			
		||||
    TC2 has a number of boot parameters which are:
 | 
			
		||||
 | 
			
		||||
        :root_mount: Defaults to '/media/VEMSD'
 | 
			
		||||
        :boot_firmware: It has only two boot firmware options, which are
 | 
			
		||||
                        uefi and bootmon. Defaults to 'uefi'.
 | 
			
		||||
        :fs_medium: Defaults to 'usb'.
 | 
			
		||||
        :device_working_directory: The direcitory that WA will be using to copy
 | 
			
		||||
                                   files to. Defaults to 'data/local/usecase'
 | 
			
		||||
        :serial_device: The serial device which TC2 is connected to. Defaults to
 | 
			
		||||
                        '/dev/ttyS0'.
 | 
			
		||||
        :serial_baud: Defaults to 38400.
 | 
			
		||||
        :serial_max_timeout: Serial timeout value in seconds. Defaults to 600.
 | 
			
		||||
        :serial_log: Defaults to standard output.
 | 
			
		||||
        :init_timeout: The timeout in seconds to init the device. Defaults set
 | 
			
		||||
                       to 30.
 | 
			
		||||
        :always_delete_uefi_entry: If true, it will delete the ufi entry.
 | 
			
		||||
                                   Defaults to True.
 | 
			
		||||
        :psci_enable: Enabling the psci. Defaults to True.
 | 
			
		||||
        :host_working_directory: The host working directory. Defaults to None.
 | 
			
		||||
        :disable_boot_configuration: Disables boot configuration through images.txt and board.txt. When
 | 
			
		||||
                                     this is ``True``, those two files will not be overwritten in VEMSD.
 | 
			
		||||
                                     This option may be necessary if the firmware version in the ``TC2``
 | 
			
		||||
                                     is not compatible with the templates in WA. Please note that enabling
 | 
			
		||||
                                     this will prevent you form being able to set ``boot_firmware`` and
 | 
			
		||||
                                     ``mode`` parameters. Defaults to ``False``.
 | 
			
		||||
 | 
			
		||||
    TC2 can also have a number of different booting mode, which are:
 | 
			
		||||
 | 
			
		||||
        :mp_a7_only: Only the A7 cluster.
 | 
			
		||||
        :mp_a7_bootcluster: Both A7 and A15 clusters, but it boots on A7
 | 
			
		||||
                            cluster.
 | 
			
		||||
        :mp_a15_only: Only the A15 cluster.
 | 
			
		||||
        :mp_a15_bootcluster: Both A7 and A15 clusters, but it boots on A15
 | 
			
		||||
                             clusters.
 | 
			
		||||
        :iks_cpu: Only A7 cluster with only 2 cpus.
 | 
			
		||||
        :iks_a15: Only A15 cluster.
 | 
			
		||||
        :iks_a7: Same as iks_cpu
 | 
			
		||||
        :iks_ns_a15: Both A7 and A15 clusters.
 | 
			
		||||
        :iks_ns_a7: Both A7 and A15 clusters.
 | 
			
		||||
 | 
			
		||||
    The difference between mp and iks is the scheduling policy.
 | 
			
		||||
 | 
			
		||||
    TC2 takes the following runtime parameters
 | 
			
		||||
 | 
			
		||||
        :a7_cores: Number of active A7 cores.
 | 
			
		||||
        :a15_cores: Number of active A15 cores.
 | 
			
		||||
        :a7_governor: CPUFreq governor for the A7 cluster.
 | 
			
		||||
        :a15_governor: CPUFreq governor for the A15 cluster.
 | 
			
		||||
        :a7_min_frequency: Minimum CPU frequency for the A7 cluster.
 | 
			
		||||
        :a15_min_frequency: Minimum CPU frequency for the A15 cluster.
 | 
			
		||||
        :a7_max_frequency: Maximum CPU frequency for the A7 cluster.
 | 
			
		||||
        :a15_max_frequency: Maximum CPU frequency for the A7 cluster.
 | 
			
		||||
        :irq_affinity: lambda x: Which cluster will receive IRQs.
 | 
			
		||||
        :cpuidle: Whether idle states should be enabled.
 | 
			
		||||
        :sysfile_values: A dict mapping a complete file path to the value that
 | 
			
		||||
                         should be echo'd into it. By default, the file will be
 | 
			
		||||
                         subsequently read to verify that the value was written
 | 
			
		||||
                         into it with DeviceError raised otherwise. For write-only
 | 
			
		||||
                         files, this check can be disabled by appending a ``!`` to
 | 
			
		||||
                         the end of the file path.
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    has_gpu = False
 | 
			
		||||
    a15_only_modes = A15_ONLY_MODES
 | 
			
		||||
    a7_only_modes = A7_ONLY_MODES
 | 
			
		||||
    not_configurable_modes = ['iks_a7', 'iks_cpu', 'iks_a15']
 | 
			
		||||
 | 
			
		||||
    parameters = [
 | 
			
		||||
        Parameter('core_names', mandatory=False, override=True,
 | 
			
		||||
                  description='This parameter will be ignored for TC2'),
 | 
			
		||||
        Parameter('core_clusters', mandatory=False, override=True,
 | 
			
		||||
                  description='This parameter will be ignored for TC2'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    runtime_parameters = [
 | 
			
		||||
        RuntimeParameter('irq_affinity', lambda d, x: d.set_irq_affinity(x.lower()), lambda: None),
 | 
			
		||||
        RuntimeParameter('cpuidle', lambda d, x: d.enable_idle_states() if boolean(x) else d.disable_idle_states(),
 | 
			
		||||
                         lambda d: d.get_cpuidle())
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    def get_mode(self):
 | 
			
		||||
        return self.config.mode
 | 
			
		||||
 | 
			
		||||
    def set_mode(self, mode):
 | 
			
		||||
        if self._has_booted:
 | 
			
		||||
            raise DeviceError('Attempting to set boot mode when already booted.')
 | 
			
		||||
        valid_modes = MODES.keys()
 | 
			
		||||
        if mode is None:
 | 
			
		||||
            mode = self.config.default_mode
 | 
			
		||||
        if mode not in valid_modes:
 | 
			
		||||
            message = 'Invalid mode: {}; must be in {}'.format(mode, valid_modes)
 | 
			
		||||
            raise ConfigError(message)
 | 
			
		||||
        self.config.mode = mode
 | 
			
		||||
 | 
			
		||||
    mode = property(get_mode, set_mode)
 | 
			
		||||
 | 
			
		||||
    def _get_core_names(self):
 | 
			
		||||
        return MODES[self.mode]['cpus']
 | 
			
		||||
 | 
			
		||||
    def _set_core_names(self, value):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    core_names = property(_get_core_names, _set_core_names)
 | 
			
		||||
 | 
			
		||||
    def _get_core_clusters(self):
 | 
			
		||||
        seen = set([])
 | 
			
		||||
        core_clusters = []
 | 
			
		||||
        cluster_id = -1
 | 
			
		||||
        for core in MODES[self.mode]['cpus']:
 | 
			
		||||
            if core not in seen:
 | 
			
		||||
                seen.add(core)
 | 
			
		||||
                cluster_id += 1
 | 
			
		||||
            core_clusters.append(cluster_id)
 | 
			
		||||
        return core_clusters
 | 
			
		||||
 | 
			
		||||
    def _set_core_clusters(self, value):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    core_clusters = property(_get_core_clusters, _set_core_clusters)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def cpu_cores(self):
 | 
			
		||||
        return MODES[self.mode]['cpus']
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def max_a7_cores(self):
 | 
			
		||||
        return Counter(MODES[self.mode]['cpus'])['a7']
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def max_a15_cores(self):
 | 
			
		||||
        return Counter(MODES[self.mode]['cpus'])['a15']
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def a7_governor_tunables(self):
 | 
			
		||||
        return self.config.a7_governor_tunables
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def a15_governor_tunables(self):
 | 
			
		||||
        return self.config.a15_governor_tunables
 | 
			
		||||
 | 
			
		||||
    def __init__(self, **kwargs):
 | 
			
		||||
        super(TC2Device, self).__init__()
 | 
			
		||||
        self.config = _TC2DeviceConfig(**kwargs)
 | 
			
		||||
        self.working_directory = self.config.working_directory
 | 
			
		||||
        self._serial = None
 | 
			
		||||
        self._has_booted = None
 | 
			
		||||
 | 
			
		||||
    def boot(self, **kwargs):  # NOQA
 | 
			
		||||
        mode = kwargs.get('os_mode', None)
 | 
			
		||||
        self._is_ready = False
 | 
			
		||||
        self._has_booted = False
 | 
			
		||||
 | 
			
		||||
        self.mode = mode
 | 
			
		||||
        self.logger.debug('Booting in {} mode'.format(self.mode))
 | 
			
		||||
 | 
			
		||||
        with open_serial_connection(timeout=self.config.serial_max_timeout,
 | 
			
		||||
                                    port=self.config.serial_device,
 | 
			
		||||
                                    baudrate=self.config.serial_baud) as target:
 | 
			
		||||
            if self.config.boot_firmware == 'bootmon':
 | 
			
		||||
                self._boot_using_bootmon(target)
 | 
			
		||||
            elif self.config.boot_firmware == 'uefi':
 | 
			
		||||
                self._boot_using_uefi(target)
 | 
			
		||||
            else:
 | 
			
		||||
                message = 'Unexpected boot firmware: {}'.format(self.config.boot_firmware)
 | 
			
		||||
                raise ConfigError(message)
 | 
			
		||||
 | 
			
		||||
            try:
 | 
			
		||||
                target.sendline('')
 | 
			
		||||
                self.logger.debug('Waiting for the Android prompt.')
 | 
			
		||||
                target.expect(self.android_prompt, timeout=40)  # pylint: disable=E1101
 | 
			
		||||
            except pexpect.TIMEOUT:
 | 
			
		||||
                # Try a second time before giving up.
 | 
			
		||||
                self.logger.debug('Did not get Android prompt, retrying...')
 | 
			
		||||
                target.sendline('')
 | 
			
		||||
                target.expect(self.android_prompt, timeout=10)  # pylint: disable=E1101
 | 
			
		||||
 | 
			
		||||
            self.logger.debug('Waiting for OS to initialize...')
 | 
			
		||||
            started_waiting_time = time.time()
 | 
			
		||||
            time.sleep(20)  # we know it's not going to to take less time than this.
 | 
			
		||||
            boot_completed, got_ip_address = False, False
 | 
			
		||||
            while True:
 | 
			
		||||
                try:
 | 
			
		||||
                    if not boot_completed:
 | 
			
		||||
                        target.sendline('getprop sys.boot_completed')
 | 
			
		||||
                        boot_completed = target.expect(['0.*', '1.*'], timeout=10)
 | 
			
		||||
                    if not got_ip_address:
 | 
			
		||||
                        target.sendline('getprop dhcp.eth0.ipaddress')
 | 
			
		||||
                        # regexes  are processed in order, so ip regex has to
 | 
			
		||||
                        # come first (as we only want to match new line if we
 | 
			
		||||
                        # don't match the IP). We do a "not" make the logic
 | 
			
		||||
                        # consistent with boot_completed.
 | 
			
		||||
                        got_ip_address = not target.expect(['[1-9]\d*.\d+.\d+.\d+', '\n'], timeout=10)
 | 
			
		||||
                except pexpect.TIMEOUT:
 | 
			
		||||
                    pass  # We have our own timeout -- see below.
 | 
			
		||||
                if boot_completed and got_ip_address:
 | 
			
		||||
                    break
 | 
			
		||||
                time.sleep(5)
 | 
			
		||||
                if (time.time() - started_waiting_time) > self.config.init_timeout:
 | 
			
		||||
                    raise DeviceError('Timed out waiting for the device to initialize.')
 | 
			
		||||
 | 
			
		||||
        self._has_booted = True
 | 
			
		||||
 | 
			
		||||
    def connect(self):
 | 
			
		||||
        if not self._is_ready:
 | 
			
		||||
            if self.config.adb_name:
 | 
			
		||||
                self.adb_name = self.config.adb_name  # pylint: disable=attribute-defined-outside-init
 | 
			
		||||
            else:
 | 
			
		||||
                with open_serial_connection(timeout=self.config.serial_max_timeout,
 | 
			
		||||
                                            port=self.config.serial_device,
 | 
			
		||||
                                            baudrate=self.config.serial_baud) as target:
 | 
			
		||||
                    # Get IP address and push the Gator and PMU logger.
 | 
			
		||||
                    target.sendline('su')  # as of Android v5.0.2, Linux does not boot into root shell
 | 
			
		||||
                    target.sendline('netcfg')
 | 
			
		||||
                    ipaddr_re = re.compile('eth0 +UP +(.+)/.+', re.MULTILINE)
 | 
			
		||||
                    target.expect(ipaddr_re)
 | 
			
		||||
                    output = target.after
 | 
			
		||||
                    match = re.search('eth0 +UP +(.+)/.+', output)
 | 
			
		||||
                    if not match:
 | 
			
		||||
                        raise DeviceError('Could not get adb IP address.')
 | 
			
		||||
                    ipaddr = match.group(1)
 | 
			
		||||
 | 
			
		||||
                    # Connect to device using adb.
 | 
			
		||||
                    target.expect(self.android_prompt)  # pylint: disable=E1101
 | 
			
		||||
                    self.adb_name = ipaddr + ":5555"  # pylint: disable=W0201
 | 
			
		||||
 | 
			
		||||
            if self.adb_name in adb_list_devices():
 | 
			
		||||
                adb_disconnect(self.adb_name)
 | 
			
		||||
            adb_connect(self.adb_name)
 | 
			
		||||
            self._is_ready = True
 | 
			
		||||
            self.execute("input keyevent 82", timeout=ADB_SHELL_TIMEOUT)
 | 
			
		||||
            self.execute("svc power stayon true", timeout=ADB_SHELL_TIMEOUT)
 | 
			
		||||
 | 
			
		||||
    def disconnect(self):
 | 
			
		||||
        adb_disconnect(self.adb_name)
 | 
			
		||||
        self._is_ready = False
 | 
			
		||||
 | 
			
		||||
    # TC2-specific methods. You should avoid calling these in
 | 
			
		||||
    # Workloads/Instruments as that would tie them to TC2 (and if that is
 | 
			
		||||
    # the case, then you should set the supported_devices parameter in the
 | 
			
		||||
    # Workload/Instrument accordingly). Most of these can be replace with a
 | 
			
		||||
    # call to set_runtime_parameters.
 | 
			
		||||
 | 
			
		||||
    def get_cpuidle(self):
 | 
			
		||||
        return self.get_sysfile_value('/sys/devices/system/cpu/cpu0/cpuidle/state1/disable')
 | 
			
		||||
 | 
			
		||||
    def enable_idle_states(self):
 | 
			
		||||
        """
 | 
			
		||||
        Fully enables idle states on TC2.
 | 
			
		||||
        See http://wiki.arm.com/Research/TC2SetupAndUsage ("Enabling Idle Modes" section)
 | 
			
		||||
        and http://wiki.arm.com/ASD/ControllingPowerManagementInLinaroKernels
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        # Enable C1 (cluster shutdown).
 | 
			
		||||
        self.set_sysfile_value('/sys/devices/system/cpu/cpu0/cpuidle/state1/disable', 0, verify=False)
 | 
			
		||||
        # Enable C0 on A15 cluster.
 | 
			
		||||
        self.set_sysfile_value('/sys/kernel/debug/idle_debug/enable_idle', 0, verify=False)
 | 
			
		||||
        # Enable C0 on A7 cluster.
 | 
			
		||||
        self.set_sysfile_value('/sys/kernel/debug/idle_debug/enable_idle', 1, verify=False)
 | 
			
		||||
 | 
			
		||||
    def disable_idle_states(self):
 | 
			
		||||
        """
 | 
			
		||||
        Disable idle states on TC2.
 | 
			
		||||
        See http://wiki.arm.com/Research/TC2SetupAndUsage ("Enabling Idle Modes" section)
 | 
			
		||||
        and http://wiki.arm.com/ASD/ControllingPowerManagementInLinaroKernels
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        # Disable C1 (cluster shutdown).
 | 
			
		||||
        self.set_sysfile_value('/sys/devices/system/cpu/cpu0/cpuidle/state1/disable', 1, verify=False)
 | 
			
		||||
        # Disable C0.
 | 
			
		||||
        self.set_sysfile_value('/sys/kernel/debug/idle_debug/enable_idle', 0xFF, verify=False)
 | 
			
		||||
 | 
			
		||||
    def set_irq_affinity(self, cluster):
 | 
			
		||||
        """
 | 
			
		||||
        Set's IRQ affinity to the specified cluster.
 | 
			
		||||
 | 
			
		||||
        This method will only work if the device mode is mp_a7_bootcluster or
 | 
			
		||||
        mp_a15_bootcluster. This operation does not make sense if there is only one
 | 
			
		||||
        cluster active (all IRQs will obviously go to that), and it will not work for
 | 
			
		||||
        IKS kernel because clusters are not exposed to sysfs.
 | 
			
		||||
 | 
			
		||||
        :param cluster: must be either 'a15' or 'a7'.
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        if self.config.mode not in ('mp_a7_bootcluster', 'mp_a15_bootcluster'):
 | 
			
		||||
            raise ConfigError('Cannot set IRQ affinity with mode {}'.format(self.config.mode))
 | 
			
		||||
        if cluster == 'a7':
 | 
			
		||||
            self.execute('/sbin/set_irq_affinity.sh 0xc07', check_exit_code=False)
 | 
			
		||||
        elif cluster == 'a15':
 | 
			
		||||
            self.execute('/sbin/set_irq_affinity.sh 0xc0f', check_exit_code=False)
 | 
			
		||||
        else:
 | 
			
		||||
            raise ConfigError('cluster must either "a15" or "a7"; got {}'.format(cluster))
 | 
			
		||||
 | 
			
		||||
    def _boot_using_uefi(self, target):
 | 
			
		||||
        self.logger.debug('Booting using UEFI.')
 | 
			
		||||
        self._wait_for_vemsd_mount(target)
 | 
			
		||||
        self._setup_before_reboot()
 | 
			
		||||
        self._perform_uefi_reboot(target)
 | 
			
		||||
 | 
			
		||||
        # Get to the UEFI menu.
 | 
			
		||||
        self.logger.debug('Waiting for UEFI default selection.')
 | 
			
		||||
        target.sendline('reboot')
 | 
			
		||||
        target.expect('The default boot selection will start in'.rstrip())
 | 
			
		||||
        time.sleep(1)
 | 
			
		||||
        target.sendline(''.rstrip())
 | 
			
		||||
 | 
			
		||||
        # If delete every time is specified, try to delete entry.
 | 
			
		||||
        if self.config.always_delete_uefi_entry:
 | 
			
		||||
            self._delete_uefi_entry(target, entry='workload_automation_MP')
 | 
			
		||||
            self.config.always_delete_uefi_entry = False
 | 
			
		||||
 | 
			
		||||
        # Specify argument to be passed specifying that psci is (or is not) enabled
 | 
			
		||||
        if self.config.psci_enable:
 | 
			
		||||
            psci_enable = ' psci=enable'
 | 
			
		||||
        else:
 | 
			
		||||
            psci_enable = ''
 | 
			
		||||
 | 
			
		||||
        # Identify the workload automation entry.
 | 
			
		||||
        selection_pattern = r'\[([0-9]*)\] '
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            target.expect(re.compile(selection_pattern + 'workload_automation_MP'), timeout=5)
 | 
			
		||||
            wl_menu_item = target.match.group(1)
 | 
			
		||||
        except pexpect.TIMEOUT:
 | 
			
		||||
            self._create_uefi_entry(target, psci_enable, entry_name='workload_automation_MP')
 | 
			
		||||
            # At this point the board should be rebooted so we need to retry to boot
 | 
			
		||||
            self._boot_using_uefi(target)
 | 
			
		||||
        else:  # Did not time out.
 | 
			
		||||
            try:
 | 
			
		||||
                #Identify the boot manager menu item
 | 
			
		||||
                target.expect(re.compile(selection_pattern + 'Boot Manager'))
 | 
			
		||||
                boot_manager_menu_item = target.match.group(1)
 | 
			
		||||
 | 
			
		||||
                #Update FDT
 | 
			
		||||
                target.sendline(boot_manager_menu_item)
 | 
			
		||||
                target.expect(re.compile(selection_pattern + 'Update FDT path'), timeout=15)
 | 
			
		||||
                update_fdt_menu_item = target.match.group(1)
 | 
			
		||||
                target.sendline(update_fdt_menu_item)
 | 
			
		||||
                target.expect(re.compile(selection_pattern + 'NOR Flash .*'), timeout=15)
 | 
			
		||||
                bootmonfs_menu_item = target.match.group(1)
 | 
			
		||||
                target.sendline(bootmonfs_menu_item)
 | 
			
		||||
                target.expect('File path of the FDT blob:')
 | 
			
		||||
                target.sendline(self.config.dtb)
 | 
			
		||||
 | 
			
		||||
                #Return to main manu and boot from wl automation
 | 
			
		||||
                target.expect(re.compile(selection_pattern + 'Return to main menu'), timeout=15)
 | 
			
		||||
                return_to_main_menu_item = target.match.group(1)
 | 
			
		||||
                target.sendline(return_to_main_menu_item)
 | 
			
		||||
                target.sendline(wl_menu_item)
 | 
			
		||||
            except pexpect.TIMEOUT:
 | 
			
		||||
                raise DeviceError('Timed out')
 | 
			
		||||
 | 
			
		||||
    def _setup_before_reboot(self):
 | 
			
		||||
        if not self.config.disable_boot_configuration:
 | 
			
		||||
            self.logger.debug('Performing pre-boot setup.')
 | 
			
		||||
            substitution = {
 | 
			
		||||
                'SCC_0x010': self.config.SCC_0x010,
 | 
			
		||||
                'SCC_0x700': self.config.SCC_0x700,
 | 
			
		||||
            }
 | 
			
		||||
            with open(self.config.src_board_template_file, 'r') as fh:
 | 
			
		||||
                template_board_txt = string.Template(fh.read())
 | 
			
		||||
                with open(self.config.src_board_file, 'w') as wfh:
 | 
			
		||||
                    wfh.write(template_board_txt.substitute(substitution))
 | 
			
		||||
 | 
			
		||||
            with open(self.config.src_images_template_file, 'r') as fh:
 | 
			
		||||
                template_images_txt = string.Template(fh.read())
 | 
			
		||||
                with open(self.config.src_images_file, 'w') as wfh:
 | 
			
		||||
                    wfh.write(template_images_txt.substitute({'bm_image': self.config.bm_image}))
 | 
			
		||||
 | 
			
		||||
            shutil.copyfile(self.config.src_board_file,
 | 
			
		||||
                            os.path.join(self.config.board_dir, self.config.board_file))
 | 
			
		||||
            shutil.copyfile(self.config.src_images_file,
 | 
			
		||||
                            os.path.join(self.config.board_dir, self.config.images_file))
 | 
			
		||||
            os.system('sync')  # make sure everything is flushed to microSD
 | 
			
		||||
        else:
 | 
			
		||||
            self.logger.debug('Boot configuration disabled proceeding with existing board.txt and images.txt.')
 | 
			
		||||
 | 
			
		||||
    def _delete_uefi_entry(self, target, entry):  # pylint: disable=R0201
 | 
			
		||||
        """
 | 
			
		||||
        this method deletes the entry specified as parameter
 | 
			
		||||
        as a precondition serial port input needs to be parsed AT MOST up to
 | 
			
		||||
        the point BEFORE recognizing this entry (both entry and boot manager has
 | 
			
		||||
        not yet been parsed)
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        try:
 | 
			
		||||
            selection_pattern = r'\[([0-9]+)\] *'
 | 
			
		||||
 | 
			
		||||
            try:
 | 
			
		||||
                target.expect(re.compile(selection_pattern + entry), timeout=5)
 | 
			
		||||
                wl_menu_item = target.match.group(1)
 | 
			
		||||
            except pexpect.TIMEOUT:
 | 
			
		||||
                return  # Entry does not exist, nothing to delete here...
 | 
			
		||||
 | 
			
		||||
            # Identify and select boot manager menu item
 | 
			
		||||
            target.expect(selection_pattern + 'Boot Manager', timeout=15)
 | 
			
		||||
            bootmanager_item = target.match.group(1)
 | 
			
		||||
            target.sendline(bootmanager_item)
 | 
			
		||||
 | 
			
		||||
            # Identify and select 'Remove entry'
 | 
			
		||||
            target.expect(selection_pattern + 'Remove Boot Device Entry', timeout=15)
 | 
			
		||||
            new_entry_item = target.match.group(1)
 | 
			
		||||
            target.sendline(new_entry_item)
 | 
			
		||||
 | 
			
		||||
            # Delete entry
 | 
			
		||||
            target.expect(re.compile(selection_pattern + entry), timeout=5)
 | 
			
		||||
            wl_menu_item = target.match.group(1)
 | 
			
		||||
            target.sendline(wl_menu_item)
 | 
			
		||||
 | 
			
		||||
            # Return to main manu
 | 
			
		||||
            target.expect(re.compile(selection_pattern + 'Return to main menu'), timeout=15)
 | 
			
		||||
            return_to_main_menu_item = target.match.group(1)
 | 
			
		||||
            target.sendline(return_to_main_menu_item)
 | 
			
		||||
        except pexpect.TIMEOUT:
 | 
			
		||||
            raise DeviceError('Timed out while deleting UEFI entry.')
 | 
			
		||||
 | 
			
		||||
    def _create_uefi_entry(self, target, psci_enable, entry_name):
 | 
			
		||||
        """
 | 
			
		||||
        Creates the default boot entry that is expected when booting in uefi mode.
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        self._wait_for_vemsd_mount(target)
 | 
			
		||||
        try:
 | 
			
		||||
            selection_pattern = '\[([0-9]+)\] *'
 | 
			
		||||
 | 
			
		||||
            # Identify and select boot manager menu item.
 | 
			
		||||
            target.expect(selection_pattern + 'Boot Manager', timeout=15)
 | 
			
		||||
            bootmanager_item = target.match.group(1)
 | 
			
		||||
            target.sendline(bootmanager_item)
 | 
			
		||||
 | 
			
		||||
            # Identify and select 'add new entry'.
 | 
			
		||||
            target.expect(selection_pattern + 'Add Boot Device Entry', timeout=15)
 | 
			
		||||
            new_entry_item = target.match.group(1)
 | 
			
		||||
            target.sendline(new_entry_item)
 | 
			
		||||
 | 
			
		||||
            # Identify and select BootMonFs.
 | 
			
		||||
            target.expect(selection_pattern + 'NOR Flash .*', timeout=15)
 | 
			
		||||
            BootMonFs_item = target.match.group(1)
 | 
			
		||||
            target.sendline(BootMonFs_item)
 | 
			
		||||
 | 
			
		||||
            # Specify the parameters of the new entry.
 | 
			
		||||
            target.expect('.+the kernel', timeout=5)
 | 
			
		||||
            target.sendline(self.config.kernel)  # kernel path
 | 
			
		||||
            target.expect('Has FDT support\?.*\[y\/n\].*', timeout=5)
 | 
			
		||||
            time.sleep(0.5)
 | 
			
		||||
            target.sendline('y')   # Has Fdt support? -> y
 | 
			
		||||
            target.expect('Add an initrd.*\[y\/n\].*', timeout=5)
 | 
			
		||||
            time.sleep(0.5)
 | 
			
		||||
            target.sendline('y')   # add an initrd? -> y
 | 
			
		||||
            target.expect('.+the initrd.*', timeout=5)
 | 
			
		||||
            time.sleep(0.5)
 | 
			
		||||
            target.sendline(self.config.initrd)  # initrd path
 | 
			
		||||
            target.expect('.+to the binary.*', timeout=5)
 | 
			
		||||
            time.sleep(0.5)
 | 
			
		||||
            _slow_sendline(target, self.config.kernel_arguments + psci_enable)  # arguments to pass to binary
 | 
			
		||||
            time.sleep(0.5)
 | 
			
		||||
            target.expect('.+new Entry.+', timeout=5)
 | 
			
		||||
            _slow_sendline(target, entry_name)  # Entry name
 | 
			
		||||
            target.expect('Choice.+', timeout=15)
 | 
			
		||||
            time.sleep(2)
 | 
			
		||||
        except pexpect.TIMEOUT:
 | 
			
		||||
            raise DeviceError('Timed out while creating UEFI entry.')
 | 
			
		||||
        self._perform_uefi_reboot(target)
 | 
			
		||||
 | 
			
		||||
    def _perform_uefi_reboot(self, target):
 | 
			
		||||
        self._wait_for_vemsd_mount(target)
 | 
			
		||||
        open(os.path.join(self.config.root_mount, 'reboot.txt'), 'a').close()
 | 
			
		||||
 | 
			
		||||
    def _wait_for_vemsd_mount(self, target, timeout=100):
 | 
			
		||||
        attempts = 1 + self.config.reboot_attempts
 | 
			
		||||
        if os.path.exists(os.path.join(self.config.root_mount, 'config.txt')):
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        self.logger.debug('Waiting for VEMSD to mount...')
 | 
			
		||||
        for i in xrange(attempts):
 | 
			
		||||
            if i:  # Do not reboot on the first attempt.
 | 
			
		||||
                target.sendline('reboot')
 | 
			
		||||
            target.sendline('usb_on')
 | 
			
		||||
            for _ in xrange(timeout):
 | 
			
		||||
                time.sleep(1)
 | 
			
		||||
                if os.path.exists(os.path.join(self.config.root_mount, 'config.txt')):
 | 
			
		||||
                    return
 | 
			
		||||
 | 
			
		||||
        raise DeviceError('Timed out waiting for VEMSD to mount.')
 | 
			
		||||
 | 
			
		||||
    def _boot_using_bootmon(self, target):
 | 
			
		||||
        """
 | 
			
		||||
        This method Boots TC2 using the bootmon interface.
 | 
			
		||||
        """
 | 
			
		||||
        self.logger.debug('Booting using bootmon.')
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            self._wait_for_vemsd_mount(target, timeout=20)
 | 
			
		||||
        except DeviceError:
 | 
			
		||||
            # OK, something's wrong. Reboot the board and try again.
 | 
			
		||||
            self.logger.debug('VEMSD not mounted, attempting to power cycle device.')
 | 
			
		||||
            target.sendline(' ')
 | 
			
		||||
            state = target.expect(['Cmd> ', self.config.bootmon_prompt, self.android_prompt])  # pylint: disable=E1101
 | 
			
		||||
 | 
			
		||||
            if state == 0 or state == 1:
 | 
			
		||||
                # Reboot - Bootmon
 | 
			
		||||
                target.sendline('reboot')
 | 
			
		||||
                target.expect('Powering up system...')
 | 
			
		||||
            elif state == 2:
 | 
			
		||||
                target.sendline('reboot -n')
 | 
			
		||||
                target.expect('Powering up system...')
 | 
			
		||||
            else:
 | 
			
		||||
                raise DeviceError('Unexpected board state {}; should be 0, 1 or 2'.format(state))
 | 
			
		||||
 | 
			
		||||
            self._wait_for_vemsd_mount(target)
 | 
			
		||||
 | 
			
		||||
        self._setup_before_reboot()
 | 
			
		||||
 | 
			
		||||
        # Reboot - Bootmon
 | 
			
		||||
        self.logger.debug('Rebooting into bootloader...')
 | 
			
		||||
        open(os.path.join(self.config.root_mount, 'reboot.txt'), 'a').close()
 | 
			
		||||
        target.expect('Powering up system...')
 | 
			
		||||
        target.expect(self.config.bootmon_prompt)
 | 
			
		||||
 | 
			
		||||
        # Wait for VEMSD to mount
 | 
			
		||||
        self._wait_for_vemsd_mount(target)
 | 
			
		||||
 | 
			
		||||
        #Boot Linux - Bootmon
 | 
			
		||||
        target.sendline('fl linux fdt ' + self.config.dtb)
 | 
			
		||||
        target.expect(self.config.bootmon_prompt)
 | 
			
		||||
        target.sendline('fl linux initrd ' + self.config.initrd)
 | 
			
		||||
        target.expect(self.config.bootmon_prompt)
 | 
			
		||||
        #Workaround TC2 bootmon serial issue for loading large initrd blob
 | 
			
		||||
        target.sendline(' ')
 | 
			
		||||
        target.expect(self.config.bootmon_prompt)
 | 
			
		||||
        target.sendline('fl linux boot ' + self.config.kernel + self.config.kernel_arguments)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Utility functions.
 | 
			
		||||
 | 
			
		||||
def _slow_sendline(target, line):
 | 
			
		||||
    for c in line:
 | 
			
		||||
        target.send(c)
 | 
			
		||||
        time.sleep(0.1)
 | 
			
		||||
    target.sendline('')
 | 
			
		||||
 | 
			
		||||
@@ -1,96 +0,0 @@
 | 
			
		||||
BOARD: HBI0249
 | 
			
		||||
TITLE: V2P-CA15_A7 Configuration File
 | 
			
		||||
 | 
			
		||||
[DCCS]
 | 
			
		||||
TOTALDCCS: 1                    ;Total Number of DCCS
 | 
			
		||||
M0FILE: dbb_v110.ebf            ;DCC0 Filename
 | 
			
		||||
M0MODE: MICRO                   ;DCC0 Programming Mode
 | 
			
		||||
 | 
			
		||||
[FPGAS]
 | 
			
		||||
TOTALFPGAS: 0                   ;Total Number of FPGAs
 | 
			
		||||
 | 
			
		||||
[TAPS]
 | 
			
		||||
TOTALTAPS: 3                    ;Total Number of TAPs
 | 
			
		||||
T0NAME: STM32TMC                ;TAP0 Device Name
 | 
			
		||||
T0FILE: NONE                    ;TAP0 Filename
 | 
			
		||||
T0MODE: NONE                    ;TAP0 Programming Mode
 | 
			
		||||
T1NAME: STM32CM3                ;TAP1 Device Name
 | 
			
		||||
T1FILE: NONE                    ;TAP1 Filename
 | 
			
		||||
T1MODE: NONE                    ;TAP1 Programming Mode
 | 
			
		||||
T2NAME: CORTEXA15               ;TAP2 Device Name
 | 
			
		||||
T2FILE: NONE      		;TAP2 Filename
 | 
			
		||||
T2MODE: NONE                    ;TAP2 Programming Mode
 | 
			
		||||
 | 
			
		||||
[OSCCLKS]
 | 
			
		||||
TOTALOSCCLKS: 9                 ;Total Number of OSCCLKS
 | 
			
		||||
OSC0: 50.0                      ;CPUREFCLK0 A15 CPU (20:1 - 1.0GHz)
 | 
			
		||||
OSC1: 50.0                      ;CPUREFCLK1 A15 CPU (20:1 - 1.0GHz)
 | 
			
		||||
OSC2: 40.0                      ;CPUREFCLK0 A7  CPU (20:1 - 800MHz)
 | 
			
		||||
OSC3: 40.0                      ;CPUREFCLK1 A7  CPU (20:1 - 800MHz)
 | 
			
		||||
OSC4: 40.0                      ;HSBM AXI (40MHz)
 | 
			
		||||
OSC5: 23.75                     ;HDLCD (23.75MHz - TC PLL is in bypass)
 | 
			
		||||
OSC6: 50.0                      ;SMB (50MHz)
 | 
			
		||||
OSC7: 50.0                      ;SYSREFCLK (20:1 - 1.0GHz, ACLK - 500MHz)
 | 
			
		||||
OSC8: 50.0                      ;DDR2 (8:1 - 400MHz)
 | 
			
		||||
 | 
			
		||||
[SCC REGISTERS]
 | 
			
		||||
TOTALSCCS: 33                   ;Total Number of SCC registers
 | 
			
		||||
 | 
			
		||||
;SCC: 0x010 0x000003D0          ;Remap to NOR0
 | 
			
		||||
SCC: 0x010 $SCC_0x010           ;Switch between NOR0/NOR1
 | 
			
		||||
SCC: 0x01C 0xFF00FF00           ;CFGRW3  - SMC CS6/7 N/U
 | 
			
		||||
SCC: 0x118 0x01CD1011           ;CFGRW17 - HDLCD PLL external bypass
 | 
			
		||||
;SCC: 0x700 0x00320003           ;CFGRW48 - [25:24]Boot CPU [28]Boot Cluster (default CA7_0)
 | 
			
		||||
SCC: 0x700 $SCC_0x700          	;CFGRW48 - [25:24]Boot CPU [28]Boot Cluster (default CA7_0)
 | 
			
		||||
                                ;          Bootmon configuration:
 | 
			
		||||
                                ;          [15]: A7 Event stream generation (default: disabled)
 | 
			
		||||
                                ;          [14]: A15 Event stream generation (default: disabled)
 | 
			
		||||
                                ;          [13]: Power down the non-boot cluster (default: disabled)
 | 
			
		||||
                                ;          [12]: Use per-cpu mailboxes for power management (default: disabled)
 | 
			
		||||
                                ;          [11]: A15 executes WFEs as nops (default: disabled)
 | 
			
		||||
 | 
			
		||||
SCC: 0x400 0x33330c00           ;CFGREG41 - A15 configuration register 0 (Default 0x33330c80)
 | 
			
		||||
                                ;       [29:28] SPNIDEN
 | 
			
		||||
                                ;       [25:24] SPIDEN
 | 
			
		||||
                                ;       [21:20] NIDEN
 | 
			
		||||
                                ;       [17:16] DBGEN
 | 
			
		||||
                                ;       [13:12] CFGTE
 | 
			
		||||
                                ;       [9:8] VINITHI_CORE
 | 
			
		||||
                                ;       [7] IMINLN
 | 
			
		||||
                                ;       [3:0] CLUSTER_ID
 | 
			
		||||
 | 
			
		||||
                                ;Set the CPU clock PLLs
 | 
			
		||||
SCC: 0x120 0x022F1010           ;CFGRW19 - CA15_0 PLL control - 20:1 (lock OFF)
 | 
			
		||||
SCC: 0x124 0x0011710D           ;CFGRW20 - CA15_0 PLL value
 | 
			
		||||
SCC: 0x128 0x022F1010           ;CFGRW21 - CA15_1 PLL control - 20:1 (lock OFF)
 | 
			
		||||
SCC: 0x12C 0x0011710D           ;CFGRW22 - CA15_1 PLL value
 | 
			
		||||
SCC: 0x130 0x022F1010           ;CFGRW23 - CA7_0  PLL control - 20:1 (lock OFF)
 | 
			
		||||
SCC: 0x134 0x0011710D           ;CFGRW24 - CA7_0  PLL value
 | 
			
		||||
SCC: 0x138 0x022F1010           ;CFGRW25 - CA7_1  PLL control - 20:1 (lock OFF)
 | 
			
		||||
SCC: 0x13C 0x0011710D           ;CFGRW26 - CA7_1  PLL value
 | 
			
		||||
 | 
			
		||||
                                ;Power management interface
 | 
			
		||||
SCC: 0xC00 0x00000005           ;Control: [0]PMI_EN [1]DBG_EN [2]SPC_SYSCFG
 | 
			
		||||
SCC: 0xC04 0x060E0356           ;Latency in uS max: [15:0]DVFS [31:16]PWRUP
 | 
			
		||||
SCC: 0xC08 0x00000000           ;Reserved
 | 
			
		||||
SCC: 0xC0C 0x00000000           ;Reserved
 | 
			
		||||
 | 
			
		||||
                                ;CA15 performance values: 0xVVVFFFFF
 | 
			
		||||
SCC: 0xC10 0x384061A8           ;CA15 PERFVAL0,  900mV, 20,000*20= 500MHz
 | 
			
		||||
SCC: 0xC14 0x38407530           ;CA15 PERFVAL1,  900mV, 25,000*20= 600MHz
 | 
			
		||||
SCC: 0xC18 0x384088B8           ;CA15 PERFVAL2,  900mV, 30,000*20= 700MHz
 | 
			
		||||
SCC: 0xC1C 0x38409C40           ;CA15 PERFVAL3,  900mV, 35,000*20= 800MHz
 | 
			
		||||
SCC: 0xC20 0x3840AFC8           ;CA15 PERFVAL4,  900mV, 40,000*20= 900MHz
 | 
			
		||||
SCC: 0xC24 0x3840C350           ;CA15 PERFVAL5,  900mV, 45,000*20=1000MHz
 | 
			
		||||
SCC: 0xC28 0x3CF0D6D8           ;CA15 PERFVAL6,  975mV, 50,000*20=1100MHz
 | 
			
		||||
SCC: 0xC2C 0x41A0EA60           ;CA15 PERFVAL7, 1050mV, 55,000*20=1200MHz
 | 
			
		||||
 | 
			
		||||
                                ;CA7 performance values: 0xVVVFFFFF
 | 
			
		||||
SCC: 0xC30 0x3840445C           ;CA7 PERFVAL0,  900mV, 10,000*20= 350MHz
 | 
			
		||||
SCC: 0xC34 0x38404E20           ;CA7 PERFVAL1,  900mV, 15,000*20= 400MHz
 | 
			
		||||
SCC: 0xC38 0x384061A8           ;CA7 PERFVAL2,  900mV, 20,000*20= 500MHz
 | 
			
		||||
SCC: 0xC3C 0x38407530           ;CA7 PERFVAL3,  900mV, 25,000*20= 600MHz
 | 
			
		||||
SCC: 0xC40 0x384088B8           ;CA7 PERFVAL4,  900mV, 30,000*20= 700MHz
 | 
			
		||||
SCC: 0xC44 0x38409C40           ;CA7 PERFVAL5,  900mV, 35,000*20= 800MHz
 | 
			
		||||
SCC: 0xC48 0x3CF0AFC8           ;CA7 PERFVAL6,  975mV, 40,000*20= 900MHz
 | 
			
		||||
SCC: 0xC4C 0x41A0C350           ;CA7 PERFVAL7, 1050mV, 45,000*20=1000MHz
 | 
			
		||||
@@ -1,25 +0,0 @@
 | 
			
		||||
TITLE: Versatile Express Images Configuration File
 | 
			
		||||
 | 
			
		||||
[IMAGES]
 | 
			
		||||
TOTALIMAGES: 4                   ;Number of Images (Max : 32)
 | 
			
		||||
NOR0UPDATE: AUTO                 ;Image Update:NONE/AUTO/FORCE
 | 
			
		||||
NOR0ADDRESS: BOOT                ;Image Flash Address
 | 
			
		||||
NOR0FILE: \SOFTWARE\$bm_image    ;Image File Name
 | 
			
		||||
 | 
			
		||||
NOR1UPDATE: AUTO                 ;IMAGE UPDATE:NONE/AUTO/FORCE
 | 
			
		||||
NOR1ADDRESS: 0x00000000          ;Image Flash Address
 | 
			
		||||
NOR1FILE: \SOFTWARE\kern_iks.bin  ;Image File Name
 | 
			
		||||
NOR1LOAD: 0x80008000
 | 
			
		||||
NOR1ENTRY: 0x80008000
 | 
			
		||||
 | 
			
		||||
NOR2UPDATE: AUTO                 ;IMAGE UPDATE:NONE/AUTO/FORCE
 | 
			
		||||
NOR2ADDRESS: 0x00000000          ;Image Flash Address
 | 
			
		||||
NOR2FILE: \SOFTWARE\iks.dtb 	 ;Image File Name for booting in A7 cluster
 | 
			
		||||
NOR2LOAD: 0x84000000
 | 
			
		||||
NOR2ENTRY: 0x84000000
 | 
			
		||||
 | 
			
		||||
NOR3UPDATE: AUTO                 ;IMAGE UPDATE:NONE/AUTO/FORCE
 | 
			
		||||
NOR3ADDRESS: 0x00000000          ;Image Flash Address
 | 
			
		||||
NOR3FILE: \SOFTWARE\init_iks.bin ;Image File Name
 | 
			
		||||
NOR3LOAD: 0x90100000
 | 
			
		||||
NOR3ENTRY: 0x90100000
 | 
			
		||||
@@ -1,55 +0,0 @@
 | 
			
		||||
TITLE: Versatile Express Images Configuration File
 | 
			
		||||
[IMAGES]
 | 
			
		||||
TOTALIMAGES: 9                  ;Number of Images (Max: 32)
 | 
			
		||||
NOR0UPDATE: AUTO                 ;Image Update:NONE/AUTO/FORCE
 | 
			
		||||
NOR0ADDRESS: BOOT                ;Image Flash Address
 | 
			
		||||
NOR0FILE: \SOFTWARE\$bm_image    ;Image File Name
 | 
			
		||||
 | 
			
		||||
NOR1UPDATE: AUTO                 ;IMAGE UPDATE:NONE/AUTO/FORCE
 | 
			
		||||
NOR1ADDRESS: 0x0E000000          ;Image Flash Address
 | 
			
		||||
NOR1FILE: \SOFTWARE\kern_mp.bin  ;Image File Name
 | 
			
		||||
NOR1LOAD: 0x80008000
 | 
			
		||||
NOR1ENTRY: 0x80008000
 | 
			
		||||
 | 
			
		||||
NOR2UPDATE: AUTO                 ;IMAGE UPDATE:NONE/AUTO/FORCE
 | 
			
		||||
NOR2ADDRESS: 0x0E800000          ;Image Flash Address
 | 
			
		||||
NOR2FILE: \SOFTWARE\mp_a7.dtb ;Image File Name for booting in A7 cluster
 | 
			
		||||
NOR2LOAD: 0x84000000
 | 
			
		||||
NOR2ENTRY: 0x84000000
 | 
			
		||||
 | 
			
		||||
NOR3UPDATE: AUTO                 ;IMAGE UPDATE:NONE/AUTO/FORCE
 | 
			
		||||
NOR3ADDRESS: 0x0E900000          ;Image Flash Address
 | 
			
		||||
NOR3FILE: \SOFTWARE\mp_a15.dtb    ;Image File Name
 | 
			
		||||
NOR3LOAD: 0x84000000
 | 
			
		||||
NOR3ENTRY: 0x84000000
 | 
			
		||||
 | 
			
		||||
NOR4UPDATE: AUTO                 ;IMAGE UPDATE:NONE/AUTO/FORCE
 | 
			
		||||
NOR4ADDRESS: 0x0EA00000          ;Image Flash Address
 | 
			
		||||
NOR4FILE: \SOFTWARE\mp_a7bc.dtb   ;Image File Name
 | 
			
		||||
NOR4LOAD: 0x84000000
 | 
			
		||||
NOR4ENTRY: 0x84000000
 | 
			
		||||
 | 
			
		||||
NOR5UPDATE: AUTO                    ;IMAGE UPDATE:NONE/AUTO/FORCE
 | 
			
		||||
NOR5ADDRESS: 0x0EB00000             ;Image Flash Address
 | 
			
		||||
NOR5FILE: \SOFTWARE\mp_a15bc.dtb    ;Image File Name
 | 
			
		||||
NOR5LOAD: 0x84000000
 | 
			
		||||
NOR5ENTRY: 0x84000000
 | 
			
		||||
 | 
			
		||||
NOR6UPDATE: AUTO                 ;IMAGE UPDATE:NONE/AUTO/FORCE
 | 
			
		||||
NOR6ADDRESS: 0x0EC00000          ;Image Flash Address
 | 
			
		||||
NOR6FILE: \SOFTWARE\init_mp.bin  ;Image File Name
 | 
			
		||||
NOR6LOAD: 0x85000000
 | 
			
		||||
NOR6ENTRY: 0x85000000
 | 
			
		||||
 | 
			
		||||
NOR7UPDATE: AUTO                 ;IMAGE UPDATE:NONE/AUTO/FORCE
 | 
			
		||||
NOR7ADDRESS: 0x0C000000          ;Image Flash Address
 | 
			
		||||
NOR7FILE: \SOFTWARE\tc2_sec.bin   ;Image File Name
 | 
			
		||||
NOR7LOAD: 0
 | 
			
		||||
NOR7ENTRY: 0
 | 
			
		||||
 | 
			
		||||
NOR8UPDATE: AUTO                 ;IMAGE UPDATE:NONE/AUTO/FORCE
 | 
			
		||||
NOR8ADDRESS: 0x0D000000          ;Image Flash Address
 | 
			
		||||
NOR8FILE: \SOFTWARE\tc2_uefi.bin   ;Image File Name
 | 
			
		||||
NOR8LOAD: 0
 | 
			
		||||
NOR8ENTRY: 0
 | 
			
		||||
 | 
			
		||||
@@ -1,36 +0,0 @@
 | 
			
		||||
#    Copyright 2014-2015 ARM Limited
 | 
			
		||||
#
 | 
			
		||||
# Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
# you may not use this file except in compliance with the License.
 | 
			
		||||
# You may obtain a copy of the License at
 | 
			
		||||
#
 | 
			
		||||
#     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
#
 | 
			
		||||
# Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
# distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
# See the License for the specific language governing permissions and
 | 
			
		||||
# limitations under the License.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
from wlauto import LinuxDevice, Parameter
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Xe503c12Chormebook(LinuxDevice):
 | 
			
		||||
 | 
			
		||||
    name = "XE503C12"
 | 
			
		||||
    description = 'A developer-unlocked Samsung XE503C12 running sshd.'
 | 
			
		||||
    platform = 'chromeos'
 | 
			
		||||
 | 
			
		||||
    parameters = [
 | 
			
		||||
        Parameter('core_names', default=['a15', 'a15', 'a15', 'a15'], override=True),
 | 
			
		||||
        Parameter('core_clusters', default=[0, 0, 0, 0], override=True),
 | 
			
		||||
        Parameter('username', default='chronos', override=True),
 | 
			
		||||
        Parameter('password', default='', override=True),
 | 
			
		||||
        Parameter('password_prompt', default='Password:', override=True),
 | 
			
		||||
        Parameter('binaries_directory', default='/home/chronos/bin', override=True),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    abi = 'armeabi'
 | 
			
		||||
 | 
			
		||||
@@ -1,16 +0,0 @@
 | 
			
		||||
#    Copyright 2014-2015 ARM Limited
 | 
			
		||||
#
 | 
			
		||||
# Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
# you may not use this file except in compliance with the License.
 | 
			
		||||
# You may obtain a copy of the License at
 | 
			
		||||
#
 | 
			
		||||
#     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
#
 | 
			
		||||
# Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
# distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
# See the License for the specific language governing permissions and
 | 
			
		||||
# limitations under the License.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -1,100 +0,0 @@
 | 
			
		||||
#    Copyright 2014-2015 ARM Limited
 | 
			
		||||
#
 | 
			
		||||
# Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
# you may not use this file except in compliance with the License.
 | 
			
		||||
# You may obtain a copy of the License at
 | 
			
		||||
#
 | 
			
		||||
#     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
#
 | 
			
		||||
# Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
# distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
# See the License for the specific language governing permissions and
 | 
			
		||||
# limitations under the License.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
import re
 | 
			
		||||
 | 
			
		||||
from wlauto import LinuxDevice, Parameter
 | 
			
		||||
from wlauto.exceptions import DeviceError
 | 
			
		||||
from wlauto.core.device import RuntimeParameter
 | 
			
		||||
from wlauto.utils.misc import convert_new_lines
 | 
			
		||||
from wlauto.utils.types import boolean
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ChromeOsDevice(LinuxDevice):
 | 
			
		||||
 | 
			
		||||
    name = "chromeos_test_image"
 | 
			
		||||
    description = """
 | 
			
		||||
    Chrome OS test image device. Use this if you are working on a Chrome OS device with a test
 | 
			
		||||
    image. An off the shelf device will not work with this device interface.
 | 
			
		||||
 | 
			
		||||
    More information on how to build a Chrome OS test image can be found here:
 | 
			
		||||
 | 
			
		||||
        https://www.chromium.org/chromium-os/developer-guide#TOC-Build-a-disk-image-for-your-board
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    platform = 'chromeos'
 | 
			
		||||
    abi = 'armeabi'
 | 
			
		||||
    has_gpu = True
 | 
			
		||||
    default_timeout = 100
 | 
			
		||||
 | 
			
		||||
    parameters = [
 | 
			
		||||
        Parameter('core_names', default=[], override=True),
 | 
			
		||||
        Parameter('core_clusters', default=[], override=True),
 | 
			
		||||
        Parameter('username', default='root', override=True),
 | 
			
		||||
        Parameter('password_prompt', default='Password:', override=True),
 | 
			
		||||
        Parameter('binaries_directory', default='/usr/local/bin', override=True),
 | 
			
		||||
        Parameter('working_directory', default='/home/root/wa-working', override=True),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    runtime_parameters = [
 | 
			
		||||
        RuntimeParameter('ui', 'get_ui_status', 'set_ui_status', value_name='status'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    def __init__(self, **kwargs):
 | 
			
		||||
        super(ChromeOsDevice, self).__init__(**kwargs)
 | 
			
		||||
        self.ui_status = None
 | 
			
		||||
 | 
			
		||||
    def validate(self):
 | 
			
		||||
        # pylint: disable=access-member-before-definition,attribute-defined-outside-init
 | 
			
		||||
        if self.password is None and not self.keyfile:
 | 
			
		||||
            self.password = 'test0000'
 | 
			
		||||
 | 
			
		||||
    def initialize(self, context, *args, **kwargs):
 | 
			
		||||
        if self.busybox == 'busybox':
 | 
			
		||||
            self.logger.debug('Busybox already installed on the device: replacing with wa version')
 | 
			
		||||
            self.uninstall('busybox')
 | 
			
		||||
            self.busybox = self.deploy_busybox(context)
 | 
			
		||||
 | 
			
		||||
    def get_ui_status(self):
 | 
			
		||||
        return self.ui_status
 | 
			
		||||
 | 
			
		||||
    def set_ui_status(self, status):
 | 
			
		||||
        self.ui_status = boolean(status)
 | 
			
		||||
        if self.ui_status is None:
 | 
			
		||||
            pass
 | 
			
		||||
        elif self.ui_status:
 | 
			
		||||
            try:
 | 
			
		||||
                self.execute('start ui')
 | 
			
		||||
            except DeviceError:
 | 
			
		||||
                pass
 | 
			
		||||
        else:
 | 
			
		||||
            try:
 | 
			
		||||
                self.execute('stop ui')
 | 
			
		||||
            except DeviceError:
 | 
			
		||||
                pass
 | 
			
		||||
 | 
			
		||||
    def stop(self):
 | 
			
		||||
        if self.ui_status is None:
 | 
			
		||||
            pass
 | 
			
		||||
        elif not self.ui_status:
 | 
			
		||||
            try:
 | 
			
		||||
                self.execute('start ui')
 | 
			
		||||
            except DeviceError:
 | 
			
		||||
                pass
 | 
			
		||||
        else:
 | 
			
		||||
            pass
 | 
			
		||||
        self.ui_status = None
 | 
			
		||||
 | 
			
		||||
@@ -1,120 +0,0 @@
 | 
			
		||||
#    Copyright 2014-2015 ARM Limited
 | 
			
		||||
#
 | 
			
		||||
# Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
# you may not use this file except in compliance with the License.
 | 
			
		||||
# You may obtain a copy of the License at
 | 
			
		||||
#
 | 
			
		||||
#     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
#
 | 
			
		||||
# Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
# distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
# See the License for the specific language governing permissions and
 | 
			
		||||
# limitations under the License.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
# Original implementation by Rene de Jong. Updated by Sascha Bischoff.
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
from wlauto import LinuxDevice, Parameter
 | 
			
		||||
from wlauto.common.gem5.device import BaseGem5Device
 | 
			
		||||
from wlauto.utils import types
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Gem5LinuxDevice(BaseGem5Device, LinuxDevice):
 | 
			
		||||
    """
 | 
			
		||||
    Implements gem5 Linux device.
 | 
			
		||||
 | 
			
		||||
    This class allows a user to connect WA to a simulation using gem5. The
 | 
			
		||||
    connection to the device is made using the telnet connection of the
 | 
			
		||||
    simulator, and is used for all commands. The simulator does not have ADB
 | 
			
		||||
    support, and therefore we need to fall back to using standard shell
 | 
			
		||||
    commands.
 | 
			
		||||
 | 
			
		||||
    Files are copied into the simulation using a VirtIO 9P device in gem5. Files
 | 
			
		||||
    are copied out of the simulated environment using the m5 writefile command
 | 
			
		||||
    within the simulated system.
 | 
			
		||||
 | 
			
		||||
    When starting the workload run, the simulator is automatically started by
 | 
			
		||||
    Workload Automation, and a connection to the simulator is established. WA
 | 
			
		||||
    will then wait for Android to boot on the simulated system (which can take
 | 
			
		||||
    hours), prior to executing any other commands on the device. It is also
 | 
			
		||||
    possible to resume from a checkpoint when starting the simulation. To do
 | 
			
		||||
    this, please append the relevant checkpoint commands from the gem5
 | 
			
		||||
    simulation script to the gem5_discription argument in the agenda.
 | 
			
		||||
 | 
			
		||||
    Host system requirements:
 | 
			
		||||
        * VirtIO support. We rely on diod on the host system. This can be
 | 
			
		||||
          installed on ubuntu using the following command:
 | 
			
		||||
 | 
			
		||||
                sudo apt-get install diod
 | 
			
		||||
 | 
			
		||||
    Guest requirements:
 | 
			
		||||
        * VirtIO support. We rely on VirtIO to move files into the simulation.
 | 
			
		||||
          Please make sure that the following are set in the kernel
 | 
			
		||||
          configuration:
 | 
			
		||||
 | 
			
		||||
                CONFIG_NET_9P=y
 | 
			
		||||
 | 
			
		||||
                CONFIG_NET_9P_VIRTIO=y
 | 
			
		||||
 | 
			
		||||
                CONFIG_9P_FS=y
 | 
			
		||||
 | 
			
		||||
                CONFIG_9P_FS_POSIX_ACL=y
 | 
			
		||||
 | 
			
		||||
                CONFIG_9P_FS_SECURITY=y
 | 
			
		||||
 | 
			
		||||
                CONFIG_VIRTIO_BLK=y
 | 
			
		||||
 | 
			
		||||
        * m5 binary. Please make sure that the m5 binary is on the device and
 | 
			
		||||
          can by found in the path.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    name = 'gem5_linux'
 | 
			
		||||
    platform = 'linux'
 | 
			
		||||
 | 
			
		||||
    parameters = [
 | 
			
		||||
        Parameter('core_names', default=[], override=True),
 | 
			
		||||
        Parameter('core_clusters', default=[], override=True),
 | 
			
		||||
        Parameter('host', default='localhost', override=True,
 | 
			
		||||
                  description='Host name or IP address for the device.'),
 | 
			
		||||
        Parameter('login_prompt', kind=types.list_of_strs,
 | 
			
		||||
                  default=['login:', 'AEL login:', 'username:'],
 | 
			
		||||
                  mandatory=False),
 | 
			
		||||
        Parameter('login_password_prompt', kind=types.list_of_strs,
 | 
			
		||||
                  default=['password:'], mandatory=False),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    # Overwritten from Device. For documentation, see corresponding method in
 | 
			
		||||
    # Device.
 | 
			
		||||
 | 
			
		||||
    def __init__(self, **kwargs):
 | 
			
		||||
        self.logger = logging.getLogger('Gem5LinuxDevice')
 | 
			
		||||
        LinuxDevice.__init__(self, **kwargs)
 | 
			
		||||
        BaseGem5Device.__init__(self)
 | 
			
		||||
 | 
			
		||||
    def login_to_device(self):
 | 
			
		||||
        # Wait for the login prompt
 | 
			
		||||
        prompt = self.login_prompt + [self.sckt.UNIQUE_PROMPT]
 | 
			
		||||
        i = self.sckt.expect(prompt, timeout=10)
 | 
			
		||||
        # Check if we are already at a prompt, or if we need to log in.
 | 
			
		||||
        if i < len(prompt) - 1:
 | 
			
		||||
            self.sckt.sendline("{}".format(self.username))
 | 
			
		||||
            password_prompt = self.login_password_prompt + [r'# ', self.sckt.UNIQUE_PROMPT]
 | 
			
		||||
            j = self.sckt.expect(password_prompt, timeout=self.delay)
 | 
			
		||||
            if j < len(password_prompt) - 2:
 | 
			
		||||
                self.sckt.sendline("{}".format(self.password))
 | 
			
		||||
                self.sckt.expect([r'# ', self.sckt.UNIQUE_PROMPT], timeout=self.delay)
 | 
			
		||||
 | 
			
		||||
    def capture_screen(self, filepath):
 | 
			
		||||
        if BaseGem5Device.capture_screen(self, filepath):
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        # If we didn't manage to do the above, call the parent class.
 | 
			
		||||
        self.logger.warning("capture_screen: falling back to parent class implementation")
 | 
			
		||||
        LinuxDevice.capture_screen(self, filepath)
 | 
			
		||||
 | 
			
		||||
    def initialize(self, context):
 | 
			
		||||
        self.resize_shell()
 | 
			
		||||
        self.deploy_m5(context, force=False)
 | 
			
		||||
@@ -1,37 +0,0 @@
 | 
			
		||||
#    Copyright 2014-2015 ARM Limited
 | 
			
		||||
#
 | 
			
		||||
# Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
# you may not use this file except in compliance with the License.
 | 
			
		||||
# You may obtain a copy of the License at
 | 
			
		||||
#
 | 
			
		||||
#     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
#
 | 
			
		||||
# Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
# distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
# See the License for the specific language governing permissions and
 | 
			
		||||
# limitations under the License.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
from wlauto import LinuxDevice, Parameter
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class GenericDevice(LinuxDevice):
 | 
			
		||||
    name = 'generic_linux'
 | 
			
		||||
    description = """
 | 
			
		||||
    A generic Linux device interface. Use this if you do not have an interface
 | 
			
		||||
    for your device.
 | 
			
		||||
 | 
			
		||||
    This should allow basic WA functionality on most Linux devices with SSH access
 | 
			
		||||
    configured. Some additional configuration may be required for some WA extensions
 | 
			
		||||
    (e.g. configuring ``core_names`` and ``core_clusters``).
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    has_gpu = True
 | 
			
		||||
 | 
			
		||||
    parameters = [
 | 
			
		||||
        Parameter('core_names', default=[], override=True),
 | 
			
		||||
        Parameter('core_clusters', default=[], override=True),
 | 
			
		||||
    ]
 | 
			
		||||
@@ -1,35 +0,0 @@
 | 
			
		||||
#    Copyright 2014-2015 ARM Limited
 | 
			
		||||
#
 | 
			
		||||
# Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
# you may not use this file except in compliance with the License.
 | 
			
		||||
# You may obtain a copy of the License at
 | 
			
		||||
#
 | 
			
		||||
#     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
#
 | 
			
		||||
# Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
# distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
# See the License for the specific language governing permissions and
 | 
			
		||||
# limitations under the License.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
from wlauto import LinuxDevice, Parameter
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class OdroidXU3LinuxDevice(LinuxDevice):
 | 
			
		||||
 | 
			
		||||
    name = "odroidxu3_linux"
 | 
			
		||||
    description = 'HardKernel Odroid XU3 development board (Ubuntu image).'
 | 
			
		||||
 | 
			
		||||
    core_modules = [
 | 
			
		||||
        'odroidxu3-fan',
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    parameters = [
 | 
			
		||||
        Parameter('core_names', default=['a7', 'a7', 'a7', 'a7', 'a15', 'a15', 'a15', 'a15'], override=True),
 | 
			
		||||
        Parameter('core_clusters', default=[0, 0, 0, 0, 1, 1, 1, 1], override=True),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    abi = 'armeabi'
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user