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
59 changed files with 1694 additions and 380 deletions

View File

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

View File

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

View File

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

View File

@@ -12,6 +12,7 @@
#include <getopt.h> #include <getopt.h>
#include <setjmp.h> #include <setjmp.h>
#include <signal.h> #include <signal.h>
#include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.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); 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) off_t os_lseek(int fd, off_t offset, int whence)
{ {
if (whence == OS_SEEK_SET) if (whence == OS_SEEK_SET)

View File

@@ -8,20 +8,13 @@
#include <common.h> #include <common.h>
#include <command.h> #include <command.h>
#include <dm/root.h>
#include <dm/util.h> #include <dm/util.h>
static int do_dm_dump_all(struct cmd_tbl *cmdtp, int flag, int argc, static int do_dm_dump_driver_compat(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[]) char * const argv[])
{ {
dm_dump_all(); dm_dump_driver_compat();
return 0;
}
static int do_dm_dump_uclass(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[])
{
dm_dump_uclass();
return 0; return 0;
} }
@@ -42,37 +35,68 @@ static int do_dm_dump_drivers(struct cmd_tbl *cmdtp, int flag, int argc,
return 0; return 0;
} }
static int do_dm_dump_driver_compat(struct cmd_tbl *cmdtp, int flag, int argc, #if CONFIG_IS_ENABLED(DM_STATS)
char * const argv[]) 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; return 0;
} }
#endif /* DM_STATS */
static int do_dm_dump_static_driver_info(struct cmd_tbl *cmdtp, int flag, int argc, static int do_dm_dump_static_driver_info(struct cmd_tbl *cmdtp, int flag,
char * const argv[]) int argc, char * const argv[])
{ {
dm_dump_static_driver_info(); dm_dump_static_driver_info();
return 0; 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) #if CONFIG_IS_ENABLED(SYS_LONGHELP)
static char dm_help_text[] = static char dm_help_text[] =
"tree Dump driver model tree ('*' = activated)\n" "compat Dump list of drivers with compatibility strings\n"
"dm uclass Dump list of instances for each uclass\n"
"dm devres Dump list of device resources for each device\n" "dm devres Dump list of device resources for each device\n"
"dm drivers Dump list of drivers with uclass and instances\n" "dm drivers Dump list of drivers with uclass and instances\n"
"dm compat Dump list of drivers with compatibility strings\n" DM_MEM_HELP
"dm static Dump list of drivers with static platform data" "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 #endif
U_BOOT_CMD_WITH_SUBCMDS(dm, "Driver model low level access", dm_help_text, 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(compat, 1, 1, do_dm_dump_driver_compat),
U_BOOT_SUBCMD_MKENT(uclass, 1, 1, do_dm_dump_uclass),
U_BOOT_SUBCMD_MKENT(devres, 1, 1, do_dm_dump_devres), 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(drivers, 1, 1, do_dm_dump_drivers),
U_BOOT_SUBCMD_MKENT(compat, 1, 1, do_dm_dump_driver_compat), DM_MEM
U_BOOT_SUBCMD_MKENT(static, 1, 1, do_dm_dump_static_driver_info)); 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 depends on SPL_FRAMEWORK && BINMAN
default y default y
help help
This enables use of symbols in SPL which refer to U-Boot, enabling SPL This enables use of symbols in SPL which refer to other entries in
to obtain the location of U-Boot simply by calling spl_get_image_pos() the same binman image as the SPL. These can be declared with the
and spl_get_image_size(). 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 See tools/binman/binman.rst for a detailed explanation.
binman can update SPL with the location of it.
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" source "common/spl/Kconfig.nxp"

View File

@@ -9,16 +9,29 @@ config TPL_SIZE_LIMIT
If this value is zero, it is ignored. If this value is zero, it is ignored.
config TPL_BINMAN_SYMBOLS config TPL_BINMAN_SYMBOLS
bool "Declare binman symbols in TPL" bool "Support binman symbols in TPL"
depends on SPL_FRAMEWORK && BINMAN depends on TPL_FRAMEWORK && BINMAN
default y default y
help help
This enables use of symbols in TPL which refer to U-Boot, enabling TPL This enables use of symbols in TPL which refer to other entries in
to obtain the location of U-Boot simply by calling spl_get_image_pos() the same binman image as the TPL. These can be declared with the
and spl_get_image_size(). 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 See tools/binman/binman.rst for a detailed explanation.
binman can update TPL with the location of it.
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 config TPL_FRAMEWORK
bool "Support TPL based upon the common SPL framework" bool "Support TPL based upon the common SPL framework"

View File

@@ -198,4 +198,29 @@ config VPL_TEXT_BASE
help help
The address in memory that VPL will be running from. 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 endmenu

View File

@@ -34,12 +34,14 @@
#include <malloc.h> #include <malloc.h>
#include <mapmem.h> #include <mapmem.h>
#include <dm/root.h> #include <dm/root.h>
#include <dm/util.h>
#include <linux/compiler.h> #include <linux/compiler.h>
#include <fdt_support.h> #include <fdt_support.h>
#include <bootcount.h> #include <bootcount.h>
#include <wdt.h> #include <wdt.h>
DECLARE_GLOBAL_DATA_PTR; DECLARE_GLOBAL_DATA_PTR;
DECLARE_BINMAN_MAGIC_SYM;
#ifndef CONFIG_SYS_UBOOT_START #ifndef CONFIG_SYS_UBOOT_START
#define CONFIG_SYS_UBOOT_START CONFIG_SYS_TEXT_BASE #define CONFIG_SYS_UBOOT_START CONFIG_SYS_TEXT_BASE
@@ -51,11 +53,10 @@ DECLARE_GLOBAL_DATA_PTR;
u32 *boot_params_ptr = NULL; 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 */ /* See spl.h for information about this */
binman_sym_declare(ulong, u_boot_any, image_pos); binman_sym_declare(ulong, u_boot_any, image_pos);
binman_sym_declare(ulong, u_boot_any, size); binman_sym_declare(ulong, u_boot_any, size);
#endif
#ifdef CONFIG_TPL #ifdef CONFIG_TPL
binman_sym_declare(ulong, u_boot_spl, image_pos); 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); binman_sym_declare(ulong, u_boot_vpl, size);
#endif #endif
#endif /* BINMAN_UBOOT_SYMBOLS */
/* Define board data structure */ /* Define board data structure */
static struct bd_info bdata __attribute__ ((section(".data"))); static struct bd_info bdata __attribute__ ((section(".data")));
@@ -149,9 +152,11 @@ void spl_fixup_fdt(void *fdt_blob)
#endif #endif
} }
#if CONFIG_IS_ENABLED(BINMAN_SYMBOLS)
ulong spl_get_image_pos(void) ulong spl_get_image_pos(void)
{ {
if (!CONFIG_IS_ENABLED(BINMAN_UBOOT_SYMBOLS))
return BINMAN_SYM_MISSING;
#ifdef CONFIG_VPL #ifdef CONFIG_VPL
if (spl_next_phase() == PHASE_VPL) if (spl_next_phase() == PHASE_VPL)
return binman_sym(ulong, u_boot_vpl, image_pos); 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) ulong spl_get_image_size(void)
{ {
if (!CONFIG_IS_ENABLED(BINMAN_UBOOT_SYMBOLS))
return BINMAN_SYM_MISSING;
#ifdef CONFIG_VPL #ifdef CONFIG_VPL
if (spl_next_phase() == PHASE_VPL) if (spl_next_phase() == PHASE_VPL)
return binman_sym(ulong, u_boot_vpl, size); 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_spl, size) :
binman_sym(ulong, u_boot_any, size); binman_sym(ulong, u_boot_any, size);
} }
#endif /* BINMAN_SYMBOLS */
ulong spl_get_image_text_base(void) 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) 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; spl_image->size = CONFIG_SYS_MONITOR_LEN;
@@ -780,6 +787,14 @@ void board_init_r(gd_t *dummy1, ulong dummy2)
bootcount_inc(); 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)); memset(&spl_image, '\0', sizeof(spl_image));
#ifdef CONFIG_SYS_SPL_ARGS_ADDR #ifdef CONFIG_SYS_SPL_ARGS_ADDR
spl_image.arg = (void *)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; load.read = spl_ram_load_read;
spl_load_simple_fit(spl_image, &load, 0, header); spl_load_simple_fit(spl_image, &load, 0, header);
} else { } 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"); 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 owner can find it and update it. Each record is generally described by a C
structure defined by the code that owns it. 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 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/bootz
cmd/cbsysinfo cmd/cbsysinfo
cmd/conitrace cmd/conitrace
cmd/dm
cmd/echo cmd/echo
cmd/env cmd/env
cmd/event cmd/event

View File

@@ -75,6 +75,27 @@ config DM_DEBUG
help help
Say Y here if you want to compile in debug messages in DM core. 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 config DM_DEVICE_REMOVE
bool "Support device removal" bool "Support device removal"
depends on DM depends on DM

View File

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

View File

@@ -284,8 +284,7 @@ int device_reparent(struct udevice *dev, struct udevice *new_parent)
assert(dev); assert(dev);
assert(new_parent); assert(new_parent);
list_for_each_entry_safe(pos, n, &dev->parent->child_head, device_foreach_child_safe(pos, n, dev->parent) {
sibling_node) {
if (pos->driver != dev->driver) if (pos->driver != dev->driver)
continue; continue;
@@ -675,6 +674,71 @@ void *dev_get_parent_priv(const struct udevice *dev)
return dm_priv_to_rw(dev->parent_priv_); 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, static int device_get_device_tail(struct udevice *dev, int ret,
struct udevice **devp) struct udevice **devp)
{ {
@@ -724,7 +788,7 @@ int device_get_child(const struct udevice *parent, int index,
{ {
struct udevice *dev; struct udevice *dev;
list_for_each_entry(dev, &parent->child_head, sibling_node) { device_foreach_child(dev, parent) {
if (!index--) if (!index--)
return device_get_device_tail(dev, 0, devp); return device_get_device_tail(dev, 0, devp);
} }
@@ -737,7 +801,7 @@ int device_get_child_count(const struct udevice *parent)
struct udevice *dev; struct udevice *dev;
int count = 0; int count = 0;
list_for_each_entry(dev, &parent->child_head, sibling_node) device_foreach_child(dev, parent)
count++; count++;
return count; return count;
@@ -748,7 +812,7 @@ int device_get_decendent_count(const struct udevice *parent)
const struct udevice *dev; const struct udevice *dev;
int count = 1; int count = 1;
list_for_each_entry(dev, &parent->child_head, sibling_node) device_foreach_child(dev, parent)
count += device_get_decendent_count(dev); count += device_get_decendent_count(dev);
return count; return count;
@@ -761,7 +825,7 @@ int device_find_child_by_seq(const struct udevice *parent, int seq,
*devp = NULL; *devp = NULL;
list_for_each_entry(dev, &parent->child_head, sibling_node) { device_foreach_child(dev, parent) {
if (dev->seq_ == seq) { if (dev->seq_ == seq) {
*devp = dev; *devp = dev;
return 0; return 0;
@@ -790,7 +854,7 @@ int device_find_child_by_of_offset(const struct udevice *parent, int of_offset,
*devp = NULL; *devp = NULL;
list_for_each_entry(dev, &parent->child_head, sibling_node) { device_foreach_child(dev, parent) {
if (dev_of_offset(dev) == of_offset) { if (dev_of_offset(dev) == of_offset) {
*devp = dev; *devp = dev;
return 0; return 0;
@@ -819,7 +883,7 @@ static struct udevice *_device_find_global_by_ofnode(struct udevice *parent,
if (ofnode_equal(dev_ofnode(parent), ofnode)) if (ofnode_equal(dev_ofnode(parent), ofnode))
return parent; 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); found = _device_find_global_by_ofnode(dev, ofnode);
if (found) if (found)
return found; return found;
@@ -897,7 +961,7 @@ int device_find_first_inactive_child(const struct udevice *parent,
struct udevice *dev; struct udevice *dev;
*devp = NULL; *devp = NULL;
list_for_each_entry(dev, &parent->child_head, sibling_node) { device_foreach_child(dev, parent) {
if (!device_active(dev) && if (!device_active(dev) &&
device_get_uclass_id(dev) == uclass_id) { device_get_uclass_id(dev) == uclass_id) {
*devp = dev; *devp = dev;
@@ -915,7 +979,7 @@ int device_find_first_child_by_uclass(const struct udevice *parent,
struct udevice *dev; struct udevice *dev;
*devp = NULL; *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) { if (device_get_uclass_id(dev) == uclass_id) {
*devp = dev; *devp = dev;
return 0; return 0;
@@ -932,7 +996,7 @@ int device_find_child_by_namelen(const struct udevice *parent, const char *name,
*devp = NULL; *devp = NULL;
list_for_each_entry(dev, &parent->child_head, sibling_node) { device_foreach_child(dev, parent) {
if (!strncmp(dev->name, name, len) && if (!strncmp(dev->name, name, len) &&
strlen(dev->name) == len) { strlen(dev->name) == len) {
*devp = dev; *devp = dev;

View File

@@ -232,7 +232,7 @@ static void dump_resources(struct udevice *dev, int depth)
(unsigned long)dr->size, dr->name, (unsigned long)dr->size, dr->name,
devres_phase_name[dr->phase]); 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); 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); 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); is_last = list_is_last(&child->sibling_node, &dev->child_head);
show_devices(child, depth + 1, (last_flag << 1) | is_last); show_devices(child, depth + 1, (last_flag << 1) | is_last);
} }
} }
void dm_dump_all(void) void dm_dump_tree(void)
{ {
struct udevice *root; struct udevice *root;
@@ -89,8 +89,6 @@ void dm_dump_uclass(void)
continue; continue;
printf("uclass %d: %s\n", id, uc->uc_drv->name); printf("uclass %d: %s\n", id, uc->uc_drv->name);
if (list_empty(&uc->dev_head))
continue;
uclass_foreach_dev(dev, uc) { uclass_foreach_dev(dev, uc) {
dm_display_line(dev, i); dm_display_line(dev, i);
i++; i++;
@@ -171,8 +169,79 @@ void dm_dump_static_driver_info(void)
puts("Driver Address\n"); puts("Driver Address\n");
puts("---------------------------------\n"); puts("---------------------------------\n");
for (entry = drv; entry != drv + n_ents; entry++) { for (entry = drv; entry != drv + n_ents; entry++)
printf("%-25.25s @%08lx\n", entry->name, printf("%-25.25s %p\n", entry->name, entry->plat);
(ulong)map_to_sysmem(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(); *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 #ifdef CONFIG_ACPIGEN
static int root_acpi_get_name(const struct udevice *dev, char *out_name) static int root_acpi_get_name(const struct udevice *dev, char *out_name)
{ {

View File

@@ -6,6 +6,7 @@
#include <malloc.h> #include <malloc.h>
#include <asm/global_data.h> #include <asm/global_data.h>
#include <dm/root.h>
#include <dm/tag.h> #include <dm/tag.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/list.h> #include <linux/list.h>
@@ -15,6 +16,24 @@ struct udevice;
DECLARE_GLOBAL_DATA_PTR; 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) int dev_tag_set_ptr(struct udevice *dev, enum dm_tag_t tag, void *ptr)
{ {
struct dmtag_node *node; struct dmtag_node *node;
@@ -137,3 +156,13 @@ int dev_tag_del_all(struct udevice *dev)
return -ENOENT; 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; u16 entry;
u32 control = be32_to_cpu(dma->control); 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); u32 length = be32_to_cpu(dma->length);
struct qfw_sandbox_plat *plat = dev_get_plat(dev); struct qfw_sandbox_plat *plat = dev_get_plat(dev);
struct fw_cfg_file *file; struct fw_cfg_file *file;

View File

@@ -108,8 +108,10 @@ UCLASS_DRIVER(testbus) = {
.child_pre_probe = testbus_child_pre_probe_uclass, .child_pre_probe = testbus_child_pre_probe_uclass,
.child_post_probe = testbus_child_post_probe_uclass, .child_post_probe = testbus_child_post_probe_uclass,
/* This is for dtoc testing only */ .per_device_auto = sizeof(struct dm_test_uclass_priv),
.per_device_plat_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) 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) ulong transfer_len)
{ {
debug("%s: lba=%lx, transfer_len=%lx\n", __func__, lba, transfer_len); debug("%s: lba=%lx, transfer_len=%lx\n", __func__, lba, transfer_len);
priv->read_len = transfer_len;
if (priv->fd != -1) { if (priv->fd != -1) {
os_lseek(priv->fd, lba * SANDBOX_FLASH_BLOCK_LEN, OS_SEEK_SET); os_lseek(priv->fd, lba * SANDBOX_FLASH_BLOCK_LEN, OS_SEEK_SET);
priv->read_len = transfer_len;
setup_response(priv, priv->buff, setup_response(priv, priv->buff,
transfer_len * SANDBOX_FLASH_BLOCK_LEN); transfer_len * SANDBOX_FLASH_BLOCK_LEN);
} else { } else {
@@ -336,6 +336,9 @@ static int sandbox_flash_bulk(struct udevice *dev, struct usb_device *udev,
if (priv->read_len) { if (priv->read_len) {
ulong bytes_read; ulong bytes_read;
if (priv->fd == -1)
return -EIO;
bytes_read = os_read(priv->fd, buff, len); bytes_read = os_read(priv->fd, buff, len);
if (bytes_read != len) if (bytes_read != len)
return -EIO; return -EIO;

View File

@@ -11,9 +11,11 @@
#ifndef __BINMAN_SYM_H #ifndef __BINMAN_SYM_H
#define __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) #define BINMAN_SYM_MISSING (-1UL)
#ifdef CONFIG_BINMAN #if CONFIG_IS_ENABLED(BINMAN_SYMBOLS)
/** /**
* binman_symname() - Internal function to get a binman symbol name * binman_symname() - Internal function to get a binman symbol name
@@ -62,6 +64,37 @@
__attribute__((aligned(4), weak, unused, \ __attribute__((aligned(4), weak, unused, \
section(".binman_sym"))) 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 * binman_sym() - Access a previously declared symbol
* *
@@ -72,12 +105,16 @@
* @_type: Type f the symbol (e.g. unsigned long) * @_type: Type f the symbol (e.g. unsigned long)
* @entry_name: Name of the entry to look for (e.g. 'u_boot_spl') * @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') * @_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) \ #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) #define binman_sym_declare(_type, _entry_name, _prop_name)
@@ -85,8 +122,12 @@
#define binman_sym_extern(_type, _entry_name, _prop_name) #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 #define binman_sym(_type, _entry_name, _prop_name) BINMAN_SYM_MISSING
#endif /* BINMAN */ #endif /* CONFIG_IS_ENABLED(BINMAN_SYMBOLS) */
#endif #endif

View File

@@ -3,8 +3,66 @@
* This provides a standard way of passing information between boot phases * This provides a standard way of passing information between boot phases
* (TPL -> SPL -> U-Boot proper.) * (TPL -> SPL -> U-Boot proper.)
* *
* A list of blobs of data, tagged with their owner. The list resides in memory * It consists of a list of blobs of data, tagged with their owner / contents.
* and can be updated by SPL, U-Boot, etc. * 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 * Copyright 2018 Google, Inc
* Written by Simon Glass <sjg@chromium.org> * Written by Simon Glass <sjg@chromium.org>

View File

@@ -11,6 +11,7 @@
#define _DM_DEVICE_H #define _DM_DEVICE_H
#include <dm/ofnode.h> #include <dm/ofnode.h>
#include <dm/tag.h>
#include <dm/uclass-id.h> #include <dm/uclass-id.h>
#include <fdtdec.h> #include <fdtdec.h>
#include <linker_lists.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); 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 * 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); 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 * 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); const char *ofnode_conf_read_str(const char *prop_name);
/** #else /* CONFIG_DM */
* ofnode_get_phy_node() - Get PHY node for a MAC (if not fixed-link) static inline bool ofnode_conf_read_bool(const char *prop_name)
* {
* This function parses PHY handle from the Ethernet controller's ofnode return false;
* (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);
/** static inline int ofnode_conf_read_int(const char *prop_name, int default_val)
* ofnode_read_phy_mode() - Read PHY connection type from a MAC node {
* return default_val;
* This function parses the "phy-mode" / "phy-connection-type" property and }
* returns the corresponding PHY interface type.
* static inline const char *ofnode_conf_read_str(const char *prop_name)
* @mac_node: ofnode containing the property {
* Return: one of PHY_INTERFACE_MODE_* constants, PHY_INTERFACE_MODE_NA on return NULL;
* error }
*/ #endif /* CONFIG_DM */
phy_interface_t ofnode_read_phy_mode(ofnode mac_node);
#endif #endif

View File

@@ -9,11 +9,49 @@
#ifndef _DM_ROOT_H_ #ifndef _DM_ROOT_H_
#define _DM_ROOT_H_ #define _DM_ROOT_H_
#include <dm/tag.h>
struct udevice; struct udevice;
/* Head of the uclass list if CONFIG_OF_PLATDATA_INST is enabled */ /* Head of the uclass list if CONFIG_OF_PLATDATA_INST is enabled */
extern struct list_head uclass_head; 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 * 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); 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 #endif

View File

@@ -10,11 +10,23 @@
#include <linux/list.h> #include <linux/list.h>
#include <linux/types.h> #include <linux/types.h>
struct dm_stats;
struct udevice; struct udevice;
enum dm_tag_t { 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 */ /* EFI_LOADER */
DM_TAG_EFI = 0, DM_TAG_EFI = DM_TAG_ATTACH_COUNT,
DM_TAG_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); 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 */ #endif /* _DM_TAG_H */

View File

@@ -92,6 +92,13 @@ struct dm_test_uclass_priv {
int total_add; 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 * struct dm_test_parent_data - parent's information on each child
* *

View File

@@ -6,6 +6,8 @@
#ifndef __DM_UTIL_H #ifndef __DM_UTIL_H
#define __DM_UTIL_H #define __DM_UTIL_H
struct dm_stats;
#if CONFIG_IS_ENABLED(DM_WARN) #if CONFIG_IS_ENABLED(DM_WARN)
#define dm_warn(fmt...) log(LOGC_DM, LOGL_WARNING, ##fmt) #define dm_warn(fmt...) log(LOGC_DM, LOGL_WARNING, ##fmt)
#else #else
@@ -25,7 +27,7 @@ struct list_head;
int list_count_items(struct list_head *head); int list_count_items(struct list_head *head);
/* Dump out a tree of all devices */ /* 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 */ /* Dump out a list of uclasses and their devices */
void dm_dump_uclass(void); 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 */ /* Dump out a list of drivers with static platform data */
void dm_dump_static_driver_info(void); 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) #if CONFIG_IS_ENABLED(OF_PLATDATA_INST) && CONFIG_IS_ENABLED(READ_ONLY)
void *dm_priv_to_rw(void *priv); void *dm_priv_to_rw(void *priv);
#else #else

View File

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

View File

@@ -25,6 +25,9 @@ try:
except: # pragma: no cover except: # pragma: no cover
ELF_TOOLS = False 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: # Information about an EFL symbol:
# section (str): Name of the section containing this symbol # section (str): Name of the section containing this symbol
# address (int): Address of the symbol (its value) # 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' % raise ValueError('%s has size %d: only 4 and 8 are supported' %
(msg, sym.size)) (msg, sym.size))
# Look up the symbol in our entry tables. if name == '_binman_sym_magic':
value = section.GetImage().LookupImageSymbol(name, sym.weak, msg, value = BINMAN_SYM_MAGIC_VALUE
base.address) else:
# Look up the symbol in our entry tables.
value = section.GetImage().LookupImageSymbol(name, sym.weak,
msg, base.address)
if value is None: if value is None:
value = -1 value = -1
pack_string = pack_string.lower() pack_string = pack_string.lower()

View File

@@ -127,7 +127,7 @@ class TestElf(unittest.TestCase):
elf_fname = self.ElfTestFile('u_boot_binman_syms') elf_fname = self.ElfTestFile('u_boot_binman_syms')
with self.assertRaises(ValueError) as e: with self.assertRaises(ValueError) as e:
elf.LookupAndWriteSymbols(elf_fname, entry, section) 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)) 'is a', str(e.exception))
def testMissingImageStart(self): def testMissingImageStart(self):
@@ -161,18 +161,20 @@ class TestElf(unittest.TestCase):
This should produce -1 values for all thress symbols, taking up the This should produce -1 values for all thress symbols, taking up the
first 16 bytes of the image. first 16 bytes of the image.
""" """
entry = FakeEntry(24) entry = FakeEntry(28)
section = FakeSection(sym_value=None) section = FakeSection(sym_value=None)
elf_fname = self.ElfTestFile('u_boot_binman_syms') elf_fname = self.ElfTestFile('u_boot_binman_syms')
elf.LookupAndWriteSymbols(elf_fname, entry, section) elf.LookupAndWriteSymbols(elf_fname, entry, section)
self.assertEqual(tools.get_bytes(255, 20) + tools.get_bytes(ord('a'), 4), expected = (struct.pack('<L', elf.BINMAN_SYM_MAGIC_VALUE) +
entry.data) tools.get_bytes(255, 20) +
tools.get_bytes(ord('a'), 4))
self.assertEqual(expected, entry.data)
def testDebug(self): def testDebug(self):
"""Check that enabling debug in the elf module produced debug output""" """Check that enabling debug in the elf module produced debug output"""
try: try:
tout.init(tout.DEBUG) tout.init(tout.DEBUG)
entry = FakeEntry(20) entry = FakeEntry(24)
section = FakeSection() section = FakeSection()
elf_fname = self.ElfTestFile('u_boot_binman_syms') elf_fname = self.ElfTestFile('u_boot_binman_syms')
with test_util.capture_sys_output() as (stdout, stderr): 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 # Contents of test files, corresponding to different entry types
U_BOOT_DATA = b'1234' U_BOOT_DATA = b'1234'
U_BOOT_IMG_DATA = b'img' U_BOOT_IMG_DATA = b'img'
U_BOOT_SPL_DATA = b'56780123456789abcdefghi' U_BOOT_SPL_DATA = b'56780123456789abcdefghijklm'
U_BOOT_TPL_DATA = b'tpl9876543210fedcbazyw' U_BOOT_TPL_DATA = b'tpl9876543210fedcbazywvuts'
BLOB_DATA = b'89' BLOB_DATA = b'89'
ME_DATA = b'0abcd' ME_DATA = b'0abcd'
VGA_DATA = b'vga' VGA_DATA = b'vga'
@@ -1406,8 +1406,9 @@ class TestFunctional(unittest.TestCase):
elf_fname = self.ElfTestFile('u_boot_binman_syms') elf_fname = self.ElfTestFile('u_boot_binman_syms')
syms = elf.GetSymbols(elf_fname, ['binman', 'image']) syms = elf.GetSymbols(elf_fname, ['binman', 'image'])
addr = elf.GetSymbolAddress(elf_fname, '__image_copy_start') 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, self.assertEqual(syms['_binman_u_boot_spl_any_prop_offset'].address,
addr) addr + 4)
self._SetupSplElf('u_boot_binman_syms') self._SetupSplElf('u_boot_binman_syms')
data = self._DoReadFileDtb(dts, entry_args=entry_args, 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 # 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, # Note that image_pos is adjusted by the base address of the image,
# which is 0x10 in our test image # which is 0x10 in our test image
sym_values = struct.pack('<LQLL', 0x00, sym_values = struct.pack('<LLQLL', elf.BINMAN_SYM_MAGIC_VALUE,
u_boot_offset + len(U_BOOT_DATA), 0x00, u_boot_offset + len(U_BOOT_DATA),
0x10 + u_boot_offset, 0x04) 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 + tools.get_bytes(0xff, 1) + U_BOOT_DATA + sym_values +
base_data[20:]) base_data[24:])
self.assertEqual(expected, data) self.assertEqual(expected, data)
def testSymbols(self): def testSymbols(self):
"""Test binman can assign symbols embedded in U-Boot""" """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): def testSymbolsNoDtb(self):
"""Test binman can assign symbols embedded in U-Boot SPL""" """Test binman can assign symbols embedded in U-Boot SPL"""
@@ -3610,20 +3611,20 @@ class TestFunctional(unittest.TestCase):
def _CheckSymbolsTplSection(self, dts, expected_vals): def _CheckSymbolsTplSection(self, dts, expected_vals):
data = self._DoReadFile(dts) 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) 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]) self.assertEqual(expected1, data[:upto1])
upto2 = upto1 + 1 + len(U_BOOT_SPL_DATA) 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]) 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 expected3 = tools.get_bytes(0xff, 1) + U_BOOT_DATA
self.assertEqual(expected3, data[upto2:upto3]) 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)]) self.assertEqual(expected4, data[upto3:upto3 + len(U_BOOT_TPL_DATA)])
def testSymbolsTplSection(self): def testSymbolsTplSection(self):
@@ -3631,14 +3632,14 @@ class TestFunctional(unittest.TestCase):
self._SetupSplElf('u_boot_binman_syms') self._SetupSplElf('u_boot_binman_syms')
self._SetupTplElf('u_boot_binman_syms') self._SetupTplElf('u_boot_binman_syms')
self._CheckSymbolsTplSection('149_symbols_tpl.dts', self._CheckSymbolsTplSection('149_symbols_tpl.dts',
[0x04, 0x1c, 0x10 + 0x34, 0x04]) [0x04, 0x20, 0x10 + 0x3c, 0x04])
def testSymbolsTplSectionX86(self): def testSymbolsTplSectionX86(self):
"""Test binman can assign symbols in a section with end-at-4gb""" """Test binman can assign symbols in a section with end-at-4gb"""
self._SetupSplElf('u_boot_binman_syms_x86') self._SetupSplElf('u_boot_binman_syms_x86')
self._SetupTplElf('u_boot_binman_syms_x86') self._SetupTplElf('u_boot_binman_syms_x86')
self._CheckSymbolsTplSection('155_symbols_tpl_x86.dts', self._CheckSymbolsTplSection('155_symbols_tpl_x86.dts',
[0xffffff04, 0xffffff1c, 0xffffff34, [0xffffff04, 0xffffff20, 0xffffff3c,
0x04]) 0x04])
def testPackX86RomIfwiSectiom(self): def testPackX86RomIfwiSectiom(self):
@@ -4488,7 +4489,7 @@ class TestFunctional(unittest.TestCase):
def testSymbolsSubsection(self): def testSymbolsSubsection(self):
"""Test binman can assign symbols from a subsection""" """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): def testReadImageEntryArg(self):
"""Test reading an image that would need an entry arg to generate""" """Test reading an image that would need an entry arg to generate"""

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -21,7 +21,7 @@ CC = $(CROSS_COMPILE)gcc
OBJCOPY = $(CROSS_COMPILE)objcopy OBJCOPY = $(CROSS_COMPILE)objcopy
VPATH := $(SRC) VPATH := $(SRC)
CFLAGS := -march=i386 -m32 -nostdlib -I $(SRC)../../../include \ CFLAGS := -march=i386 -m32 -nostdlib -I $(SRC)../../../include -I $(SRC) \
-Wl,--no-dynamic-linker -Wl,--no-dynamic-linker
LDS_UCODE := -T $(SRC)u_boot_ucode_ptr.lds 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. * 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> #include <binman_sym.h>
DECLARE_BINMAN_MAGIC_SYM;
binman_sym_declare(unsigned long, u_boot_spl_any, offset); 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 long, u_boot_spl2, offset);
binman_sym_declare(unsigned long, u_boot_any, image_pos); 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. * 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> #include <binman_sym.h>
DECLARE_BINMAN_MAGIC_SYM;
binman_sym_declare(char, u_boot_spl, pos); binman_sym_declare(char, u_boot_spl, pos);

View File

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

View File

@@ -31,6 +31,7 @@ from subunit import ProtocolTestCase, TestProtocolClient
from subunit.test_results import AutoTimingTestResultDecorator from subunit.test_results import AutoTimingTestResultDecorator
from testtools import ConcurrentTestSuite, iterate_tests from testtools import ConcurrentTestSuite, iterate_tests
from testtools.content import TracebackContent, text_content
_all__ = [ _all__ = [
@@ -43,11 +44,81 @@ _all__ = [
CPU_COUNT = cpu_count() 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`. """Implementation of `make_tests` used to construct `ConcurrentTestSuite`.
:param concurrency_num: number of processes to use. :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): def do_fork(suite):
"""Take suite and start up multiple runners by forking (Unix only). """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). # child actually gets keystrokes for pdb etc).
sys.stdin.close() sys.stdin.close()
subunit_result = AutoTimingTestResultDecorator( subunit_result = AutoTimingTestResultDecorator(
TestProtocolClient(stream) test_protocol_client_class(stream)
) )
process_suite.run(subunit_result) process_suite.run(subunit_result)
except: except:
@@ -93,7 +164,13 @@ def fork_for_tests(concurrency_num=CPU_COUNT):
else: else:
os.close(c2pwrite) os.close(c2pwrite)
stream = os.fdopen(c2pread, 'rb') 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) result.append(test)
return result return result
return do_fork return do_fork

View File

@@ -24,7 +24,6 @@ see doc/driver-model/of-plat.rst
from argparse import ArgumentParser from argparse import ArgumentParser
import os import os
import sys import sys
import unittest
# Bring in the patman libraries # Bring in the patman libraries
our_path = os.path.dirname(os.path.realpath(__file__)) 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_src_scan
from dtoc import test_dtoc from dtoc import test_dtoc
result = unittest.TestResult()
sys.argv = [sys.argv[0]] sys.argv = [sys.argv[0]]
test_name = args.files and args.files[0] or None test_name = args.files and args.files[0] or None
test_dtoc.setup() test_dtoc.setup()
test_util.run_test_suites( result = test_util.run_test_suites(
result, debug=True, verbosity=1, test_preserve_dirs=False, toolname='dtoc', debug=True, verbosity=1, test_preserve_dirs=False,
processes=processes, test_name=test_name, toolpath=[], processes=processes, test_name=test_name, toolpath=[],
class_and_module_list=[test_dtoc.TestDtoc,test_src_scan.TestSrcScan]) 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(): def RunTestCoverage():
"""Run the tests and check that we get 100% coverage""" """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)] u8 _denx_u_boot_test_bus_priv_some_bus[sizeof(struct dm_test_priv)]
\t__attribute__ ((section (".priv_data"))); \t__attribute__ ((section (".priv_data")));
#include <dm/test.h> #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"))); \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> #include <test.h>
DM_DEVICE_INST(some_bus) = { DM_DEVICE_INST(some_bus) = {
@@ -628,6 +631,7 @@ DM_DEVICE_INST(some_bus) = {
\t.driver_data\t= DM_TEST_TYPE_FIRST, \t.driver_data\t= DM_TEST_TYPE_FIRST,
\t.priv_\t\t= _denx_u_boot_test_bus_priv_some_bus, \t.priv_\t\t= _denx_u_boot_test_bus_priv_some_bus,
\t.uclass\t\t= DM_UCLASS_REF(testbus), \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.uclass_node\t= {
\t\t.prev = &DM_UCLASS_REF(testbus)->dev_head, \t\t.prev = &DM_UCLASS_REF(testbus)->dev_head,
\t\t.next = &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:
args: List of positional args provided to fdt. This can hold a test args: List of positional args provided to fdt. This can hold a test
name to execute (as in 'fdt -t testFdt', for example) 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) Returns:
for _, err in result.errors: Return code, 0 on success
print(err) """
for _, err in result.failures: test_name = args and args[0] or None
print(err) 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__': if __name__ != '__main__':
sys.exit(1) sys.exit(1)
@@ -816,6 +808,7 @@ parser.add_option('-T', '--test-coverage', action='store_true',
# Run our meagre tests # Run our meagre tests
if options.test: if options.test:
RunTests(args) ret_code = RunTests(args)
sys.exit(ret_code)
elif options.test_coverage: elif options.test_coverage:
RunTestCoverage() RunTestCoverage()

View File

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

View File

@@ -246,8 +246,10 @@ def _UpdateDefaults(main_parser, config):
# Collect the defaults from each parser # Collect the defaults from each parser
defaults = {} defaults = {}
parser_defaults = []
for parser in parsers: for parser in parsers:
pdefs = parser.parse_known_args()[0] pdefs = parser.parse_known_args()[0]
parser_defaults.append(pdefs)
defaults.update(vars(pdefs)) defaults.update(vars(pdefs))
# Go through the settings and collect defaults # Go through the settings and collect defaults
@@ -264,8 +266,11 @@ def _UpdateDefaults(main_parser, config):
else: else:
print("WARNING: Unknown setting %s" % name) 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) 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): def _ReadAliasFile(fname):
"""Read in the U-Boot git alias file if it exists. """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 from io import StringIO
buffer_outputs = True
use_concurrent = True use_concurrent = True
try: try:
from concurrencytest.concurrencytest import ConcurrentTestSuite from concurrencytest.concurrencytest import ConcurrentTestSuite
@@ -102,49 +103,85 @@ def capture_sys_output():
sys.stdout, sys.stderr = old_out, old_err sys.stdout, sys.stderr = old_out, old_err
def report_result(toolname:str, test_name: str, result: unittest.TestResult): class FullTextTestResult(unittest.TextTestResult):
"""Report the results from a suite of tests """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: Args:
toolname: Name of the tool that ran the tests stream: A file-like object to write results to
test_name: Name of test that was run, or None for all descriptions (bool): True to print descriptions with test names
result: A unittest.TestResult object containing the results 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 def __init__(self, stream, descriptions, verbosity):
# ImportError or AttributeError occurs while traversing name then a self.verbosity = verbosity
# synthetic test that raises that error when run will be returned. These super().__init__(stream, descriptions, verbosity)
# errors are included in the errors accumulated by result.errors.
if test_name:
errors = []
for test, err in result.errors: def printErrors(self):
if ("has no attribute '%s'" % test_name) not in err: "Called by TestRunner after test run to summarize the tests"
errors.append((test, err)) # The parent class doesn't keep unexpected successes in the same
result.testsRun -= 1 # format as the rest. Adapt it to what printErrorList expects.
result.errors = errors unexpected_successes = [
(test, 'Test was expected to fail, but succeeded.\n')
for test in self.unexpectedSuccesses
]
print(result) super().printErrors() # FAIL and ERROR
for test, err in result.errors: self.printErrorList('SKIP', self.skipped)
print(test.id(), err) self.printErrorList('XFAIL', self.expectedFailures)
for test, err in result.failures: self.printErrorList('XPASS', unexpected_successes)
print(err, result.failures)
if result.skipped: def addError(self, test, err):
print('%d %s test%s SKIPPED:' % (len(result.skipped), toolname, """Called when an error has occurred."""
's' if len(result.skipped) > 1 else '')) super().addError(test, err)
for skip_info in result.skipped: self._mirrorOutput &= self.verbosity >= 3
print('%s: %s' % (skip_info[0], skip_info[1]))
if result.errors or result.failures: def addFailure(self, test, err):
print('%s tests FAILED' % toolname) """Called when a test has failed."""
return 1 super().addFailure(test, err)
return 0 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): test_name, toolpath, class_and_module_list):
"""Run a series of test suites and collect the results """Run a series of test suites and collect the results
Args: 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 debug: True to enable debugging, which shows a full stack trace on error
verbosity: Verbosity level to use (0-4) verbosity: Verbosity level to use (0-4)
test_preserve_dirs: True to preserve the input directory used by tests 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 class_and_module_list: List of test classes (type class) and module
names (type str) to run 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]] sys.argv = [sys.argv[0]]
if debug: if debug:
sys.argv.append('-D') sys.argv.append('-D')
@@ -174,6 +206,22 @@ def run_test_suites(result, debug, verbosity, test_preserve_dirs, processes,
suite = unittest.TestSuite() suite = unittest.TestSuite()
loader = unittest.TestLoader() 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: for module in class_and_module_list:
if isinstance(module, str): if isinstance(module, str):
continue 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, preserve_outdirs=test_preserve_dirs and test_name is not None,
toolpath=toolpath, verbosity=verbosity) toolpath=toolpath, verbosity=verbosity)
if test_name: 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)) suite.addTests(loader.loadTestsFromName(test_name, module))
except AttributeError:
continue
else: else:
suite.addTests(loader.loadTestsFromTestCase(module)) suite.addTests(loader.loadTestsFromTestCase(module))
if use_concurrent and processes != 1:
concurrent_suite = ConcurrentTestSuite(suite, print(f" Running {toolname} tests ".center(70, "="))
fork_for_tests(processes or multiprocessing.cpu_count())) result = runner.run(suite)
concurrent_suite.run(result) print()
else:
suite.run(result) return result