def dict_or_bool(value): """ Ensures that either a dictionary or a boolean is used as a parameter. """ if isinstance(value, dict): return value return boolean(value)
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 __init__(self, config_dict): if isinstance(config_dict, UefiConfig): self.__dict__ = copy(config_dict.__dict__) else: try: self.image_name = config_dict['image_name'] self.image_args = config_dict['image_args'] self.fdt_support = boolean(config_dict['fdt_support']) except KeyError as e: raise ConfigError('Missing mandatory parameter for UEFI entry config: "{}"'.format(e)) self.initrd = config_dict.get('initrd') self.fdt_path = config_dict.get('fdt_path') if self.fdt_path and not self.fdt_support: raise ConfigError('FDT path has been specfied for UEFI entry, when FDT support is "False"')
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 __init__(self, config_dict): if isinstance(config_dict, UefiConfig): self.__dict__ = copy(config_dict.__dict__) else: try: self.image_name = config_dict['image_name'] self.image_args = config_dict['image_args'] self.fdt_support = boolean(config_dict['fdt_support']) except KeyError as e: raise ConfigError( 'Missing mandatory parameter for UEFI entry config: "{}"'. format(e)) self.initrd = config_dict.get('initrd') self.fdt_path = config_dict.get('fdt_path') if self.fdt_path and not self.fdt_support: raise ConfigError( 'FDT path has been specfied for UEFI entry, when FDT support is "False"' )
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
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)