nman external-symbol improvements

Driver model memory-usage reporting
 patman test-reporting improvements
 Add bloblist design goals
 -----BEGIN PGP SIGNATURE-----
 
 iQFFBAABCgAvFiEEslwAIq+Gp8wWVbYnfxc6PpAIreYFAmK7BHwRHHNqZ0BjaHJv
 bWl1bS5vcmcACgkQfxc6PpAIrealpwgAlCLCdEiSncV0t+Q9zvdtAQStvZdjD6CW
 VhpCS38pWUA0ckKYq/gmmlE2nW6i7pb4RdiOkpOhKdwFQI7SwbF2GdcU2yv08PMm
 qWHTRDaTm0SiGXUU462+A1Bj/aXPH86uEE9bCYu1FYtRrEtNf6aAeWF3pqNJv4fy
 CFB9OYyAfMEdywCtW2dCjS4y1FiI95Y2Jvg7lPGLVayHDyuavLSMKC8QEsVS4mR0
 91nNLhs6agko/H2i0QXle1lLjkvTIH3VR6dn/CMVjD+goJdDCk7rltHRXHejGun4
 n+a1W3EVv2sSQLwQJ0Kw5e2eBKdlM6Lpzhc0b0iJ2jKzBZeLXS+qBg==
 =2LZO
 -----END PGP SIGNATURE-----

Merge tag 'dm-pull-28jun22' of https://source.denx.de/u-boot/custodians/u-boot-dm into next

nman external-symbol improvements
Driver model memory-usage reporting
patman test-reporting improvements
Add bloblist design goals
This commit is contained in:
Tom Rini 2022-07-08 14:39:07 -04:00
commit 9ff4ce8abc
59 changed files with 1694 additions and 380 deletions

View File

@ -23,7 +23,7 @@ SECTIONS
{
.text : {
. = ALIGN(8);
*(.__image_copy_start)
__image_copy_start = .;
CPUDIR/start.o (.text*)
*(.text*)
} >.sram

View File

@ -17,11 +17,11 @@ config SANDBOX64
config SANDBOX_RAM_SIZE_MB
int "RAM size in MiB"
default 128
default 256
range 64 4095 if !SANDBOX64
range 64 268435456 if SANDBOX64
help
Memory size of the sandbox in MiB. The default value is 128 MiB.
Memory size of the sandbox in MiB. The default value is 256 MiB.
The minimum value is 64 MiB. The maximum value is 4095 MiB for the
32bit sandbox.

View File

@ -331,27 +331,27 @@ void *board_fdt_blob_setup(int *ret)
err = setup_auto_tree(blob);
if (!err)
goto done;
printf("Unable to create empty FDT: %s\n", fdt_strerror(err));
os_printf("Unable to create empty FDT: %s\n", fdt_strerror(err));
*ret = -EINVAL;
goto fail;
}
err = os_get_filesize(fname, &size);
if (err < 0) {
printf("Failed to find FDT file '%s'\n", fname);
os_printf("Failed to find FDT file '%s'\n", fname);
*ret = err;
goto fail;
}
fd = os_open(fname, OS_O_RDONLY);
if (fd < 0) {
printf("Failed to open FDT file '%s'\n", fname);
os_printf("Failed to open FDT file '%s'\n", fname);
*ret = -EACCES;
goto fail;
}
if (os_read(fd, blob, size) != size) {
os_close(fd);
printf("Failed to read FDT file '%s'\n", fname);
os_printf("Failed to read FDT file '%s'\n", fname);
*ret = -EIO;
goto fail;
}

View File

@ -12,6 +12,7 @@
#include <getopt.h>
#include <setjmp.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
@ -54,6 +55,18 @@ ssize_t os_write(int fd, const void *buf, size_t count)
return write(fd, buf, count);
}
int os_printf(const char *fmt, ...)
{
va_list args;
int i;
va_start(args, fmt);
i = vfprintf(stdout, fmt, args);
va_end(args);
return i;
}
off_t os_lseek(int fd, off_t offset, int whence)
{
if (whence == OS_SEEK_SET)

View File

@ -8,20 +8,13 @@
#include <common.h>
#include <command.h>
#include <dm/root.h>
#include <dm/util.h>
static int do_dm_dump_all(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[])
static int do_dm_dump_driver_compat(struct cmd_tbl *cmdtp, int flag, int argc,
char * const argv[])
{
dm_dump_all();
return 0;
}
static int do_dm_dump_uclass(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[])
{
dm_dump_uclass();
dm_dump_driver_compat();
return 0;
}
@ -42,37 +35,68 @@ static int do_dm_dump_drivers(struct cmd_tbl *cmdtp, int flag, int argc,
return 0;
}
static int do_dm_dump_driver_compat(struct cmd_tbl *cmdtp, int flag, int argc,
char * const argv[])
#if CONFIG_IS_ENABLED(DM_STATS)
static int do_dm_dump_mem(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[])
{
dm_dump_driver_compat();
struct dm_stats mem;
dm_get_mem(&mem);
dm_dump_mem(&mem);
return 0;
}
#endif /* DM_STATS */
static int do_dm_dump_static_driver_info(struct cmd_tbl *cmdtp, int flag, int argc,
char * const argv[])
static int do_dm_dump_static_driver_info(struct cmd_tbl *cmdtp, int flag,
int argc, char * const argv[])
{
dm_dump_static_driver_info();
return 0;
}
static int do_dm_dump_tree(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[])
{
dm_dump_tree();
return 0;
}
static int do_dm_dump_uclass(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[])
{
dm_dump_uclass();
return 0;
}
#if CONFIG_IS_ENABLED(DM_STATS)
#define DM_MEM_HELP "dm mem Provide a summary of memory usage\n"
#define DM_MEM U_BOOT_SUBCMD_MKENT(mem, 1, 1, do_dm_dump_mem),
#else
#define DM_MEM_HELP
#define DM_MEM
#endif
#if CONFIG_IS_ENABLED(SYS_LONGHELP)
static char dm_help_text[] =
"tree Dump driver model tree ('*' = activated)\n"
"dm uclass Dump list of instances for each uclass\n"
"compat Dump list of drivers with compatibility strings\n"
"dm devres Dump list of device resources for each device\n"
"dm drivers Dump list of drivers with uclass and instances\n"
"dm compat Dump list of drivers with compatibility strings\n"
"dm static Dump list of drivers with static platform data"
DM_MEM_HELP
"dm static Dump list of drivers with static platform data\n"
"dn tree Dump tree of driver model devices ('*' = activated)\n"
"dm uclass Dump list of instances for each uclass"
;
#endif
U_BOOT_CMD_WITH_SUBCMDS(dm, "Driver model low level access", dm_help_text,
U_BOOT_SUBCMD_MKENT(tree, 1, 1, do_dm_dump_all),
U_BOOT_SUBCMD_MKENT(uclass, 1, 1, do_dm_dump_uclass),
U_BOOT_SUBCMD_MKENT(compat, 1, 1, do_dm_dump_driver_compat),
U_BOOT_SUBCMD_MKENT(devres, 1, 1, do_dm_dump_devres),
U_BOOT_SUBCMD_MKENT(drivers, 1, 1, do_dm_dump_drivers),
U_BOOT_SUBCMD_MKENT(compat, 1, 1, do_dm_dump_driver_compat),
U_BOOT_SUBCMD_MKENT(static, 1, 1, do_dm_dump_static_driver_info));
DM_MEM
U_BOOT_SUBCMD_MKENT(static, 1, 1, do_dm_dump_static_driver_info),
U_BOOT_SUBCMD_MKENT(tree, 1, 1, do_dm_dump_tree),
U_BOOT_SUBCMD_MKENT(uclass, 1, 1, do_dm_dump_uclass));

View File

@ -191,12 +191,25 @@ config SPL_BINMAN_SYMBOLS
depends on SPL_FRAMEWORK && BINMAN
default y
help
This enables use of symbols in SPL which refer to U-Boot, enabling SPL
to obtain the location of U-Boot simply by calling spl_get_image_pos()
and spl_get_image_size().
This enables use of symbols in SPL which refer to other entries in
the same binman image as the SPL. These can be declared with the
binman_sym_declare(type, entry, prop) macro and accessed by the
binman_sym(type, entry, prop) macro defined in binman_sym.h.
For this to work, you must have a U-Boot image in the binman image, so
binman can update SPL with the location of it.
See tools/binman/binman.rst for a detailed explanation.
config SPL_BINMAN_UBOOT_SYMBOLS
bool "Declare binman symbols for U-Boot phases in SPL"
depends on SPL_BINMAN_SYMBOLS
default n if ARCH_IMX8M
default y
help
This enables use of symbols in SPL which refer to U-Boot phases,
enabling SPL to obtain the location and size of its next phase simply
by calling spl_get_image_pos() and spl_get_image_size().
For this to work, you must have all U-Boot phases in the same binman
image, so binman can update SPL with the locations of everything.
source "common/spl/Kconfig.nxp"

View File

@ -9,16 +9,29 @@ config TPL_SIZE_LIMIT
If this value is zero, it is ignored.
config TPL_BINMAN_SYMBOLS
bool "Declare binman symbols in TPL"
depends on SPL_FRAMEWORK && BINMAN
bool "Support binman symbols in TPL"
depends on TPL_FRAMEWORK && BINMAN
default y
help
This enables use of symbols in TPL which refer to U-Boot, enabling TPL
to obtain the location of U-Boot simply by calling spl_get_image_pos()
and spl_get_image_size().
This enables use of symbols in TPL which refer to other entries in
the same binman image as the TPL. These can be declared with the
binman_sym_declare(type, entry, prop) macro and accessed by the
binman_sym(type, entry, prop) macro defined in binman_sym.h.
For this to work, you must have a U-Boot image in the binman image, so
binman can update TPL with the location of it.
See tools/binman/binman.rst for a detailed explanation.
config TPL_BINMAN_UBOOT_SYMBOLS
bool "Declare binman symbols for U-Boot phases in TPL"
depends on TPL_BINMAN_SYMBOLS
default n if ARCH_IMX8M
default y
help
This enables use of symbols in TPL which refer to U-Boot phases,
enabling TPL to obtain the location and size of its next phase simply
by calling spl_get_image_pos() and spl_get_image_size().
For this to work, you must have all U-Boot phases in the same binman
image, so binman can update TPL with the locations of everything.
config TPL_FRAMEWORK
bool "Support TPL based upon the common SPL framework"

View File

@ -198,4 +198,29 @@ config VPL_TEXT_BASE
help
The address in memory that VPL will be running from.
config VPL_BINMAN_SYMBOLS
bool "Declare binman symbols in VPL"
depends on VPL_FRAMEWORK && BINMAN
default y
help
This enables use of symbols in VPL which refer to other entries in
the same binman image as the VPL. These can be declared with the
binman_sym_declare(type, entry, prop) macro and accessed by the
binman_sym(type, entry, prop) macro defined in binman_sym.h.
See tools/binman/binman.rst for a detailed explanation.
config VPL_BINMAN_UBOOT_SYMBOLS
bool "Declare binman symbols for U-Boot phases in VPL"
depends on VPL_BINMAN_SYMBOLS
default n if ARCH_IMX8M
default y
help
This enables use of symbols in VPL which refer to U-Boot phases,
enabling VPL to obtain the location and size of its next phase simply
by calling spl_get_image_pos() and spl_get_image_size().
For this to work, you must have all U-Boot phases in the same binman
image, so binman can update VPL with the locations of everything.
endmenu

View File

@ -34,12 +34,14 @@
#include <malloc.h>
#include <mapmem.h>
#include <dm/root.h>
#include <dm/util.h>
#include <linux/compiler.h>
#include <fdt_support.h>
#include <bootcount.h>
#include <wdt.h>
DECLARE_GLOBAL_DATA_PTR;
DECLARE_BINMAN_MAGIC_SYM;
#ifndef CONFIG_SYS_UBOOT_START
#define CONFIG_SYS_UBOOT_START CONFIG_SYS_TEXT_BASE
@ -51,11 +53,10 @@ DECLARE_GLOBAL_DATA_PTR;
u32 *boot_params_ptr = NULL;
#if CONFIG_IS_ENABLED(BINMAN_SYMBOLS)
#if CONFIG_IS_ENABLED(BINMAN_UBOOT_SYMBOLS)
/* See spl.h for information about this */
binman_sym_declare(ulong, u_boot_any, image_pos);
binman_sym_declare(ulong, u_boot_any, size);
#endif
#ifdef CONFIG_TPL
binman_sym_declare(ulong, u_boot_spl, image_pos);
@ -67,6 +68,8 @@ binman_sym_declare(ulong, u_boot_vpl, image_pos);
binman_sym_declare(ulong, u_boot_vpl, size);
#endif
#endif /* BINMAN_UBOOT_SYMBOLS */
/* Define board data structure */
static struct bd_info bdata __attribute__ ((section(".data")));
@ -149,9 +152,11 @@ void spl_fixup_fdt(void *fdt_blob)
#endif
}
#if CONFIG_IS_ENABLED(BINMAN_SYMBOLS)
ulong spl_get_image_pos(void)
{
if (!CONFIG_IS_ENABLED(BINMAN_UBOOT_SYMBOLS))
return BINMAN_SYM_MISSING;
#ifdef CONFIG_VPL
if (spl_next_phase() == PHASE_VPL)
return binman_sym(ulong, u_boot_vpl, image_pos);
@ -163,6 +168,9 @@ ulong spl_get_image_pos(void)
ulong spl_get_image_size(void)
{
if (!CONFIG_IS_ENABLED(BINMAN_UBOOT_SYMBOLS))
return BINMAN_SYM_MISSING;
#ifdef CONFIG_VPL
if (spl_next_phase() == PHASE_VPL)
return binman_sym(ulong, u_boot_vpl, size);
@ -171,7 +179,6 @@ ulong spl_get_image_size(void)
binman_sym(ulong, u_boot_spl, size) :
binman_sym(ulong, u_boot_any, size);
}
#endif /* BINMAN_SYMBOLS */
ulong spl_get_image_text_base(void)
{
@ -222,7 +229,7 @@ __weak struct image_header *spl_get_load_buffer(ssize_t offset, size_t size)
void spl_set_header_raw_uboot(struct spl_image_info *spl_image)
{
ulong u_boot_pos = binman_sym(ulong, u_boot_any, image_pos);
ulong u_boot_pos = spl_get_image_pos();
spl_image->size = CONFIG_SYS_MONITOR_LEN;
@ -780,6 +787,14 @@ void board_init_r(gd_t *dummy1, ulong dummy2)
bootcount_inc();
/* Dump driver model states to aid analysis */
if (CONFIG_IS_ENABLED(DM_STATS)) {
struct dm_stats mem;
dm_get_mem(&mem);
dm_dump_mem(&mem);
}
memset(&spl_image, '\0', sizeof(spl_image));
#ifdef CONFIG_SYS_SPL_ARGS_ADDR
spl_image.arg = (void *)CONFIG_SYS_SPL_ARGS_ADDR;

View File

@ -70,7 +70,7 @@ static int spl_ram_load_image(struct spl_image_info *spl_image,
load.read = spl_ram_load_read;
spl_load_simple_fit(spl_image, &load, 0, header);
} else {
ulong u_boot_pos = binman_sym(ulong, u_boot_any, image_pos);
ulong u_boot_pos = spl_get_image_pos();
debug("Legacy image\n");
/*

View File

@ -11,6 +11,8 @@ a central structure. Each record of information is assigned a tag so that its
owner can find it and update it. Each record is generally described by a C
structure defined by the code that owns it.
For the design goals of bloblist, please see the comments at the top of the
`bloblist.h` header file.
Passing state through the boot process
--------------------------------------

487
doc/usage/cmd/dm.rst Normal file
View File

@ -0,0 +1,487 @@
.. SPDX-License-Identifier: GPL-2.0+:
dm command
==========
Synopis
-------
::
dm compat
dm devres
dm drivers
dm static
dm tree
dm uclass
Description
-----------
The *dm* command allows viewing information about driver model, including the
tree of devices and list of available uclasses.
dm compat
~~~~~~~~~
This shows the compatible strings associated with each driver. Often there
is only one, but multiple strings are shown on their own line. These strings
can be looked up in the device tree files for each board, to see which driver is
used for each node.
dm devres
~~~~~~~~~
This shows a list of a `devres` (device resource) records for a device. Some
drivers use the devres API to allocate memory, so that it can be freed
automatically (without any code needed in the driver's remove() method) when the
device is removed.
This feature is controlled by CONFIG_DEVRES so no useful output is obtained if
this option is disabled.
dm drivers
~~~~~~~~~~
This shows all the available drivers, their uclass and a list of devices that
use that driver, each on its own line. Drivers with no devices are shown with
`<none>` as the driver name.
dm mem
~~~~~~
This subcommand is really just for debugging and exploration. It can be enabled
with the `CONFIG_DM_STATS` option.
All output is in hex except that in brackets which is decimal.
The output consists of a header shows the size of the main device model
structures (struct udevice, struct driver, struct uclass and struct uc_driver)
and the count and memory used by each (number of devices, memory used by
devices, memory used by device names, number of uclasses, memory used by
uclasses).
After that is a table of information about each type of data that can be
attached to a device, showing the number that have non-null data for that type,
the total size of all that data, the amount of memory used in total, the
amount that would be used if this type uses tags instead and the amount that
would be thus saved.
The `driver_data` line shows the number of devices which have non-NULL driver
data.
The `tags` line shows the number of tags and the memory used by those.
At the bottom is an indication of the total memory usage obtained by undertaking
various changes, none of which is currently implemented in U-Boot:
With tags
Using tags instead of all attached types
Singly linked
Using a singly linked list
driver index
Using a driver index instead of a pointer
uclass index
Using a uclass index instead of a pointer
Drop device name
Using empty device names
dm static
~~~~~~~~~
This shows devices bound by platform data, i.e. not from the device tree. There
are normally none of these, but some boards may use static devices for space
reasons.
dm tree
~~~~~~~
This shows the full tree of devices including the following fields:
uclass
Shows the name of the uclass for the device
Index
Shows the index number of the device, within the uclass. This shows the
ordering within the uclass, but not the sequence number.
Probed
Shows `+` if the device is active
Driver
Shows the name of the driver that this device uses
Name
Shows the device name as well as the tree structure, since child devices are
shown attached to their parent.
dm uclass
~~~~~~~~~
This shows each uclass along with a list of devices in that uclass. The uclass
ID is shown (e.g. uclass 7) and its name.
For each device, the format is::
n name @ a, seq s
where `n` is the index within the uclass, `a` is the address of the device in
memory and `s` is the sequence number of the device.
Examples
--------
dm compat
~~~~~~~~~
This example shows an abridged version of the sandbox output::
=> dm compat
Driver Compatible
--------------------------------
act8846_reg
sandbox_adder sandbox,adder
axi_sandbox_bus sandbox,axi
blk_partition
bootcount-rtc u-boot,bootcount-rtc
...
rockchip_rk805 rockchip,rk805
rockchip,rk808
rockchip,rk809
rockchip,rk816
rockchip,rk817
rockchip,rk818
root_driver
rtc-rv8803 microcrystal,rv8803
epson,rx8803
epson,rx8900
...
wdt_gpio linux,wdt-gpio
wdt_sandbox sandbox,wdt
dm devres
~~~~~~~~~
This example shows an abridged version of the sandbox test output (running
U-Boot with the -T flag)::
=> dm devres
- root_driver
- demo_shape_drv
- demo_simple_drv
- demo_shape_drv
...
- h-test
- devres-test
00000000130194e0 (100 byte) devm_kmalloc_release BIND
- another-test
...
- syscon@3
- a-mux-controller
0000000013025e60 (96 byte) devm_kmalloc_release PROBE
0000000013025f00 (24 byte) devm_kmalloc_release PROBE
0000000013026010 (24 byte) devm_kmalloc_release PROBE
0000000013026070 (24 byte) devm_kmalloc_release PROBE
00000000130260d0 (24 byte) devm_kmalloc_release PROBE
- syscon@3
- a-mux-controller
0000000013026150 (96 byte) devm_kmalloc_release PROBE
00000000130261f0 (24 byte) devm_kmalloc_release PROBE
0000000013026300 (24 byte) devm_kmalloc_release PROBE
0000000013026360 (24 byte) devm_kmalloc_release PROBE
00000000130263c0 (24 byte) devm_kmalloc_release PROBE
- emul-mux-controller
0000000013025fa0 (32 byte) devm_kmalloc_release PROBE
- testfdtm0
- testfdtm1
...
- pinmux_spi0_pins
- pinmux_uart0_pins
- pinctrl-single-bits
0000000013229180 (320 byte) devm_kmalloc_release PROBE
0000000013229300 (40 byte) devm_kmalloc_release PROBE
0000000013229370 (160 byte) devm_kmalloc_release PROBE
000000001322c190 (40 byte) devm_kmalloc_release PROBE
000000001322c200 (32 byte) devm_kmalloc_release PROBE
- pinmux_i2c0_pins
...
- reg@0
- reg@1
dm drivers
~~~~~~~~~~
This example shows an abridged version of the sandbox output::
=> dm drivers
Driver uid uclass Devices
----------------------------------------------------------
act8846_reg 087 regulator <none>
sandbox_adder 021 axi adder
adder
axi_sandbox_bus 021 axi axi@0
...
da7219 061 misc <none>
demo_shape_drv 001 demo demo_shape_drv
demo_shape_drv
demo_shape_drv
demo_simple_drv 001 demo demo_simple_drv
demo_simple_drv
testfdt_drv 003 testfdt a-test
b-test
d-test
e-test
f-test
g-test
another-test
chosen-test
testbus_drv 005 testbus some-bus
mmio-bus@0
mmio-bus@1
dsa-port 039 ethernet lan0
lan1
dsa_sandbox 035 dsa dsa-test
eep_sandbox 121 w1_eeprom <none>
...
pfuze100_regulator 087 regulator <none>
phy_sandbox 077 phy bind-test-child1
gen_phy@0
gen_phy@1
gen_phy@2
pinconfig 078 pinconfig gpios
gpio0
gpio1
gpio2
gpio3
i2c
groups
pins
i2s
spi
cs
pinmux_pwm_pins
pinmux_spi0_pins
pinmux_uart0_pins
pinmux_i2c0_pins
pinmux_lcd_pins
pmc_sandbox 017 power-mgr pci@1e,0
act8846 pmic 080 pmic <none>
max77686_pmic 080 pmic <none>
mc34708_pmic 080 pmic pmic@41
...
wdt_gpio 122 watchdog gpio-wdt
wdt_sandbox 122 watchdog wdt@0
=>
dm mem
~~~~~~
This example shows the sandbox output::
> dm mem
Struct sizes: udevice b0, driver 80, uclass 30, uc_driver 78
Memory: device fe:aea0, device names a16, uclass 5e:11a0
Attached type Count Size Cur Tags Save
--------------- ----- ----- ----- ----- -----
plat 45 a8f aea0 a7c4 6dc (1756)
parent_plat 1a 3b8 aea0 a718 788 (1928)
uclass_plat 3d 6b4 aea0 a7a4 6fc (1788)
priv 8a 68f3 aea0 a8d8 5c8 (1480)
parent_priv 8 38a0 aea0 a6d0 7d0 (2000)
uclass_priv 4e 14a6 aea0 a7e8 6b8 (1720)
driver_data f 0 aea0 a6ec 7b4 (1972)
uclass 6 20
Attached total 191 cb54 3164 (12644)
tags 0 0
Total size: 18b94 (101268)
With tags: 15a30 (88624)
- singly-linked: 14260 (82528)
- driver index: 13b6e (80750)
- uclass index: 1347c (78972)
Drop device name (not SRAM): a16 (2582)
=>
dm static
~~~~~~~~~
This example shows the sandbox output::
=> dm static
Driver Address
---------------------------------
demo_shape_drv 0000562edab8dca0
demo_simple_drv 0000562edab8dca0
demo_shape_drv 0000562edab8dc90
demo_simple_drv 0000562edab8dc80
demo_shape_drv 0000562edab8dc80
test_drv 0000562edaae8840
test_drv 0000562edaae8848
test_drv 0000562edaae8850
sandbox_gpio 0000000000000000
mod_exp_sw 0000000000000000
sandbox_test_proc 0000562edabb5330
qfw_sandbox 0000000000000000
sandbox_timer 0000000000000000
sandbox_serial 0000562edaa8ed00
sysreset_sandbox 0000000000000000
dm tree
-------
This example shows the abridged sandbox output::
=> dm tree
Class Index Probed Driver Name
-----------------------------------------------------------
root 0 [ + ] root_driver root_driver
demo 0 [ ] demo_shape_drv |-- demo_shape_drv
demo 1 [ ] demo_simple_drv |-- demo_simple_drv
demo 2 [ ] demo_shape_drv |-- demo_shape_drv
demo 3 [ ] demo_simple_drv |-- demo_simple_drv
demo 4 [ ] demo_shape_drv |-- demo_shape_drv
test 0 [ ] test_drv |-- test_drv
test 1 [ ] test_drv |-- test_drv
test 2 [ ] test_drv |-- test_drv
..
sysreset 0 [ ] sysreset_sandbox |-- sysreset_sandbox
bootstd 0 [ ] bootstd_drv |-- bootstd
bootmeth 0 [ ] bootmeth_distro | |-- syslinux
bootmeth 1 [ ] bootmeth_efi | `-- efi
reboot-mod 0 [ ] reboot-mode-gpio |-- reboot-mode0
reboot-mod 1 [ ] reboot-mode-rtc |-- reboot-mode@14
...
ethernet 7 [ + ] dsa-port | `-- lan1
pinctrl 0 [ + ] sandbox_pinctrl_gpio |-- pinctrl-gpio
gpio 1 [ + ] sandbox_gpio | |-- base-gpios
nop 0 [ + ] gpio_hog | | |-- hog_input_active_low
nop 1 [ + ] gpio_hog | | |-- hog_input_active_high
nop 2 [ + ] gpio_hog | | |-- hog_output_low
nop 3 [ + ] gpio_hog | | `-- hog_output_high
gpio 2 [ ] sandbox_gpio | |-- extra-gpios
gpio 3 [ ] sandbox_gpio | `-- pinmux-gpios
i2c 0 [ + ] sandbox_i2c |-- i2c@0
i2c_eeprom 0 [ ] i2c_eeprom | |-- eeprom@2c
i2c_eeprom 1 [ ] i2c_eeprom_partition | | `-- bootcount@10
rtc 0 [ ] sandbox_rtc | |-- rtc@43
rtc 1 [ + ] sandbox_rtc | |-- rtc@61
i2c_emul_p 0 [ + ] sandbox_i2c_emul_par | |-- emul
i2c_emul 0 [ ] sandbox_i2c_eeprom_e | | |-- emul-eeprom
i2c_emul 1 [ ] sandbox_i2c_rtc_emul | | |-- emul0
i2c_emul 2 [ + ] sandbox_i2c_rtc_emul | | |-- emull
i2c_emul 3 [ ] sandbox_i2c_pmic_emu | | |-- pmic-emul0
i2c_emul 4 [ ] sandbox_i2c_pmic_emu | | `-- pmic-emul1
pmic 0 [ ] sandbox_pmic | |-- sandbox_pmic
regulator 0 [ ] sandbox_buck | | |-- buck1
regulator 1 [ ] sandbox_buck | | |-- buck2
regulator 2 [ ] sandbox_ldo | | |-- ldo1
regulator 3 [ ] sandbox_ldo | | |-- ldo2
regulator 4 [ ] sandbox_buck | | `-- no_match_by_nodename
pmic 1 [ ] mc34708_pmic | `-- pmic@41
bootcount 0 [ + ] bootcount-rtc |-- bootcount@0
bootcount 1 [ ] bootcount-i2c-eeprom |-- bootcount
...
clk 4 [ ] fixed_clock |-- osc
firmware 0 [ ] sandbox_firmware |-- sandbox-firmware
scmi_agent 0 [ ] sandbox-scmi_agent `-- scmi
clk 5 [ ] scmi_clk |-- protocol@14
reset 2 [ ] scmi_reset_domain |-- protocol@16
nop 8 [ ] scmi_voltage_domain `-- regulators
regulator 5 [ ] scmi_regulator |-- reg@0
regulator 6 [ ] scmi_regulator `-- reg@1
=>
dm uclass
~~~~~~~~~
This example shows the abridged sandbox output::
=> dm uclass
uclass 0: root
0 * root_driver @ 03015460, seq 0
uclass 1: demo
0 demo_shape_drv @ 03015560, seq 0
1 demo_simple_drv @ 03015620, seq 1
2 demo_shape_drv @ 030156e0, seq 2
3 demo_simple_drv @ 030157a0, seq 3
4 demo_shape_drv @ 03015860, seq 4
uclass 2: test
0 test_drv @ 03015980, seq 0
1 test_drv @ 03015a60, seq 1
2 test_drv @ 03015b40, seq 2
...
uclass 20: audio-codec
0 audio-codec @ 030168e0, seq 0
uclass 21: axi
0 adder @ 0301db60, seq 1
1 adder @ 0301dc40, seq 2
2 axi@0 @ 030217d0, seq 0
uclass 22: blk
0 mmc2.blk @ 0301ca00, seq 0
1 mmc1.blk @ 0301cee0, seq 1
2 mmc0.blk @ 0301d380, seq 2
uclass 23: bootcount
0 * bootcount@0 @ 0301b3f0, seq 0
1 bootcount @ 0301b4b0, seq 1
2 bootcount_4@0 @ 0301b570, seq 2
3 bootcount_2@0 @ 0301b630, seq 3
uclass 24: bootdev
0 mmc2.bootdev @ 0301cbb0, seq 0
1 mmc1.bootdev @ 0301d050, seq 1
2 mmc0.bootdev @ 0301d4f0, seq 2
...
uclass 78: pinconfig
0 gpios @ 03022410, seq 0
1 gpio0 @ 030224d0, seq 1
2 gpio1 @ 03022590, seq 2
3 gpio2 @ 03022650, seq 3
4 gpio3 @ 03022710, seq 4
5 i2c @ 030227d0, seq 5
6 groups @ 03022890, seq 6
7 pins @ 03022950, seq 7
8 i2s @ 03022a10, seq 8
9 spi @ 03022ad0, seq 9
10 cs @ 03022b90, seq 10
11 pinmux_pwm_pins @ 03022e10, seq 11
12 pinmux_spi0_pins @ 03022ed0, seq 12
13 pinmux_uart0_pins @ 03022f90, seq 13
14 * pinmux_i2c0_pins @ 03023130, seq 14
15 * pinmux_lcd_pins @ 030231f0, seq 15
...
uclass 119: virtio
0 sandbox_virtio1 @ 030220d0, seq 0
1 sandbox_virtio2 @ 03022190, seq 1
uclass 120: w1
uclass 121: w1_eeprom
uclass 122: watchdog
0 * gpio-wdt @ 0301c070, seq 0
1 * wdt@0 @ 03021710, seq 1
=>

View File

@ -33,6 +33,7 @@ Shell commands
cmd/bootz
cmd/cbsysinfo
cmd/conitrace
cmd/dm
cmd/echo
cmd/env
cmd/event

View File

@ -75,6 +75,27 @@ config DM_DEBUG
help
Say Y here if you want to compile in debug messages in DM core.
config DM_STATS
bool "Collect and show driver model stats"
depends on DM
default y if SANDBOX
help
Enable this to collect and display memory statistics about driver
model. This can help to figure out where all the memory is going and
to find optimisations.
To display the memory stats, use the 'dm mem' command.
config SPL_DM_STATS
bool "Collect and show driver model stats in SPL"
depends on DM_SPL
help
Enable this to collect and display memory statistics about driver
model. This can help to figure out where all the memory is going and
to find optimisations.
The stats are displayed just before SPL boots to the next phase.
config DM_DEVICE_REMOVE
bool "Support device removal"
depends on DM

View File

@ -29,7 +29,7 @@ int device_chld_unbind(struct udevice *dev, struct driver *drv)
assert(dev);
list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) {
device_foreach_child_safe(pos, n, dev) {
if (drv && (pos->driver != drv))
continue;
@ -52,7 +52,7 @@ int device_chld_remove(struct udevice *dev, struct driver *drv,
assert(dev);
list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) {
device_foreach_child_safe(pos, n, dev) {
int ret;
if (drv && (pos->driver != drv))

View File

@ -284,8 +284,7 @@ int device_reparent(struct udevice *dev, struct udevice *new_parent)
assert(dev);
assert(new_parent);
list_for_each_entry_safe(pos, n, &dev->parent->child_head,
sibling_node) {
device_foreach_child_safe(pos, n, dev->parent) {
if (pos->driver != dev->driver)
continue;
@ -675,6 +674,71 @@ void *dev_get_parent_priv(const struct udevice *dev)
return dm_priv_to_rw(dev->parent_priv_);
}
void *dev_get_attach_ptr(const struct udevice *dev, enum dm_tag_t tag)
{
switch (tag) {
case DM_TAG_PLAT:
return dev_get_plat(dev);
case DM_TAG_PARENT_PLAT:
return dev_get_parent_plat(dev);
case DM_TAG_UC_PLAT:
return dev_get_uclass_plat(dev);
case DM_TAG_PRIV:
return dev_get_priv(dev);
case DM_TAG_PARENT_PRIV:
return dev_get_parent_priv(dev);
case DM_TAG_UC_PRIV:
return dev_get_uclass_priv(dev);
default:
return NULL;
}
}
int dev_get_attach_size(const struct udevice *dev, enum dm_tag_t tag)
{
const struct udevice *parent = dev_get_parent(dev);
const struct uclass *uc = dev->uclass;
const struct uclass_driver *uc_drv = uc->uc_drv;
const struct driver *parent_drv = NULL;
int size = 0;
if (parent)
parent_drv = parent->driver;
switch (tag) {
case DM_TAG_PLAT:
size = dev->driver->plat_auto;
break;
case DM_TAG_PARENT_PLAT:
if (parent) {
size = parent_drv->per_child_plat_auto;
if (!size)
size = parent->uclass->uc_drv->per_child_plat_auto;
}
break;
case DM_TAG_UC_PLAT:
size = uc_drv->per_device_plat_auto;
break;
case DM_TAG_PRIV:
size = dev->driver->priv_auto;
break;
case DM_TAG_PARENT_PRIV:
if (parent) {
size = parent_drv->per_child_auto;
if (!size)
size = parent->uclass->uc_drv->per_child_auto;
}
break;
case DM_TAG_UC_PRIV:
size = uc_drv->per_device_auto;
break;
default:
break;
}
return size;
}
static int device_get_device_tail(struct udevice *dev, int ret,
struct udevice **devp)
{
@ -724,7 +788,7 @@ int device_get_child(const struct udevice *parent, int index,
{
struct udevice *dev;
list_for_each_entry(dev, &parent->child_head, sibling_node) {
device_foreach_child(dev, parent) {
if (!index--)
return device_get_device_tail(dev, 0, devp);
}
@ -737,7 +801,7 @@ int device_get_child_count(const struct udevice *parent)
struct udevice *dev;
int count = 0;
list_for_each_entry(dev, &parent->child_head, sibling_node)
device_foreach_child(dev, parent)
count++;
return count;
@ -748,7 +812,7 @@ int device_get_decendent_count(const struct udevice *parent)
const struct udevice *dev;
int count = 1;
list_for_each_entry(dev, &parent->child_head, sibling_node)
device_foreach_child(dev, parent)
count += device_get_decendent_count(dev);
return count;
@ -761,7 +825,7 @@ int device_find_child_by_seq(const struct udevice *parent, int seq,
*devp = NULL;
list_for_each_entry(dev, &parent->child_head, sibling_node) {
device_foreach_child(dev, parent) {
if (dev->seq_ == seq) {
*devp = dev;
return 0;
@ -790,7 +854,7 @@ int device_find_child_by_of_offset(const struct udevice *parent, int of_offset,
*devp = NULL;
list_for_each_entry(dev, &parent->child_head, sibling_node) {
device_foreach_child(dev, parent) {
if (dev_of_offset(dev) == of_offset) {
*devp = dev;
return 0;
@ -819,7 +883,7 @@ static struct udevice *_device_find_global_by_ofnode(struct udevice *parent,
if (ofnode_equal(dev_ofnode(parent), ofnode))
return parent;
list_for_each_entry(dev, &parent->child_head, sibling_node) {
device_foreach_child(dev, parent) {
found = _device_find_global_by_ofnode(dev, ofnode);
if (found)
return found;
@ -897,7 +961,7 @@ int device_find_first_inactive_child(const struct udevice *parent,
struct udevice *dev;
*devp = NULL;
list_for_each_entry(dev, &parent->child_head, sibling_node) {
device_foreach_child(dev, parent) {
if (!device_active(dev) &&
device_get_uclass_id(dev) == uclass_id) {
*devp = dev;
@ -915,7 +979,7 @@ int device_find_first_child_by_uclass(const struct udevice *parent,
struct udevice *dev;
*devp = NULL;
list_for_each_entry(dev, &parent->child_head, sibling_node) {
device_foreach_child(dev, parent) {
if (device_get_uclass_id(dev) == uclass_id) {
*devp = dev;
return 0;
@ -932,7 +996,7 @@ int device_find_child_by_namelen(const struct udevice *parent, const char *name,
*devp = NULL;
list_for_each_entry(dev, &parent->child_head, sibling_node) {
device_foreach_child(dev, parent) {
if (!strncmp(dev->name, name, len) &&
strlen(dev->name) == len) {
*devp = dev;

View File

@ -232,7 +232,7 @@ static void dump_resources(struct udevice *dev, int depth)
(unsigned long)dr->size, dr->name,
devres_phase_name[dr->phase]);
list_for_each_entry(child, &dev->child_head, sibling_node)
device_foreach_child(child, dev)
dump_resources(child, depth + 1);
}

View File

@ -39,13 +39,13 @@ static void show_devices(struct udevice *dev, int depth, int last_flag)
printf("%s\n", dev->name);
list_for_each_entry(child, &dev->child_head, sibling_node) {
device_foreach_child(child, dev) {
is_last = list_is_last(&child->sibling_node, &dev->child_head);
show_devices(child, depth + 1, (last_flag << 1) | is_last);
}
}
void dm_dump_all(void)
void dm_dump_tree(void)
{
struct udevice *root;
@ -89,8 +89,6 @@ void dm_dump_uclass(void)
continue;
printf("uclass %d: %s\n", id, uc->uc_drv->name);
if (list_empty(&uc->dev_head))
continue;
uclass_foreach_dev(dev, uc) {
dm_display_line(dev, i);
i++;
@ -171,8 +169,79 @@ void dm_dump_static_driver_info(void)
puts("Driver Address\n");
puts("---------------------------------\n");
for (entry = drv; entry != drv + n_ents; entry++) {
printf("%-25.25s @%08lx\n", entry->name,
(ulong)map_to_sysmem(entry->plat));
}
for (entry = drv; entry != drv + n_ents; entry++)
printf("%-25.25s %p\n", entry->name, entry->plat);
}
void dm_dump_mem(struct dm_stats *stats)
{
int total, total_delta;
int i;
/* Support SPL printf() */
printf("Struct sizes: udevice %x, driver %x, uclass %x, uc_driver %x\n",
(int)sizeof(struct udevice), (int)sizeof(struct driver),
(int)sizeof(struct uclass), (int)sizeof(struct uclass_driver));
printf("Memory: device %x:%x, device names %x, uclass %x:%x\n",
stats->dev_count, stats->dev_size, stats->dev_name_size,
stats->uc_count, stats->uc_size);
printf("\n");
printf("%-15s %5s %5s %5s %5s %5s\n", "Attached type", "Count",
"Size", "Cur", "Tags", "Save");
printf("%-15s %5s %5s %5s %5s %5s\n", "---------------", "-----",
"-----", "-----", "-----", "-----");
total_delta = 0;
for (i = 0; i < DM_TAG_ATTACH_COUNT; i++) {
int cur_size, new_size, delta;
cur_size = stats->dev_count * sizeof(struct udevice);
new_size = stats->dev_count * (sizeof(struct udevice) -
sizeof(void *));
/*
* Let's assume we can fit each dmtag_node into 32 bits. We can
* limit the 'tiny tags' feature to SPL with
* CONFIG_SPL_SYS_MALLOC_F_LEN <= 64KB, so needing 14 bits to
* point to anything in that region (with 4-byte alignment).
* So:
* 4 bits for tag
* 14 bits for offset of dev
* 14 bits for offset of data
*/
new_size += stats->attach_count[i] * sizeof(u32);
delta = cur_size - new_size;
total_delta += delta;
printf("%-16s %5x %6x %6x %6x %6x (%d)\n", tag_get_name(i),
stats->attach_count[i], stats->attach_size[i],
cur_size, new_size, delta > 0 ? delta : 0, delta);
}
printf("%-16s %5x %6x\n", "uclass", stats->uc_attach_count,
stats->uc_attach_size);
printf("%-16s %5x %6x %5s %5s %6x (%d)\n", "Attached total",
stats->attach_count_total + stats->uc_attach_count,
stats->attach_size_total + stats->uc_attach_size, "", "",
total_delta > 0 ? total_delta : 0, total_delta);
printf("%-16s %5x %6x\n", "tags", stats->tag_count, stats->tag_size);
printf("\n");
printf("Total size: %x (%d)\n", stats->total_size, stats->total_size);
printf("\n");
total = stats->total_size;
total -= total_delta;
printf("With tags: %x (%d)\n", total, total);
/* Use singly linked lists in struct udevice (3 nodes in each) */
total -= sizeof(void *) * 3 * stats->dev_count;
printf("- singly-linked: %x (%d)\n", total, total);
/* Use an index into the struct_driver list instead of a pointer */
total = total + stats->dev_count * (1 - sizeof(void *));
printf("- driver index: %x (%d)\n", total, total);
/* Same with the uclass */
total = total + stats->dev_count * (1 - sizeof(void *));
printf("- uclass index: %x (%d)\n", total, total);
/* Drop the device name */
printf("Drop device name (not SRAM): %x (%d)\n", stats->dev_name_size,
stats->dev_name_size);
}

View File

@ -449,6 +449,59 @@ void dm_get_stats(int *device_countp, int *uclass_countp)
*uclass_countp = uclass_get_count();
}
void dev_collect_stats(struct dm_stats *stats, const struct udevice *parent)
{
const struct udevice *dev;
int i;
stats->dev_count++;
stats->dev_size += sizeof(struct udevice);
stats->dev_name_size += strlen(parent->name) + 1;
for (i = 0; i < DM_TAG_ATTACH_COUNT; i++) {
int size = dev_get_attach_size(parent, i);
if (size ||
(i == DM_TAG_DRIVER_DATA && parent->driver_data)) {
stats->attach_count[i]++;
stats->attach_size[i] += size;
stats->attach_count_total++;
stats->attach_size_total += size;
}
}
list_for_each_entry(dev, &parent->child_head, sibling_node)
dev_collect_stats(stats, dev);
}
void uclass_collect_stats(struct dm_stats *stats)
{
struct uclass *uc;
list_for_each_entry(uc, gd->uclass_root, sibling_node) {
int size;
stats->uc_count++;
stats->uc_size += sizeof(struct uclass);
size = uc->uc_drv->priv_auto;
if (size) {
stats->uc_attach_count++;
stats->uc_attach_size += size;
}
}
}
void dm_get_mem(struct dm_stats *stats)
{
memset(stats, '\0', sizeof(*stats));
dev_collect_stats(stats, gd->dm_root);
uclass_collect_stats(stats);
dev_tag_collect_stats(stats);
stats->total_size = stats->dev_size + stats->uc_size +
stats->attach_size_total + stats->uc_attach_size +
stats->tag_size;
}
#ifdef CONFIG_ACPIGEN
static int root_acpi_get_name(const struct udevice *dev, char *out_name)
{

View File

@ -6,6 +6,7 @@
#include <malloc.h>
#include <asm/global_data.h>
#include <dm/root.h>
#include <dm/tag.h>
#include <linux/err.h>
#include <linux/list.h>
@ -15,6 +16,24 @@ struct udevice;
DECLARE_GLOBAL_DATA_PTR;
static const char *const tag_name[] = {
[DM_TAG_PLAT] = "plat",
[DM_TAG_PARENT_PLAT] = "parent_plat",
[DM_TAG_UC_PLAT] = "uclass_plat",
[DM_TAG_PRIV] = "priv",
[DM_TAG_PARENT_PRIV] = "parent_priv",
[DM_TAG_UC_PRIV] = "uclass_priv",
[DM_TAG_DRIVER_DATA] = "driver_data",
[DM_TAG_EFI] = "efi",
};
const char *tag_get_name(enum dm_tag_t tag)
{
return tag_name[tag];
}
int dev_tag_set_ptr(struct udevice *dev, enum dm_tag_t tag, void *ptr)
{
struct dmtag_node *node;
@ -137,3 +156,13 @@ int dev_tag_del_all(struct udevice *dev)
return -ENOENT;
}
void dev_tag_collect_stats(struct dm_stats *stats)
{
struct dmtag_node *node;
list_for_each_entry(node, &gd->dmtag_list, sibling) {
stats->tag_count++;
stats->tag_size += sizeof(struct dmtag_node);
}
}

View File

@ -48,7 +48,7 @@ static void qfw_sandbox_read_entry_dma(struct udevice *dev, struct qfw_dma *dma)
{
u16 entry;
u32 control = be32_to_cpu(dma->control);
void *address = (void *)be64_to_cpu(dma->address);
void *address = (void *)(uintptr_t)be64_to_cpu(dma->address);
u32 length = be32_to_cpu(dma->length);
struct qfw_sandbox_plat *plat = dev_get_plat(dev);
struct fw_cfg_file *file;

View File

@ -108,8 +108,10 @@ UCLASS_DRIVER(testbus) = {
.child_pre_probe = testbus_child_pre_probe_uclass,
.child_post_probe = testbus_child_post_probe_uclass,
/* This is for dtoc testing only */
.per_device_plat_auto = sizeof(struct dm_test_uclass_priv),
.per_device_auto = sizeof(struct dm_test_uclass_priv),
/* Note: this is for dtoc testing as well as tags*/
.per_device_plat_auto = sizeof(struct dm_test_uclass_plat),
};
static int testfdt_drv_ping(struct udevice *dev, int pingval, int *pingret)

View File

@ -228,9 +228,9 @@ static void handle_read(struct sandbox_flash_priv *priv, ulong lba,
ulong transfer_len)
{
debug("%s: lba=%lx, transfer_len=%lx\n", __func__, lba, transfer_len);
priv->read_len = transfer_len;
if (priv->fd != -1) {
os_lseek(priv->fd, lba * SANDBOX_FLASH_BLOCK_LEN, OS_SEEK_SET);
priv->read_len = transfer_len;
setup_response(priv, priv->buff,
transfer_len * SANDBOX_FLASH_BLOCK_LEN);
} else {
@ -336,6 +336,9 @@ static int sandbox_flash_bulk(struct udevice *dev, struct usb_device *udev,
if (priv->read_len) {
ulong bytes_read;
if (priv->fd == -1)
return -EIO;
bytes_read = os_read(priv->fd, buff, len);
if (bytes_read != len)
return -EIO;

View File

@ -11,9 +11,11 @@
#ifndef __BINMAN_SYM_H
#define __BINMAN_SYM_H
/* BSYM in little endian, keep in sync with tools/binman/elf.py */
#define BINMAN_SYM_MAGIC_VALUE (0x4d595342UL)
#define BINMAN_SYM_MISSING (-1UL)
#ifdef CONFIG_BINMAN
#if CONFIG_IS_ENABLED(BINMAN_SYMBOLS)
/**
* binman_symname() - Internal function to get a binman symbol name
@ -62,6 +64,37 @@
__attribute__((aligned(4), weak, unused, \
section(".binman_sym")))
/**
* _binman_sym_magic - Internal magic symbol for validity checks
*
* When building images, binman fills in this symbol with the magic
* value #defined above. This is used to check at runtime if the
* symbol values were filled in and are OK to use.
*/
extern ulong _binman_sym_magic;
/**
* DECLARE_BINMAN_MAGIC_SYM - Declare the internal magic symbol
*
* This macro declares the _binman_sym_magic symbol so that it exists.
* Declaring it here would cause errors during linking due to multiple
* definitions of the symbol.
*/
#define DECLARE_BINMAN_MAGIC_SYM \
ulong _binman_sym_magic \
__attribute__((aligned(4), section(".binman_sym")))
/**
* BINMAN_SYMS_OK - Check if the symbol values are valid
*
* This macro checks if the magic symbol's value is filled properly,
* which indicates that other symbols are OK to use as well.
*
* Return: 1 if binman symbol values are usable, 0 if not
*/
#define BINMAN_SYMS_OK \
(*(ulong *)&_binman_sym_magic == BINMAN_SYM_MAGIC_VALUE)
/**
* binman_sym() - Access a previously declared symbol
*
@ -72,12 +105,16 @@
* @_type: Type f the symbol (e.g. unsigned long)
* @entry_name: Name of the entry to look for (e.g. 'u_boot_spl')
* @_prop_name: Property value to get from that entry (e.g. 'pos')
* @returns value of that property (filled in by binman)
*
* Return: value of that property (filled in by binman), or
* BINMAN_SYM_MISSING if the value is unavailable
*/
#define binman_sym(_type, _entry_name, _prop_name) \
(*(_type *)&binman_symname(_entry_name, _prop_name))
(BINMAN_SYMS_OK ? \
(*(_type *)&binman_symname(_entry_name, _prop_name)) : \
BINMAN_SYM_MISSING)
#else /* !BINMAN */
#else /* !CONFIG_IS_ENABLED(BINMAN_SYMBOLS) */
#define binman_sym_declare(_type, _entry_name, _prop_name)
@ -85,8 +122,12 @@
#define binman_sym_extern(_type, _entry_name, _prop_name)
#define DECLARE_BINMAN_MAGIC_SYM
#define BINMAN_SYMS_OK (0)
#define binman_sym(_type, _entry_name, _prop_name) BINMAN_SYM_MISSING
#endif /* BINMAN */
#endif /* CONFIG_IS_ENABLED(BINMAN_SYMBOLS) */
#endif

View File

@ -3,8 +3,66 @@
* This provides a standard way of passing information between boot phases
* (TPL -> SPL -> U-Boot proper.)
*
* A list of blobs of data, tagged with their owner. The list resides in memory
* and can be updated by SPL, U-Boot, etc.
* It consists of a list of blobs of data, tagged with their owner / contents.
* The list resides in memory and can be updated by SPL, U-Boot, etc.
*
* Design goals for bloblist:
*
* 1. Small and efficient structure. This avoids UUIDs or 16-byte name fields,
* since a 32-bit tag provides enough space for all the tags we will even need.
* If UUIDs are desired, they can be added inside a particular blob.
*
* 2. Avoids use of pointers, so the structure can be relocated in memory. The
* data in each blob is inline, rather than using pointers.
*
* 3. Bloblist is designed to start small in TPL or SPL, when only a few things
* are needed, like the memory size or whether console output should be enabled.
* Then it can grow in U-Boot proper, e.g. to include space for ACPI tables.
*
* 4. The bloblist structure is simple enough that it can be implemented in a
* small amount of C code. The API does not require use of strings or UUIDs,
* which would add to code size. For Thumb-2 the code size needed in SPL is
* approximately 940 bytes (e.g. for chromebook_bob).
*
* 5. Bloblist uses 16-byte alignment internally and is designed to start on a
* 16-byte boundary. Its headers are multiples of 16 bytes. This makes it easier
* to deal with data structures which need this level of alignment, such as ACPI
* tables. For use in SPL and TPL the alignment can be relaxed, since it can be
* relocated to an aligned address in U-Boot proper.
*
* 6. Bloblist is designed to be passed to Linux as reserved memory. While linux
* doesn't understand the bloblist header, it can be passed the indivdual blobs.
* For example, ACPI tables can reside in a blob and the address of those is
* passed to Linux, without Linux ever being away of the existence of a
* bloblist. Having all the blobs contiguous in memory simplifies the
* reserved-memory space.
*
* 7. Bloblist tags are defined in the enum below. There is an area for
* project-specific stuff (e.g. U-Boot, TF-A) and vendor-specific stuff, e.g.
* something used only on a particular SoC. There is also a private area for
* temporary, local use.
*
* 8. Bloblist includes a simple checksum, so that each boot phase can update
* this and allow the next phase to check that all is well. While the bloblist
* is small, this is quite cheap to calculate. When it grows (e.g. in U-Boot\
* proper), the CPU is likely running faster, so it is not prohibitive. Having
* said that, U-Boot is often the last phase that uses bloblist, so calculating
* the checksum there may not be necessary.
*
* 9. It would be possible to extend bloblist to support a non-contiguous
* structure, e.g. by creating a blob type that points to the next bloblist.
* This does not seem necessary for now. It adds complexity and code. We can
* always just copy it.
*
* 10. Bloblist is designed for simple structures, those that can be defined by
* a single C struct. More complex structures should be passed in a device tree.
* There are some exceptions, chiefly the various binary structures that Intel
* is fond of creating. But device tree provides a dictionary-type format which
* is fairly efficient (for use in U-Boot proper and Linux at least), along with
* a schema and a good set of tools. New formats should be designed around
* device tree rather than creating new binary formats, unless they are needed
* early in boot (where libfdt's 3KB of overhead is too large) and are trival
* enough to be described by a C struct.
*
* Copyright 2018 Google, Inc
* Written by Simon Glass <sjg@chromium.org>

View File

@ -11,6 +11,7 @@
#define _DM_DEVICE_H
#include <dm/ofnode.h>
#include <dm/tag.h>
#include <dm/uclass-id.h>
#include <fdtdec.h>
#include <linker_lists.h>
@ -546,6 +547,30 @@ void *dev_get_parent_priv(const struct udevice *dev);
*/
void *dev_get_uclass_priv(const struct udevice *dev);
/**
* dev_get_attach_ptr() - Get the value of an attached pointed tag
*
* The tag is assumed to hold a pointer, if it exists
*
* @dev: Device to look at
* @tag: Tag to access
* @return value of tag, or NULL if there is no tag of this type
*/
void *dev_get_attach_ptr(const struct udevice *dev, enum dm_tag_t tag);
/**
* dev_get_attach_size() - Get the size of an attached tag
*
* Core tags have an automatic-allocation mechanism where the allocated size is
* defined by the device, parent or uclass. This returns the size associated
* with a particular tag
*
* @dev: Device to look at
* @tag: Tag to access
* @return size of auto-allocated data, 0 if none
*/
int dev_get_attach_size(const struct udevice *dev, enum dm_tag_t tag);
/**
* dev_get_parent() - Get the parent of a device
*

View File

@ -1181,6 +1181,33 @@ int ofnode_write_string(ofnode node, const char *propname, const char *value);
*/
int ofnode_set_enabled(ofnode node, bool value);
/**
* ofnode_get_phy_node() - Get PHY node for a MAC (if not fixed-link)
*
* This function parses PHY handle from the Ethernet controller's ofnode
* (trying all possible PHY handle property names), and returns the PHY ofnode.
*
* Before this is used, ofnode_phy_is_fixed_link() should be checked first, and
* if the result to that is true, this function should not be called.
*
* @eth_node: ofnode belonging to the Ethernet controller
* Return: ofnode of the PHY, if it exists, otherwise an invalid ofnode
*/
ofnode ofnode_get_phy_node(ofnode eth_node);
/**
* ofnode_read_phy_mode() - Read PHY connection type from a MAC node
*
* This function parses the "phy-mode" / "phy-connection-type" property and
* returns the corresponding PHY interface type.
*
* @mac_node: ofnode containing the property
* Return: one of PHY_INTERFACE_MODE_* constants, PHY_INTERFACE_MODE_NA on
* error
*/
phy_interface_t ofnode_read_phy_mode(ofnode mac_node);
#if CONFIG_IS_ENABLED(DM)
/**
* ofnode_conf_read_bool() - Read a boolean value from the U-Boot config
*
@ -1218,30 +1245,21 @@ int ofnode_conf_read_int(const char *prop_name, int default_val);
*/
const char *ofnode_conf_read_str(const char *prop_name);
/**
* ofnode_get_phy_node() - Get PHY node for a MAC (if not fixed-link)
*
* This function parses PHY handle from the Ethernet controller's ofnode
* (trying all possible PHY handle property names), and returns the PHY ofnode.
*
* Before this is used, ofnode_phy_is_fixed_link() should be checked first, and
* if the result to that is true, this function should not be called.
*
* @eth_node: ofnode belonging to the Ethernet controller
* Return: ofnode of the PHY, if it exists, otherwise an invalid ofnode
*/
ofnode ofnode_get_phy_node(ofnode eth_node);
#else /* CONFIG_DM */
static inline bool ofnode_conf_read_bool(const char *prop_name)
{
return false;
}
/**
* ofnode_read_phy_mode() - Read PHY connection type from a MAC node
*
* This function parses the "phy-mode" / "phy-connection-type" property and
* returns the corresponding PHY interface type.
*
* @mac_node: ofnode containing the property
* Return: one of PHY_INTERFACE_MODE_* constants, PHY_INTERFACE_MODE_NA on
* error
*/
phy_interface_t ofnode_read_phy_mode(ofnode mac_node);
static inline int ofnode_conf_read_int(const char *prop_name, int default_val)
{
return default_val;
}
static inline const char *ofnode_conf_read_str(const char *prop_name)
{
return NULL;
}
#endif /* CONFIG_DM */
#endif

View File

@ -9,11 +9,49 @@
#ifndef _DM_ROOT_H_
#define _DM_ROOT_H_
#include <dm/tag.h>
struct udevice;
/* Head of the uclass list if CONFIG_OF_PLATDATA_INST is enabled */
extern struct list_head uclass_head;
/**
* struct dm_stats - Information about driver model memory usage
*
* @total_size: All data
* @dev_count: Number of devices
* @dev_size: Size of all devices (just the struct udevice)
* @dev_name_size: Bytes used by device names
* @uc_count: Number of uclasses
* @uc_size: Size of all uclasses (just the struct uclass)
* @tag_count: Number of tags
* @tag_size: Bytes used by all tags
* @uc_attach_count: Number of uclasses with attached data (priv)
* @uc_attach_size: Total size of that attached data
* @attach_count_total: Total number of attached data items for all udevices and
* uclasses
* @attach_size_total: Total number of bytes of attached data
* @attach_count: Number of devices with attached, for each type
* @attach_size: Total number of bytes of attached data, for each type
*/
struct dm_stats {
int total_size;
int dev_count;
int dev_size;
int dev_name_size;
int uc_count;
int uc_size;
int tag_count;
int tag_size;
int uc_attach_count;
int uc_attach_size;
int attach_count_total;
int attach_size_total;
int attach_count[DM_TAG_ATTACH_COUNT];
int attach_size[DM_TAG_ATTACH_COUNT];
};
/**
* dm_root() - Return pointer to the top of the driver tree
*
@ -141,4 +179,11 @@ static inline int dm_remove_devices_flags(uint flags) { return 0; }
*/
void dm_get_stats(int *device_countp, int *uclass_countp);
/**
* dm_get_mem() - Get stats on memory usage in driver model
*
* @stats: Place to put the information
*/
void dm_get_mem(struct dm_stats *stats);
#endif

View File

@ -10,11 +10,23 @@
#include <linux/list.h>
#include <linux/types.h>
struct dm_stats;
struct udevice;
enum dm_tag_t {
/* Types of core tags that can be attached to devices */
DM_TAG_PLAT,
DM_TAG_PARENT_PLAT,
DM_TAG_UC_PLAT,
DM_TAG_PRIV,
DM_TAG_PARENT_PRIV,
DM_TAG_UC_PRIV,
DM_TAG_DRIVER_DATA,
DM_TAG_ATTACH_COUNT,
/* EFI_LOADER */
DM_TAG_EFI = 0,
DM_TAG_EFI = DM_TAG_ATTACH_COUNT,
DM_TAG_COUNT,
};
@ -107,4 +119,22 @@ int dev_tag_del(struct udevice *dev, enum dm_tag_t tag);
*/
int dev_tag_del_all(struct udevice *dev);
/**
* dev_tag_collect_stats() - Collect information on driver model performance
*
* This collects information on how driver model is performing. For now it only
* includes memory usage
*
* @stats: Place to put the collected information
*/
void dev_tag_collect_stats(struct dm_stats *stats);
/**
* tag_get_name() - Get the name of a tag
*
* @tag: Tag to look up, which must be valid
* Returns: Name of tag
*/
const char *tag_get_name(enum dm_tag_t tag);
#endif /* _DM_TAG_H */

View File

@ -92,6 +92,13 @@ struct dm_test_uclass_priv {
int total_add;
};
/**
* struct dm_test_uclass_plat - private plat data for test uclass
*/
struct dm_test_uclass_plat {
char dummy[32];
};
/**
* struct dm_test_parent_data - parent's information on each child
*

View File

@ -6,6 +6,8 @@
#ifndef __DM_UTIL_H
#define __DM_UTIL_H
struct dm_stats;
#if CONFIG_IS_ENABLED(DM_WARN)
#define dm_warn(fmt...) log(LOGC_DM, LOGL_WARNING, ##fmt)
#else
@ -25,7 +27,7 @@ struct list_head;
int list_count_items(struct list_head *head);
/* Dump out a tree of all devices */
void dm_dump_all(void);
void dm_dump_tree(void);
/* Dump out a list of uclasses and their devices */
void dm_dump_uclass(void);
@ -48,6 +50,13 @@ void dm_dump_driver_compat(void);
/* Dump out a list of drivers with static platform data */
void dm_dump_static_driver_info(void);
/**
* dm_dump_mem() - Dump stats on memory usage in driver model
*
* @mem: Stats to dump
*/
void dm_dump_mem(struct dm_stats *stats);
#if CONFIG_IS_ENABLED(OF_PLATDATA_INST) && CONFIG_IS_ENABLED(READ_ONLY)
void *dm_priv_to_rw(void *priv);
#else

View File

@ -16,6 +16,13 @@
struct rtc_time;
struct sandbox_state;
/**
* os_printf() - print directly to OS console
*
* @format: format string
*/
int os_printf(const char *format, ...);
/**
* Access to the OS read() system call
*

View File

@ -288,6 +288,8 @@ binman_sym_extern(ulong, u_boot_any, image_pos);
binman_sym_extern(ulong, u_boot_any, size);
binman_sym_extern(ulong, u_boot_spl, image_pos);
binman_sym_extern(ulong, u_boot_spl, size);
binman_sym_extern(ulong, u_boot_vpl, image_pos);
binman_sym_extern(ulong, u_boot_vpl, size);
/**
* spl_get_image_pos() - get the image position of the next phase

View File

@ -1275,3 +1275,94 @@ static int dm_test_uclass_find_device(struct unit_test_state *uts)
return 0;
}
DM_TEST(dm_test_uclass_find_device, UT_TESTF_SCAN_FDT);
/* Test getting information about tags attached to devices */
static int dm_test_dev_get_attach(struct unit_test_state *uts)
{
struct udevice *dev;
ut_assertok(uclass_first_device_err(UCLASS_TEST_FDT, &dev));
ut_asserteq_str("a-test", dev->name);
ut_assertnonnull(dev_get_attach_ptr(dev, DM_TAG_PLAT));
ut_assertnonnull(dev_get_attach_ptr(dev, DM_TAG_PRIV));
ut_assertnull(dev_get_attach_ptr(dev, DM_TAG_UC_PRIV));
ut_assertnull(dev_get_attach_ptr(dev, DM_TAG_UC_PLAT));
ut_assertnull(dev_get_attach_ptr(dev, DM_TAG_PARENT_PLAT));
ut_assertnull(dev_get_attach_ptr(dev, DM_TAG_PARENT_PRIV));
ut_asserteq(sizeof(struct dm_test_pdata),
dev_get_attach_size(dev, DM_TAG_PLAT));
ut_asserteq(sizeof(struct dm_test_priv),
dev_get_attach_size(dev, DM_TAG_PRIV));
ut_asserteq(0, dev_get_attach_size(dev, DM_TAG_UC_PRIV));
ut_asserteq(0, dev_get_attach_size(dev, DM_TAG_UC_PLAT));
ut_asserteq(0, dev_get_attach_size(dev, DM_TAG_PARENT_PLAT));
ut_asserteq(0, dev_get_attach_size(dev, DM_TAG_PARENT_PRIV));
return 0;
}
DM_TEST(dm_test_dev_get_attach, UT_TESTF_SCAN_FDT);
/* Test getting information about tags attached to bus devices */
static int dm_test_dev_get_attach_bus(struct unit_test_state *uts)
{
struct udevice *dev, *child;
ut_assertok(uclass_first_device_err(UCLASS_TEST_BUS, &dev));
ut_asserteq_str("some-bus", dev->name);
ut_assertnonnull(dev_get_attach_ptr(dev, DM_TAG_PLAT));
ut_assertnonnull(dev_get_attach_ptr(dev, DM_TAG_PRIV));
ut_assertnonnull(dev_get_attach_ptr(dev, DM_TAG_UC_PRIV));
ut_assertnonnull(dev_get_attach_ptr(dev, DM_TAG_UC_PLAT));
ut_assertnull(dev_get_attach_ptr(dev, DM_TAG_PARENT_PLAT));
ut_assertnull(dev_get_attach_ptr(dev, DM_TAG_PARENT_PRIV));
ut_asserteq(sizeof(struct dm_test_pdata),
dev_get_attach_size(dev, DM_TAG_PLAT));
ut_asserteq(sizeof(struct dm_test_priv),
dev_get_attach_size(dev, DM_TAG_PRIV));
ut_asserteq(sizeof(struct dm_test_uclass_priv),
dev_get_attach_size(dev, DM_TAG_UC_PRIV));
ut_asserteq(sizeof(struct dm_test_uclass_plat),
dev_get_attach_size(dev, DM_TAG_UC_PLAT));
ut_asserteq(0, dev_get_attach_size(dev, DM_TAG_PARENT_PLAT));
ut_asserteq(0, dev_get_attach_size(dev, DM_TAG_PARENT_PRIV));
/* Now try the child of the bus */
ut_assertok(device_first_child_err(dev, &child));
ut_asserteq_str("c-test@5", child->name);
ut_assertnonnull(dev_get_attach_ptr(child, DM_TAG_PLAT));
ut_assertnonnull(dev_get_attach_ptr(child, DM_TAG_PRIV));
ut_assertnull(dev_get_attach_ptr(child, DM_TAG_UC_PRIV));
ut_assertnull(dev_get_attach_ptr(child, DM_TAG_UC_PLAT));
ut_assertnonnull(dev_get_attach_ptr(child, DM_TAG_PARENT_PLAT));
ut_assertnonnull(dev_get_attach_ptr(child, DM_TAG_PARENT_PRIV));
ut_asserteq(sizeof(struct dm_test_pdata),
dev_get_attach_size(child, DM_TAG_PLAT));
ut_asserteq(sizeof(struct dm_test_priv),
dev_get_attach_size(child, DM_TAG_PRIV));
ut_asserteq(0, dev_get_attach_size(child, DM_TAG_UC_PRIV));
ut_asserteq(0, dev_get_attach_size(child, DM_TAG_UC_PLAT));
ut_asserteq(sizeof(struct dm_test_parent_plat),
dev_get_attach_size(child, DM_TAG_PARENT_PLAT));
ut_asserteq(sizeof(struct dm_test_parent_data),
dev_get_attach_size(child, DM_TAG_PARENT_PRIV));
return 0;
}
DM_TEST(dm_test_dev_get_attach_bus, UT_TESTF_SCAN_FDT);
/* Test getting information about tags attached to bus devices */
static int dm_test_dev_get_mem(struct unit_test_state *uts)
{
struct dm_stats stats;
dm_get_mem(&stats);
return 0;
}
DM_TEST(dm_test_dev_get_mem, UT_TESTF_SCAN_FDT);

View File

@ -1,186 +1,191 @@
# SPDX-License-Identifier: GPL-2.0
# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
import os.path
import pytest
""" Test for bind command """
import re
import pytest
def in_tree(response, name, uclass, drv, depth, last_child):
lines = [x.strip() for x in response.splitlines()]
leaf = ''
if depth != 0:
leaf = ' ' + ' ' * (depth - 1) ;
if not last_child:
leaf = leaf + r'\|'
else:
leaf = leaf + '`'
lines = [x.strip() for x in response.splitlines()]
leaf = ''
if depth != 0:
leaf = ' ' + ' ' * (depth - 1)
if not last_child:
leaf = leaf + r'\|'
else:
leaf = leaf + '`'
leaf = leaf + '-- ' + name
line = (r' *{:10.10} *[0-9]* \[ [ +] \] {:20.20} [` |]{}$'
.format(uclass, drv, leaf))
prog = re.compile(line)
for l in lines:
if prog.match(l):
return True
return False
leaf = leaf + '-- ' + name
line = (r' *{:10.10} *[0-9]* \[ [ +] \] {:20.20} [` |]{}$'
.format(uclass, drv, leaf))
prog = re.compile(line)
for l in lines:
if prog.match(l):
return True
return False
@pytest.mark.buildconfigspec('cmd_bind')
def test_bind_unbind_with_node(u_boot_console):
tree = u_boot_console.run_command('dm tree')
assert in_tree(tree, 'bind-test', 'simple_bus', 'simple_bus', 0, True)
assert in_tree(tree, 'bind-test-child1', 'phy', 'phy_sandbox', 1, False)
assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, True)
tree = u_boot_console.run_command('dm tree')
assert in_tree(tree, 'bind-test', 'simple_bus', 'simple_bus', 0, True)
assert in_tree(tree, 'bind-test-child1', 'phy', 'phy_sandbox', 1, False)
assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, True)
#bind usb_ether driver (which has no compatible) to usb@1 node.
##New entry usb_ether should appear in the dm tree
response = u_boot_console.run_command('bind /usb@1 usb_ether')
assert response == ''
tree = u_boot_console.run_command('dm tree')
assert in_tree(tree, 'usb@1', 'ethernet', 'usb_ether', 1, True)
#bind usb_ether driver (which has no compatible) to usb@1 node.
##New entry usb_ether should appear in the dm tree
response = u_boot_console.run_command('bind /usb@1 usb_ether')
assert response == ''
tree = u_boot_console.run_command('dm tree')
assert in_tree(tree, 'usb@1', 'ethernet', 'usb_ether', 1, True)
#Unbind child #1. No error expected and all devices should be there except for bind-test-child1
response = u_boot_console.run_command('unbind /bind-test/bind-test-child1')
assert response == ''
tree = u_boot_console.run_command('dm tree')
assert in_tree(tree, 'bind-test', 'simple_bus', 'simple_bus', 0, True)
assert 'bind-test-child1' not in tree
assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, True)
#Unbind child #1. No error expected and all devices should be there except for bind-test-child1
response = u_boot_console.run_command('unbind /bind-test/bind-test-child1')
assert response == ''
tree = u_boot_console.run_command('dm tree')
assert in_tree(tree, 'bind-test', 'simple_bus', 'simple_bus', 0, True)
assert 'bind-test-child1' not in tree
assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, True)
#bind child #1. No error expected and all devices should be there
response = u_boot_console.run_command('bind /bind-test/bind-test-child1 phy_sandbox')
assert response == ''
tree = u_boot_console.run_command('dm tree')
assert in_tree(tree, 'bind-test', 'simple_bus', 'simple_bus', 0, True)
assert in_tree(tree, 'bind-test-child1', 'phy', 'phy_sandbox', 1, True)
assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, False)
#bind child #1. No error expected and all devices should be there
response = u_boot_console.run_command('bind /bind-test/bind-test-child1 phy_sandbox')
assert response == ''
tree = u_boot_console.run_command('dm tree')
assert in_tree(tree, 'bind-test', 'simple_bus', 'simple_bus', 0, True)
assert in_tree(tree, 'bind-test-child1', 'phy', 'phy_sandbox', 1, True)
assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, False)
#Unbind child #2. No error expected and all devices should be there except for bind-test-child2
response = u_boot_console.run_command('unbind /bind-test/bind-test-child2')
assert response == ''
tree = u_boot_console.run_command('dm tree')
assert in_tree(tree, 'bind-test', 'simple_bus', 'simple_bus', 0, True)
assert in_tree(tree, 'bind-test-child1', 'phy', 'phy_sandbox', 1, True)
assert 'bind-test-child2' not in tree
#Unbind child #2. No error expected and all devices should be there except for bind-test-child2
response = u_boot_console.run_command('unbind /bind-test/bind-test-child2')
assert response == ''
tree = u_boot_console.run_command('dm tree')
assert in_tree(tree, 'bind-test', 'simple_bus', 'simple_bus', 0, True)
assert in_tree(tree, 'bind-test-child1', 'phy', 'phy_sandbox', 1, True)
assert 'bind-test-child2' not in tree
#Bind child #2. No error expected and all devices should be there
response = u_boot_console.run_command('bind /bind-test/bind-test-child2 simple_bus')
assert response == ''
tree = u_boot_console.run_command('dm tree')
assert in_tree(tree, 'bind-test', 'simple_bus', 'simple_bus', 0, True)
assert in_tree(tree, 'bind-test-child1', 'phy', 'phy_sandbox', 1, False)
assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, True)
#Bind child #2. No error expected and all devices should be there
response = u_boot_console.run_command('bind /bind-test/bind-test-child2 simple_bus')
assert response == ''
tree = u_boot_console.run_command('dm tree')
assert in_tree(tree, 'bind-test', 'simple_bus', 'simple_bus', 0, True)
assert in_tree(tree, 'bind-test-child1', 'phy', 'phy_sandbox', 1, False)
assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, True)
#Unbind parent. No error expected. All devices should be removed and unbound
response = u_boot_console.run_command('unbind /bind-test')
assert response == ''
tree = u_boot_console.run_command('dm tree')
assert 'bind-test' not in tree
assert 'bind-test-child1' not in tree
assert 'bind-test-child2' not in tree
#Unbind parent. No error expected. All devices should be removed and unbound
response = u_boot_console.run_command('unbind /bind-test')
assert response == ''
tree = u_boot_console.run_command('dm tree')
assert 'bind-test' not in tree
assert 'bind-test-child1' not in tree
assert 'bind-test-child2' not in tree
#try binding invalid node with valid driver
response = u_boot_console.run_command('bind /not-a-valid-node simple_bus')
assert response != ''
tree = u_boot_console.run_command('dm tree')
assert 'not-a-valid-node' not in tree
#try binding invalid node with valid driver
response = u_boot_console.run_command('bind /not-a-valid-node simple_bus')
assert response != ''
tree = u_boot_console.run_command('dm tree')
assert 'not-a-valid-node' not in tree
#try binding valid node with invalid driver
response = u_boot_console.run_command('bind /bind-test not_a_driver')
assert response != ''
tree = u_boot_console.run_command('dm tree')
assert 'bind-test' not in tree
#try binding valid node with invalid driver
response = u_boot_console.run_command('bind /bind-test not_a_driver')
assert response != ''
tree = u_boot_console.run_command('dm tree')
assert 'bind-test' not in tree
#bind /bind-test. Device should come up as well as its children
response = u_boot_console.run_command('bind /bind-test simple_bus')
assert response == ''
tree = u_boot_console.run_command('dm tree')
assert in_tree(tree, 'bind-test', 'simple_bus', 'simple_bus', 0, True)
assert in_tree(tree, 'bind-test-child1', 'phy', 'phy_sandbox', 1, False)
assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, True)
#bind /bind-test. Device should come up as well as its children
response = u_boot_console.run_command('bind /bind-test simple_bus')
assert response == ''
tree = u_boot_console.run_command('dm tree')
assert in_tree(tree, 'bind-test', 'simple_bus', 'simple_bus', 0, True)
assert in_tree(tree, 'bind-test-child1', 'phy', 'phy_sandbox', 1, False)
assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, True)
response = u_boot_console.run_command('unbind /bind-test')
assert response == ''
response = u_boot_console.run_command('unbind /bind-test')
assert response == ''
def get_next_line(tree, name):
treelines = [x.strip() for x in tree.splitlines() if x.strip()]
child_line = ''
for idx, line in enumerate(treelines):
if ('-- ' + name) in line:
try:
child_line = treelines[idx+1]
except:
pass
break
return child_line
treelines = [x.strip() for x in tree.splitlines() if x.strip()]
child_line = ''
for idx, line in enumerate(treelines):
if '-- ' + name in line:
try:
child_line = treelines[idx+1]
except:
pass
break
return child_line
@pytest.mark.buildconfigspec('cmd_bind')
def test_bind_unbind_with_uclass(u_boot_console):
#bind /bind-test
response = u_boot_console.run_command('bind /bind-test simple_bus')
assert response == ''
#bind /bind-test
response = u_boot_console.run_command('bind /bind-test simple_bus')
assert response == ''
#make sure bind-test-child2 is there and get its uclass/index pair
tree = u_boot_console.run_command('dm tree')
child2_line = [x.strip() for x in tree.splitlines() if '-- bind-test-child2' in x]
assert len(child2_line) == 1
#make sure bind-test-child2 is there and get its uclass/index pair
tree = u_boot_console.run_command('dm tree')
child2_line = [x.strip() for x in tree.splitlines() if '-- bind-test-child2' in x]
assert len(child2_line) == 1
child2_uclass = child2_line[0].split()[0]
child2_index = int(child2_line[0].split()[1])
child2_uclass = child2_line[0].split()[0]
child2_index = int(child2_line[0].split()[1])
#bind simple_bus as a child of bind-test-child2
response = u_boot_console.run_command('bind {} {} simple_bus'.format(child2_uclass, child2_index))
#bind simple_bus as a child of bind-test-child2
response = u_boot_console.run_command(
'bind {} {} simple_bus'.format(child2_uclass, child2_index))
#check that the child is there and its uclass/index pair is right
tree = u_boot_console.run_command('dm tree')
#check that the child is there and its uclass/index pair is right
tree = u_boot_console.run_command('dm tree')
child_of_child2_line = get_next_line(tree, 'bind-test-child2')
assert child_of_child2_line
child_of_child2_index = int(child_of_child2_line.split()[1])
assert in_tree(tree, 'simple_bus', 'simple_bus', 'simple_bus', 2, True)
assert child_of_child2_index == child2_index + 1
child_of_child2_line = get_next_line(tree, 'bind-test-child2')
assert child_of_child2_line
child_of_child2_index = int(child_of_child2_line.split()[1])
assert in_tree(tree, 'simple_bus', 'simple_bus', 'simple_bus', 2, True)
assert child_of_child2_index == child2_index + 1
#unbind the child and check it has been removed
response = u_boot_console.run_command('unbind simple_bus {}'.format(child_of_child2_index))
assert response == ''
tree = u_boot_console.run_command('dm tree')
assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, True)
assert not in_tree(tree, 'simple_bus', 'simple_bus', 'simple_bus', 2, True)
child_of_child2_line = get_next_line(tree, 'bind-test-child2')
assert child_of_child2_line == ''
#unbind the child and check it has been removed
response = u_boot_console.run_command('unbind simple_bus {}'.format(child_of_child2_index))
assert response == ''
tree = u_boot_console.run_command('dm tree')
assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, True)
assert not in_tree(tree, 'simple_bus', 'simple_bus', 'simple_bus', 2, True)
child_of_child2_line = get_next_line(tree, 'bind-test-child2')
assert child_of_child2_line == ''
#bind simple_bus as a child of bind-test-child2
response = u_boot_console.run_command('bind {} {} simple_bus'.format(child2_uclass, child2_index))
#bind simple_bus as a child of bind-test-child2
response = u_boot_console.run_command(
'bind {} {} simple_bus'.format(child2_uclass, child2_index))
#check that the child is there and its uclass/index pair is right
tree = u_boot_console.run_command('dm tree')
treelines = [x.strip() for x in tree.splitlines() if x.strip()]
#check that the child is there and its uclass/index pair is right
tree = u_boot_console.run_command('dm tree')
treelines = [x.strip() for x in tree.splitlines() if x.strip()]
child_of_child2_line = get_next_line(tree, 'bind-test-child2')
assert child_of_child2_line
child_of_child2_index = int(child_of_child2_line.split()[1])
assert in_tree(tree, 'simple_bus', 'simple_bus', 'simple_bus', 2, True)
assert child_of_child2_index == child2_index + 1
child_of_child2_line = get_next_line(tree, 'bind-test-child2')
assert child_of_child2_line
child_of_child2_index = int(child_of_child2_line.split()[1])
assert in_tree(tree, 'simple_bus', 'simple_bus', 'simple_bus', 2, True)
assert child_of_child2_index == child2_index + 1
#unbind the child and check it has been removed
response = u_boot_console.run_command('unbind {} {} simple_bus'.format(child2_uclass, child2_index))
assert response == ''
#unbind the child and check it has been removed
response = u_boot_console.run_command(
'unbind {} {} simple_bus'.format(child2_uclass, child2_index))
assert response == ''
tree = u_boot_console.run_command('dm tree')
assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, True)
tree = u_boot_console.run_command('dm tree')
assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, True)
child_of_child2_line = get_next_line(tree, 'bind-test-child2')
assert child_of_child2_line == ''
child_of_child2_line = get_next_line(tree, 'bind-test-child2')
assert child_of_child2_line == ''
#unbind the child again and check it doesn't change the tree
tree_old = u_boot_console.run_command('dm tree')
response = u_boot_console.run_command('unbind {} {} simple_bus'.format(child2_uclass, child2_index))
tree_new = u_boot_console.run_command('dm tree')
#unbind the child again and check it doesn't change the tree
tree_old = u_boot_console.run_command('dm tree')
response = u_boot_console.run_command(
'unbind {} {} simple_bus'.format(child2_uclass, child2_index))
tree_new = u_boot_console.run_command('dm tree')
assert response == ''
assert tree_old == tree_new
assert response == ''
assert tree_old == tree_new
response = u_boot_console.run_command('unbind /bind-test')
assert response == ''
response = u_boot_console.run_command('unbind /bind-test')
assert response == ''

View File

@ -25,6 +25,9 @@ try:
except: # pragma: no cover
ELF_TOOLS = False
# BSYM in little endian, keep in sync with include/binman_sym.h
BINMAN_SYM_MAGIC_VALUE = 0x4d595342
# Information about an EFL symbol:
# section (str): Name of the section containing this symbol
# address (int): Address of the symbol (its value)
@ -223,9 +226,12 @@ def LookupAndWriteSymbols(elf_fname, entry, section):
raise ValueError('%s has size %d: only 4 and 8 are supported' %
(msg, sym.size))
# Look up the symbol in our entry tables.
value = section.GetImage().LookupImageSymbol(name, sym.weak, msg,
base.address)
if name == '_binman_sym_magic':
value = BINMAN_SYM_MAGIC_VALUE
else:
# Look up the symbol in our entry tables.
value = section.GetImage().LookupImageSymbol(name, sym.weak,
msg, base.address)
if value is None:
value = -1
pack_string = pack_string.lower()

View File

@ -127,7 +127,7 @@ class TestElf(unittest.TestCase):
elf_fname = self.ElfTestFile('u_boot_binman_syms')
with self.assertRaises(ValueError) as e:
elf.LookupAndWriteSymbols(elf_fname, entry, section)
self.assertIn('entry_path has offset 4 (size 8) but the contents size '
self.assertIn('entry_path has offset 8 (size 8) but the contents size '
'is a', str(e.exception))
def testMissingImageStart(self):
@ -161,18 +161,20 @@ class TestElf(unittest.TestCase):
This should produce -1 values for all thress symbols, taking up the
first 16 bytes of the image.
"""
entry = FakeEntry(24)
entry = FakeEntry(28)
section = FakeSection(sym_value=None)
elf_fname = self.ElfTestFile('u_boot_binman_syms')
elf.LookupAndWriteSymbols(elf_fname, entry, section)
self.assertEqual(tools.get_bytes(255, 20) + tools.get_bytes(ord('a'), 4),
entry.data)
expected = (struct.pack('<L', elf.BINMAN_SYM_MAGIC_VALUE) +
tools.get_bytes(255, 20) +
tools.get_bytes(ord('a'), 4))
self.assertEqual(expected, entry.data)
def testDebug(self):
"""Check that enabling debug in the elf module produced debug output"""
try:
tout.init(tout.DEBUG)
entry = FakeEntry(20)
entry = FakeEntry(24)
section = FakeSection()
elf_fname = self.ElfTestFile('u_boot_binman_syms')
with test_util.capture_sys_output() as (stdout, stderr):

View File

@ -43,8 +43,8 @@ from patman import tout
# Contents of test files, corresponding to different entry types
U_BOOT_DATA = b'1234'
U_BOOT_IMG_DATA = b'img'
U_BOOT_SPL_DATA = b'56780123456789abcdefghi'
U_BOOT_TPL_DATA = b'tpl9876543210fedcbazyw'
U_BOOT_SPL_DATA = b'56780123456789abcdefghijklm'
U_BOOT_TPL_DATA = b'tpl9876543210fedcbazywvuts'
BLOB_DATA = b'89'
ME_DATA = b'0abcd'
VGA_DATA = b'vga'
@ -1406,8 +1406,9 @@ class TestFunctional(unittest.TestCase):
elf_fname = self.ElfTestFile('u_boot_binman_syms')
syms = elf.GetSymbols(elf_fname, ['binman', 'image'])
addr = elf.GetSymbolAddress(elf_fname, '__image_copy_start')
self.assertEqual(syms['_binman_sym_magic'].address, addr)
self.assertEqual(syms['_binman_u_boot_spl_any_prop_offset'].address,
addr)
addr + 4)
self._SetupSplElf('u_boot_binman_syms')
data = self._DoReadFileDtb(dts, entry_args=entry_args,
@ -1415,17 +1416,17 @@ class TestFunctional(unittest.TestCase):
# The image should contain the symbols from u_boot_binman_syms.c
# Note that image_pos is adjusted by the base address of the image,
# which is 0x10 in our test image
sym_values = struct.pack('<LQLL', 0x00,
u_boot_offset + len(U_BOOT_DATA),
sym_values = struct.pack('<LLQLL', elf.BINMAN_SYM_MAGIC_VALUE,
0x00, u_boot_offset + len(U_BOOT_DATA),
0x10 + u_boot_offset, 0x04)
expected = (sym_values + base_data[20:] +
expected = (sym_values + base_data[24:] +
tools.get_bytes(0xff, 1) + U_BOOT_DATA + sym_values +
base_data[20:])
base_data[24:])
self.assertEqual(expected, data)
def testSymbols(self):
"""Test binman can assign symbols embedded in U-Boot"""
self.checkSymbols('053_symbols.dts', U_BOOT_SPL_DATA, 0x18)
self.checkSymbols('053_symbols.dts', U_BOOT_SPL_DATA, 0x1c)
def testSymbolsNoDtb(self):
"""Test binman can assign symbols embedded in U-Boot SPL"""
@ -3610,20 +3611,20 @@ class TestFunctional(unittest.TestCase):
def _CheckSymbolsTplSection(self, dts, expected_vals):
data = self._DoReadFile(dts)
sym_values = struct.pack('<LQLL', *expected_vals)
sym_values = struct.pack('<LLQLL', elf.BINMAN_SYM_MAGIC_VALUE, *expected_vals)
upto1 = 4 + len(U_BOOT_SPL_DATA)
expected1 = tools.get_bytes(0xff, 4) + sym_values + U_BOOT_SPL_DATA[20:]
expected1 = tools.get_bytes(0xff, 4) + sym_values + U_BOOT_SPL_DATA[24:]
self.assertEqual(expected1, data[:upto1])
upto2 = upto1 + 1 + len(U_BOOT_SPL_DATA)
expected2 = tools.get_bytes(0xff, 1) + sym_values + U_BOOT_SPL_DATA[20:]
expected2 = tools.get_bytes(0xff, 1) + sym_values + U_BOOT_SPL_DATA[24:]
self.assertEqual(expected2, data[upto1:upto2])
upto3 = 0x34 + len(U_BOOT_DATA)
upto3 = 0x3c + len(U_BOOT_DATA)
expected3 = tools.get_bytes(0xff, 1) + U_BOOT_DATA
self.assertEqual(expected3, data[upto2:upto3])
expected4 = sym_values + U_BOOT_TPL_DATA[20:]
expected4 = sym_values + U_BOOT_TPL_DATA[24:]
self.assertEqual(expected4, data[upto3:upto3 + len(U_BOOT_TPL_DATA)])
def testSymbolsTplSection(self):
@ -3631,14 +3632,14 @@ class TestFunctional(unittest.TestCase):
self._SetupSplElf('u_boot_binman_syms')
self._SetupTplElf('u_boot_binman_syms')
self._CheckSymbolsTplSection('149_symbols_tpl.dts',
[0x04, 0x1c, 0x10 + 0x34, 0x04])
[0x04, 0x20, 0x10 + 0x3c, 0x04])
def testSymbolsTplSectionX86(self):
"""Test binman can assign symbols in a section with end-at-4gb"""
self._SetupSplElf('u_boot_binman_syms_x86')
self._SetupTplElf('u_boot_binman_syms_x86')
self._CheckSymbolsTplSection('155_symbols_tpl_x86.dts',
[0xffffff04, 0xffffff1c, 0xffffff34,
[0xffffff04, 0xffffff20, 0xffffff3c,
0x04])
def testPackX86RomIfwiSectiom(self):
@ -4488,7 +4489,7 @@ class TestFunctional(unittest.TestCase):
def testSymbolsSubsection(self):
"""Test binman can assign symbols from a subsection"""
self.checkSymbols('187_symbols_sub.dts', U_BOOT_SPL_DATA, 0x18)
self.checkSymbols('187_symbols_sub.dts', U_BOOT_SPL_DATA, 0x1c)
def testReadImageEntryArg(self):
"""Test reading an image that would need an entry arg to generate"""

View File

@ -13,7 +13,6 @@ import os
import site
import sys
import traceback
import unittest
# Get the absolute path to this file at run-time
our_path = os.path.dirname(os.path.realpath(__file__))
@ -73,19 +72,18 @@ def RunTests(debug, verbosity, processes, test_preserve_dirs, args, toolpath):
from binman import image_test
import doctest
result = unittest.TestResult()
test_name = args and args[0] or None
# Run the entry tests first ,since these need to be the first to import the
# 'entry' module.
test_util.run_test_suites(
result, debug, verbosity, test_preserve_dirs, processes, test_name,
result = test_util.run_test_suites(
'binman', debug, verbosity, test_preserve_dirs, processes, test_name,
toolpath,
[bintool_test.TestBintool, entry_test.TestEntry, ftest.TestFunctional,
fdt_test.TestFdt, elf_test.TestElf, image_test.TestImage,
cbfs_util_test.TestCbfs, fip_util_test.TestFip])
return test_util.report_result('binman', test_name, result)
return (0 if result.wasSuccessful() else 1)
def RunTestCoverage(toolpath):
"""Run the tests and check that we get 100% coverage"""

View File

@ -10,7 +10,7 @@
};
u-boot {
offset = <24>;
offset = <28>;
};
};
};

View File

@ -7,7 +7,7 @@
binman {
sort-by-offset;
u-boot {
offset = <26>;
offset = <30>;
};
u-boot-spl {

View File

@ -13,7 +13,7 @@
};
u-boot-spl {
offset = <0xffffffe7>;
offset = <0xffffffe3>;
};
};
};

View File

@ -7,13 +7,13 @@
binman {
sort-by-offset;
end-at-4gb;
size = <32>;
size = <36>;
u-boot {
offset = <0xffffffe0>;
offset = <0xffffffdc>;
};
u-boot-spl {
offset = <0xffffffe7>;
offset = <0xffffffe3>;
};
};
};

View File

@ -10,7 +10,7 @@
};
u-boot {
offset = <0x18>;
offset = <0x1c>;
};
u-boot-spl2 {

View File

@ -11,12 +11,12 @@
};
u-boot-spl2 {
offset = <0x1c>;
offset = <0x20>;
type = "u-boot-spl";
};
u-boot {
offset = <0x34>;
offset = <0x3c>;
};
section {

View File

@ -14,12 +14,12 @@
};
u-boot-spl2 {
offset = <0xffffff1c>;
offset = <0xffffff20>;
type = "u-boot-spl";
};
u-boot {
offset = <0xffffff34>;
offset = <0xffffff3c>;
};
section {

View File

@ -11,7 +11,7 @@
};
u-boot {
offset = <24>;
offset = <28>;
};
};

View File

@ -21,7 +21,7 @@ CC = $(CROSS_COMPILE)gcc
OBJCOPY = $(CROSS_COMPILE)objcopy
VPATH := $(SRC)
CFLAGS := -march=i386 -m32 -nostdlib -I $(SRC)../../../include \
CFLAGS := -march=i386 -m32 -nostdlib -I $(SRC)../../../include -I $(SRC) \
-Wl,--no-dynamic-linker
LDS_UCODE := -T $(SRC)u_boot_ucode_ptr.lds

View File

@ -0,0 +1,3 @@
#define CONFIG_BINMAN 1
#define CONFIG_SPL_BUILD 1
#define CONFIG_SPL_BINMAN_SYMBOLS 1

View File

@ -5,9 +5,13 @@
* Simple program to create some binman symbols. This is used by binman tests.
*/
#define CONFIG_BINMAN
typedef unsigned long ulong;
#include <linux/kconfig.h>
#include <binman_sym.h>
DECLARE_BINMAN_MAGIC_SYM;
binman_sym_declare(unsigned long, u_boot_spl_any, offset);
binman_sym_declare(unsigned long long, u_boot_spl2, offset);
binman_sym_declare(unsigned long, u_boot_any, image_pos);

View File

@ -5,7 +5,11 @@
* Simple program to create some binman symbols. This is used by binman tests.
*/
#define CONFIG_BINMAN
typedef unsigned long ulong;
#include <linux/kconfig.h>
#include <binman_sym.h>
DECLARE_BINMAN_MAGIC_SYM;
binman_sym_declare(char, u_boot_spl, pos);

View File

@ -11,7 +11,6 @@ import multiprocessing
import os
import re
import sys
import unittest
# Bring in the patman libraries
our_path = os.path.dirname(os.path.realpath(__file__))
@ -34,19 +33,18 @@ def RunTests(skip_net_tests, verboose, args):
from buildman import test
import doctest
result = unittest.TestResult()
test_name = args and args[0] or None
if skip_net_tests:
test.use_network = False
# Run the entry tests first ,since these need to be the first to import the
# 'entry' module.
test_util.run_test_suites(
result, False, verboose, False, None, test_name, [],
result = test_util.run_test_suites(
'buildman', False, verboose, False, None, test_name, [],
[test.TestBuild, func_test.TestFunctional,
'buildman.toolchain', 'patman.gitutil'])
return test_util.report_result('buildman', test_name, result)
return (0 if result.wasSuccessful() else 1)
options, args = cmdline.ParseArgs()

View File

@ -31,6 +31,7 @@ from subunit import ProtocolTestCase, TestProtocolClient
from subunit.test_results import AutoTimingTestResultDecorator
from testtools import ConcurrentTestSuite, iterate_tests
from testtools.content import TracebackContent, text_content
_all__ = [
@ -43,11 +44,81 @@ _all__ = [
CPU_COUNT = cpu_count()
def fork_for_tests(concurrency_num=CPU_COUNT):
class BufferingTestProtocolClient(TestProtocolClient):
"""A TestProtocolClient which can buffer the test outputs
This class captures the stdout and stderr output streams of the
tests as it runs them, and includes the output texts in the subunit
stream as additional details.
Args:
stream: A file-like object to write a subunit stream to
buffer (bool): True to capture test stdout/stderr outputs and
include them in the test details
"""
def __init__(self, stream, buffer=True):
super().__init__(stream)
self.buffer = buffer
def _addOutcome(self, outcome, test, error=None, details=None,
error_permitted=True):
"""Report a test outcome to the subunit stream
The parent class uses this function as a common implementation
for various methods that report successes, errors, failures, etc.
This version automatically upgrades the error tracebacks to the
new 'details' format by wrapping them in a Content object, so
that we can include the captured test output in the test result
details.
Args:
outcome: A string describing the outcome - used as the
event name in the subunit stream.
test: The test case whose outcome is to be reported
error: Standard unittest positional argument form - an
exc_info tuple.
details: New Testing-in-python drafted API; a dict from
string to subunit.Content objects.
error_permitted: If True then one and only one of error or
details must be supplied. If False then error must not
be supplied and details is still optional.
"""
if details is None:
details = {}
# Parent will raise an exception if error_permitted is False but
# error is not None. We want that exception in that case, so
# don't touch error when error_permitted is explicitly False.
if error_permitted and error is not None:
# Parent class prefers error over details
details['traceback'] = TracebackContent(error, test)
error_permitted = False
error = None
if self.buffer:
stdout = sys.stdout.getvalue()
if stdout:
details['stdout'] = text_content(stdout)
stderr = sys.stderr.getvalue()
if stderr:
details['stderr'] = text_content(stderr)
return super()._addOutcome(outcome, test, error=error,
details=details, error_permitted=error_permitted)
def fork_for_tests(concurrency_num=CPU_COUNT, buffer=False):
"""Implementation of `make_tests` used to construct `ConcurrentTestSuite`.
:param concurrency_num: number of processes to use.
"""
if buffer:
test_protocol_client_class = BufferingTestProtocolClient
else:
test_protocol_client_class = TestProtocolClient
def do_fork(suite):
"""Take suite and start up multiple runners by forking (Unix only).
@ -76,7 +147,7 @@ def fork_for_tests(concurrency_num=CPU_COUNT):
# child actually gets keystrokes for pdb etc).
sys.stdin.close()
subunit_result = AutoTimingTestResultDecorator(
TestProtocolClient(stream)
test_protocol_client_class(stream)
)
process_suite.run(subunit_result)
except:
@ -93,7 +164,13 @@ def fork_for_tests(concurrency_num=CPU_COUNT):
else:
os.close(c2pwrite)
stream = os.fdopen(c2pread, 'rb')
test = ProtocolTestCase(stream)
# If we don't pass the second argument here, it defaults
# to sys.stdout.buffer down the line. But if we don't
# pass it *now*, it may be resolved after sys.stdout is
# replaced with a StringIO (to capture tests' outputs)
# which doesn't have a buffer attribute and can end up
# occasionally causing a 'broken-runner' error.
test = ProtocolTestCase(stream, sys.stdout.buffer)
result.append(test)
return result
return do_fork

View File

@ -24,7 +24,6 @@ see doc/driver-model/of-plat.rst
from argparse import ArgumentParser
import os
import sys
import unittest
# Bring in the patman libraries
our_path = os.path.dirname(os.path.realpath(__file__))
@ -49,18 +48,18 @@ def run_tests(processes, args):
from dtoc import test_src_scan
from dtoc import test_dtoc
result = unittest.TestResult()
sys.argv = [sys.argv[0]]
test_name = args.files and args.files[0] or None
test_dtoc.setup()
test_util.run_test_suites(
result, debug=True, verbosity=1, test_preserve_dirs=False,
result = test_util.run_test_suites(
toolname='dtoc', debug=True, verbosity=1, test_preserve_dirs=False,
processes=processes, test_name=test_name, toolpath=[],
class_and_module_list=[test_dtoc.TestDtoc,test_src_scan.TestSrcScan])
return test_util.report_result('binman', test_name, result)
return (0 if result.wasSuccessful() else 1)
def RunTestCoverage():
"""Run the tests and check that we get 100% coverage"""

View File

@ -616,8 +616,11 @@ struct dm_test_pdata __attribute__ ((section (".priv_data")))
u8 _denx_u_boot_test_bus_priv_some_bus[sizeof(struct dm_test_priv)]
\t__attribute__ ((section (".priv_data")));
#include <dm/test.h>
u8 _denx_u_boot_test_bus_ucplat_some_bus[sizeof(struct dm_test_uclass_priv)]
u8 _denx_u_boot_test_bus_ucplat_some_bus[sizeof(struct dm_test_uclass_plat)]
\t__attribute__ ((section (".priv_data")));
#include <dm/test.h>
u8 _denx_u_boot_test_bus_uc_priv_some_bus[sizeof(struct dm_test_uclass_priv)]
__attribute__ ((section (".priv_data")));
#include <test.h>
DM_DEVICE_INST(some_bus) = {
@ -628,6 +631,7 @@ DM_DEVICE_INST(some_bus) = {
\t.driver_data\t= DM_TEST_TYPE_FIRST,
\t.priv_\t\t= _denx_u_boot_test_bus_priv_some_bus,
\t.uclass\t\t= DM_UCLASS_REF(testbus),
\t.uclass_priv_ = _denx_u_boot_test_bus_uc_priv_some_bus,
\t.uclass_node\t= {
\t\t.prev = &DM_UCLASS_REF(testbus)->dev_head,
\t\t.next = &DM_UCLASS_REF(testbus)->dev_head,

View File

@ -780,25 +780,17 @@ def RunTests(args):
Args:
args: List of positional args provided to fdt. This can hold a test
name to execute (as in 'fdt -t testFdt', for example)
"""
result = unittest.TestResult()
sys.argv = [sys.argv[0]]
test_name = args and args[0] or None
for module in (TestFdt, TestNode, TestProp, TestFdtUtil):
if test_name:
try:
suite = unittest.TestLoader().loadTestsFromName(test_name, module)
except AttributeError:
continue
else:
suite = unittest.TestLoader().loadTestsFromTestCase(module)
suite.run(result)
print(result)
for _, err in result.errors:
print(err)
for _, err in result.failures:
print(err)
Returns:
Return code, 0 on success
"""
test_name = args and args[0] or None
result = test_util.run_test_suites(
'test_fdt', False, False, False, None, test_name, None,
[TestFdt, TestNode, TestProp, TestFdtUtil])
return (0 if result.wasSuccessful() else 1)
if __name__ != '__main__':
sys.exit(1)
@ -816,6 +808,7 @@ parser.add_option('-T', '--test-coverage', action='store_true',
# Run our meagre tests
if options.test:
RunTests(args)
ret_code = RunTests(args)
sys.exit(ret_code)
elif options.test_coverage:
RunTestCoverage()

View File

@ -12,7 +12,6 @@ import re
import shutil
import sys
import traceback
import unittest
if __name__ == "__main__":
# Allow 'from patman import xxx to work'
@ -134,13 +133,12 @@ if args.cmd == 'test':
import doctest
from patman import func_test
result = unittest.TestResult()
test_util.run_test_suites(
result, False, False, False, None, None, None,
result = test_util.run_test_suites(
'patman', False, False, False, None, None, None,
[test_checkpatch.TestPatch, func_test.TestFunctional,
'gitutil', 'settings', 'terminal'])
sys.exit(test_util.report_result('patman', args.testname, result))
sys.exit(0 if result.wasSuccessful() else 1)
# Process commits, produce patches files, check them, email them
elif args.cmd == 'send':

View File

@ -246,8 +246,10 @@ def _UpdateDefaults(main_parser, config):
# Collect the defaults from each parser
defaults = {}
parser_defaults = []
for parser in parsers:
pdefs = parser.parse_known_args()[0]
parser_defaults.append(pdefs)
defaults.update(vars(pdefs))
# Go through the settings and collect defaults
@ -264,8 +266,11 @@ def _UpdateDefaults(main_parser, config):
else:
print("WARNING: Unknown setting %s" % name)
# Set all the defaults (this propagates through all subparsers)
# Set all the defaults and manually propagate them to subparsers
main_parser.set_defaults(**defaults)
for parser, pdefs in zip(parsers, parser_defaults):
parser.set_defaults(**{ k: v for k, v in defaults.items()
if k in pdefs })
def _ReadAliasFile(fname):
"""Read in the U-Boot git alias file if it exists.

View File

@ -15,6 +15,7 @@ from patman import command
from io import StringIO
buffer_outputs = True
use_concurrent = True
try:
from concurrencytest.concurrencytest import ConcurrentTestSuite
@ -102,49 +103,85 @@ def capture_sys_output():
sys.stdout, sys.stderr = old_out, old_err
def report_result(toolname:str, test_name: str, result: unittest.TestResult):
"""Report the results from a suite of tests
class FullTextTestResult(unittest.TextTestResult):
"""A test result class that can print extended text results to a stream
This is meant to be used by a TestRunner as a result class. Like
TextTestResult, this prints out the names of tests as they are run,
errors as they occur, and a summary of the results at the end of the
test run. Beyond those, this prints information about skipped tests,
expected failures and unexpected successes.
Args:
toolname: Name of the tool that ran the tests
test_name: Name of test that was run, or None for all
result: A unittest.TestResult object containing the results
stream: A file-like object to write results to
descriptions (bool): True to print descriptions with test names
verbosity (int): Detail of printed output per test as they run
Test stdout and stderr always get printed when buffering
them is disabled by the test runner. In addition to that,
0: Print nothing
1: Print a dot per test
2: Print test names
3: Print test names, and buffered outputs for failing tests
"""
# Remove errors which just indicate a missing test. Since Python v3.5 If an
# ImportError or AttributeError occurs while traversing name then a
# synthetic test that raises that error when run will be returned. These
# errors are included in the errors accumulated by result.errors.
if test_name:
errors = []
def __init__(self, stream, descriptions, verbosity):
self.verbosity = verbosity
super().__init__(stream, descriptions, verbosity)
for test, err in result.errors:
if ("has no attribute '%s'" % test_name) not in err:
errors.append((test, err))
result.testsRun -= 1
result.errors = errors
def printErrors(self):
"Called by TestRunner after test run to summarize the tests"
# The parent class doesn't keep unexpected successes in the same
# format as the rest. Adapt it to what printErrorList expects.
unexpected_successes = [
(test, 'Test was expected to fail, but succeeded.\n')
for test in self.unexpectedSuccesses
]
print(result)
for test, err in result.errors:
print(test.id(), err)
for test, err in result.failures:
print(err, result.failures)
if result.skipped:
print('%d %s test%s SKIPPED:' % (len(result.skipped), toolname,
's' if len(result.skipped) > 1 else ''))
for skip_info in result.skipped:
print('%s: %s' % (skip_info[0], skip_info[1]))
if result.errors or result.failures:
print('%s tests FAILED' % toolname)
return 1
return 0
super().printErrors() # FAIL and ERROR
self.printErrorList('SKIP', self.skipped)
self.printErrorList('XFAIL', self.expectedFailures)
self.printErrorList('XPASS', unexpected_successes)
def addError(self, test, err):
"""Called when an error has occurred."""
super().addError(test, err)
self._mirrorOutput &= self.verbosity >= 3
def addFailure(self, test, err):
"""Called when a test has failed."""
super().addFailure(test, err)
self._mirrorOutput &= self.verbosity >= 3
def addSubTest(self, test, subtest, err):
"""Called at the end of a subtest."""
super().addSubTest(test, subtest, err)
self._mirrorOutput &= self.verbosity >= 3
def addSuccess(self, test):
"""Called when a test has completed successfully"""
super().addSuccess(test)
# Don't print stdout/stderr for successful tests
self._mirrorOutput = False
def addSkip(self, test, reason):
"""Called when a test is skipped."""
# Add empty line to keep spacing consistent with other results
if not reason.endswith('\n'):
reason += '\n'
super().addSkip(test, reason)
self._mirrorOutput &= self.verbosity >= 3
def addExpectedFailure(self, test, err):
"""Called when an expected failure/error occurred."""
super().addExpectedFailure(test, err)
self._mirrorOutput &= self.verbosity >= 3
def run_test_suites(result, debug, verbosity, test_preserve_dirs, processes,
def run_test_suites(toolname, debug, verbosity, test_preserve_dirs, processes,
test_name, toolpath, class_and_module_list):
"""Run a series of test suites and collect the results
Args:
result: A unittest.TestResult object to add the results to
toolname: Name of the tool that ran the tests
debug: True to enable debugging, which shows a full stack trace on error
verbosity: Verbosity level to use (0-4)
test_preserve_dirs: True to preserve the input directory used by tests
@ -158,11 +195,6 @@ def run_test_suites(result, debug, verbosity, test_preserve_dirs, processes,
class_and_module_list: List of test classes (type class) and module
names (type str) to run
"""
for module in class_and_module_list:
if isinstance(module, str) and (not test_name or test_name == module):
suite = doctest.DocTestSuite(module)
suite.run(result)
sys.argv = [sys.argv[0]]
if debug:
sys.argv.append('-D')
@ -174,6 +206,22 @@ def run_test_suites(result, debug, verbosity, test_preserve_dirs, processes,
suite = unittest.TestSuite()
loader = unittest.TestLoader()
runner = unittest.TextTestRunner(
stream=sys.stdout,
verbosity=(1 if verbosity is None else verbosity),
buffer=buffer_outputs,
resultclass=FullTextTestResult,
)
if use_concurrent and processes != 1:
suite = ConcurrentTestSuite(suite,
fork_for_tests(processes or multiprocessing.cpu_count(),
buffer=buffer_outputs))
for module in class_and_module_list:
if isinstance(module, str) and (not test_name or test_name == module):
suite.addTests(doctest.DocTestSuite(module))
for module in class_and_module_list:
if isinstance(module, str):
continue
@ -184,15 +232,17 @@ def run_test_suites(result, debug, verbosity, test_preserve_dirs, processes,
preserve_outdirs=test_preserve_dirs and test_name is not None,
toolpath=toolpath, verbosity=verbosity)
if test_name:
try:
# Since Python v3.5 If an ImportError or AttributeError occurs
# while traversing a name then a synthetic test that raises that
# error when run will be returned. Check that the requested test
# exists, otherwise these errors are included in the results.
if test_name in loader.getTestCaseNames(module):
suite.addTests(loader.loadTestsFromName(test_name, module))
except AttributeError:
continue
else:
suite.addTests(loader.loadTestsFromTestCase(module))
if use_concurrent and processes != 1:
concurrent_suite = ConcurrentTestSuite(suite,
fork_for_tests(processes or multiprocessing.cpu_count()))
concurrent_suite.run(result)
else:
suite.run(result)
print(f" Running {toolname} tests ".center(70, "="))
result = runner.run(suite)
print()
return result