X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;ds=sidebyside;f=tools%2Fdtoc%2Fdtoc.py;h=8e05b434318ac46597dc8add71dd20e662da13c2;hb=83a45187715e719bfe520b9ca0373bd8b5bee8aa;hp=c0ab13ad34fa279e78fb386e65262d7538caa072;hpb=2cce586651bbeb0489c6cc6f04489f65bd039b9e;p=oweals%2Fu-boot.git diff --git a/tools/dtoc/dtoc.py b/tools/dtoc/dtoc.py index c0ab13ad34..8e05b43431 100755 --- a/tools/dtoc/dtoc.py +++ b/tools/dtoc/dtoc.py @@ -1,450 +1,114 @@ -#!/usr/bin/python +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0+ # # Copyright (C) 2016 Google, Inc # Written by Simon Glass # -# SPDX-License-Identifier: GPL-2.0+ -# -import copy -from optparse import OptionError, OptionParser +"""Device tree to C tool + +This tool converts a device tree binary file (.dtb) into two C files. The +indent is to allow a C program to access data from the device tree without +having to link against libfdt. By putting the data from the device tree into +C structures, normal C code can be used. This helps to reduce the size of the +compiled program. + +Dtoc produces two output files: + + dt-structs.h - contains struct definitions + dt-platdata.c - contains data from the device tree using the struct + definitions, as well as U-Boot driver definitions. + +This tool is used in U-Boot to provide device tree data to SPL without +increasing the code size of SPL. This supports the CONFIG_SPL_OF_PLATDATA +options. For more information about the use of this options and tool please +see doc/driver-model/of-plat.rst +""" + +from optparse import OptionParser import os -import struct import sys +import unittest # Bring in the patman libraries our_path = os.path.dirname(os.path.realpath(__file__)) sys.path.append(os.path.join(our_path, '../patman')) -import fdt -import fdt_util - -# When we see these properties we ignore them - i.e. do not create a structure member -PROP_IGNORE_LIST = [ - '#address-cells', - '#gpio-cells', - '#size-cells', - 'compatible', - 'linux,phandle', - "status", - 'phandle', - 'u-boot,dm-pre-reloc', - 'u-boot,dm-tpl', - 'u-boot,dm-spl', -] - -# C type declarations for the tyues we support -TYPE_NAMES = { - fdt.TYPE_INT: 'fdt32_t', - fdt.TYPE_BYTE: 'unsigned char', - fdt.TYPE_STRING: 'const char *', - fdt.TYPE_BOOL: 'bool', -}; +# Bring in the libfdt module +sys.path.insert(0, 'scripts/dtc/pylibfdt') +sys.path.insert(0, os.path.join(our_path, + '../../build-sandbox_spl/scripts/dtc/pylibfdt')) -STRUCT_PREFIX = 'dtd_' -VAL_PREFIX = 'dtv_' +import dtb_platdata +import test_util -def Conv_name_to_c(name): - """Convert a device-tree name to a C identifier +def run_tests(args): + """Run all the test we have for dtoc Args: - name: Name to convert - Return: - String containing the C version of this name + args: List of positional args provided to dtoc. This can hold a test + name to execute (as in 'dtoc -t test_empty_file', for example) """ - str = name.replace('@', '_at_') - str = str.replace('-', '_') - str = str.replace(',', '_') - str = str.replace('.', '_') - str = str.replace('/', '__') - return str - -def TabTo(num_tabs, str): - if len(str) >= num_tabs * 8: - return str + ' ' - return str + '\t' * (num_tabs - len(str) // 8) - -class DtbPlatdata: - """Provide a means to convert device tree binary data to platform data - - The output of this process is C structures which can be used in space- - constrained encvironments where the ~3KB code overhead of device tree - code is not affordable. - - Properties: - fdt: Fdt object, referencing the device tree - _dtb_fname: Filename of the input device tree binary file - _valid_nodes: A list of Node object with compatible strings - _options: Command-line options - _phandle_node: A dict of nodes indexed by phandle number (1, 2...) - _outfile: The current output file (sys.stdout or a real file) - _lines: Stashed list of output lines for outputting in the future - _phandle_node: A dict of Nodes indexed by phandle (an integer) - """ - def __init__(self, dtb_fname, options): - self._dtb_fname = dtb_fname - self._valid_nodes = None - self._options = options - self._phandle_node = {} - self._outfile = None - self._lines = [] - self._aliases = {} - - def SetupOutput(self, fname): - """Set up the output destination - - Once this is done, future calls to self.Out() will output to this - file. - - Args: - fname: Filename to send output to, or '-' for stdout - """ - if fname == '-': - self._outfile = sys.stdout - else: - self._outfile = open(fname, 'w') - - def Out(self, str): - """Output a string to the output file - - Args: - str: String to output - """ - self._outfile.write(str) - - def Buf(self, str): - """Buffer up a string to send later - - Args: - str: String to add to our 'buffer' list - """ - self._lines.append(str) - - def GetBuf(self): - """Get the contents of the output buffer, and clear it - - Returns: - The output buffer, which is then cleared for future use - """ - lines = self._lines - self._lines = [] - return lines - - def GetValue(self, type, value): - """Get a value as a C expression - - For integers this returns a byte-swapped (little-endian) hex string - For bytes this returns a hex string, e.g. 0x12 - For strings this returns a literal string enclosed in quotes - For booleans this return 'true' - - Args: - type: Data type (fdt_util) - value: Data value, as a string of bytes - """ - if type == fdt.TYPE_INT: - return '%#x' % fdt_util.fdt32_to_cpu(value) - elif type == fdt.TYPE_BYTE: - return '%#x' % ord(value[0]) - elif type == fdt.TYPE_STRING: - return '"%s"' % value - elif type == fdt.TYPE_BOOL: - return 'true' - - def GetCompatName(self, node): - """Get a node's first compatible string as a C identifier - - Args: - node: Node object to check - Return: - C identifier for the first compatible string - """ - compat = node.props['compatible'].value - aliases = [] - if type(compat) == list: - compat, aliases = compat[0], compat[1:] - return Conv_name_to_c(compat), [Conv_name_to_c(a) for a in aliases] - - def ScanDtb(self): - """Scan the device tree to obtain a tree of notes and properties - - Once this is done, self.fdt.GetRoot() can be called to obtain the - device tree root node, and progress from there. - """ - self.fdt = fdt.FdtScan(self._dtb_fname) - - def ScanNode(self, root): - for node in root.subnodes: - if 'compatible' in node.props: - status = node.props.get('status') - if (not options.include_disabled and not status or - status.value != 'disabled'): - self._valid_nodes.append(node) - phandle_prop = node.props.get('phandle') - if phandle_prop: - phandle = phandle_prop.GetPhandle() - self._phandle_node[phandle] = node - - # recurse to handle any subnodes - self.ScanNode(node); - - def ScanTree(self): - """Scan the device tree for useful information - - This fills in the following properties: - _phandle_node: A dict of Nodes indexed by phandle (an integer) - _valid_nodes: A list of nodes we wish to consider include in the - platform data - """ - self._phandle_node = {} - self._valid_nodes = [] - return self.ScanNode(self.fdt.GetRoot()); - - for node in self.fdt.GetRoot().subnodes: - if 'compatible' in node.props: - status = node.props.get('status') - if (not options.include_disabled and not status or - status.value != 'disabled'): - node_list.append(node) - phandle_prop = node.props.get('phandle') - if phandle_prop: - phandle = phandle_prop.GetPhandle() - self._phandle_node[phandle] = node - - self._valid_nodes = node_list - - def IsPhandle(self, prop): - """Check if a node contains phandles - - We have no reliable way of detecting whether a node uses a phandle - or not. As an interim measure, use a list of known property names. - - Args: - prop: Prop object to check - Return: - True if the object value contains phandles, else False - """ - if prop.name in ['clocks']: - return True - return False - - def ScanStructs(self): - """Scan the device tree building up the C structures we will use. - - Build a dict keyed by C struct name containing a dict of Prop - object for each struct field (keyed by property name). Where the - same struct appears multiple times, try to use the 'widest' - property, i.e. the one with a type which can express all others. - - Once the widest property is determined, all other properties are - updated to match that width. - """ - structs = {} - for node in self._valid_nodes: - node_name, _ = self.GetCompatName(node) - fields = {} - - # Get a list of all the valid properties in this node. - for name, prop in node.props.items(): - if name not in PROP_IGNORE_LIST and name[0] != '#': - fields[name] = copy.deepcopy(prop) - - # If we've seen this node_name before, update the existing struct. - if node_name in structs: - struct = structs[node_name] - for name, prop in fields.items(): - oldprop = struct.get(name) - if oldprop: - oldprop.Widen(prop) - else: - struct[name] = prop - - # Otherwise store this as a new struct. - else: - structs[node_name] = fields - - upto = 0 - for node in self._valid_nodes: - node_name, _ = self.GetCompatName(node) - struct = structs[node_name] - for name, prop in node.props.items(): - if name not in PROP_IGNORE_LIST and name[0] != '#': - prop.Widen(struct[name]) - upto += 1 - - struct_name, aliases = self.GetCompatName(node) - for alias in aliases: - self._aliases[alias] = struct_name - - return structs - - def ScanPhandles(self): - """Figure out what phandles each node uses - - We need to be careful when outputing nodes that use phandles since - they must come after the declaration of the phandles in the C file. - Otherwise we get a compiler error since the phandle struct is not yet - declared. - - This function adds to each node a list of phandle nodes that the node - depends on. This allows us to output things in the right order. - """ - for node in self._valid_nodes: - node.phandles = set() - for pname, prop in node.props.items(): - if pname in PROP_IGNORE_LIST or pname[0] == '#': - continue - if type(prop.value) == list: - if self.IsPhandle(prop): - # Process the list as pairs of (phandle, id) - it = iter(prop.value) - for phandle_cell, id_cell in zip(it, it): - phandle = fdt_util.fdt32_to_cpu(phandle_cell) - id = fdt_util.fdt32_to_cpu(id_cell) - target_node = self._phandle_node[phandle] - node.phandles.add(target_node) - - - def GenerateStructs(self, structs): - """Generate struct defintions for the platform data - - This writes out the body of a header file consisting of structure - definitions for node in self._valid_nodes. See the documentation in - README.of-plat for more information. - """ - self.Out('#include \n') - self.Out('#include \n') - - # Output the struct definition - for name in sorted(structs): - self.Out('struct %s%s {\n' % (STRUCT_PREFIX, name)); - for pname in sorted(structs[name]): - prop = structs[name][pname] - if self.IsPhandle(prop): - # For phandles, include a reference to the target - self.Out('\t%s%s[%d]' % (TabTo(2, 'struct phandle_2_cell'), - Conv_name_to_c(prop.name), - len(prop.value) / 2)) - else: - ptype = TYPE_NAMES[prop.type] - self.Out('\t%s%s' % (TabTo(2, ptype), - Conv_name_to_c(prop.name))) - if type(prop.value) == list: - self.Out('[%d]' % len(prop.value)) - self.Out(';\n') - self.Out('};\n') - - for alias, struct_name in self._aliases.iteritems(): - self.Out('#define %s%s %s%s\n'% (STRUCT_PREFIX, alias, - STRUCT_PREFIX, struct_name)) - - def OutputNode(self, node): - """Output the C code for a node - - Args: - node: node to output - """ - struct_name, _ = self.GetCompatName(node) - var_name = Conv_name_to_c(node.name) - self.Buf('static struct %s%s %s%s = {\n' % - (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name)) - for pname, prop in node.props.items(): - if pname in PROP_IGNORE_LIST or pname[0] == '#': + import test_dtoc + + result = unittest.TestResult() + sys.argv = [sys.argv[0]] + test_name = args and args[0] or None + for module in (test_dtoc.TestDtoc,): + if test_name: + try: + suite = unittest.TestLoader().loadTestsFromName(test_name, module) + except AttributeError: continue - ptype = TYPE_NAMES[prop.type] - member_name = Conv_name_to_c(prop.name) - self.Buf('\t%s= ' % TabTo(3, '.' + member_name)) - - # Special handling for lists - if type(prop.value) == list: - self.Buf('{') - vals = [] - # For phandles, output a reference to the platform data - # of the target node. - if self.IsPhandle(prop): - # Process the list as pairs of (phandle, id) - it = iter(prop.value) - for phandle_cell, id_cell in zip(it, it): - phandle = fdt_util.fdt32_to_cpu(phandle_cell) - id = fdt_util.fdt32_to_cpu(id_cell) - target_node = self._phandle_node[phandle] - name = Conv_name_to_c(target_node.name) - vals.append('{&%s%s, %d}' % (VAL_PREFIX, name, id)) - else: - for val in prop.value: - vals.append(self.GetValue(prop.type, val)) - self.Buf(', '.join(vals)) - self.Buf('}') - else: - self.Buf(self.GetValue(prop.type, prop.value)) - self.Buf(',\n') - self.Buf('};\n') - - # Add a device declaration - self.Buf('U_BOOT_DEVICE(%s) = {\n' % var_name) - self.Buf('\t.name\t\t= "%s",\n' % struct_name) - self.Buf('\t.platdata\t= &%s%s,\n' % (VAL_PREFIX, var_name)) - self.Buf('\t.platdata_size\t= sizeof(%s%s),\n' % - (VAL_PREFIX, var_name)) - self.Buf('};\n') - self.Buf('\n') - - self.Out(''.join(self.GetBuf())) - - def GenerateTables(self): - """Generate device defintions for the platform data - - This writes out C platform data initialisation data and - U_BOOT_DEVICE() declarations for each valid node. Where a node has - multiple compatible strings, a #define is used to make them equivalent. + else: + suite = unittest.TestLoader().loadTestsFromTestCase(module) + suite.run(result) - See the documentation in doc/driver-model/of-plat.txt for more - information. - """ - self.Out('#include \n') - self.Out('#include \n') - self.Out('#include \n') - self.Out('\n') - nodes_to_output = list(self._valid_nodes) + print(result) + for _, err in result.errors: + print(err) + for _, err in result.failures: + print(err) + if result.errors or result.failures: + print('dtoc tests FAILED') + return 1 + return 0 - # Keep outputing nodes until there is none left - while nodes_to_output: - node = nodes_to_output[0] - # Output all the node's dependencies first - for req_node in node.phandles: - if req_node in nodes_to_output: - self.OutputNode(req_node) - nodes_to_output.remove(req_node) - self.OutputNode(node) - nodes_to_output.remove(node) +def RunTestCoverage(): + """Run the tests and check that we get 100% coverage""" + sys.argv = [sys.argv[0]] + test_util.RunTestCoverage('tools/dtoc/dtoc.py', '/dtoc.py', + ['tools/patman/*.py', '*/fdt*', '*test*'], options.build_dir) -if __name__ != "__main__": - pass +if __name__ != '__main__': + sys.exit(1) parser = OptionParser() +parser.add_option('-B', '--build-dir', type='string', default='b', + help='Directory containing the build output') parser.add_option('-d', '--dtb-file', action='store', help='Specify the .dtb input file') parser.add_option('--include-disabled', action='store_true', help='Include disabled nodes') parser.add_option('-o', '--output', action='store', default='-', help='Select output filename') +parser.add_option('-P', '--processes', type=int, + help='set number of processes to use for running tests') +parser.add_option('-t', '--test', action='store_true', dest='test', + default=False, help='run tests') +parser.add_option('-T', '--test-coverage', action='store_true', + default=False, help='run tests and check for 100% coverage') (options, args) = parser.parse_args() -if not args: - raise ValueError('Please specify a command: struct, platdata') +# Run our meagre tests +if options.test: + ret_code = run_tests(args) + sys.exit(ret_code) -plat = DtbPlatdata(options.dtb_file, options) -plat.ScanDtb() -plat.ScanTree() -plat.SetupOutput(options.output) -structs = plat.ScanStructs() -plat.ScanPhandles() +elif options.test_coverage: + RunTestCoverage() -for cmd in args[0].split(','): - if cmd == 'struct': - plat.GenerateStructs(structs) - elif cmd == 'platdata': - plat.GenerateTables() - else: - raise ValueError("Unknown command '%s': (use: struct, platdata)" % cmd) +else: + dtb_platdata.run_steps(args, options.dtb_file, options.include_disabled, + options.output)