of-platdata and dtoc improvements

sandbox SPL tests
 binman support for compressed sections
 -----BEGIN PGP SIGNATURE-----
 
 iQFFBAABCgAvFiEEslwAIq+Gp8wWVbYnfxc6PpAIreYFAl+cXssRHHNqZ0BjaHJv
 bWl1bS5vcmcACgkQfxc6PpAIreaYuAgAjG05oxt8a4DXhdzGuXBCbehZv2T7X5Lg
 2+i9uwyg3MkKWp0Spm2J+0flWs+1Bynw335hgNVgq+bh29sajOQz2BtgfDKFY+Aw
 D+D16YBEiEuIbIXLzSsv+ct1va83A3JmlxxoaJ7+ZvoN/5Z3ZvJIZ7F4AzmwlAFT
 5pOFgDEfVM4MYFU4R2wZAaJMnYKQrqR5Tvrxecc6OkhvWIgq7j3elE4xOuh2hL/L
 Bz/KM+4Eq+EUALG+quyEzd+gDKoPY2rNOojxS/lg7PRsN6S8engwE+LkcAkMaNG3
 uDVrTYsA8lCJJwso0in25fUy8sEYWaCSVr/82xfRJMWksPajubem7w==
 =9u1O
 -----END PGP SIGNATURE-----

Merge tag 'dm-pull-30oct20' of https://gitlab.denx.de/u-boot/custodians/u-boot-dm

of-platdata and dtoc improvements
sandbox SPL tests
binman support for compressed sections
This commit is contained in:
Tom Rini 2020-10-30 15:24:30 -04:00
commit 63d4607e03
89 changed files with 1861 additions and 502 deletions

View File

@ -182,7 +182,7 @@ jobs:
OVERRIDE: "-O clang-10"
sandbox_spl:
TEST_PY_BD: "sandbox_spl"
TEST_PY_TEST_SPEC: "test_ofplatdata or test_handoff"
TEST_PY_TEST_SPEC: "test_ofplatdata or test_handoff or test_spl"
sandbox_flattree:
TEST_PY_BD: "sandbox_flattree"
evb_ast2500:

View File

@ -198,7 +198,7 @@ sandbox_spl test.py:
tags: [ 'all' ]
variables:
TEST_PY_BD: "sandbox_spl"
TEST_PY_TEST_SPEC: "test_ofplatdata or test_handoff"
TEST_PY_TEST_SPEC: "test_ofplatdata or test_handoff or test_spl"
<<: *buildman_and_testpy_dfn
evb-ast2500 test.py:

View File

@ -512,7 +512,7 @@ matrix:
- name: "test/py sandbox_spl"
env:
- TEST_PY_BD="sandbox_spl"
TEST_PY_TEST_SPEC="test_ofplatdata or test_handoff"
TEST_PY_TEST_SPEC="test_ofplatdata or test_handoff or test_spl"
TOOLCHAIN="i386"
TEST_PY_TOOLS="yes"
- name: "test/py sandbox_flattree"

View File

@ -807,7 +807,7 @@ libs-$(CONFIG_API) += api/
ifdef CONFIG_POST
libs-y += post/
endif
libs-$(CONFIG_UNIT_TEST) += test/ test/dm/
libs-$(CONFIG_UNIT_TEST) += test/
libs-$(CONFIG_UT_ENV) += test/env/
libs-$(CONFIG_UT_OPTEE) += test/optee/
libs-$(CONFIG_UT_OVERLAY) += test/overlay/

View File

@ -34,7 +34,7 @@ struct scu_regs {
u32 fpga_rev;
};
#if defined(CONFIG_IMX_THERMAL)
#if !defined(CONFIG_SPL_BUILD) && defined(CONFIG_IMX_THERMAL)
static const struct imx_thermal_plat imx6_thermal_plat = {
.regs = (void *)ANATOP_BASE_ADDR,
.fuse_bank = 1,

View File

@ -127,7 +127,8 @@ int sandbox_sdl_init_display(int width, int height, int log2_bpp,
sdl.pitch = sdl.width * sdl.depth / 8;
SDL_Window *screen = SDL_CreateWindow("U-Boot", SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
sdl.vis_width, sdl.vis_height, 0);
sdl.vis_width, sdl.vis_height,
SDL_WINDOW_RESIZABLE);
if (!screen) {
printf("Unable to initialise SDL screen: %s\n",
SDL_GetError());

View File

@ -12,6 +12,7 @@
#include <spl.h>
#include <asm/spl.h>
#include <asm/state.h>
#include <test/test.h>
DECLARE_GLOBAL_DATA_PTR;
@ -53,19 +54,14 @@ SPL_LOAD_IMAGE_METHOD("sandbox", 9, BOOT_DEVICE_BOARD, spl_board_load_image);
void spl_board_init(void)
{
struct sandbox_state *state = state_get_current();
struct udevice *dev;
preloader_console_init();
if (state->show_of_platdata) {
/*
* Scan all the devices so that we can output their platform
* data. See sandbox_spl_probe().
*/
printf("Scanning misc devices\n");
for (uclass_first_device(UCLASS_MISC, &dev);
dev;
uclass_next_device(&dev))
;
if (state->run_unittests) {
int ret;
ret = dm_test_main(state->select_unittests);
/* continue execution into U-Boot */
}
}

View File

@ -365,14 +365,23 @@ static int sandbox_cmdline_cb_log_level(struct sandbox_state *state,
SANDBOX_CMDLINE_OPT_SHORT(log_level, 'L', 1,
"Set log level (0=panic, 7=debug)");
static int sandbox_cmdline_cb_show_of_platdata(struct sandbox_state *state,
const char *arg)
static int sandbox_cmdline_cb_unittests(struct sandbox_state *state,
const char *arg)
{
state->show_of_platdata = true;
state->run_unittests = true;
return 0;
}
SANDBOX_CMDLINE_OPT(show_of_platdata, 0, "Show of-platdata in SPL");
SANDBOX_CMDLINE_OPT_SHORT(unittests, 'u', 0, "Run unit tests");
static int sandbox_cmdline_cb_select_unittests(struct sandbox_state *state,
const char *arg)
{
state->select_unittests = arg;
return 0;
}
SANDBOX_CMDLINE_OPT_SHORT(select_unittests, 'k', 1, "Select unit tests to run");
static void setup_ram_buf(struct sandbox_state *state)
{

View File

@ -69,6 +69,7 @@
clock-frequency = <400000>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c0>;
u-boot,dm-pre-reloc;
};
pcic: pci@0 {

View File

@ -29,6 +29,32 @@
};
};
clk_fixed: clk-fixed {
u-boot,dm-pre-reloc;
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <1234>;
};
clk_sandbox: clk-sbox {
u-boot,dm-pre-reloc;
compatible = "sandbox,clk";
#clock-cells = <1>;
assigned-clocks = <&clk_sandbox 3>;
assigned-clock-rates = <321>;
};
clk-test {
u-boot,dm-pre-reloc;
compatible = "sandbox,clk-test";
clocks = <&clk_fixed>,
<&clk_sandbox 1>,
<&clk_sandbox 0>,
<&clk_sandbox 3>,
<&clk_sandbox 2>;
clock-names = "fixed", "i2c", "spi", "uart2", "uart1";
};
gpio_a: gpios@0 {
u-boot,dm-pre-reloc;
gpio-controller;
@ -64,6 +90,7 @@
reg = <0x43>;
compatible = "sandbox-rtc";
sandbox,emul = <&emul0>;
u-boot,dm-pre-reloc;
};
sandbox_pmic: sandbox_pmic {
reg = <0x40>;

View File

@ -90,8 +90,9 @@ struct sandbox_state {
bool skip_delays; /* Ignore any time delays (for test) */
bool show_test_output; /* Don't suppress stdout in tests */
int default_log_level; /* Default log level for sandbox */
bool show_of_platdata; /* Show of-platdata in SPL */
bool ram_buf_read; /* true if we read the RAM buffer */
bool run_unittests; /* Run unit tests */
const char *select_unittests; /* Unit test to run */
/* Pointer to information for each SPI bus/cs */
struct sandbox_spi_info spi[CONFIG_SANDBOX_SPI_MAX_BUS]

View File

@ -85,6 +85,8 @@ config APL_SPI_FLASH_BOOT
bool "Support booting with SPI-flash driver instead memory-mapped SPI"
select TPL_SPI_FLASH_SUPPORT
select TPL_SPI_SUPPORT
select TPL_DM_SPI
select TPL_DM_SPI_FLASH
help
This enables SPI and SPI flash in TPL. Without the this only
available boot method is to use memory-mapped SPI. Since this is

View File

@ -90,7 +90,8 @@ static int apl_flash_probe(struct udevice *dev)
*/
static int apl_flash_bind(struct udevice *dev)
{
if (CONFIG_IS_ENABLED(OF_PLATDATA)) {
if (CONFIG_IS_ENABLED(OF_PLATDATA) &&
!CONFIG_IS_ENABLED(OF_PLATDATA_PARENT)) {
struct dm_spi_slave_platdata *plat;
struct udevice *spi;
int ret;

View File

@ -21,10 +21,12 @@
*/
gd_t *gd;
#if !CONFIG_IS_ENABLED(OF_PLATDATA)
/* Add a simple GPIO device */
U_BOOT_DEVICE(gpio_sandbox) = {
.name = "sandbox_gpio",
};
#endif
void flush_cache(unsigned long start, unsigned long size)
{

View File

@ -296,20 +296,21 @@ static int initr_noncached(void)
}
#endif
#ifdef CONFIG_OF_LIVE
static int initr_of_live(void)
{
int ret;
if (CONFIG_IS_ENABLED(OF_LIVE)) {
int ret;
bootstage_start(BOOTSTAGE_ID_ACCUM_OF_LIVE, "of_live");
ret = of_live_build(gd->fdt_blob, (struct device_node **)&gd->of_root);
bootstage_accum(BOOTSTAGE_ID_ACCUM_OF_LIVE);
if (ret)
return ret;
bootstage_start(BOOTSTAGE_ID_ACCUM_OF_LIVE, "of_live");
ret = of_live_build(gd->fdt_blob,
(struct device_node **)gd_of_root_ptr());
bootstage_accum(BOOTSTAGE_ID_ACCUM_OF_LIVE);
if (ret)
return ret;
}
return 0;
}
#endif
#ifdef CONFIG_DM
static int initr_dm(void)
@ -713,9 +714,7 @@ static init_fnc_t init_sequence_r[] = {
#ifdef CONFIG_SYS_NONCACHED_MEMORY
initr_noncached,
#endif
#ifdef CONFIG_OF_LIVE
initr_of_live,
#endif
#ifdef CONFIG_DM
initr_dm,
#endif

View File

@ -22,11 +22,12 @@ CONFIG_BOOTSTAGE_STASH=y
CONFIG_BOOTSTAGE_STASH_SIZE=0x4096
CONFIG_CONSOLE_RECORD=y
CONFIG_CONSOLE_RECORD_OUT_SIZE=0x1000
CONFIG_SILENT_CONSOLE=y
CONFIG_DISPLAY_BOARDINFO_LATE=y
CONFIG_HANDOFF=y
CONFIG_SPL_BOARD_INIT=y
CONFIG_SPL_ENV_SUPPORT=y
CONFIG_SPL_I2C_SUPPORT=y
CONFIG_SPL_RTC_SUPPORT=y
CONFIG_CMD_CPU=y
CONFIG_CMD_LICENSE=y
CONFIG_CMD_BOOTZ=y
@ -105,6 +106,7 @@ CONFIG_ADC_SANDBOX=y
CONFIG_AXI=y
CONFIG_AXI_SANDBOX=y
CONFIG_CLK=y
CONFIG_SPL_CLK=y
CONFIG_CPU=y
CONFIG_DM_DEMO=y
CONFIG_DM_DEMO_SIMPLE=y
@ -120,7 +122,6 @@ CONFIG_I2C_CROS_EC_LDO=y
CONFIG_DM_I2C_GPIO=y
CONFIG_SYS_I2C_SANDBOX=y
CONFIG_I2C_MUX=y
CONFIG_SPL_I2C_MUX=y
CONFIG_I2C_ARB_GPIO_CHALLENGE=y
CONFIG_CROS_EC_KEYB=y
CONFIG_I8042_KEYB=y
@ -187,6 +188,7 @@ CONFIG_REMOTEPROC_SANDBOX=y
CONFIG_DM_RESET=y
CONFIG_SANDBOX_RESET=y
CONFIG_DM_RTC=y
CONFIG_SPL_DM_RTC=y
CONFIG_SANDBOX_SERIAL=y
CONFIG_SOUND=y
CONFIG_SOUND_SANDBOX=y
@ -221,5 +223,6 @@ CONFIG_TPM=y
CONFIG_LZ4=y
CONFIG_ERRNO_STR=y
CONFIG_UNIT_TEST=y
CONFIG_SPL_UNIT_TEST=y
CONFIG_UT_TIME=y
CONFIG_UT_DM=y

View File

@ -66,12 +66,6 @@ strictly necessary. Notable problems include:
normally also supports device tree it must use #ifdef to separate
out this code, since the structures are only available in SPL.
- Correct relations between nodes are not implemented. This means that
parent/child relations (like bus device iteration) do not work yet.
Some phandles (those that are recognised as such) are converted into
a pointer to struct driver_info. This pointer can be used to access
the referenced device.
How it works
------------
@ -134,10 +128,14 @@ the following C struct declaration:
fdt32_t vmmc_supply;
};
and the following device declaration:
and the following device declarations:
.. code-block:: c
/* Node /clock-controller@ff760000 index 0 */
...
/* Node /dwmmc@ff0c0000 index 2 */
static struct dtd_rockchip_rk3288_dw_mshc dtv_dwmmc_at_ff0c0000 = {
.fifo_depth = 0x100,
.cap_sd_highspeed = true,
@ -145,10 +143,10 @@ and the following device declaration:
.clock_freq_min_max = {0x61a80, 0x8f0d180},
.vmmc_supply = 0xb,
.num_slots = 0x1,
.clocks = {{NULL, 456},
{NULL, 68},
{NULL, 114},
{NULL, 118}},
.clocks = {{0, 456},
{0, 68},
{0, 114},
{0, 118}},
.cap_mmc_highspeed = true,
.disable_wp = true,
.bus_width = 0x4,
@ -161,13 +159,10 @@ and the following device declaration:
.name = "rockchip_rk3288_dw_mshc",
.platdata = &dtv_dwmmc_at_ff0c0000,
.platdata_size = sizeof(dtv_dwmmc_at_ff0c0000),
.parent_idx = -1,
};
void dm_populate_phandle_data(void) {
dtv_dwmmc_at_ff0c0000.clocks[0].node = DM_GET_DEVICE(clock_controller_at_ff760000);
dtv_dwmmc_at_ff0c0000.clocks[1].node = DM_GET_DEVICE(clock_controller_at_ff760000);
dtv_dwmmc_at_ff0c0000.clocks[2].node = DM_GET_DEVICE(clock_controller_at_ff760000);
dtv_dwmmc_at_ff0c0000.clocks[3].node = DM_GET_DEVICE(clock_controller_at_ff760000);
}
The device is then instantiated at run-time and the platform data can be
@ -193,6 +188,13 @@ In order to make this a bit more flexible U_BOOT_DRIVER_ALIAS macro can be
used to declare an alias for a driver name, typically a 'compatible' string.
This macro produces no code, but it is by dtoc tool.
The parent_idx is the index of the parent driver_info structure within its
linker list (instantiated by the U_BOOT_DEVICE() macro). This is used to support
dev_get_parent(). The dm_populate_phandle_data() is included to allow for
fix-ups required by dtoc. It is not currently used. The values in 'clocks' are
the index of the driver_info for the target device followed by any phandle
arguments. This is used to support device_get_by_driver_info_idx().
During the build process dtoc parses both U_BOOT_DRIVER and U_BOOT_DRIVER_ALIAS
to build a list of valid driver names and driver aliases. If the 'compatible'
string used for a device does not not match a valid driver name, it will be
@ -339,12 +341,7 @@ spl/dt-platdata.c. It additionally contains the definition of
dm_populate_phandle_data() which is responsible of filling the phandle
information by adding references to U_BOOT_DEVICE by using DM_GET_DEVICE
The beginnings of a libfdt Python module are provided. So far this only
implements a subset of the features.
The 'swig' tool is needed to build the libfdt Python module. If this is not
found then the Python model is not used and a fallback is used instead, which
makes use of fdtget.
The pylibfdt Python module is used to access the devicetree.
Credits
@ -357,11 +354,10 @@ Future work
-----------
- Consider programmatically reading binding files instead of device tree
contents
- Complete the phandle feature
- Move to using a full Python libfdt module
.. Simon Glass <sjg@chromium.org>
.. Google, Inc
.. 6/6/16
.. Updated Independence Day 2016
.. Updated 1st October 2020

View File

@ -38,8 +38,7 @@ int clk_get_by_driver_info(struct udevice *dev, struct phandle_1_arg *cells,
{
int ret;
ret = device_get_by_driver_info((struct driver_info *)cells->node,
&clk->dev);
ret = device_get_by_driver_info_idx(cells->idx, &clk->dev);
if (ret)
return ret;
clk->id = cells->arg[0];

View File

@ -46,8 +46,8 @@ static const struct udevice_id clk_fixed_rate_match[] = {
{ /* sentinel */ }
};
U_BOOT_DRIVER(clk_fixed_rate) = {
.name = "fixed_rate_clock",
U_BOOT_DRIVER(fixed_clock) = {
.name = "fixed_clock",
.id = UCLASS_CLK,
.of_match = clk_fixed_rate_match,
.ofdata_to_platdata = clk_fixed_rate_ofdata_to_platdata,

View File

@ -124,8 +124,8 @@ static const struct udevice_id sandbox_clk_ids[] = {
{ }
};
U_BOOT_DRIVER(clk_sandbox) = {
.name = "clk_sandbox",
U_BOOT_DRIVER(sandbox_clk) = {
.name = "sandbox_clk",
.id = UCLASS_CLK,
.of_match = sandbox_clk_ids,
.ops = &sandbox_clk_ops,

View File

@ -40,10 +40,24 @@ config DM_WARN
depends on DM
default y
help
Enable this to see warnings related to driver model.
Warnings may help with debugging, such as when expected devices do
not bind correctly. If the option is disabled, dm_warn() is compiled
out - it will do nothing when called.
config SPL_DM_WARN
bool "Enable warnings in driver model wuth SPL"
depends on SPL_DM
help
Enable this to see warnings related to driver model in SPL
The dm_warn() function can use up quite a bit of space for its
strings. By default this is disabled for SPL builds to save space.
This will cause dm_warn() to be compiled out - it will do nothing
when called.
Warnings may help with debugging, such as when expected devices do
not bind correctly. If the option is disabled, dm_warn() is compiled
out - it will do nothing when called.
config DM_DEBUG
bool "Enable debug messages in driver model core"

View File

@ -11,7 +11,7 @@ obj-$(CONFIG_SIMPLE_PM_BUS) += simple-pm-bus.o
obj-$(CONFIG_DM) += dump.o
obj-$(CONFIG_$(SPL_TPL_)REGMAP) += regmap.o
obj-$(CONFIG_$(SPL_TPL_)SYSCON) += syscon-uclass.o
obj-$(CONFIG_OF_LIVE) += of_access.o of_addr.o
obj-$(CONFIG_$(SPL_)OF_LIVE) += of_access.o of_addr.o
ifndef CONFIG_DM_DEV_READ_INLINE
obj-$(CONFIG_OF_CONTROL) += read.o
endif

View File

@ -249,7 +249,7 @@ int device_bind_ofnode(struct udevice *parent, const struct driver *drv,
}
int device_bind_by_name(struct udevice *parent, bool pre_reloc_only,
struct driver_info *info, struct udevice **devp)
const struct driver_info *info, struct udevice **devp)
{
struct driver *drv;
uint platdata_size = 0;
@ -269,9 +269,6 @@ int device_bind_by_name(struct udevice *parent, bool pre_reloc_only,
platdata_size, devp);
if (ret)
return ret;
#if CONFIG_IS_ENABLED(OF_PLATDATA)
info->dev = *devp;
#endif
return ret;
}
@ -764,9 +761,25 @@ int device_get_global_by_ofnode(ofnode ofnode, struct udevice **devp)
int device_get_by_driver_info(const struct driver_info *info,
struct udevice **devp)
{
struct driver_info *info_base =
ll_entry_start(struct driver_info, driver_info);
int idx = info - info_base;
struct driver_rt *drt = gd_dm_driver_rt() + idx;
struct udevice *dev;
dev = info->dev;
dev = drt->dev;
*devp = NULL;
return device_get_device_tail(dev, dev ? 0 : -ENOENT, devp);
}
int device_get_by_driver_info_idx(uint idx, struct udevice **devp)
{
struct driver_rt *drt = gd_dm_driver_rt() + idx;
struct udevice *dev;
dev = drt->dev;
*devp = NULL;
return device_get_device_tail(dev, dev ? 0 : -ENOENT, devp);
}

View File

@ -51,25 +51,81 @@ struct uclass_driver *lists_uclass_lookup(enum uclass_id id)
return NULL;
}
int lists_bind_drivers(struct udevice *parent, bool pre_reloc_only)
static int bind_drivers_pass(struct udevice *parent, bool pre_reloc_only)
{
struct driver_info *info =
ll_entry_start(struct driver_info, driver_info);
const int n_ents = ll_entry_count(struct driver_info, driver_info);
struct driver_info *entry;
struct udevice *dev;
bool missing_parent = false;
int result = 0;
int ret;
uint idx;
for (entry = info; entry != info + n_ents; entry++) {
ret = device_bind_by_name(parent, pre_reloc_only, entry, &dev);
if (ret && ret != -EPERM) {
/*
* Do one iteration through the driver_info records. For of-platdata,
* bind only devices whose parent is already bound. If we find any
* device we can't bind, set missing_parent to true, which will cause
* this function to be called again.
*/
for (idx = 0; idx < n_ents; idx++) {
struct udevice *par = parent;
const struct driver_info *entry = info + idx;
struct driver_rt *drt = gd_dm_driver_rt() + idx;
struct udevice *dev;
int ret;
if (CONFIG_IS_ENABLED(OF_PLATDATA)) {
int parent_idx = driver_info_parent_id(entry);
if (drt->dev)
continue;
if (CONFIG_IS_ENABLED(OF_PLATDATA_PARENT) &&
parent_idx != -1) {
struct driver_rt *parent_drt;
parent_drt = gd_dm_driver_rt() + parent_idx;
if (!parent_drt->dev) {
missing_parent = true;
continue;
}
par = parent_drt->dev;
}
}
ret = device_bind_by_name(par, pre_reloc_only, entry, &dev);
if (!ret) {
if (CONFIG_IS_ENABLED(OF_PLATDATA))
drt->dev = dev;
} else if (ret != -EPERM) {
dm_warn("No match for driver '%s'\n", entry->name);
if (!result || ret != -ENOENT)
result = ret;
}
}
return result ? result : missing_parent ? -EAGAIN : 0;
}
int lists_bind_drivers(struct udevice *parent, bool pre_reloc_only)
{
int result = 0;
int pass;
/*
* 10 passes is 10 levels deep in the devicetree, which is plenty. If
* OF_PLATDATA_PARENT is not enabled, then bind_drivers_pass() will
* always succeed on the first pass.
*/
for (pass = 0; pass < 10; pass++) {
int ret;
ret = bind_drivers_pass(parent, pre_reloc_only);
if (!ret)
break;
if (ret != -EAGAIN && !result)
result = ret;
}
return result;
}

View File

@ -50,7 +50,6 @@ void dm_fixup_for_gd_move(struct global_data *new_gd)
}
}
#if defined(CONFIG_NEEDS_MANUAL_RELOC)
void fix_drivers(void)
{
struct driver *drv =
@ -61,7 +60,7 @@ void fix_drivers(void)
for (entry = drv; entry != drv + n_ents; entry++) {
if (entry->of_match)
entry->of_match = (const struct udevice_id *)
((u32)entry->of_match + gd->reloc_off);
((ulong)entry->of_match + gd->reloc_off);
if (entry->bind)
entry->bind += gd->reloc_off;
if (entry->probe)
@ -129,8 +128,6 @@ void fix_devices(void)
}
}
#endif
int dm_init(bool of_live)
{
int ret;
@ -141,21 +138,19 @@ int dm_init(bool of_live)
}
INIT_LIST_HEAD(&DM_UCLASS_ROOT_NON_CONST);
#if defined(CONFIG_NEEDS_MANUAL_RELOC)
fix_drivers();
fix_uclass();
fix_devices();
#endif
if (IS_ENABLED(CONFIG_NEEDS_MANUAL_RELOC)) {
fix_drivers();
fix_uclass();
fix_devices();
}
ret = device_bind_by_name(NULL, false, &root_info, &DM_ROOT_NON_CONST);
if (ret)
return ret;
#if CONFIG_IS_ENABLED(OF_CONTROL)
# if CONFIG_IS_ENABLED(OF_LIVE)
if (of_live)
DM_ROOT_NON_CONST->node = np_to_ofnode(gd->of_root);
if (CONFIG_IS_ENABLED(OF_LIVE) && of_live)
DM_ROOT_NON_CONST->node = np_to_ofnode(gd_of_root());
else
#endif
DM_ROOT_NON_CONST->node = offset_to_ofnode(0);
#endif
ret = device_probe(DM_ROOT_NON_CONST);
@ -187,6 +182,17 @@ int dm_scan_platdata(bool pre_reloc_only)
{
int ret;
if (CONFIG_IS_ENABLED(OF_PLATDATA)) {
struct driver_rt *dyn;
int n_ents;
n_ents = ll_entry_count(struct driver_info, driver_info);
dyn = calloc(n_ents, sizeof(struct driver_rt));
if (!dyn)
return -ENOMEM;
gd_set_dm_driver_rt(dyn);
}
ret = lists_bind_drivers(DM_ROOT_NON_CONST, pre_reloc_only);
if (ret == -ENOENT) {
dm_warn("Some drivers were not found\n");
@ -196,7 +202,7 @@ int dm_scan_platdata(bool pre_reloc_only)
return ret;
}
#if CONFIG_IS_ENABLED(OF_LIVE)
#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)
static int dm_scan_fdt_live(struct udevice *parent,
const struct device_node *node_parent,
bool pre_reloc_only)
@ -223,9 +229,7 @@ static int dm_scan_fdt_live(struct udevice *parent,
return ret;
}
#endif /* CONFIG_IS_ENABLED(OF_LIVE) */
#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)
/**
* dm_scan_fdt_node() - Scan the device tree and bind drivers for a node
*
@ -272,24 +276,20 @@ int dm_scan_fdt_dev(struct udevice *dev)
if (!dev_of_valid(dev))
return 0;
#if CONFIG_IS_ENABLED(OF_LIVE)
if (of_live_active())
return dm_scan_fdt_live(dev, dev_np(dev),
gd->flags & GD_FLG_RELOC ? false : true);
else
#endif
return dm_scan_fdt_node(dev, gd->fdt_blob, dev_of_offset(dev),
gd->flags & GD_FLG_RELOC ? false : true);
}
int dm_scan_fdt(const void *blob, bool pre_reloc_only)
{
#if CONFIG_IS_ENABLED(OF_LIVE)
if (of_live_active())
return dm_scan_fdt_live(gd->dm_root, gd->of_root,
return dm_scan_fdt_live(gd->dm_root, gd_of_root(),
pre_reloc_only);
else
#endif
return dm_scan_fdt_node(gd->dm_root, blob, 0, pre_reloc_only);
}
@ -302,10 +302,9 @@ static int dm_scan_fdt_ofnode_path(const void *blob, const char *path,
if (!ofnode_valid(node))
return 0;
#if CONFIG_IS_ENABLED(OF_LIVE)
if (of_live_active())
return dm_scan_fdt_live(gd->dm_root, node.np, pre_reloc_only);
#endif
return dm_scan_fdt_node(gd->dm_root, blob, node.of_offset,
pre_reloc_only);
}
@ -348,11 +347,10 @@ int dm_init_and_scan(bool pre_reloc_only)
{
int ret;
#if CONFIG_IS_ENABLED(OF_PLATDATA)
dm_populate_phandle_data();
#endif
if (CONFIG_IS_ENABLED(OF_PLATDATA))
dm_populate_phandle_data();
ret = dm_init(IS_ENABLED(CONFIG_OF_LIVE));
ret = dm_init(CONFIG_IS_ENABLED(OF_LIVE));
if (ret) {
debug("dm_init() failed: %d\n", ret);
return ret;

View File

@ -11,7 +11,7 @@
#include <linux/libfdt.h>
#include <vsprintf.h>
#ifdef CONFIG_DM_WARN
#if CONFIG_IS_ENABLED(DM_WARN)
void dm_warn(const char *fmt, ...)
{
va_list args;

View File

@ -6,7 +6,7 @@ obj-$(CONFIG_DM_I2C) += i2c-uclass.o
ifdef CONFIG_ACPIGEN
obj-$(CONFIG_DM_I2C) += acpi_i2c.o
endif
obj-$(CONFIG_DM_I2C_GPIO) += i2c-gpio.o
obj-$(CONFIG_$(SPL_)DM_I2C_GPIO) += i2c-gpio.o
obj-$(CONFIG_$(SPL_)I2C_CROS_EC_TUNNEL) += cros_ec_tunnel.o
obj-$(CONFIG_$(SPL_)I2C_CROS_EC_LDO) += cros_ec_ldo.o

View File

@ -76,7 +76,9 @@ UCLASS_DRIVER(i2c_emul) = {
UCLASS_DRIVER(i2c_emul_parent) = {
.id = UCLASS_I2C_EMUL_PARENT,
.name = "i2c_emul_parent",
#if !CONFIG_IS_ENABLED(OF_PLATDATA)
.post_bind = dm_scan_fdt_dev,
#endif
};
static const struct udevice_id i2c_emul_parent_ids[] = {

View File

@ -93,8 +93,8 @@ static const struct udevice_id sandbox_i2c_ids[] = {
{ }
};
U_BOOT_DRIVER(i2c_sandbox) = {
.name = "i2c_sandbox",
U_BOOT_DRIVER(sandbox_i2c) = {
.name = "sandbox_i2c",
.id = UCLASS_I2C,
.of_match = sandbox_i2c_ids,
.ops = &sandbox_i2c_ops,

View File

@ -69,7 +69,7 @@ int irq_get_by_driver_info(struct udevice *dev,
{
int ret;
ret = device_get_by_driver_info(cells->node, &irq->dev);
ret = device_get_by_driver_info_idx(cells->idx, &irq->dev);
if (ret)
return ret;
irq->id = cells->arg[0];

View File

@ -174,19 +174,20 @@ int p2sb_set_port_id(struct udevice *dev, int portid)
if (!CONFIG_IS_ENABLED(OF_PLATDATA))
return -ENOSYS;
uclass_find_first_device(UCLASS_P2SB, &ps2b);
if (!ps2b)
return -EDEADLK;
dev->parent = ps2b;
if (!CONFIG_IS_ENABLED(OF_PLATDATA_PARENT)) {
uclass_find_first_device(UCLASS_P2SB, &ps2b);
if (!ps2b)
return -EDEADLK;
dev->parent = ps2b;
/*
* We must allocate this, since when the device was bound it did not
* have a parent.
* TODO(sjg@chromium.org): Add a parent pointer to child devices in dtoc
*/
dev->parent_platdata = malloc(sizeof(*pplat));
if (!dev->parent_platdata)
return -ENOMEM;
/*
* We must allocate this, since when the device was bound it did
* not have a parent.
*/
dev->parent_platdata = malloc(sizeof(*pplat));
if (!dev->parent_platdata)
return -ENOMEM;
}
pplat = dev_get_parent_platdata(dev);
pplat->pid = portid;

View File

@ -8,43 +8,8 @@
#include <dm.h>
#include <dt-structs.h>
static int sandbox_spl_probe(struct udevice *dev)
{
struct dtd_sandbox_spl_test *plat = dev_get_platdata(dev);
int i;
printf("of-platdata probe:\n");
printf("bool %d\n", plat->boolval);
printf("byte %02x\n", plat->byteval);
printf("bytearray");
for (i = 0; i < sizeof(plat->bytearray); i++)
printf(" %02x", plat->bytearray[i]);
printf("\n");
printf("int %d\n", plat->intval);
printf("intarray");
for (i = 0; i < ARRAY_SIZE(plat->intarray); i++)
printf(" %d", plat->intarray[i]);
printf("\n");
printf("longbytearray");
for (i = 0; i < sizeof(plat->longbytearray); i++)
printf(" %02x", plat->longbytearray[i]);
printf("\n");
printf("string %s\n", plat->stringval);
printf("stringarray");
for (i = 0; i < ARRAY_SIZE(plat->stringarray); i++)
printf(" \"%s\"", plat->stringarray[i]);
printf("\n");
return 0;
}
U_BOOT_DRIVER(sandbox_spl_test) = {
.name = "sandbox_spl_test",
.id = UCLASS_MISC,
.flags = DM_FLAG_PRE_RELOC,
.probe = sandbox_spl_probe,
};

View File

@ -1504,12 +1504,9 @@ static int fsl_esdhc_probe(struct udevice *dev)
if (CONFIG_IS_ENABLED(DM_GPIO) && !priv->non_removable) {
struct udevice *gpiodev;
struct driver_info *info;
info = (struct driver_info *)dtplat->cd_gpios->node;
ret = device_get_by_driver_info(info, &gpiodev);
ret = device_get_by_driver_info_idx(dtplat->cd_gpios->idx,
&gpiodev);
if (ret)
return ret;

View File

@ -174,5 +174,7 @@ int rtc_write32(struct udevice *dev, unsigned int reg, u32 value)
UCLASS_DRIVER(rtc) = {
.name = "rtc",
.id = UCLASS_RTC,
#if !CONFIG_IS_ENABLED(OF_PLATDATA)
.post_bind = dm_scan_fdt_dev,
#endif
};

View File

@ -92,8 +92,8 @@ static const struct udevice_id sandbox_rtc_ids[] = {
{ }
};
U_BOOT_DRIVER(rtc_sandbox) = {
.name = "rtc-sandbox",
U_BOOT_DRIVER(sandbox_rtc) = {
.name = "sandbox_rtc",
.id = UCLASS_RTC,
.of_match = sandbox_rtc_ids,
.ops = &sandbox_rtc_ops,

View File

@ -267,6 +267,7 @@ U_BOOT_DRIVER(sandbox_serial) = {
.flags = DM_FLAG_PRE_RELOC,
};
#if !CONFIG_IS_ENABLED(OF_PLATDATA)
static const struct sandbox_serial_platdata platdata_non_fdt = {
.colour = -1,
};
@ -275,4 +276,6 @@ U_BOOT_DEVICE(serial_sandbox_non_fdt) = {
.name = "sandbox_serial",
.platdata = &platdata_non_fdt,
};
#endif
#endif /* CONFIG_IS_ENABLED(OF_CONTROL) */

View File

@ -615,6 +615,7 @@ static int ich_spi_exec_op(struct spi_slave *slave, const struct spi_mem_op *op)
return ret;
}
#if !CONFIG_IS_ENABLED(OF_PLATDATA)
/**
* ich_spi_get_basics() - Get basic information about the ICH device
*
@ -657,6 +658,7 @@ static int ich_spi_get_basics(struct udevice *bus, bool can_probe,
return ret;
}
#endif
/**
* ich_get_mmap_bus() - Handle the get_mmap() method for a bus
@ -946,10 +948,10 @@ static int ich_spi_child_pre_probe(struct udevice *dev)
static int ich_spi_ofdata_to_platdata(struct udevice *dev)
{
struct ich_spi_platdata *plat = dev_get_platdata(dev);
int ret;
#if !CONFIG_IS_ENABLED(OF_PLATDATA)
struct ich_spi_priv *priv = dev_get_priv(dev);
int ret;
ret = ich_spi_get_basics(dev, true, &priv->pch, &plat->ich_version,
&plat->mmio_base);

View File

@ -130,7 +130,9 @@ U_BOOT_DRIVER(warm_sysreset_sandbox) = {
.ops = &sandbox_warm_sysreset_ops,
};
#if !CONFIG_IS_ENABLED(OF_PLATDATA)
/* This is here in case we don't have a device tree */
U_BOOT_DEVICE(sysreset_sandbox_non_fdt) = {
.name = "sysreset_sandbox",
};
#endif

View File

@ -355,6 +355,15 @@ config SPL_OF_PLATDATA
compatible string, then adding platform data and U_BOOT_DEVICE
declarations for each node. See of-plat.txt for more information.
config SPL_OF_PLATDATA_PARENT
bool "Support parent information in devices"
depends on SPL_OF_PLATDATA
default y
help
Generally it is useful to be able to access the parent of a device
with of-platdata. To save space this can be disabled, but in that
case dev_get_parent() will always return NULL;
config TPL_OF_PLATDATA
bool "Generate platform data for use in TPL"
depends on TPL_OF_CONTROL
@ -376,4 +385,13 @@ config TPL_OF_PLATDATA
compatible string, then adding platform data and U_BOOT_DEVICE
declarations for each node. See of-plat.txt for more information.
config TPL_OF_PLATDATA_PARENT
bool "Support parent information in devices"
depends on TPL_OF_PLATDATA
default y
help
Generally it is useful to be able to access the parent of a device
with of-platdata. To save space this can be disabled, but in that
case dev_get_parent() will always return NULL;
endmenu

View File

@ -24,6 +24,8 @@
#include <membuff.h>
#include <linux/list.h>
struct driver_rt;
typedef struct global_data gd_t;
/**
@ -192,6 +194,10 @@ struct global_data {
* @uclass_root: head of core tree
*/
struct list_head uclass_root;
# if CONFIG_IS_ENABLED(OF_PLATDATA)
/** Dynamic info about the driver */
struct driver_rt *dm_driver_rt;
# endif
#endif
#ifdef CONFIG_TIMER
/**
@ -211,7 +217,7 @@ struct global_data {
* @fdt_size: space reserved for relocated device space
*/
unsigned long fdt_size;
#ifdef CONFIG_OF_LIVE
#if CONFIG_IS_ENABLED(OF_LIVE)
/**
* @of_root: root node of the live tree
*/
@ -427,6 +433,25 @@ struct global_data {
#define gd_board_type() 0
#endif
/* These macros help avoid #ifdefs in the code */
#if CONFIG_IS_ENABLED(OF_LIVE)
#define gd_of_root() gd->of_root
#define gd_of_root_ptr() &gd->of_root
#define gd_set_of_root(_root) gd->of_root = (_root)
#else
#define gd_of_root() NULL
#define gd_of_root_ptr() NULL
#define gd_set_of_root(_root)
#endif
#if CONFIG_IS_ENABLED(OF_PLATDATA)
#define gd_set_dm_driver_rt(dyn) gd->dm_driver_rt = dyn
#define gd_dm_driver_rt() gd->dm_driver_rt
#else
#define gd_set_dm_driver_rt(dyn)
#define gd_dm_driver_rt() NULL
#endif
/**
* enum gd_flags - global data flags
*

View File

@ -42,6 +42,13 @@ int binman_entry_map(ofnode parent, const char *name, void **bufp, int *sizep);
*/
void binman_set_rom_offset(int rom_offset);
/**
* binman_get_rom_offset() - Get the ROM memory-map offset
*
* @returns offset from an image_pos to the memory-mapped address
*/
int binman_get_rom_offset(void);
/**
* binman_entry_find() - Find a binman symbol
*

View File

@ -16,7 +16,6 @@
#undef CONFIG_DM_SPI
#endif
#undef CONFIG_DM_WARN
#undef CONFIG_DM_STDIO
#endif /* CONFIG_SPL_BUILD */

View File

@ -81,7 +81,7 @@ int device_bind_with_driver_data(struct udevice *parent,
* @return 0 if OK, -ve on error
*/
int device_bind_by_name(struct udevice *parent, bool pre_reloc_only,
struct driver_info *info, struct udevice **devp);
const struct driver_info *info, struct udevice **devp);
/**
* device_reparent: reparent the device to a new parent

View File

@ -553,6 +553,20 @@ int device_get_global_by_ofnode(ofnode node, struct udevice **devp);
int device_get_by_driver_info(const struct driver_info *info,
struct udevice **devp);
/**
* device_get_by_driver_info_idx() - Get a device based on driver_info index
*
* Locates a device by its struct driver_info, by using its index number which
* is written into the idx field of struct phandle_1_arg, etc.
*
* The device is probed to activate it ready for use.
*
* @idx: Index number of the driver_info structure (0=first)
* @devp: Returns pointer to device if found, otherwise this is set to NULL
* @return 0 if OK, -ve on error
*/
int device_get_by_driver_info_idx(uint idx, struct udevice **devp);
/**
* device_find_first_child() - Find the first child of a device
*
@ -823,7 +837,7 @@ static inline bool device_is_on_pci_bus(const struct udevice *dev)
_ret = device_next_child_err(&dev))
/**
* dm_scan_fdt_dev() - Bind child device in a the device tree
* dm_scan_fdt_dev() - Bind child device in the device tree
*
* This handles device which have sub-nodes in the device tree. It scans all
* sub-nodes and binds drivers for each node where a driver can be found.

View File

@ -90,17 +90,10 @@ DECLARE_GLOBAL_DATA_PTR;
*
* @returns true if livetree is active, false it not
*/
#ifdef CONFIG_OF_LIVE
static inline bool of_live_active(void)
{
return gd->of_root != NULL;
return gd_of_root() != NULL;
}
#else
static inline bool of_live_active(void)
{
return false;
}
#endif
#define OF_BAD_ADDR ((u64)-1)

View File

@ -22,30 +22,71 @@
* @name: Driver name
* @platdata: Driver-specific platform data
* @platdata_size: Size of platform data structure
* @dev: Device created from this structure data
* @parent_idx: Index of the parent driver_info structure
*/
struct driver_info {
const char *name;
const void *platdata;
#if CONFIG_IS_ENABLED(OF_PLATDATA)
uint platdata_size;
struct udevice *dev;
unsigned short platdata_size;
short parent_idx;
#endif
};
#if CONFIG_IS_ENABLED(OF_PLATDATA)
#define driver_info_parent_id(driver_info) driver_info->parent_idx
#else
#define driver_info_parent_id(driver_info) (-1)
#endif
/**
* driver_rt - runtime information set up by U-Boot
*
* There is one of these for every driver_info in the linker list, indexed by
* the driver_info idx value.
*
* @dev: Device created from this idx
*/
struct driver_rt {
struct udevice *dev;
};
/**
* NOTE: Avoid using these except in extreme circumstances, where device tree
* is not feasible (e.g. serial driver in SPL where <8KB of SRAM is
* available). U-Boot's driver model uses device tree for configuration.
*
* When of-platdata is in use, U_BOOT_DEVICE() cannot be used outside of the
* dt-platdata.c file created by dtoc
*/
#if CONFIG_IS_ENABLED(OF_PLATDATA) && !defined(DT_PLATDATA_C)
#define U_BOOT_DEVICE(__name) _Static_assert(false, \
"Cannot use U_BOOT_DEVICE with of-platdata. Please use devicetree instead")
#else
#define U_BOOT_DEVICE(__name) \
ll_entry_declare(struct driver_info, __name, driver_info)
#endif
/* Declare a list of devices. The argument is a driver_info[] array */
#define U_BOOT_DEVICES(__name) \
ll_entry_declare_list(struct driver_info, __name, driver_info)
/* Get a pointer to a given driver */
/**
* Get a pointer to a given device info given its name
*
* With the declaration U_BOOT_DEVICE(name), DM_GET_DEVICE(name) will return a
* pointer to the struct driver_info created by that declaration.
*
* if OF_PLATDATA is enabled, from this it is possible to use the @dev member of
* struct driver_info to find the device pointer itself.
*
* TODO(sjg@chromium.org): U_BOOT_DEVICE() tells U-Boot to create a device, so
* the naming seems sensible, but DM_GET_DEVICE() is a bit of misnomer, since it
* finds the driver_info record, not the device.
*
* @__name: Driver name (C identifier, not a string. E.g. gpio7_at_ff7e0000)
* @return struct driver_info * to the driver that created the device
*/
#define DM_GET_DEVICE(__name) \
ll_entry_get(struct driver_info, __name, driver_info)

View File

@ -6,7 +6,7 @@
#ifndef __DM_UTIL_H
#define __DM_UTIL_H
#ifdef CONFIG_DM_WARN
#if CONFIG_IS_ENABLED(DM_WARN)
void dm_warn(const char *fmt, ...);
#else
static inline void dm_warn(const char *fmt, ...)

View File

@ -8,18 +8,20 @@
/* These structures may only be used in SPL */
#if CONFIG_IS_ENABLED(OF_PLATDATA)
struct driver_info;
struct phandle_0_arg {
const void *node;
uint idx;
int arg[0];
};
struct phandle_1_arg {
const void *node;
uint idx;
int arg[1];
};
struct phandle_2_arg {
const void *node;
uint idx;
int arg[2];
};
#include <generated/dt-structs-gen.h>

View File

@ -94,4 +94,15 @@ enum {
TEST_DEVRES_SIZE3 = 37,
};
/**
* dm_test_main() - Run driver model tests
*
* Run all the available driver model tests, or a selection
*
* @test_name: Name of single test to run (e.g. "dm_test_fdt_pre_reloc" or just
* "fdt_pre_reloc"), or NULL to run all
* @return 0 if all tests passed, 1 if not
*/
int dm_test_main(const char *test_name);
#endif /* __TEST_TEST_H */

View File

@ -43,7 +43,7 @@ static int binman_entry_find_internal(ofnode node, const char *name,
ret = ofnode_read_u32(node, "image-pos", &entry->image_pos);
if (ret)
return log_msg_ret("import-pos", ret);
return log_msg_ret("image-pos", ret);
ret = ofnode_read_u32(node, "size", &entry->size);
if (ret)
return log_msg_ret("size", ret);
@ -83,6 +83,11 @@ void binman_set_rom_offset(int rom_offset)
binman->rom_offset = rom_offset;
}
int binman_get_rom_offset(void)
{
return binman->rom_offset;
}
int binman_init(void)
{
binman = malloc(sizeof(struct binman_info));
@ -91,6 +96,13 @@ int binman_init(void)
binman->image = ofnode_path("/binman");
if (!ofnode_valid(binman->image))
return log_msg_ret("binman node", -EINVAL);
if (ofnode_read_bool(binman->image, "multiple-images")) {
ofnode node = ofnode_first_subnode(binman->image);
if (!ofnode_valid(node))
return log_msg_ret("first image", -ENOENT);
binman->image = node;
}
binman->rom_offset = ROM_OFFSET_NONE;
return 0;

View File

@ -99,6 +99,7 @@ libs-y += dts/
libs-y += fs/
libs-$(CONFIG_SPL_POST_MEM_SUPPORT) += post/drivers/
libs-$(CONFIG_SPL_NET_SUPPORT) += net/
libs-$(CONFIG_SPL_UNIT_TEST) += test/
head-y := $(addprefix $(obj)/,$(head-y))
libs-y := $(addprefix $(obj)/,$(libs-y))
@ -213,7 +214,7 @@ spl/boot.bin: $(obj)/$(SPL_BIN)-align.bin FORCE
$(call if_changed,mkimage)
endif
INPUTS-y += $(obj)/$(SPL_BIN).bin
INPUTS-y += $(obj)/$(SPL_BIN).bin $(obj)/$(SPL_BIN).sym
ifdef CONFIG_SAMSUNG
INPUTS-y += $(obj)/$(BOARD)-spl.bin
@ -407,6 +408,11 @@ MKIMAGEFLAGS_u-boot-spl-mtk.bin = -T mtk_image \
$(obj)/u-boot-spl-mtk.bin: $(obj)/u-boot-spl.bin FORCE
$(call if_changed,mkimage)
quiet_cmd_sym ?= SYM $@
cmd_sym ?= $(OBJDUMP) -t $< > $@
$(obj)/$(SPL_BIN).sym: $(obj)/$(SPL_BIN) FORCE
$(call if_changed,sym)
# Rule to link u-boot-spl
# May be overridden by arch/$(ARCH)/config.mk
quiet_cmd_u-boot-spl ?= LD $@

View File

@ -6,6 +6,16 @@ menuconfig UNIT_TEST
This does not require sandbox to be included, but it is most
often used there.
config SPL_UNIT_TEST
bool "Unit tests in SPL"
# We need to be able to unbind devices for tests to work
select SPL_DM_DEVICE_REMOVE
help
Select this to enable unit tests in SPL. Most test are designed for
running in U-Boot proper, but some are intended for SPL, such as
of-platdata and SPL handover. To run these tests with the sandbox_spl
board, use the -u (unit test) option.
config UT_LIB
bool "Unit tests for library functions"
depends on UNIT_TEST

View File

@ -2,15 +2,19 @@
#
# (C) Copyright 2012 The Chromium Authors
obj-$(CONFIG_SANDBOX) += bloblist.o
obj-$(CONFIG_CMDLINE) += cmd/
obj-$(CONFIG_UNIT_TEST) += cmd_ut.o
obj-$(CONFIG_UNIT_TEST) += ut.o
obj-$(CONFIG_SANDBOX) += command_ut.o
obj-$(CONFIG_SANDBOX) += compression.o
obj-$(CONFIG_SANDBOX) += print_ut.o
obj-$(CONFIG_SANDBOX) += str_ut.o
obj-$(CONFIG_$(SPL_)CMDLINE) += bloblist.o
obj-$(CONFIG_$(SPL_)CMDLINE) += cmd/
obj-$(CONFIG_$(SPL_)CMDLINE) += cmd_ut.o
obj-$(CONFIG_$(SPL_)CMDLINE) += command_ut.o
obj-$(CONFIG_$(SPL_)CMDLINE) += compression.o
obj-y += dm/
obj-$(CONFIG_$(SPL_)CMDLINE) += print_ut.o
obj-$(CONFIG_$(SPL_)CMDLINE) += str_ut.o
obj-$(CONFIG_UT_TIME) += time_ut.o
obj-$(CONFIG_UT_UNICODE) += unicode_ut.o
obj-y += log/
obj-y += ut.o
ifeq ($(CONFIG_SPL_BUILD),)
obj-$(CONFIG_UNIT_TEST) += lib/
obj-y += log/
obj-$(CONFIG_$(SPL_)UT_UNICODE) += unicode_ut.o
endif

View File

@ -2,15 +2,18 @@
#
# Copyright (c) 2013 Google, Inc
obj-$(CONFIG_UT_DM) += bus.o
obj-$(CONFIG_UT_DM) += nop.o
obj-$(CONFIG_UT_DM) += test-driver.o
obj-$(CONFIG_UT_DM) += test-fdt.o
obj-$(CONFIG_UT_DM) += test-main.o
obj-$(CONFIG_UT_DM) += test-uclass.o
# Tests for particular subsystems - when enabling driver model for a new
# subsystem you must add sandbox tests here.
ifeq ($(CONFIG_SPL_BUILD),y)
obj-$(CONFIG_SPL_OF_PLATDATA) += of_platdata.o
else
obj-$(CONFIG_UT_DM) += bus.o
obj-$(CONFIG_UT_DM) += test-driver.o
obj-$(CONFIG_UT_DM) += test-fdt.o
obj-$(CONFIG_UT_DM) += test-uclass.o
obj-$(CONFIG_UT_DM) += core.o
ifneq ($(CONFIG_SANDBOX),)
obj-$(CONFIG_ACPIGEN) += acpi.o
@ -36,6 +39,7 @@ obj-$(CONFIG_DM_MAILBOX) += mailbox.o
obj-$(CONFIG_DM_MMC) += mmc.o
obj-$(CONFIG_CMD_MUX) += mux-cmd.o
obj-y += fdtdec.o
obj-$(CONFIG_UT_DM) += nop.o
obj-y += ofnode.o
obj-y += ofread.o
obj-$(CONFIG_OSD) += osd.o
@ -88,3 +92,4 @@ ifneq ($(CONFIG_PINMUX),)
obj-$(CONFIG_PINCONF) += pinmux.o
endif
endif
endif # !SPL

222
test/dm/of_platdata.c Normal file
View File

@ -0,0 +1,222 @@
// SPDX-License-Identifier: GPL-2.0+
#include <common.h>
#include <dm.h>
#include <dt-structs.h>
#include <dm/test.h>
#include <test/test.h>
#include <test/ut.h>
/* Test that we can find a device using of-platdata */
static int dm_test_of_platdata_base(struct unit_test_state *uts)
{
struct udevice *dev;
ut_assertok(uclass_first_device_err(UCLASS_SERIAL, &dev));
ut_asserteq_str("sandbox_serial", dev->name);
return 0;
}
DM_TEST(dm_test_of_platdata_base, UT_TESTF_SCAN_PDATA);
/* Test that we can read properties from a device */
static int dm_test_of_platdata_props(struct unit_test_state *uts)
{
struct dtd_sandbox_spl_test *plat;
struct udevice *dev;
int i;
/* Skip the clock */
ut_assertok(uclass_first_device_err(UCLASS_MISC, &dev));
ut_asserteq_str("sandbox_clk_test", dev->name);
ut_assertok(uclass_next_device_err(&dev));
plat = dev_get_platdata(dev);
ut_assert(plat->boolval);
ut_asserteq(1, plat->intval);
ut_asserteq(4, ARRAY_SIZE(plat->intarray));
ut_asserteq(2, plat->intarray[0]);
ut_asserteq(3, plat->intarray[1]);
ut_asserteq(4, plat->intarray[2]);
ut_asserteq(0, plat->intarray[3]);
ut_asserteq(5, plat->byteval);
ut_asserteq(3, ARRAY_SIZE(plat->bytearray));
ut_asserteq(6, plat->bytearray[0]);
ut_asserteq(0, plat->bytearray[1]);
ut_asserteq(0, plat->bytearray[2]);
ut_asserteq(9, ARRAY_SIZE(plat->longbytearray));
for (i = 0; i < ARRAY_SIZE(plat->longbytearray); i++)
ut_asserteq(9 + i, plat->longbytearray[i]);
ut_asserteq_str("message", plat->stringval);
ut_asserteq(3, ARRAY_SIZE(plat->stringarray));
ut_asserteq_str("multi-word", plat->stringarray[0]);
ut_asserteq_str("message", plat->stringarray[1]);
ut_asserteq_str("", plat->stringarray[2]);
ut_assertok(uclass_next_device_err(&dev));
plat = dev_get_platdata(dev);
ut_assert(!plat->boolval);
ut_asserteq(3, plat->intval);
ut_asserteq(5, plat->intarray[0]);
ut_asserteq(0, plat->intarray[1]);
ut_asserteq(0, plat->intarray[2]);
ut_asserteq(0, plat->intarray[3]);
ut_asserteq(8, plat->byteval);
ut_asserteq(3, ARRAY_SIZE(plat->bytearray));
ut_asserteq(1, plat->bytearray[0]);
ut_asserteq(0x23, plat->bytearray[1]);
ut_asserteq(0x34, plat->bytearray[2]);
for (i = 0; i < ARRAY_SIZE(plat->longbytearray); i++)
ut_asserteq(i < 4 ? 9 + i : 0, plat->longbytearray[i]);
ut_asserteq_str("message2", plat->stringval);
ut_asserteq_str("another", plat->stringarray[0]);
ut_asserteq_str("multi-word", plat->stringarray[1]);
ut_asserteq_str("message", plat->stringarray[2]);
ut_assertok(uclass_next_device_err(&dev));
plat = dev_get_platdata(dev);
ut_assert(!plat->boolval);
ut_asserteq_str("one", plat->stringarray[0]);
ut_asserteq_str("", plat->stringarray[1]);
ut_asserteq_str("", plat->stringarray[2]);
ut_assertok(uclass_next_device_err(&dev));
plat = dev_get_platdata(dev);
ut_assert(!plat->boolval);
ut_asserteq_str("spl", plat->stringarray[0]);
ut_asserteq(-ENODEV, uclass_next_device_err(&dev));
return 0;
}
DM_TEST(dm_test_of_platdata_props, UT_TESTF_SCAN_PDATA);
/*
* find_driver_info - recursively find the driver_info for a device
*
* This sets found[idx] to true when it finds the driver_info record for a
* device, where idx is the index in the driver_info linker list.
*
* @uts: Test state
* @parent: Parent to search
* @found: bool array to update
* @return 0 if OK, non-zero on error
*/
static int find_driver_info(struct unit_test_state *uts, struct udevice *parent,
bool found[])
{
struct udevice *dev;
/* If not the root device, find the entry that caused it to be bound */
if (parent->parent) {
const int n_ents =
ll_entry_count(struct driver_info, driver_info);
int idx = -1;
int i;
for (i = 0; i < n_ents; i++) {
const struct driver_rt *drt = gd_dm_driver_rt() + i;
if (drt->dev == parent) {
idx = i;
found[idx] = true;
break;
}
}
ut_assert(idx != -1);
}
device_foreach_child(dev, parent) {
int ret;
ret = find_driver_info(uts, dev, found);
if (ret < 0)
return ret;
}
return 0;
}
/* Check that every device is recorded in its driver_info struct */
static int dm_test_of_platdata_dev(struct unit_test_state *uts)
{
const struct driver_info *info =
ll_entry_start(struct driver_info, driver_info);
const int n_ents = ll_entry_count(struct driver_info, driver_info);
bool found[n_ents];
uint i;
/* Record the indexes that are found */
memset(found, '\0', sizeof(found));
ut_assertok(find_driver_info(uts, gd->dm_root, found));
/* Make sure that the driver entries without devices have no ->dev */
for (i = 0; i < n_ents; i++) {
const struct driver_rt *drt = gd_dm_driver_rt() + i;
const struct driver_info *entry = info + i;
struct udevice *dev;
if (found[i]) {
/* Make sure we can find it */
ut_assertnonnull(drt->dev);
ut_assertok(device_get_by_driver_info(entry, &dev));
ut_asserteq_ptr(dev, drt->dev);
} else {
ut_assertnull(drt->dev);
ut_asserteq(-ENOENT,
device_get_by_driver_info(entry, &dev));
}
}
return 0;
}
DM_TEST(dm_test_of_platdata_dev, UT_TESTF_SCAN_PDATA);
/* Test handling of phandles that point to other devices */
static int dm_test_of_platdata_phandle(struct unit_test_state *uts)
{
struct dtd_sandbox_clk_test *plat;
struct udevice *dev, *clk;
ut_assertok(uclass_first_device_err(UCLASS_MISC, &dev));
ut_asserteq_str("sandbox_clk_test", dev->name);
plat = dev_get_platdata(dev);
ut_assertok(device_get_by_driver_info_idx(plat->clocks[0].idx, &clk));
ut_asserteq_str("fixed_clock", clk->name);
ut_assertok(device_get_by_driver_info_idx(plat->clocks[1].idx, &clk));
ut_asserteq_str("sandbox_clk", clk->name);
ut_asserteq(1, plat->clocks[1].arg[0]);
ut_assertok(device_get_by_driver_info_idx(plat->clocks[2].idx, &clk));
ut_asserteq_str("sandbox_clk", clk->name);
ut_asserteq(0, plat->clocks[2].arg[0]);
ut_assertok(device_get_by_driver_info_idx(plat->clocks[3].idx, &clk));
ut_asserteq_str("sandbox_clk", clk->name);
ut_asserteq(3, plat->clocks[3].arg[0]);
ut_assertok(device_get_by_driver_info_idx(plat->clocks[4].idx, &clk));
ut_asserteq_str("sandbox_clk", clk->name);
ut_asserteq(2, plat->clocks[4].arg[0]);
return 0;
}
DM_TEST(dm_test_of_platdata_phandle, UT_TESTF_SCAN_PDATA);
#if CONFIG_IS_ENABLED(OF_PLATDATA_PARENT)
/* Test that device parents are correctly set up */
static int dm_test_of_platdata_parent(struct unit_test_state *uts)
{
struct udevice *rtc, *i2c;
ut_assertok(uclass_first_device_err(UCLASS_RTC, &rtc));
ut_assertok(uclass_first_device_err(UCLASS_I2C, &i2c));
ut_asserteq_ptr(i2c, dev_get_parent(rtc));
return 0;
}
DM_TEST(dm_test_of_platdata_parent, UT_TESTF_SCAN_PDATA);
#endif

View File

@ -30,13 +30,12 @@ static int dm_test_init(struct unit_test_state *uts, bool of_live)
memset(dms, '\0', sizeof(*dms));
gd->dm_root = NULL;
memset(dm_testdrv_op_count, '\0', sizeof(dm_testdrv_op_count));
if (!CONFIG_IS_ENABLED(OF_PLATDATA))
memset(dm_testdrv_op_count, '\0', sizeof(dm_testdrv_op_count));
state_reset_for_test(state_get_current());
#ifdef CONFIG_OF_LIVE
/* Determine whether to make the live tree available */
gd->of_root = of_live ? uts->of_root : NULL;
#endif
gd_set_of_root(of_live ? uts->of_root : NULL);
ut_assertok(dm_init(of_live));
dms->root = dm_root();
@ -93,7 +92,8 @@ static int dm_do_test(struct unit_test_state *uts, struct unit_test *test,
ut_assertok(dm_scan_platdata(false));
if (test->flags & UT_TESTF_PROBE_TEST)
ut_assertok(do_autoprobe(uts));
if (test->flags & UT_TESTF_SCAN_FDT)
if (!CONFIG_IS_ENABLED(OF_PLATDATA) &&
(test->flags & UT_TESTF_SCAN_FDT))
ut_assertok(dm_extended_scan_fdt(gd->fdt_blob, false));
/*
@ -127,7 +127,25 @@ static bool dm_test_run_on_flattree(struct unit_test *test)
return !strstr(fname, "video") || strstr(test->name, "video_base");
}
static int dm_test_main(const char *test_name)
static bool test_matches(const char *test_name, const char *find_name)
{
if (!find_name)
return true;
if (!strcmp(test_name, find_name))
return true;
/* All tests have this prefix */
if (!strncmp(test_name, "dm_test_", 8))
test_name += 8;
if (!strcmp(test_name, find_name))
return true;
return false;
}
int dm_test_main(const char *test_name)
{
struct unit_test *tests = ll_entry_start(struct unit_test, dm_test);
const int n_ents = ll_entry_count(struct unit_test, dm_test);
@ -138,36 +156,34 @@ static int dm_test_main(const char *test_name)
uts->priv = &_global_priv_dm_test_state;
uts->fail_count = 0;
/*
* If we have no device tree, or it only has a root node, then these
* tests clearly aren't going to work...
*/
if (!gd->fdt_blob || fdt_next_node(gd->fdt_blob, 0, NULL) < 0) {
puts("Please run with test device tree:\n"
" ./u-boot -d arch/sandbox/dts/test.dtb\n");
ut_assert(gd->fdt_blob);
if (!CONFIG_IS_ENABLED(OF_PLATDATA)) {
/*
* If we have no device tree, or it only has a root node, then
* these * tests clearly aren't going to work...
*/
if (!gd->fdt_blob || fdt_next_node(gd->fdt_blob, 0, NULL) < 0) {
puts("Please run with test device tree:\n"
" ./u-boot -d arch/sandbox/dts/test.dtb\n");
ut_assert(gd->fdt_blob);
}
}
if (!test_name)
printf("Running %d driver model tests\n", n_ents);
else
found = 0;
#ifdef CONFIG_OF_LIVE
uts->of_root = gd->of_root;
#endif
uts->of_root = gd_of_root();
for (test = tests; test < tests + n_ents; test++) {
const char *name = test->name;
int runs;
/* All tests have this prefix */
if (!strncmp(name, "dm_test_", 8))
name += 8;
if (test_name && strcmp(test_name, name))
if (!test_matches(name, test_name))
continue;
/* Run with the live tree if possible */
runs = 0;
if (IS_ENABLED(CONFIG_OF_LIVE)) {
if (CONFIG_IS_ENABLED(OF_LIVE)) {
if (!(test->flags & UT_TESTF_FLAT_TREE)) {
ut_assertok(dm_do_test(uts, test, true));
runs++;
@ -192,13 +208,12 @@ static int dm_test_main(const char *test_name)
printf("Failures: %d\n", uts->fail_count);
/* Put everything back to normal so that sandbox works as expected */
#ifdef CONFIG_OF_LIVE
gd->of_root = uts->of_root;
#endif
gd_set_of_root(uts->of_root);
gd->dm_root = NULL;
ut_assertok(dm_init(IS_ENABLED(CONFIG_OF_LIVE)));
ut_assertok(dm_init(CONFIG_IS_ENABLED(OF_LIVE)));
dm_scan_platdata(false);
dm_scan_fdt(gd->fdt_blob, false);
if (!CONFIG_IS_ENABLED(OF_PLATDATA))
dm_scan_fdt(gd->fdt_blob, false);
return uts->fail_count ? CMD_RET_FAILURE : 0;
}

View File

@ -227,7 +227,7 @@ def pytest_configure(config):
console = u_boot_console_exec_attach.ConsoleExecAttach(log, ubconfig)
re_ut_test_list = re.compile(r'_u_boot_list_2_(.*)_test_2_\1_test_(.*)\s*$')
def generate_ut_subtest(metafunc, fixture_name):
def generate_ut_subtest(metafunc, fixture_name, sym_path):
"""Provide parametrization for a ut_subtest fixture.
Determines the set of unit tests built into a U-Boot binary by parsing the
@ -237,12 +237,13 @@ def generate_ut_subtest(metafunc, fixture_name):
Args:
metafunc: The pytest test function.
fixture_name: The fixture name to test.
sym_path: Relative path to the symbol file with preceding '/'
(e.g. '/u-boot.sym')
Returns:
Nothing.
"""
fn = console.config.build_dir + '/u-boot.sym'
fn = console.config.build_dir + sym_path
try:
with open(fn, 'rt') as f:
lines = f.readlines()
@ -317,10 +318,12 @@ def pytest_generate_tests(metafunc):
Returns:
Nothing.
"""
for fn in metafunc.fixturenames:
if fn == 'ut_subtest':
generate_ut_subtest(metafunc, fn)
generate_ut_subtest(metafunc, fn, '/u-boot.sym')
continue
if fn == 'ut_spl_subtest':
generate_ut_subtest(metafunc, fn, '/spl/u-boot-spl.sym')
continue
generate_config(metafunc, fn)

View File

@ -4,53 +4,6 @@
import pytest
import u_boot_utils as util
OF_PLATDATA_OUTPUT = '''
of-platdata probe:
bool 1
byte 05
bytearray 06 00 00
int 1
intarray 2 3 4 0
longbytearray 09 0a 0b 0c 0d 0e 0f 10 11
string message
stringarray "multi-word" "message" ""
of-platdata probe:
bool 0
byte 08
bytearray 01 23 34
int 3
intarray 5 0 0 0
longbytearray 09 00 00 00 00 00 00 00 00
string message2
stringarray "another" "multi-word" "message"
of-platdata probe:
bool 0
byte 00
bytearray 00 00 00
int 0
intarray 0 0 0 0
longbytearray 00 00 00 00 00 00 00 00 00
string <NULL>
stringarray "one" "" ""
of-platdata probe:
bool 0
byte 00
bytearray 00 00 00
int 0
intarray 0 0 0 0
longbytearray 00 00 00 00 00 00 00 00 00
string <NULL>
stringarray "spl" "" ""
'''
@pytest.mark.buildconfigspec('spl_of_platdata')
def test_ofplatdata(u_boot_console):
"""Test that of-platdata can be generated and used in sandbox"""
cons = u_boot_console
cons.restart_uboot_with_flags(['--show_of_platdata'])
output = cons.get_spawn_output().replace('\r', '')
assert OF_PLATDATA_OUTPUT in output
@pytest.mark.buildconfigspec('spl_of_platdata')
def test_spl_devicetree(u_boot_console):
"""Test content of spl device-tree"""

34
test/py/tests/test_spl.py Normal file
View File

@ -0,0 +1,34 @@
# SPDX-License-Identifier: GPL-2.0
# Copyright 2020 Google LLC
# Written by Simon Glass <sjg@chromium.org>
import os.path
import pytest
def test_spl(u_boot_console, ut_spl_subtest):
"""Execute a "ut" subtest.
The subtests are collected in function generate_ut_subtest() from linker
generated lists by applying a regular expression to the lines of file
spl/u-boot-spl.sym. The list entries are created using the C macro
UNIT_TEST().
Strict naming conventions have to be followed to match the regular
expression. Use UNIT_TEST(foo_test_bar, _flags, foo_test) for a test bar in
test suite foo that can be executed via command 'ut foo bar' and is
implemented in C function foo_test_bar().
Args:
u_boot_console (ConsoleBase): U-Boot console
ut_subtest (str): SPL test to be executed (e.g. 'dm platdata_phandle')
"""
try:
cons = u_boot_console
cons.restart_uboot_with_flags(['-u', '-k', ut_spl_subtest.split()[1]])
output = cons.get_spawn_output().replace('\r', '')
assert 'Failures: 0' in output
finally:
# Restart afterward in case a non-SPL test is run next. This should not
# happen since SPL tests are run in their own invocation of test.py, but
# the cost of doing this is not too great at present.
u_boot_console.restart_uboot()

View File

@ -28,7 +28,7 @@ fi
# Run tests which require sandbox_spl
run_test "sandbox_spl" ./test/py/test.py --bd sandbox_spl --build \
-k 'test_ofplatdata or test_handoff'
-k 'test_ofplatdata or test_handoff or test_spl'
if [ -z "$tools_only" ]; then
# Run tests for the flat-device-tree version of sandbox. This is a special

View File

@ -278,9 +278,12 @@ offset:
align:
This sets the alignment of the entry. The entry offset is adjusted
so that the entry starts on an aligned boundary within the image. For
example 'align = <16>' means that the entry will start on a 16-byte
boundary. Alignment shold be a power of 2. If 'align' is not
so that the entry starts on an aligned boundary within the containing
section or image. For example 'align = <16>' means that the entry will
start on a 16-byte boundary. This may mean that padding is added before
the entry. The padding is part of the containing section but is not
included in the entry, meaning that an empty space may be created before
the entry starts. Alignment should be a power of 2. If 'align' is not
provided, no alignment is performed.
size:
@ -290,26 +293,40 @@ size:
pad-before:
Padding before the contents of the entry. Normally this is 0, meaning
that the contents start at the beginning of the entry. This can be
offset the entry contents a little. Defaults to 0.
that the contents start at the beginning of the entry. This can be used
to offset the entry contents a little. While this does not affect the
contents of the entry within binman itself (the padding is performed
only when its parent section is assembled), the end result will be that
the entry starts with the padding bytes, so may grow. Defaults to 0.
pad-after:
Padding after the contents of the entry. Normally this is 0, meaning
that the entry ends at the last byte of content (unless adjusted by
other properties). This allows room to be created in the image for
this entry to expand later. Defaults to 0.
this entry to expand later. While this does not affect the contents of
the entry within binman itself (the padding is performed only when its
parent section is assembled), the end result will be that the entry ends
with the padding bytes, so may grow. Defaults to 0.
align-size:
This sets the alignment of the entry size. For example, to ensure
that the size of an entry is a multiple of 64 bytes, set this to 64.
If 'align-size' is not provided, no alignment is performed.
While this does not affect the contents of the entry within binman
itself (the padding is performed only when its parent section is
assembled), the end result is that the entry ends with the padding
bytes, so may grow. If 'align-size' is not provided, no alignment is
performed.
align-end:
This sets the alignment of the end of an entry. Some entries require
that they end on an alignment boundary, regardless of where they
start. This does not move the start of the entry, so the contents of
the entry will still start at the beginning. But there may be padding
at the end. If 'align-end' is not provided, no alignment is performed.
This sets the alignment of the end of an entry with respect to the
containing section. Some entries require that they end on an alignment
boundary, regardless of where they start. This does not move the start
of the entry, so the contents of the entry will still start at the
beginning. But there may be padding at the end. While this does not
affect the contents of the entry within binman itself (the padding is
performed only when its parent section is assembled), the end result
is that the entry ends with the padding bytes, so may grow.
If 'align-end' is not provided, no alignment is performed.
filename:
For 'blob' types this provides the filename containing the binary to
@ -695,41 +712,38 @@ size of an entry. The 'current' image offset is passed in, and the function
returns the offset immediately after the entry being packed. The default
implementation of Pack() is usually sufficient.
6. CheckSize() - checks that the contents of all the entries fits within
the image size. If the image does not have a defined size, the size is set
large enough to hold all the entries.
Note: for sections, this also checks that the entries do not overlap, nor extend
outside the section. If the section does not have a defined size, the size is
set large enough to hold all the entries.
7. CheckEntries() - checks that the entries do not overlap, nor extend
outside the image.
8. SetImagePos() - sets the image position of every entry. This is the absolute
6. SetImagePos() - sets the image position of every entry. This is the absolute
position 'image-pos', as opposed to 'offset' which is relative to the containing
section. This must be done after all offsets are known, which is why it is quite
late in the ordering.
9. SetCalculatedProperties() - update any calculated properties in the device
7. SetCalculatedProperties() - update any calculated properties in the device
tree. This sets the correct 'offset' and 'size' vaues, for example.
10. ProcessEntryContents() - this calls Entry.ProcessContents() on each entry.
8. ProcessEntryContents() - this calls Entry.ProcessContents() on each entry.
The default implementatoin does nothing. This can be overriden to adjust the
contents of an entry in some way. For example, it would be possible to create
an entry containing a hash of the contents of some other entries. At this
stage the offset and size of entries should not be adjusted unless absolutely
necessary, since it requires a repack (going back to PackEntries()).
11. ResetForPack() - if the ProcessEntryContents() step failed, in that an entry
9. ResetForPack() - if the ProcessEntryContents() step failed, in that an entry
has changed its size, then there is no alternative but to go back to step 5 and
try again, repacking the entries with the updated size. ResetForPack() removes
the fixed offset/size values added by binman, so that the packing can start from
scratch.
12. WriteSymbols() - write the value of symbols into the U-Boot SPL binary.
10. WriteSymbols() - write the value of symbols into the U-Boot SPL binary.
See 'Access to binman entry offsets at run time' below for a description of
what happens in this stage.
13. BuildImage() - builds the image and writes it to a file
11. BuildImage() - builds the image and writes it to a file
14. WriteMap() - writes a text file containing a map of the image. This is the
12. WriteMap() - writes a text file containing a map of the image. This is the
final step.
@ -839,6 +853,14 @@ The entry will then contain the compressed data, using the 'lz4' compression
algorithm. Currently this is the only one that is supported. The uncompressed
size is written to the node in an 'uncomp-size' property, if -u is used.
Compression is also supported for sections. In that case the entire section is
compressed in one block, including all its contents. This means that accessing
an entry from the section required decompressing the entire section. Also, the
size of a section indicates the space that it consumes in its parent section
(and typically the image). With compression, the section may contain more data,
and the uncomp-size property indicates that, as above. The contents of the
section is compressed first, before any padding is added. This ensures that the
padding itself is not compressed, which would be a waste of time.
Map files

View File

@ -299,7 +299,7 @@ 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:
- files-compress: Compression algorithm to use:
none: No compression
lz4: Use lz4 compression (via 'lz4' command-line utility)
@ -406,6 +406,10 @@ The 'default' property, if present, will be automatically set to the name
if of configuration whose devicetree matches the 'default-dt' entry
argument, e.g. with '-a default-dt=sun50i-a64-pine64-lts'.
Available substitutions for '@' property values are:
DEFAULT-SEQ Sequence number of the default fdt,as provided by the
'default-dt' entry argument
Properties (in the 'fit' node itself):
fit,external-offset: Indicates that the contents of the FIT are external
@ -739,6 +743,16 @@ placed at offset 'RESET_VECTOR_ADDRESS - 0xffc'.
Entry: scp: Entry containing a System Control Processor (SCP) firmware blob
---------------------------------------------------------------------------
Properties / Entry arguments:
- scp-path: Filename of file to read into the entry, typically scp.bin
This entry holds firmware for an external platform-specific coprocessor.
Entry: section: Entry that contains other entries
-------------------------------------------------
@ -878,6 +892,15 @@ relocated to any address for execution.
Entry: u-boot-env: An entry which contains a U-Boot environment
---------------------------------------------------------------
Properties / Entry arguments:
- filename: File containing the environment text, with each line in the
form var=value
Entry: u-boot-img: U-Boot legacy image
--------------------------------------

View File

@ -37,6 +37,7 @@ controlled by a description in the board device tree.'''
'3=info, 4=detail, 5=debug')
subparsers = parser.add_subparsers(dest='cmd')
subparsers.required = True
build_parser = subparsers.add_parser('build', help='Build firmware image')
build_parser.add_argument('-a', '--entry-arg', type=str, action='append',

View File

@ -462,7 +462,7 @@ def PrepareImagesAndDtbs(dtb_fname, select_images, update_fdt):
for image in images.values():
image.ExpandEntries()
if update_fdt:
image.AddMissingProperties()
image.AddMissingProperties(True)
image.ProcessFdt(dtb)
for dtb_item in state.GetAllFdts():
@ -513,8 +513,6 @@ def ProcessImage(image, update_fdt, write_map, get_contents=True,
for pack_pass in range(passes):
try:
image.PackEntries()
image.CheckSize()
image.CheckEntries()
except Exception as e:
if write_map:
fname = image.WriteMap()

View File

@ -48,12 +48,22 @@ class Entry(object):
uncomp_size: Size of uncompressed data in bytes, if the entry is
compressed, else None
contents_size: Size of contents in bytes, 0 by default
align: Entry start offset alignment, or None
align: Entry start offset alignment relative to the start of the
containing section, or None
align_size: Entry size alignment, or None
align_end: Entry end offset alignment, or None
pad_before: Number of pad bytes before the contents, 0 if none
pad_after: Number of pad bytes after the contents, 0 if none
data: Contents of entry (string of bytes)
align_end: Entry end offset alignment relative to the start of the
containing section, or None
pad_before: Number of pad bytes before the contents when it is placed
in the containing section, 0 if none. The pad bytes become part of
the entry.
pad_after: Number of pad bytes after the contents when it is placed in
the containing section, 0 if none. The pad bytes become part of
the entry.
data: Contents of entry (string of bytes). This does not include
padding created by pad_before or pad_after. If the entry is
compressed, this contains the compressed data.
uncomp_data: Original uncompressed data, if this entry is compressed,
else None
compress: Compression algoithm used (e.g. 'lz4'), 'none' if none
orig_offset: Original offset value read from node
orig_size: Original size value read from node
@ -76,6 +86,7 @@ class Entry(object):
self.pre_reset_size = None
self.uncomp_size = None
self.data = None
self.uncomp_data = None
self.contents_size = 0
self.align = None
self.align_size = None
@ -180,6 +191,9 @@ class Entry(object):
self.expand_size = fdt_util.GetBool(self._node, 'expand-size')
self.missing_msg = fdt_util.GetString(self._node, 'missing-msg')
# This is only supported by blobs and sections at present
self.compress = fdt_util.GetString(self._node, 'compress', 'none')
def GetDefaultFilename(self):
return None
@ -199,11 +213,20 @@ class Entry(object):
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']:
def AddMissingProperties(self, have_image_pos):
"""Add new properties to the device tree as needed for this entry
Args:
have_image_pos: True if this entry has an image position. This can
be False if its parent section is compressed, since compression
groups all entries together into a compressed block of data,
obscuring the start of each individual child entry
"""
for prop in ['offset', 'size']:
if not prop in self._node.props:
state.AddZeroProp(self._node, prop)
if have_image_pos and 'image-pos' not in self._node.props:
state.AddZeroProp(self._node, 'image-pos')
if self.GetImage().allow_repack:
if self.orig_offset is not None:
state.AddZeroProp(self._node, 'orig-offset', True)
@ -221,7 +244,8 @@ class Entry(object):
state.SetInt(self._node, 'offset', self.offset)
state.SetInt(self._node, 'size', self.size)
base = self.section.GetRootSkipAtStart() if self.section else 0
state.SetInt(self._node, 'image-pos', self.image_pos - base)
if self.image_pos is not None:
state.SetInt(self._node, 'image-pos', self.image_pos)
if self.GetImage().allow_repack:
if self.orig_offset is not None:
state.SetInt(self._node, 'orig-offset', self.orig_offset, True)
@ -423,6 +447,12 @@ class Entry(object):
return self._node.path
def GetData(self):
"""Get the contents of an entry
Returns:
bytes content of the entry, excluding any padding. If the entry is
compressed, the compressed data is returned
"""
self.Detail('GetData: size %s' % ToHexSize(self.data))
return self.data
@ -490,7 +520,7 @@ class Entry(object):
"""
pass
def CheckOffset(self):
def CheckEntries(self):
"""Check that the entry offsets are correct
This is used for entries which have extra offset requirements (other
@ -836,3 +866,18 @@ features to produce new behaviours.
list of possible tags, most desirable first
"""
return list(filter(None, [self.missing_msg, self.name, self.etype]))
def CompressData(self, indata):
"""Compress data according to the entry's compression method
Args:
indata: Data to compress
Returns:
Compressed data (first word is the compressed size)
"""
self.uncomp_data = indata
if self.compress != 'none':
self.uncomp_size = len(indata)
data = tools.Compress(indata, self.compress)
return data

View File

@ -33,7 +33,6 @@ class Entry_blob(Entry):
def __init__(self, section, etype, node):
super().__init__(section, etype, node)
self._filename = fdt_util.GetString(self._node, 'filename', self.etype)
self.compress = fdt_util.GetString(self._node, 'compress', 'none')
def ObtainContents(self):
self._filename = self.GetDefaultFilename()
@ -48,12 +47,6 @@ class Entry_blob(Entry):
self.ReadBlobContents()
return True
def CompressData(self, indata):
if self.compress != 'none':
self.uncomp_size = len(indata)
data = tools.Compress(indata, self.compress)
return data
def ReadBlobContents(self):
"""Read blob contents into memory

View File

@ -237,10 +237,10 @@ class Entry_cbfs(Entry):
if entry._cbfs_compress:
entry.uncomp_size = cfile.memlen
def AddMissingProperties(self):
super().AddMissingProperties()
def AddMissingProperties(self, have_image_pos):
super().AddMissingProperties(have_image_pos)
for entry in self._cbfs_entries.values():
entry.AddMissingProperties()
entry.AddMissingProperties(have_image_pos)
if entry._cbfs_compress:
state.AddZeroProp(entry._node, 'uncomp-size')
# Store the 'compress' property, since we don't look at

View File

@ -19,7 +19,7 @@ class Entry_files(Entry_section):
Properties / Entry arguments:
- pattern: Filename pattern to match the files to include
- compress: Compression algorithm to use:
- files-compress: Compression algorithm to use:
none: No compression
lz4: Use lz4 compression (via 'lz4' command-line utility)
@ -36,7 +36,8 @@ class Entry_files(Entry_section):
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._files_compress = fdt_util.GetString(self._node, 'files-compress',
'none')
self._require_matches = fdt_util.GetBool(self._node,
'require-matches')
@ -53,7 +54,7 @@ class Entry_files(Entry_section):
subnode = state.AddSubnode(self._node, name)
state.AddString(subnode, 'type', 'blob')
state.AddString(subnode, 'filename', fname)
state.AddString(subnode, 'compress', self._compress)
state.AddString(subnode, 'compress', self._files_compress)
# Read entries again, now that we have some
self._ReadEntries()

View File

@ -16,6 +16,7 @@ from binman.entry import Entry
from dtoc import fdt_util
from patman import tools
from patman import tout
from patman.tools import ToHexSize
class Entry_section(Entry):
@ -56,7 +57,7 @@ class Entry_section(Entry):
self._end_4gb = False
def ReadNode(self):
"""Read properties from the image node"""
"""Read properties from the section node"""
super().ReadNode()
self._pad_byte = fdt_util.GetInt(self._node, 'pad-byte', 0)
self._sort = fdt_util.GetBool(self._node, 'sort-by-offset')
@ -135,32 +136,113 @@ class Entry_section(Entry):
for entry in self._entries.values():
entry.ExpandEntries()
def AddMissingProperties(self):
def AddMissingProperties(self, have_image_pos):
"""Add new properties to the device tree as needed for this entry"""
super().AddMissingProperties()
super().AddMissingProperties(have_image_pos)
if self.compress != 'none':
have_image_pos = False
for entry in self._entries.values():
entry.AddMissingProperties()
entry.AddMissingProperties(have_image_pos)
def ObtainContents(self):
return self.GetEntryContents()
def GetData(self):
def GetPaddedDataForEntry(self, entry, entry_data):
"""Get the data for an entry including any padding
Gets the entry data and uses the section pad-byte value to add padding
before and after as defined by the pad-before and pad-after properties.
This does not consider alignment.
Args:
entry: Entry to check
Returns:
Contents of the entry along with any pad bytes before and
after it (bytes)
"""
pad_byte = (entry._pad_byte if isinstance(entry, Entry_section)
else self._pad_byte)
data = b''
# Handle padding before the entry
if entry.pad_before:
data += tools.GetBytes(self._pad_byte, entry.pad_before)
# Add in the actual entry data
data += entry_data
# Handle padding after the entry
if entry.pad_after:
data += tools.GetBytes(self._pad_byte, entry.pad_after)
if entry.size:
data += tools.GetBytes(pad_byte, entry.size - len(data))
self.Detail('GetPaddedDataForEntry: size %s' % ToHexSize(self.data))
return data
def _BuildSectionData(self):
"""Build the contents of a section
This places all entries at the right place, dealing with padding before
and after entries. It does not do padding for the section itself (the
pad-before and pad-after properties in the section items) since that is
handled by the parent section.
Returns:
Contents of the section (bytes)
"""
section_data = b''
for entry in self._entries.values():
data = entry.GetData()
base = self.pad_before + (entry.offset or 0) - self._skip_at_start
pad = base - len(section_data) + (entry.pad_before or 0)
data = self.GetPaddedDataForEntry(entry, entry.GetData())
# Handle empty space before the entry
pad = (entry.offset or 0) - self._skip_at_start - len(section_data)
if pad > 0:
section_data += tools.GetBytes(self._pad_byte, pad)
# Add in the actual entry data
section_data += data
if self.size:
pad = self.size - len(section_data)
if pad > 0:
section_data += tools.GetBytes(self._pad_byte, pad)
self.Detail('GetData: %d entries, total size %#x' %
(len(self._entries), len(section_data)))
return section_data
return self.CompressData(section_data)
def GetPaddedData(self, data=None):
"""Get the data for a section including any padding
Gets the section data and uses the parent section's pad-byte value to
add padding before and after as defined by the pad-before and pad-after
properties. If this is a top-level section (i.e. an image), this is the
same as GetData(), since padding is not supported.
This does not consider alignment.
Returns:
Contents of the section along with any pad bytes before and
after it (bytes)
"""
section = self.section or self
if data is None:
data = self.GetData()
return section.GetPaddedDataForEntry(self, data)
def GetData(self):
"""Get the contents of an entry
This builds the contents of the section, stores this as the contents of
the section and returns it
Returns:
bytes content of the section, made up for all all of its subentries.
This excludes any padding. If the section is compressed, the
compressed data is returned
"""
data = self._BuildSectionData()
self.SetContents(data)
return data
def GetOffsets(self):
"""Handle entries that want to set the offset/size of other entries
@ -180,14 +262,25 @@ class Entry_section(Entry):
def Pack(self, offset):
"""Pack all entries into the section"""
self._PackEntries()
return super().Pack(offset)
if self._sort:
self._SortEntries()
self._ExpandEntries()
data = self._BuildSectionData()
self.SetContents(data)
self.CheckSize()
offset = super().Pack(offset)
self.CheckEntries()
return offset
def _PackEntries(self):
"""Pack all entries into the image"""
"""Pack all entries into the section"""
offset = self._skip_at_start
for entry in self._entries.values():
offset = entry.Pack(offset)
self.size = self.CheckSize()
return offset
def _ExpandEntries(self):
"""Expand any entries that are permitted to"""
@ -209,21 +302,22 @@ class Entry_section(Entry):
self._entries[entry._node.name] = entry
def CheckEntries(self):
"""Check that entries do not overlap or extend outside the image"""
if self._sort:
self._SortEntries()
self._ExpandEntries()
"""Check that entries do not overlap or extend outside the section"""
max_size = self.size if self.uncomp_size is None else self.uncomp_size
offset = 0
prev_name = 'None'
for entry in self._entries.values():
entry.CheckOffset()
entry.CheckEntries()
if (entry.offset < self._skip_at_start or
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,
self._skip_at_start))
max_size):
entry.Raise('Offset %#x (%d) size %#x (%d) is outside the '
"section '%s' starting at %#x (%d) "
'of size %#x (%d)' %
(entry.offset, entry.offset, entry.size, entry.size,
self._node.path, self._skip_at_start,
self._skip_at_start, max_size, max_size))
if entry.offset < offset and entry.size:
entry.Raise("Offset %#x (%d) overlaps with previous entry '%s' "
"ending at %#x (%d)" %
@ -243,8 +337,9 @@ class Entry_section(Entry):
def SetImagePos(self, image_pos):
super().SetImagePos(image_pos)
for entry in self._entries.values():
entry.SetImagePos(image_pos + self.offset)
if self.compress == 'none':
for entry in self._entries.values():
entry.SetImagePos(image_pos + self.offset)
def ProcessContents(self):
sizes_ok_base = super(Entry_section, self).ProcessContents()
@ -254,9 +349,6 @@ class Entry_section(Entry):
sizes_ok = False
return sizes_ok and sizes_ok_base
def CheckOffset(self):
self.CheckEntries()
def WriteMap(self, fd, indent):
"""Write a map of the section to a .map file
@ -412,7 +504,7 @@ class Entry_section(Entry):
return None
def GetEntryContents(self):
"""Call ObtainContents() for the section
"""Call ObtainContents() for each entry in the section
"""
todo = self._entries.values()
for passnum in range(3):
@ -454,18 +546,13 @@ class Entry_section(Entry):
for name, info in offset_dict.items():
self._SetEntryOffsetSize(name, *info)
def CheckSize(self):
"""Check that the image contents does not exceed its size, etc."""
contents_size = 0
for entry in self._entries.values():
contents_size = max(contents_size, entry.offset + entry.size)
contents_size -= self._skip_at_start
contents_size = len(self.data)
size = self.size
if not size:
size = self.pad_before + contents_size + self.pad_after
data = self.GetPaddedData(self.data)
size = len(data)
size = tools.Align(size, self.align_size)
if self.size and contents_size > self.size:

View File

@ -81,6 +81,7 @@ class Entry_u_boot_ucode(Entry_blob):
if fdt_entry:
break
if not fdt_entry:
self.data = b''
return True
if not fdt_entry.ready:
return False

View File

@ -70,6 +70,7 @@ VBLOCK_DATA = b'vblk'
FILES_DATA = (b"sorry I'm late\nOh, don't bother apologising, I'm " +
b"sorry you're alive\n")
COMPRESS_DATA = b'compress xxxxxxxxxxxxxxxxxxxxxx data'
COMPRESS_DATA_BIG = COMPRESS_DATA * 2
REFCODE_DATA = b'refcode'
FSP_M_DATA = b'fsp_m'
FSP_S_DATA = b'fsp_s'
@ -175,6 +176,7 @@ class TestFunctional(unittest.TestCase):
os.path.join(cls._indir, 'files'))
TestFunctional._MakeInputFile('compress', COMPRESS_DATA)
TestFunctional._MakeInputFile('compress_big', COMPRESS_DATA_BIG)
TestFunctional._MakeInputFile('bl31.bin', ATF_BL31_DATA)
TestFunctional._MakeInputFile('scp.bin', SCP_DATA)
@ -785,9 +787,9 @@ class TestFunctional(unittest.TestCase):
def testPackExtra(self):
"""Test that extra packing feature works as expected"""
retcode = self._DoTestFile('009_pack_extra.dts')
data, _, _, out_dtb_fname = self._DoReadFileDtb('009_pack_extra.dts',
update_dtb=True)
self.assertEqual(0, retcode)
self.assertIn('image', control.images)
image = control.images['image']
entries = image.GetEntries()
@ -799,34 +801,82 @@ class TestFunctional(unittest.TestCase):
self.assertEqual(0, entry.offset)
self.assertEqual(3, entry.pad_before)
self.assertEqual(3 + 5 + len(U_BOOT_DATA), entry.size)
self.assertEqual(U_BOOT_DATA, entry.data)
self.assertEqual(tools.GetBytes(0, 3) + U_BOOT_DATA +
tools.GetBytes(0, 5), data[:entry.size])
pos = entry.size
# Second u-boot has an aligned size, but it has no effect
self.assertIn('u-boot-align-size-nop', entries)
entry = entries['u-boot-align-size-nop']
self.assertEqual(12, entry.offset)
self.assertEqual(4, entry.size)
self.assertEqual(pos, entry.offset)
self.assertEqual(len(U_BOOT_DATA), entry.size)
self.assertEqual(U_BOOT_DATA, entry.data)
self.assertEqual(U_BOOT_DATA, data[pos:pos + entry.size])
pos += entry.size
# Third u-boot has an aligned size too
self.assertIn('u-boot-align-size', entries)
entry = entries['u-boot-align-size']
self.assertEqual(16, entry.offset)
self.assertEqual(pos, entry.offset)
self.assertEqual(32, entry.size)
self.assertEqual(U_BOOT_DATA, entry.data)
self.assertEqual(U_BOOT_DATA + tools.GetBytes(0, 32 - len(U_BOOT_DATA)),
data[pos:pos + entry.size])
pos += entry.size
# Fourth u-boot has an aligned end
self.assertIn('u-boot-align-end', entries)
entry = entries['u-boot-align-end']
self.assertEqual(48, entry.offset)
self.assertEqual(16, entry.size)
self.assertEqual(U_BOOT_DATA, entry.data[:len(U_BOOT_DATA)])
self.assertEqual(U_BOOT_DATA + tools.GetBytes(0, 16 - len(U_BOOT_DATA)),
data[pos:pos + entry.size])
pos += entry.size
# Fifth u-boot immediately afterwards
self.assertIn('u-boot-align-both', entries)
entry = entries['u-boot-align-both']
self.assertEqual(64, entry.offset)
self.assertEqual(64, entry.size)
self.assertEqual(U_BOOT_DATA, entry.data[:len(U_BOOT_DATA)])
self.assertEqual(U_BOOT_DATA + tools.GetBytes(0, 64 - len(U_BOOT_DATA)),
data[pos:pos + entry.size])
self.CheckNoGaps(entries)
self.assertEqual(128, image.size)
dtb = fdt.Fdt(out_dtb_fname)
dtb.Scan()
props = self._GetPropTree(dtb, ['size', 'offset', 'image-pos'])
expected = {
'image-pos': 0,
'offset': 0,
'size': 128,
'u-boot:image-pos': 0,
'u-boot:offset': 0,
'u-boot:size': 3 + 5 + len(U_BOOT_DATA),
'u-boot-align-size-nop:image-pos': 12,
'u-boot-align-size-nop:offset': 12,
'u-boot-align-size-nop:size': 4,
'u-boot-align-size:image-pos': 16,
'u-boot-align-size:offset': 16,
'u-boot-align-size:size': 32,
'u-boot-align-end:image-pos': 48,
'u-boot-align-end:offset': 48,
'u-boot-align-end:size': 16,
'u-boot-align-both:image-pos': 64,
'u-boot-align-both:offset': 64,
'u-boot-align-both:size': 64,
}
self.assertEqual(expected, props)
def testPackAlignPowerOf2(self):
"""Test that invalid entry alignment is detected"""
with self.assertRaises(ValueError) as e:
@ -970,8 +1020,9 @@ class TestFunctional(unittest.TestCase):
"""Test that the end-at-4gb property checks for offset boundaries"""
with self.assertRaises(ValueError) as e:
self._DoTestFile('028_pack_4gb_outside.dts')
self.assertIn("Node '/binman/u-boot': Offset 0x0 (0) is outside "
"the section starting at 0xffffffe0 (4294967264)",
self.assertIn("Node '/binman/u-boot': Offset 0x0 (0) size 0x4 (4) "
"is outside the section '/binman' starting at "
'0xffffffe0 (4294967264) of size 0x20 (32)',
str(e.exception))
def testPackX86Rom(self):
@ -1761,6 +1812,20 @@ class TestFunctional(unittest.TestCase):
props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
orig = self._decompress(data)
self.assertEquals(COMPRESS_DATA, orig)
# Do a sanity check on various fields
image = control.images['image']
entries = image.GetEntries()
self.assertEqual(1, len(entries))
entry = entries['blob']
self.assertEqual(COMPRESS_DATA, entry.uncomp_data)
self.assertEqual(len(COMPRESS_DATA), entry.uncomp_size)
orig = self._decompress(entry.data)
self.assertEqual(orig, entry.uncomp_data)
self.assertEqual(image.data, entry.data)
expected = {
'blob:uncomp-size': len(COMPRESS_DATA),
'blob:size': len(data),
@ -1961,7 +2026,7 @@ class TestFunctional(unittest.TestCase):
self.assertTrue(os.path.exists(map_fname))
map_data = tools.ReadFile(map_fname, binary=False)
self.assertEqual('''ImagePos Offset Size Name
<none> 00000000 00000007 main-section
<none> 00000000 00000008 main-section
<none> 00000000 00000004 u-boot
<none> 00000003 00000004 u-boot-align
''', map_data)
@ -3530,12 +3595,39 @@ class TestFunctional(unittest.TestCase):
def testPadInSections(self):
"""Test pad-before, pad-after for entries in sections"""
data = self._DoReadFile('166_pad_in_sections.dts')
data, _, _, out_dtb_fname = self._DoReadFileDtb(
'166_pad_in_sections.dts', update_dtb=True)
expected = (U_BOOT_DATA + tools.GetBytes(ord('!'), 12) +
U_BOOT_DATA + tools.GetBytes(ord('!'), 6) +
U_BOOT_DATA)
self.assertEqual(expected, data)
dtb = fdt.Fdt(out_dtb_fname)
dtb.Scan()
props = self._GetPropTree(dtb, ['size', 'image-pos', 'offset'])
expected = {
'image-pos': 0,
'offset': 0,
'size': 12 + 6 + 3 * len(U_BOOT_DATA),
'section:image-pos': 0,
'section:offset': 0,
'section:size': 12 + 6 + 3 * len(U_BOOT_DATA),
'section/before:image-pos': 0,
'section/before:offset': 0,
'section/before:size': len(U_BOOT_DATA),
'section/u-boot:image-pos': 4,
'section/u-boot:offset': 4,
'section/u-boot:size': 12 + len(U_BOOT_DATA) + 6,
'section/after:image-pos': 26,
'section/after:offset': 26,
'section/after:size': len(U_BOOT_DATA),
}
self.assertEqual(expected, props)
def testFitImageSubentryAlignment(self):
"""Test relative alignability of FIT image subentries"""
entry_args = {
@ -3737,14 +3829,14 @@ class TestFunctional(unittest.TestCase):
def testEnvironmentNoSize(self):
"""Test that a missing 'size' property is detected"""
with self.assertRaises(ValueError) as e:
data = self._DoTestFile('175_env_no_size.dts')
self._DoTestFile('175_env_no_size.dts')
self.assertIn("'u-boot-env' entry must have a size property",
str(e.exception))
def testEnvironmentTooSmall(self):
"""Test handling of an environment that does not fit"""
with self.assertRaises(ValueError) as e:
data = self._DoTestFile('176_env_too_small.dts')
self._DoTestFile('176_env_too_small.dts')
# checksum, start byte, environment with \0 terminator, final \0
need = 4 + 1 + len(ENV_DATA) + 1 + 1
@ -3752,6 +3844,301 @@ class TestFunctional(unittest.TestCase):
self.assertIn("too small to hold data (need %#x more bytes)" % short,
str(e.exception))
def testSkipAtStart(self):
"""Test handling of skip-at-start section"""
data = self._DoReadFile('177_skip_at_start.dts')
self.assertEqual(U_BOOT_DATA, data)
image = control.images['image']
entries = image.GetEntries()
section = entries['section']
self.assertEqual(0, section.offset)
self.assertEqual(len(U_BOOT_DATA), section.size)
self.assertEqual(U_BOOT_DATA, section.GetData())
entry = section.GetEntries()['u-boot']
self.assertEqual(16, entry.offset)
self.assertEqual(len(U_BOOT_DATA), entry.size)
self.assertEqual(U_BOOT_DATA, entry.data)
def testSkipAtStartPad(self):
"""Test handling of skip-at-start section with padded entry"""
data = self._DoReadFile('178_skip_at_start_pad.dts')
before = tools.GetBytes(0, 8)
after = tools.GetBytes(0, 4)
all = before + U_BOOT_DATA + after
self.assertEqual(all, data)
image = control.images['image']
entries = image.GetEntries()
section = entries['section']
self.assertEqual(0, section.offset)
self.assertEqual(len(all), section.size)
self.assertEqual(all, section.GetData())
entry = section.GetEntries()['u-boot']
self.assertEqual(16, entry.offset)
self.assertEqual(len(all), entry.size)
self.assertEqual(U_BOOT_DATA, entry.data)
def testSkipAtStartSectionPad(self):
"""Test handling of skip-at-start section with padding"""
data = self._DoReadFile('179_skip_at_start_section_pad.dts')
before = tools.GetBytes(0, 8)
after = tools.GetBytes(0, 4)
all = before + U_BOOT_DATA + after
self.assertEqual(all, data)
image = control.images['image']
entries = image.GetEntries()
section = entries['section']
self.assertEqual(0, section.offset)
self.assertEqual(len(all), section.size)
self.assertEqual(U_BOOT_DATA, section.data)
self.assertEqual(all, section.GetPaddedData())
entry = section.GetEntries()['u-boot']
self.assertEqual(16, entry.offset)
self.assertEqual(len(U_BOOT_DATA), entry.size)
self.assertEqual(U_BOOT_DATA, entry.data)
def testSectionPad(self):
"""Testing padding with sections"""
data = self._DoReadFile('180_section_pad.dts')
expected = (tools.GetBytes(ord('&'), 3) +
tools.GetBytes(ord('!'), 5) +
U_BOOT_DATA +
tools.GetBytes(ord('!'), 1) +
tools.GetBytes(ord('&'), 2))
self.assertEqual(expected, data)
def testSectionAlign(self):
"""Testing alignment with sections"""
data = self._DoReadFileDtb('181_section_align.dts', map=True)[0]
expected = (b'\0' + # fill section
tools.GetBytes(ord('&'), 1) + # padding to section align
b'\0' + # fill section
tools.GetBytes(ord('!'), 3) + # padding to u-boot align
U_BOOT_DATA +
tools.GetBytes(ord('!'), 4) + # padding to u-boot size
tools.GetBytes(ord('!'), 4)) # padding to section size
self.assertEqual(expected, data)
def testCompressImage(self):
"""Test compression of the entire image"""
self._CheckLz4()
data, _, _, out_dtb_fname = self._DoReadFileDtb(
'182_compress_image.dts', use_real_dtb=True, update_dtb=True)
dtb = fdt.Fdt(out_dtb_fname)
dtb.Scan()
props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
'uncomp-size'])
orig = self._decompress(data)
self.assertEquals(COMPRESS_DATA + U_BOOT_DATA, orig)
# Do a sanity check on various fields
image = control.images['image']
entries = image.GetEntries()
self.assertEqual(2, len(entries))
entry = entries['blob']
self.assertEqual(COMPRESS_DATA, entry.data)
self.assertEqual(len(COMPRESS_DATA), entry.size)
entry = entries['u-boot']
self.assertEqual(U_BOOT_DATA, entry.data)
self.assertEqual(len(U_BOOT_DATA), entry.size)
self.assertEqual(len(data), image.size)
self.assertEqual(COMPRESS_DATA + U_BOOT_DATA, image.uncomp_data)
self.assertEqual(len(COMPRESS_DATA + U_BOOT_DATA), image.uncomp_size)
orig = self._decompress(image.data)
self.assertEqual(orig, image.uncomp_data)
expected = {
'blob:offset': 0,
'blob:size': len(COMPRESS_DATA),
'u-boot:offset': len(COMPRESS_DATA),
'u-boot:size': len(U_BOOT_DATA),
'uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
'offset': 0,
'image-pos': 0,
'size': len(data),
}
self.assertEqual(expected, props)
def testCompressImageLess(self):
"""Test compression where compression reduces the image size"""
self._CheckLz4()
data, _, _, out_dtb_fname = self._DoReadFileDtb(
'183_compress_image_less.dts', use_real_dtb=True, update_dtb=True)
dtb = fdt.Fdt(out_dtb_fname)
dtb.Scan()
props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
'uncomp-size'])
orig = self._decompress(data)
self.assertEquals(COMPRESS_DATA + COMPRESS_DATA + U_BOOT_DATA, orig)
# Do a sanity check on various fields
image = control.images['image']
entries = image.GetEntries()
self.assertEqual(2, len(entries))
entry = entries['blob']
self.assertEqual(COMPRESS_DATA_BIG, entry.data)
self.assertEqual(len(COMPRESS_DATA_BIG), entry.size)
entry = entries['u-boot']
self.assertEqual(U_BOOT_DATA, entry.data)
self.assertEqual(len(U_BOOT_DATA), entry.size)
self.assertEqual(len(data), image.size)
self.assertEqual(COMPRESS_DATA_BIG + U_BOOT_DATA, image.uncomp_data)
self.assertEqual(len(COMPRESS_DATA_BIG + U_BOOT_DATA),
image.uncomp_size)
orig = self._decompress(image.data)
self.assertEqual(orig, image.uncomp_data)
expected = {
'blob:offset': 0,
'blob:size': len(COMPRESS_DATA_BIG),
'u-boot:offset': len(COMPRESS_DATA_BIG),
'u-boot:size': len(U_BOOT_DATA),
'uncomp-size': len(COMPRESS_DATA_BIG + U_BOOT_DATA),
'offset': 0,
'image-pos': 0,
'size': len(data),
}
self.assertEqual(expected, props)
def testCompressSectionSize(self):
"""Test compression of a section with a fixed size"""
self._CheckLz4()
data, _, _, out_dtb_fname = self._DoReadFileDtb(
'184_compress_section_size.dts', use_real_dtb=True, update_dtb=True)
dtb = fdt.Fdt(out_dtb_fname)
dtb.Scan()
props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
'uncomp-size'])
orig = self._decompress(data)
self.assertEquals(COMPRESS_DATA + U_BOOT_DATA, orig)
expected = {
'section/blob:offset': 0,
'section/blob:size': len(COMPRESS_DATA),
'section/u-boot:offset': len(COMPRESS_DATA),
'section/u-boot:size': len(U_BOOT_DATA),
'section:offset': 0,
'section:image-pos': 0,
'section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
'section:size': 0x30,
'offset': 0,
'image-pos': 0,
'size': 0x30,
}
self.assertEqual(expected, props)
def testCompressSection(self):
"""Test compression of a section with no fixed size"""
self._CheckLz4()
data, _, _, out_dtb_fname = self._DoReadFileDtb(
'185_compress_section.dts', use_real_dtb=True, update_dtb=True)
dtb = fdt.Fdt(out_dtb_fname)
dtb.Scan()
props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
'uncomp-size'])
orig = self._decompress(data)
self.assertEquals(COMPRESS_DATA + U_BOOT_DATA, orig)
expected = {
'section/blob:offset': 0,
'section/blob:size': len(COMPRESS_DATA),
'section/u-boot:offset': len(COMPRESS_DATA),
'section/u-boot:size': len(U_BOOT_DATA),
'section:offset': 0,
'section:image-pos': 0,
'section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
'section:size': len(data),
'offset': 0,
'image-pos': 0,
'size': len(data),
}
self.assertEqual(expected, props)
def testCompressExtra(self):
"""Test compression of a section with no fixed size"""
self._CheckLz4()
data, _, _, out_dtb_fname = self._DoReadFileDtb(
'186_compress_extra.dts', use_real_dtb=True, update_dtb=True)
dtb = fdt.Fdt(out_dtb_fname)
dtb.Scan()
props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
'uncomp-size'])
base = data[len(U_BOOT_DATA):]
self.assertEquals(U_BOOT_DATA, base[:len(U_BOOT_DATA)])
rest = base[len(U_BOOT_DATA):]
# Check compressed data
section1 = self._decompress(rest)
expect1 = tools.Compress(COMPRESS_DATA + U_BOOT_DATA, 'lz4')
self.assertEquals(expect1, rest[:len(expect1)])
self.assertEquals(COMPRESS_DATA + U_BOOT_DATA, section1)
rest1 = rest[len(expect1):]
section2 = self._decompress(rest1)
expect2 = tools.Compress(COMPRESS_DATA + COMPRESS_DATA, 'lz4')
self.assertEquals(expect2, rest1[:len(expect2)])
self.assertEquals(COMPRESS_DATA + COMPRESS_DATA, section2)
rest2 = rest1[len(expect2):]
expect_size = (len(U_BOOT_DATA) + len(U_BOOT_DATA) + len(expect1) +
len(expect2) + len(U_BOOT_DATA))
#self.assertEquals(expect_size, len(data))
#self.assertEquals(U_BOOT_DATA, rest2)
self.maxDiff = None
expected = {
'u-boot:offset': 0,
'u-boot:image-pos': 0,
'u-boot:size': len(U_BOOT_DATA),
'base:offset': len(U_BOOT_DATA),
'base:image-pos': len(U_BOOT_DATA),
'base:size': len(data) - len(U_BOOT_DATA),
'base/u-boot:offset': 0,
'base/u-boot:image-pos': len(U_BOOT_DATA),
'base/u-boot:size': len(U_BOOT_DATA),
'base/u-boot2:offset': len(U_BOOT_DATA) + len(expect1) +
len(expect2),
'base/u-boot2:image-pos': len(U_BOOT_DATA) * 2 + len(expect1) +
len(expect2),
'base/u-boot2:size': len(U_BOOT_DATA),
'base/section:offset': len(U_BOOT_DATA),
'base/section:image-pos': len(U_BOOT_DATA) * 2,
'base/section:size': len(expect1),
'base/section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
'base/section/blob:offset': 0,
'base/section/blob:size': len(COMPRESS_DATA),
'base/section/u-boot:offset': len(COMPRESS_DATA),
'base/section/u-boot:size': len(U_BOOT_DATA),
'base/section2:offset': len(U_BOOT_DATA) + len(expect1),
'base/section2:image-pos': len(U_BOOT_DATA) * 2 + len(expect1),
'base/section2:size': len(expect2),
'base/section2:uncomp-size': len(COMPRESS_DATA + COMPRESS_DATA),
'base/section2/blob:offset': 0,
'base/section2/blob:size': len(COMPRESS_DATA),
'base/section2/blob2:offset': len(COMPRESS_DATA),
'base/section2/blob2:size': len(COMPRESS_DATA),
'offset': 0,
'image-pos': 0,
'size': len(data),
}
self.assertEqual(expected, props)
if __name__ == "__main__":
unittest.main()

View File

@ -146,7 +146,7 @@ class Image(section.Entry_section):
fname = tools.GetOutputFilename(self._filename)
tout.Info("Writing image to '%s'" % fname)
with open(fname, 'wb') as fd:
data = self.GetData()
data = self.GetPaddedData()
fd.write(data)
tout.Info("Wrote %#x bytes" % len(data))

View File

@ -28,7 +28,7 @@
u-boot-align-both {
type = "u-boot";
align= <64>;
align = <64>;
align-end = <128>;
};
};

View File

@ -5,7 +5,7 @@
binman {
files {
pattern = "files/*.dat";
compress = "lz4";
files-compress = "lz4";
};
};
};

View File

@ -0,0 +1,19 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2018 NXP
*/
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
section {
skip-at-start = <16>;
u-boot {
};
};
};
};

View File

@ -0,0 +1,21 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2018 NXP
*/
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
section {
skip-at-start = <16>;
u-boot {
pad-before = <8>;
pad-after = <4>;
};
};
};
};

View File

@ -0,0 +1,22 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2018 NXP
*/
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
section {
skip-at-start = <16>;
pad-before = <8>;
pad-after = <4>;
u-boot {
};
};
};
};

View File

@ -0,0 +1,27 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
pad-byte = <0x26>;
section@0 {
read-only;
/* Padding for the section uses the 0x26 pad byte */
pad-before = <3>;
pad-after = <2>;
/* Set the padding byte for entries, i.e. u-boot */
pad-byte = <0x21>;
u-boot {
pad-before = <5>;
pad-after = <1>;
};
};
};
};

View File

@ -0,0 +1,34 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
pad-byte = <0x26>;
fill {
size = <1>;
};
section@1 {
read-only;
/* Padding for the section uses the 0x26 pad byte */
align = <2>;
align-size = <0x10>;
/* Set the padding byte for entries, i.e. u-boot */
pad-byte = <0x21>;
fill {
size = <1>;
};
u-boot {
align = <4>;
align-size = <8>;
};
};
};
};

View File

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

View File

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

View File

@ -0,0 +1,17 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
binman {
section {
size = <0x30>;
compress = "lz4";
blob {
filename = "compress";
};
u-boot {
};
};
};
};

View File

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

View File

@ -0,0 +1,37 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
binman {
u-boot {
};
base {
type = "section";
u-boot {
};
section {
compress = "lz4";
blob {
filename = "compress";
};
u-boot {
};
};
section2 {
type = "section";
compress = "lz4";
blob {
filename = "compress";
};
blob2 {
type = "blob";
filename = "compress";
};
};
u-boot2 {
type = "u-boot";
};
};
};
};

View File

@ -54,6 +54,13 @@ VAL_PREFIX = 'dtv_'
# phandles is len(args). This is a list of integers.
PhandleInfo = collections.namedtuple('PhandleInfo', ['max_args', 'args'])
# Holds a single phandle link, allowing a C struct value to be assigned to point
# to a device
#
# var_node: C variable to assign (e.g. 'dtv_mmc.clocks[0].node')
# dev_name: Name of device to assign to (e.g. 'clock')
PhandleLink = collections.namedtuple('PhandleLink', ['var_node', 'dev_name'])
def conv_name_to_c(name):
"""Convert a device-tree name to a C identifier
@ -136,7 +143,8 @@ class DtbPlatdata(object):
Properties:
_fdt: Fdt object, referencing the device tree
_dtb_fname: Filename of the input device tree binary file
_valid_nodes: A list of Node object with compatible strings
_valid_nodes: A list of Node object with compatible strings. The list
is ordered by conv_name_to_c(node.name)
_include_disabled: true to include nodes marked status = "disabled"
_outfile: The current output file (sys.stdout or a real file)
_warning_disabled: true to disable warnings about driver names not found
@ -146,7 +154,6 @@ class DtbPlatdata(object):
key: Driver alias declared with
U_BOOT_DRIVER_ALIAS(driver_alias, driver_name)
value: Driver name declared with U_BOOT_DRIVER(driver_name)
_links: List of links to be included in dm_populate_phandle_data()
_drivers_additional: List of additional drivers to use during scanning
"""
def __init__(self, dtb_fname, include_disabled, warning_disabled,
@ -160,7 +167,6 @@ class DtbPlatdata(object):
self._lines = []
self._drivers = []
self._driver_aliases = {}
self._links = []
self._drivers_additional = drivers_additional
def get_normalized_compat_name(self, node):
@ -359,23 +365,24 @@ class DtbPlatdata(object):
"""
self._fdt = fdt.FdtScan(self._dtb_fname)
def scan_node(self, root):
def scan_node(self, root, valid_nodes):
"""Scan a node and subnodes to build a tree of node and phandle info
This adds each node to self._valid_nodes.
Args:
root: Root node for scan
valid_nodes: List of Node objects to add to
"""
for node in root.subnodes:
if 'compatible' in node.props:
status = node.props.get('status')
if (not self._include_disabled and not status or
status.value != 'disabled'):
self._valid_nodes.append(node)
valid_nodes.append(node)
# recurse to handle any subnodes
self.scan_node(node)
self.scan_node(node, valid_nodes)
def scan_tree(self):
"""Scan the device tree for useful information
@ -384,8 +391,12 @@ class DtbPlatdata(object):
_valid_nodes: A list of nodes we wish to consider include in the
platform data
"""
self._valid_nodes = []
return self.scan_node(self._fdt.GetRoot())
valid_nodes = []
self.scan_node(self._fdt.GetRoot(), valid_nodes)
self._valid_nodes = sorted(valid_nodes,
key=lambda x: conv_name_to_c(x.name))
for idx, node in enumerate(self._valid_nodes):
node.idx = idx
@staticmethod
def get_num_cells(node):
@ -458,8 +469,15 @@ class DtbPlatdata(object):
Once the widest property is determined, all other properties are
updated to match that width.
Returns:
dict containing structures:
key (str): Node name, as a C identifier
value: dict containing structure fields:
key (str): Field name
value: Prop object with field information
"""
structs = {}
structs = collections.OrderedDict()
for node in self._valid_nodes:
node_name, _ = self.get_normalized_compat_name(node)
fields = {}
@ -528,6 +546,14 @@ class DtbPlatdata(object):
This writes out the body of a header file consisting of structure
definitions for node in self._valid_nodes. See the documentation in
doc/driver-model/of-plat.rst for more information.
Args:
structs: dict containing structures:
key (str): Node name, as a C identifier
value: dict containing structure fields:
key (str): Field name
value: Prop object with field information
"""
self.out_header()
self.out('#include <stdbool.h>\n')
@ -560,8 +586,51 @@ class DtbPlatdata(object):
Args:
node: node to output
"""
def _output_list(node, prop):
"""Output the C code for a devicetree property that holds a list
Args:
node (fdt.Node): Node to output
prop (fdt.Prop): Prop to output
"""
self.buf('{')
vals = []
# For phandles, output a reference to the platform data
# of the target node.
info = self.get_phandle_argc(prop, node.name)
if info:
# Process the list as pairs of (phandle, id)
pos = 0
item = 0
for args in info.args:
phandle_cell = prop.value[pos]
phandle = fdt_util.fdt32_to_cpu(phandle_cell)
target_node = self._fdt.phandle_to_node[phandle]
name = conv_name_to_c(target_node.name)
arg_values = []
for i in range(args):
arg_values.append(
str(fdt_util.fdt32_to_cpu(prop.value[pos + 1 + i])))
pos += 1 + args
vals.append('\t{%d, {%s}}' % (target_node.idx,
', '.join(arg_values)))
item += 1
for val in vals:
self.buf('\n\t\t%s,' % val)
else:
for val in prop.value:
vals.append(get_value(prop.type, val))
# Put 8 values per line to avoid very long lines.
for i in range(0, len(vals), 8):
if i:
self.buf(',\n\t\t')
self.buf(', '.join(vals[i:i + 8]))
self.buf('}')
struct_name, _ = self.get_normalized_compat_name(node)
var_name = conv_name_to_c(node.name)
self.buf('/* Node %s index %d */\n' % (node.path, node.idx))
self.buf('static struct %s%s %s%s = {\n' %
(STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
for pname in sorted(node.props):
@ -573,46 +642,7 @@ class DtbPlatdata(object):
# Special handling for lists
if isinstance(prop.value, list):
self.buf('{')
vals = []
# For phandles, output a reference to the platform data
# of the target node.
info = self.get_phandle_argc(prop, node.name)
if info:
# Process the list as pairs of (phandle, id)
pos = 0
item = 0
for args in info.args:
phandle_cell = prop.value[pos]
phandle = fdt_util.fdt32_to_cpu(phandle_cell)
target_node = self._fdt.phandle_to_node[phandle]
name = conv_name_to_c(target_node.name)
arg_values = []
for i in range(args):
arg_values.append(str(fdt_util.fdt32_to_cpu(prop.value[pos + 1 + i])))
pos += 1 + args
# node member is filled with NULL as the real value
# will be update at run-time during dm_init_and_scan()
# by dm_populate_phandle_data()
vals.append('\t{NULL, {%s}}' % (', '.join(arg_values)))
var_node = '%s%s.%s[%d].node' % \
(VAL_PREFIX, var_name, member_name, item)
# Save the the link information to be use to define
# dm_populate_phandle_data()
self._links.append({'var_node': var_node, 'dev_name': name})
item += 1
for val in vals:
self.buf('\n\t\t%s,' % val)
else:
for val in prop.value:
vals.append(get_value(prop.type, val))
# Put 8 values per line to avoid very long lines.
for i in range(0, len(vals), 8):
if i:
self.buf(',\n\t\t')
self.buf(', '.join(vals[i:i + 8]))
self.buf('}')
_output_list(node, prop)
else:
self.buf(get_value(prop.type, prop.value))
self.buf(',\n')
@ -623,6 +653,10 @@ class DtbPlatdata(object):
self.buf('\t.name\t\t= "%s",\n' % struct_name)
self.buf('\t.platdata\t= &%s%s,\n' % (VAL_PREFIX, var_name))
self.buf('\t.platdata_size\t= sizeof(%s%s),\n' % (VAL_PREFIX, var_name))
idx = -1
if node.parent and node.parent in self._valid_nodes:
idx = node.parent.idx
self.buf('\t.parent_idx\t= %d,\n' % idx)
self.buf('};\n')
self.buf('\n')
@ -639,6 +673,9 @@ class DtbPlatdata(object):
information.
"""
self.out_header()
self.out('/* Allow use of U_BOOT_DEVICE() in this file */\n')
self.out('#define DT_PLATDATA_C\n')
self.out('\n')
self.out('#include <common.h>\n')
self.out('#include <dm.h>\n')
self.out('#include <dt-structs.h>\n')
@ -660,9 +697,6 @@ class DtbPlatdata(object):
# nodes using DM_GET_DEVICE
# dtv_dmc_at_xxx.clocks[0].node = DM_GET_DEVICE(clock_controller_at_xxx)
self.buf('void dm_populate_phandle_data(void) {\n')
for l in self._links:
self.buf('\t%s = DM_GET_DEVICE(%s);\n' %
(l['var_node'], l['dev_name']))
self.buf('}\n')
self.out(''.join(self.get_buf()))

View File

@ -41,6 +41,7 @@
u-boot,dm-pre-reloc;
compatible = "sandbox,spl-test";
stringarray = "one";
longbytearray = [09 0a 0b 0c 0d 0e 0f 10];
};
spl-test4 {

View File

@ -129,6 +129,15 @@ class Prop:
specific.
"""
if newprop.type < self.type:
# Special handling to convert an int into bytes
if self.type == TYPE_INT and newprop.type == TYPE_BYTE:
if type(self.value) == list:
new_value = []
for val in self.value:
new_value += [tools.ToChar(by) for by in val]
else:
new_value = [tools.ToChar(by) for by in self.value]
self.value = new_value
self.type = newprop.type
if type(newprop.value) == list and type(self.value) != list:

View File

@ -44,6 +44,9 @@ C_HEADER = '''/*
* This file was generated by dtoc from a .dtb (device tree binary) file.
*/
/* Allow use of U_BOOT_DEVICE() in this file */
#define DT_PLATDATA_C
#include <common.h>
#include <dm.h>
#include <dt-structs.h>
@ -209,6 +212,29 @@ struct dtd_sandbox_spl_test_2 {
with open(output) as infile:
data = infile.read()
self._CheckStrings(C_HEADER + '''
/* Node /i2c@0 index 0 */
static struct dtd_sandbox_i2c_test dtv_i2c_at_0 = {
};
U_BOOT_DEVICE(i2c_at_0) = {
\t.name\t\t= "sandbox_i2c_test",
\t.platdata\t= &dtv_i2c_at_0,
\t.platdata_size\t= sizeof(dtv_i2c_at_0),
\t.parent_idx\t= -1,
};
/* Node /i2c@0/pmic@9 index 1 */
static struct dtd_sandbox_pmic_test dtv_pmic_at_9 = {
\t.low_power\t\t= true,
\t.reg\t\t\t= {0x9, 0x0},
};
U_BOOT_DEVICE(pmic_at_9) = {
\t.name\t\t= "sandbox_pmic_test",
\t.platdata\t= &dtv_pmic_at_9,
\t.platdata_size\t= sizeof(dtv_pmic_at_9),
\t.parent_idx\t= 0,
};
/* Node /spl-test index 2 */
static struct dtd_sandbox_spl_test dtv_spl_test = {
\t.boolval\t\t= true,
\t.bytearray\t\t= {0x6, 0x0, 0x0},
@ -225,15 +251,17 @@ U_BOOT_DEVICE(spl_test) = {
\t.name\t\t= "sandbox_spl_test",
\t.platdata\t= &dtv_spl_test,
\t.platdata_size\t= sizeof(dtv_spl_test),
\t.parent_idx\t= -1,
};
/* Node /spl-test2 index 3 */
static struct dtd_sandbox_spl_test dtv_spl_test2 = {
\t.acpi_name\t\t= "\\\\_SB.GPO0",
\t.bytearray\t\t= {0x1, 0x23, 0x34},
\t.byteval\t\t= 0x8,
\t.intarray\t\t= {0x5, 0x0, 0x0, 0x0},
\t.intval\t\t\t= 0x3,
\t.longbytearray\t\t= {0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
\t.longbytearray\t\t= {0x9, 0xa, 0xb, 0xc, 0x0, 0x0, 0x0, 0x0,
\t\t0x0},
\t.stringarray\t\t= {"another", "multi-word", "message"},
\t.stringval\t\t= "message2",
@ -242,41 +270,30 @@ U_BOOT_DEVICE(spl_test2) = {
\t.name\t\t= "sandbox_spl_test",
\t.platdata\t= &dtv_spl_test2,
\t.platdata_size\t= sizeof(dtv_spl_test2),
\t.parent_idx\t= -1,
};
/* Node /spl-test3 index 4 */
static struct dtd_sandbox_spl_test dtv_spl_test3 = {
\t.longbytearray\t\t= {0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10,
\t\t0x0},
\t.stringarray\t\t= {"one", "", ""},
};
U_BOOT_DEVICE(spl_test3) = {
\t.name\t\t= "sandbox_spl_test",
\t.platdata\t= &dtv_spl_test3,
\t.platdata_size\t= sizeof(dtv_spl_test3),
\t.parent_idx\t= -1,
};
/* Node /spl-test4 index 5 */
static struct dtd_sandbox_spl_test_2 dtv_spl_test4 = {
};
U_BOOT_DEVICE(spl_test4) = {
\t.name\t\t= "sandbox_spl_test_2",
\t.platdata\t= &dtv_spl_test4,
\t.platdata_size\t= sizeof(dtv_spl_test4),
};
static struct dtd_sandbox_i2c_test dtv_i2c_at_0 = {
};
U_BOOT_DEVICE(i2c_at_0) = {
\t.name\t\t= "sandbox_i2c_test",
\t.platdata\t= &dtv_i2c_at_0,
\t.platdata_size\t= sizeof(dtv_i2c_at_0),
};
static struct dtd_sandbox_pmic_test dtv_pmic_at_9 = {
\t.low_power\t\t= true,
\t.reg\t\t\t= {0x9, 0x0},
};
U_BOOT_DEVICE(pmic_at_9) = {
\t.name\t\t= "sandbox_pmic_test",
\t.platdata\t= &dtv_pmic_at_9,
\t.platdata_size\t= sizeof(dtv_pmic_at_9),
\t.parent_idx\t= -1,
};
''' + C_EMPTY_POPULATE_PHANDLE_DATA, data)
@ -300,6 +317,7 @@ struct dtd_sandbox_gpio {
with open(output) as infile:
data = infile.read()
self._CheckStrings(C_HEADER + '''
/* Node /gpios@0 index 0 */
static struct dtd_sandbox_gpio dtv_gpios_at_0 = {
\t.gpio_bank_name\t\t= "a",
\t.gpio_controller\t= true,
@ -309,6 +327,7 @@ U_BOOT_DEVICE(gpios_at_0) = {
\t.name\t\t= "sandbox_gpio",
\t.platdata\t= &dtv_gpios_at_0,
\t.platdata_size\t= sizeof(dtv_gpios_at_0),
\t.parent_idx\t= -1,
};
void dm_populate_phandle_data(void) {
@ -333,12 +352,14 @@ struct dtd_invalid {
with open(output) as infile:
data = infile.read()
self._CheckStrings(C_HEADER + '''
/* Node /spl-test index 0 */
static struct dtd_invalid dtv_spl_test = {
};
U_BOOT_DEVICE(spl_test) = {
\t.name\t\t= "invalid",
\t.platdata\t= &dtv_spl_test,
\t.platdata_size\t= sizeof(dtv_spl_test),
\t.parent_idx\t= -1,
};
void dm_populate_phandle_data(void) {
@ -365,15 +386,7 @@ struct dtd_target {
with open(output) as infile:
data = infile.read()
self._CheckStrings(C_HEADER + '''
static struct dtd_target dtv_phandle_target = {
\t.intval\t\t\t= 0x0,
};
U_BOOT_DEVICE(phandle_target) = {
\t.name\t\t= "target",
\t.platdata\t= &dtv_phandle_target,
\t.platdata_size\t= sizeof(dtv_phandle_target),
};
/* Node /phandle2-target index 0 */
static struct dtd_target dtv_phandle2_target = {
\t.intval\t\t\t= 0x1,
};
@ -381,8 +394,10 @@ U_BOOT_DEVICE(phandle2_target) = {
\t.name\t\t= "target",
\t.platdata\t= &dtv_phandle2_target,
\t.platdata_size\t= sizeof(dtv_phandle2_target),
\t.parent_idx\t= -1,
};
/* Node /phandle3-target index 1 */
static struct dtd_target dtv_phandle3_target = {
\t.intval\t\t\t= 0x2,
};
@ -390,37 +405,48 @@ U_BOOT_DEVICE(phandle3_target) = {
\t.name\t\t= "target",
\t.platdata\t= &dtv_phandle3_target,
\t.platdata_size\t= sizeof(dtv_phandle3_target),
\t.parent_idx\t= -1,
};
/* Node /phandle-target index 4 */
static struct dtd_target dtv_phandle_target = {
\t.intval\t\t\t= 0x0,
};
U_BOOT_DEVICE(phandle_target) = {
\t.name\t\t= "target",
\t.platdata\t= &dtv_phandle_target,
\t.platdata_size\t= sizeof(dtv_phandle_target),
\t.parent_idx\t= -1,
};
/* Node /phandle-source index 2 */
static struct dtd_source dtv_phandle_source = {
\t.clocks\t\t\t= {
\t\t\t{NULL, {}},
\t\t\t{NULL, {11}},
\t\t\t{NULL, {12, 13}},
\t\t\t{NULL, {}},},
\t\t\t{4, {}},
\t\t\t{0, {11}},
\t\t\t{1, {12, 13}},
\t\t\t{4, {}},},
};
U_BOOT_DEVICE(phandle_source) = {
\t.name\t\t= "source",
\t.platdata\t= &dtv_phandle_source,
\t.platdata_size\t= sizeof(dtv_phandle_source),
\t.parent_idx\t= -1,
};
/* Node /phandle-source2 index 3 */
static struct dtd_source dtv_phandle_source2 = {
\t.clocks\t\t\t= {
\t\t\t{NULL, {}},},
\t\t\t{4, {}},},
};
U_BOOT_DEVICE(phandle_source2) = {
\t.name\t\t= "source",
\t.platdata\t= &dtv_phandle_source2,
\t.platdata_size\t= sizeof(dtv_phandle_source2),
\t.parent_idx\t= -1,
};
void dm_populate_phandle_data(void) {
\tdtv_phandle_source.clocks[0].node = DM_GET_DEVICE(phandle_target);
\tdtv_phandle_source.clocks[1].node = DM_GET_DEVICE(phandle2_target);
\tdtv_phandle_source.clocks[2].node = DM_GET_DEVICE(phandle3_target);
\tdtv_phandle_source.clocks[3].node = DM_GET_DEVICE(phandle_target);
\tdtv_phandle_source2.clocks[0].node = DM_GET_DEVICE(phandle_target);
}
''', data)
@ -448,26 +474,29 @@ struct dtd_target {
with open(output) as infile:
data = infile.read()
self._CheckStrings(C_HEADER + '''
/* Node /phandle-target index 1 */
static struct dtd_target dtv_phandle_target = {
};
U_BOOT_DEVICE(phandle_target) = {
\t.name\t\t= "target",
\t.platdata\t= &dtv_phandle_target,
\t.platdata_size\t= sizeof(dtv_phandle_target),
\t.parent_idx\t= -1,
};
/* Node /phandle-source2 index 0 */
static struct dtd_source dtv_phandle_source2 = {
\t.clocks\t\t\t= {
\t\t\t{NULL, {}},},
\t\t\t{1, {}},},
};
U_BOOT_DEVICE(phandle_source2) = {
\t.name\t\t= "source",
\t.platdata\t= &dtv_phandle_source2,
\t.platdata_size\t= sizeof(dtv_phandle_source2),
\t.parent_idx\t= -1,
};
void dm_populate_phandle_data(void) {
\tdtv_phandle_source2.clocks[0].node = DM_GET_DEVICE(phandle_target);
}
''', data)
@ -479,15 +508,7 @@ void dm_populate_phandle_data(void) {
with open(output) as infile:
data = infile.read()
self._CheckStrings(C_HEADER + '''
static struct dtd_target dtv_phandle_target = {
\t.intval\t\t\t= 0x0,
};
U_BOOT_DEVICE(phandle_target) = {
\t.name\t\t= "target",
\t.platdata\t= &dtv_phandle_target,
\t.platdata_size\t= sizeof(dtv_phandle_target),
};
/* Node /phandle2-target index 0 */
static struct dtd_target dtv_phandle2_target = {
\t.intval\t\t\t= 0x1,
};
@ -495,8 +516,10 @@ U_BOOT_DEVICE(phandle2_target) = {
\t.name\t\t= "target",
\t.platdata\t= &dtv_phandle2_target,
\t.platdata_size\t= sizeof(dtv_phandle2_target),
\t.parent_idx\t= -1,
};
/* Node /phandle3-target index 1 */
static struct dtd_target dtv_phandle3_target = {
\t.intval\t\t\t= 0x2,
};
@ -504,37 +527,48 @@ U_BOOT_DEVICE(phandle3_target) = {
\t.name\t\t= "target",
\t.platdata\t= &dtv_phandle3_target,
\t.platdata_size\t= sizeof(dtv_phandle3_target),
\t.parent_idx\t= -1,
};
/* Node /phandle-target index 4 */
static struct dtd_target dtv_phandle_target = {
\t.intval\t\t\t= 0x0,
};
U_BOOT_DEVICE(phandle_target) = {
\t.name\t\t= "target",
\t.platdata\t= &dtv_phandle_target,
\t.platdata_size\t= sizeof(dtv_phandle_target),
\t.parent_idx\t= -1,
};
/* Node /phandle-source index 2 */
static struct dtd_source dtv_phandle_source = {
\t.cd_gpios\t\t= {
\t\t\t{NULL, {}},
\t\t\t{NULL, {11}},
\t\t\t{NULL, {12, 13}},
\t\t\t{NULL, {}},},
\t\t\t{4, {}},
\t\t\t{0, {11}},
\t\t\t{1, {12, 13}},
\t\t\t{4, {}},},
};
U_BOOT_DEVICE(phandle_source) = {
\t.name\t\t= "source",
\t.platdata\t= &dtv_phandle_source,
\t.platdata_size\t= sizeof(dtv_phandle_source),
\t.parent_idx\t= -1,
};
/* Node /phandle-source2 index 3 */
static struct dtd_source dtv_phandle_source2 = {
\t.cd_gpios\t\t= {
\t\t\t{NULL, {}},},
\t\t\t{4, {}},},
};
U_BOOT_DEVICE(phandle_source2) = {
\t.name\t\t= "source",
\t.platdata\t= &dtv_phandle_source2,
\t.platdata_size\t= sizeof(dtv_phandle_source2),
\t.parent_idx\t= -1,
};
void dm_populate_phandle_data(void) {
\tdtv_phandle_source.cd_gpios[0].node = DM_GET_DEVICE(phandle_target);
\tdtv_phandle_source.cd_gpios[1].node = DM_GET_DEVICE(phandle2_target);
\tdtv_phandle_source.cd_gpios[2].node = DM_GET_DEVICE(phandle3_target);
\tdtv_phandle_source.cd_gpios[3].node = DM_GET_DEVICE(phandle_target);
\tdtv_phandle_source2.cd_gpios[0].node = DM_GET_DEVICE(phandle_target);
}
''', data)
@ -581,6 +615,7 @@ struct dtd_test3 {
with open(output) as infile:
data = infile.read()
self._CheckStrings(C_HEADER + '''
/* Node /test1 index 0 */
static struct dtd_test1 dtv_test1 = {
\t.reg\t\t\t= {0x1234, 0x5678},
};
@ -588,8 +623,10 @@ U_BOOT_DEVICE(test1) = {
\t.name\t\t= "test1",
\t.platdata\t= &dtv_test1,
\t.platdata_size\t= sizeof(dtv_test1),
\t.parent_idx\t= -1,
};
/* Node /test2 index 1 */
static struct dtd_test2 dtv_test2 = {
\t.reg\t\t\t= {0x1234567890123456, 0x9876543210987654},
};
@ -597,8 +634,10 @@ U_BOOT_DEVICE(test2) = {
\t.name\t\t= "test2",
\t.platdata\t= &dtv_test2,
\t.platdata_size\t= sizeof(dtv_test2),
\t.parent_idx\t= -1,
};
/* Node /test3 index 2 */
static struct dtd_test3 dtv_test3 = {
\t.reg\t\t\t= {0x1234567890123456, 0x9876543210987654, 0x2, 0x3},
};
@ -606,6 +645,7 @@ U_BOOT_DEVICE(test3) = {
\t.name\t\t= "test3",
\t.platdata\t= &dtv_test3,
\t.platdata_size\t= sizeof(dtv_test3),
\t.parent_idx\t= -1,
};
''' + C_EMPTY_POPULATE_PHANDLE_DATA, data)
@ -630,6 +670,7 @@ struct dtd_test2 {
with open(output) as infile:
data = infile.read()
self._CheckStrings(C_HEADER + '''
/* Node /test1 index 0 */
static struct dtd_test1 dtv_test1 = {
\t.reg\t\t\t= {0x1234, 0x5678},
};
@ -637,8 +678,10 @@ U_BOOT_DEVICE(test1) = {
\t.name\t\t= "test1",
\t.platdata\t= &dtv_test1,
\t.platdata_size\t= sizeof(dtv_test1),
\t.parent_idx\t= -1,
};
/* Node /test2 index 1 */
static struct dtd_test2 dtv_test2 = {
\t.reg\t\t\t= {0x12345678, 0x98765432, 0x2, 0x3},
};
@ -646,6 +689,7 @@ U_BOOT_DEVICE(test2) = {
\t.name\t\t= "test2",
\t.platdata\t= &dtv_test2,
\t.platdata_size\t= sizeof(dtv_test2),
\t.parent_idx\t= -1,
};
''' + C_EMPTY_POPULATE_PHANDLE_DATA, data)
@ -673,6 +717,7 @@ struct dtd_test3 {
with open(output) as infile:
data = infile.read()
self._CheckStrings(C_HEADER + '''
/* Node /test1 index 0 */
static struct dtd_test1 dtv_test1 = {
\t.reg\t\t\t= {0x123400000000, 0x5678},
};
@ -680,8 +725,10 @@ U_BOOT_DEVICE(test1) = {
\t.name\t\t= "test1",
\t.platdata\t= &dtv_test1,
\t.platdata_size\t= sizeof(dtv_test1),
\t.parent_idx\t= -1,
};
/* Node /test2 index 1 */
static struct dtd_test2 dtv_test2 = {
\t.reg\t\t\t= {0x1234567890123456, 0x98765432},
};
@ -689,8 +736,10 @@ U_BOOT_DEVICE(test2) = {
\t.name\t\t= "test2",
\t.platdata\t= &dtv_test2,
\t.platdata_size\t= sizeof(dtv_test2),
\t.parent_idx\t= -1,
};
/* Node /test3 index 2 */
static struct dtd_test3 dtv_test3 = {
\t.reg\t\t\t= {0x1234567890123456, 0x98765432, 0x2, 0x3},
};
@ -698,6 +747,7 @@ U_BOOT_DEVICE(test3) = {
\t.name\t\t= "test3",
\t.platdata\t= &dtv_test3,
\t.platdata_size\t= sizeof(dtv_test3),
\t.parent_idx\t= -1,
};
''' + C_EMPTY_POPULATE_PHANDLE_DATA, data)
@ -725,6 +775,7 @@ struct dtd_test3 {
with open(output) as infile:
data = infile.read()
self._CheckStrings(C_HEADER + '''
/* Node /test1 index 0 */
static struct dtd_test1 dtv_test1 = {
\t.reg\t\t\t= {0x1234, 0x567800000000},
};
@ -732,8 +783,10 @@ U_BOOT_DEVICE(test1) = {
\t.name\t\t= "test1",
\t.platdata\t= &dtv_test1,
\t.platdata_size\t= sizeof(dtv_test1),
\t.parent_idx\t= -1,
};
/* Node /test2 index 1 */
static struct dtd_test2 dtv_test2 = {
\t.reg\t\t\t= {0x12345678, 0x9876543210987654},
};
@ -741,8 +794,10 @@ U_BOOT_DEVICE(test2) = {
\t.name\t\t= "test2",
\t.platdata\t= &dtv_test2,
\t.platdata_size\t= sizeof(dtv_test2),
\t.parent_idx\t= -1,
};
/* Node /test3 index 2 */
static struct dtd_test3 dtv_test3 = {
\t.reg\t\t\t= {0x12345678, 0x9876543210987654, 0x2, 0x3},
};
@ -750,6 +805,7 @@ U_BOOT_DEVICE(test3) = {
\t.name\t\t= "test3",
\t.platdata\t= &dtv_test3,
\t.platdata_size\t= sizeof(dtv_test3),
\t.parent_idx\t= -1,
};
''' + C_EMPTY_POPULATE_PHANDLE_DATA, data)
@ -792,6 +848,7 @@ struct dtd_sandbox_spl_test {
with open(output) as infile:
data = infile.read()
self._CheckStrings(C_HEADER + '''
/* Node /spl-test index 0 */
static struct dtd_sandbox_spl_test dtv_spl_test = {
\t.intval\t\t\t= 0x1,
};
@ -799,8 +856,10 @@ U_BOOT_DEVICE(spl_test) = {
\t.name\t\t= "sandbox_spl_test",
\t.platdata\t= &dtv_spl_test,
\t.platdata_size\t= sizeof(dtv_spl_test),
\t.parent_idx\t= -1,
};
/* Node /spl-test2 index 1 */
static struct dtd_sandbox_spl_test dtv_spl_test2 = {
\t.intarray\t\t= 0x5,
};
@ -808,6 +867,7 @@ U_BOOT_DEVICE(spl_test2) = {
\t.name\t\t= "sandbox_spl_test",
\t.platdata\t= &dtv_spl_test2,
\t.platdata_size\t= sizeof(dtv_spl_test2),
\t.parent_idx\t= -1,
};
''' + C_EMPTY_POPULATE_PHANDLE_DATA, data)

View File

@ -298,6 +298,7 @@ class TestProp(unittest.TestCase):
def testWiden(self):
"""Test widening of values"""
node2 = self.dtb.GetNode('/spl-test2')
node3 = self.dtb.GetNode('/spl-test3')
prop = self.node.props['intval']
# No action
@ -316,11 +317,20 @@ class TestProp(unittest.TestCase):
# byte array, it should turn into an array.
prop = self.node.props['longbytearray']
prop2 = node2.props['longbytearray']
prop3 = node3.props['longbytearray']
self.assertFalse(isinstance(prop2.value, list))
self.assertEqual(4, len(prop2.value))
self.assertEqual(b'\x09\x0a\x0b\x0c', prop2.value)
prop2.Widen(prop)
self.assertTrue(isinstance(prop2.value, list))
self.assertEqual(9, len(prop2.value))
self.assertEqual(['\x09', '\x0a', '\x0b', '\x0c', '\0',
'\0', '\0', '\0', '\0'], prop2.value)
prop3.Widen(prop)
self.assertTrue(isinstance(prop3.value, list))
self.assertEqual(9, len(prop3.value))
self.assertEqual(['\x09', '\x0a', '\x0b', '\x0c', '\x0d',
'\x0e', '\x0f', '\x10', '\0'], prop3.value)
# Similarly for a string array
prop = self.node.props['stringval']