1# SPDX-License-Identifier: GPL-2.0+ 2# Copyright (c) 2016 Google, Inc 3# Written by Simon Glass <sjg@chromium.org> 4# 5# Creates binary images from input files controlled by a description 6# 7 8from collections import OrderedDict 9import os 10import re 11import sys 12import tools 13 14import command 15import elf 16from image import Image 17import tout 18 19# List of images we plan to create 20# Make this global so that it can be referenced from tests 21images = OrderedDict() 22 23# Records the device-tree files known to binman, keyed by filename (e.g. 24# 'u-boot-spl.dtb') 25fdt_files = {} 26 27# Arguments passed to binman to provide arguments to entries 28entry_args = {} 29 30 31def _ReadImageDesc(binman_node): 32 """Read the image descriptions from the /binman node 33 34 This normally produces a single Image object called 'image'. But if 35 multiple images are present, they will all be returned. 36 37 Args: 38 binman_node: Node object of the /binman node 39 Returns: 40 OrderedDict of Image objects, each of which describes an image 41 """ 42 images = OrderedDict() 43 if 'multiple-images' in binman_node.props: 44 for node in binman_node.subnodes: 45 images[node.name] = Image(node.name, node) 46 else: 47 images['image'] = Image('image', binman_node) 48 return images 49 50def _FindBinmanNode(dtb): 51 """Find the 'binman' node in the device tree 52 53 Args: 54 dtb: Fdt object to scan 55 Returns: 56 Node object of /binman node, or None if not found 57 """ 58 for node in dtb.GetRoot().subnodes: 59 if node.name == 'binman': 60 return node 61 return None 62 63def GetFdt(fname): 64 """Get the Fdt object for a particular device-tree filename 65 66 Binman keeps track of at least one device-tree file called u-boot.dtb but 67 can also have others (e.g. for SPL). This function looks up the given 68 filename and returns the associated Fdt object. 69 70 Args: 71 fname: Filename to look up (e.g. 'u-boot.dtb'). 72 73 Returns: 74 Fdt object associated with the filename 75 """ 76 return fdt_files[fname] 77 78def GetFdtPath(fname): 79 return fdt_files[fname]._fname 80 81def SetEntryArgs(args): 82 global entry_args 83 84 entry_args = {} 85 if args: 86 for arg in args: 87 m = re.match('([^=]*)=(.*)', arg) 88 if not m: 89 raise ValueError("Invalid entry arguemnt '%s'" % arg) 90 entry_args[m.group(1)] = m.group(2) 91 92def GetEntryArg(name): 93 return entry_args.get(name) 94 95def WriteEntryDocs(modules, test_missing=None): 96 from entry import Entry 97 Entry.WriteDocs(modules, test_missing) 98 99def Binman(options, args): 100 """The main control code for binman 101 102 This assumes that help and test options have already been dealt with. It 103 deals with the core task of building images. 104 105 Args: 106 options: Command line options object 107 args: Command line arguments (list of strings) 108 """ 109 global images 110 111 if options.full_help: 112 pager = os.getenv('PAGER') 113 if not pager: 114 pager = 'more' 115 fname = os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])), 116 'README') 117 command.Run(pager, fname) 118 return 0 119 120 # Try to figure out which device tree contains our image description 121 if options.dt: 122 dtb_fname = options.dt 123 else: 124 board = options.board 125 if not board: 126 raise ValueError('Must provide a board to process (use -b <board>)') 127 board_pathname = os.path.join(options.build_dir, board) 128 dtb_fname = os.path.join(board_pathname, 'u-boot.dtb') 129 if not options.indir: 130 options.indir = ['.'] 131 options.indir.append(board_pathname) 132 133 try: 134 # Import these here in case libfdt.py is not available, in which case 135 # the above help option still works. 136 import fdt 137 import fdt_util 138 139 tout.Init(options.verbosity) 140 elf.debug = options.debug 141 try: 142 tools.SetInputDirs(options.indir) 143 tools.PrepareOutputDir(options.outdir, options.preserve) 144 SetEntryArgs(options.entry_arg) 145 146 # Get the device tree ready by compiling it and copying the compiled 147 # output into a file in our output directly. Then scan it for use 148 # in binman. 149 dtb_fname = fdt_util.EnsureCompiled(dtb_fname) 150 fname = tools.GetOutputFilename('u-boot-out.dtb') 151 with open(dtb_fname) as infd: 152 with open(fname, 'wb') as outfd: 153 outfd.write(infd.read()) 154 dtb = fdt.FdtScan(fname) 155 156 # Note the file so that GetFdt() can find it 157 fdt_files['u-boot.dtb'] = dtb 158 node = _FindBinmanNode(dtb) 159 if not node: 160 raise ValueError("Device tree '%s' does not have a 'binman' " 161 "node" % dtb_fname) 162 163 images = _ReadImageDesc(node) 164 165 # Prepare the device tree by making sure that any missing 166 # properties are added (e.g. 'pos' and 'size'). The values of these 167 # may not be correct yet, but we add placeholders so that the 168 # size of the device tree is correct. Later, in 169 # SetCalculatedProperties() we will insert the correct values 170 # without changing the device-tree size, thus ensuring that our 171 # entry offsets remain the same. 172 for image in images.values(): 173 if options.update_fdt: 174 image.AddMissingProperties() 175 image.ProcessFdt(dtb) 176 177 dtb.Pack() 178 dtb.Flush() 179 180 for image in images.values(): 181 # Perform all steps for this image, including checking and 182 # writing it. This means that errors found with a later 183 # image will be reported after earlier images are already 184 # completed and written, but that does not seem important. 185 image.GetEntryContents() 186 image.GetEntryOffsets() 187 image.PackEntries() 188 image.CheckSize() 189 image.CheckEntries() 190 image.SetImagePos() 191 if options.update_fdt: 192 image.SetCalculatedProperties() 193 image.ProcessEntryContents() 194 image.WriteSymbols() 195 image.BuildImage() 196 if options.map: 197 image.WriteMap() 198 with open(fname, 'wb') as outfd: 199 outfd.write(dtb.GetContents()) 200 finally: 201 tools.FinaliseOutputDir() 202 finally: 203 tout.Uninit() 204 205 return 0 206