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

lime2-spi
Tom Rini 6 years ago
commit d24c1d0f4d
  1. 1
      .travis.yml
  2. 46
      Documentation/devicetree/bindings/board/gdsys,board_gazerbeam.txt
  3. 1
      arch/Kconfig
  4. 10
      arch/sandbox/dts/test.dts
  5. 24
      board/sandbox/README.sandbox
  6. 37
      cmd/clk.c
  7. 2
      configs/sandbox64_defconfig
  8. 2
      configs/sandbox_defconfig
  9. 2
      configs/sandbox_flattree_defconfig
  10. 2
      configs/sandbox_noblk_defconfig
  11. 2
      configs/sandbox_spl_defconfig
  12. 2
      drivers/Kconfig
  13. 1
      drivers/Makefile
  14. 22
      drivers/board/Kconfig
  15. 7
      drivers/board/Makefile
  16. 60
      drivers/board/board-uclass.c
  17. 262
      drivers/board/gazerbeam.c
  18. 18
      drivers/board/gazerbeam.h
  19. 107
      drivers/board/sandbox.c
  20. 12
      drivers/board/sandbox.h
  21. 78
      drivers/core/device.c
  22. 70
      drivers/core/ofnode.c
  23. 2
      drivers/firmware/Kconfig
  24. 1
      drivers/firmware/Makefile
  25. 20
      drivers/firmware/firmware-sandbox.c
  26. 2
      drivers/firmware/firmware-uclass.c
  27. 139
      include/board.h
  28. 2
      include/clk.h
  29. 1
      include/dm.h
  30. 16
      include/dm/device.h
  31. 46
      include/dm/ofnode.h
  32. 1
      include/dm/uclass-id.h
  33. 44
      lib/fdtdec.c
  34. 34
      scripts/dtc/pylibfdt/libfdt.i_shipped
  35. 2
      test/dm/Makefile
  36. 57
      test/dm/board.c
  37. 22
      test/dm/firmware.c
  38. 82
      test/dm/test-fdt.c
  39. 49
      tools/binman/README
  40. 101
      tools/binman/README.entries
  41. 69
      tools/binman/bsection.py
  42. 4
      tools/binman/cmdline.py
  43. 96
      tools/binman/control.py
  44. 92
      tools/binman/entry.py
  45. 16
      tools/binman/entry_test.py
  46. 7
      tools/binman/etype/_testing.py
  47. 49
      tools/binman/etype/blob.py
  48. 33
      tools/binman/etype/blob_dtb.py
  49. 57
      tools/binman/etype/files.py
  50. 2
      tools/binman/etype/fill.py
  51. 11
      tools/binman/etype/fmap.py
  52. 14
      tools/binman/etype/section.py
  53. 3
      tools/binman/etype/text.py
  54. 9
      tools/binman/etype/u_boot_dtb.py
  55. 27
      tools/binman/etype/u_boot_dtb_with_ucode.py
  56. 39
      tools/binman/etype/u_boot_elf.py
  57. 6
      tools/binman/etype/u_boot_spl_dtb.py
  58. 24
      tools/binman/etype/u_boot_spl_elf.py
  59. 2
      tools/binman/etype/u_boot_spl_with_ucode_ptr.py
  60. 6
      tools/binman/etype/u_boot_tpl_dtb.py
  61. 25
      tools/binman/etype/u_boot_tpl_dtb_with_ucode.py
  62. 27
      tools/binman/etype/u_boot_tpl_with_ucode_ptr.py
  63. 26
      tools/binman/etype/u_boot_ucode.py
  64. 24
      tools/binman/etype/u_boot_with_ucode_ptr.py
  65. 11
      tools/binman/etype/vblock.py
  66. 30
      tools/binman/etype/x86_start16_tpl.py
  67. 6
      tools/binman/fmap_util.py
  68. 394
      tools/binman/ftest.py
  69. 30
      tools/binman/image.py
  70. 253
      tools/binman/state.py
  71. 15
      tools/binman/test/80_fill_empty.dts
  72. 14
      tools/binman/test/81_x86-start16-tpl.dts
  73. 18
      tools/binman/test/82_fdt_update_all.dts
  74. 11
      tools/binman/test/83_compress.dts
  75. 11
      tools/binman/test/84_files.dts
  76. 11
      tools/binman/test/85_files_compress.dts
  77. 12
      tools/binman/test/86_files_none.dts
  78. 11
      tools/binman/test/87_files_no_pattern.dts
  79. 43
      tools/binman/test/88_expand_size.dts
  80. 14
      tools/binman/test/89_expand_size_bad.dts
  81. 12
      tools/binman/test/90_hash.dts
  82. 11
      tools/binman/test/91_hash_no_algo.dts
  83. 12
      tools/binman/test/92_hash_bad_algo.dts
  84. 29
      tools/binman/test/93_x86_tpl_ucode.dts
  85. 20
      tools/binman/test/94_fmap_x86.dts
  86. 22
      tools/binman/test/95_fmap_x86_section.dts
  87. 14
      tools/binman/test/96_elf.dts
  88. 15
      tools/binman/test/97_elf_strip.dts
  89. 18
      tools/binman/test/99_hash_section.dts
  90. 1
      tools/binman/test/files/1.dat
  91. 1
      tools/binman/test/files/2.dat
  92. 0
      tools/binman/test/files/ignored_dir.dat/ignore
  93. 1
      tools/binman/test/files/not-this-one
  94. 2
      tools/buildman/builder.py
  95. 197
      tools/dtoc/fdt.py
  96. 67
      tools/dtoc/test_fdt.py
  97. 47
      tools/patman/tools.py

@ -27,6 +27,7 @@ addons:
- wget
- device-tree-compiler
- lzop
- liblz4-tool
before_install:
- sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y

@ -0,0 +1,46 @@
gdsys Gazerbeam board driver
This driver provides capabilities to access the gdsys Gazerbeam board's device
information. Furthermore, phandles to some internal devices are provided for
the board files.
Required properties:
- compatible: should be "gdsys,board_gazerbeam"
- csb: phandle to the board's coherent system bus (CSB) device node
- rxaui[0-3]: phandles to the rxaui control device nodes
- fpga[0-1]: phandles to the board's gdsys FPGA device nodes
- ioep[0-1]: phandles to the board's IO endpoint device nodes
- ver-gpios: GPIO list to read the hardware version from
- var-gpios: GPIO list to read the hardware variant information from
- reset-gpios: GPIO list for the board's reset GPIOs
Example:
board {
compatible = "gdsys,board_gazerbeam";
csb = <&board_soc>;
serdes = <&SERDES>;
rxaui0 = <&RXAUI0>;
rxaui1 = <&RXAUI1>;
rxaui2 = <&RXAUI2>;
rxaui3 = <&RXAUI3>;
fpga0 = <&FPGA0>;
fpga1 = <&FPGA1>;
ioep0 = <&IOEP0>;
ioep1 = <&IOEP1>;
ver-gpios = <&PPCPCA 12 0
&PPCPCA 13 0
&PPCPCA 14 0
&PPCPCA 15 0>;
/* MC2/SC-Board */
var-gpios-mc2 = <&GPIO_VB0 0 0 /* VAR-MC_SC */
&GPIO_VB0 11 0>; /* VAR-CON */
/* MC4-Board */
var-gpios-mc4 = <&GPIO_VB1 0 0 /* VAR-MC_SC */
&GPIO_VB1 11 0>; /* VAR-CON */
reset-gpios = <&gpio0 1 0 &gpio0 2 1>;
};

@ -89,6 +89,7 @@ config SANDBOX
imply CMD_SF_TEST
imply CRC32_VERIFY
imply FAT_WRITE
imply FIRMWARE
imply HASH_VERIFY
imply LZMA
imply SCSI

@ -191,6 +191,12 @@
fake-host-hwaddr = [00 00 66 44 22 22];
};
firmware {
sandbox_firmware: sandbox-firmware {
compatible = "sandbox,firmware";
};
};
gpio_a: base-gpios {
compatible = "sandbox,gpio";
gpio-controller;
@ -647,6 +653,10 @@
osd {
compatible = "sandbox,sandbox_osd";
};
board {
compatible = "sandbox,board_sandbox";
};
};
#include "sandbox_pmic.dtsi"

@ -193,6 +193,30 @@ device are supported.
Also sandbox supports driver model (CONFIG_DM) and associated commands.
Sandbox Variants
----------------
There are unfortunately quite a few variants at present:
sandbox - should be used for most tests
sandbox64 - special build that forces a 64-bit host
sandbox_flattree - builds with dev_read_...() functions defined as inline.
We need this build so that we can test those inline functions, and we
cannot build with both the inline functions and the non-inline functions
since they are named the same.
sandbox_noblk - builds without CONFIG_BLK, which means the legacy block
drivers are used. We cannot use both the legacy and driver-model block
drivers since they implement the same functions
sandbox_spl - builds sandbox with SPL support, so you can run spl/u-boot-spl
and it will start up and then load ./u-boot. It is also possible to
run ./u-boot directly.
Of these sandbox_noblk can be removed once CONFIG_BLK is used everwhere, and
sandbox_spl can probably be removed since it is a superset of sandbox.
Most of the config options should be identical between these variants.
Linux RAW Networking Bridge
---------------------------

@ -5,11 +5,48 @@
#include <common.h>
#include <command.h>
#include <clk.h>
#if defined(CONFIG_DM) && defined(CONFIG_CLK)
#include <dm.h>
#include <dm/device-internal.h>
#endif
int __weak soc_clk_dump(void)
{
#if defined(CONFIG_DM) && defined(CONFIG_CLK)
struct udevice *dev;
struct uclass *uc;
struct clk clk;
int ret;
/* Device addresses start at 1 */
ret = uclass_get(UCLASS_CLK, &uc);
if (ret)
return ret;
uclass_foreach_dev(dev, uc) {
memset(&clk, 0, sizeof(clk));
ret = device_probe(dev);
if (ret) {
printf("%-30.30s : ? Hz\n", dev->name);
continue;
}
ret = clk_request(dev, &clk);
if (ret) {
printf("%-30.30s : ? Hz\n", dev->name);
continue;
}
printf("%-30.30s : %lu Hz\n", dev->name, clk_get_rate(&clk));
clk_free(&clk);
}
return 0;
#else
puts("Not implemented\n");
return 1;
#endif
}
static int do_clk_dump(cmd_tbl_t *cmdtp, int flag, int argc,

@ -87,6 +87,8 @@ CONFIG_CPU=y
CONFIG_DM_DEMO=y
CONFIG_DM_DEMO_SIMPLE=y
CONFIG_DM_DEMO_SHAPE=y
CONFIG_BOARD=y
CONFIG_BOARD_SANDBOX=y
CONFIG_PM8916_GPIO=y
CONFIG_SANDBOX_GPIO=y
CONFIG_DM_I2C_COMPAT=y

@ -92,6 +92,8 @@ CONFIG_CPU=y
CONFIG_DM_DEMO=y
CONFIG_DM_DEMO_SIMPLE=y
CONFIG_DM_DEMO_SHAPE=y
CONFIG_BOARD=y
CONFIG_BOARD_SANDBOX=y
CONFIG_PM8916_GPIO=y
CONFIG_SANDBOX_GPIO=y
CONFIG_DM_I2C_COMPAT=y

@ -71,6 +71,8 @@ CONFIG_CPU=y
CONFIG_DM_DEMO=y
CONFIG_DM_DEMO_SIMPLE=y
CONFIG_DM_DEMO_SHAPE=y
CONFIG_BOARD=y
CONFIG_BOARD_SANDBOX=y
CONFIG_PM8916_GPIO=y
CONFIG_SANDBOX_GPIO=y
CONFIG_DM_I2C_COMPAT=y

@ -78,6 +78,8 @@ CONFIG_CPU=y
CONFIG_DM_DEMO=y
CONFIG_DM_DEMO_SIMPLE=y
CONFIG_DM_DEMO_SHAPE=y
CONFIG_BOARD=y
CONFIG_BOARD_SANDBOX=y
CONFIG_PM8916_GPIO=y
CONFIG_SANDBOX_GPIO=y
CONFIG_DM_I2C_COMPAT=y

@ -92,6 +92,8 @@ CONFIG_CPU=y
CONFIG_DM_DEMO=y
CONFIG_DM_DEMO_SIMPLE=y
CONFIG_DM_DEMO_SHAPE=y
CONFIG_BOARD=y
CONFIG_BOARD_SANDBOX=y
CONFIG_PM8916_GPIO=y
CONFIG_SANDBOX_GPIO=y
CONFIG_DM_I2C_COMPAT=y

@ -24,6 +24,8 @@ source "drivers/ddr/Kconfig"
source "drivers/demo/Kconfig"
source "drivers/board/Kconfig"
source "drivers/ddr/fsl/Kconfig"
source "drivers/dfu/Kconfig"

@ -71,6 +71,7 @@ obj-y += ata/
obj-$(CONFIG_DM_DEMO) += demo/
obj-$(CONFIG_BIOSEMU) += bios_emulator/
obj-y += block/
obj-y += board/
obj-$(CONFIG_BOOTCOUNT_LIMIT) += bootcount/
obj-$(CONFIG_CPU) += cpu/
obj-y += crypto/

@ -0,0 +1,22 @@
menuconfig BOARD
bool "Device Information"
help
Support methods to query hardware configurations from internal
mechanisms (e.g. reading GPIO values, determining the presence of
devices on busses, etc.). This enables the usage of U-Boot with
modular board architectures.
if BOARD
config BOARD_GAZERBEAM
bool "Enable board driver for the Gazerbeam board"
help
Support querying device information for the gdsys Gazerbeam board.
config BOARD_SANDBOX
bool "Enable board driver for the Sandbox board"
help
Support querying device information for the Sandbox boards.
endif

@ -0,0 +1,7 @@
# SPDX-License-Identifier: GPL-2.0+
#
# (C) Copyright 2017
# Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
obj-$(CONFIG_BOARD) += board-uclass.o
obj-$(CONFIG_BOARD_GAZERBEAM) += gazerbeam.o
obj-$(CONFIG_BOARD_SANDBOX) += sandbox.o

@ -0,0 +1,60 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2017
* Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
*/
#include <common.h>
#include <dm.h>
#include <board.h>
int board_get(struct udevice **devp)
{
return uclass_first_device_err(UCLASS_BOARD, devp);
}
int board_detect(struct udevice *dev)
{
struct board_ops *ops = board_get_ops(dev);
if (!ops->detect)
return -ENOSYS;
return ops->detect(dev);
}
int board_get_bool(struct udevice *dev, int id, bool *val)
{
struct board_ops *ops = board_get_ops(dev);
if (!ops->get_bool)
return -ENOSYS;
return ops->get_bool(dev, id, val);
}
int board_get_int(struct udevice *dev, int id, int *val)
{
struct board_ops *ops = board_get_ops(dev);
if (!ops->get_int)
return -ENOSYS;
return ops->get_int(dev, id, val);
}
int board_get_str(struct udevice *dev, int id, size_t size, char *val)
{
struct board_ops *ops = board_get_ops(dev);
if (!ops->get_str)
return -ENOSYS;
return ops->get_str(dev, id, size, val);
}
UCLASS_DRIVER(board) = {
.id = UCLASS_BOARD,
.name = "board",
.post_bind = dm_scan_fdt_dev,
};

@ -0,0 +1,262 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2017
* Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
*/
#include <common.h>
#include <dm.h>
#include <board.h>
#include <i2c.h>
#include <asm/gpio.h>
#include "gazerbeam.h"
/* Sequence number of I2C bus that holds the GPIO expanders */
static const int I2C_BUS_SEQ_NO = 1;
/* I2C address of SC/MC2 expander */
static const int MC2_EXPANDER_ADDR = 0x20;
/* I2C address of MC4 expander */
static const int MC4_EXPANDER_ADDR = 0x22;
/* Number of the GPIO to read the SC data from */
static const int SC_GPIO_NO;
/* Number of the GPIO to read the CON data from */
static const int CON_GPIO_NO = 1;
/**
* struct board_gazerbeam_priv - Private data structure for the gazerbeam board
* driver.
* @reset_gpios: GPIOs for the board's reset GPIOs.
* @var_gpios: GPIOs for the board's hardware variant GPIOs
* @ver_gpios: GPIOs for the board's hardware version GPIOs
* @variant: Container for the board's hardware variant (CON/CPU)
* @multichannel: Container for the board's multichannel variant (MC4/MC2/SC)
* @hwversion: Container for the board's hardware version
*/
struct board_gazerbeam_priv {
struct gpio_desc reset_gpios[2];
struct gpio_desc var_gpios[2];
struct gpio_desc ver_gpios[4];
int variant;
int multichannel;
int hwversion;
};
/**
* _read_board_variant_data() - Read variant information from the hardware.
* @dev: The board device for which to determine the multichannel and device
* type information.
*
* The data read from the board's hardware (mostly hard-wired GPIOs) is stored
* in the private data structure of the driver to be used by other driver
* methods.
*
* Return: 0 if OK, -ve on error.
*/
static int _read_board_variant_data(struct udevice *dev)
{
struct board_gazerbeam_priv *priv = dev_get_priv(dev);
struct udevice *i2c_bus;
struct udevice *dummy;
char *listname;
int mc4, mc2, sc, con;
int gpio_num;
int res;
res = uclass_get_device_by_seq(UCLASS_I2C, I2C_BUS_SEQ_NO, &i2c_bus);
if (res) {
debug("%s: Could not get I2C bus %d (err = %d)\n",
dev->name, I2C_BUS_SEQ_NO, res);
return res;
}
if (!i2c_bus) {
debug("%s: Could not get I2C bus %d\n",
dev->name, I2C_BUS_SEQ_NO);
return -EIO;
}
mc2 = !dm_i2c_probe(i2c_bus, MC2_EXPANDER_ADDR, 0, &dummy);
mc4 = !dm_i2c_probe(i2c_bus, MC4_EXPANDER_ADDR, 0, &dummy);
if (mc2 && mc4) {
debug("%s: Board hardware configuration inconsistent.\n",
dev->name);
return -EINVAL;
}
listname = mc2 ? "var-gpios-mc2" : "var-gpios-mc4";
gpio_num = gpio_request_list_by_name(dev, listname, priv->var_gpios,
ARRAY_SIZE(priv->var_gpios),
GPIOD_IS_IN);
if (gpio_num < 0) {
debug("%s: Requesting gpio list %s failed (err = %d).\n",
dev->name, listname, gpio_num);
return gpio_num;
}
sc = dm_gpio_get_value(&priv->var_gpios[SC_GPIO_NO]);
if (sc < 0) {
debug("%s: Error while reading 'sc' GPIO (err = %d)",
dev->name, sc);
return sc;
}
con = dm_gpio_get_value(&priv->var_gpios[CON_GPIO_NO]);
if (con < 0) {
debug("%s: Error while reading 'con' GPIO (err = %d)",
dev->name, con);
return con;
}
if ((sc && mc2) || (sc && mc4) || (!sc && !mc2 && !mc4)) {
debug("%s: Board hardware configuration inconsistent.\n",
dev->name);
return -EINVAL;
}
priv->variant = con ? VAR_CON : VAR_CPU;
priv->multichannel = mc4 ? 4 : (mc2 ? 2 : (sc ? 1 : 0));
return 0;
}
/**
* _read_hwversion() - Read the hardware version from the board.
* @dev: The board device for which to read the hardware version.
*
* The hardware version read from the board (from hard-wired GPIOs) is stored
* in the private data structure of the driver to be used by other driver
* methods.
*
* Return: 0 if OK, -ve on error.
*/
static int _read_hwversion(struct udevice *dev)
{
struct board_gazerbeam_priv *priv = dev_get_priv(dev);
int res;
res = gpio_request_list_by_name(dev, "ver-gpios", priv->ver_gpios,
ARRAY_SIZE(priv->ver_gpios),
GPIOD_IS_IN);
if (res < 0) {
debug("%s: Error getting GPIO list 'ver-gpios' (err = %d)\n",
dev->name, res);
return -ENODEV;
}
res = dm_gpio_get_values_as_int(priv->ver_gpios,
ARRAY_SIZE(priv->ver_gpios));
if (res < 0) {
debug("%s: Error reading HW version from expander (err = %d)\n",
dev->name, res);
return res;
}
priv->hwversion = res;
res = gpio_free_list(dev, priv->ver_gpios, ARRAY_SIZE(priv->ver_gpios));
if (res < 0) {
debug("%s: Error freeing HW version GPIO list (err = %d)\n",
dev->name, res);
return res;
}
return 0;
}
static int board_gazerbeam_detect(struct udevice *dev)
{
int res;
res = _read_board_variant_data(dev);
if (res) {
debug("%s: Error reading multichannel variant (err = %d)\n",
dev->name, res);
return res;
}
res = _read_hwversion(dev);
if (res) {
debug("%s: Error reading hardware version (err = %d)\n",
dev->name, res);
return res;
}
return 0;
}
static int board_gazerbeam_get_int(struct udevice *dev, int id, int *val)
{
struct board_gazerbeam_priv *priv = dev_get_priv(dev);
switch (id) {
case BOARD_MULTICHANNEL:
*val = priv->multichannel;
break;
case BOARD_VARIANT:
*val = priv->variant;
break;
case BOARD_HWVERSION:
*val = priv->hwversion;
break;
default:
debug("%s: Integer value %d unknown\n", dev->name, id);
return -EINVAL;
}
return 0;
}
static const struct udevice_id board_gazerbeam_ids[] = {
{ .compatible = "gdsys,board_gazerbeam" },
{ /* sentinel */ }
};
static const struct board_ops board_gazerbeam_ops = {
.detect = board_gazerbeam_detect,
.get_int = board_gazerbeam_get_int,
};
static int board_gazerbeam_probe(struct udevice *dev)
{
struct board_gazerbeam_priv *priv = dev_get_priv(dev);
int gpio_num, i;
gpio_num = gpio_request_list_by_name(dev, "reset-gpios",
priv->reset_gpios,
ARRAY_SIZE(priv->reset_gpios),
GPIOD_IS_OUT);
if (gpio_num < 0) {
debug("%s: Error getting GPIO list 'reset-gpios' (err = %d)\n",
dev->name, gpio_num);
return gpio_num;
}
/* Set startup-finished GPIOs */
for (i = 0; i < ARRAY_SIZE(priv->reset_gpios); i++) {
int res = dm_gpio_set_value(&priv->reset_gpios[i], 0);
if (res) {
debug("%s: Error while setting GPIO %d (err = %d)\n",
dev->name, i, res);
return res;
}
}
return 0;
}
U_BOOT_DRIVER(board_gazerbeam) = {
.name = "board_gazerbeam",
.id = UCLASS_BOARD,
.of_match = board_gazerbeam_ids,
.ops = &board_gazerbeam_ops,
.priv_auto_alloc_size = sizeof(struct board_gazerbeam_priv),
.probe = board_gazerbeam_probe,
};

@ -0,0 +1,18 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* (C) Copyright 2017
* Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
*
* SPDX-License-Identifier: GPL-2.0+
*/
enum {
BOARD_MULTICHANNEL,
BOARD_VARIANT,
BOARD_HWVERSION,
};
enum {
VAR_CON,
VAR_CPU,
};

@ -0,0 +1,107 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2018
* Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
*/
#include <common.h>
#include <dm.h>
#include <board.h>
#include "sandbox.h"
struct board_sandbox_priv {
bool called_detect;
int test_i1;
int test_i2;
};
char vacation_spots[][64] = {"R'lyeh", "Dreamlands", "Plateau of Leng",
"Carcosa", "Yuggoth", "The Nameless City"};
int board_sandbox_detect(struct udevice *dev)
{
struct board_sandbox_priv *priv = dev_get_priv(dev);
priv->called_detect = true;
priv->test_i2 = 100;
return 0;
}
int board_sandbox_get_bool(struct udevice *dev, int id, bool *val)
{
struct board_sandbox_priv *priv = dev_get_priv(dev);
switch (id) {
case BOOL_CALLED_DETECT:
/* Checks if the dectect method has been called */
*val = priv->called_detect;
return 0;
}
return -ENOENT;
}
int board_sandbox_get_int(struct udevice *dev, int id, int *val)
{
struct board_sandbox_priv *priv = dev_get_priv(dev);
switch (id) {
case INT_TEST1:
*val = priv->test_i1;
/* Increments with every call */
priv->test_i1++;
return 0;
case INT_TEST2:
*val = priv->test_i2;
/* Decrements with every call */
priv->test_i2--;
return 0;
}
return -ENOENT;
}
int board_sandbox_get_str(struct udevice *dev, int id, size_t size, char *val)
{
struct board_sandbox_priv *priv = dev_get_priv(dev);
int i1 = priv->test_i1;
int i2 = priv->test_i2;
int index = (i1 * i2) % ARRAY_SIZE(vacation_spots);
switch (id) {
case STR_VACATIONSPOT:
/* Picks a vacation spot depending on i1 and i2 */
snprintf(val, size, vacation_spots[index]);
return 0;
}
return -ENOENT;
}
static const struct udevice_id board_sandbox_ids[] = {
{ .compatible = "sandbox,board_sandbox" },
{ /* sentinel */ }
};
static const struct board_ops board_sandbox_ops = {
.detect = board_sandbox_detect,
.get_bool = board_sandbox_get_bool,
.get_int = board_sandbox_get_int,
.get_str = board_sandbox_get_str,
};
int board_sandbox_probe(struct udevice *dev)
{
return 0;
}
U_BOOT_DRIVER(board_sandbox) = {
.name = "board_sandbox",
.id = UCLASS_BOARD,
.of_match = board_sandbox_ids,
.ops = &board_sandbox_ops,
.priv_auto_alloc_size = sizeof(struct board_sandbox_priv),
.probe = board_sandbox_probe,
};

@ -0,0 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* (C) Copyright 2018
* Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
*/
enum {
BOOL_CALLED_DETECT,
INT_TEST1,
INT_TEST2,
STR_VACATIONSPOT,
};

@ -516,6 +516,33 @@ static int device_get_device_tail(struct udevice *dev, int ret,
return 0;
}
/**
* device_find_by_ofnode() - Return device associated with given ofnode
*
* The returned device is *not* activated.
*
* @node: The ofnode for which a associated device should be looked up
* @devp: Pointer to structure to hold the found device
* Return: 0 if OK, -ve on error
*/
static int device_find_by_ofnode(ofnode node, struct udevice **devp)
{
struct uclass *uc;
struct udevice *dev;
int ret;
list_for_each_entry(uc, &gd->uclass_root, sibling_node) {
ret = uclass_find_device_by_ofnode(uc->uc_drv->id, node,
&dev);
if (!ret || dev) {
*devp = dev;
return 0;
}
}
return -ENODEV;
}
int device_get_child(struct udevice *parent, int index, struct udevice **devp)
{
struct udevice *dev;
@ -739,3 +766,54 @@ bool of_machine_is_compatible(const char *compat)
return !fdt_node_check_compatible(fdt, 0, compat);
}
int dev_disable_by_path(const char *path)
{
struct uclass *uc;
ofnode node = ofnode_path(path);
struct udevice *dev;
int ret = 1;
if (!of_live_active())
return -ENOSYS;
list_for_each_entry(uc, &gd->uclass_root, sibling_node) {
ret = uclass_find_device_by_ofnode(uc->uc_drv->id, node, &dev);
if (!ret)
break;
}
if (ret)
return ret;
ret = device_remove(dev, DM_REMOVE_NORMAL);
if (ret)
return ret;
ret = device_unbind(dev);
if (ret)
return ret;
return ofnode_set_enabled(node, false);
}
int dev_enable_by_path(const char *path)
{
ofnode node = ofnode_path(path);
ofnode pnode = ofnode_get_parent(node);
struct udevice *parent;
int ret = 1;
if (!of_live_active())
return -ENOSYS;
ret = device_find_by_ofnode(pnode, &parent);
if (ret)
return ret;
ret = ofnode_set_enabled(node, true);
if (ret)
return ret;
return lists_bind_fdt(parent, node, NULL);
}

@ -791,3 +791,73 @@ ofnode ofnode_by_prop_value(ofnode from, const char *propname,
propname, propval, proplen));
}
}
int ofnode_write_prop(ofnode node, const char *propname, int len,
const void *value)
{
const struct device_node *np = ofnode_to_np(node);
struct property *pp;
struct property *pp_last = NULL;
struct property *new;
if (!of_live_active())
return -ENOSYS;
if (!np)
return -EINVAL;
for (pp = np->properties; pp; pp = pp->next) {
if (strcmp(pp->name, propname) == 0) {
/* Property exists -> change value */
pp->value = (void *)value;
pp->length = len;
return 0;
}
pp_last = pp;
}
if (!pp_last)
return -ENOENT;
/* Property does not exist -> append new property */
new = malloc(sizeof(struct property));
if (!new)
return -ENOMEM;
new->name = strdup(propname);
if (!new->name)
return -ENOMEM;
new->value = (void *)value;
new->length = len;
new->next = NULL;
pp_last->next = new;
return 0;
}
int ofnode_write_string(ofnode node, const char *propname, const char *value)
{
if (!of_live_active())
return -ENOSYS;
assert(ofnode_valid(node));
debug("%s: %s = %s", __func__, propname, value);
return ofnode_write_prop(node, propname, strlen(value) + 1, value);
}
int ofnode_set_enabled(ofnode node, bool value)
{
if (!of_live_active())
return -ENOSYS;
assert(ofnode_valid(node));
if (value)
return ofnode_write_string(node, "status", "okay");
else
return ofnode_write_string(node, "status", "disable");
}

@ -1,5 +1,5 @@
config FIRMWARE
bool
bool "Enable Firmware driver support"
config ARM_PSCI_FW
bool

@ -1,3 +1,4 @@
obj-$(CONFIG_FIRMWARE) += firmware-uclass.o
obj-$(CONFIG_ARM_PSCI_FW) += psci.o
obj-$(CONFIG_TI_SCI_PROTOCOL) += ti_sci.o
obj-$(CONFIG_SANDBOX) += firmware-sandbox.o

@ -0,0 +1,20 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* sandbox firmware driver
*
* Copyright (C) 2018 Xilinx, Inc.
*/
#include <common.h>
#include <dm.h>
static const struct udevice_id generic_sandbox_firmware_ids[] = {
{ .compatible = "sandbox,firmware" },
{ }
};
U_BOOT_DRIVER(sandbox_firmware) = {
.name = "sandbox_firmware",
.id = UCLASS_FIRMWARE,
.of_match = generic_sandbox_firmware_ids,
};

@ -7,7 +7,7 @@
UCLASS_DRIVER(firmware) = {
.id = UCLASS_FIRMWARE,
.name = "firmware",
#if CONFIG_IS_ENABLED(OF_CONTROL)
#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)
.post_bind = dm_scan_fdt_dev,
#endif
};

@ -0,0 +1,139 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* (C) Copyright 2017
* Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
*/
/*
* This uclass encapsulates hardware methods to gather information about a
* board or a specific device such as hard-wired GPIOs on GPIO expanders,
* read-only data in flash ICs, or similar.
*
* The interface offers functions to read the usual standard data types (bool,
* int, string) from the device, each of which is identified by a static
* numeric ID (which will usually be defined as a enum in a header file).
*
* If for example the board had a read-only serial number flash IC, we could
* call
*
* ret = board_detect(dev);
* if (ret) {
* debug("board device not found.");
* return ret;
* }
*
* ret = board_get_int(dev, ID_SERIAL_NUMBER, &serial);
* if (ret) {
* debug("Error when reading serial number from device.");
* return ret;
* }
*
* to read the serial number.
*/
struct board_ops {
/**
* detect() - Run the hardware info detection procedure for this
* device.
* @dev: The device containing the information
*
* This operation might take a long time (e.g. read from EEPROM,
* check the presence of a device on a bus etc.), hence this is not
* done in the probe() method, but later during operation in this
* dedicated method.
*
* Return: 0 if OK, -ve on error.
*/
int (*detect)(struct udevice *dev);
/**
* get_bool() - Read a specific bool data value that describes the
* hardware setup.
* @dev: The board instance to gather the data.
* @id: A unique identifier for the bool value to be read.
* @val: Pointer to a buffer that receives the value read.
*
* Return: 0 if OK, -ve on error.
*/
int (*get_bool)(struct udevice *dev, int id, bool *val);
/**
* get_int() - Read a specific int data value that describes the
* hardware setup.
* @dev: The board instance to gather the data.
* @id: A unique identifier for the int value to be read.
* @val: Pointer to a buffer that receives the value read.
*
* Return: 0 if OK, -ve on error.
*/
int (*get_int)(struct udevice *dev, int id, int *val);
/**
* get_str() - Read a specific string data value that describes the
* hardware setup.
* @dev: The board instance to gather the data.
* @id: A unique identifier for the string value to be read.
* @size: The size of the buffer to receive the string data.
* @val: Pointer to a buffer that receives the value read.
*
* Return: 0 if OK, -ve on error.
*/
int (*get_str)(struct udevice *dev, int id, size_t size, char *val);
};
#define board_get_ops(dev) ((struct board_ops *)(dev)->driver->ops)
/**
* board_detect() - Run the hardware info detection procedure for this device.
*
* @dev: The device containing the information
*
* Return: 0 if OK, -ve on error.
*/
int board_detect(struct udevice *dev);
/**
* board_get_bool() - Read a specific bool data value that describes the
* hardware setup.
* @dev: The board instance to gather the data.
* @id: A unique identifier for the bool value to be read.
* @val: Pointer to a buffer that receives the value read.
*
* Return: 0 if OK, -ve on error.
*/
int board_get_bool(struct udevice *dev, int id, bool *val);
/**
* board_get_int() - Read a specific int data value that describes the
* hardware setup.
* @dev: The board instance to gather the data.
* @id: A unique identifier for the int value to be read.
* @val: Pointer to a buffer that receives the value read.
*
* Return: 0 if OK, -ve on error.
*/
int board_get_int(struct udevice *dev, int id, int *val);
/**
* board_get_str() - Read a specific string data value that describes the
* hardware setup.
* @dev: The board instance to gather the data.
* @id: A unique identifier for the string value to be read.
* @size: The size of the buffer to receive the string data.
* @val: Pointer to a buffer that receives the value read.
*
* Return: 0 if OK, -ve on error.
*/
int board_get_str(struct udevice *dev, int id, size_t size, char *val);
/**
* board_get() - Return the board device for the board in question.
* @devp: Pointer to structure to receive the board device.
*
* Since there can only be at most one board instance, the API can supply a
* function that returns the unique device. This is especially useful for use
* in board files.
*
* Return: 0 if OK, -ve on error.
*/
int board_get(struct udevice **devp);

@ -21,7 +21,7 @@
*
* A driver that implements UCLASS_CLOCK is a clock provider. A provider will
* often implement multiple separate clocks, since the hardware it manages
* often has this capability. clock_uclass.h describes the interface which
* often has this capability. clk-uclass.h describes the interface which
* clock providers must implement.
*
* Clock consumers/clients are the HW modules driven by the clock signals. This

@ -6,7 +6,6 @@
#ifndef _DM_H_
#define _DM_H_
#include <dm/ofnode.h>
#include <dm/device.h>
#include <dm/fdtaddr.h>
#include <dm/ofnode.h>

@ -601,6 +601,22 @@ bool device_is_compatible(struct udevice *dev, const char *compat);
bool of_machine_is_compatible(const char *compat);
/**
* dev_disable_by_path() - Disable a device given its device tree path
*
* @path: The device tree path identifying the device to be disabled
* @return 0 on success, -ve on error
*/
int dev_disable_by_path(const char *path);
/**
* dev_enable_by_path() - Enable a device given its device tree path
*
* @path: The device tree path identifying the device to be enabled
* @return 0 on success, -ve on error
*/
int dev_enable_by_path(const char *path);
/**
* device_is_on_pci_bus - Test if a device is on a PCI bus
*
* @dev: device to test

@ -764,4 +764,50 @@ u64 ofnode_translate_address(ofnode node, const fdt32_t *in_addr);
* @return true if OK, false if the compatible is not found
*/
int ofnode_device_is_compatible(ofnode node, const char *compat);
/**
* ofnode_write_prop() - Set a property of a ofnode
*
* Note that the value passed to the function is *not* allocated by the
* function itself, but must be allocated by the caller if necessary.
*
* @node: The node for whose property should be set
* @propname: The name of the property to set
* @len: The length of the new value of the property
* @value: The new value of the property (must be valid prior to calling
* the function)
* @return 0 if successful, -ve on error
*/
int ofnode_write_prop(ofnode node, const char *propname, int len,
const void *value);
/**
* ofnode_write_string() - Set a string property of a ofnode
*
* Note that the value passed to the function is *not* allocated by the
* function itself, but must be allocated by the caller if necessary.
*
* @node: The node for whose string property should be set
* @propname: The name of the string property to set
* @value: The new value of the string property (must be valid prior to
* calling the function)
* @return 0 if successful, -ve on error
*/
int ofnode_write_string(ofnode node, const char *propname, const char *value);
/**
* ofnode_set_enabled() - Enable or disable a device tree node given by its
* ofnode
*
* This function effectively sets the node's "status" property to either "okay"
* or "disable", hence making it available for driver model initialization or
* not.
*
* @node: The node to enable
* @value: Flag that tells the function to either disable or enable the
* node
* @return 0 if successful, -ve on error
*/
int ofnode_set_enabled(ofnode node, bool value);
#endif

@ -30,6 +30,7 @@ enum uclass_id {
UCLASS_ADC, /* Analog-to-digital converter */
UCLASS_AHCI, /* SATA disk controller */
UCLASS_BLK, /* Block device */
UCLASS_BOARD, /* Device information from hardware */
UCLASS_CLK, /* Clock source, e.g. used by peripherals */
UCLASS_CPU, /* CPU, typically part of an SoC */
UCLASS_CROS_EC, /* Chrome OS EC */

@ -15,6 +15,7 @@
#include <serial.h>
#include <asm/sections.h>
#include <linux/ctype.h>
#include <linux/ioport.h>
#include <linux/lzo.h>
DECLARE_GLOBAL_DATA_PTR;
@ -1181,41 +1182,34 @@ int fdtdec_setup_mem_size_base(void)
#if defined(CONFIG_NR_DRAM_BANKS)
static int get_next_memory_node(const void *blob, int mem)
static ofnode get_next_memory_node(ofnode mem)
{
do {
mem = fdt_node_offset_by_prop_value(gd->fdt_blob, mem,
"device_type", "memory", 7);
} while (!fdtdec_get_is_enabled(blob, mem));
mem = ofnode_by_prop_value(mem, "device_type", "memory", 7);
} while (ofnode_valid(mem) && !ofnode_is_available(mem));
return mem;
}
int fdtdec_setup_memory_banksize(void)
{
int bank, ret, mem, reg = 0;
struct fdt_resource res;
int bank, reg = 0;
struct resource res;
ofnode mem;
mem = get_next_memory_node(gd->fdt_blob, -1);
if (mem < 0) {
debug("%s: Missing /memory node\n", __func__);
return -EINVAL;
}
mem = get_next_memory_node(ofnode_null());
if (!ofnode_valid(mem))
goto missing_node;
for (bank = 0; bank < CONFIG_NR_DRAM_BANKS; bank++) {
ret = fdt_get_resource(gd->fdt_blob, mem, "reg", reg++, &res);
if (ret == -FDT_ERR_NOTFOUND) {
while (ofnode_read_resource(mem, reg++, &res)) {
reg = 0;
mem = get_next_memory_node(gd->fdt_blob, mem);
if (mem == -FDT_ERR_NOTFOUND)
break;
ret = fdt_get_resource(gd->fdt_blob, mem, "reg", reg++, &res);
if (ret == -FDT_ERR_NOTFOUND)
break;
}
if (ret != 0) {
return -EINVAL;
mem = get_next_memory_node(mem);
if (!ofnode_valid(mem)) {
if (bank)
return 0;
goto missing_node;
}
}
gd->bd->bi_dram[bank].start = (phys_addr_t)res.start;
@ -1229,6 +1223,10 @@ int fdtdec_setup_memory_banksize(void)
}
return 0;
missing_node:
debug("%s: Missing /memory node\n", __func__);
return -EINVAL;
}
#endif

@ -628,28 +628,50 @@ class Fdt(FdtRo):
return check_err(fdt_setprop(self._fdt, nodeoffset, prop_name,
val, len(val)), quiet)
def delprop(self, nodeoffset, prop_name):
def delprop(self, nodeoffset, prop_name, quiet=()):
"""Delete a property from a node
Args:
nodeoffset: Node offset containing property to delete
prop_name: Name of property to delete
quiet: Errors to ignore (empty to raise on all errors)
Returns:
Error code, or 0 if OK
Raises:
FdtError if the property does not exist, or another error occurs
"""
return check_err(fdt_delprop(self._fdt, nodeoffset, prop_name))
return check_err(fdt_delprop(self._fdt, nodeoffset, prop_name), quiet)
def add_subnode(self, parentoffset, name, quiet=()):
"""Add a new subnode to a node
def del_node(self, nodeoffset):
Args:
parentoffset: Parent offset to add the subnode to
name: Name of node to add
Returns:
offset of the node created, or negative error code on failure
Raises:
FdtError if there is not enough space, or another error occurs
"""
return check_err(fdt_add_subnode(self._fdt, parentoffset, name), quiet)
def del_node(self, nodeoffset, quiet=()):
"""Delete a node
Args:
nodeoffset: Node offset containing property to delete
nodeoffset: Offset of node to delete
Returns:
Error code, or 0 if OK
Raises:
FdtError if the node does not exist, or another error occurs
FdtError if an error occurs
"""
return check_err(fdt_del_node(self._fdt, nodeoffset))
return check_err(fdt_del_node(self._fdt, nodeoffset), quiet)
class Property(bytearray):

@ -14,8 +14,10 @@ obj-$(CONFIG_UT_DM) += test-uclass.o
obj-$(CONFIG_UT_DM) += core.o
ifneq ($(CONFIG_SANDBOX),)
obj-$(CONFIG_BLK) += blk.o
obj-$(CONFIG_BOARD) += board.o
obj-$(CONFIG_CLK) += clk.o
obj-$(CONFIG_DM_ETH) += eth.o
obj-$(CONFIG_FIRMWARE) += firmware.o
obj-$(CONFIG_DM_GPIO) += gpio.o
obj-$(CONFIG_DM_I2C) += i2c.o
obj-$(CONFIG_LED) += led.o

@ -0,0 +1,57 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2018
* Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
*/
#include <common.h>
#include <dm.h>
#include <dm/test.h>
#include <board.h>
#include <test/ut.h>
#include "../../drivers/board/sandbox.h"
static int dm_test_board(struct unit_test_state *uts)
{
struct udevice *board;
bool called_detect;
char str[64];
int i;
board_get(&board);
ut_assert(board);
board_get_bool(board, BOOL_CALLED_DETECT, &called_detect);
ut_assert(!called_detect);
board_detect(board);
board_get_bool(board, BOOL_CALLED_DETECT, &called_detect);
ut_assert(called_detect);
board_get_str(board, STR_VACATIONSPOT, sizeof(str), str);
ut_assertok(strcmp(str, "R'lyeh"));
board_get_int(board, INT_TEST1, &i);
ut_asserteq(0, i);
board_get_int(board, INT_TEST2, &i);
ut_asserteq(100, i);
board_get_str(board, STR_VACATIONSPOT, sizeof(str), str);
ut_assertok(strcmp(str, "Carcosa"));
board_get_int(board, INT_TEST1, &i);
ut_asserteq(1, i);
board_get_int(board, INT_TEST2, &i);
ut_asserteq(99, i);
board_get_str(board, STR_VACATIONSPOT, sizeof(str), str);
ut_assertok(strcmp(str, "Yuggoth"));
return 0;
}
DM_TEST(dm_test_board, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);

@ -0,0 +1,22 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2018 Xilinx, Inc.
*/
#include <common.h>
#include <dm.h>
#include <syscon.h>
#include <asm/test.h>
#include <dm/test.h>
#include <test/ut.h>
/* Base test of firmware probe */
static int dm_test_firmware_probe(struct unit_test_state *uts)
{
struct udevice *dev;
ut_assertok(uclass_get_device_by_name(UCLASS_FIRMWARE,
"sandbox-firmware", &dev));
return 0;
}
DM_TEST(dm_test_firmware_probe, DM_TESTF_SCAN_FDT);

@ -14,6 +14,8 @@
#include <dm/device-internal.h>
#include <dm/uclass-internal.h>
#include <dm/util.h>
#include <dm/lists.h>
#include <dm/of_access.h>
#include <test/ut.h>
DECLARE_GLOBAL_DATA_PTR;
@ -503,3 +505,83 @@ static int dm_test_fdt_remap_addr_live(struct unit_test_state *uts)
}
DM_TEST(dm_test_fdt_remap_addr_live,
DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
static int dm_test_fdt_livetree_writing(struct unit_test_state *uts)
{
struct udevice *dev;
ofnode node;
if (!of_live_active()) {
printf("Live tree not active; ignore test\n");
return 0;
}
/* Test enabling devices */
node = ofnode_path("/usb@2");
ut_assert(!of_device_is_available(ofnode_to_np(node)));
ofnode_set_enabled(node, true);
ut_assert(of_device_is_available(ofnode_to_np(node)));
device_bind_driver_to_node(dm_root(), "usb_sandbox", "usb@2", node,
&dev);
ut_assertok(uclass_find_device_by_seq(UCLASS_USB, 2, true, &dev));
/* Test string property setting */
ut_assert(device_is_compatible(dev, "sandbox,usb"));
ofnode_write_string(node, "compatible", "gdsys,super-usb");
ut_assert(device_is_compatible(dev, "gdsys,super-usb"));
ofnode_write_string(node, "compatible", "sandbox,usb");
ut_assert(device_is_compatible(dev, "sandbox,usb"));
/* Test setting generic properties */
/* Non-existent in DTB */
ut_asserteq(FDT_ADDR_T_NONE, dev_read_addr(dev));
/* reg = 0x42, size = 0x100 */
ut_assertok(ofnode_write_prop(node, "reg", 8,
"\x00\x00\x00\x42\x00\x00\x01\x00"));
ut_asserteq(0x42, dev_read_addr(dev));
/* Test disabling devices */
device_remove(dev, DM_REMOVE_NORMAL);
device_unbind(dev);
ut_assert(of_device_is_available(ofnode_to_np(node)));
ofnode_set_enabled(node, false);
ut_assert(!of_device_is_available(ofnode_to_np(node)));
return 0;
}
DM_TEST(dm_test_fdt_livetree_writing, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
static int dm_test_fdt_disable_enable_by_path(struct unit_test_state *uts)
{
ofnode node;
if (!of_live_active()) {
printf("Live tree not active; ignore test\n");
return 0;
}
node = ofnode_path("/usb@2");
/* Test enabling devices */
ut_assert(!of_device_is_available(ofnode_to_np(node)));
dev_enable_by_path("/usb@2");
ut_assert(of_device_is_available(ofnode_to_np(node)));
/* Test disabling devices */
ut_assert(of_device_is_available(ofnode_to_np(node)));
dev_disable_by_path("/usb@2");
ut_assert(!of_device_is_available(ofnode_to_np(node)));
return 0;
}
DM_TEST(dm_test_fdt_disable_enable_by_path, DM_TESTF_SCAN_PDATA |
DM_TESTF_SCAN_FDT);

@ -330,9 +330,13 @@ image-pos:
for each entry. This makes it easy to find out exactly where the entry
ended up in the image, regardless of parent sections, etc.
expand-size:
Expand the size of this entry to fit available space. This space is only
limited by the size of the image/section and the position of the next
entry.
The attributes supported for images are described below. Several are similar
to those for entries.
The attributes supported for images and sections are described below. Several
are similar to those for entries.
size:
Sets the image size in bytes, for example 'size = <0x100000>' for a
@ -471,15 +475,26 @@ see README.entries. This is generated from the source code using:
binman -E >tools/binman/README.entries
Special properties
------------------
Hashing Entries
---------------
Some entries support special properties, documented here:
It is possible to ask binman to hash the contents of an entry and write that
value back to the device-tree node. For example:
u-boot-with-ucode-ptr:
optional-ucode: boolean property to make microcode optional. If the
u-boot.bin image does not include microcode, no error will
be generated.
binman {
u-boot {
hash {
algo = "sha256";
};
};
};
Here, a new 'value' property will be written to the 'hash' node containing
the hash of the 'u-boot' entry. Only SHA256 is supported at present. Whole
sections can be hased if desired, by adding the 'hash' node to the section.
The has value can be chcked at runtime by hashing the data actually read and
comparing this has to the value in the device tree.
Order of image creation
@ -613,6 +628,22 @@ the device tree. These can be used by U-Boot at run-time to find the location
of each entry.
Compression
-----------
Binman support compression for 'blob' entries (those of type 'blob' and
derivatives). To enable this for an entry, add a 'compression' property:
blob {
filename = "datafile";
compression = "lz4";
};
The entry will then contain the compressed data, using the 'lz4' compression
algorithm. Currently this is the only one that is supported.
Map files
---------

@ -19,11 +19,27 @@ class by other entry types.
Properties / Entry arguments:
- filename: Filename of file to read into entry
- compress: Compression algorithm to use:
none: No compression
lz4: Use lz4 compression (via 'lz4' command-line utility)
This entry reads data from a file and places it in the entry. The
default filename is often specified specified by the subclass. See for
example the 'u_boot' entry which provides the filename 'u-boot.bin'.
If compression is enabled, an extra 'uncomp-size' property is written to
the node (if enabled with -u) which provides the uncompressed size of the
data.
Entry: blob-dtb: A blob that holds a device tree
------------------------------------------------
This is a blob containing a device tree. The contents of the blob are
obtained from the list of available device-tree files, managed by the
'state' module.
Entry: blob-named-by-arg: A blob entry which gets its filename property from its subclass
@ -55,6 +71,21 @@ updating the EC on startup via software sync.
Entry: files: Entry containing a set of files
---------------------------------------------
Properties / Entry arguments:
- pattern: Filename pattern to match the files to include
- compress: Compression algorithm to use:
none: No compression
lz4: Use lz4 compression (via 'lz4' command-line utility)
This entry reads a number of files and places each in a separate sub-entry
within this entry. To access these you need to enable device-tree updates
at run-time so you can obtain the file positions.
Entry: fill: An entry which is filled to a particular byte value
----------------------------------------------------------------
@ -321,6 +352,9 @@ This is the U-Boot device tree, containing configuration information for
U-Boot. U-Boot needs this to know what devices are present and which drivers
to activate.
Note: This is mostly an internal entry type, used by others. This allows
binman to know which entries contain a device tree.
Entry: u-boot-dtb-with-ucode: A U-Boot device tree file, with the microcode removed
@ -339,6 +373,17 @@ it available to u_boot_ucode.
Entry: u-boot-elf: U-Boot ELF image
-----------------------------------
Properties / Entry arguments:
- filename: Filename of u-boot (default 'u-boot')
This is the U-Boot ELF image. It does not include a device tree but can be
relocated to any address for execution.
Entry: u-boot-img: U-Boot legacy image
--------------------------------------
@ -422,6 +467,17 @@ to activate.
Entry: u-boot-spl-elf: U-Boot SPL ELF image
-------------------------------------------
Properties / Entry arguments:
- filename: Filename of SPL u-boot (default 'spl/u-boot')
This is the U-Boot SPL ELF image. It does not include a device tree but can
be relocated to any address for execution.
Entry: u-boot-spl-nodtb: SPL binary without device tree appended
----------------------------------------------------------------
@ -440,6 +496,8 @@ both SPL and the device tree).
Entry: u-boot-spl-with-ucode-ptr: U-Boot SPL with embedded microcode pointer
----------------------------------------------------------------------------
This is used when SPL must set up the microcode for U-Boot.
See Entry_u_boot_ucode for full details of the entries involved in this
process.
@ -481,6 +539,24 @@ to activate.
Entry: u-boot-tpl-dtb-with-ucode: U-Boot TPL with embedded microcode pointer
----------------------------------------------------------------------------
This is used when TPL must set up the microcode for U-Boot.
See Entry_u_boot_ucode for full details of the entries involved in this
process.
Entry: u-boot-tpl-with-ucode-ptr: U-Boot TPL with embedded microcode pointer
----------------------------------------------------------------------------
See Entry_u_boot_ucode for full details of the entries involved in this
process.
Entry: u-boot-ucode: U-Boot microcode block
-------------------------------------------
@ -536,6 +612,9 @@ Entry: u-boot-with-ucode-ptr: U-Boot with embedded microcode pointer
Properties / Entry arguments:
- filename: Filename of u-boot-nodtb.dtb (default 'u-boot-nodtb.dtb')
- optional-ucode: boolean property to make microcode optional. If the
u-boot.bin image does not include microcode, no error will
be generated.
See Entry_u_boot_ucode for full details of the three entries involved in
this process. This entry updates U-Boot with the offset and size of the
@ -555,6 +634,11 @@ Properties / Entry arguments:
- kernelkey: Name of the kernel key to use (inside keydir)
- preamble-flags: Value of the vboot preamble flags (typically 0)
Output files:
- input.<unique_name> - input file passed to futility
- vblock.<unique_name> - output file generated by futility (which is
used as the entry contents)
Chromium OS signs the read-write firmware and kernel, writing the signature
in this block. This allows U-Boot to verify that the next firmware stage
and kernel are genuine.
@ -595,3 +679,20 @@ For 32-bit U-Boot, the 'x86_start16' entry type is used instead.
Entry: x86-start16-tpl: x86 16-bit start-up code for TPL
--------------------------------------------------------
Properties / Entry arguments:
- filename: Filename of tpl/u-boot-x86-16bit-tpl.bin (default
'tpl/u-boot-x86-16bit-tpl.bin')
x86 CPUs start up in 16-bit mode, even if they are 64-bit CPUs. This code
must be placed at a particular address. This entry holds that code. It is
typically placed at offset CONFIG_SYS_X86_START16. The code is responsible
for changing to 32-bit mode and starting TPL, which in turn jumps to SPL.
If TPL is not being used, the 'x86_start16_spl or 'x86_start16' entry types
may be used instead.

@ -8,10 +8,12 @@
from __future__ import print_function
from collections import OrderedDict
from sets import Set
import sys
import fdt_util
import re
import state
import tools
class Section(object):
@ -22,6 +24,7 @@ class Section(object):
Attributes:
_node: Node object that contains the section definition in device tree
_parent_section: Parent Section object which created this Section
_size: Section size in bytes, or None if not known yet
_align_size: Section size alignment, or None
_pad_before: Number of bytes before the first entry starts. This
@ -44,14 +47,16 @@ class Section(object):
section
_entries: OrderedDict() of entries
"""
def __init__(self, name, node, test=False):
def __init__(self, name, parent_section, node, image, test=False):
global entry
global Entry
import entry
from entry import Entry
self._parent_section = parent_section
self._name = name
self._node = node
self._image = image
self._offset = 0
self._size = None
self._align_size = None
@ -63,6 +68,7 @@ class Section(object):
self._end_4gb = False
self._name_prefix = ''
self._entries = OrderedDict()
self._image_pos = None
if not test:
self._ReadNode()
self._ReadEntries()
@ -94,25 +100,42 @@ class Section(object):
def _ReadEntries(self):
for node in self._node.subnodes:
if node.name == 'hash':
continue
entry = Entry.Create(self, node)
entry.SetPrefix(self._name_prefix)
self._entries[node.name] = entry
def GetFdtSet(self):
"""Get the set of device tree files used by this image"""
fdt_set = Set()
for entry in self._entries.values():
fdt_set.update(entry.GetFdtSet())
return fdt_set
def SetOffset(self, offset):
self._offset = offset
def ExpandEntries(self):
for entry in self._entries.values():
entry.ExpandEntries()
def AddMissingProperties(self):
"""Add new properties to the device tree as needed for this entry"""
for prop in ['offset', 'size', 'image-pos']:
if not prop in self._node.props:
self._node.AddZeroProp(prop)
state.AddZeroProp(self._node, prop)
state.CheckAddHashProp(self._node)
for entry in self._entries.values():
entry.AddMissingProperties()
def SetCalculatedProperties(self):
self._node.SetInt('offset', self._offset)
self._node.SetInt('size', self._size)
self._node.SetInt('image-pos', self._image_pos)
state.SetInt(self._node, 'offset', self._offset)
state.SetInt(self._node, 'size', self._size)
image_pos = self._image_pos
if self._parent_section:
image_pos -= self._parent_section.GetRootSkipAtStart()
state.SetInt(self._node, 'image-pos', image_pos)
for entry in self._entries.values():
entry.SetCalculatedProperties()
@ -247,16 +270,32 @@ class Section(object):
for entry in entries:
self._entries[entry._node.name] = entry
def _ExpandEntries(self):
"""Expand any entries that are permitted to"""
exp_entry = None
for entry in self._entries.values():
if exp_entry:
exp_entry.ExpandToLimit(entry.offset)
exp_entry = None
if entry.expand_size:
exp_entry = entry
if exp_entry:
exp_entry.ExpandToLimit(self._size)
def CheckEntries(self):
"""Check that entries do not overlap or extend outside the section"""
"""Check that entries do not overlap or extend outside the section
This also sorts entries, if needed and expands
"""
if self._sort:
self._SortEntries()
self._ExpandEntries()
offset = 0
prev_name = 'None'
for entry in self._entries.values():
entry.CheckOffset()
if (entry.offset < self._skip_at_start or
entry.offset >= self._skip_at_start + self._size):
entry.offset + entry.size > self._skip_at_start + self._size):
entry.Raise("Offset %#x (%d) is outside the section starting "
"at %#x (%d)" %
(entry.offset, entry.offset, self._skip_at_start,
@ -409,7 +448,17 @@ class Section(object):
source_entry.Raise("Cannot find node for phandle %d" % phandle)
for entry in self._entries.values():
if entry._node == node:
if entry.data is None:
return None
return entry.data
return entry.GetData()
source_entry.Raise("Cannot find entry for node '%s'" % node.name)
def ExpandSize(self, size):
if size != self._size:
self._size = size
def GetRootSkipAtStart(self):
if self._parent_section:
return self._parent_section.GetRootSkipAtStart()
return self._skip_at_start
def GetImageSize(self):
return self._image._size

@ -30,6 +30,10 @@ def ParseArgs(argv):
help='Enabling debugging (provides a full traceback on error)')
parser.add_option('-E', '--entry-docs', action='store_true',
help='Write out entry documentation (see README.entries)')
parser.add_option('--fake-dtb', action='store_true',
help='Use fake device tree contents (for testing only)')
parser.add_option('-i', '--image', type='string', action='append',
help='Image filename to build (if not specified, build all)')
parser.add_option('-I', '--indir', action='append',
help='Add a path to a directory to use for input files')
parser.add_option('-H', '--full-help', action='store_true',

@ -7,27 +7,19 @@
from collections import OrderedDict
import os
import re
import sys
import tools
import command
import elf
from image import Image
import state
import tout
# List of images we plan to create
# 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 = {}
# Arguments passed to binman to provide arguments to entries
entry_args = {}
def _ReadImageDesc(binman_node):
"""Read the image descriptions from the /binman node
@ -60,39 +52,15 @@ 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.
def WriteEntryDocs(modules, test_missing=None):
"""Write out documentation for all entries
Args:
fname: Filename to look up (e.g. 'u-boot.dtb').
Returns:
Fdt object associated with the filename
modules: List of Module objects to get docs for
test_missing: Used for testing only, to force an entry's documeentation
to show as missing even if it is present. Should be set to None in
normal use.
"""
return fdt_files[fname]
def GetFdtPath(fname):
return fdt_files[fname]._fname
def SetEntryArgs(args):
global entry_args
entry_args = {}
if args:
for arg in args:
m = re.match('([^=]*)=(.*)', arg)
if not m:
raise ValueError("Invalid entry arguemnt '%s'" % arg)
entry_args[m.group(1)] = m.group(2)
def GetEntryArg(name):
return entry_args.get(name)
def WriteEntryDocs(modules, test_missing=None):
from entry import Entry
Entry.WriteDocs(modules, test_missing)
@ -138,23 +106,20 @@ def Binman(options, args):
tout.Init(options.verbosity)
elf.debug = options.debug
state.use_fake_dtb = options.fake_dtb
try:
tools.SetInputDirs(options.indir)
tools.PrepareOutputDir(options.outdir, options.preserve)
SetEntryArgs(options.entry_arg)
state.SetEntryArgs(options.entry_arg)
# 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())
fname = tools.GetOutputFilename('u-boot.dtb.out')
tools.WriteFile(fname, tools.ReadFile(dtb_fname))
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' "
@ -162,6 +127,17 @@ def Binman(options, args):
images = _ReadImageDesc(node)
if options.image:
skip = []
for name, image in images.iteritems():
if name not in options.image:
del images[name]
skip.append(name)
if skip:
print 'Skipping images: %s\n' % ', '.join(skip)
state.Prepare(images, dtb)
# 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
@ -170,12 +146,15 @@ def Binman(options, args):
# without changing the device-tree size, thus ensuring that our
# entry offsets remain the same.
for image in images.values():
image.ExpandEntries()
if options.update_fdt:
image.AddMissingProperties()
image.ProcessFdt(dtb)
dtb.Pack()
dtb.Flush()
for dtb_item in state.GetFdts():
dtb_item.Sync(auto_resize=True)
dtb_item.Pack()
dtb_item.Flush()
for image in images.values():
# Perform all steps for this image, including checking and
@ -184,19 +163,30 @@ def Binman(options, args):
# completed and written, but that does not seem important.
image.GetEntryContents()
image.GetEntryOffsets()
image.PackEntries()
image.CheckSize()
image.CheckEntries()
try:
image.PackEntries()
image.CheckSize()
image.CheckEntries()
except Exception as e:
if options.map:
fname = image.WriteMap()
print "Wrote map file '%s' to show errors" % fname
raise
image.SetImagePos()
if options.update_fdt:
image.SetCalculatedProperties()
for dtb_item in state.GetFdts():
dtb_item.Sync()
image.ProcessEntryContents()
image.WriteSymbols()
image.BuildImage()
if options.map:
image.WriteMap()
with open(fname, 'wb') as outfd:
outfd.write(dtb.GetContents())
# Write the updated FDTs to our output files
for dtb_item in state.GetFdts():
tools.WriteFile(dtb_item._fname, dtb_item.GetContents())
finally:
tools.FinaliseOutputDir()
finally:

@ -17,10 +17,12 @@ try:
except:
have_importlib = False
import fdt_util
import control
import os
from sets import Set
import sys
import fdt_util
import state
import tools
modules = {}
@ -74,6 +76,7 @@ class Entry(object):
self.pad_after = 0
self.offset_unset = False
self.image_pos = None
self._expand_size = False
if read_node:
self.ReadNode()
@ -159,20 +162,57 @@ class Entry(object):
"of two" % (self._node.path, self.align_size))
self.align_end = fdt_util.GetInt(self._node, 'align-end')
self.offset_unset = fdt_util.GetBool(self._node, 'offset-unset')
self.expand_size = fdt_util.GetBool(self._node, 'expand-size')
def GetDefaultFilename(self):
return None
def GetFdtSet(self):
"""Get the set of device trees used by this entry
Returns:
Set containing the filename from this entry, if it is a .dtb, else
an empty set
"""
fname = self.GetDefaultFilename()
# It would be better to use isinstance(self, Entry_blob_dtb) here but
# we cannot access Entry_blob_dtb
if fname and fname.endswith('.dtb'):
return Set([fname])
return Set()
def ExpandEntries(self):
pass
def AddMissingProperties(self):
"""Add new properties to the device tree as needed for this entry"""
for prop in ['offset', 'size', 'image-pos']:
if not prop in self._node.props:
self._node.AddZeroProp(prop)
state.AddZeroProp(self._node, prop)
err = state.CheckAddHashProp(self._node)
if err:
self.Raise(err)
def SetCalculatedProperties(self):
"""Set the value of device-tree properties calculated by binman"""
self._node.SetInt('offset', self.offset)
self._node.SetInt('size', self.size)
self._node.SetInt('image-pos', self.image_pos)
state.SetInt(self._node, 'offset', self.offset)
state.SetInt(self._node, 'size', self.size)
state.SetInt(self._node, 'image-pos',
self.image_pos - self.section.GetRootSkipAtStart())
state.CheckSetHashValue(self._node, self.GetData)
def ProcessFdt(self, fdt):
"""Allow entries to adjust the device tree
Some entries need to adjust the device tree for their purposes. This
may involve adding or deleting properties.
Returns:
True if processing is complete
False if processing could not be completed due to a dependency.
This will cause the entry to be retried after others have been
called
"""
return True
def SetPrefix(self, prefix):
@ -351,9 +391,16 @@ class Entry(object):
pass
@staticmethod
def GetStr(value):
if value is None:
return '<none> '
return '%08x' % value
@staticmethod
def WriteMapLine(fd, indent, name, offset, size, image_pos):
print('%08x %s%08x %08x %s' % (image_pos, ' ' * indent, offset,
size, name), file=fd)
print('%s %s%s %s %s' % (Entry.GetStr(image_pos), ' ' * indent,
Entry.GetStr(offset), Entry.GetStr(size),
name), file=fd)
def WriteMap(self, fd, indent):
"""Write a map of the entry to a .map file
@ -390,7 +437,7 @@ class Entry(object):
Raises:
ValueError if the argument cannot be converted to in
"""
value = control.GetEntryArg(name)
value = state.GetEntryArg(name)
if value is not None:
if datatype == int:
try:
@ -456,3 +503,30 @@ features to produce new behaviours.
if missing:
raise ValueError('Documentation is missing for modules: %s' %
', '.join(missing))
def GetUniqueName(self):
"""Get a unique name for a node
Returns:
String containing a unique name for a node, consisting of the name
of all ancestors (starting from within the 'binman' node) separated
by a dot ('.'). This can be useful for generating unique filesnames
in the output directory.
"""
name = self.name
node = self._node
while node.parent:
node = node.parent
if node.name == 'binman':
break
name = '%s.%s' % (node.name, name)
return name
def ExpandToLimit(self, limit):
"""Expand an entry so that it ends at the given offset limit"""
if self.offset + self.size < limit:
self.size = limit - self.offset
# Request the contents again, since changing the size requires that
# the data grows. This should not fail, but check it to be sure.
if not self.ObtainContents():
self.Raise('Cannot obtain contents when expanding entry')

@ -54,6 +54,22 @@ class TestEntry(unittest.TestCase):
self.assertIn("Unknown entry type 'invalid-name' in node "
"'invalid-path'", str(e.exception))
def testUniqueName(self):
"""Test Entry.GetUniqueName"""
import entry
Node = collections.namedtuple('Node', ['name', 'parent'])
base_node = Node('root', None)
base_entry = entry.Entry(None, None, base_node, read_node=False)
self.assertEqual('root', base_entry.GetUniqueName())
sub_node = Node('subnode', base_node)
sub_entry = entry.Entry(None, None, sub_node, read_node=False)
self.assertEqual('root.subnode', sub_entry.GetUniqueName())
def testGetDefaultFilename(self):
"""Trivial test for this base class function"""
import entry
base_entry = entry.Entry(None, None, None, read_node=False)
self.assertIsNone(base_entry.GetDefaultFilename())
if __name__ == "__main__":
unittest.main()

@ -48,6 +48,8 @@ class Entry__testing(Entry):
'return-unknown-contents')
self.bad_update_contents = fdt_util.GetBool(self._node,
'bad-update-contents')
self.return_contents_once = fdt_util.GetBool(self._node,
'return-contents-once')
# Set to True when the entry is ready to process the FDT.
self.process_fdt_ready = False
@ -68,12 +70,15 @@ class Entry__testing(Entry):
EntryArg('test-existing-prop', str)], self.require_args)
if self.force_bad_datatype:
self.GetEntryArgsOrProps([EntryArg('test-bad-datatype-arg', bool)])
self.return_contents = True
def ObtainContents(self):
if self.return_unknown_contents:
if self.return_unknown_contents or not self.return_contents:
return False
self.data = 'a'
self.contents_size = len(self.data)
if self.return_contents_once:
self.return_contents = False
return True
def GetOffsets(self):

@ -7,6 +7,7 @@
from entry import Entry
import fdt_util
import state
import tools
class Entry_blob(Entry):
@ -17,14 +18,23 @@ class Entry_blob(Entry):
Properties / Entry arguments:
- filename: Filename of file to read into entry
- compress: Compression algorithm to use:
none: No compression
lz4: Use lz4 compression (via 'lz4' command-line utility)
This entry reads data from a file and places it in the entry. The
default filename is often specified specified by the subclass. See for
example the 'u_boot' entry which provides the filename 'u-boot.bin'.
If compression is enabled, an extra 'uncomp-size' property is written to
the node (if enabled with -u) which provides the uncompressed size of the
data.
"""
def __init__(self, section, etype, node):
Entry.__init__(self, section, etype, node)
self._filename = fdt_util.GetString(self._node, "filename", self.etype)
self._filename = fdt_util.GetString(self._node, 'filename', self.etype)
self._compress = fdt_util.GetString(self._node, 'compress', 'none')
self._uncompressed_size = None
def ObtainContents(self):
self._filename = self.GetDefaultFilename()
@ -33,15 +43,36 @@ class Entry_blob(Entry):
return True
def ReadBlobContents(self):
with open(self._pathname) as fd:
# We assume the data is small enough to fit into memory. If this
# is used for large filesystem image that might not be true.
# In that case, Image.BuildImage() could be adjusted to use a
# 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.SetContents(fd.read())
# We assume the data is small enough to fit into memory. If this
# is used for large filesystem image that might not be true.
# In that case, Image.BuildImage() could be adjusted to use a
# 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.
data = tools.ReadFile(self._pathname)
if self._compress == 'lz4':
self._uncompressed_size = len(data)
'''
import lz4 # Import this only if needed (python-lz4 dependency)
try:
data = lz4.frame.compress(data)
except AttributeError:
data = lz4.compress(data)
'''
data = tools.Run('lz4', '-c', self._pathname, )
self.SetContents(data)
return True
def GetDefaultFilename(self):
return self._filename
def AddMissingProperties(self):
Entry.AddMissingProperties(self)
if self._compress != 'none':
state.AddZeroProp(self._node, 'uncomp-size')
def SetCalculatedProperties(self):
Entry.SetCalculatedProperties(self)
if self._uncompressed_size is not None:
state.SetInt(self._node, 'uncomp-size', self._uncompressed_size)

@ -0,0 +1,33 @@
# SPDX-License-Identifier: GPL-2.0+
# Copyright (c) 2018 Google, Inc
# Written by Simon Glass <sjg@chromium.org>
#
# Entry-type module for U-Boot device tree files
#
import state
from entry import Entry
from blob import Entry_blob
class Entry_blob_dtb(Entry_blob):
"""A blob that holds a device tree
This is a blob containing a device tree. The contents of the blob are
obtained from the list of available device-tree files, managed by the
'state' module.
"""
def __init__(self, section, etype, node):
Entry_blob.__init__(self, section, etype, node)
def ObtainContents(self):
"""Get the device-tree from the list held by the 'state' module"""
self._filename = self.GetDefaultFilename()
self._pathname, data = state.GetFdtContents(self._filename)
self.SetContents(data)
return True
def ProcessContents(self):
"""Re-read the DTB contents so that we get any calculated properties"""
_, data = state.GetFdtContents(self._filename)
self.SetContents(data)

@ -0,0 +1,57 @@
# SPDX-License-Identifier: GPL-2.0+
# Copyright (c) 2018 Google, Inc
# Written by Simon Glass <sjg@chromium.org>
#
# Entry-type module for a set of files which are placed in individual
# sub-entries
#
import glob
import os
from section import Entry_section
import fdt_util
import state
import tools
import bsection
class Entry_files(Entry_section):
"""Entry containing a set of files
Properties / Entry arguments:
- pattern: Filename pattern to match the files to include
- compress: Compression algorithm to use:
none: No compression
lz4: Use lz4 compression (via 'lz4' command-line utility)
This entry reads a number of files and places each in a separate sub-entry
within this entry. To access these you need to enable device-tree updates
at run-time so you can obtain the file positions.
"""
def __init__(self, section, etype, node):
Entry_section.__init__(self, section, etype, node)
self._pattern = fdt_util.GetString(self._node, 'pattern')
if not self._pattern:
self.Raise("Missing 'pattern' property")
self._compress = fdt_util.GetString(self._node, 'compress', 'none')
self._require_matches = fdt_util.GetBool(self._node,
'require-matches')
def ExpandEntries(self):
files = tools.GetInputFilenameGlob(self._pattern)
if self._require_matches and not files:
self.Raise("Pattern '%s' matched no files" % self._pattern)
for fname in files:
if not os.path.isfile(fname):
continue
name = os.path.basename(fname)
subnode = self._node.FindNode(name)
if not subnode:
subnode = state.AddSubnode(self._node, name)
state.AddString(subnode, 'type', 'blob')
state.AddString(subnode, 'filename', fname)
state.AddString(subnode, 'compress', self._compress)
# Read entries again, now that we have some
self._section._ReadEntries()

@ -23,7 +23,7 @@ class Entry_fill(Entry):
"""
def __init__(self, section, etype, node):
Entry.__init__(self, section, etype, node)
if not self.size:
if self.size is None:
self.Raise("'fill' entry must have a size property")
self.fill_value = fdt_util.GetByte(self._node, 'fill-byte', 0)

@ -42,14 +42,17 @@ class Entry_fmap(Entry):
for subentry in entries.values():
_AddEntries(areas, subentry)
else:
areas.append(fmap_util.FmapArea(entry.image_pos or 0,
entry.size or 0, entry.name, 0))
pos = entry.image_pos
if pos is not None:
pos -= entry.section.GetRootSkipAtStart()
areas.append(fmap_util.FmapArea(pos or 0, entry.size or 0,
entry.name, 0))
entries = self.section.GetEntries()
entries = self.section._image.GetEntries()
areas = []
for entry in entries.values():
_AddEntries(areas, entry)
return fmap_util.EncodeFmap(self.section.GetSize() or 0, self.name,
return fmap_util.EncodeFmap(self.section.GetImageSize() or 0, self.name,
areas)
def ObtainContents(self):

@ -32,11 +32,19 @@ class Entry_section(Entry):
"""
def __init__(self, section, etype, node):
Entry.__init__(self, section, etype, node)
self._section = bsection.Section(node.name, node)
self._section = bsection.Section(node.name, section, node,
section._image)
def GetFdtSet(self):
return self._section.GetFdtSet()
def ProcessFdt(self, fdt):
return self._section.ProcessFdt(fdt)
def ExpandEntries(self):
Entry.ExpandEntries(self)
self._section.ExpandEntries()
def AddMissingProperties(self):
Entry.AddMissingProperties(self)
self._section.AddMissingProperties()
@ -92,3 +100,7 @@ class Entry_section(Entry):
def GetEntries(self):
return self._section.GetEntries()
def ExpandToLimit(self, limit):
super(Entry_section, self).ExpandToLimit(limit)
self._section.ExpandSize(self.size)

@ -51,6 +51,9 @@ class Entry_text(Entry):
self.text_label, = self.GetEntryArgsOrProps(
[EntryArg('text-label', str)])
self.value, = self.GetEntryArgsOrProps([EntryArg(self.text_label, str)])
if not self.value:
self.Raise("No value provided for text label '%s'" %
self.text_label)
def ObtainContents(self):
self.SetContents(self.value)

@ -6,9 +6,9 @@
#
from entry import Entry
from blob import Entry_blob
from blob_dtb import Entry_blob_dtb
class Entry_u_boot_dtb(Entry_blob):
class Entry_u_boot_dtb(Entry_blob_dtb):
"""U-Boot device tree
Properties / Entry arguments:
@ -17,9 +17,12 @@ class Entry_u_boot_dtb(Entry_blob):
This is the U-Boot device tree, containing configuration information for
U-Boot. U-Boot needs this to know what devices are present and which drivers
to activate.
Note: This is mostly an internal entry type, used by others. This allows
binman to know which entries contain a device tree.
"""
def __init__(self, section, etype, node):
Entry_blob.__init__(self, section, etype, node)
Entry_blob_dtb.__init__(self, section, etype, node)
def GetDefaultFilename(self):
return 'u-boot.dtb'

@ -5,12 +5,12 @@
# Entry-type module for U-Boot device tree with the microcode removed
#
import control
from entry import Entry
from blob import Entry_blob
from blob_dtb import Entry_blob_dtb
import state
import tools
class Entry_u_boot_dtb_with_ucode(Entry_blob):
class Entry_u_boot_dtb_with_ucode(Entry_blob_dtb):
"""A U-Boot device tree file, with the microcode removed
Properties / Entry arguments:
@ -25,7 +25,7 @@ class Entry_u_boot_dtb_with_ucode(Entry_blob):
it available to u_boot_ucode.
"""
def __init__(self, section, etype, node):
Entry_blob.__init__(self, section, etype, node)
Entry_blob_dtb.__init__(self, section, etype, node)
self.ucode_data = ''
self.collate = False
self.ucode_offset = None
@ -45,13 +45,16 @@ class Entry_u_boot_dtb_with_ucode(Entry_blob):
'u-boot-spl-with-ucode-ptr')
if not ucode_dest_entry or not ucode_dest_entry.target_offset:
ucode_dest_entry = self.section.FindEntryType(
'u-boot-tpl-with-ucode-ptr')
if not ucode_dest_entry or not ucode_dest_entry.target_offset:
ucode_dest_entry = self.section.FindEntryType(
'u-boot-with-ucode-ptr')
if not ucode_dest_entry or not ucode_dest_entry.target_offset:
return True
# Remove the microcode
fname = self.GetDefaultFilename()
fdt = control.GetFdt(fname)
fdt = state.GetFdt(fname)
self.ucode = fdt.GetNode('/microcode')
if not self.ucode:
raise self.Raise("No /microcode node found in '%s'" % fname)
@ -69,15 +72,15 @@ class Entry_u_boot_dtb_with_ucode(Entry_blob):
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.ReadBlobContents()
if self.ucode:
Entry_blob_dtb.ObtainContents(self)
if self.ucode and not self.collate:
for node in self.ucode.subnodes:
data_prop = node.props.get('data')
if data_prop and not self.collate:
if data_prop:
# 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)
self.ready = True
return True
self.ready = True
else:
self.ready = True
return self.ready

@ -0,0 +1,39 @@
# SPDX-License-Identifier: GPL-2.0+
# Copyright (c) 2018 Google, Inc
# Written by Simon Glass <sjg@chromium.org>
#
# Entry-type module for U-Boot ELF image
#
from entry import Entry
from blob import Entry_blob
import fdt_util
import tools
class Entry_u_boot_elf(Entry_blob):
"""U-Boot ELF image
Properties / Entry arguments:
- filename: Filename of u-boot (default 'u-boot')
This is the U-Boot ELF image. It does not include a device tree but can be
relocated to any address for execution.
"""
def __init__(self, section, etype, node):
Entry_blob.__init__(self, section, etype, node)
self._strip = fdt_util.GetBool(self._node, 'strip')
def ReadBlobContents(self):
if self._strip:
uniq = self.GetUniqueName()
out_fname = tools.GetOutputFilename('%s.stripped' % uniq)
tools.WriteFile(out_fname, tools.ReadFile(self._pathname))
tools.Run('strip', out_fname)
self.SetContents(tools.ReadFile(out_fname))
else:
self.SetContents(tools.ReadFile(self._pathname))
return True
def GetDefaultFilename(self):
return 'u-boot'

@ -6,9 +6,9 @@
#
from entry import Entry
from blob import Entry_blob
from blob_dtb import Entry_blob_dtb
class Entry_u_boot_spl_dtb(Entry_blob):
class Entry_u_boot_spl_dtb(Entry_blob_dtb):
"""U-Boot SPL device tree
Properties / Entry arguments:
@ -19,7 +19,7 @@ class Entry_u_boot_spl_dtb(Entry_blob):
to activate.
"""
def __init__(self, section, etype, node):
Entry_blob.__init__(self, section, etype, node)
Entry_blob_dtb.__init__(self, section, etype, node)
def GetDefaultFilename(self):
return 'spl/u-boot-spl.dtb'

@ -0,0 +1,24 @@
# SPDX-License-Identifier: GPL-2.0+
# Copyright (c) 2018 Google, Inc
# Written by Simon Glass <sjg@chromium.org>
#
# Entry-type module for U-Boot SPL ELF image
#
from entry import Entry
from blob import Entry_blob
class Entry_u_boot_spl_elf(Entry_blob):
"""U-Boot SPL ELF image
Properties / Entry arguments:
- filename: Filename of SPL u-boot (default 'spl/u-boot')
This is the U-Boot SPL ELF image. It does not include a device tree but can
be relocated to any address for execution.
"""
def __init__(self, section, etype, node):
Entry_blob.__init__(self, section, etype, node)
def GetDefaultFilename(self):
return 'spl/u-boot-spl'

@ -16,6 +16,8 @@ import tools
class Entry_u_boot_spl_with_ucode_ptr(Entry_u_boot_with_ucode_ptr):
"""U-Boot SPL with embedded microcode pointer
This is used when SPL must set up the microcode for U-Boot.
See Entry_u_boot_ucode for full details of the entries involved in this
process.
"""

@ -6,9 +6,9 @@
#
from entry import Entry
from blob import Entry_blob
from blob_dtb import Entry_blob_dtb
class Entry_u_boot_tpl_dtb(Entry_blob):
class Entry_u_boot_tpl_dtb(Entry_blob_dtb):
"""U-Boot TPL device tree
Properties / Entry arguments:
@ -19,7 +19,7 @@ class Entry_u_boot_tpl_dtb(Entry_blob):
to activate.
"""
def __init__(self, section, etype, node):
Entry_blob.__init__(self, section, etype, node)
Entry_blob_dtb.__init__(self, section, etype, node)
def GetDefaultFilename(self):
return 'tpl/u-boot-tpl.dtb'

@ -0,0 +1,25 @@
# SPDX-License-Identifier: GPL-2.0+
# Copyright (c) 2016 Google, Inc
# Written by Simon Glass <sjg@chromium.org>
#
# Entry-type module for U-Boot device tree with the microcode removed
#
import control
from entry import Entry
from u_boot_dtb_with_ucode import Entry_u_boot_dtb_with_ucode
import tools
class Entry_u_boot_tpl_dtb_with_ucode(Entry_u_boot_dtb_with_ucode):
"""U-Boot TPL with embedded microcode pointer
This is used when TPL must set up the microcode for U-Boot.
See Entry_u_boot_ucode for full details of the entries involved in this
process.
"""
def __init__(self, section, etype, node):
Entry_u_boot_dtb_with_ucode.__init__(self, section, etype, node)
def GetDefaultFilename(self):
return 'tpl/u-boot-tpl.dtb'

@ -0,0 +1,27 @@
# SPDX-License-Identifier: GPL-2.0+
# Copyright (c) 2016 Google, Inc
# Written by Simon Glass <sjg@chromium.org>
#
# Entry-type module for an TPL binary with an embedded microcode pointer
#
import struct
import command
from entry import Entry
from blob import Entry_blob
from u_boot_with_ucode_ptr import Entry_u_boot_with_ucode_ptr
import tools
class Entry_u_boot_tpl_with_ucode_ptr(Entry_u_boot_with_ucode_ptr):
"""U-Boot TPL with embedded microcode pointer
See Entry_u_boot_ucode for full details of the entries involved in this
process.
"""
def __init__(self, section, etype, node):
Entry_u_boot_with_ucode_ptr.__init__(self, section, etype, node)
self.elf_fname = 'tpl/u-boot-tpl'
def GetDefaultFilename(self):
return 'tpl/u-boot-tpl-nodtb.bin'

@ -62,19 +62,24 @@ class Entry_u_boot_ucode(Entry_blob):
def ObtainContents(self):
# If the section does not need microcode, there is nothing to do
ucode_dest_entry = self.section.FindEntryType('u-boot-with-ucode-ptr')
ucode_dest_entry_spl = self.section.FindEntryType(
'u-boot-spl-with-ucode-ptr')
if ((not ucode_dest_entry or not ucode_dest_entry.target_offset) and
(not ucode_dest_entry_spl or not ucode_dest_entry_spl.target_offset)):
found = False
for suffix in ['', '-spl', '-tpl']:
name = 'u-boot%s-with-ucode-ptr' % suffix
entry = self.section.FindEntryType(name)
if entry and entry.target_offset:
found = True
if not found:
self.data = ''
return True
# 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')
for suffix in ['', '-spl', '-tpl']:
name = 'u-boot%s-dtb-with-ucode' % suffix
fdt_entry = self.section.FindEntryType(name)
if fdt_entry:
break
if not fdt_entry:
return True
if not fdt_entry.ready:
@ -86,12 +91,9 @@ class Entry_u_boot_ucode(Entry_blob):
return True
# Write it out to a file
dtb_name = 'u-boot-ucode.bin'
fname = tools.GetOutputFilename(dtb_name)
with open(fname, 'wb') as fd:
fd.write(fdt_entry.ucode_data)
self._pathname = tools.GetOutputFilename('u-boot-ucode.bin')
tools.WriteFile(self._pathname, fdt_entry.ucode_data)
self._pathname = fname
self.ReadBlobContents()
return True

@ -19,6 +19,9 @@ class Entry_u_boot_with_ucode_ptr(Entry_blob):
Properties / Entry arguments:
- filename: Filename of u-boot-nodtb.dtb (default 'u-boot-nodtb.dtb')
- optional-ucode: boolean property to make microcode optional. If the
u-boot.bin image does not include microcode, no error will
be generated.
See Entry_u_boot_ucode for full details of the three entries involved in
this process. This entry updates U-Boot with the offset and size of the
@ -63,28 +66,31 @@ class Entry_u_boot_with_ucode_ptr(Entry_blob):
# the U-Boot region must start at offset 7MB in the section. In this
# case the ROM starts at 0xff800000, so the offset of the first
# entry in the section corresponds to that.
if (self.target_offset < self.offset or
self.target_offset >= self.offset + self.size):
self.Raise('Microcode pointer _dt_ucode_base_size at %08x is '
'outside the section ranging from %08x to %08x' %
(self.target_offset, self.offset, self.offset + self.size))
if (self.target_offset < self.image_pos or
self.target_offset >= self.image_pos + self.size):
self.Raise('Microcode pointer _dt_ucode_base_size at %08x is outside the section ranging from %08x to %08x' %
(self.target_offset, self.image_pos,
self.image_pos + self.size))
# Get the microcode, either from u-boot-ucode or u-boot-dtb-with-ucode.
# If we have left the microcode in the device tree, then it will be
# in the former. If we extracted the microcode from the device tree
# and collated it in one place, it will be in the latter.
# in the latter. If we extracted the microcode from the device tree
# and collated it in one place, it will be in the former.
if ucode_entry.size:
offset, size = ucode_entry.offset, ucode_entry.size
else:
dtb_entry = self.section.FindEntryType('u-boot-dtb-with-ucode')
if not dtb_entry or not dtb_entry.ready:
if not dtb_entry:
dtb_entry = self.section.FindEntryType(
'u-boot-tpl-dtb-with-ucode')
if not dtb_entry:
self.Raise('Cannot find microcode region u-boot-dtb-with-ucode')
offset = dtb_entry.offset + dtb_entry.ucode_offset
size = dtb_entry.ucode_size
# Write the microcode offset and size into the entry
offset_and_size = struct.pack('<2L', offset, size)
self.target_offset -= self.offset
self.target_offset -= self.image_pos
self.ProcessContentsUpdate(self.data[:self.target_offset] +
offset_and_size +
self.data[self.target_offset + 8:])

@ -25,6 +25,11 @@ class Entry_vblock(Entry):
- kernelkey: Name of the kernel key to use (inside keydir)
- preamble-flags: Value of the vboot preamble flags (typically 0)
Output files:
- input.<unique_name> - input file passed to futility
- vblock.<unique_name> - output file generated by futility (which is
used as the entry contents)
Chromium OS signs the read-write firmware and kernel, writing the signature
in this block. This allows U-Boot to verify that the next firmware stage
and kernel are genuine.
@ -53,8 +58,9 @@ class Entry_vblock(Entry):
return False
input_data += data
output_fname = tools.GetOutputFilename('vblock.%s' % self.name)
input_fname = tools.GetOutputFilename('input.%s' % self.name)
uniq = self.GetUniqueName()
output_fname = tools.GetOutputFilename('vblock.%s' % uniq)
input_fname = tools.GetOutputFilename('input.%s' % uniq)
tools.WriteFile(input_fname, input_data)
prefix = self.keydir + '/'
args = [
@ -69,6 +75,5 @@ class Entry_vblock(Entry):
]
#out.Notice("Sign '%s' into %s" % (', '.join(self.value), self.label))
stdout = tools.Run('futility', *args)
#out.Debug(stdout)
self.SetContents(tools.ReadFile(output_fname))
return True

@ -0,0 +1,30 @@
# SPDX-License-Identifier: GPL-2.0+
# Copyright (c) 2018 Google, Inc
# Written by Simon Glass <sjg@chromium.org>
#
# Entry-type module for the 16-bit x86 start-up code for U-Boot TPL
#
from entry import Entry
from blob import Entry_blob
class Entry_x86_start16_tpl(Entry_blob):
"""x86 16-bit start-up code for TPL
Properties / Entry arguments:
- filename: Filename of tpl/u-boot-x86-16bit-tpl.bin (default
'tpl/u-boot-x86-16bit-tpl.bin')
x86 CPUs start up in 16-bit mode, even if they are 64-bit CPUs. This code
must be placed at a particular address. This entry holds that code. It is
typically placed at offset CONFIG_SYS_X86_START16. The code is responsible
for changing to 32-bit mode and starting TPL, which in turn jumps to SPL.
If TPL is not being used, the 'x86_start16_spl or 'x86_start16' entry types
may be used instead.
"""
def __init__(self, section, etype, node):
Entry_blob.__init__(self, section, etype, node)
def GetDefaultFilename(self):
return 'tpl/u-boot-x86-16bit-tpl.bin'

@ -49,6 +49,9 @@ FmapHeader = collections.namedtuple('FmapHeader', FMAP_HEADER_NAMES)
FmapArea = collections.namedtuple('FmapArea', FMAP_AREA_NAMES)
def NameToFmap(name):
return name.replace('\0', '').replace('-', '_').upper()
def ConvertName(field_names, fields):
"""Convert a name to something flashrom likes
@ -62,7 +65,7 @@ def ConvertName(field_names, fields):
value: value of that field (string for the ones we support)
"""
name_index = field_names.index('name')
fields[name_index] = fields[name_index].replace('\0', '').replace('-', '_').upper()
fields[name_index] = NameToFmap(fields[name_index])
def DecodeFmap(data):
"""Decode a flashmap into a header and list of areas
@ -100,6 +103,7 @@ def EncodeFmap(image_size, name, areas):
"""
def _FormatBlob(fmt, names, obj):
params = [getattr(obj, name) for name in names]
ConvertName(names, params)
return struct.pack(fmt, *params)
values = FmapHeader(FMAP_SIGNATURE, 1, 0, 0, image_size, name, len(areas))

@ -6,6 +6,7 @@
#
# python -m unittest func_test.TestFunctional.testHelp
import hashlib
from optparse import OptionParser
import os
import shutil
@ -23,6 +24,7 @@ import fdt
import fdt_util
import fmap_util
import test_util
import state
import tools
import tout
@ -39,9 +41,11 @@ U_BOOT_SPL_DTB_DATA = 'spldtb'
U_BOOT_TPL_DTB_DATA = 'tpldtb'
X86_START16_DATA = 'start16'
X86_START16_SPL_DATA = 'start16spl'
X86_START16_TPL_DATA = 'start16tpl'
PPC_MPC85XX_BR_DATA = 'ppcmpc85xxbr'
U_BOOT_NODTB_DATA = 'nodtb with microcode pointer somewhere in here'
U_BOOT_SPL_NODTB_DATA = 'splnodtb with microcode pointer somewhere in here'
U_BOOT_TPL_NODTB_DATA = 'tplnodtb with microcode pointer somewhere in here'
FSP_DATA = 'fsp'
CMC_DATA = 'cmc'
VBT_DATA = 'vbt'
@ -53,6 +57,9 @@ CROS_EC_RW_DATA = 'ecrw'
GBB_DATA = 'gbbd'
BMPBLK_DATA = 'bmp'
VBLOCK_DATA = 'vblk'
FILES_DATA = ("sorry I'm late\nOh, don't bother apologising, I'm " +
"sorry you're alive\n")
COMPRESS_DATA = 'data to compress'
class TestFunctional(unittest.TestCase):
@ -94,9 +101,13 @@ class TestFunctional(unittest.TestCase):
TestFunctional._MakeInputFile('u-boot-br.bin', PPC_MPC85XX_BR_DATA)
TestFunctional._MakeInputFile('spl/u-boot-x86-16bit-spl.bin',
X86_START16_SPL_DATA)
TestFunctional._MakeInputFile('tpl/u-boot-x86-16bit-tpl.bin',
X86_START16_TPL_DATA)
TestFunctional._MakeInputFile('u-boot-nodtb.bin', U_BOOT_NODTB_DATA)
TestFunctional._MakeInputFile('spl/u-boot-spl-nodtb.bin',
U_BOOT_SPL_NODTB_DATA)
TestFunctional._MakeInputFile('tpl/u-boot-tpl-nodtb.bin',
U_BOOT_TPL_NODTB_DATA)
TestFunctional._MakeInputFile('fsp.bin', FSP_DATA)
TestFunctional._MakeInputFile('cmc.bin', CMC_DATA)
TestFunctional._MakeInputFile('vbt.bin', VBT_DATA)
@ -114,6 +125,11 @@ class TestFunctional(unittest.TestCase):
with open(self.TestFile('descriptor.bin')) as fd:
TestFunctional._MakeInputFile('descriptor.bin', fd.read())
shutil.copytree(self.TestFile('files'),
os.path.join(self._indir, 'files'))
TestFunctional._MakeInputFile('compress', COMPRESS_DATA)
@classmethod
def tearDownClass(self):
"""Remove the temporary input directory and its contents"""
@ -170,7 +186,7 @@ class TestFunctional(unittest.TestCase):
return control.Binman(options, args)
def _DoTestFile(self, fname, debug=False, map=False, update_dtb=False,
entry_args=None):
entry_args=None, images=None, use_real_dtb=False):
"""Run binman with a given test file
Args:
@ -179,6 +195,10 @@ class TestFunctional(unittest.TestCase):
map: True to output map files for the images
update_dtb: Update the offset and size of each entry in the device
tree before packing it into the image
entry_args: Dict of entry args to supply to binman
key: arg name
value: value of that arg
images: List of image names to build
"""
args = ['-p', '-I', self._indir, '-d', self.TestFile(fname)]
if debug:
@ -187,9 +207,14 @@ class TestFunctional(unittest.TestCase):
args.append('-m')
if update_dtb:
args.append('-up')
if not use_real_dtb:
args.append('--fake-dtb')
if entry_args:
for arg, value in entry_args.iteritems():
args.append('-a%s=%s' % (arg, value))
if images:
for image in images:
args += ['-i', image]
return self._DoBinman(*args)
def _SetupDtb(self, fname, outfile='u-boot.dtb'):
@ -214,8 +239,26 @@ class TestFunctional(unittest.TestCase):
TestFunctional._MakeInputFile(outfile, data)
return data
def _GetDtbContentsForSplTpl(self, dtb_data, name):
"""Create a version of the main DTB for SPL or SPL
For testing we don't actually have different versions of the DTB. With
U-Boot we normally run fdtgrep to remove unwanted nodes, but for tests
we don't normally have any unwanted nodes.
We still want the DTBs for SPL and TPL to be different though, since
otherwise it is confusing to know which one we are looking at. So add
an 'spl' or 'tpl' property to the top-level node.
"""
dtb = fdt.Fdt.FromData(dtb_data)
dtb.Scan()
dtb.GetNode('/binman').AddZeroProp(name)
dtb.Sync(auto_resize=True)
dtb.Pack()
return dtb.GetContents()
def _DoReadFileDtb(self, fname, use_real_dtb=False, map=False,
update_dtb=False, entry_args=None):
update_dtb=False, entry_args=None, reset_dtbs=True):
"""Run binman and return the resulting image
This runs binman with a given test file and then reads the resulting
@ -245,12 +288,21 @@ class TestFunctional(unittest.TestCase):
# Use the compiled test file as the u-boot-dtb input
if use_real_dtb:
dtb_data = self._SetupDtb(fname)
infile = os.path.join(self._indir, 'u-boot.dtb')
# For testing purposes, make a copy of the DT for SPL and TPL. Add
# a node indicating which it is, so aid verification.
for name in ['spl', 'tpl']:
dtb_fname = '%s/u-boot-%s.dtb' % (name, name)
outfile = os.path.join(self._indir, dtb_fname)
TestFunctional._MakeInputFile(dtb_fname,
self._GetDtbContentsForSplTpl(dtb_data, name))
try:
retcode = self._DoTestFile(fname, map=map, update_dtb=update_dtb,
entry_args=entry_args)
entry_args=entry_args, use_real_dtb=use_real_dtb)
self.assertEqual(0, retcode)
out_dtb_fname = control.GetFdtPath('u-boot.dtb')
out_dtb_fname = tools.GetOutputFilename('u-boot.dtb.out')
# Find the (only) image, read it and return its contents
image = control.images['image']
@ -266,7 +318,7 @@ class TestFunctional(unittest.TestCase):
return fd.read(), dtb_data, map_data, out_dtb_fname
finally:
# Put the test file back
if use_real_dtb:
if reset_dtbs and use_real_dtb:
self._ResetDtbs()
def _DoReadFile(self, fname, use_real_dtb=False):
@ -980,7 +1032,7 @@ class TestFunctional(unittest.TestCase):
str(e.exception))
def testPackStart16Spl(self):
"""Test that an image with an x86 start16 region can be created"""
"""Test that an image with an x86 start16 SPL region can be created"""
data = self._DoReadFile('48_x86-start16-spl.dts')
self.assertEqual(X86_START16_SPL_DATA, data[:len(X86_START16_SPL_DATA)])
@ -1329,7 +1381,7 @@ class TestFunctional(unittest.TestCase):
"""Fake calls to the futility utility"""
if pipe_list[0][0] == 'futility':
fname = pipe_list[0][3]
with open(fname, 'w') as fd:
with open(fname, 'wb') as fd:
fd.write(VBLOCK_DATA)
return command.CommandResult()
@ -1380,6 +1432,334 @@ class TestFunctional(unittest.TestCase):
self.assertIn("Node '/binman/u-boot': Please use 'offset' instead of "
"'pos'", str(e.exception))
def testFillZero(self):
"""Test for an fill entry type with a size of 0"""
data = self._DoReadFile('80_fill_empty.dts')
self.assertEqual(chr(0) * 16, data)
def testTextMissing(self):
"""Test for a text entry type where there is no text"""
with self.assertRaises(ValueError) as e:
self._DoReadFileDtb('66_text.dts',)
self.assertIn("Node '/binman/text': No value provided for text label "
"'test-id'", str(e.exception))
def testPackStart16Tpl(self):
"""Test that an image with an x86 start16 TPL region can be created"""
data = self._DoReadFile('81_x86-start16-tpl.dts')
self.assertEqual(X86_START16_TPL_DATA, data[:len(X86_START16_TPL_DATA)])
def testSelectImage(self):
"""Test that we can select which images to build"""
with test_util.capture_sys_output() as (stdout, stderr):
retcode = self._DoTestFile('06_dual_image.dts', images=['image2'])
self.assertEqual(0, retcode)
self.assertIn('Skipping images: image1', stdout.getvalue())
self.assertFalse(os.path.exists(tools.GetOutputFilename('image1.bin')))
self.assertTrue(os.path.exists(tools.GetOutputFilename('image2.bin')))
def testUpdateFdtAll(self):
"""Test that all device trees are updated with offset/size info"""
data, _, _, _ = self._DoReadFileDtb('82_fdt_update_all.dts',
use_real_dtb=True, update_dtb=True)
base_expected = {
'section:image-pos': 0,
'u-boot-tpl-dtb:size': 513,
'u-boot-spl-dtb:size': 513,
'u-boot-spl-dtb:offset': 493,
'image-pos': 0,
'section/u-boot-dtb:image-pos': 0,
'u-boot-spl-dtb:image-pos': 493,
'section/u-boot-dtb:size': 493,
'u-boot-tpl-dtb:image-pos': 1006,
'section/u-boot-dtb:offset': 0,
'section:size': 493,
'offset': 0,
'section:offset': 0,
'u-boot-tpl-dtb:offset': 1006,
'size': 1519
}
# We expect three device-tree files in the output, one after the other.
# Read them in sequence. We look for an 'spl' property in the SPL tree,
# and 'tpl' in the TPL tree, to make sure they are distinct from the
# main U-Boot tree. All three should have the same postions and offset.
start = 0
for item in ['', 'spl', 'tpl']:
dtb = fdt.Fdt.FromData(data[start:])
dtb.Scan()
props = self._GetPropTree(dtb, ['offset', 'size', 'image-pos',
'spl', 'tpl'])
expected = dict(base_expected)
if item:
expected[item] = 0
self.assertEqual(expected, props)
start += dtb._fdt_obj.totalsize()
def testUpdateFdtOutput(self):
"""Test that output DTB files are updated"""
try:
data, dtb_data, _, _ = self._DoReadFileDtb('82_fdt_update_all.dts',
use_real_dtb=True, update_dtb=True, reset_dtbs=False)
# Unfortunately, compiling a source file always results in a file
# called source.dtb (see fdt_util.EnsureCompiled()). The test
# source file (e.g. test/75_fdt_update_all.dts) thus does not enter
# binman as a file called u-boot.dtb. To fix this, copy the file
# over to the expected place.
#tools.WriteFile(os.path.join(self._indir, 'u-boot.dtb'),
#tools.ReadFile(tools.GetOutputFilename('source.dtb')))
start = 0
for fname in ['u-boot.dtb.out', 'spl/u-boot-spl.dtb.out',
'tpl/u-boot-tpl.dtb.out']:
dtb = fdt.Fdt.FromData(data[start:])
size = dtb._fdt_obj.totalsize()
pathname = tools.GetOutputFilename(os.path.split(fname)[1])
outdata = tools.ReadFile(pathname)
name = os.path.split(fname)[0]
if name:
orig_indata = self._GetDtbContentsForSplTpl(dtb_data, name)
else:
orig_indata = dtb_data
self.assertNotEqual(outdata, orig_indata,
"Expected output file '%s' be updated" % pathname)
self.assertEqual(outdata, data[start:start + size],
"Expected output file '%s' to match output image" %
pathname)
start += size
finally:
self._ResetDtbs()
def _decompress(self, data):
out = os.path.join(self._indir, 'lz4.tmp')
with open(out, 'wb') as fd:
fd.write(data)
return tools.Run('lz4', '-dc', out)
'''
try:
orig = lz4.frame.decompress(data)
except AttributeError:
orig = lz4.decompress(data)
'''
def testCompress(self):
"""Test compression of blobs"""
data, _, _, out_dtb_fname = self._DoReadFileDtb('83_compress.dts',
use_real_dtb=True, update_dtb=True)
dtb = fdt.Fdt(out_dtb_fname)
dtb.Scan()
props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
orig = self._decompress(data)
self.assertEquals(COMPRESS_DATA, orig)
expected = {
'blob:uncomp-size': len(COMPRESS_DATA),
'blob:size': len(data),
'size': len(data),
}
self.assertEqual(expected, props)
def testFiles(self):
"""Test bringing in multiple files"""
data = self._DoReadFile('84_files.dts')
self.assertEqual(FILES_DATA, data)
def testFilesCompress(self):
"""Test bringing in multiple files and compressing them"""
data = self._DoReadFile('85_files_compress.dts')
image = control.images['image']
entries = image.GetEntries()
files = entries['files']
entries = files._section._entries
orig = ''
for i in range(1, 3):
key = '%d.dat' % i
start = entries[key].image_pos
len = entries[key].size
chunk = data[start:start + len]
orig += self._decompress(chunk)
self.assertEqual(FILES_DATA, orig)
def testFilesMissing(self):
"""Test missing files"""
with self.assertRaises(ValueError) as e:
data = self._DoReadFile('86_files_none.dts')
self.assertIn("Node '/binman/files': Pattern \'files/*.none\' matched "
'no files', str(e.exception))
def testFilesNoPattern(self):
"""Test missing files"""
with self.assertRaises(ValueError) as e:
data = self._DoReadFile('87_files_no_pattern.dts')
self.assertIn("Node '/binman/files': Missing 'pattern' property",
str(e.exception))
def testExpandSize(self):
"""Test an expanding entry"""
data, _, map_data, _ = self._DoReadFileDtb('88_expand_size.dts',
map=True)
expect = ('a' * 8 + U_BOOT_DATA +
MRC_DATA + 'b' * 1 + U_BOOT_DATA +
'c' * 8 + U_BOOT_DATA +
'd' * 8)
self.assertEqual(expect, data)
self.assertEqual('''ImagePos Offset Size Name
00000000 00000000 00000028 main-section
00000000 00000000 00000008 fill
00000008 00000008 00000004 u-boot
0000000c 0000000c 00000004 section
0000000c 00000000 00000003 intel-mrc
00000010 00000010 00000004 u-boot2
00000014 00000014 0000000c section2
00000014 00000000 00000008 fill
0000001c 00000008 00000004 u-boot
00000020 00000020 00000008 fill2
''', map_data)
def testExpandSizeBad(self):
"""Test an expanding entry which fails to provide contents"""
with test_util.capture_sys_output() as (stdout, stderr):
with self.assertRaises(ValueError) as e:
self._DoReadFileDtb('89_expand_size_bad.dts', map=True)
self.assertIn("Node '/binman/_testing': Cannot obtain contents when "
'expanding entry', str(e.exception))
def testHash(self):
"""Test hashing of the contents of an entry"""
_, _, _, out_dtb_fname = self._DoReadFileDtb('90_hash.dts',
use_real_dtb=True, update_dtb=True)
dtb = fdt.Fdt(out_dtb_fname)
dtb.Scan()
hash_node = dtb.GetNode('/binman/u-boot/hash').props['value']
m = hashlib.sha256()
m.update(U_BOOT_DATA)
self.assertEqual(m.digest(), ''.join(hash_node.value))
def testHashNoAlgo(self):
with self.assertRaises(ValueError) as e:
self._DoReadFileDtb('91_hash_no_algo.dts', update_dtb=True)
self.assertIn("Node \'/binman/u-boot\': Missing \'algo\' property for "
'hash node', str(e.exception))
def testHashBadAlgo(self):
with self.assertRaises(ValueError) as e:
self._DoReadFileDtb('92_hash_bad_algo.dts', update_dtb=True)
self.assertIn("Node '/binman/u-boot': Unknown hash algorithm",
str(e.exception))
def testHashSection(self):
"""Test hashing of the contents of an entry"""
_, _, _, out_dtb_fname = self._DoReadFileDtb('99_hash_section.dts',
use_real_dtb=True, update_dtb=True)
dtb = fdt.Fdt(out_dtb_fname)
dtb.Scan()
hash_node = dtb.GetNode('/binman/section/hash').props['value']
m = hashlib.sha256()
m.update(U_BOOT_DATA)
m.update(16 * 'a')
self.assertEqual(m.digest(), ''.join(hash_node.value))
def testPackUBootTplMicrocode(self):
"""Test that x86 microcode can be handled correctly in TPL
We expect to see the following in the image, in order:
u-boot-tpl-nodtb.bin with a microcode pointer inserted at the correct
place
u-boot-tpl.dtb with the microcode removed
the microcode
"""
with open(self.TestFile('u_boot_ucode_ptr')) as fd:
TestFunctional._MakeInputFile('tpl/u-boot-tpl', fd.read())
first, pos_and_size = self._RunMicrocodeTest('93_x86_tpl_ucode.dts',
U_BOOT_TPL_NODTB_DATA)
self.assertEqual('tplnodtb with microc' + pos_and_size +
'ter somewhere in here', first)
def testFmapX86(self):
"""Basic test of generation of a flashrom fmap"""
data = self._DoReadFile('94_fmap_x86.dts')
fhdr, fentries = fmap_util.DecodeFmap(data[32:])
expected = U_BOOT_DATA + MRC_DATA + 'a' * (32 - 7)
self.assertEqual(expected, data[:32])
fhdr, fentries = fmap_util.DecodeFmap(data[32:])
self.assertEqual(0x100, fhdr.image_size)
self.assertEqual(0, fentries[0].offset)
self.assertEqual(4, fentries[0].size)
self.assertEqual('U_BOOT', fentries[0].name)
self.assertEqual(4, fentries[1].offset)
self.assertEqual(3, fentries[1].size)
self.assertEqual('INTEL_MRC', fentries[1].name)
self.assertEqual(32, fentries[2].offset)
self.assertEqual(fmap_util.FMAP_HEADER_LEN +
fmap_util.FMAP_AREA_LEN * 3, fentries[2].size)
self.assertEqual('FMAP', fentries[2].name)
def testFmapX86Section(self):
"""Basic test of generation of a flashrom fmap"""
data = self._DoReadFile('95_fmap_x86_section.dts')
expected = U_BOOT_DATA + MRC_DATA + 'b' * (32 - 7)
self.assertEqual(expected, data[:32])
fhdr, fentries = fmap_util.DecodeFmap(data[36:])
self.assertEqual(0x100, fhdr.image_size)
self.assertEqual(0, fentries[0].offset)
self.assertEqual(4, fentries[0].size)
self.assertEqual('U_BOOT', fentries[0].name)
self.assertEqual(4, fentries[1].offset)
self.assertEqual(3, fentries[1].size)
self.assertEqual('INTEL_MRC', fentries[1].name)
self.assertEqual(36, fentries[2].offset)
self.assertEqual(fmap_util.FMAP_HEADER_LEN +
fmap_util.FMAP_AREA_LEN * 3, fentries[2].size)
self.assertEqual('FMAP', fentries[2].name)
def testElf(self):
"""Basic test of ELF entries"""
with open(self.TestFile('bss_data')) as fd:
TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
with open(self.TestFile('bss_data')) as fd:
TestFunctional._MakeInputFile('-boot', fd.read())
data = self._DoReadFile('96_elf.dts')
def testElfStripg(self):
"""Basic test of ELF entries"""
with open(self.TestFile('bss_data')) as fd:
TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
with open(self.TestFile('bss_data')) as fd:
TestFunctional._MakeInputFile('-boot', fd.read())
data = self._DoReadFile('97_elf_strip.dts')
def testPackOverlapMap(self):
"""Test that overlapping regions are detected"""
with test_util.capture_sys_output() as (stdout, stderr):
with self.assertRaises(ValueError) as e:
self._DoTestFile('14_pack_overlap.dts', map=True)
map_fname = tools.GetOutputFilename('image.map')
self.assertEqual("Wrote map file '%s' to show errors\n" % map_fname,
stdout.getvalue())
# We should not get an inmage, but there should be a map file
self.assertFalse(os.path.exists(tools.GetOutputFilename('image.bin')))
self.assertTrue(os.path.exists(map_fname))
map_data = tools.ReadFile(map_fname)
self.assertEqual('''ImagePos Offset Size Name
<none> 00000000 00000007 main-section
<none> 00000000 00000004 u-boot
<none> 00000003 00000004 u-boot-align
''', map_data)
if __name__ == "__main__":
unittest.main()

@ -42,7 +42,8 @@ class Image:
self._size = None
self._filename = '%s.bin' % self._name
if test:
self._section = bsection.Section('main-section', self._node, True)
self._section = bsection.Section('main-section', None, self._node,
self, True)
else:
self._ReadNode()
@ -52,7 +53,20 @@ class Image:
filename = fdt_util.GetString(self._node, 'filename')
if filename:
self._filename = filename
self._section = bsection.Section('main-section', self._node)
self._section = bsection.Section('main-section', None, self._node, self)
def GetFdtSet(self):
"""Get the set of device tree files used by this image"""
return self._section.GetFdtSet()
def ExpandEntries(self):
"""Expand out any entries which have calculated sub-entries
Some entries are expanded out at runtime, e.g. 'files', which produces
a section containing a list of files. Process these entries so that
this information is added to the device tree.
"""
self._section.ExpandEntries()
def AddMissingProperties(self):
"""Add properties that are not present in the device tree
@ -66,6 +80,11 @@ class Image:
self._section.AddMissingProperties()
def ProcessFdt(self, fdt):
"""Allow entries to adjust the device tree
Some entries need to adjust the device tree for their purposes. This
may involve adding or deleting properties.
"""
return self._section.ProcessFdt(fdt)
def GetEntryContents(self):
@ -120,10 +139,15 @@ class Image:
return self._section.GetEntries()
def WriteMap(self):
"""Write a map of the image to a .map file"""
"""Write a map of the image to a .map file
Returns:
Filename of map file written
"""
filename = '%s.map' % self._name
fname = tools.GetOutputFilename(filename)
with open(fname, 'w') as fd:
print('%8s %8s %8s %s' % ('ImagePos', 'Offset', 'Size', 'Name'),
file=fd)
self._section.WriteMap(fd, 0)
return fname

@ -0,0 +1,253 @@
# SPDX-License-Identifier: GPL-2.0+
# Copyright 2018 Google, Inc
# Written by Simon Glass <sjg@chromium.org>
#
# Holds and modifies the state information held by binman
#
import hashlib
import re
from sets import Set
import os
import tools
# Records the device-tree files known to binman, keyed by filename (e.g.
# 'u-boot-spl.dtb')
fdt_files = {}
# Arguments passed to binman to provide arguments to entries
entry_args = {}
# True to use fake device-tree files for testing (see U_BOOT_DTB_DATA in
# ftest.py)
use_fake_dtb = False
# Set of all device tree files references by images
fdt_set = Set()
# Same as above, but excluding the main one
fdt_subset = Set()
# The DTB which contains the full image information
main_dtb = 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):
"""Get the full pathname of a particular Fdt object
Similar to GetFdt() but returns the pathname associated with the Fdt.
Args:
fname: Filename to look up (e.g. 'u-boot.dtb').
Returns:
Full path name to the associated Fdt
"""
return fdt_files[fname]._fname
def GetFdtContents(fname):
"""Looks up the FDT pathname and contents
This is used to obtain the Fdt pathname and contents when needed by an
entry. It supports a 'fake' dtb, allowing tests to substitute test data for
the real dtb.
Args:
fname: Filename to look up (e.g. 'u-boot.dtb').
Returns:
tuple:
pathname to Fdt
Fdt data (as bytes)
"""
if fname in fdt_files and not use_fake_dtb:
pathname = GetFdtPath(fname)
data = GetFdt(fname).GetContents()
else:
pathname = tools.GetInputFilename(fname)
data = tools.ReadFile(pathname)
return pathname, data
def SetEntryArgs(args):
"""Set the value of the entry args
This sets up the entry_args dict which is used to supply entry arguments to
entries.
Args:
args: List of entry arguments, each in the format "name=value"
"""
global entry_args
entry_args = {}
if args:
for arg in args:
m = re.match('([^=]*)=(.*)', arg)
if not m:
raise ValueError("Invalid entry arguemnt '%s'" % arg)
entry_args[m.group(1)] = m.group(2)
def GetEntryArg(name):
"""Get the value of an entry argument
Args:
name: Name of argument to retrieve
Returns:
String value of argument
"""
return entry_args.get(name)
def Prepare(images, dtb):
"""Get device tree files ready for use
This sets up a set of device tree files that can be retrieved by GetFdts().
At present there is only one, that for U-Boot proper.
Args:
images: List of images being used
dtb: Main dtb
"""
global fdt_set, fdt_subset, fdt_files, main_dtb
# Import these here in case libfdt.py is not available, in which case
# the above help option still works.
import fdt
import fdt_util
# If we are updating the DTBs we need to put these updated versions
# where Entry_blob_dtb can find them. We can ignore 'u-boot.dtb'
# since it is assumed to be the one passed in with options.dt, and
# was handled just above.
main_dtb = dtb
fdt_files.clear()
fdt_files['u-boot.dtb'] = dtb
fdt_subset = Set()
if not use_fake_dtb:
for image in images.values():
fdt_subset.update(image.GetFdtSet())
fdt_subset.discard('u-boot.dtb')
for other_fname in fdt_subset:
infile = tools.GetInputFilename(other_fname)
other_fname_dtb = fdt_util.EnsureCompiled(infile)
out_fname = tools.GetOutputFilename('%s.out' %
os.path.split(other_fname)[1])
tools.WriteFile(out_fname, tools.ReadFile(other_fname_dtb))
other_dtb = fdt.FdtScan(out_fname)
fdt_files[other_fname] = other_dtb
def GetFdts():
"""Yield all device tree files being used by binman
Yields:
Device trees being used (U-Boot proper, SPL, TPL)
"""
yield main_dtb
for other_fname in fdt_subset:
yield fdt_files[other_fname]
def GetUpdateNodes(node):
"""Yield all the nodes that need to be updated in all device trees
The property referenced by this node is added to any device trees which
have the given node. Due to removable of unwanted notes, SPL and TPL may
not have this node.
Args:
node: Node object in the main device tree to look up
Yields:
Node objects in each device tree that is in use (U-Boot proper, which
is node, SPL and TPL)
"""
yield node
for dtb in fdt_files.values():
if dtb != node.GetFdt():
other_node = dtb.GetNode(node.path)
if other_node:
yield other_node
def AddZeroProp(node, prop):
"""Add a new property to affected device trees with an integer value of 0.
Args:
prop_name: Name of property
"""
for n in GetUpdateNodes(node):
n.AddZeroProp(prop)
def AddSubnode(node, name):
"""Add a new subnode to a node in affected device trees
Args:
node: Node to add to
name: name of node to add
Returns:
New subnode that was created in main tree
"""
first = None
for n in GetUpdateNodes(node):
subnode = n.AddSubnode(name)
if not first:
first = subnode
return first
def AddString(node, prop, value):
"""Add a new string property to affected device trees
Args:
prop_name: Name of property
value: String value (which will be \0-terminated in the DT)
"""
for n in GetUpdateNodes(node):
n.AddString(prop, value)
def SetInt(node, prop, value):
"""Update an integer property in affected device trees with an integer value
This is not allowed to change the size of the FDT.
Args:
prop_name: Name of property
"""
for n in GetUpdateNodes(node):
n.SetInt(prop, value)
def CheckAddHashProp(node):
hash_node = node.FindNode('hash')
if hash_node:
algo = hash_node.props.get('algo')
if not algo:
return "Missing 'algo' property for hash node"
if algo.value == 'sha256':
size = 32
else:
return "Unknown hash algorithm '%s'" % algo
for n in GetUpdateNodes(hash_node):
n.AddEmptyProp('value', size)
def CheckSetHashValue(node, get_data_func):
hash_node = node.FindNode('hash')
if hash_node:
algo = hash_node.props.get('algo').value
if algo == 'sha256':
m = hashlib.sha256()
m.update(get_data_func())
data = m.digest()
for n in GetUpdateNodes(hash_node):
n.SetData('value', data)

@ -0,0 +1,15 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
size = <16>;
fill {
size = <0>;
fill-byte = [ff];
};
};
};

@ -0,0 +1,14 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
size = <16>;
x86-start16-tpl {
};
};
};

@ -0,0 +1,18 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
section {
u-boot-dtb {
};
};
u-boot-spl-dtb {
};
u-boot-tpl-dtb {
};
};
};

@ -0,0 +1,11 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
binman {
blob {
filename = "compress";
compress = "lz4";
};
};
};

@ -0,0 +1,11 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
binman {
files {
pattern = "files/*.dat";
compress = "none";
};
};
};

@ -0,0 +1,11 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
binman {
files {
pattern = "files/*.dat";
compress = "lz4";
};
};
};

@ -0,0 +1,12 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
binman {
files {
pattern = "files/*.none";
compress = "none";
require-matches;
};
};
};

@ -0,0 +1,11 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
binman {
files {
compress = "none";
require-matches;
};
};
};

@ -0,0 +1,43 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
binman {
size = <40>;
fill {
expand-size;
fill-byte = [61];
size = <0>;
};
u-boot {
offset = <8>;
};
section {
expand-size;
pad-byte = <0x62>;
intel-mrc {
};
};
u-boot2 {
type = "u-boot";
offset = <16>;
};
section2 {
type = "section";
fill {
expand-size;
fill-byte = [63];
size = <0>;
};
u-boot {
offset = <8>;
};
};
fill2 {
type = "fill";
expand-size;
fill-byte = [64];
size = <0>;
};
};
};

@ -0,0 +1,14 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
binman {
_testing {
expand-size;
return-contents-once;
};
u-boot {
offset = <8>;
};
};
};

@ -0,0 +1,12 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
binman {
u-boot {
hash {
algo = "sha256";
};
};
};
};

@ -0,0 +1,11 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
binman {
u-boot {
hash {
};
};
};
};

@ -0,0 +1,12 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
binman {
u-boot {
hash {
algo = "invalid";
};
};
};
};

@ -0,0 +1,29 @@
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
sort-by-offset;
end-at-4gb;
size = <0x200>;
u-boot-tpl-with-ucode-ptr {
};
u-boot-tpl-dtb-with-ucode {
};
u-boot-ucode {
};
};
microcode {
update@0 {
data = <0x12345678 0x12345679>;
};
update@1 {
data = <0xabcd0000 0x78235609>;
};
};
};

@ -0,0 +1,20 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
end-at-4gb;
size = <0x100>;
pad-byte = <0x61>;
u-boot {
};
intel-mrc {
};
fmap {
offset = <0xffffff20>;
};
};
};

@ -0,0 +1,22 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
end-at-4gb;
size = <0x100>;
u-boot {
};
section {
pad-byte = <0x62>;
intel-mrc {
};
fmap {
offset = <0x20>;
};
};
};
};

@ -0,0 +1,14 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
u-boot-elf {
};
u-boot-spl-elf {
};
};
};

@ -0,0 +1,15 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
u-boot-elf {
strip;
};
u-boot-spl-elf {
};
};
};

@ -0,0 +1,18 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
binman {
section {
u-boot {
};
fill {
size = <0x10>;
fill-byte = [61];
};
hash {
algo = "sha256";
};
};
};
};

@ -0,0 +1 @@
Oh, don't bother apologising, I'm sorry you're alive

@ -0,0 +1 @@
this does not have a .dat extenion

@ -408,7 +408,7 @@ class Builder:
"""
cmd = [self.gnu_make] + list(args)
result = command.RunPipe([cmd], capture=True, capture_stderr=True,
cwd=cwd, raise_on_error=False, **kwargs)
cwd=cwd, raise_on_error=False, infile='/dev/null', **kwargs)
if self.verbose_build:
result.stdout = '%s\n' % (' '.join(cmd)) + result.stdout
result.combined = '%s\n' % (' '.join(cmd)) + result.combined

@ -43,6 +43,7 @@ class Prop:
self.name = name
self.value = None
self.bytes = str(bytes)
self.dirty = False
if not bytes:
self.type = TYPE_BOOL
self.value = True
@ -145,7 +146,7 @@ class Prop:
if type == TYPE_BYTE:
return chr(0)
elif type == TYPE_INT:
return struct.pack('<I', 0);
return struct.pack('>I', 0);
elif type == TYPE_STRING:
return ''
else:
@ -160,6 +161,55 @@ class Prop:
self._node._fdt.CheckCache()
return self._node._fdt.GetStructOffset(self._offset)
def SetInt(self, val):
"""Set the integer value of the property
The device tree is marked dirty so that the value will be written to
the block on the next sync.
Args:
val: Integer value (32-bit, single cell)
"""
self.bytes = struct.pack('>I', val);
self.value = val
self.type = TYPE_INT
self.dirty = True
def SetData(self, bytes):
"""Set the value of a property as bytes
Args:
bytes: New property value to set
"""
self.bytes = str(bytes)
self.type, self.value = self.BytesToValue(bytes)
self.dirty = True
def Sync(self, auto_resize=False):
"""Sync property changes back to the device tree
This updates the device tree blob with any changes to this property
since the last sync.
Args:
auto_resize: Resize the device tree automatically if it does not
have enough space for the update
Raises:
FdtException if auto_resize is False and there is not enough space
"""
if self._offset is None or self.dirty:
node = self._node
fdt_obj = node._fdt._fdt_obj
if auto_resize:
while fdt_obj.setprop(node.Offset(), self.name, self.bytes,
(libfdt.NOSPACE,)) == -libfdt.NOSPACE:
fdt_obj.resize(fdt_obj.totalsize() + 1024)
fdt_obj.setprop(node.Offset(), self.name, self.bytes)
else:
fdt_obj.setprop(node.Offset(), self.name, self.bytes)
class Node:
"""A device tree node
@ -284,25 +334,125 @@ class Node:
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.resize(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()
self.props[prop_name] = Prop(self, None, prop_name, '\0' * 4)
def AddEmptyProp(self, prop_name, len):
"""Add a property with a fixed data size, for filling in later
The device tree is marked dirty so that the value will be written to
the blob on the next sync.
Args:
prop_name: Name of property
len: Length of data in property
"""
value = chr(0) * len
self.props[prop_name] = Prop(self, None, prop_name, value)
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.
The device tree is marked dirty so that the value will be written to
the blob on the next sync.
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)
self.props[prop_name].SetInt(val)
def SetData(self, prop_name, val):
"""Set the data value of a property
The device tree is marked dirty so that the value will be written to
the blob on the next sync.
Args:
prop_name: Name of property to set
val: Data value to set
"""
self.props[prop_name].SetData(val)
def SetString(self, prop_name, val):
"""Set the string value of a property
The device tree is marked dirty so that the value will be written to
the blob on the next sync.
Args:
prop_name: Name of property to set
val: String value to set (will be \0-terminated in DT)
"""
self.props[prop_name].SetData(val + chr(0))
def AddString(self, prop_name, val):
"""Add a new string property to a node
The device tree is marked dirty so that the value will be written to
the blob on the next sync.
Args:
prop_name: Name of property to add
val: String value of property
"""
self.props[prop_name] = Prop(self, None, prop_name, val + chr(0))
def AddSubnode(self, name):
"""Add a new subnode to the node
Args:
name: name of node to add
Returns:
New subnode that was created
"""
path = self.path + '/' + name
subnode = Node(self._fdt, self, None, name, path)
self.subnodes.append(subnode)
return subnode
def Sync(self, auto_resize=False):
"""Sync node changes back to the device tree
This updates the device tree blob with any changes to this node and its
subnodes since the last sync.
Args:
auto_resize: Resize the device tree automatically if it does not
have enough space for the update
Raises:
FdtException if auto_resize is False and there is not enough space
"""
if self._offset is None:
# The subnode doesn't exist yet, so add it
fdt_obj = self._fdt._fdt_obj
if auto_resize:
while True:
offset = fdt_obj.add_subnode(self.parent._offset, self.name,
(libfdt.NOSPACE,))
if offset != -libfdt.NOSPACE:
break
fdt_obj.resize(fdt_obj.totalsize() + 1024)
else:
offset = fdt_obj.add_subnode(self.parent._offset, self.name)
self._offset = offset
# Sync subnodes in reverse so that we don't disturb node offsets for
# nodes that are earlier in the DT. This avoids an O(n^2) rescan of
# node offsets.
for node in reversed(self.subnodes):
node.Sync(auto_resize)
# Sync properties now, whose offsets should not have been disturbed.
# We do this after subnodes, since this disturbs the offsets of these
# properties.
prop_list = sorted(self.props.values(), key=lambda prop: prop._offset,
reverse=True)
for prop in prop_list:
prop.Sync(auto_resize)
class Fdt:
@ -322,6 +472,20 @@ class Fdt:
with open(self._fname) as fd:
self._fdt_obj = libfdt.Fdt(fd.read())
@staticmethod
def FromData(data):
"""Create a new Fdt object from the given data
Args:
data: Device-tree data blob
Returns:
Fdt object containing the data
"""
fdt = Fdt(None)
fdt._fdt_obj = libfdt.Fdt(bytearray(data))
return fdt
def LookupPhandle(self, phandle):
"""Look up a phandle
@ -381,6 +545,19 @@ class Fdt:
with open(self._fname, 'wb') as fd:
fd.write(self._fdt_obj.as_bytearray())
def Sync(self, auto_resize=False):
"""Make sure any DT changes are written to the blob
Args:
auto_resize: Resize the device tree automatically if it does not
have enough space for the update
Raises:
FdtException if auto_resize is False and there is not enough space
"""
self._root.Sync(auto_resize)
self.Invalidate()
def Pack(self):
"""Pack the device tree down to its minimum size

@ -337,6 +337,7 @@ class TestProp(unittest.TestCase):
self.node.AddZeroProp('one')
self.node.AddZeroProp('two')
self.node.AddZeroProp('three')
self.dtb.Sync(auto_resize=True)
# Updating existing properties should be OK, since the device-tree size
# does not change
@ -344,11 +345,75 @@ class TestProp(unittest.TestCase):
self.node.SetInt('one', 1)
self.node.SetInt('two', 2)
self.node.SetInt('three', 3)
self.dtb.Sync(auto_resize=False)
# This should fail since it would need to increase the device-tree size
self.node.AddZeroProp('four')
with self.assertRaises(libfdt.FdtException) as e:
self.node.SetInt('four', 4)
self.dtb.Sync(auto_resize=False)
self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
self.dtb.Sync(auto_resize=True)
def testAddNode(self):
self.fdt.pack()
self.node.AddSubnode('subnode')
with self.assertRaises(libfdt.FdtException) as e:
self.dtb.Sync(auto_resize=False)
self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
self.dtb.Sync(auto_resize=True)
offset = self.fdt.path_offset('/spl-test/subnode')
self.assertTrue(offset > 0)
def testAddMore(self):
"""Test various other methods for adding and setting properties"""
self.node.AddZeroProp('one')
self.dtb.Sync(auto_resize=True)
data = self.fdt.getprop(self.node.Offset(), 'one')
self.assertEqual(0, fdt32_to_cpu(data))
self.node.SetInt('one', 1)
self.dtb.Sync(auto_resize=False)
data = self.fdt.getprop(self.node.Offset(), 'one')
self.assertEqual(1, fdt32_to_cpu(data))
val = '123' + chr(0) + '456'
self.node.AddString('string', val)
self.dtb.Sync(auto_resize=True)
data = self.fdt.getprop(self.node.Offset(), 'string')
self.assertEqual(val + '\0', data)
self.fdt.pack()
self.node.SetString('string', val + 'x')
with self.assertRaises(libfdt.FdtException) as e:
self.dtb.Sync(auto_resize=False)
self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
self.node.SetString('string', val[:-1])
prop = self.node.props['string']
prop.SetData(val)
self.dtb.Sync(auto_resize=False)
data = self.fdt.getprop(self.node.Offset(), 'string')
self.assertEqual(val, data)
self.node.AddEmptyProp('empty', 5)
self.dtb.Sync(auto_resize=True)
prop = self.node.props['empty']
prop.SetData(val)
self.dtb.Sync(auto_resize=False)
data = self.fdt.getprop(self.node.Offset(), 'empty')
self.assertEqual(val, data)
self.node.SetData('empty', '123')
self.assertEqual('123', prop.bytes)
def testFromData(self):
dtb2 = fdt.Fdt.FromData(self.dtb.GetContents())
self.assertEqual(dtb2.GetContents(), self.dtb.GetContents())
self.node.AddEmptyProp('empty', 5)
self.dtb.Sync(auto_resize=True)
self.assertTrue(dtb2.GetContents() != self.dtb.GetContents())
class TestFdtUtil(unittest.TestCase):

@ -4,6 +4,7 @@
#
import command
import glob
import os
import shutil
import tempfile
@ -22,6 +23,10 @@ chroot_path = None
# Search paths to use for Filename(), used to find files
search_paths = []
# Tools and the packages that contain them, on debian
packages = {
'lz4': 'liblz4-tool',
}
def PrepareOutputDir(dirname, preserve=False):
"""Select an output directory, ensuring it exists.
@ -119,6 +124,23 @@ def GetInputFilename(fname):
raise ValueError("Filename '%s' not found in input path (%s) (cwd='%s')" %
(fname, ','.join(indir), os.getcwd()))
def GetInputFilenameGlob(pattern):
"""Return a list of filenames for use as input.
Args:
pattern: Filename pattern to search for
Returns:
A list of matching files in all input directories
"""
if not indir:
return glob.glob(fname)
files = []
for dirname in indir:
pathname = os.path.join(dirname, pattern)
files += glob.glob(pathname)
return sorted(files)
def Align(pos, align):
if align:
mask = align - 1
@ -128,8 +150,31 @@ def Align(pos, align):
def NotPowerOfTwo(num):
return num and (num & (num - 1))
def PathHasFile(fname):
"""Check if a given filename is in the PATH
Args:
fname: Filename to check
Returns:
True if found, False if not
"""
for dir in os.environ['PATH'].split(':'):
if os.path.exists(os.path.join(dir, fname)):
return True
return False
def Run(name, *args):
command.Run(name, *args, cwd=outdir)
try:
return command.Run(name, *args, cwd=outdir, capture=True)
except:
if not PathHasFile(name):
msg = "Plesae install tool '%s'" % name
package = packages.get(name)
if package:
msg += " (e.g. from package '%s')" % package
raise ValueError(msg)
raise
def Filename(fname):
"""Resolve a file path to an absolute path.

Loading…
Cancel
Save