Add a Python version of the libfdt library which contains enough features to support the dtoc tool. This is only a very bare-bones implementation. It requires the 'swig' to build. Signed-off-by: Simon Glass <sjg@chromium.org>master
parent
dbbe2e6401
commit
76bce10d21
@ -0,0 +1,89 @@ |
||||
/* File: libfdt.i */ |
||||
%module libfdt |
||||
|
||||
%{ |
||||
#define SWIG_FILE_WITH_INIT |
||||
#include "libfdt.h" |
||||
%} |
||||
|
||||
%pythoncode %{ |
||||
def Raise(errnum): |
||||
raise ValueError('Error %s' % fdt_strerror(errnum)) |
||||
|
||||
def Name(fdt, offset): |
||||
name, len = fdt_get_name(fdt, offset) |
||||
return name |
||||
|
||||
def String(fdt, offset): |
||||
offset = fdt32_to_cpu(offset) |
||||
name = fdt_string(fdt, offset) |
||||
return name |
||||
|
||||
def swap32(x): |
||||
return (((x << 24) & 0xFF000000) | |
||||
((x << 8) & 0x00FF0000) | |
||||
((x >> 8) & 0x0000FF00) | |
||||
((x >> 24) & 0x000000FF)) |
||||
|
||||
def fdt32_to_cpu(x): |
||||
return swap32(x) |
||||
|
||||
def Data(prop): |
||||
set_prop(prop) |
||||
return get_prop_data() |
||||
%} |
||||
|
||||
%include "typemaps.i" |
||||
%include "cstring.i" |
||||
|
||||
%typemap(in) void* = char*; |
||||
|
||||
typedef int fdt32_t; |
||||
|
||||
struct fdt_property { |
||||
fdt32_t tag; |
||||
fdt32_t len; |
||||
fdt32_t nameoff; |
||||
char data[0]; |
||||
}; |
||||
|
||||
/* |
||||
* This is a work-around since I'm not sure of a better way to copy out the |
||||
* contents of a string. This is used in dtoc/GetProps(). The intent is to |
||||
* pass in a pointer to a property and access the data field at the end of |
||||
* it. Ideally the Data() function above would be able to do this directly, |
||||
* but I'm not sure how to do that. |
||||
*/ |
||||
#pragma SWIG nowarn=454 |
||||
%inline %{ |
||||
static struct fdt_property *cur_prop; |
||||
|
||||
void set_prop(struct fdt_property *prop) { |
||||
cur_prop = prop; |
||||
} |
||||
%} |
||||
|
||||
%cstring_output_allocate_size(char **s, int *sz, free(*$1)); |
||||
%inline %{ |
||||
void get_prop_data(char **s, int *sz) { |
||||
*sz = fdt32_to_cpu(cur_prop->len); |
||||
*s = (char *)malloc(*sz); |
||||
if (!*s) |
||||
*sz = 0; |
||||
else |
||||
memcpy(*s, cur_prop + 1, *sz); |
||||
} |
||||
%} |
||||
|
||||
const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int checklen); |
||||
int fdt_path_offset(const void *fdt, const char *path); |
||||
int fdt_first_property_offset(const void *fdt, int nodeoffset); |
||||
int fdt_next_property_offset(const void *fdt, int offset); |
||||
const char *fdt_strerror(int errval); |
||||
const struct fdt_property *fdt_get_property_by_offset(const void *fdt, |
||||
int offset, |
||||
int *OUTPUT); |
||||
const char *fdt_get_name(const void *fdt, int nodeoffset, int *OUTPUT); |
||||
const char *fdt_string(const void *fdt, int stroffset); |
||||
int fdt_first_subnode(const void *fdt, int offset); |
||||
int fdt_next_subnode(const void *fdt, int offset); |
@ -0,0 +1,38 @@ |
||||
#!/usr/bin/env python |
||||
|
||||
""" |
||||
setup.py file for SWIG libfdt |
||||
""" |
||||
|
||||
from distutils.core import setup, Extension |
||||
import os |
||||
import sys |
||||
|
||||
# Don't cross-compile - always use the host compiler. |
||||
del os.environ['CROSS_COMPILE'] |
||||
del os.environ['CC'] |
||||
|
||||
progname = sys.argv[0] |
||||
cflags = sys.argv[1] |
||||
files = sys.argv[2:] |
||||
|
||||
if cflags: |
||||
cflags = [flag for flag in cflags.split(' ') if flag] |
||||
else: |
||||
cflags = None |
||||
|
||||
libfdt_module = Extension( |
||||
'_libfdt', |
||||
sources = files, |
||||
extra_compile_args = cflags |
||||
) |
||||
|
||||
sys.argv = [progname, '--quiet', 'build_ext', '--inplace'] |
||||
|
||||
setup (name = 'libfdt', |
||||
version = '0.1', |
||||
author = "SWIG Docs", |
||||
description = """Simple swig libfdt from docs""", |
||||
ext_modules = [libfdt_module], |
||||
py_modules = ["libfdt"], |
||||
) |
@ -0,0 +1,14 @@ |
||||
#!/usr/bin/python |
||||
|
||||
import os |
||||
import sys |
||||
|
||||
our_path = os.path.dirname(os.path.realpath(__file__)) |
||||
sys.path.append(os.path.join(our_path, '../../b/sandbox_spl/tools')) |
||||
|
||||
import libfdt |
||||
|
||||
with open('b/sandbox_spl/u-boot.dtb') as fd: |
||||
fdt = fd.read() |
||||
|
||||
print libfdt.fdt_path_offset(fdt, "/aliases") |
@ -0,0 +1,180 @@ |
||||
#!/usr/bin/python |
||||
# |
||||
# Copyright (C) 2016 Google, Inc |
||||
# Written by Simon Glass <sjg@chromium.org> |
||||
# |
||||
# SPDX-License-Identifier: GPL-2.0+ |
||||
# |
||||
|
||||
import fdt_util |
||||
import libfdt |
||||
import sys |
||||
|
||||
# This deals with a device tree, presenting it as a list of Node and Prop |
||||
# objects, representing nodes and properties, respectively. |
||||
# |
||||
# This implementation uses a libfdt Python library to access the device tree, |
||||
# so it is fairly efficient. |
||||
|
||||
class Prop: |
||||
"""A device tree property |
||||
|
||||
Properties: |
||||
name: Property name (as per the device tree) |
||||
value: Property value as a string of bytes, or a list of strings of |
||||
bytes |
||||
type: Value type |
||||
""" |
||||
def __init__(self, name, bytes): |
||||
self.name = name |
||||
self.value = None |
||||
if not bytes: |
||||
self.type = fdt_util.TYPE_BOOL |
||||
self.value = True |
||||
return |
||||
self.type, self.value = fdt_util.BytesToValue(bytes) |
||||
|
||||
def GetPhandle(self): |
||||
"""Get a (single) phandle value from a property |
||||
|
||||
Gets the phandle valuie from a property and returns it as an integer |
||||
""" |
||||
return fdt_util.fdt32_to_cpu(self.value[:4]) |
||||
|
||||
def Widen(self, newprop): |
||||
"""Figure out which property type is more general |
||||
|
||||
Given a current property and a new property, this function returns the |
||||
one that is less specific as to type. The less specific property will |
||||
be ble to represent the data in the more specific property. This is |
||||
used for things like: |
||||
|
||||
node1 { |
||||
compatible = "fred"; |
||||
value = <1>; |
||||
}; |
||||
node1 { |
||||
compatible = "fred"; |
||||
value = <1 2>; |
||||
}; |
||||
|
||||
He we want to use an int array for 'value'. The first property |
||||
suggests that a single int is enough, but the second one shows that |
||||
it is not. Calling this function with these two propertes would |
||||
update the current property to be like the second, since it is less |
||||
specific. |
||||
""" |
||||
if newprop.type < self.type: |
||||
self.type = newprop.type |
||||
|
||||
if type(newprop.value) == list and type(self.value) != list: |
||||
self.value = [self.value] |
||||
|
||||
if type(self.value) == list and len(newprop.value) > len(self.value): |
||||
val = fdt_util.GetEmpty(self.type) |
||||
while len(self.value) < len(newprop.value): |
||||
self.value.append(val) |
||||
|
||||
|
||||
class Node: |
||||
"""A device tree node |
||||
|
||||
Properties: |
||||
offset: Integer offset in the device tree |
||||
name: Device tree node tname |
||||
path: Full path to node, along with the node name itself |
||||
_fdt: Device tree object |
||||
subnodes: A list of subnodes for this node, each a Node object |
||||
props: A dict of properties for this node, each a Prop object. |
||||
Keyed by property name |
||||
""" |
||||
def __init__(self, fdt, offset, name, path): |
||||
self.offset = offset |
||||
self.name = name |
||||
self.path = path |
||||
self._fdt = fdt |
||||
self.subnodes = [] |
||||
self.props = {} |
||||
|
||||
def Scan(self): |
||||
"""Scan a node's properties and subnodes |
||||
|
||||
This fills in the props and subnodes properties, recursively |
||||
searching into subnodes so that the entire tree is built. |
||||
""" |
||||
self.props = self._fdt.GetProps(self.path) |
||||
|
||||
offset = libfdt.fdt_first_subnode(self._fdt.GetFdt(), self.offset) |
||||
while offset >= 0: |
||||
sep = '' if self.path[-1] == '/' else '/' |
||||
name = libfdt.Name(self._fdt.GetFdt(), offset) |
||||
path = self.path + sep + name |
||||
node = Node(self._fdt, offset, name, path) |
||||
self.subnodes.append(node) |
||||
|
||||
node.Scan() |
||||
offset = libfdt.fdt_next_subnode(self._fdt.GetFdt(), offset) |
||||
|
||||
|
||||
class Fdt: |
||||
"""Provides simple access to a flat device tree blob. |
||||
|
||||
Properties: |
||||
fname: Filename of fdt |
||||
_root: Root of device tree (a Node object) |
||||
""" |
||||
|
||||
def __init__(self, fname): |
||||
self.fname = fname |
||||
with open(fname) as fd: |
||||
self._fdt = fd.read() |
||||
|
||||
def GetFdt(self): |
||||
"""Get the contents of the FDT |
||||
|
||||
Returns: |
||||
The FDT contents as a string of bytes |
||||
""" |
||||
return self._fdt |
||||
|
||||
def Scan(self): |
||||
"""Scan a device tree, building up a tree of Node objects |
||||
|
||||
This fills in the self._root property |
||||
""" |
||||
self._root = Node(self, 0, '/', '/') |
||||
self._root.Scan() |
||||
|
||||
def GetRoot(self): |
||||
"""Get the root Node of the device tree |
||||
|
||||
Returns: |
||||
The root Node object |
||||
""" |
||||
return self._root |
||||
|
||||
def GetProps(self, node): |
||||
"""Get all properties from a node. |
||||
|
||||
Args: |
||||
node: Full path to node name to look in. |
||||
|
||||
Returns: |
||||
A dictionary containing all the properties, indexed by node name. |
||||
The entries are Prop objects. |
||||
|
||||
Raises: |
||||
ValueError: if the node does not exist. |
||||
""" |
||||
offset = libfdt.fdt_path_offset(self._fdt, node) |
||||
if offset < 0: |
||||
libfdt.Raise(offset) |
||||
props_dict = {} |
||||
poffset = libfdt.fdt_first_property_offset(self._fdt, offset) |
||||
while poffset >= 0: |
||||
dprop, plen = libfdt.fdt_get_property_by_offset(self._fdt, poffset) |
||||
prop = Prop(libfdt.String(self._fdt, dprop.nameoff), libfdt.Data(dprop)) |
||||
props_dict[prop.name] = prop |
||||
|
||||
poffset = libfdt.fdt_next_property_offset(self._fdt, poffset) |
||||
return props_dict |
Loading…
Reference in new issue