blob: a8a5e979438f0b58ddaa18b0fa696b61442b5dba [file] [log] [blame]
#!/usr/bin/python
# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""This library provides basic access to an fdt blob."""
import optparse
import os
import re
import shutil
import sys
import cros_output
import tools
from tools import Tools
from tools import CmdError
_base = os.path.dirname(sys.argv[0])
class Fdt:
"""Provides simple access to a flat device tree blob
Properties:
fname: Filename of fdt
"""
def __init__(self, tools, fname):
self.fname = fname
self.tools = tools
def GetProp(self, key, default=None):
"""Get a property from a device tree.
>>> tools = Tools(cros_output.Output())
>>> fdt = Fdt(tools, os.path.join(_base, '../tests/test.dtb'))
>>> fdt.GetProp('/lcd/width')
'1366'
>>> fdt.GetProp('/fluffy')
Traceback (most recent call last):
...
CmdError: Command failed: dtget ../tests/test.dtb /fluffy
Error at 'fluffy': FDT_ERR_NOTFOUND
(Node / exists but you didn't specify a property to print)
<BLANKLINE>
This looks up the given key and returns the value as a string, The key
consists of a node with a property attached, like:
/path/to/node/property
If the node or property does not exist, this will return the default value.
Args:
key: Key to look up.
default: Default value to return if nothing is present in the fdt, or
None to raise in this case. This will be converted to a string.
Returns:
string containing the property value.
Raises:
CmdError: if the property does not exist and no default is provided.
"""
args = [self.fname, key]
if default is not None:
args += ['-d', str(default)]
out = self.tools.Run('dtget', args)
return out.strip()
def GetProps(self, key, convert_dashes=False):
"""Get all properties from a node.
>>> tools = Tools(cros_output.Output())
>>> fdt = Fdt(tools, os.path.join(_base, '../tests/test.dtb'))
>>> fdt.GetProps('/')
{'compatible': '1853253988 1767976051 1700881007 1634886656 1853253988 \
1767976052 1701278305 842346496', '#size-cells': '1', 'model': \
'NVIDIA Seaboard', '#address-cells': '1', 'interrupt-parent': '1'}
Args:
key: node name to look in.
convert_dashes: True to convert - to _ in node names.
Returns:
A dictionary containing all the properties, indexed by node name.
The entries are simply strings - no decoding of lists or numbers is
done.
Raises:
CmdError: if the node does not exist.
"""
out = self.tools.Run('dtget', [self.fname, key, '-p'])
props = out.strip().splitlines()
props_dict = {}
for prop in props:
name = prop
if convert_dashes:
prop = re.sub('-', '_', prop)
props_dict[prop] = self.GetProp(key + '/' + name)
return props_dict
def DecodeIntList(self, key, int_list_str, num_values=None):
"""Decode a string into a list of integers.
>>> tools = Tools(cros_output.Output())
>>> fdt = Fdt(tools, os.path.join(_base, '../tests/test.dtb'))
>>> fdt.DecodeIntList('galveston', '1 2 3 4')
[1, 2, 3, 4]
>>> fdt.DecodeIntList('galveston', '1 2 3 4', 4)
[1, 2, 3, 4]
>>> fdt.DecodeIntList('galveston', '1 2 3 4', 3)
Traceback (most recent call last):
...
ValueError: GetIntList of key 'galveston' returns '<type 'list'>', \
which has 4 elements, but 3 expected
This decodes a string containing a list of integers like '1 2 3' into
a list like [1 2 3].
Args:
key: Key where the value came from (only used for error message).
int_list_str: String to decode.
num_values: If not None, then the array is checked to make sure it
has this many values, and an error is raised if not.
Returns:
List of integers.
Raises:
ValueError if the list is the wrong size.
"""
int_list = int_list_str.split()
if num_values and num_values != len(int_list):
raise ValueError, ("GetIntList of key '%s' returns '%s', which has "
"%d elements, but %d expected" % (key, list, len(int_list),
num_values))
return [int(item) for item in int_list]
def GetIntList(self, key, num_values=None):
"""Read a key and decode it into a list of integers.
>>> tools = Tools(cros_output.Output())
>>> fdt = Fdt(tools, os.path.join(_base, '../tests/test.dtb'))
>>> fdt.GetIntList('/flash@0/shared-dev-cfg@180000/reg')
[1572864, 262144]
>>> fdt.GetIntList('/flash/shared-dev-cfg/reg')
[1572864, 262144]
>>> fdt.GetIntList('/flash/shared-dev-cfg/reg', 3)
Traceback (most recent call last):
...
ValueError: GetIntList of key '/flash/shared-dev-cfg/reg' returns \
'<type 'list'>', which has 2 elements, but 3 expected
>>> fdt.GetIntList('/swaffham/bulbeck', 2)
Traceback (most recent call last):
...
CmdError: Command failed: dtget ../tests/test.dtb /swaffham/bulbeck
Error at '/swaffham/bulbeck': FDT_ERR_NOTFOUND
<BLANKLINE>
>>> fdt.GetIntList('/lcd/bulbeck', 2, '5 6')
[5, 6]
This decodes a key containing a list of integers like '1 2 3' into
a list like [1 2 3].
Args:
key: Key to read to get value.
num_values: If not None, then the array is checked to make sure it
has this many values, and an error is raised if not.
Returns:
List of integers.
Raises:
ValueError if the list is the wrong size.
CmdError: if the property does not exist.
"""
return self.DecodeIntList(key, self.GetProp(key), num_values)
def GetInt(self, key):
"""Gets an integer from a device tree property.
>>> tools = Tools(cros_output.Output())
>>> fdt = Fdt(tools, os.path.join(_base, '../tests/test.dtb'))
>>> fdt.GetInt('/lcd/width')
1366
>>> fdt.GetInt('/lcd/rangiora')
Traceback (most recent call last):
...
CmdError: Command failed: dtget ../tests/test.dtb /lcd/rangiora
Error at 'rangiora': FDT_ERR_NOTFOUND
(Node / exists but you didn't specify a property to print)
<BLANKLINE>
>>> fdt.GetInt('/lcd/rangiora', 1366)
1366
Args:
key: Key to read to get value.
Raises:
ValueError if the property cannot be converted to an integer.
CmdError: if the property does not exist.
"""
value = self.GetIntList(key, 1)[0]
return int(value)
def GetString(self, key, default=None):
"""Gets a string from a device tree property.
>>> tools = Tools(cros_output.Output())
>>> fdt = Fdt(tools, os.path.join(_base, '../tests/test.dtb'))
>>> fdt.GetString('/display/compatible')
'nvidia,tegra250-display'
Args:
key: Key to read to get value.
Raises:
CmdError: if the property does not exist.
"""
return self.GetProp(key, default)
def GetFlashPart(self, section, part):
"""Returns the setup of the given section/part number in the flash map.
>>> tools = Tools(cros_output.Output())
>>> fdt = Fdt(tools, os.path.join(_base, '../tests/test.dtb'))
>>> fdt.GetFlashPart('ro', 'onestop')
[65536, 524288]
Args:
section: Section name to look at: ro, rw-a, etc.
part: Partition name to look at: gbb, vpd, etc.
Returns:
Tuple (position, size) of flash area in bytes.
"""
return self.GetIntList('/flash/%s-%s/reg' % (section, part), 2)
def GetFlashPartSize(self, section, part):
"""Returns the size of the given section/part number in the flash map.
>>> tools = Tools(cros_output.Output())
>>> fdt = Fdt(tools, os.path.join(_base, '../tests/test.dtb'))
>>> fdt.GetFlashPartSize('ro', 'onestop')
524288
Args:
section: Section name to look at: ro, rw-a, etc.
part: Partition name to look at: gbb, vpd, etc.
Returns:
Size of flash area in bytes.
"""
return self.GetFlashPart(section, part)[1]
def GetChildren(self, key):
"""Returns a list of children of a given node.
>>> tools = Tools(cros_output.Output())
>>> fdt = Fdt(tools, os.path.join(_base, '../tests/test.dtb'))
>>> fdt.GetChildren('/amba')
['interrupt-controller@50041000']
>>> fdt.GetChildren('/flash@0')
['onestop-layout@0', 'firmware-image@0', 'verification-block@7df00', \
'firmware-id@7ff00', 'readonly@0', 'bct@0', 'ro-onestop@10000', \
'ro-gbb@90000', 'ro-data@b0000', 'ro-vpd@c0000', 'fmap@d0000', \
'readwrite@100000', 'rw-vpd@100000', 'shared-dev-cfg@180000', \
'shared-data@1c0000', 'shared-env@1ff000', 'readwrite-a@200000', \
'rw-a-onestop@200000', 'readwrite-b@300000', 'rw-b-onestop@300000']
Args:
node: Node to return children from.
Returns:
List of children in the node.
Raises:
CmdError: if the node does not exist.
"""
out = self.tools.Run('dtget', [self.fname, '-l', key])
return out.strip().splitlines()
def GetLabel(self, key):
"""Returns the label property of a given node.
>>> tools = Tools(cros_output.Output())
>>> fdt = Fdt(tools, os.path.join(_base, '../tests/test.dtb'))
>>> fdt.GetLabel('/flash/ro-onestop')
'ro-onestop'
>>> fdt.GetLabel('/go/hotspurs')
Traceback (most recent call last):
...
CmdError: Command failed: dtget ../tests/test.dtb /go/hotspurs/label
Error at '/go/hotspurs/label': FDT_ERR_NOTFOUND
<BLANKLINE>
Args:
key: Node to return label property from.
Raises:
CmdError: if the node or property does not exist.
"""
return self.GetString(key + '/label')
def Copy(self, new_name):
"""Make a copy of the FDT into another file, and return its object.
>>> tools = Tools(cros_output.Output())
>>> fdt = Fdt(tools, os.path.join(_base, '../tests/test.dtb'))
>>> our_copy = fdt.Copy(os.path.join(_base, '../tests/copy.dtb'))
>>> our_copy.PutString('/display/compatible', 'north')
>>> fdt.GetString('/display/compatible')
'nvidia,tegra250-display'
>>> our_copy.GetString('/display/compatible')
'north'
This copies the FDT into a supplied file, then creates an FDT object to
access the copy.
Args:
new_name: Filename to write copy to.
Returns:
An Fdt object for the copy.
"""
shutil.copyfile(self.tools.Filename(self.fname),
self.tools.Filename(new_name))
return Fdt(self.tools, new_name)
def PutString(self, key, value_str):
"""Writes a string to a property in the fdt.
>>> tools = Tools(cros_output.Output())
>>> fdt = Fdt(tools, os.path.join(_base, '../tests/test.dtb'))
>>> our_copy = fdt.Copy(os.path.join(_base, '../tests/copy.dtb'))
>>> our_copy.PutString('/display/compatible', 'north')
>>> fdt.GetString('/display/compatible')
'nvidia,tegra250-display'
>>> our_copy.PutString('/display/compatible', 'south')
>>> our_copy.GetString('/display/compatible')
'south'
Args:
key: Key to write value to.
value_str: String to write.
"""
args = ['-t', 's', self.fname, key, value_str]
self.tools.Run('dtput', args)
def PutInteger(self, key, value_int):
"""Writes a string to a property in the fdt.
>>> tools = Tools(cros_output.Output())
>>> fdt = Fdt(tools, os.path.join(_base, '../tests/test.dtb'))
>>> our_copy = fdt.Copy(os.path.join(_base, '../tests/copy.dtb'))
>>> our_copy.PutString('/display/compatible', 'north')
>>> fdt.GetString('/display/compatible')
'nvidia,tegra250-display'
>>> our_copy.PutString('/display/compatible', 'south')
>>> our_copy.GetString('/display/compatible')
'south'
Args:
key: Key to write value to.
value_int: Integer to write.
"""
args = ['-t', 'i', self.fname, key, str(value_int)]
self.tools.Run('dtput', args)
def main():
"""Main function for cros_bundle_firmware.
This just lists out the children of the root node, along with all their
properties.
"""
parser = optparse.OptionParser()
parser.add_option('-d', '--dt', dest='fdt', type='string', action='store',
help='Path to fdt file to use (binary ,dtb)', default='u-boot.dtb')
(options, args) = parser.parse_args(sys.argv)
tools = Tools(cros_output.Output())
fdt = Fdt(tools, options.fdt)
children = fdt.GetChildren('/')
for child in children:
print '%s: %s\n' % (child, fdt.GetProps('/' + child))
def _Test(argv):
"""Run any built-in tests."""
import doctest
doctest.testmod()
if __name__ == '__main__':
# If first argument is --test, run testing code.
if sys.argv[1:2] == ["--test"]:
_Test([sys.argv[0]] + sys.argv[2:])
else:
main()