def BuildSectionData(self, required): """Build FIT entry contents This adds the 'data' properties to the input ITB (Image-tree Binary) then runs mkimage to process it. Args: required (bool): True if the data must be present, False if it is OK to return None Returns: bytes: Contents of the section """ data = self._build_input() uniq = self.GetUniqueName() input_fname = tools.get_output_filename(f'{uniq}.itb') output_fname = tools.get_output_filename(f'{uniq}.fit') tools.write_file(input_fname, data) tools.write_file(output_fname, data) args = {} ext_offset = self._fit_props.get('fit,external-offset') if ext_offset is not None: args = { 'external': True, 'pad': fdt_util.fdt32_to_cpu(ext_offset.value) } if self.mkimage.run(reset_timestamp=True, output_fname=output_fname, **args) is None: # Bintool is missing; just use empty data as the output self.record_missing_bintool(self.mkimage) return tools.get_bytes(0, 1024) return tools.read_file(output_fname)
def collect_contents_to_file(self, entries, prefix): """Put the contents of a list of entries into a file Args: entries (list of Entry): Entries to collect prefix (str): Filename prefix of file to write to If any entry does not have contents yet, this function returns False for the data. Returns: Tuple: bytes: Concatenated data from all the entries (or False) str: Filename of file written (or False if no data) str: Unique portion of filename (or False if no data) """ data = b'' for entry in entries: # First get the input data and put it in a file. If not available, # try later. if not entry.ObtainContents(): return False, False, False data += entry.GetData() uniq = self.GetUniqueName() fname = tools.get_output_filename(f'{prefix}.{uniq}') tools.write_file(fname, data) return data, fname, uniq
def ObtainContents(self): gbb = 'gbb.bin' fname = tools.get_output_filename(gbb) if not self.size: self.Raise('GBB must have a fixed size') gbb_size = self.size bmpfv_size = gbb_size - 0x2180 if bmpfv_size < 0: self.Raise('GBB is too small (minimum 0x2180 bytes)') keydir = tools.get_input_filename(self.keydir) stdout = self.futility.gbb_create(fname, [0x100, 0x1000, bmpfv_size, 0x1000]) if stdout is not None: stdout = self.futility.gbb_set( fname, hwid=self.hardware_id, rootkey='%s/root_key.vbpubk' % keydir, recoverykey='%s/recovery_key.vbpubk' % keydir, flags=self.gbb_flags, bmpfv=tools.get_input_filename(self.bmpblk)) if stdout is not None: self.SetContents(tools.read_file(fname)) else: # Bintool is missing; just use the required amount of zero data self.record_missing_bintool(self.futility) self.SetContents(tools.get_bytes(0, gbb_size)) return True
def BuildImage(self): """Write the image to a file""" fname = tools.get_output_filename(self._filename) tout.info("Writing image to '%s'" % fname) with open(fname, 'wb') as fd: data = self.GetPaddedData() fd.write(data) tout.info("Wrote %#x bytes" % len(data))
def EnsureCompiled(fname, tmpdir=None, capture_stderr=False): """Compile an fdt .dts source file into a .dtb binary blob if needed. Args: fname: Filename (if .dts it will be compiled). It not it will be left alone tmpdir: Temporary directory for output files, or None to use the tools-module output directory Returns: Filename of resulting .dtb file """ _, ext = os.path.splitext(fname) if ext != '.dts': return fname if tmpdir: dts_input = os.path.join(tmpdir, 'source.dts') dtb_output = os.path.join(tmpdir, 'source.dtb') else: dts_input = tools.get_output_filename('source.dts') dtb_output = tools.get_output_filename('source.dtb') search_paths = [os.path.join(os.getcwd(), 'include')] root, _ = os.path.splitext(fname) cc, args = tools.get_target_compile_tool('cc') args += ['-E', '-P', '-x', 'assembler-with-cpp', '-D__ASSEMBLY__'] args += ['-Ulinux'] for path in search_paths: args.extend(['-I', path]) args += ['-o', dts_input, fname] command.run(cc, *args) # If we don't have a directory, put it in the tools tempdir search_list = [] for path in search_paths: search_list.extend(['-i', path]) dtc, args = tools.get_target_compile_tool('dtc') args += [ '-I', 'dts', '-o', dtb_output, '-O', 'dtb', '-W', 'no-unit_address_vs_reg' ] args.extend(search_list) args.append(dts_input) command.run(dtc, *args, capture_stderr=capture_stderr) return dtb_output
def ReadBlobContents(self): if self._strip: uniq = self.GetUniqueName() out_fname = tools.get_output_filename('%s.stripped' % uniq) tools.write_file(out_fname, tools.read_file(self._pathname)) tools.run('strip', out_fname) self._pathname = out_fname super().ReadBlobContents() return True
def test_struct_scan_errors(self): """Test scanning a header file with an invalid unicode file""" output = tools.get_output_filename('output.h') tools.write_file(output, b'struct this is a test \x81 of bad unicode') scan = src_scan.Scanner(None, None) with test_util.capture_sys_output() as (stdout, _): scan.scan_header(output) self.assertIn('due to unicode error', stdout.getvalue())
def ObtainContents(self): data = b'' for entry in self._mkimage_entries.values(): # First get the input data and put it in a file. If not available, # try later. if not entry.ObtainContents(): return False data += entry.GetData() uniq = self.GetUniqueName() input_fname = tools.get_output_filename('mkimage.%s' % uniq) tools.write_file(input_fname, data) output_fname = tools.get_output_filename('mkimage-out.%s' % uniq) if self.mkimage.run_cmd('-d', input_fname, *self._args, output_fname) is not None: self.SetContents(tools.read_file(output_fname)) else: # Bintool is missing; just use the input data as the output self.record_missing_bintool(self.mkimage) self.SetContents(data) return True
def _BuildIfwi(self): """Build the contents of the IFWI and write it to the 'data' property""" # Create the IFWI file if needed if self._convert_fit: inname = self._pathname outname = tools.get_output_filename('ifwi.bin') if self.ifwitool.create_ifwi(inname, outname) is None: # Bintool is missing; just create a zeroed ifwi.bin self.record_missing_bintool(self.ifwitool) self.SetContents(tools.get_bytes(0, 1024)) self._filename = 'ifwi.bin' self._pathname = outname else: # Provide a different code path here to ensure we have test coverage outname = self._pathname # Delete OBBP if it is there, then add the required new items if self.ifwitool.delete_subpart(outname, 'OBBP') is None: # Bintool is missing; just use zero data self.record_missing_bintool(self.ifwitool) self.SetContents(tools.get_bytes(0, 1024)) return True for entry in self._ifwi_entries.values(): # First get the input data and put it in a file data = entry.GetPaddedData() uniq = self.GetUniqueName() input_fname = tools.get_output_filename('input.%s' % uniq) tools.write_file(input_fname, data) # At this point we know that ifwitool is present, so we don't need # to check for None here self.ifwitool.add_subpart(outname, entry._ifwi_subpart, entry._ifwi_entry_name, input_fname, entry._ifwi_replace) self.ReadBlobContents() return True
def WriteMap(self): """Write a map of the image to a .map file Returns: Filename of map file written """ filename = '%s.map' % self.image_name fname = tools.get_output_filename(filename) with open(fname, 'w') as fd: print('%8s %8s %8s %s' % ('ImagePos', 'Offset', 'Size', 'Name'), file=fd) super().WriteMap(fd, 0) return fname
def FromFile(cls, fname): """Convert an image file into an Image for use in binman Args: fname: Filename of image file to read Returns: Image object on success Raises: ValueError if something goes wrong """ data = tools.read_file(fname) size = len(data) # First look for an image header pos = image_header.LocateHeaderOffset(data) if pos is None: # Look for the FDT map pos = fdtmap.LocateFdtmap(data) if pos is None: raise ValueError('Cannot find FDT map in image') # We don't know the FDT size, so check its header first probe_dtb = fdt.Fdt.FromData(data[pos + fdtmap.FDTMAP_HDR_LEN:pos + 256]) dtb_size = probe_dtb.GetFdtObj().totalsize() fdtmap_data = data[pos:pos + dtb_size + fdtmap.FDTMAP_HDR_LEN] fdt_data = fdtmap_data[fdtmap.FDTMAP_HDR_LEN:] out_fname = tools.get_output_filename('fdtmap.in.dtb') tools.write_file(out_fname, fdt_data) dtb = fdt.Fdt(out_fname) dtb.Scan() # Return an Image with the associated nodes root = dtb.GetRoot() image = Image('image', root, copy_to_orig=False, ignore_missing=True, missing_etype=True, generate=False) image.image_node = fdt_util.GetString(root, 'image-node', 'image') image.fdtmap_dtb = dtb image.fdtmap_data = fdtmap_data image._data = data image._filename = fname image.image_name, _ = os.path.splitext(fname) return image
def GetVblock(self, required): """Get the contents of this entry Args: required: True if the data must be present, False if it is OK to return None Returns: bytes content of the entry, which is the signed vblock for the provided data """ # Join up the data files to be signed input_data = self.GetContents(required) if input_data is None: return None uniq = self.GetUniqueName() output_fname = tools.get_output_filename('vblock.%s' % uniq) input_fname = tools.get_output_filename('input.%s' % uniq) tools.write_file(input_fname, input_data) prefix = self.keydir + '/' stdout = self.futility.sign_firmware(vblock=output_fname, keyblock=prefix + self.keyblock, signprivate=prefix + self.signprivate, version=f'{self.version,}', firmware=input_fname, kernelkey=prefix + self.kernelkey, flags=f'{self.preamble_flags}') if stdout is not None: data = tools.read_file(output_fname) else: # Bintool is missing; just use 4KB of zero data self.record_missing_bintool(self.futility) data = tools.get_bytes(0, 4096) return data
def ObtainContents(self): data, input_fname, uniq = self.collect_contents_to_file( self._mkimage_entries.values(), 'mkimage') if data is False: return False output_fname = tools.get_output_filename('mkimage-out.%s' % uniq) if self.mkimage.run_cmd('-d', input_fname, *self._args, output_fname) is not None: self.SetContents(tools.read_file(output_fname)) else: # Bintool is missing; just use the input data as the output self.record_missing_bintool(self.mkimage) self.SetContents(data) return True
def check_fake_fname(self, fname): """If the file is missing and the entry allows fake blobs, fake it Sets self.faked to True if faked Args: fname (str): Filename to check Returns: fname (str): Filename of faked file """ if self.allow_fake and not pathlib.Path(fname).is_file(): outfname = tools.get_output_filename(os.path.basename(fname)) with open(outfname, "wb") as out: out.truncate(1024) self.faked = True return outfname return fname
def test_fiptool_list(self): """Create a FIP and check that fiptool can read it""" fwu = b'my data' tb_fw = b'some more data' fip = fip_util.FipWriter(0x123, 0x10) fip.add_entry('fwu', fwu, 0x456) fip.add_entry('tb-fw', tb_fw, 0) fip.add_entry(bytes(range(16)), tb_fw, 0) data = fip.get_data() fname = tools.get_output_filename('data.fip') tools.write_file(fname, data) result = FIPTOOL.info(fname) self.assertEqual( '''Firmware Updater NS_BL2U: offset=0xB0, size=0x7, cmdline="--fwu" Trusted Boot Firmware BL2: offset=0xC0, size=0xE, cmdline="--tb-fw" 00010203-0405-0607-0809-0A0B0C0D0E0F: offset=0xD0, size=0xE, cmdline="--blob" ''', result)
def setUp(self): # Create a temporary directory for test files self._indir = tempfile.mkdtemp(prefix='fip_util.') tools.set_input_dirs([self._indir]) # Set up a temporary output directory, used by the tools library when # compressing files tools.prepare_output_dir(None) self.src_file = os.path.join(self._indir, 'orig.py') self.outname = tools.get_output_filename('out.py') self.args = ['-D', '-s', self._indir, '-o', self.outname] self.readme = os.path.join(self._indir, 'readme.rst') self.macro_dir = os.path.join(self._indir, 'include/tools_share') self.macro_fname = os.path.join(self.macro_dir, 'firmware_image_package.h') self.name_dir = os.path.join(self._indir, 'tools/fiptool') self.name_fname = os.path.join(self.name_dir, 'tbbr_config.c')
def PrepareFromLoadedData(image): """Get device tree files ready for use with a loaded image Loaded images are different from images that are being created by binman, since there is generally already an fdtmap and we read the description from that. This provides the position and size of every entry in the image with no calculation required. This function uses the same output_fdt_info[] as Prepare(). It finds the device tree files, adds a reference to the fdtmap and sets the FDT path prefix to translate from the fdtmap (where the root node is the image node) to the normal device tree (where the image node is under a /binman node). Args: images: List of images being used """ global output_fdt_info, main_dtb, fdt_path_prefix tout.info('Preparing device trees') output_fdt_info.clear() fdt_path_prefix = '' output_fdt_info['fdtmap'] = [image.fdtmap_dtb, 'u-boot.dtb'] main_dtb = None tout.info(" Found device tree type 'fdtmap' '%s'" % image.fdtmap_dtb.name) for etype, value in image.GetFdts().items(): entry, fname = value out_fname = tools.get_output_filename('%s.dtb' % entry.etype) tout.info(" Found device tree type '%s' at '%s' path '%s'" % (etype, out_fname, entry.GetPath())) entry._filename = entry.GetDefaultFilename() data = entry.ReadData() tools.write_file(out_fname, data) dtb = fdt.Fdt(out_fname) dtb.Scan() image_node = dtb.GetNode('/binman') if 'multiple-images' in image_node.props: image_node = dtb.GetNode('/binman/%s' % image.image_node) fdt_path_prefix = image_node.path output_fdt_info[etype] = [dtb, None] tout.info(" FDT path prefix '%s'" % fdt_path_prefix)
def create_fiptool_image(self): """Create an image with fiptool which we can use for testing Returns: FipReader: reader for the image """ fwu = os.path.join(self._indir, 'fwu') tools.write_file(fwu, self.fwu_data) tb_fw = os.path.join(self._indir, 'tb_fw') tools.write_file(tb_fw, self.tb_fw_data) other_fw = os.path.join(self._indir, 'other_fw') tools.write_file(other_fw, self.other_fw_data) fname = tools.get_output_filename('data.fip') uuid = 'e3b78d9e-4a64-11ec-b45c-fba2b9b49788' FIPTOOL.create_new(fname, 8, 0x123, fwu, tb_fw, uuid, other_fw) return fip_util.FipReader(tools.read_file(fname))
def check_fake_fname(self, fname, size=0): """If the file is missing and the entry allows fake blobs, fake it Sets self.faked to True if faked Args: fname (str): Filename to check size (int): Size of fake file to create Returns: tuple: fname (str): Filename of faked file bool: True if the blob was faked, False if not """ if self.allow_fake and not pathlib.Path(fname).is_file(): outfname = tools.get_output_filename(os.path.basename(fname)) with open(outfname, "wb") as out: out.truncate(size) self.faked = True tout.info(f"Entry '{self._node.path}': Faked file '{outfname}'") return outfname, True return fname, False
def Prepare(images, dtb): """Get device tree files ready for use This sets up a set of device tree files that can be retrieved by GetAllFdts(). This includes U-Boot proper and any SPL device trees. Args: images: List of images being used dtb: Main dtb """ global output_fdt_info, main_dtb, fdt_path_prefix # Import these here in case libfdt.py is not available, in which case # the above help option still works. from dtoc import fdt from dtoc import fdt_util # If we are updating the DTBs we need to put these updated versions # where Entry_blob_dtb can find them. We can ignore 'u-boot.dtb' # since it is assumed to be the one passed in with options.dt, and # was handled just above. main_dtb = dtb output_fdt_info.clear() fdt_path_prefix = '' output_fdt_info['u-boot-dtb'] = [dtb, 'u-boot.dtb'] if use_fake_dtb: for etype, fname in DTB_TYPE_FNAME.items(): output_fdt_info[etype] = [dtb, fname] else: fdt_set = {} for etype, fname in DTB_TYPE_FNAME.items(): infile = tools.get_input_filename(fname, allow_missing=True) if infile and os.path.exists(infile): fname_dtb = fdt_util.EnsureCompiled(infile) out_fname = tools.get_output_filename('%s.out' % os.path.split(fname)[1]) tools.write_file(out_fname, tools.read_file(fname_dtb)) other_dtb = fdt.FdtScan(out_fname) output_fdt_info[etype] = [other_dtb, out_fname]
def ObtainContents(self): # If the section does not need microcode, there is nothing to do found = False for suffix in ['', '-spl', '-tpl']: name = 'u-boot%s-with-ucode-ptr' % suffix entry = self.section.FindEntryType(name) if entry and entry.target_offset: found = True if not found: self.data = b'' return True # Get the microcode from the device tree entry. If it is not available # yet, return False so we will be called later. If the section simply # doesn't exist, then we may as well return True, since we are going to # get an error anyway. for suffix in ['', '-spl', '-tpl']: name = 'u-boot%s-dtb-with-ucode' % suffix fdt_entry = self.section.FindEntryType(name) if fdt_entry: break if not fdt_entry: self.data = b'' return True if not fdt_entry.ready: return False if not fdt_entry.collate: # This binary can be empty self.data = b'' return True # Write it out to a file self._pathname = tools.get_output_filename('u-boot-ucode.bin') tools.write_file(self._pathname, fdt_entry.ucode_data) self.ReadBlobContents() return True
def testGetFilename(self): """Test the dtb filename can be provided""" self.assertEqual(tools.get_output_filename('source.dtb'), self.dtb.GetFilename())
def PrepareImagesAndDtbs(dtb_fname, select_images, update_fdt, use_expanded): """Prepare the images to be processed and select the device tree This function: - reads in the device tree - finds and scans the binman node to create all entries - selects which images to build - Updates the device tress with placeholder properties for offset, image-pos, etc. Args: dtb_fname: Filename of the device tree file to use (.dts or .dtb) selected_images: List of images to output, or None for all update_fdt: True to update the FDT wth entry offsets, etc. use_expanded: True to use expanded versions of entries, if available. So if 'u-boot' is called for, we use 'u-boot-expanded' instead. This is needed if update_fdt is True (although tests may disable it) Returns: OrderedDict of images: key: Image name (str) value: Image object """ # Import these here in case libfdt.py is not available, in which case # the above help option still works. from dtoc import fdt from dtoc import fdt_util global images # Get the device tree ready by compiling it and copying the compiled # output into a file in our output directly. Then scan it for use # in binman. dtb_fname = fdt_util.EnsureCompiled(dtb_fname) fname = tools.get_output_filename('u-boot.dtb.out') tools.write_file(fname, tools.read_file(dtb_fname)) dtb = fdt.FdtScan(fname) node = _FindBinmanNode(dtb) if not node: raise ValueError("Device tree '%s' does not have a 'binman' " "node" % dtb_fname) images = _ReadImageDesc(node, use_expanded) if select_images: skip = [] new_images = OrderedDict() for name, image in images.items(): if name in select_images: new_images[name] = image else: skip.append(name) images = new_images tout.notice('Skipping images: %s' % ', '.join(skip)) state.Prepare(images, dtb) # Prepare the device tree by making sure that any missing # properties are added (e.g. 'pos' and 'size'). The values of these # may not be correct yet, but we add placeholders so that the # size of the device tree is correct. Later, in # SetCalculatedProperties() we will insert the correct values # without changing the device-tree size, thus ensuring that our # entry offsets remain the same. for image in images.values(): image.CollectBintools() image.gen_entries() if update_fdt: image.AddMissingProperties(True) image.ProcessFdt(dtb) for dtb_item in state.GetAllFdts(): dtb_item.Sync(auto_resize=True) dtb_item.Pack() dtb_item.Flush() return images