Source code for wlauto.devices.android.tc2

#    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)


[docs]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()) ]
[docs] def get_mode(self): return self.config.mode
[docs] 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
[docs] 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
[docs] 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)
[docs] 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.
[docs] def get_cpuidle(self): return self.get_sysfile_value('/sys/devices/system/cpu/cpu0/cpuidle/state1/disable')
[docs] 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)
[docs] 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)
[docs] 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('')