1*4882a593Smuzhiyun#!/usr/bin/python 2*4882a593Smuzhiyun# 3*4882a593Smuzhiyun# Copyright (C) 2016 Google, Inc 4*4882a593Smuzhiyun# Written by Simon Glass <sjg@chromium.org> 5*4882a593Smuzhiyun# 6*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0+ 7*4882a593Smuzhiyun# 8*4882a593Smuzhiyun 9*4882a593Smuzhiyunimport struct 10*4882a593Smuzhiyunimport sys 11*4882a593Smuzhiyun 12*4882a593Smuzhiyunimport fdt_util 13*4882a593Smuzhiyunimport libfdt 14*4882a593Smuzhiyun 15*4882a593Smuzhiyun# This deals with a device tree, presenting it as an assortment of Node and 16*4882a593Smuzhiyun# Prop objects, representing nodes and properties, respectively. This file 17*4882a593Smuzhiyun# contains the base classes and defines the high-level API. You can use 18*4882a593Smuzhiyun# FdtScan() as a convenience function to create and scan an Fdt. 19*4882a593Smuzhiyun 20*4882a593Smuzhiyun# This implementation uses a libfdt Python library to access the device tree, 21*4882a593Smuzhiyun# so it is fairly efficient. 22*4882a593Smuzhiyun 23*4882a593Smuzhiyun# A list of types we support 24*4882a593Smuzhiyun(TYPE_BYTE, TYPE_INT, TYPE_STRING, TYPE_BOOL, TYPE_INT64) = range(5) 25*4882a593Smuzhiyun 26*4882a593Smuzhiyundef CheckErr(errnum, msg): 27*4882a593Smuzhiyun if errnum: 28*4882a593Smuzhiyun raise ValueError('Error %d: %s: %s' % 29*4882a593Smuzhiyun (errnum, libfdt.fdt_strerror(errnum), msg)) 30*4882a593Smuzhiyun 31*4882a593Smuzhiyunclass Prop: 32*4882a593Smuzhiyun """A device tree property 33*4882a593Smuzhiyun 34*4882a593Smuzhiyun Properties: 35*4882a593Smuzhiyun name: Property name (as per the device tree) 36*4882a593Smuzhiyun value: Property value as a string of bytes, or a list of strings of 37*4882a593Smuzhiyun bytes 38*4882a593Smuzhiyun type: Value type 39*4882a593Smuzhiyun """ 40*4882a593Smuzhiyun def __init__(self, node, offset, name, bytes): 41*4882a593Smuzhiyun self._node = node 42*4882a593Smuzhiyun self._offset = offset 43*4882a593Smuzhiyun self.name = name 44*4882a593Smuzhiyun self.value = None 45*4882a593Smuzhiyun self.bytes = str(bytes) 46*4882a593Smuzhiyun if not bytes: 47*4882a593Smuzhiyun self.type = TYPE_BOOL 48*4882a593Smuzhiyun self.value = True 49*4882a593Smuzhiyun return 50*4882a593Smuzhiyun self.type, self.value = self.BytesToValue(bytes) 51*4882a593Smuzhiyun 52*4882a593Smuzhiyun def GetPhandle(self): 53*4882a593Smuzhiyun """Get a (single) phandle value from a property 54*4882a593Smuzhiyun 55*4882a593Smuzhiyun Gets the phandle valuie from a property and returns it as an integer 56*4882a593Smuzhiyun """ 57*4882a593Smuzhiyun return fdt_util.fdt32_to_cpu(self.value[:4]) 58*4882a593Smuzhiyun 59*4882a593Smuzhiyun def Widen(self, newprop): 60*4882a593Smuzhiyun """Figure out which property type is more general 61*4882a593Smuzhiyun 62*4882a593Smuzhiyun Given a current property and a new property, this function returns the 63*4882a593Smuzhiyun one that is less specific as to type. The less specific property will 64*4882a593Smuzhiyun be ble to represent the data in the more specific property. This is 65*4882a593Smuzhiyun used for things like: 66*4882a593Smuzhiyun 67*4882a593Smuzhiyun node1 { 68*4882a593Smuzhiyun compatible = "fred"; 69*4882a593Smuzhiyun value = <1>; 70*4882a593Smuzhiyun }; 71*4882a593Smuzhiyun node1 { 72*4882a593Smuzhiyun compatible = "fred"; 73*4882a593Smuzhiyun value = <1 2>; 74*4882a593Smuzhiyun }; 75*4882a593Smuzhiyun 76*4882a593Smuzhiyun He we want to use an int array for 'value'. The first property 77*4882a593Smuzhiyun suggests that a single int is enough, but the second one shows that 78*4882a593Smuzhiyun it is not. Calling this function with these two propertes would 79*4882a593Smuzhiyun update the current property to be like the second, since it is less 80*4882a593Smuzhiyun specific. 81*4882a593Smuzhiyun """ 82*4882a593Smuzhiyun if newprop.type < self.type: 83*4882a593Smuzhiyun self.type = newprop.type 84*4882a593Smuzhiyun 85*4882a593Smuzhiyun if type(newprop.value) == list and type(self.value) != list: 86*4882a593Smuzhiyun self.value = [self.value] 87*4882a593Smuzhiyun 88*4882a593Smuzhiyun if type(self.value) == list and len(newprop.value) > len(self.value): 89*4882a593Smuzhiyun val = self.GetEmpty(self.type) 90*4882a593Smuzhiyun while len(self.value) < len(newprop.value): 91*4882a593Smuzhiyun self.value.append(val) 92*4882a593Smuzhiyun 93*4882a593Smuzhiyun def BytesToValue(self, bytes): 94*4882a593Smuzhiyun """Converts a string of bytes into a type and value 95*4882a593Smuzhiyun 96*4882a593Smuzhiyun Args: 97*4882a593Smuzhiyun A string containing bytes 98*4882a593Smuzhiyun 99*4882a593Smuzhiyun Return: 100*4882a593Smuzhiyun A tuple: 101*4882a593Smuzhiyun Type of data 102*4882a593Smuzhiyun Data, either a single element or a list of elements. Each element 103*4882a593Smuzhiyun is one of: 104*4882a593Smuzhiyun TYPE_STRING: string value from the property 105*4882a593Smuzhiyun TYPE_INT: a byte-swapped integer stored as a 4-byte string 106*4882a593Smuzhiyun TYPE_BYTE: a byte stored as a single-byte string 107*4882a593Smuzhiyun """ 108*4882a593Smuzhiyun bytes = str(bytes) 109*4882a593Smuzhiyun size = len(bytes) 110*4882a593Smuzhiyun strings = bytes.split('\0') 111*4882a593Smuzhiyun is_string = True 112*4882a593Smuzhiyun count = len(strings) - 1 113*4882a593Smuzhiyun if count > 0 and not strings[-1]: 114*4882a593Smuzhiyun for string in strings[:-1]: 115*4882a593Smuzhiyun if not string: 116*4882a593Smuzhiyun is_string = False 117*4882a593Smuzhiyun break 118*4882a593Smuzhiyun for ch in string: 119*4882a593Smuzhiyun if ch < ' ' or ch > '~': 120*4882a593Smuzhiyun is_string = False 121*4882a593Smuzhiyun break 122*4882a593Smuzhiyun else: 123*4882a593Smuzhiyun is_string = False 124*4882a593Smuzhiyun if is_string: 125*4882a593Smuzhiyun if count == 1: 126*4882a593Smuzhiyun return TYPE_STRING, strings[0] 127*4882a593Smuzhiyun else: 128*4882a593Smuzhiyun return TYPE_STRING, strings[:-1] 129*4882a593Smuzhiyun if size % 4: 130*4882a593Smuzhiyun if size == 1: 131*4882a593Smuzhiyun return TYPE_BYTE, bytes[0] 132*4882a593Smuzhiyun else: 133*4882a593Smuzhiyun return TYPE_BYTE, list(bytes) 134*4882a593Smuzhiyun val = [] 135*4882a593Smuzhiyun for i in range(0, size, 4): 136*4882a593Smuzhiyun val.append(bytes[i:i + 4]) 137*4882a593Smuzhiyun if size == 4: 138*4882a593Smuzhiyun return TYPE_INT, val[0] 139*4882a593Smuzhiyun else: 140*4882a593Smuzhiyun return TYPE_INT, val 141*4882a593Smuzhiyun 142*4882a593Smuzhiyun def GetEmpty(self, type): 143*4882a593Smuzhiyun """Get an empty / zero value of the given type 144*4882a593Smuzhiyun 145*4882a593Smuzhiyun Returns: 146*4882a593Smuzhiyun A single value of the given type 147*4882a593Smuzhiyun """ 148*4882a593Smuzhiyun if type == TYPE_BYTE: 149*4882a593Smuzhiyun return chr(0) 150*4882a593Smuzhiyun elif type == TYPE_INT: 151*4882a593Smuzhiyun return struct.pack('<I', 0); 152*4882a593Smuzhiyun elif type == TYPE_STRING: 153*4882a593Smuzhiyun return '' 154*4882a593Smuzhiyun else: 155*4882a593Smuzhiyun return True 156*4882a593Smuzhiyun 157*4882a593Smuzhiyun def GetOffset(self): 158*4882a593Smuzhiyun """Get the offset of a property 159*4882a593Smuzhiyun 160*4882a593Smuzhiyun Returns: 161*4882a593Smuzhiyun The offset of the property (struct fdt_property) within the file 162*4882a593Smuzhiyun """ 163*4882a593Smuzhiyun return self._node._fdt.GetStructOffset(self._offset) 164*4882a593Smuzhiyun 165*4882a593Smuzhiyunclass Node: 166*4882a593Smuzhiyun """A device tree node 167*4882a593Smuzhiyun 168*4882a593Smuzhiyun Properties: 169*4882a593Smuzhiyun offset: Integer offset in the device tree 170*4882a593Smuzhiyun name: Device tree node tname 171*4882a593Smuzhiyun path: Full path to node, along with the node name itself 172*4882a593Smuzhiyun _fdt: Device tree object 173*4882a593Smuzhiyun subnodes: A list of subnodes for this node, each a Node object 174*4882a593Smuzhiyun props: A dict of properties for this node, each a Prop object. 175*4882a593Smuzhiyun Keyed by property name 176*4882a593Smuzhiyun """ 177*4882a593Smuzhiyun def __init__(self, fdt, parent, offset, name, path): 178*4882a593Smuzhiyun self._fdt = fdt 179*4882a593Smuzhiyun self.parent = parent 180*4882a593Smuzhiyun self._offset = offset 181*4882a593Smuzhiyun self.name = name 182*4882a593Smuzhiyun self.path = path 183*4882a593Smuzhiyun self.subnodes = [] 184*4882a593Smuzhiyun self.props = {} 185*4882a593Smuzhiyun 186*4882a593Smuzhiyun def _FindNode(self, name): 187*4882a593Smuzhiyun """Find a node given its name 188*4882a593Smuzhiyun 189*4882a593Smuzhiyun Args: 190*4882a593Smuzhiyun name: Node name to look for 191*4882a593Smuzhiyun Returns: 192*4882a593Smuzhiyun Node object if found, else None 193*4882a593Smuzhiyun """ 194*4882a593Smuzhiyun for subnode in self.subnodes: 195*4882a593Smuzhiyun if subnode.name == name: 196*4882a593Smuzhiyun return subnode 197*4882a593Smuzhiyun return None 198*4882a593Smuzhiyun 199*4882a593Smuzhiyun def Offset(self): 200*4882a593Smuzhiyun """Returns the offset of a node, after checking the cache 201*4882a593Smuzhiyun 202*4882a593Smuzhiyun This should be used instead of self._offset directly, to ensure that 203*4882a593Smuzhiyun the cache does not contain invalid offsets. 204*4882a593Smuzhiyun """ 205*4882a593Smuzhiyun self._fdt.CheckCache() 206*4882a593Smuzhiyun return self._offset 207*4882a593Smuzhiyun 208*4882a593Smuzhiyun def Scan(self): 209*4882a593Smuzhiyun """Scan a node's properties and subnodes 210*4882a593Smuzhiyun 211*4882a593Smuzhiyun This fills in the props and subnodes properties, recursively 212*4882a593Smuzhiyun searching into subnodes so that the entire tree is built. 213*4882a593Smuzhiyun """ 214*4882a593Smuzhiyun self.props = self._fdt.GetProps(self) 215*4882a593Smuzhiyun phandle = self.props.get('phandle') 216*4882a593Smuzhiyun if phandle: 217*4882a593Smuzhiyun val = fdt_util.fdt32_to_cpu(phandle.value) 218*4882a593Smuzhiyun self._fdt.phandle_to_node[val] = self 219*4882a593Smuzhiyun 220*4882a593Smuzhiyun offset = libfdt.fdt_first_subnode(self._fdt.GetFdt(), self.Offset()) 221*4882a593Smuzhiyun while offset >= 0: 222*4882a593Smuzhiyun sep = '' if self.path[-1] == '/' else '/' 223*4882a593Smuzhiyun name = self._fdt._fdt_obj.get_name(offset) 224*4882a593Smuzhiyun path = self.path + sep + name 225*4882a593Smuzhiyun node = Node(self._fdt, self, offset, name, path) 226*4882a593Smuzhiyun self.subnodes.append(node) 227*4882a593Smuzhiyun 228*4882a593Smuzhiyun node.Scan() 229*4882a593Smuzhiyun offset = libfdt.fdt_next_subnode(self._fdt.GetFdt(), offset) 230*4882a593Smuzhiyun 231*4882a593Smuzhiyun def Refresh(self, my_offset): 232*4882a593Smuzhiyun """Fix up the _offset for each node, recursively 233*4882a593Smuzhiyun 234*4882a593Smuzhiyun Note: This does not take account of property offsets - these will not 235*4882a593Smuzhiyun be updated. 236*4882a593Smuzhiyun """ 237*4882a593Smuzhiyun if self._offset != my_offset: 238*4882a593Smuzhiyun #print '%s: %d -> %d\n' % (self.path, self._offset, my_offset) 239*4882a593Smuzhiyun self._offset = my_offset 240*4882a593Smuzhiyun offset = libfdt.fdt_first_subnode(self._fdt.GetFdt(), self._offset) 241*4882a593Smuzhiyun for subnode in self.subnodes: 242*4882a593Smuzhiyun subnode.Refresh(offset) 243*4882a593Smuzhiyun offset = libfdt.fdt_next_subnode(self._fdt.GetFdt(), offset) 244*4882a593Smuzhiyun 245*4882a593Smuzhiyun def DeleteProp(self, prop_name): 246*4882a593Smuzhiyun """Delete a property of a node 247*4882a593Smuzhiyun 248*4882a593Smuzhiyun The property is deleted and the offset cache is invalidated. 249*4882a593Smuzhiyun 250*4882a593Smuzhiyun Args: 251*4882a593Smuzhiyun prop_name: Name of the property to delete 252*4882a593Smuzhiyun Raises: 253*4882a593Smuzhiyun ValueError if the property does not exist 254*4882a593Smuzhiyun """ 255*4882a593Smuzhiyun CheckErr(libfdt.fdt_delprop(self._fdt.GetFdt(), self.Offset(), prop_name), 256*4882a593Smuzhiyun "Node '%s': delete property: '%s'" % (self.path, prop_name)) 257*4882a593Smuzhiyun del self.props[prop_name] 258*4882a593Smuzhiyun self._fdt.Invalidate() 259*4882a593Smuzhiyun 260*4882a593Smuzhiyunclass Fdt: 261*4882a593Smuzhiyun """Provides simple access to a flat device tree blob using libfdts. 262*4882a593Smuzhiyun 263*4882a593Smuzhiyun Properties: 264*4882a593Smuzhiyun fname: Filename of fdt 265*4882a593Smuzhiyun _root: Root of device tree (a Node object) 266*4882a593Smuzhiyun """ 267*4882a593Smuzhiyun def __init__(self, fname): 268*4882a593Smuzhiyun self._fname = fname 269*4882a593Smuzhiyun self._cached_offsets = False 270*4882a593Smuzhiyun self.phandle_to_node = {} 271*4882a593Smuzhiyun if self._fname: 272*4882a593Smuzhiyun self._fname = fdt_util.EnsureCompiled(self._fname) 273*4882a593Smuzhiyun 274*4882a593Smuzhiyun with open(self._fname) as fd: 275*4882a593Smuzhiyun self._fdt = bytearray(fd.read()) 276*4882a593Smuzhiyun self._fdt_obj = libfdt.Fdt(self._fdt) 277*4882a593Smuzhiyun 278*4882a593Smuzhiyun def Scan(self, root='/'): 279*4882a593Smuzhiyun """Scan a device tree, building up a tree of Node objects 280*4882a593Smuzhiyun 281*4882a593Smuzhiyun This fills in the self._root property 282*4882a593Smuzhiyun 283*4882a593Smuzhiyun Args: 284*4882a593Smuzhiyun root: Ignored 285*4882a593Smuzhiyun 286*4882a593Smuzhiyun TODO(sjg@chromium.org): Implement the 'root' parameter 287*4882a593Smuzhiyun """ 288*4882a593Smuzhiyun self._root = self.Node(self, None, 0, '/', '/') 289*4882a593Smuzhiyun self._root.Scan() 290*4882a593Smuzhiyun 291*4882a593Smuzhiyun def GetRoot(self): 292*4882a593Smuzhiyun """Get the root Node of the device tree 293*4882a593Smuzhiyun 294*4882a593Smuzhiyun Returns: 295*4882a593Smuzhiyun The root Node object 296*4882a593Smuzhiyun """ 297*4882a593Smuzhiyun return self._root 298*4882a593Smuzhiyun 299*4882a593Smuzhiyun def GetNode(self, path): 300*4882a593Smuzhiyun """Look up a node from its path 301*4882a593Smuzhiyun 302*4882a593Smuzhiyun Args: 303*4882a593Smuzhiyun path: Path to look up, e.g. '/microcode/update@0' 304*4882a593Smuzhiyun Returns: 305*4882a593Smuzhiyun Node object, or None if not found 306*4882a593Smuzhiyun """ 307*4882a593Smuzhiyun node = self._root 308*4882a593Smuzhiyun for part in path.split('/')[1:]: 309*4882a593Smuzhiyun node = node._FindNode(part) 310*4882a593Smuzhiyun if not node: 311*4882a593Smuzhiyun return None 312*4882a593Smuzhiyun return node 313*4882a593Smuzhiyun 314*4882a593Smuzhiyun def Flush(self): 315*4882a593Smuzhiyun """Flush device tree changes back to the file 316*4882a593Smuzhiyun 317*4882a593Smuzhiyun If the device tree has changed in memory, write it back to the file. 318*4882a593Smuzhiyun """ 319*4882a593Smuzhiyun with open(self._fname, 'wb') as fd: 320*4882a593Smuzhiyun fd.write(self._fdt) 321*4882a593Smuzhiyun 322*4882a593Smuzhiyun def Pack(self): 323*4882a593Smuzhiyun """Pack the device tree down to its minimum size 324*4882a593Smuzhiyun 325*4882a593Smuzhiyun When nodes and properties shrink or are deleted, wasted space can 326*4882a593Smuzhiyun build up in the device tree binary. 327*4882a593Smuzhiyun """ 328*4882a593Smuzhiyun CheckErr(libfdt.fdt_pack(self._fdt), 'pack') 329*4882a593Smuzhiyun fdt_len = libfdt.fdt_totalsize(self._fdt) 330*4882a593Smuzhiyun del self._fdt[fdt_len:] 331*4882a593Smuzhiyun 332*4882a593Smuzhiyun def GetFdt(self): 333*4882a593Smuzhiyun """Get the contents of the FDT 334*4882a593Smuzhiyun 335*4882a593Smuzhiyun Returns: 336*4882a593Smuzhiyun The FDT contents as a string of bytes 337*4882a593Smuzhiyun """ 338*4882a593Smuzhiyun return self._fdt 339*4882a593Smuzhiyun 340*4882a593Smuzhiyun def CheckErr(errnum, msg): 341*4882a593Smuzhiyun if errnum: 342*4882a593Smuzhiyun raise ValueError('Error %d: %s: %s' % 343*4882a593Smuzhiyun (errnum, libfdt.fdt_strerror(errnum), msg)) 344*4882a593Smuzhiyun 345*4882a593Smuzhiyun 346*4882a593Smuzhiyun def GetProps(self, node): 347*4882a593Smuzhiyun """Get all properties from a node. 348*4882a593Smuzhiyun 349*4882a593Smuzhiyun Args: 350*4882a593Smuzhiyun node: Full path to node name to look in. 351*4882a593Smuzhiyun 352*4882a593Smuzhiyun Returns: 353*4882a593Smuzhiyun A dictionary containing all the properties, indexed by node name. 354*4882a593Smuzhiyun The entries are Prop objects. 355*4882a593Smuzhiyun 356*4882a593Smuzhiyun Raises: 357*4882a593Smuzhiyun ValueError: if the node does not exist. 358*4882a593Smuzhiyun """ 359*4882a593Smuzhiyun props_dict = {} 360*4882a593Smuzhiyun poffset = libfdt.fdt_first_property_offset(self._fdt, node._offset) 361*4882a593Smuzhiyun while poffset >= 0: 362*4882a593Smuzhiyun p = self._fdt_obj.get_property_by_offset(poffset) 363*4882a593Smuzhiyun prop = Prop(node, poffset, p.name, p.value) 364*4882a593Smuzhiyun props_dict[prop.name] = prop 365*4882a593Smuzhiyun 366*4882a593Smuzhiyun poffset = libfdt.fdt_next_property_offset(self._fdt, poffset) 367*4882a593Smuzhiyun return props_dict 368*4882a593Smuzhiyun 369*4882a593Smuzhiyun def Invalidate(self): 370*4882a593Smuzhiyun """Mark our offset cache as invalid""" 371*4882a593Smuzhiyun self._cached_offsets = False 372*4882a593Smuzhiyun 373*4882a593Smuzhiyun def CheckCache(self): 374*4882a593Smuzhiyun """Refresh the offset cache if needed""" 375*4882a593Smuzhiyun if self._cached_offsets: 376*4882a593Smuzhiyun return 377*4882a593Smuzhiyun self.Refresh() 378*4882a593Smuzhiyun self._cached_offsets = True 379*4882a593Smuzhiyun 380*4882a593Smuzhiyun def Refresh(self): 381*4882a593Smuzhiyun """Refresh the offset cache""" 382*4882a593Smuzhiyun self._root.Refresh(0) 383*4882a593Smuzhiyun 384*4882a593Smuzhiyun def GetStructOffset(self, offset): 385*4882a593Smuzhiyun """Get the file offset of a given struct offset 386*4882a593Smuzhiyun 387*4882a593Smuzhiyun Args: 388*4882a593Smuzhiyun offset: Offset within the 'struct' region of the device tree 389*4882a593Smuzhiyun Returns: 390*4882a593Smuzhiyun Position of @offset within the device tree binary 391*4882a593Smuzhiyun """ 392*4882a593Smuzhiyun return libfdt.fdt_off_dt_struct(self._fdt) + offset 393*4882a593Smuzhiyun 394*4882a593Smuzhiyun @classmethod 395*4882a593Smuzhiyun def Node(self, fdt, parent, offset, name, path): 396*4882a593Smuzhiyun """Create a new node 397*4882a593Smuzhiyun 398*4882a593Smuzhiyun This is used by Fdt.Scan() to create a new node using the correct 399*4882a593Smuzhiyun class. 400*4882a593Smuzhiyun 401*4882a593Smuzhiyun Args: 402*4882a593Smuzhiyun fdt: Fdt object 403*4882a593Smuzhiyun parent: Parent node, or None if this is the root node 404*4882a593Smuzhiyun offset: Offset of node 405*4882a593Smuzhiyun name: Node name 406*4882a593Smuzhiyun path: Full path to node 407*4882a593Smuzhiyun """ 408*4882a593Smuzhiyun node = Node(fdt, parent, offset, name, path) 409*4882a593Smuzhiyun return node 410*4882a593Smuzhiyun 411*4882a593Smuzhiyundef FdtScan(fname): 412*4882a593Smuzhiyun """Returns a new Fdt object from the implementation we are using""" 413*4882a593Smuzhiyun dtb = Fdt(fname) 414*4882a593Smuzhiyun dtb.Scan() 415*4882a593Smuzhiyun return dtb 416