# Copyright (C) 2010, 2011 Linaro # # Author: Guilherme Salgado # # This file is part of Linaro Image Tools. # # Linaro Image Tools is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Linaro Image Tools is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Linaro Image Tools. If not, see . """Configuration for boards supported by linaro-media-create. To add support for a new board, you need to create a subclass of BoardConfig, set appropriate values for its variables and add it to board_configs at the bottom of this file. """ from binascii import crc32 from parted import Device import atexit import glob import logging import os import re import string import struct import tempfile from linaro_image_tools import cmd_runner from linaro_image_tools.hwpack.handler import HardwarepackHandler from linaro_image_tools.media_create.partitions import ( SECTOR_SIZE, partition_mounted, register_loopback, ) from linaro_image_tools.hwpack.hwpack_fields import ( BOOTFS, BOOTFS16, BOOT_MIN_SIZE_FIELD, BOOT_SCRIPT_FIELD, DTB_ADDR_FIELD, DTB_FILES_FIELD, DTB_FILE_FIELD, ENV_DD_FIELD, EXTRA_BOOT_OPTIONS_FIELD, EXTRA_SERIAL_OPTIONS_FIELD, INITRD_ADDR_FIELD, KERNEL_ADDR_FIELD, LOADER_MIN_SIZE_FIELD, LOADER_START_FIELD, LOAD_ADDR_FIELD, MMC_ID_FIELD, PARTITION_LAYOUT_FIELD, RESERVED_BOOTFS, ROOT_MIN_SIZE_FIELD, SAMSUNG_BL1_LEN_FIELD, SAMSUNG_BL1_START_FIELD, SAMSUNG_BL2_LEN_FIELD, SAMSUNG_BL2_START_FIELD, SAMSUNG_ENV_LEN_FIELD, SAMSUNG_ENV_START_FIELD, SERIAL_TTY_FIELD, SNOWBALL_STARTUP_FILES_CONFIG_FIELD, SPL_DD_FIELD, SPL_IN_BOOT_PART_FIELD, WIRED_INTERFACES_FIELD, WIRELESS_INTERFACES_FIELD, ) logger = logging.getLogger(__name__) KERNEL_GLOB = 'vmlinuz-*-%(kernel_flavor)s' INITRD_GLOB = 'initrd.img-*-%(kernel_flavor)s' DTB_GLOB = 'dt-*-%(kernel_flavor)s/%(dtb_name)s' # Notes: # * since we align partitions on 4 MiB by default, geometry is currently 128 # heads and 32 sectors (2 MiB) as to have CHS-aligned partition start/end # offsets most of the time and hence avoid some warnings with disk # partitioning tools # * apparently some OMAP3 ROMs don't tolerate vfat length of an odd number of # sectors (only sizes rounded to 1 KiB seem to boot) # * we want partitions aligned on 4 MiB as to get the best performance and # limit wear-leveling # * image_size is passed on the command-line and should preferably be a power # of 2; it should be used as a "don't go over this size" information for a # real device, and a "give me a file exactly this big" requirement for an # image file. Having exactly a power of 2 helps with QEMU; there seem to be # some truncating issues otherwise. XXX to be researched # align on 4 MiB PART_ALIGN_S = 4 * 1024 * 1024 / SECTOR_SIZE def align_up(value, align): """Round value to the next multiple of align.""" return (value + align - 1) / align * align def align_partition(min_start, min_length, start_alignment, end_alignment): """Compute partition start and end offsets based on specified constraints. :param min_start: Minimal start offset of partition :param min_lengh: Minimal length of partition :param start_alignment: Alignment of this partition :param end_alignment: Alignment of the data following this partition :return: start offset, end offset (inclusive), length """ start = align_up(min_start, start_alignment) # end offset is inclusive, so substact one end = align_up(start + min_length, end_alignment) - 1 # and add one to length length = end - start + 1 return start, end, length def copy_drop(src, dest_dir): """Copy a file from src to destdir, dropping root ownership on the way. """ cmd = ["cp", "-v", src, dest_dir] cmd_runner.run(cmd, as_root=True).wait() final = os.path.join(dest_dir, os.path.basename(src)) cmd = ["chown", "%s:%s" % (os.getuid(), os.getgid()), final] cmd_runner.run(cmd, as_root=True).wait() class BoardException(Exception): """Class for board related exceptions.""" class BoardConfig(object): """The configuration used when building an image for a board.""" LOADER_MIN_SIZE_S = align_up(1 * 1024 ** 2, SECTOR_SIZE) / SECTOR_SIZE BOOT_MIN_SIZE_S = align_up(50 * 1024 ** 2, SECTOR_SIZE) / SECTOR_SIZE ROOT_MIN_SIZE_S = align_up(50 * 1024 ** 2, SECTOR_SIZE) / SECTOR_SIZE def __init__(self): super(BoardConfig, self).__init__() # XXX: when killing v1 hwpack, we might rename these two without the # leading underscore. It is done in this way since sublcasses use # placeholders in the string for dinamically change values. But this # is done only for hwpack v1. self._extra_serial_options = '' self._live_serial_options = '' self.board = None self.boot_script = None self.bootloader_dd = False self.bootloader_file_in_boot_part = False self.bootloader_flavor = None self.dtb_addr = None self.dtb_file = None self.dtb_files = None self.dtb_name = None self.env_dd = False self.extra_boot_args_options = None self.fat_size = 32 self.fatload_command = 'fatload' self.load_interface = 'mmc' self.bootfs_type = 'vfat' self.fdt_high = '0xffffffff' self.hardwarepack_handler = None self.hwpack_format = None self.initrd_addr = None self.initrd_high = '0xffffffff' self.kernel_addr = None self.kernel_flavors = None self.load_addr = None self.loader_start_s = 1 self.mmc_device_id = 0 self.mmc_id = None self.mmc_option = '0:1' self.mmc_part_offset = 0 self.partition_layout = None self.serial_tty = None self.spl_dd = False self.spl_in_boot_part = False self.supports_writing_to_mmc = True self.uimage_path = '' self.wired_interfaces = None self.wireless_interfaces = None # Samsung Boot-loader implementation notes and terminology # # BL0, BL1, BL2, BL3 are the boot loader stages in order of execution # # BL0 - embedded boot loader on the internal ROM # BL1 - chip-specific boot loader provided by Samsung # BL2 - tiny boot loader; SPL (Second Program Loader) # BL3 - customized boot loader; U-Boot # # In linaro-image-tools, variables have been named samsung_blN-1 # e.g BL1 is samsung_bl0, BL2 is samsung_bl1, BL3 is samsung_bl2 # # samsung_bl0_{start,len}: Offset and maximum size for BL1 # samsung_bl1_{start,len}: Offset and maximum size for BL2 # samsung_bl2_{start,len}: Offset and maximum size for BL3 # samsung_env_{start,len}: Offset and maximum size for settings # self.samsung_bl1_start = 1 self.samsung_bl1_len = 32 self.samsung_bl2_start = 65 self.samsung_bl2_len = 1024 self.samsung_env_start = 33 self.samsung_env_len = 32 # XXX: attributes that are not listed in hwpackV3, should be removed? self.vmlinuz = None self.initrd = None # XXX: can be removed when killing v1 hwpack. def _get_live_serial_options(self): live_serial = self._live_serial_options if live_serial: if isinstance(live_serial, list): live_serial = ' '.join(live_serial) if self._check_placeholder_presence(live_serial, r'%s'): live_serial = live_serial % self.serial_tty return live_serial def _set_live_serial_options(self, value): self._live_serial_options = value live_serial_options = property(_get_live_serial_options, _set_live_serial_options) # XXX: can be removed when killing v1 hwpack. def _get_extra_serial_options(self): extra_serial = self._extra_serial_options if extra_serial: if isinstance(extra_serial, list): extra_serial = ' '.join(extra_serial) if self._check_placeholder_presence(extra_serial, r'%s'): extra_serial = extra_serial % self.serial_tty return extra_serial def _set_extra_serial_options(self, value): self._extra_serial_options = value extra_serial_options = property(_get_extra_serial_options, _set_extra_serial_options) def get_metadata_field(self, field_name): """ Return the metadata value for field_name if it can be found. """ data, _ = self.hardwarepack_handler.get_field(field_name) return data def set_metadata(self, hwpacks, bootloader=None, board=None): self.hardwarepack_handler = HardwarepackHandler(hwpacks, bootloader, board) with self.hardwarepack_handler: self.hwpack_format = self.hardwarepack_handler.get_format() if (self.hwpack_format == self.hardwarepack_handler.FORMAT_1): self.bootloader_copy_files = None return if (self.hwpack_format != self.hardwarepack_handler.FORMAT_1): # Clear V1 defaults. # TODO When removing v1 support, remove also default values # in the constructor and avoid all this. self.kernel_addr = None self.initrd_addr = None self.load_addr = None self.serial_tty = None self.fat_size = None self.dtb_name = None self.dtb_addr = None self.extra_boot_args_options = None self.boot_script = None self.kernel_flavors = None self.mmc_option = None self.mmc_part_offset = None self.samsung_bl1_start = None self.samsung_bl1_len = None self.samsung_env_len = None self.samsung_bl2_len = None # self.samsung_bl2_start and self.samsung_env_start should # be initialized to default value for backward compatibility. self.board = board # Set new values from metadata. self.kernel_addr = self.get_metadata_field(KERNEL_ADDR_FIELD) self.initrd_addr = self.get_metadata_field(INITRD_ADDR_FIELD) self.load_addr = self.get_metadata_field(LOAD_ADDR_FIELD) self.dtb_addr = self.get_metadata_field(DTB_ADDR_FIELD) self.serial_tty = self.get_metadata_field(SERIAL_TTY_FIELD) wired_interfaces = self.get_metadata_field(WIRED_INTERFACES_FIELD) if wired_interfaces: self.wired_interfaces = wired_interfaces wireless_interfaces = self.get_metadata_field( WIRELESS_INTERFACES_FIELD) if wireless_interfaces: self.wireless_interfaces = wireless_interfaces self.dtb_file = self.get_metadata_field(DTB_FILE_FIELD) # XXX: need to deprecate dtb_file field and use only dtb_files # for multiple entries. if self.dtb_file: logger.warn("Deprecation warning: use the 'dtb_files' field " "instead of 'dtb_file'.") self.dtb_files = self.get_metadata_field(DTB_FILES_FIELD) self.extra_boot_args_options = self.get_metadata_field( EXTRA_BOOT_OPTIONS_FIELD) self.boot_script = self.get_metadata_field(BOOT_SCRIPT_FIELD) self.extra_serial_options = self.get_metadata_field( EXTRA_SERIAL_OPTIONS_FIELD) self.snowball_startup_files_config = self.get_metadata_field( SNOWBALL_STARTUP_FILES_CONFIG_FIELD) self.partition_layout = self.get_metadata_field( PARTITION_LAYOUT_FIELD) if self.partition_layout in [BOOTFS, RESERVED_BOOTFS, None]: self.fat_size = 32 elif self.partition_layout == BOOTFS16: self.fat_size = 16 else: raise AssertionError("Unknown partition layout '%s'." % self.partition_layout) self.mmc_option = self.get_metadata_field(MMC_ID_FIELD) if self.mmc_option: self.mmc_device_id = int(self.mmc_option.split(':')[0]) self.mmc_part_offset = int(self.mmc_option.split(':')[1]) - 1 # XXX: need to fix these values. boot_min_size = self.get_metadata_field(BOOT_MIN_SIZE_FIELD) if boot_min_size: self.BOOT_MIN_SIZE_S = align_up(int(boot_min_size) * 1024 ** 2, SECTOR_SIZE) / SECTOR_SIZE root_min_size = self.get_metadata_field(ROOT_MIN_SIZE_FIELD) if root_min_size: self.ROOT_MIN_SIZE_S = align_up(int(root_min_size) * 1024 ** 2, SECTOR_SIZE) / SECTOR_SIZE loader_min_size = self.get_metadata_field(LOADER_MIN_SIZE_FIELD) if loader_min_size: self.LOADER_MIN_SIZE_S = ( align_up(int(loader_min_size) * 1024 ** 2, SECTOR_SIZE) / SECTOR_SIZE) spl_in_boot_part = self.get_metadata_field(SPL_IN_BOOT_PART_FIELD) if spl_in_boot_part is None: self.spl_in_boot_part = False elif string.lower(spl_in_boot_part) == 'yes': self.spl_in_boot_part = True elif string.lower(spl_in_boot_part) == 'no': self.spl_in_boot_part = False env_dd = self.get_metadata_field(ENV_DD_FIELD) if env_dd is None: self.env_dd = False elif string.lower(env_dd) == 'yes': self.env_dd = True elif string.lower(env_dd) == 'no': self.env_dd = False # XXX: in hwpack v3 this field is just called 'dd'. # Need to check its use. bootloader_dd = self.get_metadata_field('bootloader_dd') # Either bootloader_dd is not specified, or it contains the dd # offset. if bootloader_dd is None: self.bootloader_dd = False else: self.bootloader_dd = int(bootloader_dd) spl_dd = self.get_metadata_field(SPL_DD_FIELD) # Either spl_dd is not specified, or it contains the dd offset. if spl_dd is None: self.spl_dd = False else: self.spl_dd = int(spl_dd) loader_start = self.get_metadata_field(LOADER_START_FIELD) if loader_start: self.loader_start_s = int(loader_start) samsung_bl1_start = self.get_metadata_field( SAMSUNG_BL1_START_FIELD) if samsung_bl1_start: self.samsung_bl1_start = int(samsung_bl1_start) samsung_bl1_len = self.get_metadata_field( SAMSUNG_BL1_LEN_FIELD) if samsung_bl1_len: self.samsung_bl1_len = int(samsung_bl1_len) samsung_bl2_start = self.get_metadata_field( SAMSUNG_BL2_START_FIELD) if samsung_bl2_start: self.samsung_bl2_start = int(samsung_bl2_start) samsung_bl2_len = self.get_metadata_field( SAMSUNG_BL2_LEN_FIELD) if samsung_bl2_len: self.samsung_bl2_len = int(samsung_bl2_len) samsung_env_start = self.get_metadata_field( SAMSUNG_ENV_START_FIELD) if samsung_env_start is not None: self.samsung_env_start = int(samsung_env_start) samsung_env_len = self.get_metadata_field( SAMSUNG_ENV_LEN_FIELD) if samsung_env_len: self.samsung_env_len = int(samsung_env_len) self.bootloader_copy_files = self.hardwarepack_handler.get_field( "bootloader_copy_files")[0] # XXX: no reference in hwpackV3 format of these fields, double # check if they can be dropped when killing v1. self.bootloader = self.hardwarepack_handler.get_field( "bootloader") self.vmlinuz = self.get_metadata_field('vmlinuz') self.initrd = self.get_metadata_field('initrd') bootloader_file_in_boot_part = self.get_metadata_field( 'bootloader_file_in_boot_part') if bootloader_file_in_boot_part is None: self.bootloader_file_in_boot_part = False elif string.lower(bootloader_file_in_boot_part) == 'yes': self.bootloader_file_in_boot_part = True elif string.lower(bootloader_file_in_boot_part) == 'no': self.bootloader_file_in_boot_part = False def get_file(self, file_alias, default=None): # XXX remove the 'default' parameter when V1 support is removed! file_in_hwpack = self.hardwarepack_handler.get_file(file_alias) if file_in_hwpack is not None: return file_in_hwpack else: return default def get_v1_sfdisk_cmd(self, should_align_boot_part=False): # XXX: This default implementation and all overrides are left for V1 # compatibility only. They should be removed as part of the work to # kill off hwpacks V1. return self.get_normal_sfdisk_cmd(should_align_boot_part) def get_normal_sfdisk_cmd(self, should_align_boot_part=False): """Return the sfdisk command to partition the media. :param should_align_boot_part: Whether to align the boot partition too. This returns a boot partition of type FAT16 or FAT32 or Linux, followed by a root partition. """ if self.bootfs_type == 'vfat': if self.fat_size == 32: partition_type = '0x0C' else: partition_type = '0x0E' else: partition_type = '0x83' # align on sector 63 for compatibility with broken versions of x-loader # unless align_boot_part is set # XXX OMAP specific, might break other boards? boot_align = 63 if should_align_boot_part: boot_align = PART_ALIGN_S # can only start on sector 1 (sector 0 is MBR / partition table) boot_start, boot_end, boot_len = align_partition( 1, self.BOOT_MIN_SIZE_S, boot_align, PART_ALIGN_S) # apparently OMAP3 ROMs require the vfat length to be an even number # of sectors (multiple of 1 KiB); decrease the length if it's odd, # there should still be enough room # XXX OMAP specific, might break other boards? boot_len = boot_len - boot_len % 2 boot_end = boot_start + boot_len - 1 # we ignore _root_end / _root_len and return a sfdisk command to # instruct the use of all remaining space; XXX we now have root size # config, so we can do something more sensible root_start, _root_end, _root_len = align_partition( boot_end + 1, self.ROOT_MIN_SIZE_S, PART_ALIGN_S, PART_ALIGN_S) return '%s,%s,%s,*\n%s,,,-' % ( boot_start, boot_len, partition_type, root_start) def get_reserved_sfdisk_cmd(self, should_align_boot_part=None): """Return the sfdisk command to partition the media. :param should_align_boot_part: Ignored. This returns a loader partition, then a boot vfat partition of type FAT16 or FAT32, followed by a root partition. """ loader_start, loader_end, loader_len = align_partition( self.loader_start_s, self.LOADER_MIN_SIZE_S, 1, PART_ALIGN_S) boot_start, boot_end, boot_len = align_partition( loader_end + 1, self.BOOT_MIN_SIZE_S, PART_ALIGN_S, PART_ALIGN_S) root_start, _root_end, _root_len = align_partition( boot_end + 1, self.ROOT_MIN_SIZE_S, PART_ALIGN_S, PART_ALIGN_S) return '%s,%s,0xDA\n%s,%s,0x0C,*\n%s,,,-' % ( loader_start, loader_len, boot_start, boot_len, root_start) def get_sfdisk_cmd(self, should_align_boot_part=False): if (self.partition_layout in ['bootfs_rootfs', 'bootfs16_rootfs'] or self.board == 'snowball_sd'): return self.get_normal_sfdisk_cmd(should_align_boot_part) elif self.partition_layout in ['reserved_bootfs_rootfs']: return self.get_reserved_sfdisk_cmd(should_align_boot_part) else: assert (self.hwpack_format == HardwarepackHandler.FORMAT_1), ( "Hwpack format is not 1.0 but " "partition_layout is unspecified.") return self.get_v1_sfdisk_cmd(should_align_boot_part) def _get_bootcmd(self, i_img_data, d_img_data): """Get the bootcmd for this board. In general subclasses should not have to override this. """ replacements = dict( fatload_command=self.fatload_command, uimage_path=self.uimage_path, mmc_option=self.mmc_option, kernel_addr=self.kernel_addr, initrd_addr=self.initrd_addr, dtb_addr=self.dtb_addr, load_interface=self.load_interface) boot_script = ( ("%(fatload_command)s %(load_interface)s %(mmc_option)s " "%(kernel_addr)s %(uimage_path)suImage; ")) % replacements boot_script_bootm = (("bootm %(kernel_addr)s")) % replacements if i_img_data is not None and d_img_data is not None: boot_script += ( ("%(fatload_command)s %(load_interface)s %(mmc_option)s " "%(initrd_addr)s %(uimage_path)suInitrd; " "%(fatload_command)s %(load_interface)s %(mmc_option)s " "%(dtb_addr)s board.dtb; ")) % replacements boot_script_bootm += ( (" %(initrd_addr)s %(dtb_addr)s")) % replacements elif i_img_data is None and d_img_data is not None: boot_script += ( ("%(fatload_command)s %(load_interface)s %(mmc_option)s " "%(dtb_addr)s board.dtb; ")) % replacements boot_script_bootm += ((" - %(dtb_addr)s")) % replacements elif i_img_data is not None and d_img_data is None: boot_script += ( ("%(fatload_command)s %(load_interface)s %(mmc_option)s " "%(initrd_addr)s %(uimage_path)suInitrd; ")) % replacements boot_script_bootm += ((" %(initrd_addr)s")) % replacements boot_script += boot_script_bootm return boot_script def add_boot_args(self, extra_args): if extra_args is not None: if self.extra_boot_args_options is None: self.extra_boot_args_options = extra_args else: self.extra_boot_args_options += ' %s' % extra_args def add_boot_args_from_file(self, path): if path is not None: with open(path, 'r') as boot_args_file: self.add_boot_args(boot_args_file.read().strip()) def _get_bootargs(self, is_live, is_lowmem, consoles, rootfs_id): """Get the bootargs for this board. In general subclasses should not have to override this. """ boot_args_options = 'rootwait ro' serial_options = '' if self.extra_boot_args_options: boot_args_options += ' %s' % self.extra_boot_args_options if self.extra_serial_options: serial_options = self.extra_serial_options for console in consoles: serial_options += ' console=%s' % console lowmem_opt = '' boot_snippet = 'root=%s' % rootfs_id if is_live: serial_options += ' %s' % self.live_serial_options boot_snippet = 'boot=casper' if is_lowmem: lowmem_opt = 'only-ubiquity' replacements = dict(serial_options=serial_options.strip(), lowmem_opt=lowmem_opt, boot_snippet=boot_snippet, boot_args_options=boot_args_options) boot_args = ("%(serial_options)s %(lowmem_opt)s %(boot_snippet)s" " %(boot_args_options)s" % replacements).strip() return boot_args def _get_boot_env(self, is_live, is_lowmem, consoles, rootfs_id, i_img_data, d_img_data): """Get the boot environment for this board. In general subclasses should not have to override this. """ boot_env = {} boot_env["bootargs"] = self._get_bootargs( is_live, is_lowmem, consoles, rootfs_id) boot_env["bootcmd"] = self._get_bootcmd(i_img_data, d_img_data) boot_env["initrd_high"] = self.initrd_high boot_env["fdt_high"] = self.fdt_high return boot_env def make_boot_files(self, bootloader_parts_dir, is_live, is_lowmem, consoles, chroot_dir, rootfs_id, boot_dir, boot_device_or_file): if self.hwpack_format == HardwarepackHandler.FORMAT_1: parts_dir = bootloader_parts_dir else: parts_dir = chroot_dir (k_img_data, i_img_data, d_img_data) = self._get_kflavor_files( parts_dir) boot_env = self._get_boot_env(is_live, is_lowmem, consoles, rootfs_id, i_img_data, d_img_data) if self.hwpack_format == HardwarepackHandler.FORMAT_1: self._make_boot_files( boot_env, chroot_dir, boot_dir, boot_device_or_file, k_img_data, i_img_data, d_img_data) else: self._make_boot_files_v2( boot_env, chroot_dir, boot_dir, boot_device_or_file, k_img_data, i_img_data, d_img_data) def _copy_dtb_files(self, dtb_files, dest_dir, search_dir): """Copy the files defined in dtb_files into the boot directory. :param dtb_files: The list of dtb files :param dest_dir: The directory where to copy each dtb file. :param search_dir: The directory where to search for the real file. """ logger.info("Copying dtb files") for dtb_file in dtb_files: if dtb_file: if isinstance(dtb_file, dict): for key, value in dtb_file.iteritems(): # The name of the dtb file in the new position. to_file = os.path.basename(key) # The directory where to copy the dtb file. to_dir = os.path.join(dest_dir, os.path.dirname(key)) from_file = value # User specified only the directory, without renaming # the file. if not to_file: to_file = os.path.basename(from_file) if not os.path.exists(to_dir): cmd_runner.run(["mkdir", "-p", to_dir], as_root=True).wait() dtb = _get_file_matching(os.path.join(search_dir, from_file)) if not dtb: logger.warn('Could not find a valid dtb file, ' 'skipping it.') continue else: dest = os.path.join(to_dir, to_file) logger.debug('Copying %s into %s' % (dtb, dest)) cmd_runner.run(['cp', dtb, dest], as_root=True).wait() else: # Hopefully we should never get here. # This should only happen if the hwpack config YAML file is # wrong. logger.warn('WARNING: Wrong syntax in metadata file. ' 'Check the hwpack configuration file used to ' 'generate the hwpack archive.') def _dd_file(self, from_file, to_file, seek, max_size=None): assert from_file is not None, "No source file name given." if max_size is not None: assert os.path.getsize(from_file) <= max_size, ( "'%s' is larger than %s" % (from_file, max_size)) logger.info("Writing '%s' to '%s' at %s." % (from_file, to_file, seek)) _dd(from_file, to_file, seek=seek) def install_samsung_boot_loader(self, samsung_spl_file, bootloader_file, boot_device_or_file): self._dd_file(samsung_spl_file, boot_device_or_file, self.samsung_bl1_start, self.samsung_bl1_len * SECTOR_SIZE) self._dd_file(bootloader_file, boot_device_or_file, self.samsung_bl2_start, self.samsung_bl2_len * SECTOR_SIZE) def _make_boot_files_v2(self, boot_env, chroot_dir, boot_dir, boot_device_or_file, k_img_data, i_img_data, d_img_data): with self.hardwarepack_handler: spl_file = self.get_file('spl_file') if self.spl_in_boot_part: assert spl_file is not None, ( "SPL binary could not be found") logger.info( "Copying spl '%s' to boot partition." % spl_file) cmd_runner.run(["cp", "-v", spl_file, boot_dir], as_root=True).wait() # XXX: Is this really needed? cmd_runner.run(["sync"]).wait() if self.spl_dd: self._dd_file(spl_file, boot_device_or_file, self.spl_dd) bootloader_file = self.get_file('bootloader_file') if self.bootloader_dd: self._dd_file(bootloader_file, boot_device_or_file, self.bootloader_dd) make_uImage(self.load_addr, k_img_data, boot_dir) if i_img_data is not None: make_uInitrd(i_img_data, boot_dir) if d_img_data is not None: make_dtb(d_img_data, boot_dir) if self.boot_script is not None: boot_script_path = os.path.join(boot_dir, self.boot_script) make_boot_script(boot_env, boot_script_path) # Only used for Omap, will this be bad for the other boards? make_boot_ini(boot_script_path, boot_dir) if (self.snowball_startup_files_config is not None and self.board != 'snowball_sd'): self.populate_raw_partition(boot_device_or_file, chroot_dir) if self.env_dd: # Do we need to zero out the env before flashing it? _dd("/dev/zero", boot_device_or_file, count=self.samsung_env_len, seek=self.samsung_env_start) env_size = self.samsung_env_len * SECTOR_SIZE env_file = make_flashable_env(boot_env, env_size) self._dd_file(env_file, boot_device_or_file, self.samsung_env_start) def _make_boot_files(self, boot_env, chroot_dir, boot_dir, boot_device_or_file, k_img_data, i_img_data, d_img_data): """Make the necessary boot files for this board. This is usually board-specific so ought to be defined in every subclass. """ raise NotImplementedError() def populate_boot(self, chroot_dir, rootfs_id, boot_partition, boot_disk, boot_device_or_file, is_live, is_lowmem, consoles): parts_dir = 'boot' if is_live: parts_dir = 'casper' bootloader_parts_dir = os.path.join(chroot_dir, parts_dir) cmd_runner.run(['mkdir', '-p', boot_disk]).wait() with partition_mounted(boot_partition, boot_disk): with self.hardwarepack_handler: if self.bootloader_file_in_boot_part: # if self.bootloader_flavor is not None: default = os.path.join( chroot_dir, 'usr', 'lib', 'u-boot', self.bootloader_flavor, 'u-boot.img') if not os.path.exists(default): default = os.path.join( chroot_dir, 'usr', 'lib', 'u-boot', self.bootloader_flavor, 'u-boot.bin') else: default = None # bootloader_bin = self.get_file('bootloader_file', default=default) assert bootloader_bin is not None, ( "bootloader binary could not be found") proc = cmd_runner.run( ['cp', '-v', bootloader_bin, boot_disk], as_root=True) proc.wait() # Handle copy_files field. self.copy_files(boot_disk) # Handle dtb_files field. if self.dtb_files: self._copy_dtb_files(self.dtb_files, boot_disk, chroot_dir) self.make_boot_files( bootloader_parts_dir, is_live, is_lowmem, consoles, chroot_dir, rootfs_id, boot_disk, boot_device_or_file) def copy_files(self, boot_disk): """Handle the copy_files metadata field.""" # Extract anything specified by copy_files sections # self.bootloader_copy_files is always of the form: # {'source_package': # [ # {'source_path': 'dest_path'} # ] # } if self.bootloader_copy_files is None: return for source_package, file_list in \ self.bootloader_copy_files.iteritems(): for file_info in file_list: for source_path, dest_path in file_info.iteritems(): source = self.hardwarepack_handler.get_file_from_package( source_path, source_package) dest_path = dest_path.lstrip("/\\") dirname = os.path.dirname(dest_path) dirname = os.path.join(boot_disk, dirname) if not os.path.exists(dirname): cmd_runner.run(["mkdir", "-p", dirname], as_root=True).wait() proc = cmd_runner.run( ['cp', '-v', source, os.path.join(boot_disk, dest_path)], as_root=True) proc.wait() def _get_kflavor_files(self, path): """Search for kernel, initrd and optional dtb in path.""" if self.kernel_flavors is None: # V2 metadata specifies each glob, not flavors. # XXX This duplication is temporary until V1 dies. return self._get_kflavor_files_v2(path) for flavor in self.kernel_flavors: kregex = KERNEL_GLOB % {'kernel_flavor': flavor} iregex = INITRD_GLOB % {'kernel_flavor': flavor} dregex = DTB_GLOB % {'kernel_flavor': flavor, 'dtb_name': self.dtb_name} kernel = _get_file_matching(os.path.join(path, kregex)) if kernel is not None: initrd = _get_file_matching(os.path.join(path, iregex)) if initrd is not None: dtb = None if self.dtb_name is not None: dtb = _get_file_matching(os.path.join(path, dregex)) return (kernel, initrd, dtb) raise ValueError( "Found kernel for flavor %s but no initrd matching %s" % ( flavor, iregex)) raise ValueError( "No kernel found matching %s for flavors %s" % ( KERNEL_GLOB, " ".join(self.kernel_flavors))) def _get_kflavor_files_v2(self, path): kernel = initrd = dtb = None if self.vmlinuz: kernel = _get_file_matching(os.path.join(path, self.vmlinuz)) if not self.vmlinuz or not kernel: raise ValueError("Unable to find a valid kernel image.") if self.initrd: initrd = _get_file_matching(os.path.join(path, self.initrd)) if not self.initrd or not initrd: logger.warn("Could not find a valid initrd, skipping uInitrd.") if self.dtb_file: dtb = _get_file_matching(os.path.join(path, self.dtb_file)) if not self.dtb_file or not dtb: logger.warn("Could not find a valid dtb file, skipping it.") logger.info("Will use kernel=%s, initrd=%s, dtb=%s." % (kernel, initrd, dtb)) return (kernel, initrd, dtb) def populate_raw_partition(self, media, boot_dir): # Override in subclass if needed pass def snowball_config(self, chroot_dir): # Override in subclasses where applicable raise NotImplementedError( "snowball_config() must only be called on BoardConfigs that " "use the Snowball startupfiles.") # XXX: can be removed when killing v1 hwpack and updating the attributes # that use it. @staticmethod def _check_placeholder_presence(string, placeholder): """Checks if the passed string contains the particular placeholder.""" # Very simple way of achieving that. presence = False if string and placeholder in string: presence = True return presence class OmapConfig(BoardConfig): def __init__(self): super(OmapConfig, self).__init__() self.kernel_flavors = ['linaro-omap4', 'linaro-lt-omap', 'linaro-omap', 'omap4'] self.bootloader_file_in_boot_part = True # XXX: Here we define these things as dynamic properties because our # temporary hack to fix bug 697824 relies on changing the board's # serial_tty at run time. self._serial_tty = None # XXX: when killing v1 hwpack this should be safely removed. def _get_serial_tty(self): return self._serial_tty def _set_serial_tty(self, value): self._serial_tty = value serial_tty = property(_get_serial_tty, _set_serial_tty) def set_appropriate_serial_tty(self, chroot_dir): """Set the appropriate serial_tty depending on the kernel used. If the kernel found in the chroot dir is << 2.6.36 we use tyyS2, else we use the default value (_serial_tty). """ # XXX: delete this method when hwpacks V1 can die assert self.hwpack_format == HardwarepackHandler.FORMAT_1 # XXX: This is also part of our temporary hack to fix bug 697824. # cls.serial_tty = classproperty(lambda cls: cls._serial_tty) vmlinuz = _get_file_matching( os.path.join(chroot_dir, 'boot', 'vmlinuz*')) basename = os.path.basename(vmlinuz) match = re.match('.*2\.6\.([0-9]{2}).*', basename) # Assume if it doesn't match that it is 3.0 or later. if match is not None: minor_version = match.group(1) if int(minor_version) < 36: self.serial_tty = 'ttyS2' def make_boot_files(self, bootloader_parts_dir, is_live, is_lowmem, consoles, chroot_dir, rootfs_id, boot_dir, boot_device_or_file): # XXX: This is also part of our temporary hack to fix bug 697824; we # need to call set_appropriate_serial_tty() before doing anything that # may use self.serial_tty. if self.hwpack_format == HardwarepackHandler.FORMAT_1: self.set_appropriate_serial_tty(chroot_dir) super(OmapConfig, self).make_boot_files( bootloader_parts_dir, is_live, is_lowmem, consoles, chroot_dir, rootfs_id, boot_dir, boot_device_or_file) def _make_boot_files(self, boot_env, chroot_dir, boot_dir, boot_device_or_file, k_img_data, i_img_data, d_img_data): # XXX: delete this method when hwpacks V1 can die assert self.hwpack_format == HardwarepackHandler.FORMAT_1 install_omap_boot_loader(chroot_dir, boot_dir, self) make_uImage(self.load_addr, k_img_data, boot_dir) make_uInitrd(i_img_data, boot_dir) make_dtb(d_img_data, boot_dir) boot_script_path = os.path.join(boot_dir, self.boot_script) make_boot_script(boot_env, boot_script_path) make_boot_ini(boot_script_path, boot_dir) class BeagleConfig(OmapConfig): def __init__(self): super(BeagleConfig, self).__init__() self.boot_script = 'boot.scr' self.bootloader_flavor = 'omap3_beagle' self.dtb_addr = '0x815f0000' self.dtb_name = 'omap3-beagle.dtb' self.extra_boot_args_options = ( 'earlyprintk fixrtc nocompcache vram=12M ' 'omapfb.mode=dvi:1280x720MR-16@60 mpurate=${mpurate}') self.initrd_addr = '0x81600000' self.kernel_addr = '0x80000000' self.load_addr = '0x80008000' self._serial_tty = 'ttyO2' self._extra_serial_options = 'console=tty0 console=%s,115200n8' self._live_serial_options = 'serialtty=%s' class OveroConfig(OmapConfig): def __init__(self): super(OveroConfig, self).__init__() self.boot_script = 'boot.scr' self.bootloader_flavor = 'omap3_overo' self.dtb_addr = '0x815f0000' self.dtb_name = 'omap3-overo.dtb' self.extra_boot_args_options = ( 'earlyprintk mpurate=${mpurate} vram=12M ' 'omapdss.def_disp=${defaultdisplay} omapfb.mode=dvi:${dvimode}') self.initrd_addr = '0x81600000' self.kernel_addr = '0x80000000' self.load_addr = '0x80008000' self._extra_serial_options = 'console=tty0 console=%s,115200n8' self._serial_tty = 'ttyO2' class PandaConfig(OmapConfig): def __init__(self): super(PandaConfig, self).__init__() self._serial_tty = 'ttyO2' self.boot_script = 'boot.scr' self.bootloader_flavor = 'omap4_panda' self.dtb_addr = '0x815f0000' self.dtb_name = 'omap4-panda.dtb' self.extra_boot_args_options = ( 'earlyprintk fixrtc nocompcache vram=48M ' 'omapfb.vram=0:24M mem=456M@0x80000000 mem=512M@0xA0000000') self.initrd_addr = '0x81600000' self.kernel_addr = '0x80200000' self.load_addr = '0x80008000' self._extra_serial_options = 'console=tty0 console=%s,115200n8' self._live_serial_options = 'serialtty=%s' class BeagleBoneConfig(OmapConfig): def __init__(self): super(BeagleBoneConfig, self).__init__() self.boot_script = 'boot.scr' self.bootloader_flavor = 'am335x_evm' self.kernel_flavors = ['am335x'] self._serial_tty = 'ttyO0' self.dtb_addr = '0x815f0000' self.initrd_addr = '0x81600000' self.kernel_addr = '0x80200000' self.load_addr = '0x80008000' self.extra_boot_args_options = ('fixrtc') self._extra_serial_options = 'console=ttyO0,115200n8' class IgepConfig(BeagleConfig): def __init__(self): super(IgepConfig, self).__init__() self.bootloader_file_in_boot_part = False self.bootloader_flavor = None self.dtb_name = 'isee-igep-v2.dtb' def _make_boot_files(self, boot_env, chroot_dir, boot_dir, boot_device_or_file, k_img_data, i_img_data, d_img_data): # XXX: delete this method when hwpacks V1 can die assert self.hwpack_format == HardwarepackHandler.FORMAT_1 make_uImage(self.load_addr, k_img_data, boot_dir) make_uInitrd(i_img_data, boot_dir) make_dtb(d_img_data, boot_dir) boot_script_path = os.path.join(boot_dir, self.boot_script) make_boot_script(boot_env, boot_script_path) make_boot_ini(boot_script_path, boot_dir) class Ux500Config(BoardConfig): def __init__(self): super(Ux500Config, self).__init__() self.boot_script = 'flash.scr' self.extra_boot_args_options = ( 'earlyprintk rootdelay=1 fixrtc nocompcache ' 'mem=96M@0 mem_modem=32M@96M mem=44M@128M pmem=22M@172M ' 'mem=30M@194M mem_mali=32M@224M pmem_hwb=54M@256M ' 'hwmem=48M@302M mem=152M@360M') self.initrd_addr = '0x08000000' self.kernel_addr = '0x00100000' self.kernel_flavors = ['u8500', 'ux500'] self.load_addr = '0x00008000' self.mmc_option = '1:1' self.serial_tty = 'ttyAMA2' self._extra_serial_options = 'console=tty0 console=%s,115200n8' self._live_serial_options = 'serialtty=%s' def _make_boot_files(self, boot_env, chroot_dir, boot_dir, boot_device_or_file, k_img_data, i_img_data, d_img_data): # XXX: delete this method when hwpacks V1 can die assert self.hwpack_format == HardwarepackHandler.FORMAT_1 make_uImage(self.load_addr, k_img_data, boot_dir) make_uInitrd(i_img_data, boot_dir) boot_script_path = os.path.join(boot_dir, self.boot_script) make_boot_script(boot_env, boot_script_path) class SnowballSdConfig(Ux500Config): '''Use only with --mmc option. Creates the standard vfat and ext2 partitions for kernel and rootfs on an SD card. Note that the Snowball board needs a loader partition on the internal eMMC flash to boot. That partition is created with the SnowballConfigImage configuration.''' def __init__(self): super(SnowballSdConfig, self).__init__() def _make_boot_files(self, boot_env, chroot_dir, boot_dir, boot_device_or_file, k_img_data, i_img_data, d_img_data): # XXX: delete this method when hwpacks V1 can die assert self.hwpack_format == HardwarepackHandler.FORMAT_1 make_uImage(self.load_addr, k_img_data, boot_dir) boot_script_path = os.path.join(boot_dir, self.boot_script) make_boot_script(boot_env, boot_script_path) class SnowballEmmcConfig(SnowballSdConfig): '''Use only with --image option. Creates a raw image which contains an additional (raw) loader partition, containing some boot stages and u-boot.''' SNOWBALL_LOADER_START_S = (128 * 1024) / SECTOR_SIZE TOC_SIZE = 512 def __init__(self): super(SnowballEmmcConfig, self).__init__() # Boot ROM looks for a boot table of contents (TOC) at 0x20000 # Actually, it first looks at address 0, but that's where l-m-c # puts the MBR, so the boot loader skips that address. self.supports_writing_to_mmc = False self.snowball_startup_files_config = 'startfiles.cfg' self.mmc_option = '0:2' def get_v1_sfdisk_cmd(self, should_align_boot_part=None): """Return the sfdisk command to partition the media. :param should_align_boot_part: Ignored. The Snowball partitioning scheme depends on whether the target is a raw image or an SD card. Both targets have the normal FAT 32 boot partition and EXT? root partition. The raw image prepends these two partitions with a raw loader partition, containing HW-dependent boot stages up to and including u-boot. This is done since the boot rom always boots off the internal memory; there simply is no point to having a loader partition on SD card. """ # boot ROM expects bootloader at 0x20000, which is sector 0x100 # with the usual SECTOR_SIZE of 0x200. # (sector 0 is MBR / partition table) loader_start, loader_end, loader_len = align_partition( self.SNOWBALL_LOADER_START_S, self.LOADER_MIN_SIZE_S, 1, PART_ALIGN_S) boot_start, boot_end, boot_len = align_partition( loader_end + 1, self.BOOT_MIN_SIZE_S, PART_ALIGN_S, PART_ALIGN_S) # we ignore _root_end / _root_len and return an sfdisk command to # instruct the use of all remaining space; XXX if we had some root size # config, we could do something more sensible root_start, _root_end, _root_len = align_partition( boot_end + 1, self.ROOT_MIN_SIZE_S, PART_ALIGN_S, PART_ALIGN_S) return '%s,%s,0xDA\n%s,%s,0x0C,*\n%s,,,-' % ( loader_start, loader_len, boot_start, boot_len, root_start) def _make_boot_files(self, boot_env, chroot_dir, boot_dir, boot_device_or_file, k_img_data, i_img_data, d_img_data): # XXX: delete this method when hwpacks V1 can die assert self.hwpack_format == HardwarepackHandler.FORMAT_1 make_uImage(self.load_addr, k_img_data, boot_dir) boot_script_path = os.path.join(boot_dir, self.boot_script) make_boot_script(boot_env, boot_script_path) self.populate_raw_partition(boot_device_or_file, chroot_dir) def populate_raw_partition(self, boot_device_or_file, chroot_dir): # Populate created raw partition with TOC and startup files. _, toc_filename = tempfile.mkstemp() config_files_dir = self.snowball_config(chroot_dir) new_files = self.get_file_info(chroot_dir, config_files_dir) with open(toc_filename, 'wb') as toc: self.create_toc(toc, new_files) self.install_snowball_boot_loader(toc_filename, new_files, boot_device_or_file, self.SNOWBALL_LOADER_START_S, self.delete_startupfiles) self.delete_file(toc_filename) if self.delete_startupfiles: self.delete_file(os.path.join(config_files_dir, self.snowball_startup_files_config)) def snowball_config(self, chroot_dir): # We will find the startupfiles in the target boot partition. return os.path.join(chroot_dir, 'boot') @property def delete_startupfiles(self): # The startupfiles will have been installed to the target boot # partition by the hwpack, and should be deleted so we don't leave # them on the target system. return True def install_snowball_boot_loader(self, toc_file_name, files, boot_device_or_file, start_sector, delete_startupfiles=False): ''' Copies TOC and boot files into the boot partition. A sector size of 1 is used for some files, as they do not necessarily start on an even address. ''' assert os.path.getsize(toc_file_name) <= self.TOC_SIZE _dd(toc_file_name, boot_device_or_file, seek=start_sector) for file in files: # XXX We need checks that these files do not overwrite each # other. This code assumes that offset and file sizes are ok. filename = file['filename'] if (file['offset'] % SECTOR_SIZE) != 0: seek_bytes = start_sector * SECTOR_SIZE + file['offset'] _dd(filename, boot_device_or_file, block_size=1, seek=seek_bytes) else: seek_sectors = start_sector + file['offset'] / SECTOR_SIZE _dd(filename, boot_device_or_file, seek=seek_sectors) if delete_startupfiles: self.delete_file(filename) def delete_file(self, file_path): cmd = ["rm", "%s" % file_path] proc = cmd_runner.run(cmd, as_root=True) proc.wait() def create_toc(self, f, files): ''' Writes a table of contents of the boot binaries. Boot rom searches this table to find the binaries.''' # Format string means: < little endian, # I; unsigned int; offset, # I; unsigned int; size, # I; unsigned int; flags, # i; int; align, # i; int; load_address, # 12s; string of char; name # http://igloocommunity.org/support/index.php/ConfigPartitionOverview toc_format = '/MLO) files = glob.glob( os.path.join(chroot_dir, 'usr', 'lib', '*', '*', 'MLO')) if len(files) == 0: # This one matches the path of MLO files installed by older # x-loader-omap package (e.g. /usr/lib/x-loader-omap[34]/MLO) files = glob.glob( os.path.join(chroot_dir, 'usr', 'lib', '*', 'MLO')) if len(files) == 1: return files[0] elif len(files) > 1: raise AssertionError( "More than one MLO file found on %s" % chroot_dir) else: raise AssertionError("No MLO files found on %s" % chroot_dir) def install_omap_boot_loader(chroot_dir, boot_disk, cls): with cls.hardwarepack_handler: try: default = _get_mlo_file(chroot_dir) except AssertionError: default = None mlo_file = cls.get_file('spl_file', default=default) cmd_runner.run(["cp", "-v", mlo_file, boot_disk], as_root=True).wait() # XXX: Is this really needed? cmd_runner.run(["sync"]).wait() def make_boot_ini(boot_script_path, boot_disk): proc = cmd_runner.run( ["cp", "-v", boot_script_path, "%s/boot.ini" % boot_disk], as_root=True) proc.wait()