Merge git://git.denx.de/u-boot-dm

lime2-spi
Tom Rini 6 years ago
commit e3396ffd72
  1. 67
      doc/device-tree-bindings/memory/memory.txt
  2. 8
      drivers/core/device.c
  3. 20
      drivers/core/of_access.c
  4. 89
      drivers/core/of_extra.c
  5. 44
      drivers/core/ofnode.c
  6. 14
      drivers/core/uclass.c
  7. 4
      drivers/misc/cros_ec.c
  8. 9
      drivers/mtd/spi/sandbox.c
  9. 1
      include/asm-generic/global_data.h
  10. 4
      include/dm/device-internal.h
  11. 16
      include/dm/of_access.h
  12. 51
      include/dm/of_extra.h
  13. 48
      include/dm/ofnode.h
  14. 10
      include/fdt_support.h
  15. 38
      include/fdtdec.h
  16. 8
      include/log.h
  17. 2
      include/spi_flash.h
  18. 109
      lib/fdtdec.c
  19. 3
      scripts/dtc/libfdt/libfdt.h
  20. 725
      scripts/dtc/pylibfdt/libfdt.i_shipped
  21. 8
      test/dm/spi.c
  22. 7
      test/run
  23. 45
      tools/binman/README
  24. 40
      tools/binman/binman.py
  25. 27
      tools/binman/bsection.py
  26. 2
      tools/binman/cmdline.py
  27. 58
      tools/binman/control.py
  28. 22
      tools/binman/elf_test.py
  29. 42
      tools/binman/entry.py
  30. 37
      tools/binman/etype/_testing.py
  31. 3
      tools/binman/etype/blob.py
  32. 13
      tools/binman/etype/section.py
  33. 46
      tools/binman/etype/u_boot_dtb_with_ucode.py
  34. 4
      tools/binman/etype/u_boot_spl_bss_pad.py
  35. 9
      tools/binman/etype/u_boot_ucode.py
  36. 11
      tools/binman/etype/u_boot_with_ucode_ptr.py
  37. 159
      tools/binman/ftest.py
  38. 17
      tools/binman/image.py
  39. 2
      tools/binman/image_test.py
  40. 1
      tools/binman/test/41_unknown_pos_size.dts
  41. 14
      tools/binman/test/57_unknown_contents.dts
  42. 36
      tools/binman/test/58_x86_ucode_spl_needs_retry.dts
  43. 14
      tools/binman/test/59_change_size.dts
  44. 31
      tools/binman/test/60_fdt_update.dts
  45. 32
      tools/binman/test/61_fdt_update_bad.dts
  46. 15
      tools/dtoc/dtb_platdata.py
  47. 35
      tools/dtoc/dtoc.py
  48. 24
      tools/dtoc/dtoc_test_add_prop.dts
  49. 2
      tools/dtoc/dtoc_test_addr32_64.dts
  50. 2
      tools/dtoc/dtoc_test_addr64_32.dts
  51. 17
      tools/dtoc/dtoc_test_bad_reg.dts
  52. 17
      tools/dtoc/dtoc_test_bad_reg2.dts
  53. 6
      tools/dtoc/dtoc_test_phandle.dts
  54. 16
      tools/dtoc/dtoc_test_phandle_bad.dts
  55. 22
      tools/dtoc/dtoc_test_phandle_bad2.dts
  56. 23
      tools/dtoc/dtoc_test_phandle_reorder.dts
  57. 23
      tools/dtoc/dtoc_test_phandle_single.dts
  58. 1
      tools/dtoc/dtoc_test_simple.dts
  59. 115
      tools/dtoc/fdt.py
  60. 29
      tools/dtoc/fdt_util.py
  61. 221
      tools/dtoc/test_dtoc.py
  62. 1
      tools/dtoc/test_fdt
  63. 450
      tools/dtoc/test_fdt.py
  64. 85
      tools/patman/test_util.py

@ -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
*

@ -219,6 +219,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
*
* Search for a property in a device node and read 32-bit value(s) from

@ -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

@ -237,6 +237,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
*
* @ref: valid node reference to read property from
@ -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,15 +647,49 @@ 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
*
* @node: child node (ofnode, lvalue)

@ -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);

@ -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
"""
return check_err(fdt_totalsize(self._fdt))
def off_dt_struct(self):
"""Return the start of the device tree struct area
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.
Returns:
Start offset of struct area
Args:
size: Required new size of device tree in bytes
"""
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,22 +463,34 @@ class Fdt:
Raises:
FdtException if any error occurs
"""
return check_err(fdt_pack(self._fdt), quiet)
err = check_err(fdt_pack(self._fdt), quiet)
if err:
return err
del self._fdt[self.totalsize():]
return err
def delprop(self, nodeoffset, prop_name):
"""Delete a property from a node
def getprop(self, nodeoffset, prop_name, quiet=()):
"""Get a property from a node
Args:
nodeoffset: Node offset containing property to delete
prop_name: Name of property to delete
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:
Value of property as a string, or -ve error number
Raises:
FdtError if the property does not exist, or another error occurs
FdtError if any error occurs (e.g. the property is not found)
"""
return check_err(fdt_delprop(self._fdt, nodeoffset, prop_name))
pdata = check_err_null(fdt_getprop(self._fdt, nodeoffset, prop_name),
quiet)
if isinstance(pdata, (int)):
return pdata
return str(pdata[0])
def getprop(self, nodeoffset, prop_name, quiet=()):
"""Get a property from a node
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
@ -309,7 +498,7 @@ class Fdt:
quiet: Errors to ignore (empty to raise on all errors)
Returns:
Value of property as a bytearray, or -ve error number
Property object, or None if not found
Raises:
FdtError if any error occurs (e.g. the property is not found)
@ -317,8 +506,8 @@ class Fdt:
pdata = check_err_null(fdt_getprop(self._fdt, nodeoffset, prop_name),
quiet)
if isinstance(pdata, (int)):
return pdata
return bytearray(pdata[0])
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));

@ -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;
};
};
};

@ -0,0 +1,14 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
_testing {
return-unknown-contents;
};
};
};

@ -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>;
};
};
};

@ -0,0 +1,14 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
_testing {
bad-update-contents;
};
};
};

@ -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 {
};
};
};

@ -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,

@ -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>;

@ -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";
};
};

@ -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>;
};
};

@ -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 */
};
};

@ -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>;
};
};

@ -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>;
};
};

@ -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))

@ -0,0 +1 @@
test_fdt.py

@ -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()

@ -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…
Cancel
Save