mirror of
https://github.com/Fishwaldo/u-boot.git
synced 2025-03-18 13:11:31 +00:00
Merge git://git.denx.de/u-boot-dm
This commit is contained in:
commit
e3396ffd72
64 changed files with 2773 additions and 289 deletions
67
doc/device-tree-bindings/memory/memory.txt
Normal file
67
doc/device-tree-bindings/memory/memory.txt
Normal file
|
@ -0,0 +1,67 @@
|
|||
* Memory binding
|
||||
|
||||
The memory binding for U-Boot is as in the ePAPR with the following additions:
|
||||
|
||||
Optional subnodes can be used defining the memory layout for different board
|
||||
ID masks. To match a set of board ids, a board-id node may define match-mask
|
||||
and match-value ints to define a mask to apply to the board id, and the value
|
||||
that the result should have for the match to be considered valid. The mask
|
||||
defaults to -1, meaning that the value must fully match the board id.
|
||||
|
||||
If subnodes are present, then the /memory node must define these properties:
|
||||
|
||||
- #address-cells: should be 1.
|
||||
- #size-cells: should be 0.
|
||||
|
||||
Each subnode must define
|
||||
|
||||
reg - board ID or mask for this subnode
|
||||
memory-banks - list of memory banks in the same format as normal
|
||||
|
||||
Each subnode may optionally define:
|
||||
|
||||
match-mask - A mask to apply to the board id. This must be accompanied by
|
||||
match-value.
|
||||
match-value - The required resulting value of the board id mask for the given
|
||||
node to be considered a match.
|
||||
auto-size - Indicates that the value given for a bank is the maximum size,
|
||||
each bank is probed to determine its actual size, which may be
|
||||
smaller
|
||||
|
||||
|
||||
The board id determination is up to the vendor and is not defined by this
|
||||
binding.
|
||||
|
||||
Example:
|
||||
|
||||
memory {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
reg = <0x20000000 0x20000000
|
||||
0x40000000 0x20000000
|
||||
0x60000000 0x20000000
|
||||
0x80000000 0x20000000>;
|
||||
auto-size;
|
||||
board-id@0 {
|
||||
match-value = <17>;
|
||||
reg = <0x20000000 0x20000000
|
||||
0x40000000 0x20000000>;
|
||||
};
|
||||
board-id@1 {
|
||||
match-mask = <2>;
|
||||
match-value = <2>;
|
||||
reg = <0x20000000 0x20000000
|
||||
0x40000000 0x20000000
|
||||
0x60000000 0x20000000
|
||||
0x80000000 0x20000000
|
||||
0xa0000000 0x20000000
|
||||
0xc0000000 0x20000000
|
||||
0xe0000000 0x20000000>;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
This shows a system with the following properties:
|
||||
* Default of 2GB of memory, auto-sized, so could be smaller
|
||||
* 3.5GB of memory (with no auto-size) if (board id & 2) is 2
|
||||
* 1GB of memory (with no auto-size) if board id is 17.
|
|
@ -230,6 +230,14 @@ int device_bind(struct udevice *parent, const struct driver *drv,
|
|||
offset_to_ofnode(of_offset), 0, devp);
|
||||
}
|
||||
|
||||
int device_bind_ofnode(struct udevice *parent, const struct driver *drv,
|
||||
const char *name, void *platdata, ofnode node,
|
||||
struct udevice **devp)
|
||||
{
|
||||
return device_bind_common(parent, drv, name, platdata, 0, node, 0,
|
||||
devp);
|
||||
}
|
||||
|
||||
int device_bind_by_name(struct udevice *parent, bool pre_reloc_only,
|
||||
const struct driver_info *info, struct udevice **devp)
|
||||
{
|
||||
|
|
|
@ -457,6 +457,26 @@ int of_read_u32_array(const struct device_node *np, const char *propname,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int of_read_u64(const struct device_node *np, const char *propname, u64 *outp)
|
||||
{
|
||||
const __be64 *val;
|
||||
|
||||
debug("%s: %s: ", __func__, propname);
|
||||
if (!np)
|
||||
return -EINVAL;
|
||||
val = of_find_property_value_of_size(np, propname, sizeof(*outp));
|
||||
if (IS_ERR(val)) {
|
||||
debug("(not found)\n");
|
||||
return PTR_ERR(val);
|
||||
}
|
||||
|
||||
*outp = be64_to_cpup(val);
|
||||
debug("%#llx (%lld)\n", (unsigned long long)*outp,
|
||||
(unsigned long long)*outp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int of_property_match_string(const struct device_node *np, const char *propname,
|
||||
const char *string)
|
||||
{
|
||||
|
|
|
@ -10,15 +10,15 @@
|
|||
#include <dm/of_extra.h>
|
||||
#include <dm/ofnode.h>
|
||||
|
||||
int of_read_fmap_entry(ofnode node, const char *name,
|
||||
struct fmap_entry *entry)
|
||||
int ofnode_read_fmap_entry(ofnode node, struct fmap_entry *entry)
|
||||
{
|
||||
const char *prop;
|
||||
u32 reg[2];
|
||||
|
||||
if (ofnode_read_u32_array(node, "reg", reg, 2)) {
|
||||
debug("Node '%s' has bad/missing 'reg' property\n", name);
|
||||
return -FDT_ERR_NOTFOUND;
|
||||
debug("Node '%s' has bad/missing 'reg' property\n",
|
||||
ofnode_get_name(node));
|
||||
return -log_ret(ENOENT);
|
||||
}
|
||||
entry->offset = reg[0];
|
||||
entry->length = reg[1];
|
||||
|
@ -34,3 +34,84 @@ int of_read_fmap_entry(ofnode node, const char *name,
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ofnode_decode_region(ofnode node, const char *prop_name, fdt_addr_t *basep,
|
||||
fdt_size_t *sizep)
|
||||
{
|
||||
const fdt_addr_t *cell;
|
||||
int len;
|
||||
|
||||
debug("%s: %s: %s\n", __func__, ofnode_get_name(node), prop_name);
|
||||
cell = ofnode_get_property(node, prop_name, &len);
|
||||
if (!cell || (len < sizeof(fdt_addr_t) * 2)) {
|
||||
debug("cell=%p, len=%d\n", cell, len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*basep = fdt_addr_to_cpu(*cell);
|
||||
*sizep = fdt_size_to_cpu(cell[1]);
|
||||
debug("%s: base=%08lx, size=%lx\n", __func__, (ulong)*basep,
|
||||
(ulong)*sizep);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ofnode_decode_memory_region(ofnode config_node, const char *mem_type,
|
||||
const char *suffix, fdt_addr_t *basep,
|
||||
fdt_size_t *sizep)
|
||||
{
|
||||
char prop_name[50];
|
||||
const char *mem;
|
||||
fdt_size_t size, offset_size;
|
||||
fdt_addr_t base, offset;
|
||||
ofnode node;
|
||||
|
||||
if (!ofnode_valid(config_node)) {
|
||||
config_node = ofnode_path("/config");
|
||||
if (!ofnode_valid(config_node)) {
|
||||
debug("%s: Cannot find /config node\n", __func__);
|
||||
return -ENOENT;
|
||||
}
|
||||
}
|
||||
if (!suffix)
|
||||
suffix = "";
|
||||
|
||||
snprintf(prop_name, sizeof(prop_name), "%s-memory%s", mem_type,
|
||||
suffix);
|
||||
mem = ofnode_read_string(config_node, prop_name);
|
||||
if (!mem) {
|
||||
debug("%s: No memory type for '%s', using /memory\n", __func__,
|
||||
prop_name);
|
||||
mem = "/memory";
|
||||
}
|
||||
|
||||
node = ofnode_path(mem);
|
||||
if (!ofnode_valid(node)) {
|
||||
debug("%s: Failed to find node '%s'\n", __func__, mem);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Not strictly correct - the memory may have multiple banks. We just
|
||||
* use the first
|
||||
*/
|
||||
if (ofnode_decode_region(node, "reg", &base, &size)) {
|
||||
debug("%s: Failed to decode memory region %s\n", __func__,
|
||||
mem);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
snprintf(prop_name, sizeof(prop_name), "%s-offset%s", mem_type,
|
||||
suffix);
|
||||
if (ofnode_decode_region(config_node, prop_name, &offset,
|
||||
&offset_size)) {
|
||||
debug("%s: Failed to decode memory region '%s'\n", __func__,
|
||||
prop_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*basep = base + offset;
|
||||
*sizep = offset_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -55,6 +55,38 @@ int ofnode_read_s32_default(ofnode node, const char *propname, s32 def)
|
|||
return def;
|
||||
}
|
||||
|
||||
int ofnode_read_u64(ofnode node, const char *propname, u64 *outp)
|
||||
{
|
||||
const fdt64_t *cell;
|
||||
int len;
|
||||
|
||||
assert(ofnode_valid(node));
|
||||
debug("%s: %s: ", __func__, propname);
|
||||
|
||||
if (ofnode_is_np(node))
|
||||
return of_read_u64(ofnode_to_np(node), propname, outp);
|
||||
|
||||
cell = fdt_getprop(gd->fdt_blob, ofnode_to_offset(node), propname,
|
||||
&len);
|
||||
if (!cell || len < sizeof(*cell)) {
|
||||
debug("(not found)\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
*outp = fdt64_to_cpu(cell[0]);
|
||||
debug("%#llx (%lld)\n", (unsigned long long)*outp,
|
||||
(unsigned long long)*outp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ofnode_read_u64_default(ofnode node, const char *propname, u64 def)
|
||||
{
|
||||
assert(ofnode_valid(node));
|
||||
ofnode_read_u64(node, propname, &def);
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
bool ofnode_read_bool(ofnode node, const char *propname)
|
||||
{
|
||||
const void *prop;
|
||||
|
@ -697,3 +729,15 @@ int ofnode_device_is_compatible(ofnode node, const char *compat)
|
|||
ofnode_to_offset(node),
|
||||
compat);
|
||||
}
|
||||
|
||||
ofnode ofnode_by_compatible(ofnode from, const char *compat)
|
||||
{
|
||||
if (of_live_active()) {
|
||||
return np_to_ofnode(of_find_compatible_node(
|
||||
(struct device_node *)ofnode_to_np(from), NULL,
|
||||
compat));
|
||||
} else {
|
||||
return offset_to_ofnode(fdt_node_offset_by_compatible(
|
||||
gd->fdt_blob, ofnode_to_offset(from), compat));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -308,6 +308,7 @@ int uclass_find_device_by_ofnode(enum uclass_id id, ofnode node,
|
|||
struct udevice *dev;
|
||||
int ret;
|
||||
|
||||
log(LOGC_DM, LOGL_DEBUG, "Looking for %s\n", ofnode_get_name(node));
|
||||
*devp = NULL;
|
||||
if (!ofnode_valid(node))
|
||||
return -ENODEV;
|
||||
|
@ -316,13 +317,19 @@ int uclass_find_device_by_ofnode(enum uclass_id id, ofnode node,
|
|||
return ret;
|
||||
|
||||
list_for_each_entry(dev, &uc->dev_head, uclass_node) {
|
||||
log(LOGC_DM, LOGL_DEBUG_CONTENT, " - checking %s\n",
|
||||
dev->name);
|
||||
if (ofnode_equal(dev_ofnode(dev), node)) {
|
||||
*devp = dev;
|
||||
return 0;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
ret = -ENODEV;
|
||||
|
||||
return -ENODEV;
|
||||
done:
|
||||
log(LOGC_DM, LOGL_DEBUG, " - result for %s: %s (ret=%d)\n",
|
||||
ofnode_get_name(node), *devp ? (*devp)->name : "(none)", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if CONFIG_IS_ENABLED(OF_CONTROL)
|
||||
|
@ -449,8 +456,11 @@ int uclass_get_device_by_ofnode(enum uclass_id id, ofnode node,
|
|||
struct udevice *dev;
|
||||
int ret;
|
||||
|
||||
log(LOGC_DM, LOGL_DEBUG, "Looking for %s\n", ofnode_get_name(node));
|
||||
*devp = NULL;
|
||||
ret = uclass_find_device_by_ofnode(id, node, &dev);
|
||||
log(LOGC_DM, LOGL_DEBUG, " - result for %s: %s (ret=%d)\n",
|
||||
ofnode_get_name(node), dev ? dev->name : "(none)", ret);
|
||||
|
||||
return uclass_get_device_tail(dev, ret, devp);
|
||||
}
|
||||
|
|
|
@ -1028,7 +1028,7 @@ int cros_ec_decode_ec_flash(struct udevice *dev, struct fdt_cros_ec *config)
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (of_read_fmap_entry(flash_node, "flash", &config->flash)) {
|
||||
if (ofnode_read_fmap_entry(flash_node, &config->flash)) {
|
||||
debug("Failed to decode flash node in chrome-ec\n");
|
||||
return -1;
|
||||
}
|
||||
|
@ -1050,7 +1050,7 @@ int cros_ec_decode_ec_flash(struct udevice *dev, struct fdt_cros_ec *config)
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (of_read_fmap_entry(node, "reg", &config->region[region])) {
|
||||
if (ofnode_read_fmap_entry(node, &config->region[region])) {
|
||||
debug("Failed to decode flash region in chrome-ec'\n");
|
||||
return -1;
|
||||
}
|
||||
|
|
|
@ -556,7 +556,7 @@ static int sandbox_cmdline_cb_spi_sf(struct sandbox_state *state,
|
|||
SANDBOX_CMDLINE_OPT(spi_sf, 1, "connect a SPI flash: <bus>:<cs>:<id>:<file>");
|
||||
|
||||
int sandbox_sf_bind_emul(struct sandbox_state *state, int busnum, int cs,
|
||||
struct udevice *bus, int of_offset, const char *spec)
|
||||
struct udevice *bus, ofnode node, const char *spec)
|
||||
{
|
||||
struct udevice *emul;
|
||||
char name[20], *str;
|
||||
|
@ -575,7 +575,7 @@ int sandbox_sf_bind_emul(struct sandbox_state *state, int busnum, int cs,
|
|||
str = strdup(name);
|
||||
if (!str)
|
||||
return -ENOMEM;
|
||||
ret = device_bind(bus, drv, str, NULL, of_offset, &emul);
|
||||
ret = device_bind_ofnode(bus, drv, str, NULL, node, &emul);
|
||||
if (ret) {
|
||||
free(str);
|
||||
printf("Cannot create emul device for spec '%s' (err=%d)\n",
|
||||
|
@ -620,7 +620,8 @@ static int sandbox_sf_bind_bus_cs(struct sandbox_state *state, int busnum,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
return sandbox_sf_bind_emul(state, busnum, cs, bus, -1, spec);
|
||||
return sandbox_sf_bind_emul(state, busnum, cs, bus, ofnode_null(),
|
||||
spec);
|
||||
}
|
||||
|
||||
int sandbox_spi_get_emul(struct sandbox_state *state,
|
||||
|
@ -638,7 +639,7 @@ int sandbox_spi_get_emul(struct sandbox_state *state,
|
|||
debug("%s: busnum=%u, cs=%u: binding SPI flash emulation: ",
|
||||
__func__, busnum, cs);
|
||||
ret = sandbox_sf_bind_emul(state, busnum, cs, bus,
|
||||
dev_of_offset(slave), slave->name);
|
||||
dev_ofnode(slave), slave->name);
|
||||
if (ret) {
|
||||
debug("failed (err=%d)\n", ret);
|
||||
return ret;
|
||||
|
|
|
@ -52,6 +52,7 @@ typedef struct global_data {
|
|||
unsigned long env_has_init; /* Bitmask of boolean of struct env_location offsets */
|
||||
int env_load_location;
|
||||
|
||||
unsigned long ram_base; /* Base address of RAM used by U-Boot */
|
||||
unsigned long ram_top; /* Top address of RAM used by U-Boot */
|
||||
unsigned long relocaddr; /* Start address of U-Boot in RAM */
|
||||
phys_size_t ram_size; /* RAM size */
|
||||
|
|
|
@ -40,6 +40,10 @@ int device_bind(struct udevice *parent, const struct driver *drv,
|
|||
const char *name, void *platdata, int of_offset,
|
||||
struct udevice **devp);
|
||||
|
||||
int device_bind_ofnode(struct udevice *parent, const struct driver *drv,
|
||||
const char *name, void *platdata, ofnode node,
|
||||
struct udevice **devp);
|
||||
|
||||
/**
|
||||
* device_bind_with_driver_data() - Create a device and bind it to a driver
|
||||
*
|
||||
|
|
|
@ -218,6 +218,22 @@ struct device_node *of_find_node_by_phandle(phandle handle);
|
|||
*/
|
||||
int of_read_u32(const struct device_node *np, const char *propname, u32 *outp);
|
||||
|
||||
/**
|
||||
* of_read_u64() - Find and read a 64-bit integer from a property
|
||||
*
|
||||
* Search for a property in a device node and read a 64-bit value from
|
||||
* it.
|
||||
*
|
||||
* @np: device node from which the property value is to be read.
|
||||
* @propname: name of the property to be searched.
|
||||
* @outp: pointer to return value, modified only if return value is 0.
|
||||
*
|
||||
* @return 0 on success, -EINVAL if the property does not exist,
|
||||
* -ENODATA if property does not have a value, and -EOVERFLOW if the
|
||||
* property data isn't large enough.
|
||||
*/
|
||||
int of_read_u64(const struct device_node *np, const char *propname, u64 *outp);
|
||||
|
||||
/**
|
||||
* of_read_u32_array() - Find and read an array of 32 bit integers
|
||||
*
|
||||
|
|
|
@ -34,12 +34,55 @@ struct fmap_entry {
|
|||
/**
|
||||
* Read a flash entry from the fdt
|
||||
*
|
||||
* @param node Reference to node to read
|
||||
* @param name Name of node being read
|
||||
* @param node Reference to node to read
|
||||
* @param entry Place to put offset and size of this node
|
||||
* @return 0 if ok, -ve on error
|
||||
*/
|
||||
int of_read_fmap_entry(ofnode node, const char *name,
|
||||
struct fmap_entry *entry);
|
||||
int ofnode_read_fmap_entry(ofnode node, struct fmap_entry *entry);
|
||||
|
||||
/**
|
||||
* ofnode_decode_region() - Decode a memory region from a node
|
||||
*
|
||||
* Look up a property in a node which contains a memory region address and
|
||||
* size. Then return a pointer to this address.
|
||||
*
|
||||
* The property must hold one address with a length. This is only tested on
|
||||
* 32-bit machines.
|
||||
*
|
||||
* @param node ofnode to examine
|
||||
* @param prop_name name of property to find
|
||||
* @param basep Returns base address of region
|
||||
* @param size Returns size of region
|
||||
* @return 0 if ok, -1 on error (property not found)
|
||||
*/
|
||||
int ofnode_decode_region(ofnode node, const char *prop_name, fdt_addr_t *basep,
|
||||
fdt_size_t *sizep);
|
||||
|
||||
/**
|
||||
* ofnode_decode_memory_region()- Decode a named region within a memory bank
|
||||
*
|
||||
* This function handles selection of a memory region. The region is
|
||||
* specified as an offset/size within a particular type of memory.
|
||||
*
|
||||
* The properties used are:
|
||||
*
|
||||
* <mem_type>-memory<suffix> for the name of the memory bank
|
||||
* <mem_type>-offset<suffix> for the offset in that bank
|
||||
*
|
||||
* The property value must have an offset and a size. The function checks
|
||||
* that the region is entirely within the memory bank.5
|
||||
*
|
||||
* @param node ofnode containing the properties (-1 for /config)
|
||||
* @param mem_type Type of memory to use, which is a name, such as
|
||||
* "u-boot" or "kernel".
|
||||
* @param suffix String to append to the memory/offset
|
||||
* property names
|
||||
* @param basep Returns base of region
|
||||
* @param sizep Returns size of region
|
||||
* @return 0 if OK, -ive on error
|
||||
*/
|
||||
int ofnode_decode_memory_region(ofnode config_node, const char *mem_type,
|
||||
const char *suffix, fdt_addr_t *basep,
|
||||
fdt_size_t *sizep);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -236,6 +236,16 @@ int ofnode_read_u32_default(ofnode ref, const char *propname, u32 def);
|
|||
*/
|
||||
int ofnode_read_s32_default(ofnode node, const char *propname, s32 def);
|
||||
|
||||
/**
|
||||
* ofnode_read_u64_default() - Read a 64-bit integer from a property
|
||||
*
|
||||
* @ref: valid node reference to read property from
|
||||
* @propname: name of the property to read from
|
||||
* @def: default value to return if the property has no value
|
||||
* @return property value, or @def if not found
|
||||
*/
|
||||
int ofnode_read_u64_default(ofnode node, const char *propname, u64 def);
|
||||
|
||||
/**
|
||||
* ofnode_read_string() - Read a string from a property
|
||||
*
|
||||
|
@ -252,6 +262,7 @@ const char *ofnode_read_string(ofnode node, const char *propname);
|
|||
* @propname: name of the property to read
|
||||
* @out_values: pointer to return value, modified only if return value is 0
|
||||
* @sz: number of array elements to read
|
||||
* @return 0 if OK, -ve on error
|
||||
*
|
||||
* Search for a property in a device node and read 32-bit value(s) from
|
||||
* it. Returns 0 on success, -EINVAL if the property does not exist,
|
||||
|
@ -480,6 +491,7 @@ ofnode ofnode_path(const char *path);
|
|||
* This looks for a property within the /chosen node and returns its value
|
||||
*
|
||||
* @propname: Property name to look for
|
||||
* @return property value if found, else NULL
|
||||
*/
|
||||
const char *ofnode_get_chosen_prop(const char *propname);
|
||||
|
||||
|
@ -635,14 +647,48 @@ int ofnode_read_simple_size_cells(ofnode node);
|
|||
* new platforms.
|
||||
*
|
||||
* @node: node to check
|
||||
* @eturns true if node is needed in SPL/TL, false otherwise
|
||||
* @return true if node is needed in SPL/TL, false otherwise
|
||||
*/
|
||||
bool ofnode_pre_reloc(ofnode node);
|
||||
|
||||
/**
|
||||
* ofnode_read_resource() - Read a resource from a node
|
||||
*
|
||||
* Read resource information from a node at the given index
|
||||
*
|
||||
* @node: Node to read from
|
||||
* @index: Index of resource to read (0 = first)
|
||||
* @res: Returns resource that was read, on success
|
||||
* @return 0 if OK, -ve on error
|
||||
*/
|
||||
int ofnode_read_resource(ofnode node, uint index, struct resource *res);
|
||||
|
||||
/**
|
||||
* ofnode_read_resource_byname() - Read a resource from a node by name
|
||||
*
|
||||
* Read resource information from a node matching the given name. This uses a
|
||||
* 'reg-names' string list property with the names matching the associated
|
||||
* 'reg' property list.
|
||||
*
|
||||
* @node: Node to read from
|
||||
* @name: Name of resource to read
|
||||
* @res: Returns resource that was read, on success
|
||||
* @return 0 if OK, -ve on error
|
||||
*/
|
||||
int ofnode_read_resource_byname(ofnode node, const char *name,
|
||||
struct resource *res);
|
||||
|
||||
/**
|
||||
* ofnode_by_compatible() - Find the next compatible node
|
||||
*
|
||||
* Find the next node after @from that is compatible with @compat
|
||||
*
|
||||
* @from: ofnode to start from (use ofnode_null() to start at the beginning)
|
||||
* @compat: Compatible string to match
|
||||
* @return ofnode found, or ofnode_null() if none
|
||||
*/
|
||||
ofnode ofnode_by_compatible(ofnode from, const char *compat);
|
||||
|
||||
/**
|
||||
* ofnode_for_each_subnode() - iterate over all subnodes of a parent
|
||||
*
|
||||
|
|
|
@ -283,6 +283,16 @@ int fdt_setup_simplefb_node(void *fdt, int node, u64 base_address, u32 width,
|
|||
|
||||
int fdt_overlay_apply_verbose(void *fdt, void *fdto);
|
||||
|
||||
/**
|
||||
* fdt_get_cells_len() - Get the length of a type of cell in top-level nodes
|
||||
*
|
||||
* Returns the length of the cell type in bytes (4 or 8).
|
||||
*
|
||||
* @blob: Pointer to device tree blob
|
||||
* @nr_cells_name: Name to lookup, e.g. "#address-cells"
|
||||
*/
|
||||
int fdt_get_cells_len(const void *blob, char *nr_cells_name);
|
||||
|
||||
#endif /* ifdef CONFIG_OF_LIBFDT */
|
||||
|
||||
#ifdef USE_HOSTCC
|
||||
|
|
|
@ -41,6 +41,8 @@ struct fdt_memory {
|
|||
fdt_addr_t end;
|
||||
};
|
||||
|
||||
struct bd_info;
|
||||
|
||||
#ifdef CONFIG_SPL_BUILD
|
||||
#define SPL_BUILD 1
|
||||
#else
|
||||
|
@ -993,6 +995,40 @@ int fdtdec_setup(void);
|
|||
* Called when CONFIG_OF_BOARD is defined, or if CONFIG_OF_SEPARATE is defined
|
||||
* and the board implements it.
|
||||
*/
|
||||
void *board_fdt_blob_setup(void);
|
||||
|
||||
/*
|
||||
* Decode the size of memory
|
||||
*
|
||||
* RAM size is normally set in a /memory node and consists of a list of
|
||||
* (base, size) cells in the 'reg' property. This information is used to
|
||||
* determine the total available memory as well as the address and size
|
||||
* of each bank.
|
||||
*
|
||||
* Optionally the memory configuration can vary depending on a board id,
|
||||
* typically read from strapping resistors or an EEPROM on the board.
|
||||
*
|
||||
* Finally, memory size can be detected (within certain limits) by probing
|
||||
* the available memory. It is safe to do so within the limits provides by
|
||||
* the board's device tree information. This makes it possible to produce
|
||||
* boards with different memory sizes, where the device tree specifies the
|
||||
* maximum memory configuration, and the smaller memory configuration is
|
||||
* probed.
|
||||
*
|
||||
* This function decodes that information, returning the memory base address,
|
||||
* size and bank information. See the memory.txt binding for full
|
||||
* documentation.
|
||||
*
|
||||
* @param blob Device tree blob
|
||||
* @param area Name of node to check (NULL means "/memory")
|
||||
* @param board_id Board ID to look up
|
||||
* @param basep Returns base address of first memory bank (NULL to
|
||||
* ignore)
|
||||
* @param sizep Returns total memory size (NULL to ignore)
|
||||
* @param bd Updated with the memory bank information (NULL to skip)
|
||||
* @return 0 if OK, -ve on error
|
||||
*/
|
||||
int fdtdec_decode_ram_size(const void *blob, const char *area, int board_id,
|
||||
phys_addr_t *basep, phys_size_t *sizep,
|
||||
struct bd_info *bd);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -166,8 +166,16 @@ void __assert_fail(const char *assertion, const char *file, unsigned int line,
|
|||
log(LOG_CATEGORY, LOGL_ERR, "returning err=%d\n", __ret); \
|
||||
__ret; \
|
||||
})
|
||||
#define log_msg_ret(_msg, _ret) ({ \
|
||||
int __ret = (_ret); \
|
||||
if (__ret < 0) \
|
||||
log(LOG_CATEGORY, LOGL_ERR, "%s: returning err=%d\n", _msg, \
|
||||
__ret); \
|
||||
__ret; \
|
||||
})
|
||||
#else
|
||||
#define log_ret(_ret) (_ret)
|
||||
#define log_msg_ret(_ret) (_ret)
|
||||
#endif
|
||||
|
||||
/**
|
||||
|
|
|
@ -185,7 +185,7 @@ static inline int spi_flash_erase(struct spi_flash *flash, u32 offset,
|
|||
struct sandbox_state;
|
||||
|
||||
int sandbox_sf_bind_emul(struct sandbox_state *state, int busnum, int cs,
|
||||
struct udevice *bus, int of_offset, const char *spec);
|
||||
struct udevice *bus, ofnode node, const char *spec);
|
||||
|
||||
void sandbox_sf_unbind_emul(struct sandbox_state *state, int busnum, int cs);
|
||||
|
||||
|
|
109
lib/fdtdec.c
109
lib/fdtdec.c
|
@ -11,6 +11,7 @@
|
|||
#include <errno.h>
|
||||
#include <fdtdec.h>
|
||||
#include <fdt_support.h>
|
||||
#include <inttypes.h>
|
||||
#include <linux/libfdt.h>
|
||||
#include <serial.h>
|
||||
#include <asm/sections.h>
|
||||
|
@ -1350,4 +1351,112 @@ int fdtdec_setup(void)
|
|||
return fdtdec_prepare_fdt();
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NR_DRAM_BANKS
|
||||
int fdtdec_decode_ram_size(const void *blob, const char *area, int board_id,
|
||||
phys_addr_t *basep, phys_size_t *sizep, bd_t *bd)
|
||||
{
|
||||
int addr_cells, size_cells;
|
||||
const u32 *cell, *end;
|
||||
u64 total_size, size, addr;
|
||||
int node, child;
|
||||
bool auto_size;
|
||||
int bank;
|
||||
int len;
|
||||
|
||||
debug("%s: board_id=%d\n", __func__, board_id);
|
||||
if (!area)
|
||||
area = "/memory";
|
||||
node = fdt_path_offset(blob, area);
|
||||
if (node < 0) {
|
||||
debug("No %s node found\n", area);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
cell = fdt_getprop(blob, node, "reg", &len);
|
||||
if (!cell) {
|
||||
debug("No reg property found\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
addr_cells = fdt_address_cells(blob, node);
|
||||
size_cells = fdt_size_cells(blob, node);
|
||||
|
||||
/* Check the board id and mask */
|
||||
for (child = fdt_first_subnode(blob, node);
|
||||
child >= 0;
|
||||
child = fdt_next_subnode(blob, child)) {
|
||||
int match_mask, match_value;
|
||||
|
||||
match_mask = fdtdec_get_int(blob, child, "match-mask", -1);
|
||||
match_value = fdtdec_get_int(blob, child, "match-value", -1);
|
||||
|
||||
if (match_value >= 0 &&
|
||||
((board_id & match_mask) == match_value)) {
|
||||
/* Found matching mask */
|
||||
debug("Found matching mask %d\n", match_mask);
|
||||
node = child;
|
||||
cell = fdt_getprop(blob, node, "reg", &len);
|
||||
if (!cell) {
|
||||
debug("No memory-banks property found\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Note: if no matching subnode was found we use the parent node */
|
||||
|
||||
if (bd) {
|
||||
memset(bd->bi_dram, '\0', sizeof(bd->bi_dram[0]) *
|
||||
CONFIG_NR_DRAM_BANKS);
|
||||
}
|
||||
|
||||
auto_size = fdtdec_get_bool(blob, node, "auto-size");
|
||||
|
||||
total_size = 0;
|
||||
end = cell + len / 4 - addr_cells - size_cells;
|
||||
debug("cell at %p, end %p\n", cell, end);
|
||||
for (bank = 0; bank < CONFIG_NR_DRAM_BANKS; bank++) {
|
||||
if (cell > end)
|
||||
break;
|
||||
addr = 0;
|
||||
if (addr_cells == 2)
|
||||
addr += (u64)fdt32_to_cpu(*cell++) << 32UL;
|
||||
addr += fdt32_to_cpu(*cell++);
|
||||
if (bd)
|
||||
bd->bi_dram[bank].start = addr;
|
||||
if (basep && !bank)
|
||||
*basep = (phys_addr_t)addr;
|
||||
|
||||
size = 0;
|
||||
if (size_cells == 2)
|
||||
size += (u64)fdt32_to_cpu(*cell++) << 32UL;
|
||||
size += fdt32_to_cpu(*cell++);
|
||||
|
||||
if (auto_size) {
|
||||
u64 new_size;
|
||||
|
||||
debug("Auto-sizing %" PRIx64 ", size %" PRIx64 ": ",
|
||||
addr, size);
|
||||
new_size = get_ram_size((long *)(uintptr_t)addr, size);
|
||||
if (new_size == size) {
|
||||
debug("OK\n");
|
||||
} else {
|
||||
debug("sized to %" PRIx64 "\n", new_size);
|
||||
size = new_size;
|
||||
}
|
||||
}
|
||||
|
||||
if (bd)
|
||||
bd->bi_dram[bank].size = size;
|
||||
total_size += size;
|
||||
}
|
||||
|
||||
debug("Memory size %" PRIu64 "\n", total_size);
|
||||
if (sizep)
|
||||
*sizep = (phys_size_t)total_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_NR_DRAM_BANKS */
|
||||
|
||||
#endif /* !USE_HOSTCC */
|
||||
|
|
|
@ -1313,10 +1313,13 @@ static inline int fdt_property_u64(void *fdt, const char *name, uint64_t val)
|
|||
fdt64_t tmp = cpu_to_fdt64(val);
|
||||
return fdt_property(fdt, name, &tmp, sizeof(tmp));
|
||||
}
|
||||
|
||||
#ifndef SWIG /* Not available in Python */
|
||||
static inline int fdt_property_cell(void *fdt, const char *name, uint32_t val)
|
||||
{
|
||||
return fdt_property_u32(fdt, name, val);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* fdt_property_placeholder - add a new property and return a ptr to its value
|
||||
|
|
|
@ -12,6 +12,17 @@
|
|||
%{
|
||||
#define SWIG_FILE_WITH_INIT
|
||||
#include "libfdt.h"
|
||||
|
||||
/*
|
||||
* We rename this function here to avoid problems with swig, since we also have
|
||||
* a struct called fdt_property. That struct causes swig to create a class in
|
||||
* libfdt.py called fdt_property(), which confuses things.
|
||||
*/
|
||||
static int _fdt_property(void *fdt, const char *name, const char *val, int len)
|
||||
{
|
||||
return fdt_property(fdt, name, val, len);
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
%pythoncode %{
|
||||
|
@ -108,6 +119,7 @@ def check_err_null(val, quiet=()):
|
|||
raise FdtException(val)
|
||||
return val
|
||||
|
||||
|
||||
class Fdt:
|
||||
"""Device tree class, supporting all operations
|
||||
|
||||
|
@ -129,6 +141,163 @@ class Fdt:
|
|||
self._fdt = bytearray(data)
|
||||
check_err(fdt_check_header(self._fdt));
|
||||
|
||||
def as_bytearray(self):
|
||||
"""Get the device tree contents as a bytearray
|
||||
|
||||
This can be passed directly to libfdt functions that access a
|
||||
const void * for the device tree.
|
||||
|
||||
Returns:
|
||||
bytearray containing the device tree
|
||||
"""
|
||||
return bytearray(self._fdt)
|
||||
|
||||
def next_node(self, nodeoffset, depth, quiet=()):
|
||||
"""Find the next subnode
|
||||
|
||||
Args:
|
||||
nodeoffset: Node offset of previous node
|
||||
depth: On input, the depth of the node at nodeoffset. On output, the
|
||||
depth of the returned node
|
||||
quiet: Errors to ignore (empty to raise on all errors)
|
||||
|
||||
Returns:
|
||||
The offset of the next node, if any
|
||||
|
||||
Raises:
|
||||
FdtException if no more nodes found or other error occurs
|
||||
"""
|
||||
return check_err(fdt_next_node(self._fdt, nodeoffset, depth), quiet)
|
||||
|
||||
def first_subnode(self, nodeoffset, quiet=()):
|
||||
"""Find the first subnode of a parent node
|
||||
|
||||
Args:
|
||||
nodeoffset: Node offset of parent node
|
||||
quiet: Errors to ignore (empty to raise on all errors)
|
||||
|
||||
Returns:
|
||||
The offset of the first subnode, if any
|
||||
|
||||
Raises:
|
||||
FdtException if no subnodes found or other error occurs
|
||||
"""
|
||||
return check_err(fdt_first_subnode(self._fdt, nodeoffset), quiet)
|
||||
|
||||
def next_subnode(self, nodeoffset, quiet=()):
|
||||
"""Find the next subnode
|
||||
|
||||
Args:
|
||||
nodeoffset: Node offset of previous subnode
|
||||
quiet: Errors to ignore (empty to raise on all errors)
|
||||
|
||||
Returns:
|
||||
The offset of the next subnode, if any
|
||||
|
||||
Raises:
|
||||
FdtException if no more subnodes found or other error occurs
|
||||
"""
|
||||
return check_err(fdt_next_subnode(self._fdt, nodeoffset), quiet)
|
||||
|
||||
def magic(self):
|
||||
"""Return the magic word from the header
|
||||
|
||||
Returns:
|
||||
Magic word
|
||||
"""
|
||||
return fdt_magic(self._fdt) & 0xffffffff
|
||||
|
||||
def totalsize(self):
|
||||
"""Return the total size of the device tree
|
||||
|
||||
Returns:
|
||||
Total tree size in bytes
|
||||
"""
|
||||
return check_err(fdt_totalsize(self._fdt))
|
||||
|
||||
def off_dt_struct(self):
|
||||
"""Return the start of the device-tree struct area
|
||||
|
||||
Returns:
|
||||
Start offset of struct area
|
||||
"""
|
||||
return check_err(fdt_off_dt_struct(self._fdt))
|
||||
|
||||
def off_dt_strings(self):
|
||||
"""Return the start of the device-tree string area
|
||||
|
||||
Returns:
|
||||
Start offset of string area
|
||||
"""
|
||||
return check_err(fdt_off_dt_strings(self._fdt))
|
||||
|
||||
def off_mem_rsvmap(self):
|
||||
"""Return the start of the memory reserve map
|
||||
|
||||
Returns:
|
||||
Start offset of memory reserve map
|
||||
"""
|
||||
return check_err(fdt_off_mem_rsvmap(self._fdt))
|
||||
|
||||
def version(self):
|
||||
"""Return the version of the device tree
|
||||
|
||||
Returns:
|
||||
Version number of the device tree
|
||||
"""
|
||||
return check_err(fdt_version(self._fdt))
|
||||
|
||||
def last_comp_version(self):
|
||||
"""Return the last compatible version of the device tree
|
||||
|
||||
Returns:
|
||||
Last compatible version number of the device tree
|
||||
"""
|
||||
return check_err(fdt_last_comp_version(self._fdt))
|
||||
|
||||
def boot_cpuid_phys(self):
|
||||
"""Return the physical boot CPU ID
|
||||
|
||||
Returns:
|
||||
Physical boot CPU ID
|
||||
"""
|
||||
return check_err(fdt_boot_cpuid_phys(self._fdt))
|
||||
|
||||
def size_dt_strings(self):
|
||||
"""Return the start of the device-tree string area
|
||||
|
||||
Returns:
|
||||
Start offset of string area
|
||||
"""
|
||||
return check_err(fdt_size_dt_strings(self._fdt))
|
||||
|
||||
def size_dt_struct(self):
|
||||
"""Return the start of the device-tree struct area
|
||||
|
||||
Returns:
|
||||
Start offset of struct area
|
||||
"""
|
||||
return check_err(fdt_size_dt_struct(self._fdt))
|
||||
|
||||
def num_mem_rsv(self, quiet=()):
|
||||
"""Return the number of memory reserve-map records
|
||||
|
||||
Returns:
|
||||
Number of memory reserve-map records
|
||||
"""
|
||||
return check_err(fdt_num_mem_rsv(self._fdt), quiet)
|
||||
|
||||
def get_mem_rsv(self, index, quiet=()):
|
||||
"""Return the indexed memory reserve-map record
|
||||
|
||||
Args:
|
||||
index: Record to return (0=first)
|
||||
|
||||
Returns:
|
||||
Number of memory reserve-map records
|
||||
"""
|
||||
return check_err(fdt_get_mem_rsv(self._fdt, index), quiet)
|
||||
|
||||
def subnode_offset(self, parentoffset, name, quiet=()):
|
||||
"""Get the offset of a named subnode
|
||||
|
||||
|
@ -161,6 +330,20 @@ class Fdt:
|
|||
"""
|
||||
return check_err(fdt_path_offset(self._fdt, path), quiet)
|
||||
|
||||
def get_name(self, nodeoffset):
|
||||
"""Get the name of a node
|
||||
|
||||
Args:
|
||||
nodeoffset: Offset of node to check
|
||||
|
||||
Returns:
|
||||
Node name
|
||||
|
||||
Raises:
|
||||
FdtException on error (e.g. nodeoffset is invalid)
|
||||
"""
|
||||
return check_err_null(fdt_get_name(self._fdt, nodeoffset))[0]
|
||||
|
||||
def first_property_offset(self, nodeoffset, quiet=()):
|
||||
"""Get the offset of the first property in a node offset
|
||||
|
||||
|
@ -195,20 +378,6 @@ class Fdt:
|
|||
return check_err(fdt_next_property_offset(self._fdt, prop_offset),
|
||||
quiet)
|
||||
|
||||
def get_name(self, nodeoffset):
|
||||
"""Get the name of a node
|
||||
|
||||
Args:
|
||||
nodeoffset: Offset of node to check
|
||||
|
||||
Returns:
|
||||
Node name
|
||||
|
||||
Raises:
|
||||
FdtException on error (e.g. nodeoffset is invalid)
|
||||
"""
|
||||
return check_err_null(fdt_get_name(self._fdt, nodeoffset))[0]
|
||||
|
||||
def get_property_by_offset(self, prop_offset, quiet=()):
|
||||
"""Obtains a property that can be examined
|
||||
|
||||
|
@ -229,51 +398,59 @@ class Fdt:
|
|||
return pdata
|
||||
return Property(pdata[0], pdata[1])
|
||||
|
||||
def first_subnode(self, nodeoffset, quiet=()):
|
||||
"""Find the first subnode of a parent node
|
||||
def get_property(self, nodeoffset, prop_name, quiet=()):
|
||||
"""Obtains a property by name
|
||||
|
||||
Args:
|
||||
nodeoffset: Node offset of parent node
|
||||
nodeoffset: Offset to the node to check
|
||||
prop_name: Name of property to get
|
||||
quiet: Errors to ignore (empty to raise on all errors)
|
||||
|
||||
Returns:
|
||||
The offset of the first subnode, if any
|
||||
Property object, or None if not found
|
||||
|
||||
Raises:
|
||||
FdtException if no subnode found or other error occurs
|
||||
FdtException on error (e.g. invalid prop_offset or device
|
||||
tree format)
|
||||
"""
|
||||
return check_err(fdt_first_subnode(self._fdt, nodeoffset), quiet)
|
||||
pdata = check_err_null(
|
||||
fdt_get_property(self._fdt, nodeoffset, prop_name), quiet)
|
||||
if isinstance(pdata, (int)):
|
||||
return pdata
|
||||
return Property(pdata[0], pdata[1])
|
||||
|
||||
def next_subnode(self, nodeoffset, quiet=()):
|
||||
"""Find the next subnode
|
||||
@staticmethod
|
||||
def create_empty_tree(size, quiet=()):
|
||||
"""Create an empty device tree ready for use
|
||||
|
||||
Args:
|
||||
nodeoffset: Node offset of previous subnode
|
||||
quiet: Errors to ignore (empty to raise on all errors)
|
||||
size: Size of device tree in bytes
|
||||
|
||||
Returns:
|
||||
The offset of the next subnode, if any
|
||||
|
||||
Raises:
|
||||
FdtException if no more subnode found or other error occurs
|
||||
Fdt object containing the device tree
|
||||
"""
|
||||
return check_err(fdt_next_subnode(self._fdt, nodeoffset), quiet)
|
||||
data = bytearray(size)
|
||||
err = check_err(fdt_create_empty_tree(data, size), quiet)
|
||||
if err:
|
||||
return err
|
||||
return Fdt(data)
|
||||
|
||||
def totalsize(self):
|
||||
"""Return the total size of the device tree
|
||||
def open_into(self, size, quiet=()):
|
||||
"""Move the device tree into a larger or smaller space
|
||||
|
||||
Returns:
|
||||
Total tree size in bytes
|
||||
This creates a new device tree of size @size and moves the existing
|
||||
device tree contents over to that. It can be used to create more space
|
||||
in a device tree.
|
||||
|
||||
Args:
|
||||
size: Required new size of device tree in bytes
|
||||
"""
|
||||
return check_err(fdt_totalsize(self._fdt))
|
||||
|
||||
def off_dt_struct(self):
|
||||
"""Return the start of the device tree struct area
|
||||
|
||||
Returns:
|
||||
Start offset of struct area
|
||||
"""
|
||||
return check_err(fdt_off_dt_struct(self._fdt))
|
||||
fdt = bytearray(size)
|
||||
fdt[:len(self._fdt)] = self._fdt
|
||||
err = check_err(fdt_open_into(self._fdt, fdt, size), quiet)
|
||||
if err:
|
||||
return err
|
||||
self._fdt = fdt
|
||||
|
||||
def pack(self, quiet=()):
|
||||
"""Pack the device tree to remove unused space
|
||||
|
@ -286,19 +463,11 @@ class Fdt:
|
|||
Raises:
|
||||
FdtException if any error occurs
|
||||
"""
|
||||
return check_err(fdt_pack(self._fdt), quiet)
|
||||
|
||||
def delprop(self, nodeoffset, prop_name):
|
||||
"""Delete a property from a node
|
||||
|
||||
Args:
|
||||
nodeoffset: Node offset containing property to delete
|
||||
prop_name: Name of property to delete
|
||||
|
||||
Raises:
|
||||
FdtError if the property does not exist, or another error occurs
|
||||
"""
|
||||
return check_err(fdt_delprop(self._fdt, nodeoffset, prop_name))
|
||||
err = check_err(fdt_pack(self._fdt), quiet)
|
||||
if err:
|
||||
return err
|
||||
del self._fdt[self.totalsize():]
|
||||
return err
|
||||
|
||||
def getprop(self, nodeoffset, prop_name, quiet=()):
|
||||
"""Get a property from a node
|
||||
|
@ -309,7 +478,7 @@ class Fdt:
|
|||
quiet: Errors to ignore (empty to raise on all errors)
|
||||
|
||||
Returns:
|
||||
Value of property as a bytearray, or -ve error number
|
||||
Value of property as a string, or -ve error number
|
||||
|
||||
Raises:
|
||||
FdtError if any error occurs (e.g. the property is not found)
|
||||
|
@ -318,7 +487,27 @@ class Fdt:
|
|||
quiet)
|
||||
if isinstance(pdata, (int)):
|
||||
return pdata
|
||||
return bytearray(pdata[0])
|
||||
return str(pdata[0])
|
||||
|
||||
def getprop_obj(self, nodeoffset, prop_name, quiet=()):
|
||||
"""Get a property from a node as a Property object
|
||||
|
||||
Args:
|
||||
nodeoffset: Node offset containing property to get
|
||||
prop_name: Name of property to get
|
||||
quiet: Errors to ignore (empty to raise on all errors)
|
||||
|
||||
Returns:
|
||||
Property object, or None if not found
|
||||
|
||||
Raises:
|
||||
FdtError if any error occurs (e.g. the property is not found)
|
||||
"""
|
||||
pdata = check_err_null(fdt_getprop(self._fdt, nodeoffset, prop_name),
|
||||
quiet)
|
||||
if isinstance(pdata, (int)):
|
||||
return None
|
||||
return Property(prop_name, bytearray(pdata[0]))
|
||||
|
||||
def get_phandle(self, nodeoffset):
|
||||
"""Get the phandle of a node
|
||||
|
@ -347,6 +536,108 @@ class Fdt:
|
|||
"""
|
||||
return check_err(fdt_parent_offset(self._fdt, nodeoffset), quiet)
|
||||
|
||||
def set_name(self, nodeoffset, name, quiet=()):
|
||||
"""Set the name of a node
|
||||
|
||||
Args:
|
||||
nodeoffset: Node offset of node to update
|
||||
name: New node name
|
||||
|
||||
Returns:
|
||||
Error code, or 0 if OK
|
||||
|
||||
Raises:
|
||||
FdtException if no parent found or other error occurs
|
||||
"""
|
||||
return check_err(fdt_set_name(self._fdt, nodeoffset, name), quiet)
|
||||
|
||||
def setprop(self, nodeoffset, prop_name, val, quiet=()):
|
||||
"""Set the value of a property
|
||||
|
||||
Args:
|
||||
nodeoffset: Node offset containing the property to create/update
|
||||
prop_name: Name of property
|
||||
val: Value to write (string or bytearray)
|
||||
quiet: Errors to ignore (empty to raise on all errors)
|
||||
|
||||
Returns:
|
||||
Error code, or 0 if OK
|
||||
|
||||
Raises:
|
||||
FdtException if no parent found or other error occurs
|
||||
"""
|
||||
return check_err(fdt_setprop(self._fdt, nodeoffset, prop_name, val,
|
||||
len(val)), quiet)
|
||||
|
||||
def setprop_u32(self, nodeoffset, prop_name, val, quiet=()):
|
||||
"""Set the value of a property
|
||||
|
||||
Args:
|
||||
nodeoffset: Node offset containing the property to create/update
|
||||
prop_name: Name of property
|
||||
val: Value to write (integer)
|
||||
quiet: Errors to ignore (empty to raise on all errors)
|
||||
|
||||
Returns:
|
||||
Error code, or 0 if OK
|
||||
|
||||
Raises:
|
||||
FdtException if no parent found or other error occurs
|
||||
"""
|
||||
return check_err(fdt_setprop_u32(self._fdt, nodeoffset, prop_name, val),
|
||||
quiet)
|
||||
|
||||
def setprop_u64(self, nodeoffset, prop_name, val, quiet=()):
|
||||
"""Set the value of a property
|
||||
|
||||
Args:
|
||||
nodeoffset: Node offset containing the property to create/update
|
||||
prop_name: Name of property
|
||||
val: Value to write (integer)
|
||||
quiet: Errors to ignore (empty to raise on all errors)
|
||||
|
||||
Returns:
|
||||
Error code, or 0 if OK
|
||||
|
||||
Raises:
|
||||
FdtException if no parent found or other error occurs
|
||||
"""
|
||||
return check_err(fdt_setprop_u64(self._fdt, nodeoffset, prop_name, val),
|
||||
quiet)
|
||||
|
||||
def setprop_str(self, nodeoffset, prop_name, val, quiet=()):
|
||||
"""Set the string value of a property
|
||||
|
||||
The property is set to the string, with a nul terminator added
|
||||
|
||||
Args:
|
||||
nodeoffset: Node offset containing the property to create/update
|
||||
prop_name: Name of property
|
||||
val: Value to write (string without nul terminator)
|
||||
quiet: Errors to ignore (empty to raise on all errors)
|
||||
|
||||
Returns:
|
||||
Error code, or 0 if OK
|
||||
|
||||
Raises:
|
||||
FdtException if no parent found or other error occurs
|
||||
"""
|
||||
val += '\0'
|
||||
return check_err(fdt_setprop(self._fdt, nodeoffset, prop_name,
|
||||
val, len(val)), quiet)
|
||||
|
||||
def delprop(self, nodeoffset, prop_name):
|
||||
"""Delete a property from a node
|
||||
|
||||
Args:
|
||||
nodeoffset: Node offset containing property to delete
|
||||
prop_name: Name of property to delete
|
||||
|
||||
Raises:
|
||||
FdtError if the property does not exist, or another error occurs
|
||||
"""
|
||||
return check_err(fdt_delprop(self._fdt, nodeoffset, prop_name))
|
||||
|
||||
def node_offset_by_phandle(self, phandle, quiet=()):
|
||||
"""Get the offset of a node with the given phandle
|
||||
|
||||
|
@ -362,7 +653,19 @@ class Fdt:
|
|||
"""
|
||||
return check_err(fdt_node_offset_by_phandle(self._fdt, phandle), quiet)
|
||||
|
||||
class Property:
|
||||
def del_node(self, nodeoffset):
|
||||
"""Delete a node
|
||||
|
||||
Args:
|
||||
nodeoffset: Node offset containing property to delete
|
||||
|
||||
Raises:
|
||||
FdtError if the node does not exist, or another error occurs
|
||||
"""
|
||||
return check_err(fdt_del_node(self._fdt, nodeoffset))
|
||||
|
||||
|
||||
class Property(bytearray):
|
||||
"""Holds a device tree property name and value.
|
||||
|
||||
This holds a copy of a property taken from the device tree. It does not
|
||||
|
@ -371,11 +674,274 @@ class Property:
|
|||
|
||||
Properties:
|
||||
name: Property name
|
||||
value: Proper value as a bytearray
|
||||
value: Property value as a bytearray
|
||||
"""
|
||||
def __init__(self, name, value):
|
||||
bytearray.__init__(self, value)
|
||||
self.name = name
|
||||
self.value = value
|
||||
|
||||
def as_cell(self, fmt):
|
||||
return struct.unpack('>' + fmt, self)[0]
|
||||
|
||||
def as_uint32(self):
|
||||
return self.as_cell('L')
|
||||
|
||||
def as_int32(self):
|
||||
return self.as_cell('l')
|
||||
|
||||
def as_uint64(self):
|
||||
return self.as_cell('Q')
|
||||
|
||||
def as_int64(self):
|
||||
return self.as_cell('q')
|
||||
|
||||
def as_str(self):
|
||||
return self[:-1]
|
||||
|
||||
|
||||
class FdtSw(object):
|
||||
"""Software interface to create a device tree from scratch
|
||||
|
||||
The methods in this class work by adding to an existing 'partial' device
|
||||
tree buffer of a fixed size created by instantiating this class. When the
|
||||
tree is complete, call finish() to complete the device tree so that it can
|
||||
be used.
|
||||
|
||||
Similarly with nodes, a new node is started with begin_node() and finished
|
||||
with end_node().
|
||||
|
||||
The context manager functions can be used to make this a bit easier:
|
||||
|
||||
# First create the device tree with a node and property:
|
||||
with FdtSw(small_size) as sw:
|
||||
with sw.AddNode('node'):
|
||||
sw.property_u32('reg', 2)
|
||||
fdt = sw.AsFdt()
|
||||
|
||||
# Now we can use it as a real device tree
|
||||
fdt.setprop_u32(0, 'reg', 3)
|
||||
"""
|
||||
def __init__(self, size, quiet=()):
|
||||
fdtrw = bytearray(size)
|
||||
err = check_err(fdt_create(fdtrw, size))
|
||||
if err:
|
||||
return err
|
||||
self._fdtrw = fdtrw
|
||||
|
||||
def __enter__(self):
|
||||
"""Contact manager to use to create a device tree via software"""
|
||||
return self
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
check_err(fdt_finish(self._fdtrw))
|
||||
|
||||
def AsFdt(self):
|
||||
"""Convert a FdtSw into an Fdt so it can be accessed as normal
|
||||
|
||||
Note that finish() must be called before this function will work. If
|
||||
you are using the context manager (see 'with' code in the FdtSw class
|
||||
comment) then this will happen automatically.
|
||||
|
||||
Returns:
|
||||
Fdt object allowing access to the newly created device tree
|
||||
"""
|
||||
return Fdt(self._fdtrw)
|
||||
|
||||
def resize(self, size, quiet=()):
|
||||
"""Resize the buffer to accommodate a larger tree
|
||||
|
||||
Args:
|
||||
size: New size of tree
|
||||
quiet: Errors to ignore (empty to raise on all errors)
|
||||
|
||||
Raises:
|
||||
FdtException if no node found or other error occurs
|
||||
"""
|
||||
fdt = bytearray(size)
|
||||
fdt[:len(self._fdtrw)] = self._fdtrw
|
||||
err = check_err(fdt_resize(self._fdtrw, fdt, size), quiet)
|
||||
if err:
|
||||
return err
|
||||
self._fdtrw = fdt
|
||||
|
||||
def add_reservemap_entry(self, addr, size, quiet=()):
|
||||
"""Add a new memory reserve map entry
|
||||
|
||||
Once finished adding, you must call finish_reservemap().
|
||||
|
||||
Args:
|
||||
addr: 64-bit start address
|
||||
size: 64-bit size
|
||||
quiet: Errors to ignore (empty to raise on all errors)
|
||||
|
||||
Raises:
|
||||
FdtException if no node found or other error occurs
|
||||
"""
|
||||
return check_err(fdt_add_reservemap_entry(self._fdtrw, addr, size),
|
||||
quiet)
|
||||
|
||||
def finish_reservemap(self, quiet=()):
|
||||
"""Indicate that there are no more reserve map entries to add
|
||||
|
||||
Args:
|
||||
quiet: Errors to ignore (empty to raise on all errors)
|
||||
|
||||
Raises:
|
||||
FdtException if no node found or other error occurs
|
||||
"""
|
||||
return check_err(fdt_finish_reservemap(self._fdtrw), quiet)
|
||||
|
||||
def begin_node(self, name, quiet=()):
|
||||
"""Begin a new node
|
||||
|
||||
Use this before adding properties to the node. Then call end_node() to
|
||||
finish it. You can also use the context manager as shown in the FdtSw
|
||||
class comment.
|
||||
|
||||
Args:
|
||||
name: Name of node to begin
|
||||
quiet: Errors to ignore (empty to raise on all errors)
|
||||
|
||||
Raises:
|
||||
FdtException if no node found or other error occurs
|
||||
"""
|
||||
return check_err(fdt_begin_node(self._fdtrw, name), quiet)
|
||||
|
||||
def property_string(self, name, string, quiet=()):
|
||||
"""Add a property with a string value
|
||||
|
||||
The string will be nul-terminated when written to the device tree
|
||||
|
||||
Args:
|
||||
name: Name of property to add
|
||||
string: String value of property
|
||||
quiet: Errors to ignore (empty to raise on all errors)
|
||||
|
||||
Raises:
|
||||
FdtException if no node found or other error occurs
|
||||
"""
|
||||
return check_err(fdt_property_string(self._fdtrw, name, string), quiet)
|
||||
|
||||
def property_u32(self, name, val, quiet=()):
|
||||
"""Add a property with a 32-bit value
|
||||
|
||||
Write a single-cell value to the device tree
|
||||
|
||||
Args:
|
||||
name: Name of property to add
|
||||
val: Value of property
|
||||
quiet: Errors to ignore (empty to raise on all errors)
|
||||
|
||||
Raises:
|
||||
FdtException if no node found or other error occurs
|
||||
"""
|
||||
return check_err(fdt_property_u32(self._fdtrw, name, val), quiet)
|
||||
|
||||
def property_u64(self, name, val, quiet=()):
|
||||
"""Add a property with a 64-bit value
|
||||
|
||||
Write a double-cell value to the device tree in big-endian format
|
||||
|
||||
Args:
|
||||
name: Name of property to add
|
||||
val: Value of property
|
||||
quiet: Errors to ignore (empty to raise on all errors)
|
||||
|
||||
Raises:
|
||||
FdtException if no node found or other error occurs
|
||||
"""
|
||||
return check_err(fdt_property_u64(self._fdtrw, name, val), quiet)
|
||||
|
||||
def property_cell(self, name, val, quiet=()):
|
||||
"""Add a property with a single-cell value
|
||||
|
||||
Write a single-cell value to the device tree
|
||||
|
||||
Args:
|
||||
name: Name of property to add
|
||||
val: Value of property
|
||||
quiet: Errors to ignore (empty to raise on all errors)
|
||||
|
||||
Raises:
|
||||
FdtException if no node found or other error occurs
|
||||
"""
|
||||
return check_err(fdt_property_cell(self._fdtrw, name, val), quiet)
|
||||
|
||||
def property(self, name, val, quiet=()):
|
||||
"""Add a property
|
||||
|
||||
Write a new property with the given value to the device tree. The value
|
||||
is taken as is and is not nul-terminated
|
||||
|
||||
Args:
|
||||
name: Name of property to add
|
||||
val: Value of property
|
||||
quiet: Errors to ignore (empty to raise on all errors)
|
||||
|
||||
Raises:
|
||||
FdtException if no node found or other error occurs
|
||||
"""
|
||||
return check_err(_fdt_property(self._fdtrw, name, val, len(val)), quiet)
|
||||
|
||||
def end_node(self, quiet=()):
|
||||
"""End a node
|
||||
|
||||
Use this after adding properties to a node to close it off. You can also
|
||||
use the context manager as shown in the FdtSw class comment.
|
||||
|
||||
Args:
|
||||
quiet: Errors to ignore (empty to raise on all errors)
|
||||
|
||||
Raises:
|
||||
FdtException if no node found or other error occurs
|
||||
"""
|
||||
return check_err(fdt_end_node(self._fdtrw), quiet)
|
||||
|
||||
def finish(self, quiet=()):
|
||||
"""Finish writing the device tree
|
||||
|
||||
This closes off the device tree ready for use
|
||||
|
||||
Args:
|
||||
quiet: Errors to ignore (empty to raise on all errors)
|
||||
|
||||
Raises:
|
||||
FdtException if no node found or other error occurs
|
||||
"""
|
||||
return check_err(fdt_finish(self._fdtrw), quiet)
|
||||
|
||||
def AddNode(self, name):
|
||||
"""Create a new context for adding a node
|
||||
|
||||
When used in a 'with' clause this starts a new node and finishes it
|
||||
afterward.
|
||||
|
||||
Args:
|
||||
name: Name of node to add
|
||||
"""
|
||||
return NodeAdder(self._fdtrw, name)
|
||||
|
||||
|
||||
class NodeAdder():
|
||||
"""Class to provide a node context
|
||||
|
||||
This allows you to add nodes in a more natural way:
|
||||
|
||||
with fdtsw.AddNode('name'):
|
||||
fdtsw.property_string('test', 'value')
|
||||
|
||||
The node is automatically completed with a call to end_node() when the
|
||||
context exits.
|
||||
"""
|
||||
def __init__(self, fdt, name):
|
||||
self._fdt = fdt
|
||||
self._name = name
|
||||
|
||||
def __enter__(self):
|
||||
check_err(fdt_begin_node(self._fdt, self._name))
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
check_err(fdt_end_node(self._fdt))
|
||||
%}
|
||||
|
||||
%rename(fdt_property) fdt_property_func;
|
||||
|
@ -408,6 +974,7 @@ typedef int fdt32_t;
|
|||
fdt = fdt; /* avoid unused variable warning */
|
||||
}
|
||||
|
||||
/* typemap used for fdt_get_property_by_offset() */
|
||||
%typemap(out) (struct fdt_property *) {
|
||||
PyObject *buff;
|
||||
|
||||
|
@ -430,6 +997,44 @@ typedef int fdt32_t;
|
|||
$result = Py_BuildValue("s#", $1, *arg4);
|
||||
}
|
||||
|
||||
/* typemap used for fdt_setprop() */
|
||||
%typemap(in) (const void *val) {
|
||||
$1 = PyString_AsString($input); /* char *str */
|
||||
}
|
||||
|
||||
/* typemap used for fdt_add_reservemap_entry() */
|
||||
%typemap(in) uint64_t {
|
||||
$1 = PyLong_AsUnsignedLong($input);
|
||||
}
|
||||
|
||||
/* typemaps used for fdt_next_node() */
|
||||
%typemap(in, numinputs=1) int *depth (int depth) {
|
||||
depth = (int) PyInt_AsLong($input);
|
||||
$1 = &depth;
|
||||
}
|
||||
|
||||
%typemap(argout) int *depth {
|
||||
PyObject *val = Py_BuildValue("i", *arg$argnum);
|
||||
resultobj = SWIG_Python_AppendOutput(resultobj, val);
|
||||
}
|
||||
|
||||
%apply int *depth { int *depth };
|
||||
|
||||
/* typemaps for fdt_get_mem_rsv */
|
||||
%typemap(in, numinputs=0) uint64_t * (uint64_t temp) {
|
||||
$1 = &temp;
|
||||
}
|
||||
|
||||
%typemap(argout) uint64_t * {
|
||||
PyObject *val = PyLong_FromUnsignedLong(*arg$argnum);
|
||||
if (!result) {
|
||||
if (PyTuple_GET_SIZE(resultobj) == 0)
|
||||
resultobj = val;
|
||||
else
|
||||
resultobj = SWIG_Python_AppendOutput(resultobj, val);
|
||||
}
|
||||
}
|
||||
|
||||
/* We have both struct fdt_property and a function fdt_property() */
|
||||
%warnfilter(302) fdt_property;
|
||||
|
||||
|
@ -444,5 +1049,13 @@ int fdt_last_comp_version(const void *fdt);
|
|||
int fdt_boot_cpuid_phys(const void *fdt);
|
||||
int fdt_size_dt_strings(const void *fdt);
|
||||
int fdt_size_dt_struct(const void *fdt);
|
||||
int fdt_property_string(void *fdt, const char *name, const char *val);
|
||||
int fdt_property_cell(void *fdt, const char *name, uint32_t val);
|
||||
|
||||
/*
|
||||
* This function has a stub since the name fdt_property is used for both a
|
||||
* function and a struct, which confuses SWIG.
|
||||
*/
|
||||
int _fdt_property(void *fdt, const char *name, const char *val, int len);
|
||||
|
||||
%include <../libfdt/libfdt.h>
|
||||
|
|
|
@ -23,7 +23,7 @@ static int dm_test_spi_find(struct unit_test_state *uts)
|
|||
struct udevice *bus, *dev;
|
||||
const int busnum = 0, cs = 0, mode = 0, speed = 1000000, cs_b = 1;
|
||||
struct spi_cs_info info;
|
||||
int of_offset;
|
||||
ofnode node;
|
||||
|
||||
ut_asserteq(-ENODEV, uclass_find_device_by_seq(UCLASS_SPI, busnum,
|
||||
false, &bus));
|
||||
|
@ -34,7 +34,7 @@ static int dm_test_spi_find(struct unit_test_state *uts)
|
|||
*/
|
||||
ut_asserteq(0, uclass_get_device_by_seq(UCLASS_SPI, busnum, &bus));
|
||||
ut_assertok(spi_cs_info(bus, cs, &info));
|
||||
of_offset = dev_of_offset(info.dev);
|
||||
node = dev_ofnode(info.dev);
|
||||
device_remove(info.dev, DM_REMOVE_NORMAL);
|
||||
device_unbind(info.dev);
|
||||
|
||||
|
@ -65,7 +65,7 @@ static int dm_test_spi_find(struct unit_test_state *uts)
|
|||
ut_asserteq_ptr(NULL, info.dev);
|
||||
|
||||
/* Add the emulation and try again */
|
||||
ut_assertok(sandbox_sf_bind_emul(state, busnum, cs, bus, of_offset,
|
||||
ut_assertok(sandbox_sf_bind_emul(state, busnum, cs, bus, node,
|
||||
"name"));
|
||||
ut_assertok(spi_find_bus_and_cs(busnum, cs, &bus, &dev));
|
||||
ut_assertok(spi_get_bus_and_cs(busnum, cs, speed, mode,
|
||||
|
@ -75,7 +75,7 @@ static int dm_test_spi_find(struct unit_test_state *uts)
|
|||
ut_asserteq_ptr(info.dev, slave->dev);
|
||||
|
||||
/* We should be able to add something to another chip select */
|
||||
ut_assertok(sandbox_sf_bind_emul(state, busnum, cs_b, bus, of_offset,
|
||||
ut_assertok(sandbox_sf_bind_emul(state, busnum, cs_b, bus, node,
|
||||
"name"));
|
||||
ut_assertok(spi_get_bus_and_cs(busnum, cs_b, speed, mode,
|
||||
"spi_flash_std", "name", &bus, &slave));
|
||||
|
|
7
test/run
7
test/run
|
@ -26,10 +26,13 @@ PYTHONPATH=${DTC_DIR}/pylibfdt DTC=${DTC_DIR}/dtc run_test ./tools/dtoc/dtoc -t
|
|||
|
||||
# This needs you to set up Python test coverage tools.
|
||||
# To enable Python test coverage on Debian-type distributions (e.g. Ubuntu):
|
||||
# $ sudo apt-get install python-pip python-pytest
|
||||
# $ sudo pip install coverage
|
||||
# $ sudo apt-get install python-pytest python-coverage
|
||||
PYTHONPATH=${DTC_DIR}/pylibfdt DTC=${DTC_DIR}/dtc run_test \
|
||||
./tools/binman/binman -T
|
||||
PYTHONPATH=${DTC_DIR}/pylibfdt DTC=${DTC_DIR}/dtc run_test \
|
||||
./tools/dtoc/dtoc -T
|
||||
PYTHONPATH=${DTC_DIR}/pylibfdt DTC=${DTC_DIR}/dtc run_test \
|
||||
./tools/dtoc/test_fdt -T
|
||||
|
||||
if [ $result == 0 ]; then
|
||||
echo "Tests passed!"
|
||||
|
|
|
@ -462,7 +462,22 @@ Order of image creation
|
|||
|
||||
Image creation proceeds in the following order, for each entry in the image.
|
||||
|
||||
1. GetEntryContents() - the contents of each entry are obtained, normally by
|
||||
1. AddMissingProperties() - binman can add calculated values to the device
|
||||
tree as part of its processing, for example the position and size of each
|
||||
entry. This method adds any properties associated with this, expanding the
|
||||
device tree as needed. These properties can have placeholder values which are
|
||||
set later by SetCalculatedProperties(). By that stage the size of sections
|
||||
cannot be changed (since it would cause the images to need to be repacked),
|
||||
but the correct values can be inserted.
|
||||
|
||||
2. ProcessFdt() - process the device tree information as required by the
|
||||
particular entry. This may involve adding or deleting properties. If the
|
||||
processing is complete, this method should return True. If the processing
|
||||
cannot complete because it needs the ProcessFdt() method of another entry to
|
||||
run first, this method should return False, in which case it will be called
|
||||
again later.
|
||||
|
||||
3. GetEntryContents() - the contents of each entry are obtained, normally by
|
||||
reading from a file. This calls the Entry.ObtainContents() to read the
|
||||
contents. The default version of Entry.ObtainContents() calls
|
||||
Entry.GetDefaultFilename() and then reads that file. So a common mechanism
|
||||
|
@ -471,33 +486,38 @@ functions must return True when they have read the contents. Binman will
|
|||
retry calling the functions a few times if False is returned, allowing
|
||||
dependencies between the contents of different entries.
|
||||
|
||||
2. GetEntryPositions() - calls Entry.GetPositions() for each entry. This can
|
||||
4. GetEntryPositions() - calls Entry.GetPositions() for each entry. This can
|
||||
return a dict containing entries that need updating. The key should be the
|
||||
entry name and the value is a tuple (pos, size). This allows an entry to
|
||||
provide the position and size for other entries. The default implementation
|
||||
of GetEntryPositions() returns {}.
|
||||
|
||||
3. PackEntries() - calls Entry.Pack() which figures out the position and
|
||||
5. PackEntries() - calls Entry.Pack() which figures out the position and
|
||||
size of an entry. The 'current' image position is passed in, and the function
|
||||
returns the position immediately after the entry being packed. The default
|
||||
implementation of Pack() is usually sufficient.
|
||||
|
||||
4. CheckSize() - checks that the contents of all the entries fits within
|
||||
6. CheckSize() - checks that the contents of all the entries fits within
|
||||
the image size. If the image does not have a defined size, the size is set
|
||||
large enough to hold all the entries.
|
||||
|
||||
5. CheckEntries() - checks that the entries do not overlap, nor extend
|
||||
7. CheckEntries() - checks that the entries do not overlap, nor extend
|
||||
outside the image.
|
||||
|
||||
6. ProcessEntryContents() - this calls Entry.ProcessContents() on each entry.
|
||||
8. SetCalculatedProperties() - update any calculated properties in the device
|
||||
tree. This sets the correct 'pos' and 'size' vaues, for example.
|
||||
|
||||
9. ProcessEntryContents() - this calls Entry.ProcessContents() on each entry.
|
||||
The default implementatoin does nothing. This can be overriden to adjust the
|
||||
contents of an entry in some way. For example, it would be possible to create
|
||||
an entry containing a hash of the contents of some other entries. At this
|
||||
stage the position and size of entries should not be adjusted.
|
||||
|
||||
6. WriteEntryInfo()
|
||||
10. WriteSymbols() - write the value of symbols into the U-Boot SPL binary.
|
||||
See 'Access to binman entry positions at run time' below for a description of
|
||||
what happens in this stage.
|
||||
|
||||
7. BuildImage() - builds the image and writes it to a file. This is the final
|
||||
11. BuildImage() - builds the image and writes it to a file. This is the final
|
||||
step.
|
||||
|
||||
|
||||
|
@ -583,8 +603,7 @@ implementations target 100% test coverage. Run 'binman -T' to check this.
|
|||
|
||||
To enable Python test coverage on Debian-type distributions (e.g. Ubuntu):
|
||||
|
||||
$ sudo apt-get install python-pip python-pytest
|
||||
$ sudo pip install coverage
|
||||
$ sudo apt-get install python-coverage python-pytest
|
||||
|
||||
|
||||
Advanced Features / Technical docs
|
||||
|
@ -650,13 +669,11 @@ To do
|
|||
-----
|
||||
|
||||
Some ideas:
|
||||
- Fill out the device tree to include the final position and size of each
|
||||
entry (since the input file may not always specify these). See also
|
||||
'Access to binman entry positions at run time' above
|
||||
- Use of-platdata to make the information available to code that is unable
|
||||
to use device tree (such as a very small SPL image)
|
||||
- Allow easy building of images by specifying just the board name
|
||||
- Produce a full Python binding for libfdt (for upstream)
|
||||
- Produce a full Python binding for libfdt (for upstream). This is nearing
|
||||
completion but some work remains
|
||||
- Add an option to decode an image into the constituent binaries
|
||||
- Support building an image for a board (-b) more completely, with a
|
||||
configurable build directory
|
||||
|
|
|
@ -26,6 +26,7 @@ sys.path.insert(0, 'scripts/dtc/pylibfdt')
|
|||
import cmdline
|
||||
import command
|
||||
import control
|
||||
import test_util
|
||||
|
||||
def RunTests(debug, args):
|
||||
"""Run the functional tests and any embedded doctests
|
||||
|
@ -54,14 +55,12 @@ def RunTests(debug, args):
|
|||
|
||||
# Run the entry tests first ,since these need to be the first to import the
|
||||
# 'entry' module.
|
||||
suite = unittest.TestLoader().loadTestsFromTestCase(entry_test.TestEntry)
|
||||
suite.run(result)
|
||||
test_name = args and args[0] or None
|
||||
for module in (ftest.TestFunctional, fdt_test.TestFdt, elf_test.TestElf,
|
||||
image_test.TestImage):
|
||||
for module in (entry_test.TestEntry, ftest.TestFunctional, fdt_test.TestFdt,
|
||||
elf_test.TestElf, image_test.TestImage):
|
||||
if test_name:
|
||||
try:
|
||||
suite = unittest.TestLoader().loadTestsFromName(args[0], module)
|
||||
suite = unittest.TestLoader().loadTestsFromName(test_name, module)
|
||||
except AttributeError:
|
||||
continue
|
||||
else:
|
||||
|
@ -80,33 +79,12 @@ def RunTests(debug, args):
|
|||
|
||||
def RunTestCoverage():
|
||||
"""Run the tests and check that we get 100% coverage"""
|
||||
# This uses the build output from sandbox_spl to get _libfdt.so
|
||||
cmd = ('PYTHONPATH=$PYTHONPATH:%s/sandbox_spl/tools coverage run '
|
||||
'--include "tools/binman/*.py" --omit "*test*,*binman.py" '
|
||||
'tools/binman/binman.py -t' % options.build_dir)
|
||||
os.system(cmd)
|
||||
stdout = command.Output('coverage', 'report')
|
||||
lines = stdout.splitlines()
|
||||
|
||||
test_set= set([os.path.basename(line.split()[0])
|
||||
for line in lines if '/etype/' in line])
|
||||
glob_list = glob.glob(os.path.join(our_path, 'etype/*.py'))
|
||||
all_set = set([os.path.basename(item) for item in glob_list])
|
||||
missing_list = all_set
|
||||
missing_list.difference_update(test_set)
|
||||
missing_list.remove('_testing.py')
|
||||
coverage = lines[-1].split(' ')[-1]
|
||||
ok = True
|
||||
if missing_list:
|
||||
print 'Missing tests for %s' % (', '.join(missing_list))
|
||||
ok = False
|
||||
if coverage != '100%':
|
||||
print stdout
|
||||
print "Type 'coverage html' to get a report in htmlcov/index.html"
|
||||
print 'Coverage error: %s, but should be 100%%' % coverage
|
||||
ok = False
|
||||
if not ok:
|
||||
raise ValueError('Test coverage failure')
|
||||
all_set = set([os.path.splitext(os.path.basename(item))[0]
|
||||
for item in glob_list if '_testing' not in item])
|
||||
test_util.RunTestCoverage('tools/binman/binman.py', None,
|
||||
['*test*', '*binman.py', 'tools/patman/*', 'tools/dtoc/*'],
|
||||
options.build_dir, all_set)
|
||||
|
||||
def RunBinman(options, args):
|
||||
"""Main entry point to binman once arguments are parsed
|
||||
|
|
|
@ -90,6 +90,29 @@ class Section(object):
|
|||
entry.SetPrefix(self._name_prefix)
|
||||
self._entries[node.name] = entry
|
||||
|
||||
def AddMissingProperties(self):
|
||||
for entry in self._entries.values():
|
||||
entry.AddMissingProperties()
|
||||
|
||||
def SetCalculatedProperties(self):
|
||||
for entry in self._entries.values():
|
||||
entry.SetCalculatedProperties()
|
||||
|
||||
def ProcessFdt(self, fdt):
|
||||
todo = self._entries.values()
|
||||
for passnum in range(3):
|
||||
next_todo = []
|
||||
for entry in todo:
|
||||
if not entry.ProcessFdt(fdt):
|
||||
next_todo.append(entry)
|
||||
todo = next_todo
|
||||
if not todo:
|
||||
break
|
||||
if todo:
|
||||
self._Raise('Internal error: Could not complete processing of Fdt: '
|
||||
'remaining %s' % todo)
|
||||
return True
|
||||
|
||||
def CheckSize(self):
|
||||
"""Check that the section contents does not exceed its size, etc."""
|
||||
contents_size = 0
|
||||
|
@ -162,6 +185,10 @@ class Section(object):
|
|||
todo = next_todo
|
||||
if not todo:
|
||||
break
|
||||
if todo:
|
||||
self._Raise('Internal error: Could not complete processing of '
|
||||
'contents: remaining %s' % todo)
|
||||
return True
|
||||
|
||||
def _SetEntryPosSize(self, name, pos, size):
|
||||
"""Set the position and size of an entry
|
||||
|
|
|
@ -42,6 +42,8 @@ def ParseArgs(argv):
|
|||
default=False, help='run tests')
|
||||
parser.add_option('-T', '--test-coverage', action='store_true',
|
||||
default=False, help='run tests and check for 100% coverage')
|
||||
parser.add_option('-u', '--update-fdt', action='store_true',
|
||||
default=False, help='Update the binman node with position/size info')
|
||||
parser.add_option('-v', '--verbosity', default=1,
|
||||
type='int', help='Control verbosity: 0=silent, 1=progress, 3=full, '
|
||||
'4=debug')
|
||||
|
|
|
@ -21,6 +21,11 @@ import tout
|
|||
# Make this global so that it can be referenced from tests
|
||||
images = OrderedDict()
|
||||
|
||||
# Records the device-tree files known to binman, keyed by filename (e.g.
|
||||
# 'u-boot-spl.dtb')
|
||||
fdt_files = {}
|
||||
|
||||
|
||||
def _ReadImageDesc(binman_node):
|
||||
"""Read the image descriptions from the /binman node
|
||||
|
||||
|
@ -53,6 +58,24 @@ def _FindBinmanNode(dtb):
|
|||
return node
|
||||
return None
|
||||
|
||||
def GetFdt(fname):
|
||||
"""Get the Fdt object for a particular device-tree filename
|
||||
|
||||
Binman keeps track of at least one device-tree file called u-boot.dtb but
|
||||
can also have others (e.g. for SPL). This function looks up the given
|
||||
filename and returns the associated Fdt object.
|
||||
|
||||
Args:
|
||||
fname: Filename to look up (e.g. 'u-boot.dtb').
|
||||
|
||||
Returns:
|
||||
Fdt object associated with the filename
|
||||
"""
|
||||
return fdt_files[fname]
|
||||
|
||||
def GetFdtPath(fname):
|
||||
return fdt_files[fname]._fname
|
||||
|
||||
def Binman(options, args):
|
||||
"""The main control code for binman
|
||||
|
||||
|
@ -93,12 +116,41 @@ def Binman(options, args):
|
|||
try:
|
||||
tools.SetInputDirs(options.indir)
|
||||
tools.PrepareOutputDir(options.outdir, options.preserve)
|
||||
dtb = fdt.FdtScan(dtb_fname)
|
||||
|
||||
# Get the device tree ready by compiling it and copying the compiled
|
||||
# output into a file in our output directly. Then scan it for use
|
||||
# in binman.
|
||||
dtb_fname = fdt_util.EnsureCompiled(dtb_fname)
|
||||
fname = tools.GetOutputFilename('u-boot-out.dtb')
|
||||
with open(dtb_fname) as infd:
|
||||
with open(fname, 'wb') as outfd:
|
||||
outfd.write(infd.read())
|
||||
dtb = fdt.FdtScan(fname)
|
||||
|
||||
# Note the file so that GetFdt() can find it
|
||||
fdt_files['u-boot.dtb'] = dtb
|
||||
node = _FindBinmanNode(dtb)
|
||||
if not node:
|
||||
raise ValueError("Device tree '%s' does not have a 'binman' "
|
||||
"node" % dtb_fname)
|
||||
|
||||
images = _ReadImageDesc(node)
|
||||
|
||||
# Prepare the device tree by making sure that any missing
|
||||
# properties are added (e.g. 'pos' and 'size'). The values of these
|
||||
# may not be correct yet, but we add placeholders so that the
|
||||
# size of the device tree is correct. Later, in
|
||||
# SetCalculatedProperties() we will insert the correct values
|
||||
# without changing the device-tree size, thus ensuring that our
|
||||
# entry positions remain the same.
|
||||
for image in images.values():
|
||||
if options.update_fdt:
|
||||
image.AddMissingProperties()
|
||||
image.ProcessFdt(dtb)
|
||||
|
||||
dtb.Pack()
|
||||
dtb.Flush()
|
||||
|
||||
for image in images.values():
|
||||
# Perform all steps for this image, including checking and
|
||||
# writing it. This means that errors found with a later
|
||||
|
@ -109,11 +161,15 @@ def Binman(options, args):
|
|||
image.PackEntries()
|
||||
image.CheckSize()
|
||||
image.CheckEntries()
|
||||
if options.update_fdt:
|
||||
image.SetCalculatedProperties()
|
||||
image.ProcessEntryContents()
|
||||
image.WriteSymbols()
|
||||
image.BuildImage()
|
||||
if options.map:
|
||||
image.WriteMap()
|
||||
with open(fname, 'wb') as outfd:
|
||||
outfd.write(dtb.GetContents())
|
||||
finally:
|
||||
tools.FinaliseOutputDir()
|
||||
finally:
|
||||
|
|
|
@ -4,33 +4,15 @@
|
|||
#
|
||||
# Test for the elf module
|
||||
|
||||
from contextlib import contextmanager
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
try:
|
||||
from StringIO import StringIO
|
||||
except ImportError:
|
||||
from io import StringIO
|
||||
|
||||
import elf
|
||||
import test_util
|
||||
|
||||
binman_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
|
||||
|
||||
# Use this to suppress stdout/stderr output:
|
||||
# with capture_sys_output() as (stdout, stderr)
|
||||
# ...do something...
|
||||
@contextmanager
|
||||
def capture_sys_output():
|
||||
capture_out, capture_err = StringIO(), StringIO()
|
||||
old_out, old_err = sys.stdout, sys.stderr
|
||||
try:
|
||||
sys.stdout, sys.stderr = capture_out, capture_err
|
||||
yield capture_out, capture_err
|
||||
finally:
|
||||
sys.stdout, sys.stderr = old_out, old_err
|
||||
|
||||
|
||||
class FakeEntry:
|
||||
def __init__(self, contents_size):
|
||||
|
@ -110,7 +92,7 @@ class TestElf(unittest.TestCase):
|
|||
entry = FakeEntry(20)
|
||||
section = FakeSection()
|
||||
elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms')
|
||||
with capture_sys_output() as (stdout, stderr):
|
||||
with test_util.capture_sys_output() as (stdout, stderr):
|
||||
syms = elf.LookupAndWriteSymbols(elf_fname, entry, section)
|
||||
elf.debug = False
|
||||
self.assertTrue(len(stdout.getvalue()) > 0)
|
||||
|
|
|
@ -55,6 +55,7 @@ class Entry(object):
|
|||
self.name = node and (name_prefix + node.name) or 'none'
|
||||
self.pos = None
|
||||
self.size = None
|
||||
self.data = ''
|
||||
self.contents_size = 0
|
||||
self.align = None
|
||||
self.align_size = None
|
||||
|
@ -129,6 +130,20 @@ class Entry(object):
|
|||
self.align_end = fdt_util.GetInt(self._node, 'align-end')
|
||||
self.pos_unset = fdt_util.GetBool(self._node, 'pos-unset')
|
||||
|
||||
def AddMissingProperties(self):
|
||||
"""Add new properties to the device tree as needed for this entry"""
|
||||
for prop in ['pos', 'size']:
|
||||
if not prop in self._node.props:
|
||||
self._node.AddZeroProp(prop)
|
||||
|
||||
def SetCalculatedProperties(self):
|
||||
"""Set the value of device-tree properties calculated by binman"""
|
||||
self._node.SetInt('pos', self.pos)
|
||||
self._node.SetInt('size', self.size)
|
||||
|
||||
def ProcessFdt(self, fdt):
|
||||
return True
|
||||
|
||||
def SetPrefix(self, prefix):
|
||||
"""Set the name prefix for a node
|
||||
|
||||
|
@ -138,6 +153,33 @@ class Entry(object):
|
|||
if prefix:
|
||||
self.name = prefix + self.name
|
||||
|
||||
def SetContents(self, data):
|
||||
"""Set the contents of an entry
|
||||
|
||||
This sets both the data and content_size properties
|
||||
|
||||
Args:
|
||||
data: Data to set to the contents (string)
|
||||
"""
|
||||
self.data = data
|
||||
self.contents_size = len(self.data)
|
||||
|
||||
def ProcessContentsUpdate(self, data):
|
||||
"""Update the contens of an entry, after the size is fixed
|
||||
|
||||
This checks that the new data is the same size as the old.
|
||||
|
||||
Args:
|
||||
data: Data to set to the contents (string)
|
||||
|
||||
Raises:
|
||||
ValueError if the new data size is not the same as the old
|
||||
"""
|
||||
if len(data) != self.contents_size:
|
||||
self.Raise('Cannot update entry size from %d to %d' %
|
||||
(len(data), self.contents_size))
|
||||
self.SetContents(data)
|
||||
|
||||
def ObtainContents(self):
|
||||
"""Figure out the contents of an entry.
|
||||
|
||||
|
|
|
@ -9,17 +9,46 @@ from entry import Entry
|
|||
import fdt_util
|
||||
import tools
|
||||
|
||||
|
||||
class Entry__testing(Entry):
|
||||
"""A fake entry used for testing
|
||||
|
||||
Properties:
|
||||
return_invalid_entry: Return an invalid entry from GetPositions()
|
||||
"""
|
||||
def __init__(self, section, etype, node):
|
||||
Entry.__init__(self, section, etype, node)
|
||||
self.return_invalid_entry = fdt_util.GetBool(self._node,
|
||||
'return-invalid-entry')
|
||||
self.return_unknown_contents = fdt_util.GetBool(self._node,
|
||||
'return-unknown-contents')
|
||||
self.bad_update_contents = fdt_util.GetBool(self._node,
|
||||
'bad-update-contents')
|
||||
self.process_fdt_ready = False
|
||||
self.never_complete_process_fdt = fdt_util.GetBool(self._node,
|
||||
'never-complete-process-fdt')
|
||||
|
||||
def ObtainContents(self):
|
||||
if self.return_unknown_contents:
|
||||
return False
|
||||
self.data = 'a'
|
||||
self.contents_size = len(self.data)
|
||||
return True
|
||||
|
||||
def ReadContents(self):
|
||||
return True
|
||||
|
||||
def GetPositions(self):
|
||||
return {'invalid-entry': [1, 2]}
|
||||
if self.return_invalid_entry :
|
||||
return {'invalid-entry': [1, 2]}
|
||||
return {}
|
||||
|
||||
def ProcessContents(self):
|
||||
if self.bad_update_contents:
|
||||
# Request to update the conents with something larger, to cause a
|
||||
# failure.
|
||||
self.ProcessContentsUpdate('aa')
|
||||
|
||||
def ProcessFdt(self, fdt):
|
||||
"""Force reprocessing the first time"""
|
||||
ready = self.process_fdt_ready
|
||||
if not self.never_complete_process_fdt:
|
||||
self.process_fdt_ready = True
|
||||
return ready
|
||||
|
|
|
@ -28,8 +28,7 @@ class Entry_blob(Entry):
|
|||
# new Entry method which can read in chunks. Then we could copy
|
||||
# the data in chunks and avoid reading it all at once. For now
|
||||
# this seems like an unnecessary complication.
|
||||
self.data = fd.read()
|
||||
self.contents_size = len(self.data)
|
||||
self.SetContents(fd.read())
|
||||
return True
|
||||
|
||||
def GetDefaultFilename(self):
|
||||
|
|
|
@ -17,8 +17,15 @@ class Entry_section(Entry):
|
|||
Entry.__init__(self, image, etype, node)
|
||||
self._section = bsection.Section(node.name, node)
|
||||
|
||||
def ProcessFdt(self, fdt):
|
||||
return self._section.ProcessFdt(fdt)
|
||||
|
||||
def AddMissingProperties(self):
|
||||
Entry.AddMissingProperties(self)
|
||||
self._section.AddMissingProperties()
|
||||
|
||||
def ObtainContents(self):
|
||||
self._section.GetEntryContents()
|
||||
return self._section.GetEntryContents()
|
||||
|
||||
def GetData(self):
|
||||
return self._section.GetData()
|
||||
|
@ -42,6 +49,10 @@ class Entry_section(Entry):
|
|||
"""Write symbol values into binary files for access at run time"""
|
||||
self._section.WriteSymbols()
|
||||
|
||||
def SetCalculatedProperties(self):
|
||||
Entry.SetCalculatedProperties(self)
|
||||
self._section.SetCalculatedProperties()
|
||||
|
||||
def ProcessContents(self):
|
||||
self._section.ProcessEntryContents()
|
||||
super(Entry_section, self).ProcessContents()
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
# Entry-type module for U-Boot device tree with the microcode removed
|
||||
#
|
||||
|
||||
import control
|
||||
import fdt
|
||||
from entry import Entry
|
||||
from blob import Entry_blob
|
||||
|
@ -22,13 +23,13 @@ class Entry_u_boot_dtb_with_ucode(Entry_blob):
|
|||
self.collate = False
|
||||
self.ucode_offset = None
|
||||
self.ucode_size = None
|
||||
self.ucode = None
|
||||
self.ready = False
|
||||
|
||||
def GetDefaultFilename(self):
|
||||
return 'u-boot.dtb'
|
||||
|
||||
def ObtainContents(self):
|
||||
Entry_blob.ObtainContents(self)
|
||||
|
||||
def ProcessFdt(self, fdt):
|
||||
# If the section does not need microcode, there is nothing to do
|
||||
ucode_dest_entry = self.section.FindEntryType(
|
||||
'u-boot-spl-with-ucode-ptr')
|
||||
|
@ -38,36 +39,35 @@ class Entry_u_boot_dtb_with_ucode(Entry_blob):
|
|||
if not ucode_dest_entry or not ucode_dest_entry.target_pos:
|
||||
return True
|
||||
|
||||
# Create a new file to hold the copied device tree
|
||||
dtb_name = 'u-boot-dtb-with-ucode.dtb'
|
||||
fname = tools.GetOutputFilename(dtb_name)
|
||||
with open(fname, 'wb') as fd:
|
||||
fd.write(self.data)
|
||||
|
||||
# Remove the microcode
|
||||
dtb = fdt.FdtScan(fname)
|
||||
ucode = dtb.GetNode('/microcode')
|
||||
if not ucode:
|
||||
fname = self.GetDefaultFilename()
|
||||
fdt = control.GetFdt(fname)
|
||||
self.ucode = fdt.GetNode('/microcode')
|
||||
if not self.ucode:
|
||||
raise self.Raise("No /microcode node found in '%s'" % fname)
|
||||
|
||||
# There's no need to collate it (move all microcode into one place)
|
||||
# if we only have one chunk of microcode.
|
||||
self.collate = len(ucode.subnodes) > 1
|
||||
for node in ucode.subnodes:
|
||||
self.collate = len(self.ucode.subnodes) > 1
|
||||
for node in self.ucode.subnodes:
|
||||
data_prop = node.props.get('data')
|
||||
if data_prop:
|
||||
self.ucode_data += ''.join(data_prop.bytes)
|
||||
if self.collate:
|
||||
prop = node.DeleteProp('data')
|
||||
else:
|
||||
node.DeleteProp('data')
|
||||
return True
|
||||
|
||||
def ObtainContents(self):
|
||||
# Call the base class just in case it does something important.
|
||||
Entry_blob.ObtainContents(self)
|
||||
self._pathname = control.GetFdtPath(self._filename)
|
||||
self.ReadContents()
|
||||
if self.ucode:
|
||||
for node in self.ucode.subnodes:
|
||||
data_prop = node.props.get('data')
|
||||
if data_prop and not self.collate:
|
||||
# Find the offset in the device tree of the ucode data
|
||||
self.ucode_offset = data_prop.GetOffset() + 12
|
||||
self.ucode_size = len(data_prop.bytes)
|
||||
if self.collate:
|
||||
dtb.Pack()
|
||||
dtb.Flush()
|
||||
|
||||
# Make this file the contents of this entry
|
||||
self._pathname = fname
|
||||
self.ReadContents()
|
||||
self.ready = True
|
||||
return True
|
||||
|
|
|
@ -22,5 +22,5 @@ class Entry_u_boot_spl_bss_pad(Entry_blob):
|
|||
bss_size = elf.GetSymbolAddress(fname, '__bss_size')
|
||||
if not bss_size:
|
||||
self.Raise('Expected __bss_size symbol in spl/u-boot-spl')
|
||||
self.data = chr(0) * bss_size
|
||||
self.contents_size = bss_size
|
||||
self.SetContents(chr(0) * bss_size)
|
||||
return True
|
||||
|
|
|
@ -64,9 +64,14 @@ class Entry_u_boot_ucode(Entry_blob):
|
|||
self.data = ''
|
||||
return True
|
||||
|
||||
# Get the microcode from the device tree entry
|
||||
# Get the microcode from the device tree entry. If it is not available
|
||||
# yet, return False so we will be called later. If the section simply
|
||||
# doesn't exist, then we may as well return True, since we are going to
|
||||
# get an error anyway.
|
||||
fdt_entry = self.section.FindEntryType('u-boot-dtb-with-ucode')
|
||||
if not fdt_entry or not fdt_entry.ucode_data:
|
||||
if not fdt_entry:
|
||||
return True
|
||||
if not fdt_entry.ready:
|
||||
return False
|
||||
|
||||
if not fdt_entry.collate:
|
||||
|
|
|
@ -28,7 +28,7 @@ class Entry_u_boot_with_ucode_ptr(Entry_blob):
|
|||
def GetDefaultFilename(self):
|
||||
return 'u-boot-nodtb.bin'
|
||||
|
||||
def ObtainContents(self):
|
||||
def ProcessFdt(self, fdt):
|
||||
# Figure out where to put the microcode pointer
|
||||
fname = tools.GetInputFilename(self.elf_fname)
|
||||
sym = elf.GetSymbolAddress(fname, '_dt_ucode_base_size')
|
||||
|
@ -36,8 +36,7 @@ class Entry_u_boot_with_ucode_ptr(Entry_blob):
|
|||
self.target_pos = sym
|
||||
elif not fdt_util.GetBool(self._node, 'optional-ucode'):
|
||||
self.Raise('Cannot locate _dt_ucode_base_size symbol in u-boot')
|
||||
|
||||
return Entry_blob.ObtainContents(self)
|
||||
return True
|
||||
|
||||
def ProcessContents(self):
|
||||
# If the image does not need microcode, there is nothing to do
|
||||
|
@ -73,7 +72,7 @@ class Entry_u_boot_with_ucode_ptr(Entry_blob):
|
|||
pos, size = ucode_entry.pos, ucode_entry.size
|
||||
else:
|
||||
dtb_entry = self.section.FindEntryType('u-boot-dtb-with-ucode')
|
||||
if not dtb_entry:
|
||||
if not dtb_entry or not dtb_entry.ready:
|
||||
self.Raise('Cannot find microcode region u-boot-dtb-with-ucode')
|
||||
pos = dtb_entry.pos + dtb_entry.ucode_offset
|
||||
size = dtb_entry.ucode_size
|
||||
|
@ -81,5 +80,5 @@ class Entry_u_boot_with_ucode_ptr(Entry_blob):
|
|||
# Write the microcode position and size into the entry
|
||||
pos_and_size = struct.pack('<2L', pos, size)
|
||||
self.target_pos -= self.pos
|
||||
self.data = (self.data[:self.target_pos] + pos_and_size +
|
||||
self.data[self.target_pos + 8:])
|
||||
self.ProcessContentsUpdate(self.data[:self.target_pos] + pos_and_size +
|
||||
self.data[self.target_pos + 8:])
|
||||
|
|
|
@ -146,19 +146,23 @@ class TestFunctional(unittest.TestCase):
|
|||
# options.verbosity = tout.DEBUG
|
||||
return control.Binman(options, args)
|
||||
|
||||
def _DoTestFile(self, fname, debug=False, map=False):
|
||||
def _DoTestFile(self, fname, debug=False, map=False, update_dtb=False):
|
||||
"""Run binman with a given test file
|
||||
|
||||
Args:
|
||||
fname: Device-tree source filename to use (e.g. 05_simple.dts)
|
||||
debug: True to enable debugging output
|
||||
map: True to output map files for the images
|
||||
update_dtb: Update the position and size of each entry in the device
|
||||
tree before packing it into the image
|
||||
"""
|
||||
args = ['-p', '-I', self._indir, '-d', self.TestFile(fname)]
|
||||
if debug:
|
||||
args.append('-D')
|
||||
if map:
|
||||
args.append('-m')
|
||||
if update_dtb:
|
||||
args.append('-up')
|
||||
return self._DoBinman(*args)
|
||||
|
||||
def _SetupDtb(self, fname, outfile='u-boot.dtb'):
|
||||
|
@ -183,7 +187,8 @@ class TestFunctional(unittest.TestCase):
|
|||
TestFunctional._MakeInputFile(outfile, data)
|
||||
return data
|
||||
|
||||
def _DoReadFileDtb(self, fname, use_real_dtb=False, map=False):
|
||||
def _DoReadFileDtb(self, fname, use_real_dtb=False, map=False,
|
||||
update_dtb=False):
|
||||
"""Run binman and return the resulting image
|
||||
|
||||
This runs binman with a given test file and then reads the resulting
|
||||
|
@ -199,6 +204,8 @@ class TestFunctional(unittest.TestCase):
|
|||
test contents (the U_BOOT_DTB_DATA string) can be used.
|
||||
But in some test we need the real contents.
|
||||
map: True to output map files for the images
|
||||
update_dtb: Update the position and size of each entry in the device
|
||||
tree before packing it into the image
|
||||
|
||||
Returns:
|
||||
Tuple:
|
||||
|
@ -212,21 +219,22 @@ class TestFunctional(unittest.TestCase):
|
|||
dtb_data = self._SetupDtb(fname)
|
||||
|
||||
try:
|
||||
retcode = self._DoTestFile(fname, map=map)
|
||||
retcode = self._DoTestFile(fname, map=map, update_dtb=update_dtb)
|
||||
self.assertEqual(0, retcode)
|
||||
out_dtb_fname = control.GetFdtPath('u-boot.dtb')
|
||||
|
||||
# Find the (only) image, read it and return its contents
|
||||
image = control.images['image']
|
||||
fname = tools.GetOutputFilename('image.bin')
|
||||
self.assertTrue(os.path.exists(fname))
|
||||
image_fname = tools.GetOutputFilename('image.bin')
|
||||
self.assertTrue(os.path.exists(image_fname))
|
||||
if map:
|
||||
map_fname = tools.GetOutputFilename('image.map')
|
||||
with open(map_fname) as fd:
|
||||
map_data = fd.read()
|
||||
else:
|
||||
map_data = None
|
||||
with open(fname) as fd:
|
||||
return fd.read(), dtb_data, map_data
|
||||
with open(image_fname) as fd:
|
||||
return fd.read(), dtb_data, map_data, out_dtb_fname
|
||||
finally:
|
||||
# Put the test file back
|
||||
if use_real_dtb:
|
||||
|
@ -300,6 +308,26 @@ class TestFunctional(unittest.TestCase):
|
|||
"""
|
||||
return struct.unpack('>L', dtb[4:8])[0]
|
||||
|
||||
def _GetPropTree(self, dtb_data, node_names):
|
||||
def AddNode(node, path):
|
||||
if node.name != '/':
|
||||
path += '/' + node.name
|
||||
#print 'path', path
|
||||
for subnode in node.subnodes:
|
||||
for prop in subnode.props.values():
|
||||
if prop.name in node_names:
|
||||
prop_path = path + '/' + subnode.name + ':' + prop.name
|
||||
tree[prop_path[len('/binman/'):]] = fdt_util.fdt32_to_cpu(
|
||||
prop.value)
|
||||
#print ' ', prop.name
|
||||
AddNode(subnode, path)
|
||||
|
||||
tree = {}
|
||||
dtb = fdt.Fdt(dtb_data)
|
||||
dtb.Scan()
|
||||
AddNode(dtb.GetRoot(), '')
|
||||
return tree
|
||||
|
||||
def testRun(self):
|
||||
"""Test a basic run with valid args"""
|
||||
result = self._RunBinman('-h')
|
||||
|
@ -688,37 +716,56 @@ class TestFunctional(unittest.TestCase):
|
|||
data = self._DoReadFile('33_x86-start16.dts')
|
||||
self.assertEqual(X86_START16_DATA, data[:len(X86_START16_DATA)])
|
||||
|
||||
def _RunMicrocodeTest(self, dts_fname, nodtb_data):
|
||||
def _RunMicrocodeTest(self, dts_fname, nodtb_data, ucode_second=False):
|
||||
"""Handle running a test for insertion of microcode
|
||||
|
||||
Args:
|
||||
dts_fname: Name of test .dts file
|
||||
nodtb_data: Data that we expect in the first section
|
||||
ucode_second: True if the microsecond entry is second instead of
|
||||
third
|
||||
|
||||
Returns:
|
||||
Tuple:
|
||||
Contents of first region (U-Boot or SPL)
|
||||
Position and size components of microcode pointer, as inserted
|
||||
in the above (two 4-byte words)
|
||||
"""
|
||||
data = self._DoReadFile(dts_fname, True)
|
||||
|
||||
# Now check the device tree has no microcode
|
||||
second = data[len(nodtb_data):]
|
||||
if ucode_second:
|
||||
ucode_content = data[len(nodtb_data):]
|
||||
ucode_pos = len(nodtb_data)
|
||||
dtb_with_ucode = ucode_content[16:]
|
||||
fdt_len = self.GetFdtLen(dtb_with_ucode)
|
||||
else:
|
||||
dtb_with_ucode = data[len(nodtb_data):]
|
||||
fdt_len = self.GetFdtLen(dtb_with_ucode)
|
||||
ucode_content = dtb_with_ucode[fdt_len:]
|
||||
ucode_pos = len(nodtb_data) + fdt_len
|
||||
fname = tools.GetOutputFilename('test.dtb')
|
||||
with open(fname, 'wb') as fd:
|
||||
fd.write(second)
|
||||
fd.write(dtb_with_ucode)
|
||||
dtb = fdt.FdtScan(fname)
|
||||
ucode = dtb.GetNode('/microcode')
|
||||
self.assertTrue(ucode)
|
||||
for node in ucode.subnodes:
|
||||
self.assertFalse(node.props.get('data'))
|
||||
|
||||
fdt_len = self.GetFdtLen(second)
|
||||
third = second[fdt_len:]
|
||||
|
||||
# Check that the microcode appears immediately after the Fdt
|
||||
# This matches the concatenation of the data properties in
|
||||
# the /microcode/update@xxx nodes in 34_x86_ucode.dts.
|
||||
ucode_data = struct.pack('>4L', 0x12345678, 0x12345679, 0xabcd0000,
|
||||
0x78235609)
|
||||
self.assertEqual(ucode_data, third[:len(ucode_data)])
|
||||
ucode_pos = len(nodtb_data) + fdt_len
|
||||
self.assertEqual(ucode_data, ucode_content[:len(ucode_data)])
|
||||
|
||||
# Check that the microcode pointer was inserted. It should match the
|
||||
# expected position and size
|
||||
pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
|
||||
len(ucode_data))
|
||||
first = data[:len(nodtb_data)]
|
||||
return first, pos_and_size
|
||||
u_boot = data[:len(nodtb_data)]
|
||||
return u_boot, pos_and_size
|
||||
|
||||
def testPackUbootMicrocode(self):
|
||||
"""Test that x86 microcode can be handled correctly
|
||||
|
@ -826,7 +873,7 @@ class TestFunctional(unittest.TestCase):
|
|||
"""Test that we can cope with an image without microcode (e.g. qemu)"""
|
||||
with open(self.TestFile('u_boot_no_ucode_ptr')) as fd:
|
||||
TestFunctional._MakeInputFile('u-boot', fd.read())
|
||||
data, dtb, _ = self._DoReadFileDtb('44_x86_optional_ucode.dts', True)
|
||||
data, dtb, _, _ = self._DoReadFileDtb('44_x86_optional_ucode.dts', True)
|
||||
|
||||
# Now check the device tree has no microcode
|
||||
self.assertEqual(U_BOOT_NODTB_DATA, data[:len(U_BOOT_NODTB_DATA)])
|
||||
|
@ -881,23 +928,42 @@ class TestFunctional(unittest.TestCase):
|
|||
data = self._DoReadFile('48_x86-start16-spl.dts')
|
||||
self.assertEqual(X86_START16_SPL_DATA, data[:len(X86_START16_SPL_DATA)])
|
||||
|
||||
def testPackUbootSplMicrocode(self):
|
||||
"""Test that x86 microcode can be handled correctly in SPL
|
||||
def _PackUbootSplMicrocode(self, dts, ucode_second=False):
|
||||
"""Helper function for microcode tests
|
||||
|
||||
We expect to see the following in the image, in order:
|
||||
u-boot-spl-nodtb.bin with a microcode pointer inserted at the
|
||||
correct place
|
||||
u-boot.dtb with the microcode removed
|
||||
the microcode
|
||||
|
||||
Args:
|
||||
dts: Device tree file to use for test
|
||||
ucode_second: True if the microsecond entry is second instead of
|
||||
third
|
||||
"""
|
||||
# ELF file with a '_dt_ucode_base_size' symbol
|
||||
with open(self.TestFile('u_boot_ucode_ptr')) as fd:
|
||||
TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
|
||||
first, pos_and_size = self._RunMicrocodeTest('49_x86_ucode_spl.dts',
|
||||
U_BOOT_SPL_NODTB_DATA)
|
||||
first, pos_and_size = self._RunMicrocodeTest(dts, U_BOOT_SPL_NODTB_DATA,
|
||||
ucode_second=ucode_second)
|
||||
self.assertEqual('splnodtb with microc' + pos_and_size +
|
||||
'ter somewhere in here', first)
|
||||
|
||||
def testPackUbootSplMicrocode(self):
|
||||
"""Test that x86 microcode can be handled correctly in SPL"""
|
||||
self._PackUbootSplMicrocode('49_x86_ucode_spl.dts')
|
||||
|
||||
def testPackUbootSplMicrocodeReorder(self):
|
||||
"""Test that order doesn't matter for microcode entries
|
||||
|
||||
This is the same as testPackUbootSplMicrocode but when we process the
|
||||
u-boot-ucode entry we have not yet seen the u-boot-dtb-with-ucode
|
||||
entry, so we reply on binman to try later.
|
||||
"""
|
||||
self._PackUbootSplMicrocode('58_x86_ucode_spl_needs_retry.dts',
|
||||
ucode_second=True)
|
||||
|
||||
def testPackMrc(self):
|
||||
"""Test that an image with an MRC binary can be created"""
|
||||
data = self._DoReadFile('50_intel_mrc.dts')
|
||||
|
@ -942,7 +1008,7 @@ class TestFunctional(unittest.TestCase):
|
|||
|
||||
def testMap(self):
|
||||
"""Tests outputting a map of the images"""
|
||||
_, _, map_data = self._DoReadFileDtb('55_sections.dts', map=True)
|
||||
_, _, map_data, _ = self._DoReadFileDtb('55_sections.dts', map=True)
|
||||
self.assertEqual('''Position Size Name
|
||||
00000000 00000010 section@0
|
||||
00000000 00000004 u-boot
|
||||
|
@ -952,7 +1018,7 @@ class TestFunctional(unittest.TestCase):
|
|||
|
||||
def testNamePrefix(self):
|
||||
"""Tests that name prefixes are used"""
|
||||
_, _, map_data = self._DoReadFileDtb('56_name_prefix.dts', map=True)
|
||||
_, _, map_data, _ = self._DoReadFileDtb('56_name_prefix.dts', map=True)
|
||||
self.assertEqual('''Position Size Name
|
||||
00000000 00000010 section@0
|
||||
00000000 00000004 ro-u-boot
|
||||
|
@ -960,5 +1026,50 @@ class TestFunctional(unittest.TestCase):
|
|||
00000000 00000004 rw-u-boot
|
||||
''', map_data)
|
||||
|
||||
def testUnknownContents(self):
|
||||
"""Test that obtaining the contents works as expected"""
|
||||
with self.assertRaises(ValueError) as e:
|
||||
self._DoReadFile('57_unknown_contents.dts', True)
|
||||
self.assertIn("Section '/binman': Internal error: Could not complete "
|
||||
"processing of contents: remaining [<_testing.Entry__testing ",
|
||||
str(e.exception))
|
||||
|
||||
def testBadChangeSize(self):
|
||||
"""Test that trying to change the size of an entry fails"""
|
||||
with self.assertRaises(ValueError) as e:
|
||||
self._DoReadFile('59_change_size.dts', True)
|
||||
self.assertIn("Node '/binman/_testing': Cannot update entry size from "
|
||||
'2 to 1', str(e.exception))
|
||||
|
||||
def testUpdateFdt(self):
|
||||
"""Test that we can update the device tree with pos/size info"""
|
||||
_, _, _, out_dtb_fname = self._DoReadFileDtb('60_fdt_update.dts',
|
||||
update_dtb=True)
|
||||
props = self._GetPropTree(out_dtb_fname, ['pos', 'size'])
|
||||
with open('/tmp/x.dtb', 'wb') as outf:
|
||||
with open(out_dtb_fname) as inf:
|
||||
outf.write(inf.read())
|
||||
self.assertEqual({
|
||||
'_testing:pos': 32,
|
||||
'_testing:size': 1,
|
||||
'section@0/u-boot:pos': 0,
|
||||
'section@0/u-boot:size': len(U_BOOT_DATA),
|
||||
'section@0:pos': 0,
|
||||
'section@0:size': 16,
|
||||
|
||||
'section@1/u-boot:pos': 0,
|
||||
'section@1/u-boot:size': len(U_BOOT_DATA),
|
||||
'section@1:pos': 16,
|
||||
'section@1:size': 16,
|
||||
'size': 40
|
||||
}, props)
|
||||
|
||||
def testUpdateFdtBad(self):
|
||||
"""Test that we detect when ProcessFdt never completes"""
|
||||
with self.assertRaises(ValueError) as e:
|
||||
self._DoReadFileDtb('61_fdt_update_bad.dts', update_dtb=True)
|
||||
self.assertIn('Could not complete processing of Fdt: remaining '
|
||||
'[<_testing.Entry__testing', str(e.exception))
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
|
@ -54,6 +54,20 @@ class Image:
|
|||
self._filename = filename
|
||||
self._section = bsection.Section('main-section', self._node)
|
||||
|
||||
def AddMissingProperties(self):
|
||||
"""Add properties that are not present in the device tree
|
||||
|
||||
When binman has completed packing the entries the position and size of
|
||||
each entry are known. But before this the device tree may not specify
|
||||
these. Add any missing properties, with a dummy value, so that the
|
||||
size of the entry is correct. That way we can insert the correct values
|
||||
later.
|
||||
"""
|
||||
self._section.AddMissingProperties()
|
||||
|
||||
def ProcessFdt(self, fdt):
|
||||
return self._section.ProcessFdt(fdt)
|
||||
|
||||
def GetEntryContents(self):
|
||||
"""Call ObtainContents() for the section
|
||||
"""
|
||||
|
@ -79,6 +93,9 @@ class Image:
|
|||
"""Check that entries do not overlap or extend outside the image"""
|
||||
self._section.CheckEntries()
|
||||
|
||||
def SetCalculatedProperties(self):
|
||||
self._section.SetCalculatedProperties()
|
||||
|
||||
def ProcessEntryContents(self):
|
||||
"""Call the ProcessContents() method for each entry
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
import unittest
|
||||
|
||||
from image import Image
|
||||
from elf_test import capture_sys_output
|
||||
from test_util import capture_sys_output
|
||||
|
||||
class TestImage(unittest.TestCase):
|
||||
def testInvalidFormat(self):
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
binman {
|
||||
_testing {
|
||||
return-invalid-entry;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
14
tools/binman/test/57_unknown_contents.dts
Normal file
14
tools/binman/test/57_unknown_contents.dts
Normal file
|
@ -0,0 +1,14 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
binman {
|
||||
_testing {
|
||||
return-unknown-contents;
|
||||
};
|
||||
};
|
||||
};
|
36
tools/binman/test/58_x86_ucode_spl_needs_retry.dts
Normal file
36
tools/binman/test/58_x86_ucode_spl_needs_retry.dts
Normal file
|
@ -0,0 +1,36 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
binman {
|
||||
sort-by-pos;
|
||||
end-at-4gb;
|
||||
size = <0x200>;
|
||||
u-boot-spl-with-ucode-ptr {
|
||||
};
|
||||
|
||||
/*
|
||||
* Microcode goes before the DTB which contains it, so binman
|
||||
* will need to obtain the contents of the next section before
|
||||
* obtaining the contents of this one.
|
||||
*/
|
||||
u-boot-ucode {
|
||||
};
|
||||
|
||||
u-boot-dtb-with-ucode {
|
||||
};
|
||||
};
|
||||
|
||||
microcode {
|
||||
update@0 {
|
||||
data = <0x12345678 0x12345679>;
|
||||
};
|
||||
update@1 {
|
||||
data = <0xabcd0000 0x78235609>;
|
||||
};
|
||||
};
|
||||
};
|
14
tools/binman/test/59_change_size.dts
Normal file
14
tools/binman/test/59_change_size.dts
Normal file
|
@ -0,0 +1,14 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
binman {
|
||||
_testing {
|
||||
bad-update-contents;
|
||||
};
|
||||
};
|
||||
};
|
31
tools/binman/test/60_fdt_update.dts
Normal file
31
tools/binman/test/60_fdt_update.dts
Normal file
|
@ -0,0 +1,31 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
binman {
|
||||
pad-byte = <0x26>;
|
||||
size = <0x28>;
|
||||
section@0 {
|
||||
read-only;
|
||||
name-prefix = "ro-";
|
||||
size = <0x10>;
|
||||
pad-byte = <0x21>;
|
||||
|
||||
u-boot {
|
||||
};
|
||||
};
|
||||
section@1 {
|
||||
name-prefix = "rw-";
|
||||
size = <0x10>;
|
||||
pad-byte = <0x61>;
|
||||
|
||||
u-boot {
|
||||
};
|
||||
};
|
||||
_testing {
|
||||
};
|
||||
};
|
||||
};
|
32
tools/binman/test/61_fdt_update_bad.dts
Normal file
32
tools/binman/test/61_fdt_update_bad.dts
Normal file
|
@ -0,0 +1,32 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
binman {
|
||||
pad-byte = <0x26>;
|
||||
size = <0x28>;
|
||||
section@0 {
|
||||
read-only;
|
||||
name-prefix = "ro-";
|
||||
size = <0x10>;
|
||||
pad-byte = <0x21>;
|
||||
|
||||
u-boot {
|
||||
};
|
||||
};
|
||||
section@1 {
|
||||
name-prefix = "rw-";
|
||||
size = <0x10>;
|
||||
pad-byte = <0x61>;
|
||||
|
||||
u-boot {
|
||||
};
|
||||
};
|
||||
_testing {
|
||||
never-complete-process-fdt;
|
||||
};
|
||||
};
|
||||
};
|
|
@ -211,15 +211,21 @@ class DtbPlatdata(object):
|
|||
Number of argument cells is this is a phandle, else None
|
||||
"""
|
||||
if prop.name in ['clocks']:
|
||||
if not isinstance(prop.value, list):
|
||||
prop.value = [prop.value]
|
||||
val = prop.value
|
||||
if not isinstance(val, list):
|
||||
val = [val]
|
||||
i = 0
|
||||
|
||||
max_args = 0
|
||||
args = []
|
||||
while i < len(val):
|
||||
phandle = fdt_util.fdt32_to_cpu(val[i])
|
||||
# If we get to the end of the list, stop. This can happen
|
||||
# since some nodes have more phandles in the list than others,
|
||||
# but we allocate enough space for the largest list. So those
|
||||
# nodes with shorter lists end up with zeroes at the end.
|
||||
if not phandle:
|
||||
break
|
||||
target = self._fdt.phandle_to_node.get(phandle)
|
||||
if not target:
|
||||
raise ValueError("Cannot parse '%s' in node '%s'" %
|
||||
|
@ -310,7 +316,8 @@ class DtbPlatdata(object):
|
|||
total = na + ns
|
||||
|
||||
if reg.type != fdt.TYPE_INT:
|
||||
raise ValueError("Node '%s' reg property is not an int")
|
||||
raise ValueError("Node '%s' reg property is not an int" %
|
||||
node.name)
|
||||
if len(reg.value) % total:
|
||||
raise ValueError("Node '%s' reg property has %d cells "
|
||||
'which is not a multiple of na + ns = %d + %d)' %
|
||||
|
@ -400,8 +407,6 @@ class DtbPlatdata(object):
|
|||
continue
|
||||
info = self.get_phandle_argc(prop, node.name)
|
||||
if info:
|
||||
if not isinstance(prop.value, list):
|
||||
prop.value = [prop.value]
|
||||
# Process the list as pairs of (phandle, id)
|
||||
pos = 0
|
||||
for args in info.args:
|
||||
|
|
|
@ -35,15 +35,28 @@ our_path = os.path.dirname(os.path.realpath(__file__))
|
|||
sys.path.append(os.path.join(our_path, '../patman'))
|
||||
|
||||
import dtb_platdata
|
||||
import test_util
|
||||
|
||||
def run_tests():
|
||||
"""Run all the test we have for dtoc"""
|
||||
def run_tests(args):
|
||||
"""Run all the test we have for dtoc
|
||||
|
||||
Args:
|
||||
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)
|
||||
"""
|
||||
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,):
|
||||
suite = unittest.TestLoader().loadTestsFromTestCase(module)
|
||||
if test_name:
|
||||
try:
|
||||
suite = unittest.TestLoader().loadTestsFromName(test_name, module)
|
||||
except AttributeError:
|
||||
continue
|
||||
else:
|
||||
suite = unittest.TestLoader().loadTestsFromTestCase(module)
|
||||
suite.run(result)
|
||||
|
||||
print result
|
||||
|
@ -52,10 +65,19 @@ def run_tests():
|
|||
for _, err in result.failures:
|
||||
print err
|
||||
|
||||
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__':
|
||||
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',
|
||||
|
@ -64,11 +86,16 @@ parser.add_option('-o', '--output', action='store', default='-',
|
|||
help='Select output filename')
|
||||
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()
|
||||
|
||||
# Run our meagre tests
|
||||
if options.test:
|
||||
run_tests()
|
||||
run_tests(args)
|
||||
|
||||
elif options.test_coverage:
|
||||
RunTestCoverage()
|
||||
|
||||
else:
|
||||
dtb_platdata.run_steps(args, options.dtb_file, options.include_disabled,
|
||||
|
|
24
tools/dtoc/dtoc_test_add_prop.dts
Normal file
24
tools/dtoc/dtoc_test_add_prop.dts
Normal file
|
@ -0,0 +1,24 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Test device tree file for dtoc
|
||||
*
|
||||
* Copyright 2018 Google, Inc
|
||||
*/
|
||||
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
spl-test {
|
||||
u-boot,dm-pre-reloc;
|
||||
compatible = "sandbox,spl-test";
|
||||
intval = <1>;
|
||||
};
|
||||
|
||||
spl-test2 {
|
||||
u-boot,dm-pre-reloc;
|
||||
compatible = "sandbox,spl-test";
|
||||
intarray = <5>;
|
||||
};
|
||||
};
|
|
@ -5,7 +5,7 @@
|
|||
* Copyright 2017 Google, Inc
|
||||
*/
|
||||
|
||||
/dts-v1/;
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
#address-cells = <1>;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* Copyright 2017 Google, Inc
|
||||
*/
|
||||
|
||||
/dts-v1/;
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
#address-cells = <2>;
|
||||
|
|
17
tools/dtoc/dtoc_test_bad_reg.dts
Normal file
17
tools/dtoc/dtoc_test_bad_reg.dts
Normal file
|
@ -0,0 +1,17 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Test device tree file for dtoc
|
||||
*
|
||||
* Copyright 2018 Google, Inc
|
||||
*/
|
||||
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
spl-test {
|
||||
compatible = "test";
|
||||
reg = "fre";
|
||||
};
|
||||
};
|
17
tools/dtoc/dtoc_test_bad_reg2.dts
Normal file
17
tools/dtoc/dtoc_test_bad_reg2.dts
Normal file
|
@ -0,0 +1,17 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Test device tree file for dtoc
|
||||
*
|
||||
* Copyright 2018 Google, Inc
|
||||
*/
|
||||
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
spl-test {
|
||||
compatible = "test";
|
||||
reg = <1 2 3>;
|
||||
};
|
||||
};
|
|
@ -33,4 +33,10 @@
|
|||
compatible = "source";
|
||||
clocks = <&phandle &phandle_1 11 &phandle_2 12 13 &phandle>;
|
||||
};
|
||||
|
||||
phandle-source2 {
|
||||
u-boot,dm-pre-reloc;
|
||||
compatible = "source";
|
||||
clocks = <&phandle>;
|
||||
};
|
||||
};
|
||||
|
|
16
tools/dtoc/dtoc_test_phandle_bad.dts
Normal file
16
tools/dtoc/dtoc_test_phandle_bad.dts
Normal file
|
@ -0,0 +1,16 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Test device tree file for dtoc
|
||||
*
|
||||
* Copyright 2018 Google, Inc
|
||||
*/
|
||||
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
phandle-source {
|
||||
u-boot,dm-pre-reloc;
|
||||
compatible = "source";
|
||||
clocks = <20>; /* Invalid phandle */
|
||||
};
|
||||
};
|
22
tools/dtoc/dtoc_test_phandle_bad2.dts
Normal file
22
tools/dtoc/dtoc_test_phandle_bad2.dts
Normal file
|
@ -0,0 +1,22 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Test device tree file for dtoc
|
||||
*
|
||||
* Copyright 2018 Google, Inc
|
||||
*/
|
||||
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
phandle: phandle-target {
|
||||
u-boot,dm-pre-reloc;
|
||||
compatible = "target";
|
||||
intval = <0>;
|
||||
};
|
||||
|
||||
phandle-source2 {
|
||||
u-boot,dm-pre-reloc;
|
||||
compatible = "source";
|
||||
clocks = <&phandle>;
|
||||
};
|
||||
};
|
23
tools/dtoc/dtoc_test_phandle_reorder.dts
Normal file
23
tools/dtoc/dtoc_test_phandle_reorder.dts
Normal file
|
@ -0,0 +1,23 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Test device tree file for dtoc
|
||||
*
|
||||
* Copyright 2018 Google, Inc
|
||||
*/
|
||||
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
|
||||
phandle-source2 {
|
||||
u-boot,dm-pre-reloc;
|
||||
compatible = "source";
|
||||
clocks = <&phandle>;
|
||||
};
|
||||
|
||||
phandle: phandle-target {
|
||||
u-boot,dm-pre-reloc;
|
||||
compatible = "target";
|
||||
#clock-cells = <0>;
|
||||
};
|
||||
};
|
23
tools/dtoc/dtoc_test_phandle_single.dts
Normal file
23
tools/dtoc/dtoc_test_phandle_single.dts
Normal file
|
@ -0,0 +1,23 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Test device tree file for dtoc
|
||||
*
|
||||
* Copyright 2018 Google, Inc
|
||||
*/
|
||||
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
phandle: phandle-target {
|
||||
u-boot,dm-pre-reloc;
|
||||
compatible = "target";
|
||||
intval = <0>;
|
||||
#clock-cells = <0>;
|
||||
};
|
||||
|
||||
phandle-source2 {
|
||||
u-boot,dm-pre-reloc;
|
||||
compatible = "source";
|
||||
clocks = <&phandle>;
|
||||
};
|
||||
};
|
|
@ -21,6 +21,7 @@
|
|||
longbytearray = [09 0a 0b 0c 0d 0e 0f 10 11];
|
||||
stringval = "message";
|
||||
stringarray = "multi-word", "message";
|
||||
notstring = [20 21 22 10 00];
|
||||
};
|
||||
|
||||
spl-test2 {
|
||||
|
|
|
@ -10,6 +10,7 @@ import sys
|
|||
|
||||
import fdt_util
|
||||
import libfdt
|
||||
from libfdt import QUIET_NOTFOUND
|
||||
|
||||
# This deals with a device tree, presenting it as an assortment of Node and
|
||||
# Prop objects, representing nodes and properties, respectively. This file
|
||||
|
@ -48,12 +49,8 @@ class Prop:
|
|||
return
|
||||
self.type, self.value = self.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 RefreshOffset(self, poffset):
|
||||
self._offset = poffset
|
||||
|
||||
def Widen(self, newprop):
|
||||
"""Figure out which property type is more general
|
||||
|
@ -138,6 +135,7 @@ class Prop:
|
|||
else:
|
||||
return TYPE_INT, val
|
||||
|
||||
@classmethod
|
||||
def GetEmpty(self, type):
|
||||
"""Get an empty / zero value of the given type
|
||||
|
||||
|
@ -159,6 +157,7 @@ class Prop:
|
|||
Returns:
|
||||
The offset of the property (struct fdt_property) within the file
|
||||
"""
|
||||
self._node._fdt.CheckCache()
|
||||
return self._node._fdt.GetStructOffset(self._offset)
|
||||
|
||||
class Node:
|
||||
|
@ -210,22 +209,22 @@ class Node:
|
|||
This fills in the props and subnodes properties, recursively
|
||||
searching into subnodes so that the entire tree is built.
|
||||
"""
|
||||
fdt_obj = self._fdt._fdt_obj
|
||||
self.props = self._fdt.GetProps(self)
|
||||
phandle = self.props.get('phandle')
|
||||
phandle = fdt_obj.get_phandle(self.Offset())
|
||||
if phandle:
|
||||
val = fdt_util.fdt32_to_cpu(phandle.value)
|
||||
self._fdt.phandle_to_node[val] = self
|
||||
self._fdt.phandle_to_node[phandle] = self
|
||||
|
||||
offset = libfdt.fdt_first_subnode(self._fdt.GetFdt(), self.Offset())
|
||||
offset = fdt_obj.first_subnode(self.Offset(), QUIET_NOTFOUND)
|
||||
while offset >= 0:
|
||||
sep = '' if self.path[-1] == '/' else '/'
|
||||
name = self._fdt._fdt_obj.get_name(offset)
|
||||
name = fdt_obj.get_name(offset)
|
||||
path = self.path + sep + name
|
||||
node = Node(self._fdt, self, offset, name, path)
|
||||
self.subnodes.append(node)
|
||||
|
||||
node.Scan()
|
||||
offset = libfdt.fdt_next_subnode(self._fdt.GetFdt(), offset)
|
||||
offset = fdt_obj.next_subnode(offset, QUIET_NOTFOUND)
|
||||
|
||||
def Refresh(self, my_offset):
|
||||
"""Fix up the _offset for each node, recursively
|
||||
|
@ -233,13 +232,28 @@ class Node:
|
|||
Note: This does not take account of property offsets - these will not
|
||||
be updated.
|
||||
"""
|
||||
fdt_obj = self._fdt._fdt_obj
|
||||
if self._offset != my_offset:
|
||||
#print '%s: %d -> %d\n' % (self.path, self._offset, my_offset)
|
||||
self._offset = my_offset
|
||||
offset = libfdt.fdt_first_subnode(self._fdt.GetFdt(), self._offset)
|
||||
offset = fdt_obj.first_subnode(self._offset, QUIET_NOTFOUND)
|
||||
for subnode in self.subnodes:
|
||||
if subnode.name != fdt_obj.get_name(offset):
|
||||
raise ValueError('Internal error, node name mismatch %s != %s' %
|
||||
(subnode.name, fdt_obj.get_name(offset)))
|
||||
subnode.Refresh(offset)
|
||||
offset = libfdt.fdt_next_subnode(self._fdt.GetFdt(), offset)
|
||||
offset = fdt_obj.next_subnode(offset, QUIET_NOTFOUND)
|
||||
if offset != -libfdt.FDT_ERR_NOTFOUND:
|
||||
raise ValueError('Internal error, offset == %d' % offset)
|
||||
|
||||
poffset = fdt_obj.first_property_offset(self._offset, QUIET_NOTFOUND)
|
||||
while poffset >= 0:
|
||||
p = fdt_obj.get_property_by_offset(poffset)
|
||||
prop = self.props.get(p.name)
|
||||
if not prop:
|
||||
raise ValueError("Internal error, property '%s' missing, "
|
||||
'offset %d' % (p.name, poffset))
|
||||
prop.RefreshOffset(poffset)
|
||||
poffset = fdt_obj.next_property_offset(poffset, QUIET_NOTFOUND)
|
||||
|
||||
def DeleteProp(self, prop_name):
|
||||
"""Delete a property of a node
|
||||
|
@ -251,11 +265,38 @@ class Node:
|
|||
Raises:
|
||||
ValueError if the property does not exist
|
||||
"""
|
||||
CheckErr(libfdt.fdt_delprop(self._fdt.GetFdt(), self.Offset(), prop_name),
|
||||
CheckErr(self._fdt._fdt_obj.delprop(self.Offset(), prop_name),
|
||||
"Node '%s': delete property: '%s'" % (self.path, prop_name))
|
||||
del self.props[prop_name]
|
||||
self._fdt.Invalidate()
|
||||
|
||||
def AddZeroProp(self, prop_name):
|
||||
"""Add a new property to the device tree with an integer value of 0.
|
||||
|
||||
Args:
|
||||
prop_name: Name of property
|
||||
"""
|
||||
fdt_obj = self._fdt._fdt_obj
|
||||
if fdt_obj.setprop_u32(self.Offset(), prop_name, 0,
|
||||
(libfdt.NOSPACE,)) == -libfdt.NOSPACE:
|
||||
fdt_obj.open_into(fdt_obj.totalsize() + 1024)
|
||||
fdt_obj.setprop_u32(self.Offset(), prop_name, 0)
|
||||
self.props[prop_name] = Prop(self, -1, prop_name, '\0' * 4)
|
||||
self._fdt.Invalidate()
|
||||
|
||||
def SetInt(self, prop_name, val):
|
||||
"""Update an integer property int the device tree.
|
||||
|
||||
This is not allowed to change the size of the FDT.
|
||||
|
||||
Args:
|
||||
prop_name: Name of property
|
||||
val: Value to set
|
||||
"""
|
||||
fdt_obj = self._fdt._fdt_obj
|
||||
fdt_obj.setprop_u32(self.Offset(), prop_name, val)
|
||||
|
||||
|
||||
class Fdt:
|
||||
"""Provides simple access to a flat device tree blob using libfdts.
|
||||
|
||||
|
@ -271,8 +312,7 @@ class Fdt:
|
|||
self._fname = fdt_util.EnsureCompiled(self._fname)
|
||||
|
||||
with open(self._fname) as fd:
|
||||
self._fdt = bytearray(fd.read())
|
||||
self._fdt_obj = libfdt.Fdt(self._fdt)
|
||||
self._fdt_obj = libfdt.Fdt(fd.read())
|
||||
|
||||
def Scan(self, root='/'):
|
||||
"""Scan a device tree, building up a tree of Node objects
|
||||
|
@ -284,6 +324,7 @@ class Fdt:
|
|||
|
||||
TODO(sjg@chromium.org): Implement the 'root' parameter
|
||||
"""
|
||||
self._cached_offsets = True
|
||||
self._root = self.Node(self, None, 0, '/', '/')
|
||||
self._root.Scan()
|
||||
|
||||
|
@ -304,7 +345,10 @@ class Fdt:
|
|||
Node object, or None if not found
|
||||
"""
|
||||
node = self._root
|
||||
for part in path.split('/')[1:]:
|
||||
parts = path.split('/')
|
||||
if len(parts) < 2:
|
||||
return None
|
||||
for part in parts[1:]:
|
||||
node = node._FindNode(part)
|
||||
if not node:
|
||||
return None
|
||||
|
@ -316,7 +360,7 @@ class Fdt:
|
|||
If the device tree has changed in memory, write it back to the file.
|
||||
"""
|
||||
with open(self._fname, 'wb') as fd:
|
||||
fd.write(self._fdt)
|
||||
fd.write(self._fdt_obj.as_bytearray())
|
||||
|
||||
def Pack(self):
|
||||
"""Pack the device tree down to its minimum size
|
||||
|
@ -324,23 +368,24 @@ class Fdt:
|
|||
When nodes and properties shrink or are deleted, wasted space can
|
||||
build up in the device tree binary.
|
||||
"""
|
||||
CheckErr(libfdt.fdt_pack(self._fdt), 'pack')
|
||||
fdt_len = libfdt.fdt_totalsize(self._fdt)
|
||||
del self._fdt[fdt_len:]
|
||||
CheckErr(self._fdt_obj.pack(), 'pack')
|
||||
self.Invalidate()
|
||||
|
||||
def GetFdt(self):
|
||||
def GetContents(self):
|
||||
"""Get the contents of the FDT
|
||||
|
||||
Returns:
|
||||
The FDT contents as a string of bytes
|
||||
"""
|
||||
return self._fdt
|
||||
return self._fdt_obj.as_bytearray()
|
||||
|
||||
def CheckErr(errnum, msg):
|
||||
if errnum:
|
||||
raise ValueError('Error %d: %s: %s' %
|
||||
(errnum, libfdt.fdt_strerror(errnum), msg))
|
||||
def GetFdtObj(self):
|
||||
"""Get the contents of the FDT
|
||||
|
||||
Returns:
|
||||
The FDT contents as a libfdt.Fdt object
|
||||
"""
|
||||
return self._fdt_obj
|
||||
|
||||
def GetProps(self, node):
|
||||
"""Get all properties from a node.
|
||||
|
@ -356,13 +401,15 @@ class Fdt:
|
|||
ValueError: if the node does not exist.
|
||||
"""
|
||||
props_dict = {}
|
||||
poffset = libfdt.fdt_first_property_offset(self._fdt, node._offset)
|
||||
poffset = self._fdt_obj.first_property_offset(node._offset,
|
||||
QUIET_NOTFOUND)
|
||||
while poffset >= 0:
|
||||
p = self._fdt_obj.get_property_by_offset(poffset)
|
||||
prop = Prop(node, poffset, p.name, p.value)
|
||||
prop = Prop(node, poffset, p.name, p)
|
||||
props_dict[prop.name] = prop
|
||||
|
||||
poffset = libfdt.fdt_next_property_offset(self._fdt, poffset)
|
||||
poffset = self._fdt_obj.next_property_offset(poffset,
|
||||
QUIET_NOTFOUND)
|
||||
return props_dict
|
||||
|
||||
def Invalidate(self):
|
||||
|
@ -388,7 +435,7 @@ class Fdt:
|
|||
Returns:
|
||||
Position of @offset within the device tree binary
|
||||
"""
|
||||
return libfdt.fdt_off_dt_struct(self._fdt) + offset
|
||||
return self._fdt_obj.off_dt_struct() + offset
|
||||
|
||||
@classmethod
|
||||
def Node(self, fdt, parent, offset, name, path):
|
||||
|
@ -408,7 +455,7 @@ class Fdt:
|
|||
return node
|
||||
|
||||
def FdtScan(fname):
|
||||
"""Returns a new Fdt object from the implementation we are using"""
|
||||
"""Returns a new Fdt object"""
|
||||
dtb = Fdt(fname)
|
||||
dtb.Scan()
|
||||
return dtb
|
||||
|
|
|
@ -13,6 +13,14 @@ import tempfile
|
|||
import command
|
||||
import tools
|
||||
|
||||
VERSION3 = sys.version_info > (3, 0)
|
||||
|
||||
def get_plain_bytes(val):
|
||||
"""Handle Python 3 strings"""
|
||||
if isinstance(val, bytes):
|
||||
val = val.decode('utf-8')
|
||||
return val.encode('raw_unicode_escape')
|
||||
|
||||
def fdt32_to_cpu(val):
|
||||
"""Convert a device tree cell to an integer
|
||||
|
||||
|
@ -22,10 +30,9 @@ def fdt32_to_cpu(val):
|
|||
Return:
|
||||
A native-endian integer value
|
||||
"""
|
||||
if sys.version_info > (3, 0):
|
||||
if isinstance(val, bytes):
|
||||
val = val.decode('utf-8')
|
||||
val = val.encode('raw_unicode_escape')
|
||||
if VERSION3:
|
||||
# This code is not reached in Python 2
|
||||
val = get_plain_bytes(val) # pragma: no cover
|
||||
return struct.unpack('>I', val)[0]
|
||||
|
||||
def fdt_cells_to_cpu(val, cells):
|
||||
|
@ -44,7 +51,7 @@ def fdt_cells_to_cpu(val, cells):
|
|||
out = out << 32 | fdt32_to_cpu(val[1])
|
||||
return out
|
||||
|
||||
def EnsureCompiled(fname):
|
||||
def EnsureCompiled(fname, capture_stderr=False):
|
||||
"""Compile an fdt .dts source file into a .dtb binary blob if needed.
|
||||
|
||||
Args:
|
||||
|
@ -79,17 +86,17 @@ def EnsureCompiled(fname):
|
|||
args.extend(search_list)
|
||||
args.append(dts_input)
|
||||
dtc = os.environ.get('DTC') or 'dtc'
|
||||
command.Run(dtc, *args)
|
||||
command.Run(dtc, *args, capture_stderr=capture_stderr)
|
||||
return dtb_output
|
||||
|
||||
def GetInt(node, propname, default=None):
|
||||
prop = node.props.get(propname)
|
||||
if not prop:
|
||||
return default
|
||||
value = fdt32_to_cpu(prop.value)
|
||||
if type(value) == type(list):
|
||||
raise ValueError("Node '%s' property '%' has list value: expecting"
|
||||
if isinstance(prop.value, list):
|
||||
raise ValueError("Node '%s' property '%s' has list value: expecting "
|
||||
"a single integer" % (node.name, propname))
|
||||
value = fdt32_to_cpu(prop.value)
|
||||
return value
|
||||
|
||||
def GetString(node, propname, default=None):
|
||||
|
@ -97,8 +104,8 @@ def GetString(node, propname, default=None):
|
|||
if not prop:
|
||||
return default
|
||||
value = prop.value
|
||||
if type(value) == type(list):
|
||||
raise ValueError("Node '%s' property '%' has list value: expecting"
|
||||
if isinstance(value, list):
|
||||
raise ValueError("Node '%s' property '%s' has list value: expecting "
|
||||
"a single string" % (node.name, propname))
|
||||
return value
|
||||
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
|
||||
"""Tests for the dtb_platdata module
|
||||
|
||||
This includes unit tests for some functions and functional tests for
|
||||
This includes unit tests for some functions and functional tests for the dtoc
|
||||
tool.
|
||||
"""
|
||||
|
||||
import collections
|
||||
|
@ -19,6 +20,7 @@ from dtb_platdata import get_value
|
|||
from dtb_platdata import tab_to
|
||||
import fdt
|
||||
import fdt_util
|
||||
import test_util
|
||||
import tools
|
||||
|
||||
our_path = os.path.dirname(os.path.realpath(__file__))
|
||||
|
@ -45,16 +47,19 @@ C_HEADER = '''/*
|
|||
'''
|
||||
|
||||
|
||||
def get_dtb_file(dts_fname):
|
||||
|
||||
def get_dtb_file(dts_fname, capture_stderr=False):
|
||||
"""Compile a .dts file to a .dtb
|
||||
|
||||
Args:
|
||||
dts_fname: Filename of .dts file in the current directory
|
||||
capture_stderr: True to capture and discard stderr output
|
||||
|
||||
Returns:
|
||||
Filename of compiled file in output directory
|
||||
"""
|
||||
return fdt_util.EnsureCompiled(os.path.join(our_path, dts_fname))
|
||||
return fdt_util.EnsureCompiled(os.path.join(our_path, dts_fname),
|
||||
capture_stderr=capture_stderr)
|
||||
|
||||
|
||||
class TestDtoc(unittest.TestCase):
|
||||
|
@ -67,6 +72,34 @@ class TestDtoc(unittest.TestCase):
|
|||
def tearDownClass(cls):
|
||||
tools._RemoveOutputDir()
|
||||
|
||||
def _WritePythonString(self, fname, data):
|
||||
"""Write a string with tabs expanded as done in this Python file
|
||||
|
||||
Args:
|
||||
fname: Filename to write to
|
||||
data: Raw string to convert
|
||||
"""
|
||||
data = data.replace('\t', '\\t')
|
||||
with open(fname, 'w') as fd:
|
||||
fd.write(data)
|
||||
|
||||
def _CheckStrings(self, expected, actual):
|
||||
"""Check that a string matches its expected value
|
||||
|
||||
If the strings do not match, they are written to the /tmp directory in
|
||||
the same Python format as is used here in the test. This allows for
|
||||
easy comparison and update of the tests.
|
||||
|
||||
Args:
|
||||
expected: Expected string
|
||||
actual: Actual string
|
||||
"""
|
||||
if expected != actual:
|
||||
self._WritePythonString('/tmp/binman.expected', expected)
|
||||
self._WritePythonString('/tmp/binman.actual', actual)
|
||||
print 'Failures written to /tmp/binman.{expected,actual}'
|
||||
self.assertEquals(expected, actual)
|
||||
|
||||
def test_name(self):
|
||||
"""Test conversion of device tree names to C identifiers"""
|
||||
self.assertEqual('serial_at_0x12', conv_name_to_c('serial@0x12'))
|
||||
|
@ -137,7 +170,7 @@ class TestDtoc(unittest.TestCase):
|
|||
dtb_platdata.run_steps(['struct'], dtb_file, False, output)
|
||||
with open(output) as infile:
|
||||
data = infile.read()
|
||||
self.assertEqual(HEADER + '''
|
||||
self._CheckStrings(HEADER + '''
|
||||
struct dtd_sandbox_i2c_test {
|
||||
};
|
||||
struct dtd_sandbox_pmic_test {
|
||||
|
@ -151,6 +184,7 @@ struct dtd_sandbox_spl_test {
|
|||
\tfdt32_t\t\tintarray[4];
|
||||
\tfdt32_t\t\tintval;
|
||||
\tunsigned char\tlongbytearray[9];
|
||||
\tunsigned char\tnotstring[5];
|
||||
\tconst char *\tstringarray[3];
|
||||
\tconst char *\tstringval;
|
||||
};
|
||||
|
@ -161,11 +195,12 @@ struct dtd_sandbox_spl_test_2 {
|
|||
dtb_platdata.run_steps(['platdata'], dtb_file, False, output)
|
||||
with open(output) as infile:
|
||||
data = infile.read()
|
||||
self.assertEqual(C_HEADER + '''
|
||||
self._CheckStrings(C_HEADER + '''
|
||||
static struct dtd_sandbox_spl_test dtv_spl_test = {
|
||||
\t.bytearray\t\t= {0x6, 0x0, 0x0},
|
||||
\t.byteval\t\t= 0x5,
|
||||
\t.intval\t\t\t= 0x1,
|
||||
\t.notstring\t\t= {0x20, 0x21, 0x22, 0x10, 0x0},
|
||||
\t.longbytearray\t\t= {0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10,
|
||||
\t\t0x11},
|
||||
\t.stringval\t\t= "message",
|
||||
|
@ -239,7 +274,7 @@ U_BOOT_DEVICE(pmic_at_9) = {
|
|||
dtb_platdata.run_steps(['struct'], dtb_file, False, output)
|
||||
with open(output) as infile:
|
||||
data = infile.read()
|
||||
self.assertEqual(HEADER + '''
|
||||
self._CheckStrings(HEADER + '''
|
||||
struct dtd_source {
|
||||
\tstruct phandle_2_arg clocks[4];
|
||||
};
|
||||
|
@ -251,7 +286,7 @@ struct dtd_target {
|
|||
dtb_platdata.run_steps(['platdata'], dtb_file, False, output)
|
||||
with open(output) as infile:
|
||||
data = infile.read()
|
||||
self.assertEqual(C_HEADER + '''
|
||||
self._CheckStrings(C_HEADER + '''
|
||||
static struct dtd_target dtv_phandle_target = {
|
||||
\t.intval\t\t\t= 0x0,
|
||||
};
|
||||
|
@ -292,8 +327,80 @@ U_BOOT_DEVICE(phandle_source) = {
|
|||
\t.platdata_size\t= sizeof(dtv_phandle_source),
|
||||
};
|
||||
|
||||
static struct dtd_source dtv_phandle_source2 = {
|
||||
\t.clocks\t\t\t= {
|
||||
\t\t\t{&dtv_phandle_target, {}},},
|
||||
};
|
||||
U_BOOT_DEVICE(phandle_source2) = {
|
||||
\t.name\t\t= "source",
|
||||
\t.platdata\t= &dtv_phandle_source2,
|
||||
\t.platdata_size\t= sizeof(dtv_phandle_source2),
|
||||
};
|
||||
|
||||
''', data)
|
||||
|
||||
def test_phandle_single(self):
|
||||
"""Test output from a node containing a phandle reference"""
|
||||
dtb_file = get_dtb_file('dtoc_test_phandle_single.dts')
|
||||
output = tools.GetOutputFilename('output')
|
||||
dtb_platdata.run_steps(['struct'], dtb_file, False, output)
|
||||
with open(output) as infile:
|
||||
data = infile.read()
|
||||
self._CheckStrings(HEADER + '''
|
||||
struct dtd_source {
|
||||
\tstruct phandle_0_arg clocks[1];
|
||||
};
|
||||
struct dtd_target {
|
||||
\tfdt32_t\t\tintval;
|
||||
};
|
||||
''', data)
|
||||
|
||||
def test_phandle_reorder(self):
|
||||
"""Test that phandle targets are generated before their references"""
|
||||
dtb_file = get_dtb_file('dtoc_test_phandle_reorder.dts')
|
||||
output = tools.GetOutputFilename('output')
|
||||
dtb_platdata.run_steps(['platdata'], dtb_file, False, output)
|
||||
with open(output) as infile:
|
||||
data = infile.read()
|
||||
self._CheckStrings(C_HEADER + '''
|
||||
static struct dtd_target dtv_phandle_target = {
|
||||
};
|
||||
U_BOOT_DEVICE(phandle_target) = {
|
||||
\t.name\t\t= "target",
|
||||
\t.platdata\t= &dtv_phandle_target,
|
||||
\t.platdata_size\t= sizeof(dtv_phandle_target),
|
||||
};
|
||||
|
||||
static struct dtd_source dtv_phandle_source2 = {
|
||||
\t.clocks\t\t\t= {
|
||||
\t\t\t{&dtv_phandle_target, {}},},
|
||||
};
|
||||
U_BOOT_DEVICE(phandle_source2) = {
|
||||
\t.name\t\t= "source",
|
||||
\t.platdata\t= &dtv_phandle_source2,
|
||||
\t.platdata_size\t= sizeof(dtv_phandle_source2),
|
||||
};
|
||||
|
||||
''', data)
|
||||
|
||||
def test_phandle_bad(self):
|
||||
"""Test a node containing an invalid phandle fails"""
|
||||
dtb_file = get_dtb_file('dtoc_test_phandle_bad.dts')
|
||||
output = tools.GetOutputFilename('output')
|
||||
with self.assertRaises(ValueError) as e:
|
||||
dtb_platdata.run_steps(['struct'], dtb_file, False, output)
|
||||
self.assertIn("Cannot parse 'clocks' in node 'phandle-source'",
|
||||
str(e.exception))
|
||||
|
||||
def test_phandle_bad2(self):
|
||||
"""Test a phandle target missing its #*-cells property"""
|
||||
dtb_file = get_dtb_file('dtoc_test_phandle_bad2.dts')
|
||||
output = tools.GetOutputFilename('output')
|
||||
with self.assertRaises(ValueError) as e:
|
||||
dtb_platdata.run_steps(['struct'], dtb_file, False, output)
|
||||
self.assertIn("Node 'phandle-target' has no '#clock-cells' property",
|
||||
str(e.exception))
|
||||
|
||||
def test_aliases(self):
|
||||
"""Test output from a node with multiple compatible strings"""
|
||||
dtb_file = get_dtb_file('dtoc_test_aliases.dts')
|
||||
|
@ -301,7 +408,7 @@ U_BOOT_DEVICE(phandle_source) = {
|
|||
dtb_platdata.run_steps(['struct'], dtb_file, False, output)
|
||||
with open(output) as infile:
|
||||
data = infile.read()
|
||||
self.assertEqual(HEADER + '''
|
||||
self._CheckStrings(HEADER + '''
|
||||
struct dtd_compat1 {
|
||||
\tfdt32_t\t\tintval;
|
||||
};
|
||||
|
@ -312,7 +419,7 @@ struct dtd_compat1 {
|
|||
dtb_platdata.run_steps(['platdata'], dtb_file, False, output)
|
||||
with open(output) as infile:
|
||||
data = infile.read()
|
||||
self.assertEqual(C_HEADER + '''
|
||||
self._CheckStrings(C_HEADER + '''
|
||||
static struct dtd_compat1 dtv_spl_test = {
|
||||
\t.intval\t\t\t= 0x1,
|
||||
};
|
||||
|
@ -331,7 +438,7 @@ U_BOOT_DEVICE(spl_test) = {
|
|||
dtb_platdata.run_steps(['struct'], dtb_file, False, output)
|
||||
with open(output) as infile:
|
||||
data = infile.read()
|
||||
self.assertEqual(HEADER + '''
|
||||
self._CheckStrings(HEADER + '''
|
||||
struct dtd_test1 {
|
||||
\tfdt64_t\t\treg[2];
|
||||
};
|
||||
|
@ -346,7 +453,7 @@ struct dtd_test3 {
|
|||
dtb_platdata.run_steps(['platdata'], dtb_file, False, output)
|
||||
with open(output) as infile:
|
||||
data = infile.read()
|
||||
self.assertEqual(C_HEADER + '''
|
||||
self._CheckStrings(C_HEADER + '''
|
||||
static struct dtd_test1 dtv_test1 = {
|
||||
\t.reg\t\t\t= {0x1234, 0x5678},
|
||||
};
|
||||
|
@ -383,7 +490,7 @@ U_BOOT_DEVICE(test3) = {
|
|||
dtb_platdata.run_steps(['struct'], dtb_file, False, output)
|
||||
with open(output) as infile:
|
||||
data = infile.read()
|
||||
self.assertEqual(HEADER + '''
|
||||
self._CheckStrings(HEADER + '''
|
||||
struct dtd_test1 {
|
||||
\tfdt32_t\t\treg[2];
|
||||
};
|
||||
|
@ -395,7 +502,7 @@ struct dtd_test2 {
|
|||
dtb_platdata.run_steps(['platdata'], dtb_file, False, output)
|
||||
with open(output) as infile:
|
||||
data = infile.read()
|
||||
self.assertEqual(C_HEADER + '''
|
||||
self._CheckStrings(C_HEADER + '''
|
||||
static struct dtd_test1 dtv_test1 = {
|
||||
\t.reg\t\t\t= {0x1234, 0x5678},
|
||||
};
|
||||
|
@ -423,7 +530,7 @@ U_BOOT_DEVICE(test2) = {
|
|||
dtb_platdata.run_steps(['struct'], dtb_file, False, output)
|
||||
with open(output) as infile:
|
||||
data = infile.read()
|
||||
self.assertEqual(HEADER + '''
|
||||
self._CheckStrings(HEADER + '''
|
||||
struct dtd_test1 {
|
||||
\tfdt64_t\t\treg[2];
|
||||
};
|
||||
|
@ -438,7 +545,7 @@ struct dtd_test3 {
|
|||
dtb_platdata.run_steps(['platdata'], dtb_file, False, output)
|
||||
with open(output) as infile:
|
||||
data = infile.read()
|
||||
self.assertEqual(C_HEADER + '''
|
||||
self._CheckStrings(C_HEADER + '''
|
||||
static struct dtd_test1 dtv_test1 = {
|
||||
\t.reg\t\t\t= {0x123400000000, 0x5678},
|
||||
};
|
||||
|
@ -475,7 +582,7 @@ U_BOOT_DEVICE(test3) = {
|
|||
dtb_platdata.run_steps(['struct'], dtb_file, False, output)
|
||||
with open(output) as infile:
|
||||
data = infile.read()
|
||||
self.assertEqual(HEADER + '''
|
||||
self._CheckStrings(HEADER + '''
|
||||
struct dtd_test1 {
|
||||
\tfdt64_t\t\treg[2];
|
||||
};
|
||||
|
@ -490,7 +597,7 @@ struct dtd_test3 {
|
|||
dtb_platdata.run_steps(['platdata'], dtb_file, False, output)
|
||||
with open(output) as infile:
|
||||
data = infile.read()
|
||||
self.assertEqual(C_HEADER + '''
|
||||
self._CheckStrings(C_HEADER + '''
|
||||
static struct dtd_test1 dtv_test1 = {
|
||||
\t.reg\t\t\t= {0x1234, 0x567800000000},
|
||||
};
|
||||
|
@ -519,3 +626,83 @@ U_BOOT_DEVICE(test3) = {
|
|||
};
|
||||
|
||||
''', data)
|
||||
|
||||
def test_bad_reg(self):
|
||||
"""Test that a reg property with an invalid type generates an error"""
|
||||
# Capture stderr since dtc will emit warnings for this file
|
||||
dtb_file = get_dtb_file('dtoc_test_bad_reg.dts', capture_stderr=True)
|
||||
output = tools.GetOutputFilename('output')
|
||||
with self.assertRaises(ValueError) as e:
|
||||
dtb_platdata.run_steps(['struct'], dtb_file, False, output)
|
||||
self.assertIn("Node 'spl-test' reg property is not an int",
|
||||
str(e.exception))
|
||||
|
||||
def test_bad_reg2(self):
|
||||
"""Test that a reg property with an invalid cell count is detected"""
|
||||
# Capture stderr since dtc will emit warnings for this file
|
||||
dtb_file = get_dtb_file('dtoc_test_bad_reg2.dts', capture_stderr=True)
|
||||
output = tools.GetOutputFilename('output')
|
||||
with self.assertRaises(ValueError) as e:
|
||||
dtb_platdata.run_steps(['struct'], dtb_file, False, output)
|
||||
self.assertIn("Node 'spl-test' reg property has 3 cells which is not a multiple of na + ns = 1 + 1)",
|
||||
str(e.exception))
|
||||
|
||||
def test_add_prop(self):
|
||||
"""Test that a subequent node can add a new property to a struct"""
|
||||
dtb_file = get_dtb_file('dtoc_test_add_prop.dts')
|
||||
output = tools.GetOutputFilename('output')
|
||||
dtb_platdata.run_steps(['struct'], dtb_file, False, output)
|
||||
with open(output) as infile:
|
||||
data = infile.read()
|
||||
self._CheckStrings(HEADER + '''
|
||||
struct dtd_sandbox_spl_test {
|
||||
\tfdt32_t\t\tintarray;
|
||||
\tfdt32_t\t\tintval;
|
||||
};
|
||||
''', data)
|
||||
|
||||
dtb_platdata.run_steps(['platdata'], dtb_file, False, output)
|
||||
with open(output) as infile:
|
||||
data = infile.read()
|
||||
self._CheckStrings(C_HEADER + '''
|
||||
static struct dtd_sandbox_spl_test dtv_spl_test = {
|
||||
\t.intval\t\t\t= 0x1,
|
||||
};
|
||||
U_BOOT_DEVICE(spl_test) = {
|
||||
\t.name\t\t= "sandbox_spl_test",
|
||||
\t.platdata\t= &dtv_spl_test,
|
||||
\t.platdata_size\t= sizeof(dtv_spl_test),
|
||||
};
|
||||
|
||||
static struct dtd_sandbox_spl_test dtv_spl_test2 = {
|
||||
\t.intarray\t\t= 0x5,
|
||||
};
|
||||
U_BOOT_DEVICE(spl_test2) = {
|
||||
\t.name\t\t= "sandbox_spl_test",
|
||||
\t.platdata\t= &dtv_spl_test2,
|
||||
\t.platdata_size\t= sizeof(dtv_spl_test2),
|
||||
};
|
||||
|
||||
''', data)
|
||||
|
||||
def testStdout(self):
|
||||
"""Test output to stdout"""
|
||||
dtb_file = get_dtb_file('dtoc_test_simple.dts')
|
||||
with test_util.capture_sys_output() as (stdout, stderr):
|
||||
dtb_platdata.run_steps(['struct'], dtb_file, False, '-')
|
||||
|
||||
def testNoCommand(self):
|
||||
"""Test running dtoc without a command"""
|
||||
with self.assertRaises(ValueError) as e:
|
||||
dtb_platdata.run_steps([], '', False, '')
|
||||
self.assertIn("Please specify a command: struct, platdata",
|
||||
str(e.exception))
|
||||
|
||||
def testBadCommand(self):
|
||||
"""Test running dtoc with an invalid command"""
|
||||
dtb_file = get_dtb_file('dtoc_test_simple.dts')
|
||||
output = tools.GetOutputFilename('output')
|
||||
with self.assertRaises(ValueError) as e:
|
||||
dtb_platdata.run_steps(['invalid-cmd'], dtb_file, False, output)
|
||||
self.assertIn("Unknown command 'invalid-cmd': (use: struct, platdata)",
|
||||
str(e.exception))
|
||||
|
|
1
tools/dtoc/test_fdt
Symbolic link
1
tools/dtoc/test_fdt
Symbolic link
|
@ -0,0 +1 @@
|
|||
test_fdt.py
|
450
tools/dtoc/test_fdt.py
Executable file
450
tools/dtoc/test_fdt.py
Executable file
|
@ -0,0 +1,450 @@
|
|||
#!/usr/bin/python
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
# Copyright (c) 2018 Google, Inc
|
||||
# Written by Simon Glass <sjg@chromium.org>
|
||||
#
|
||||
|
||||
from optparse import OptionParser
|
||||
import glob
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
# Bring in the patman libraries
|
||||
our_path = os.path.dirname(os.path.realpath(__file__))
|
||||
for dirname in ['../patman', '..']:
|
||||
sys.path.insert(0, os.path.join(our_path, dirname))
|
||||
|
||||
import command
|
||||
import fdt
|
||||
from fdt import TYPE_BYTE, TYPE_INT, TYPE_STRING, TYPE_BOOL
|
||||
import fdt_util
|
||||
from fdt_util import fdt32_to_cpu
|
||||
import libfdt
|
||||
import test_util
|
||||
import tools
|
||||
|
||||
def _GetPropertyValue(dtb, node, prop_name):
|
||||
"""Low-level function to get the property value based on its offset
|
||||
|
||||
This looks directly in the device tree at the property's offset to find
|
||||
its value. It is useful as a check that the property is in the correct
|
||||
place.
|
||||
|
||||
Args:
|
||||
node: Node to look in
|
||||
prop_name: Property name to find
|
||||
|
||||
Returns:
|
||||
Tuple:
|
||||
Prop object found
|
||||
Value of property as a string (found using property offset)
|
||||
"""
|
||||
prop = node.props[prop_name]
|
||||
|
||||
# Add 12, which is sizeof(struct fdt_property), to get to start of data
|
||||
offset = prop.GetOffset() + 12
|
||||
data = dtb.GetContents()[offset:offset + len(prop.value)]
|
||||
return prop, [chr(x) for x in data]
|
||||
|
||||
|
||||
class TestFdt(unittest.TestCase):
|
||||
"""Tests for the Fdt module
|
||||
|
||||
This includes unit tests for some functions and functional tests for the fdt
|
||||
module.
|
||||
"""
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
tools.PrepareOutputDir(None)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
tools._FinaliseForTest()
|
||||
|
||||
def setUp(self):
|
||||
self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
|
||||
|
||||
def testFdt(self):
|
||||
"""Test that we can open an Fdt"""
|
||||
self.dtb.Scan()
|
||||
root = self.dtb.GetRoot()
|
||||
self.assertTrue(isinstance(root, fdt.Node))
|
||||
|
||||
def testGetNode(self):
|
||||
"""Test the GetNode() method"""
|
||||
node = self.dtb.GetNode('/spl-test')
|
||||
self.assertTrue(isinstance(node, fdt.Node))
|
||||
node = self.dtb.GetNode('/i2c@0/pmic@9')
|
||||
self.assertTrue(isinstance(node, fdt.Node))
|
||||
self.assertEqual('pmic@9', node.name)
|
||||
self.assertIsNone(self.dtb.GetNode('/i2c@0/pmic@9/missing'))
|
||||
|
||||
def testFlush(self):
|
||||
"""Check that we can flush the device tree out to its file"""
|
||||
fname = self.dtb._fname
|
||||
with open(fname) as fd:
|
||||
data = fd.read()
|
||||
os.remove(fname)
|
||||
with self.assertRaises(IOError):
|
||||
open(fname)
|
||||
self.dtb.Flush()
|
||||
with open(fname) as fd:
|
||||
data = fd.read()
|
||||
|
||||
def testPack(self):
|
||||
"""Test that packing a device tree works"""
|
||||
self.dtb.Pack()
|
||||
|
||||
def testGetFdt(self):
|
||||
"""Tetst that we can access the raw device-tree data"""
|
||||
self.assertTrue(isinstance(self.dtb.GetContents(), bytearray))
|
||||
|
||||
def testGetProps(self):
|
||||
"""Tests obtaining a list of properties"""
|
||||
node = self.dtb.GetNode('/spl-test')
|
||||
props = self.dtb.GetProps(node)
|
||||
self.assertEqual(['boolval', 'bytearray', 'byteval', 'compatible',
|
||||
'intarray', 'intval', 'longbytearray', 'notstring',
|
||||
'stringarray', 'stringval', 'u-boot,dm-pre-reloc'],
|
||||
sorted(props.keys()))
|
||||
|
||||
def testCheckError(self):
|
||||
"""Tests the ChecKError() function"""
|
||||
with self.assertRaises(ValueError) as e:
|
||||
fdt.CheckErr(-libfdt.NOTFOUND, 'hello')
|
||||
self.assertIn('FDT_ERR_NOTFOUND: hello', str(e.exception))
|
||||
|
||||
|
||||
class TestNode(unittest.TestCase):
|
||||
"""Test operation of the Node class"""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
tools.PrepareOutputDir(None)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
tools._FinaliseForTest()
|
||||
|
||||
def setUp(self):
|
||||
self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
|
||||
self.node = self.dtb.GetNode('/spl-test')
|
||||
|
||||
def testOffset(self):
|
||||
"""Tests that we can obtain the offset of a node"""
|
||||
self.assertTrue(self.node.Offset() > 0)
|
||||
|
||||
def testDelete(self):
|
||||
"""Tests that we can delete a property"""
|
||||
node2 = self.dtb.GetNode('/spl-test2')
|
||||
offset1 = node2.Offset()
|
||||
self.node.DeleteProp('intval')
|
||||
offset2 = node2.Offset()
|
||||
self.assertTrue(offset2 < offset1)
|
||||
self.node.DeleteProp('intarray')
|
||||
offset3 = node2.Offset()
|
||||
self.assertTrue(offset3 < offset2)
|
||||
with self.assertRaises(libfdt.FdtException):
|
||||
self.node.DeleteProp('missing')
|
||||
|
||||
def testDeleteGetOffset(self):
|
||||
"""Test that property offset update when properties are deleted"""
|
||||
self.node.DeleteProp('intval')
|
||||
prop, value = _GetPropertyValue(self.dtb, self.node, 'longbytearray')
|
||||
self.assertEqual(prop.value, value)
|
||||
|
||||
def testFindNode(self):
|
||||
"""Tests that we can find a node using the _FindNode() functoin"""
|
||||
node = self.dtb.GetRoot()._FindNode('i2c@0')
|
||||
self.assertEqual('i2c@0', node.name)
|
||||
subnode = node._FindNode('pmic@9')
|
||||
self.assertEqual('pmic@9', subnode.name)
|
||||
self.assertEqual(None, node._FindNode('missing'))
|
||||
|
||||
def testRefreshMissingNode(self):
|
||||
"""Test refreshing offsets when an extra node is present in dtb"""
|
||||
# Delete it from our tables, not the device tree
|
||||
del self.dtb._root.subnodes[-1]
|
||||
with self.assertRaises(ValueError) as e:
|
||||
self.dtb.Refresh()
|
||||
self.assertIn('Internal error, offset', str(e.exception))
|
||||
|
||||
def testRefreshExtraNode(self):
|
||||
"""Test refreshing offsets when an expected node is missing"""
|
||||
# Delete it from the device tre, not our tables
|
||||
self.dtb.GetFdtObj().del_node(self.node.Offset())
|
||||
with self.assertRaises(ValueError) as e:
|
||||
self.dtb.Refresh()
|
||||
self.assertIn('Internal error, node name mismatch '
|
||||
'spl-test != spl-test2', str(e.exception))
|
||||
|
||||
def testRefreshMissingProp(self):
|
||||
"""Test refreshing offsets when an extra property is present in dtb"""
|
||||
# Delete it from our tables, not the device tree
|
||||
del self.node.props['notstring']
|
||||
with self.assertRaises(ValueError) as e:
|
||||
self.dtb.Refresh()
|
||||
self.assertIn("Internal error, property 'notstring' missing, offset ",
|
||||
str(e.exception))
|
||||
|
||||
|
||||
class TestProp(unittest.TestCase):
|
||||
"""Test operation of the Prop class"""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
tools.PrepareOutputDir(None)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
tools._FinaliseForTest()
|
||||
|
||||
def setUp(self):
|
||||
self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
|
||||
self.node = self.dtb.GetNode('/spl-test')
|
||||
self.fdt = self.dtb.GetFdtObj()
|
||||
|
||||
def testMissingNode(self):
|
||||
self.assertEqual(None, self.dtb.GetNode('missing'))
|
||||
|
||||
def testPhandle(self):
|
||||
dtb = fdt.FdtScan('tools/dtoc/dtoc_test_phandle.dts')
|
||||
node = dtb.GetNode('/phandle-source2')
|
||||
prop = node.props['clocks']
|
||||
self.assertTrue(fdt32_to_cpu(prop.value) > 0)
|
||||
|
||||
def _ConvertProp(self, prop_name):
|
||||
"""Helper function to look up a property in self.node and return it
|
||||
|
||||
Args:
|
||||
Property name to find
|
||||
|
||||
Return fdt.Prop object for this property
|
||||
"""
|
||||
p = self.fdt.get_property(self.node.Offset(), prop_name)
|
||||
return fdt.Prop(self.node, -1, prop_name, p)
|
||||
|
||||
def testMakeProp(self):
|
||||
"""Test we can convert all the the types that are supported"""
|
||||
prop = self._ConvertProp('boolval')
|
||||
self.assertEqual(fdt.TYPE_BOOL, prop.type)
|
||||
self.assertEqual(True, prop.value)
|
||||
|
||||
prop = self._ConvertProp('intval')
|
||||
self.assertEqual(fdt.TYPE_INT, prop.type)
|
||||
self.assertEqual(1, fdt32_to_cpu(prop.value))
|
||||
|
||||
prop = self._ConvertProp('intarray')
|
||||
self.assertEqual(fdt.TYPE_INT, prop.type)
|
||||
val = [fdt32_to_cpu(val) for val in prop.value]
|
||||
self.assertEqual([2, 3, 4], val)
|
||||
|
||||
prop = self._ConvertProp('byteval')
|
||||
self.assertEqual(fdt.TYPE_BYTE, prop.type)
|
||||
self.assertEqual(5, ord(prop.value))
|
||||
|
||||
prop = self._ConvertProp('longbytearray')
|
||||
self.assertEqual(fdt.TYPE_BYTE, prop.type)
|
||||
val = [ord(val) for val in prop.value]
|
||||
self.assertEqual([9, 10, 11, 12, 13, 14, 15, 16, 17], val)
|
||||
|
||||
prop = self._ConvertProp('stringval')
|
||||
self.assertEqual(fdt.TYPE_STRING, prop.type)
|
||||
self.assertEqual('message', prop.value)
|
||||
|
||||
prop = self._ConvertProp('stringarray')
|
||||
self.assertEqual(fdt.TYPE_STRING, prop.type)
|
||||
self.assertEqual(['multi-word', 'message'], prop.value)
|
||||
|
||||
prop = self._ConvertProp('notstring')
|
||||
self.assertEqual(fdt.TYPE_BYTE, prop.type)
|
||||
val = [ord(val) for val in prop.value]
|
||||
self.assertEqual([0x20, 0x21, 0x22, 0x10, 0], val)
|
||||
|
||||
def testGetEmpty(self):
|
||||
"""Tests the GetEmpty() function for the various supported types"""
|
||||
self.assertEqual(True, fdt.Prop.GetEmpty(fdt.TYPE_BOOL))
|
||||
self.assertEqual(chr(0), fdt.Prop.GetEmpty(fdt.TYPE_BYTE))
|
||||
self.assertEqual(chr(0) * 4, fdt.Prop.GetEmpty(fdt.TYPE_INT))
|
||||
self.assertEqual('', fdt.Prop.GetEmpty(fdt.TYPE_STRING))
|
||||
|
||||
def testGetOffset(self):
|
||||
"""Test we can get the offset of a property"""
|
||||
prop, value = _GetPropertyValue(self.dtb, self.node, 'longbytearray')
|
||||
self.assertEqual(prop.value, value)
|
||||
|
||||
def testWiden(self):
|
||||
"""Test widening of values"""
|
||||
node2 = self.dtb.GetNode('/spl-test2')
|
||||
prop = self.node.props['intval']
|
||||
|
||||
# No action
|
||||
prop2 = node2.props['intval']
|
||||
prop.Widen(prop2)
|
||||
self.assertEqual(fdt.TYPE_INT, prop.type)
|
||||
self.assertEqual(1, fdt32_to_cpu(prop.value))
|
||||
|
||||
# Convert singla value to array
|
||||
prop2 = self.node.props['intarray']
|
||||
prop.Widen(prop2)
|
||||
self.assertEqual(fdt.TYPE_INT, prop.type)
|
||||
self.assertTrue(isinstance(prop.value, list))
|
||||
|
||||
# A 4-byte array looks like a single integer. When widened by a longer
|
||||
# byte array, it should turn into an array.
|
||||
prop = self.node.props['longbytearray']
|
||||
prop2 = node2.props['longbytearray']
|
||||
self.assertFalse(isinstance(prop2.value, list))
|
||||
self.assertEqual(4, len(prop2.value))
|
||||
prop2.Widen(prop)
|
||||
self.assertTrue(isinstance(prop2.value, list))
|
||||
self.assertEqual(9, len(prop2.value))
|
||||
|
||||
# Similarly for a string array
|
||||
prop = self.node.props['stringval']
|
||||
prop2 = node2.props['stringarray']
|
||||
self.assertFalse(isinstance(prop.value, list))
|
||||
self.assertEqual(7, len(prop.value))
|
||||
prop.Widen(prop2)
|
||||
self.assertTrue(isinstance(prop.value, list))
|
||||
self.assertEqual(3, len(prop.value))
|
||||
|
||||
# Enlarging an existing array
|
||||
prop = self.node.props['stringarray']
|
||||
prop2 = node2.props['stringarray']
|
||||
self.assertTrue(isinstance(prop.value, list))
|
||||
self.assertEqual(2, len(prop.value))
|
||||
prop.Widen(prop2)
|
||||
self.assertTrue(isinstance(prop.value, list))
|
||||
self.assertEqual(3, len(prop.value))
|
||||
|
||||
def testAdd(self):
|
||||
"""Test adding properties"""
|
||||
self.fdt.pack()
|
||||
# This function should automatically expand the device tree
|
||||
self.node.AddZeroProp('one')
|
||||
self.node.AddZeroProp('two')
|
||||
self.node.AddZeroProp('three')
|
||||
|
||||
# Updating existing properties should be OK, since the device-tree size
|
||||
# does not change
|
||||
self.fdt.pack()
|
||||
self.node.SetInt('one', 1)
|
||||
self.node.SetInt('two', 2)
|
||||
self.node.SetInt('three', 3)
|
||||
|
||||
# This should fail since it would need to increase the device-tree size
|
||||
with self.assertRaises(libfdt.FdtException) as e:
|
||||
self.node.SetInt('four', 4)
|
||||
self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
|
||||
|
||||
|
||||
class TestFdtUtil(unittest.TestCase):
|
||||
"""Tests for the fdt_util module
|
||||
|
||||
This module will likely be mostly replaced at some point, once upstream
|
||||
libfdt has better Python support. For now, this provides tests for current
|
||||
functionality.
|
||||
"""
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
tools.PrepareOutputDir(None)
|
||||
|
||||
def setUp(self):
|
||||
self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
|
||||
self.node = self.dtb.GetNode('/spl-test')
|
||||
|
||||
def testGetInt(self):
|
||||
self.assertEqual(1, fdt_util.GetInt(self.node, 'intval'))
|
||||
self.assertEqual(3, fdt_util.GetInt(self.node, 'missing', 3))
|
||||
|
||||
with self.assertRaises(ValueError) as e:
|
||||
self.assertEqual(3, fdt_util.GetInt(self.node, 'intarray'))
|
||||
self.assertIn("property 'intarray' has list value: expecting a single "
|
||||
'integer', str(e.exception))
|
||||
|
||||
def testGetString(self):
|
||||
self.assertEqual('message', fdt_util.GetString(self.node, 'stringval'))
|
||||
self.assertEqual('test', fdt_util.GetString(self.node, 'missing',
|
||||
'test'))
|
||||
|
||||
with self.assertRaises(ValueError) as e:
|
||||
self.assertEqual(3, fdt_util.GetString(self.node, 'stringarray'))
|
||||
self.assertIn("property 'stringarray' has list value: expecting a "
|
||||
'single string', str(e.exception))
|
||||
|
||||
def testGetBool(self):
|
||||
self.assertEqual(True, fdt_util.GetBool(self.node, 'boolval'))
|
||||
self.assertEqual(False, fdt_util.GetBool(self.node, 'missing'))
|
||||
self.assertEqual(True, fdt_util.GetBool(self.node, 'missing', True))
|
||||
self.assertEqual(False, fdt_util.GetBool(self.node, 'missing', False))
|
||||
|
||||
def testFdtCellsToCpu(self):
|
||||
val = self.node.props['intarray'].value
|
||||
self.assertEqual(0, fdt_util.fdt_cells_to_cpu(val, 0))
|
||||
self.assertEqual(2, fdt_util.fdt_cells_to_cpu(val, 1))
|
||||
|
||||
dtb2 = fdt.FdtScan('tools/dtoc/dtoc_test_addr64.dts')
|
||||
node2 = dtb2.GetNode('/test1')
|
||||
val = node2.props['reg'].value
|
||||
self.assertEqual(0x1234, fdt_util.fdt_cells_to_cpu(val, 2))
|
||||
|
||||
def testEnsureCompiled(self):
|
||||
"""Test a degenerate case of this function"""
|
||||
dtb = fdt_util.EnsureCompiled('tools/dtoc/dtoc_test_simple.dts')
|
||||
self.assertEqual(dtb, fdt_util.EnsureCompiled(dtb))
|
||||
|
||||
def testGetPlainBytes(self):
|
||||
self.assertEqual('fred', fdt_util.get_plain_bytes('fred'))
|
||||
|
||||
|
||||
def RunTestCoverage():
|
||||
"""Run the tests and check that we get 100% coverage"""
|
||||
test_util.RunTestCoverage('tools/dtoc/test_fdt.py', None,
|
||||
['tools/patman/*.py', '*test_fdt.py'], options.build_dir)
|
||||
|
||||
|
||||
def RunTests(args):
|
||||
"""Run all the test we have for the fdt model
|
||||
|
||||
Args:
|
||||
args: List of positional args provided to fdt. This can hold a test
|
||||
name to execute (as in 'fdt -t testFdt', for example)
|
||||
"""
|
||||
result = unittest.TestResult()
|
||||
sys.argv = [sys.argv[0]]
|
||||
test_name = args and args[0] or None
|
||||
for module in (TestFdt, TestNode, TestProp, TestFdtUtil):
|
||||
if test_name:
|
||||
try:
|
||||
suite = unittest.TestLoader().loadTestsFromName(test_name, module)
|
||||
except AttributeError:
|
||||
continue
|
||||
else:
|
||||
suite = unittest.TestLoader().loadTestsFromTestCase(module)
|
||||
suite.run(result)
|
||||
|
||||
print result
|
||||
for _, err in result.errors:
|
||||
print err
|
||||
for _, err in result.failures:
|
||||
print err
|
||||
|
||||
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('-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()
|
||||
|
||||
# Run our meagre tests
|
||||
if options.test:
|
||||
RunTests(args)
|
||||
elif options.test_coverage:
|
||||
RunTestCoverage()
|
85
tools/patman/test_util.py
Normal file
85
tools/patman/test_util.py
Normal file
|
@ -0,0 +1,85 @@
|
|||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
# Copyright (c) 2016 Google, Inc
|
||||
#
|
||||
|
||||
from contextlib import contextmanager
|
||||
import glob
|
||||
import os
|
||||
import sys
|
||||
|
||||
import command
|
||||
|
||||
try:
|
||||
from StringIO import StringIO
|
||||
except ImportError:
|
||||
from io import StringIO
|
||||
|
||||
|
||||
def RunTestCoverage(prog, filter_fname, exclude_list, build_dir, required=None):
|
||||
"""Run tests and check that we get 100% coverage
|
||||
|
||||
Args:
|
||||
prog: Program to run (with be passed a '-t' argument to run tests
|
||||
filter_fname: Normally all *.py files in the program's directory will
|
||||
be included. If this is not None, then it is used to filter the
|
||||
list so that only filenames that don't contain filter_fname are
|
||||
included.
|
||||
exclude_list: List of file patterns to exclude from the coverage
|
||||
calculation
|
||||
build_dir: Build directory, used to locate libfdt.py
|
||||
required: List of modules which must be in the coverage report
|
||||
|
||||
Raises:
|
||||
ValueError if the code coverage is not 100%
|
||||
"""
|
||||
# This uses the build output from sandbox_spl to get _libfdt.so
|
||||
path = os.path.dirname(prog)
|
||||
if filter_fname:
|
||||
glob_list = glob.glob(os.path.join(path, '*.py'))
|
||||
glob_list = [fname for fname in glob_list if filter_fname in fname]
|
||||
else:
|
||||
glob_list = []
|
||||
glob_list += exclude_list
|
||||
glob_list += ['*libfdt.py', '*site-packages*']
|
||||
cmd = ('PYTHONPATH=$PYTHONPATH:%s/sandbox_spl/tools python-coverage run '
|
||||
'--omit "%s" %s -t' % (build_dir, ','.join(glob_list), prog))
|
||||
os.system(cmd)
|
||||
stdout = command.Output('python-coverage', 'report')
|
||||
lines = stdout.splitlines()
|
||||
if required:
|
||||
# Convert '/path/to/name.py' just the module name 'name'
|
||||
test_set = set([os.path.splitext(os.path.basename(line.split()[0]))[0]
|
||||
for line in lines if '/etype/' in line])
|
||||
missing_list = required
|
||||
missing_list.difference_update(test_set)
|
||||
if missing_list:
|
||||
print 'Missing tests for %s' % (', '.join(missing_list))
|
||||
print stdout
|
||||
ok = False
|
||||
|
||||
coverage = lines[-1].split(' ')[-1]
|
||||
ok = True
|
||||
print coverage
|
||||
if coverage != '100%':
|
||||
print stdout
|
||||
print ("Type 'python-coverage html' to get a report in "
|
||||
'htmlcov/index.html')
|
||||
print 'Coverage error: %s, but should be 100%%' % coverage
|
||||
ok = False
|
||||
if not ok:
|
||||
raise ValueError('Test coverage failure')
|
||||
|
||||
|
||||
# Use this to suppress stdout/stderr output:
|
||||
# with capture_sys_output() as (stdout, stderr)
|
||||
# ...do something...
|
||||
@contextmanager
|
||||
def capture_sys_output():
|
||||
capture_out, capture_err = StringIO(), StringIO()
|
||||
old_out, old_err = sys.stdout, sys.stderr
|
||||
try:
|
||||
sys.stdout, sys.stderr = capture_out, capture_err
|
||||
yield capture_out, capture_err
|
||||
finally:
|
||||
sys.stdout, sys.stderr = old_out, old_err
|
Loading…
Add table
Reference in a new issue