1# SPDX-License-Identifier: GPL-2.0+ 2# Copyright 2018 Google, Inc 3# Written by Simon Glass <sjg@chromium.org> 4# 5# Holds and modifies the state information held by binman 6# 7 8import hashlib 9import re 10from sets import Set 11 12import os 13import tools 14 15# Records the device-tree files known to binman, keyed by filename (e.g. 16# 'u-boot-spl.dtb') 17fdt_files = {} 18 19# Arguments passed to binman to provide arguments to entries 20entry_args = {} 21 22# True to use fake device-tree files for testing (see U_BOOT_DTB_DATA in 23# ftest.py) 24use_fake_dtb = False 25 26# Set of all device tree files references by images 27fdt_set = Set() 28 29# Same as above, but excluding the main one 30fdt_subset = Set() 31 32# The DTB which contains the full image information 33main_dtb = None 34 35def GetFdt(fname): 36 """Get the Fdt object for a particular device-tree filename 37 38 Binman keeps track of at least one device-tree file called u-boot.dtb but 39 can also have others (e.g. for SPL). This function looks up the given 40 filename and returns the associated Fdt object. 41 42 Args: 43 fname: Filename to look up (e.g. 'u-boot.dtb'). 44 45 Returns: 46 Fdt object associated with the filename 47 """ 48 return fdt_files[fname] 49 50def GetFdtPath(fname): 51 """Get the full pathname of a particular Fdt object 52 53 Similar to GetFdt() but returns the pathname associated with the Fdt. 54 55 Args: 56 fname: Filename to look up (e.g. 'u-boot.dtb'). 57 58 Returns: 59 Full path name to the associated Fdt 60 """ 61 return fdt_files[fname]._fname 62 63def GetFdtContents(fname): 64 """Looks up the FDT pathname and contents 65 66 This is used to obtain the Fdt pathname and contents when needed by an 67 entry. It supports a 'fake' dtb, allowing tests to substitute test data for 68 the real dtb. 69 70 Args: 71 fname: Filename to look up (e.g. 'u-boot.dtb'). 72 73 Returns: 74 tuple: 75 pathname to Fdt 76 Fdt data (as bytes) 77 """ 78 if fname in fdt_files and not use_fake_dtb: 79 pathname = GetFdtPath(fname) 80 data = GetFdt(fname).GetContents() 81 else: 82 pathname = tools.GetInputFilename(fname) 83 data = tools.ReadFile(pathname) 84 return pathname, data 85 86def SetEntryArgs(args): 87 """Set the value of the entry args 88 89 This sets up the entry_args dict which is used to supply entry arguments to 90 entries. 91 92 Args: 93 args: List of entry arguments, each in the format "name=value" 94 """ 95 global entry_args 96 97 entry_args = {} 98 if args: 99 for arg in args: 100 m = re.match('([^=]*)=(.*)', arg) 101 if not m: 102 raise ValueError("Invalid entry arguemnt '%s'" % arg) 103 entry_args[m.group(1)] = m.group(2) 104 105def GetEntryArg(name): 106 """Get the value of an entry argument 107 108 Args: 109 name: Name of argument to retrieve 110 111 Returns: 112 String value of argument 113 """ 114 return entry_args.get(name) 115 116def Prepare(images, dtb): 117 """Get device tree files ready for use 118 119 This sets up a set of device tree files that can be retrieved by GetFdts(). 120 At present there is only one, that for U-Boot proper. 121 122 Args: 123 images: List of images being used 124 dtb: Main dtb 125 """ 126 global fdt_set, fdt_subset, fdt_files, main_dtb 127 # Import these here in case libfdt.py is not available, in which case 128 # the above help option still works. 129 import fdt 130 import fdt_util 131 132 # If we are updating the DTBs we need to put these updated versions 133 # where Entry_blob_dtb can find them. We can ignore 'u-boot.dtb' 134 # since it is assumed to be the one passed in with options.dt, and 135 # was handled just above. 136 main_dtb = dtb 137 fdt_files.clear() 138 fdt_files['u-boot.dtb'] = dtb 139 fdt_subset = Set() 140 if not use_fake_dtb: 141 for image in images.values(): 142 fdt_subset.update(image.GetFdtSet()) 143 fdt_subset.discard('u-boot.dtb') 144 for other_fname in fdt_subset: 145 infile = tools.GetInputFilename(other_fname) 146 other_fname_dtb = fdt_util.EnsureCompiled(infile) 147 out_fname = tools.GetOutputFilename('%s.out' % 148 os.path.split(other_fname)[1]) 149 tools.WriteFile(out_fname, tools.ReadFile(other_fname_dtb)) 150 other_dtb = fdt.FdtScan(out_fname) 151 fdt_files[other_fname] = other_dtb 152 153def GetFdts(): 154 """Yield all device tree files being used by binman 155 156 Yields: 157 Device trees being used (U-Boot proper, SPL, TPL) 158 """ 159 yield main_dtb 160 for other_fname in fdt_subset: 161 yield fdt_files[other_fname] 162 163def GetUpdateNodes(node): 164 """Yield all the nodes that need to be updated in all device trees 165 166 The property referenced by this node is added to any device trees which 167 have the given node. Due to removable of unwanted notes, SPL and TPL may 168 not have this node. 169 170 Args: 171 node: Node object in the main device tree to look up 172 173 Yields: 174 Node objects in each device tree that is in use (U-Boot proper, which 175 is node, SPL and TPL) 176 """ 177 yield node 178 for dtb in fdt_files.values(): 179 if dtb != node.GetFdt(): 180 other_node = dtb.GetNode(node.path) 181 if other_node: 182 yield other_node 183 184def AddZeroProp(node, prop): 185 """Add a new property to affected device trees with an integer value of 0. 186 187 Args: 188 prop_name: Name of property 189 """ 190 for n in GetUpdateNodes(node): 191 n.AddZeroProp(prop) 192 193def AddSubnode(node, name): 194 """Add a new subnode to a node in affected device trees 195 196 Args: 197 node: Node to add to 198 name: name of node to add 199 200 Returns: 201 New subnode that was created in main tree 202 """ 203 first = None 204 for n in GetUpdateNodes(node): 205 subnode = n.AddSubnode(name) 206 if not first: 207 first = subnode 208 return first 209 210def AddString(node, prop, value): 211 """Add a new string property to affected device trees 212 213 Args: 214 prop_name: Name of property 215 value: String value (which will be \0-terminated in the DT) 216 """ 217 for n in GetUpdateNodes(node): 218 n.AddString(prop, value) 219 220def SetInt(node, prop, value): 221 """Update an integer property in affected device trees with an integer value 222 223 This is not allowed to change the size of the FDT. 224 225 Args: 226 prop_name: Name of property 227 """ 228 for n in GetUpdateNodes(node): 229 n.SetInt(prop, value) 230 231def CheckAddHashProp(node): 232 hash_node = node.FindNode('hash') 233 if hash_node: 234 algo = hash_node.props.get('algo') 235 if not algo: 236 return "Missing 'algo' property for hash node" 237 if algo.value == 'sha256': 238 size = 32 239 else: 240 return "Unknown hash algorithm '%s'" % algo 241 for n in GetUpdateNodes(hash_node): 242 n.AddEmptyProp('value', size) 243 244def CheckSetHashValue(node, get_data_func): 245 hash_node = node.FindNode('hash') 246 if hash_node: 247 algo = hash_node.props.get('algo').value 248 if algo == 'sha256': 249 m = hashlib.sha256() 250 m.update(get_data_func()) 251 data = m.digest() 252 for n in GetUpdateNodes(hash_node): 253 n.SetData('value', data) 254