From 1599c1e0ed35b2faa5e364ab7a93fcfb3d6f80fd Mon Sep 17 00:00:00 2001 From: Sebastian Goscik Date: Tue, 16 Feb 2016 11:18:41 +0000 Subject: [PATCH] Devices: Removed Devices They are now superseded by DeviceManagers --- wlauto/__init__.py | 3 - wlauto/common/android/device.py | 765 --------------- wlauto/common/gem5/device.py | 684 -------------- wlauto/common/linux/__init__.py | 16 - wlauto/common/linux/device.py | 875 ------------------ wlauto/core/bootstrap.py | 1 - wlauto/core/device.py | 449 --------- wlauto/devices/__init__.py | 16 - wlauto/devices/android/__init__.py | 16 - wlauto/devices/android/gem5/__init__.py | 222 ----- wlauto/devices/android/generic/__init__.py | 38 - wlauto/devices/android/juno/__init__.py | 223 ----- wlauto/devices/android/nexus10/__init__.py | 48 - wlauto/devices/android/nexus5/__init__.py | 40 - wlauto/devices/android/note3/__init__.py | 76 -- wlauto/devices/android/odroidxu3/__init__.py | 38 - wlauto/devices/android/tc2/__init__.py | 850 ----------------- .../android/tc2/resources/board_template.txt | 96 -- .../android/tc2/resources/images_iks.txt | 25 - .../android/tc2/resources/images_mp.txt | 55 -- wlauto/devices/linux/XE503C12/__init__.py | 36 - wlauto/devices/linux/__init__.py | 16 - .../linux/chromeos_test_image/__init__.py | 100 -- wlauto/devices/linux/gem5/__init__.py | 120 --- wlauto/devices/linux/generic/__init__.py | 37 - .../devices/linux/odroidxu3_linux/__init__.py | 35 - 26 files changed, 4880 deletions(-) delete mode 100644 wlauto/common/android/device.py delete mode 100644 wlauto/common/gem5/device.py delete mode 100644 wlauto/common/linux/__init__.py delete mode 100644 wlauto/common/linux/device.py delete mode 100644 wlauto/core/device.py delete mode 100644 wlauto/devices/__init__.py delete mode 100644 wlauto/devices/android/__init__.py delete mode 100644 wlauto/devices/android/gem5/__init__.py delete mode 100644 wlauto/devices/android/generic/__init__.py delete mode 100644 wlauto/devices/android/juno/__init__.py delete mode 100644 wlauto/devices/android/nexus10/__init__.py delete mode 100644 wlauto/devices/android/nexus5/__init__.py delete mode 100644 wlauto/devices/android/note3/__init__.py delete mode 100644 wlauto/devices/android/odroidxu3/__init__.py delete mode 100644 wlauto/devices/android/tc2/__init__.py delete mode 100644 wlauto/devices/android/tc2/resources/board_template.txt delete mode 100644 wlauto/devices/android/tc2/resources/images_iks.txt delete mode 100644 wlauto/devices/android/tc2/resources/images_mp.txt delete mode 100644 wlauto/devices/linux/XE503C12/__init__.py delete mode 100644 wlauto/devices/linux/__init__.py delete mode 100644 wlauto/devices/linux/chromeos_test_image/__init__.py delete mode 100644 wlauto/devices/linux/gem5/__init__.py delete mode 100644 wlauto/devices/linux/generic/__init__.py delete mode 100644 wlauto/devices/linux/odroidxu3_linux/__init__.py diff --git a/wlauto/__init__.py b/wlauto/__init__.py index 9e5e29f5..a010021f 100644 --- a/wlauto/__init__.py +++ b/wlauto/__init__.py @@ -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 diff --git a/wlauto/common/android/device.py b/wlauto/common/android/device.py deleted file mode 100644 index f5dab842..00000000 --- a/wlauto/common/android/device.py +++ /dev/null @@ -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\d+)x(?P\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), - ] diff --git a/wlauto/common/gem5/device.py b/wlauto/common/gem5/device.py deleted file mode 100644 index 83a1d5df..00000000 --- a/wlauto/common/gem5/device.py +++ /dev/null @@ -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\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 diff --git a/wlauto/common/linux/__init__.py b/wlauto/common/linux/__init__.py deleted file mode 100644 index 16224d6f..00000000 --- a/wlauto/common/linux/__init__.py +++ /dev/null @@ -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. -# - - diff --git a/wlauto/common/linux/device.py b/wlauto/common/linux/device.py deleted file mode 100644 index 1877e84b..00000000 --- a/wlauto/common/linux/device.py +++ /dev/null @@ -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//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 diff --git a/wlauto/core/bootstrap.py b/wlauto/core/bootstrap.py index 5a30d435..e6fef47d 100644 --- a/wlauto/core/bootstrap.py +++ b/wlauto/core/bootstrap.py @@ -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'), diff --git a/wlauto/core/device.py b/wlauto/core/device.py deleted file mode 100644 index 18ae643e..00000000 --- a/wlauto/core/device.py +++ /dev/null @@ -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 diff --git a/wlauto/devices/__init__.py b/wlauto/devices/__init__.py deleted file mode 100644 index 16224d6f..00000000 --- a/wlauto/devices/__init__.py +++ /dev/null @@ -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. -# - - diff --git a/wlauto/devices/android/__init__.py b/wlauto/devices/android/__init__.py deleted file mode 100644 index cd5d64d6..00000000 --- a/wlauto/devices/android/__init__.py +++ /dev/null @@ -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. -# - - diff --git a/wlauto/devices/android/gem5/__init__.py b/wlauto/devices/android/gem5/__init__.py deleted file mode 100644 index 853d06ba..00000000 --- a/wlauto/devices/android/gem5/__init__.py +++ /dev/null @@ -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) diff --git a/wlauto/devices/android/generic/__init__.py b/wlauto/devices/android/generic/__init__.py deleted file mode 100644 index a4cd74ea..00000000 --- a/wlauto/devices/android/generic/__init__.py +++ /dev/null @@ -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), - ] diff --git a/wlauto/devices/android/juno/__init__.py b/wlauto/devices/android/juno/__init__.py deleted file mode 100644 index 01a52ef6..00000000 --- a/wlauto/devices/android/juno/__init__.py +++ /dev/null @@ -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' - diff --git a/wlauto/devices/android/nexus10/__init__.py b/wlauto/devices/android/nexus10/__init__.py deleted file mode 100644 index ee8894a8..00000000 --- a/wlauto/devices/android/nexus10/__init__.py +++ /dev/null @@ -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') diff --git a/wlauto/devices/android/nexus5/__init__.py b/wlauto/devices/android/nexus5/__init__.py deleted file mode 100644 index cd2f09db..00000000 --- a/wlauto/devices/android/nexus5/__init__.py +++ /dev/null @@ -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), - ] diff --git a/wlauto/devices/android/note3/__init__.py b/wlauto/devices/android/note3/__init__.py deleted file mode 100644 index f9055f5a..00000000 --- a/wlauto/devices/android/note3/__init__.py +++ /dev/null @@ -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 ') diff --git a/wlauto/devices/android/odroidxu3/__init__.py b/wlauto/devices/android/odroidxu3/__init__.py deleted file mode 100644 index 60f780b7..00000000 --- a/wlauto/devices/android/odroidxu3/__init__.py +++ /dev/null @@ -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'), - ] - diff --git a/wlauto/devices/android/tc2/__init__.py b/wlauto/devices/android/tc2/__init__.py deleted file mode 100644 index ebf2a675..00000000 --- a/wlauto/devices/android/tc2/__init__.py +++ /dev/null @@ -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('') - diff --git a/wlauto/devices/android/tc2/resources/board_template.txt b/wlauto/devices/android/tc2/resources/board_template.txt deleted file mode 100644 index 39535d13..00000000 --- a/wlauto/devices/android/tc2/resources/board_template.txt +++ /dev/null @@ -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 diff --git a/wlauto/devices/android/tc2/resources/images_iks.txt b/wlauto/devices/android/tc2/resources/images_iks.txt deleted file mode 100644 index 05707092..00000000 --- a/wlauto/devices/android/tc2/resources/images_iks.txt +++ /dev/null @@ -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 diff --git a/wlauto/devices/android/tc2/resources/images_mp.txt b/wlauto/devices/android/tc2/resources/images_mp.txt deleted file mode 100644 index e671a74b..00000000 --- a/wlauto/devices/android/tc2/resources/images_mp.txt +++ /dev/null @@ -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 - diff --git a/wlauto/devices/linux/XE503C12/__init__.py b/wlauto/devices/linux/XE503C12/__init__.py deleted file mode 100644 index fe5bbab3..00000000 --- a/wlauto/devices/linux/XE503C12/__init__.py +++ /dev/null @@ -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' - diff --git a/wlauto/devices/linux/__init__.py b/wlauto/devices/linux/__init__.py deleted file mode 100644 index 16224d6f..00000000 --- a/wlauto/devices/linux/__init__.py +++ /dev/null @@ -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. -# - - diff --git a/wlauto/devices/linux/chromeos_test_image/__init__.py b/wlauto/devices/linux/chromeos_test_image/__init__.py deleted file mode 100644 index 39947864..00000000 --- a/wlauto/devices/linux/chromeos_test_image/__init__.py +++ /dev/null @@ -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 - diff --git a/wlauto/devices/linux/gem5/__init__.py b/wlauto/devices/linux/gem5/__init__.py deleted file mode 100644 index 41e72892..00000000 --- a/wlauto/devices/linux/gem5/__init__.py +++ /dev/null @@ -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) diff --git a/wlauto/devices/linux/generic/__init__.py b/wlauto/devices/linux/generic/__init__.py deleted file mode 100644 index 93f9f6ae..00000000 --- a/wlauto/devices/linux/generic/__init__.py +++ /dev/null @@ -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), - ] diff --git a/wlauto/devices/linux/odroidxu3_linux/__init__.py b/wlauto/devices/linux/odroidxu3_linux/__init__.py deleted file mode 100644 index f174950a..00000000 --- a/wlauto/devices/linux/odroidxu3_linux/__init__.py +++ /dev/null @@ -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' -