blob: bec0c6f26a3f1f4a478bb21e8da9037dd208d160 [file] [log] [blame]
# 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=attribute-defined-outside-init
import os
import time
import tarfile
import tempfile
from devlib.module import FlashModule
from devlib.exception import HostError
from devlib.utils.android import fastboot_flash_partition, fastboot_command
from devlib.utils.misc import merge_dicts
class FastbootFlashModule(FlashModule):
name = 'fastboot'
description = """
Enables automated flashing of images using the fastboot utility.
To use this flasher, a set of image files to be flused are required.
In addition a mapping between partitions and image file is required. There are two ways
to specify those requirements:
- Image mapping: In this mode, a mapping between partitions and images is given in the agenda.
- Image Bundle: In This mode a tarball is specified, which must contain all image files as well
as well as a partition file, named ``partitions.txt`` which contains the mapping between
partitions and images.
The format of ``partitions.txt`` defines one mapping per line as such: ::
kernel zImage-dtb
ramdisk ramdisk_image
"""
delay = 0.5
partitions_file_name = 'partitions.txt'
@staticmethod
def probe(target):
return target.os == 'android'
def __call__(self, image_bundle=None, images=None, bootargs=None):
if bootargs:
raise ValueError('{} does not support boot configuration'.format(self.name))
self.prelude_done = False
to_flash = {}
if image_bundle: # pylint: disable=access-member-before-definition
image_bundle = expand_path(image_bundle)
to_flash = self._bundle_to_images(image_bundle)
to_flash = merge_dicts(to_flash, images or {}, should_normalize=False)
for partition, image_path in to_flash.iteritems():
self.logger.debug('flashing {}'.format(partition))
self._flash_image(self.target, partition, expand_path(image_path))
fastboot_command('reboot')
self.target.connect(timeout=180)
def _validate_image_bundle(self, image_bundle):
if not tarfile.is_tarfile(image_bundle):
raise HostError('File {} is not a tarfile'.format(image_bundle))
with tarfile.open(image_bundle) as tar:
files = [tf.name for tf in tar.getmembers()]
if not any(pf in files for pf in (self.partitions_file_name, '{}/{}'.format(files[0], self.partitions_file_name))):
HostError('Image bundle does not contain the required partition file (see documentation)')
def _bundle_to_images(self, image_bundle):
"""
Extracts the bundle to a temporary location and creates a mapping between the contents of the bundle
and images to be flushed.
"""
self._validate_image_bundle(image_bundle)
extract_dir = tempfile.mkdtemp()
with tarfile.open(image_bundle) as tar:
tar.extractall(path=extract_dir)
files = [tf.name for tf in tar.getmembers()]
if self.partitions_file_name not in files:
extract_dir = os.path.join(extract_dir, files[0])
partition_file = os.path.join(extract_dir, self.partitions_file_name)
return get_mapping(extract_dir, partition_file)
def _flash_image(self, target, partition, image_path):
if not self.prelude_done:
self._fastboot_prelude(target)
fastboot_flash_partition(partition, image_path)
time.sleep(self.delay)
def _fastboot_prelude(self, target):
target.reset(fastboot=True)
time.sleep(self.delay)
self.prelude_done = True
# utility functions
def expand_path(original_path):
path = os.path.abspath(os.path.expanduser(original_path))
if not os.path.exists(path):
raise HostError('{} does not exist.'.format(path))
return path
def get_mapping(base_dir, partition_file):
mapping = {}
with open(partition_file) as pf:
for line in pf:
pair = line.split()
if len(pair) != 2:
HostError('partitions.txt is not properly formated')
image_path = os.path.join(base_dir, pair[1])
if not os.path.isfile(expand_path(image_path)):
HostError('file {} was not found in the bundle or was misplaced'.format(pair[1]))
mapping[pair[0]] = image_path
return mapping