dtoc improvements to show better warnings

minor test build fixes
 sandbox fixes for SDL2 and running TPL
 bloblist resize feature
 binman multithreading
 -----BEGIN PGP SIGNATURE-----
 
 iQFFBAABCgAvFiEEslwAIq+Gp8wWVbYnfxc6PpAIreYFAmD41JkRHHNqZ0BjaHJv
 bWl1bS5vcmcACgkQfxc6PpAIrea8+wgArpcRqyC2nCYtdDl1q/8mWQabct2rUJVt
 rzIYzgyPgkRBLQjlE8xgEchzAPOuw0YzQnEz3T80wepBPIy53+QYXaqumd9iuhtm
 B1x4r/GumqS4qgn9Pfqxabw2eP8DNbxClc14ExDx3zR1pUwp0DRvnYQV/w+W2RQp
 nWFKDdhdnkxuXApiVc01RF/9I+IygstVl1TUklxlw9EGkdpirczIX5cP2lhW9rsV
 Cm4fPz4V6AphiUQ4RpllossBNVHHlbVyKOtdGWe4CrERH8Zv8tIVRVeHCr0GydPr
 ORhIZTk7hmwTav3Zi4XAQ434UZcRVrJFRkZvv8TpmMhMTVb8+gyZaQ==
 =F/gP
 -----END PGP SIGNATURE-----

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

dtoc improvements to show better warnings
minor test build fixes
sandbox fixes for SDL2 and running TPL
bloblist resize feature
binman multithreading
This commit is contained in:
Tom Rini 2021-07-22 11:15:52 -04:00
commit a15fa1ba67
48 changed files with 1210 additions and 164 deletions

View file

@ -226,7 +226,7 @@ int os_setup_signal_handlers(void)
act.sa_sigaction = os_signal_handler; act.sa_sigaction = os_signal_handler;
sigemptyset(&act.sa_mask); sigemptyset(&act.sa_mask);
act.sa_flags = SA_SIGINFO | SA_NODEFER; act.sa_flags = SA_SIGINFO;
if (sigaction(SIGILL, &act, NULL) || if (sigaction(SIGILL, &act, NULL) ||
sigaction(SIGBUS, &act, NULL) || sigaction(SIGBUS, &act, NULL) ||
sigaction(SIGSEGV, &act, NULL)) sigaction(SIGSEGV, &act, NULL))
@ -783,12 +783,14 @@ int os_jump_to_image(const void *dest, int size)
return os_jump_to_file(fname, true); return os_jump_to_file(fname, true);
} }
int os_find_u_boot(char *fname, int maxlen, bool use_img) int os_find_u_boot(char *fname, int maxlen, bool use_img,
const char *cur_prefix, const char *next_prefix)
{ {
struct sandbox_state *state = state_get_current(); struct sandbox_state *state = state_get_current();
const char *progname = state->argv[0]; const char *progname = state->argv[0];
int len = strlen(progname); int len = strlen(progname);
const char *suffix; char subdir[10];
char *suffix;
char *p; char *p;
int fd; int fd;
@ -798,45 +800,36 @@ int os_find_u_boot(char *fname, int maxlen, bool use_img)
strcpy(fname, progname); strcpy(fname, progname);
suffix = fname + len - 4; suffix = fname + len - 4;
/* If we are TPL, boot to SPL */ /* Change the existing suffix to the new one */
if (!strcmp(suffix, "-tpl")) { if (*suffix != '-')
fname[len - 3] = 's'; return -EINVAL;
fd = os_open(fname, O_RDONLY);
if (fd >= 0) {
close(fd);
return 0;
}
/* Look for 'u-boot-spl' in the spl/ directory */ if (*next_prefix)
p = strstr(fname, "/spl/"); strcpy(suffix + 1, next_prefix); /* e.g. "-tpl" to "-spl" */
if (p) { else
p[1] = 's'; *suffix = '\0'; /* e.g. "-spl" to "" */
fd = os_open(fname, O_RDONLY); fd = os_open(fname, O_RDONLY);
if (fd >= 0) { if (fd >= 0) {
close(fd); close(fd);
return 0; return 0;
}
}
return -ENOENT;
} }
/* Look for 'u-boot' in the same directory as 'u-boot-spl' */ /*
if (!strcmp(suffix, "-spl")) { * We didn't find it, so try looking for 'u-boot-xxx' in the xxx/
fname[len - 4] = '\0'; * directory. Replace the old dirname with the new one.
fd = os_open(fname, O_RDONLY); */
if (fd >= 0) { snprintf(subdir, sizeof(subdir), "/%s/", cur_prefix);
close(fd); p = strstr(fname, subdir);
return 0;
}
}
/* Look for 'u-boot' in the parent directory of spl/ */
p = strstr(fname, "spl/");
if (p) { if (p) {
/* Remove the "spl" characters */ if (*next_prefix)
memmove(p, p + 4, strlen(p + 4) + 1); /* e.g. ".../tpl/u-boot-spl" to "../spl/u-boot-spl" */
memcpy(p + 1, next_prefix, strlen(next_prefix));
else
/* e.g. ".../spl/u-boot" to ".../u-boot" */
strcpy(p, p + 1 + strlen(cur_prefix));
if (use_img) if (use_img)
strcat(p, ".img"); strcat(p, ".img");
fd = os_open(fname, O_RDONLY); fd = os_open(fname, O_RDONLY);
if (fd >= 0) { if (fd >= 0) {
close(fd); close(fd);

View file

@ -123,6 +123,9 @@ int sandbox_sdl_init_display(int width, int height, int log2_bpp,
sdl.vis_height = sdl.height; sdl.vis_height = sdl.height;
} }
if (!SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"))
printf("Unable to init hinting: %s", SDL_GetError());
sdl.depth = 1 << log2_bpp; sdl.depth = 1 << log2_bpp;
sdl.pitch = sdl.width * sdl.depth / 8; sdl.pitch = sdl.width * sdl.depth / 8;
SDL_Window *screen = SDL_CreateWindow("U-Boot", SDL_WINDOWPOS_UNDEFINED, SDL_Window *screen = SDL_CreateWindow("U-Boot", SDL_WINDOWPOS_UNDEFINED,
@ -164,8 +167,29 @@ int sandbox_sdl_init_display(int width, int height, int log2_bpp,
int sandbox_sdl_sync(void *lcd_base) int sandbox_sdl_sync(void *lcd_base)
{ {
struct SDL_Rect rect;
int ret;
if (!sdl.texture)
return 0;
SDL_RenderClear(sdl.renderer);
SDL_UpdateTexture(sdl.texture, NULL, lcd_base, sdl.pitch); SDL_UpdateTexture(sdl.texture, NULL, lcd_base, sdl.pitch);
SDL_RenderCopy(sdl.renderer, sdl.texture, NULL, NULL); ret = SDL_RenderCopy(sdl.renderer, sdl.texture, NULL, NULL);
if (ret) {
printf("SDL copy %d: %s\n", ret, SDL_GetError());
return -EIO;
}
/*
* On some machines this does not appear. Draw an empty rectangle which
* seems to fix that.
*/
rect.x = 0;
rect.y = 0;
rect.w = 0;
rect.h = 0;
SDL_RenderDrawRect(sdl.renderer, &rect);
SDL_RenderPresent(sdl.renderer); SDL_RenderPresent(sdl.renderer);
sandbox_sdl_poll_events(); sandbox_sdl_poll_events();

View file

@ -17,6 +17,20 @@
DECLARE_GLOBAL_DATA_PTR; DECLARE_GLOBAL_DATA_PTR;
int sandbox_find_next_phase(char *fname, int maxlen, bool use_img)
{
const char *cur_prefix, *next_prefix;
int ret;
cur_prefix = spl_phase_prefix(spl_phase());
next_prefix = spl_phase_prefix(spl_next_phase());
ret = os_find_u_boot(fname, maxlen, use_img, cur_prefix, next_prefix);
if (ret)
return log_msg_ret("find", ret);
return 0;
}
/* SPL / TPL init function */ /* SPL / TPL init function */
void board_init_f(ulong flag) void board_init_f(ulong flag)
{ {
@ -37,7 +51,7 @@ static int spl_board_load_image(struct spl_image_info *spl_image,
char fname[256]; char fname[256];
int ret; int ret;
ret = os_find_u_boot(fname, sizeof(fname), false); ret = sandbox_find_next_phase(fname, sizeof(fname), false);
if (ret) { if (ret) {
printf("(%s not found, error %d)\n", fname, ret); printf("(%s not found, error %d)\n", fname, ret);
return ret; return ret;

View file

@ -819,6 +819,7 @@
mmc2 { mmc2 {
compatible = "sandbox,mmc"; compatible = "sandbox,mmc";
non-removable;
}; };
mmc1 { mmc1 {

View file

@ -12,4 +12,17 @@ enum {
BOOT_DEVICE_BOARD, BOOT_DEVICE_BOARD,
}; };
/**
* sandbox_find_next_phase() - Find the next phase of U-Boot
*
* This function is intended to be called from within sandbox SPL. It uses
* a few rules to find the filename of the next U-Boot phase. See also
* os_find_u_boot().
*
* @fname: place to put full path to U-Boot
* @maxlen: maximum size of @fname
* @use_img: select the 'u-boot.img' file instead of the 'u-boot' ELF file
*/
int sandbox_find_next_phase(char *fname, int maxlen, bool use_img);
#endif #endif

View file

@ -322,6 +322,14 @@ config LOGF_FUNC
Show the function name in log messages by default. This value can Show the function name in log messages by default. This value can
be overridden using the 'log format' command. be overridden using the 'log format' command.
config LOGF_FUNC_PAD
int "Number of characters to use for function"
default 20
help
Sets the field width to use when showing the function. Set this to
a larger value if you have lots of long function names, and want
things to line up.
config LOG_SYSLOG config LOG_SYSLOG
bool "Log output to syslog server" bool "Log output to syslog server"
depends on NET depends on NET
@ -724,7 +732,7 @@ config BLOBLIST_SIZE
config BLOBLIST_ADDR config BLOBLIST_ADDR
hex "Address of bloblist" hex "Address of bloblist"
depends on BLOBLIST depends on BLOBLIST
default 0xe000 if SANDBOX default 0xc000 if SANDBOX
help help
Sets the address of the bloblist, set up by the first part of U-Boot Sets the address of the bloblist, set up by the first part of U-Boot
which runs. Subsequent U-Boot stages typically use the same address. which runs. Subsequent U-Boot stages typically use the same address.

View file

@ -57,13 +57,22 @@ static struct bloblist_rec *bloblist_first_blob(struct bloblist_hdr *hdr)
return (struct bloblist_rec *)((void *)hdr + hdr->hdr_size); return (struct bloblist_rec *)((void *)hdr + hdr->hdr_size);
} }
static struct bloblist_rec *bloblist_next_blob(struct bloblist_hdr *hdr, static ulong bloblist_blob_end_ofs(struct bloblist_hdr *hdr,
struct bloblist_rec *rec) struct bloblist_rec *rec)
{ {
ulong offset; ulong offset;
offset = (void *)rec - (void *)hdr; offset = (void *)rec - (void *)hdr;
offset += rec->hdr_size + ALIGN(rec->size, BLOBLIST_ALIGN); offset += rec->hdr_size + ALIGN(rec->size, BLOBLIST_ALIGN);
return offset;
}
static struct bloblist_rec *bloblist_next_blob(struct bloblist_hdr *hdr,
struct bloblist_rec *rec)
{
ulong offset = bloblist_blob_end_ofs(hdr, rec);
if (offset >= hdr->alloced) if (offset >= hdr->alloced)
return NULL; return NULL;
return (struct bloblist_rec *)((void *)hdr + offset); return (struct bloblist_rec *)((void *)hdr + offset);
@ -109,7 +118,7 @@ static int bloblist_addrec(uint tag, int size, int align,
/* Calculate the new allocated total */ /* Calculate the new allocated total */
new_alloced = data_start + ALIGN(size, align); new_alloced = data_start + ALIGN(size, align);
if (new_alloced >= hdr->size) { if (new_alloced > hdr->size) {
log(LOGC_BLOBLIST, LOGL_ERR, log(LOGC_BLOBLIST, LOGL_ERR,
"Failed to allocate %x bytes size=%x, need size=%x\n", "Failed to allocate %x bytes size=%x, need size=%x\n",
size, hdr->size, new_alloced); size, hdr->size, new_alloced);
@ -215,6 +224,64 @@ int bloblist_ensure_size_ret(uint tag, int *sizep, void **blobp)
return 0; return 0;
} }
static int bloblist_resize_rec(struct bloblist_hdr *hdr,
struct bloblist_rec *rec,
int new_size)
{
int expand_by; /* Number of bytes to expand by (-ve to contract) */
int new_alloced; /* New value for @hdr->alloced */
ulong next_ofs; /* Offset of the record after @rec */
expand_by = ALIGN(new_size - rec->size, BLOBLIST_ALIGN);
new_alloced = ALIGN(hdr->alloced + expand_by, BLOBLIST_ALIGN);
if (new_size < 0) {
log(LOGC_BLOBLIST, LOGL_DEBUG,
"Attempt to shrink blob size below 0 (%x)\n", new_size);
return log_msg_ret("size", -EINVAL);
}
if (new_alloced > hdr->size) {
log(LOGC_BLOBLIST, LOGL_ERR,
"Failed to allocate %x bytes size=%x, need size=%x\n",
new_size, hdr->size, new_alloced);
return log_msg_ret("alloc", -ENOSPC);
}
/* Move the following blobs up or down, if this is not the last */
next_ofs = bloblist_blob_end_ofs(hdr, rec);
if (next_ofs != hdr->alloced) {
memmove((void *)hdr + next_ofs + expand_by,
(void *)hdr + next_ofs, new_alloced - next_ofs);
}
hdr->alloced = new_alloced;
/* Zero the new part of the blob */
if (expand_by > 0) {
memset((void *)rec + rec->hdr_size + rec->size, '\0',
new_size - rec->size);
}
/* Update the size of this blob */
rec->size = new_size;
return 0;
}
int bloblist_resize(uint tag, int new_size)
{
struct bloblist_hdr *hdr = gd->bloblist;
struct bloblist_rec *rec;
int ret;
rec = bloblist_findrec(tag);
if (!rec)
return log_msg_ret("find", -ENOENT);
ret = bloblist_resize_rec(hdr, rec, new_size);
if (ret)
return log_msg_ret("resize", ret);
return 0;
}
static u32 bloblist_calc_chksum(struct bloblist_hdr *hdr) static u32 bloblist_calc_chksum(struct bloblist_hdr *hdr)
{ {
struct bloblist_rec *rec; struct bloblist_rec *rec;

View file

@ -1377,7 +1377,7 @@ int fit_image_verify(const void *fit, int image_noffset)
size_t size; size_t size;
char *err_msg = ""; char *err_msg = "";
if (strchr(name, '@')) { if (IS_ENABLED(CONFIG_FIT_SIGNATURE) && strchr(name, '@')) {
/* /*
* We don't support this since libfdt considers names with the * We don't support this since libfdt considers names with the
* name root but different @ suffix to be equal * name root but different @ suffix to be equal

View file

@ -38,7 +38,7 @@ static int log_console_emit(struct log_device *ldev, struct log_rec *rec)
if (fmt & BIT(LOGF_LINE)) if (fmt & BIT(LOGF_LINE))
printf("%d-", rec->line); printf("%d-", rec->line);
if (fmt & BIT(LOGF_FUNC)) if (fmt & BIT(LOGF_FUNC))
printf("%s()", rec->func); printf("%*s()", CONFIG_LOGF_FUNC_PAD, rec->func);
} }
if (fmt & BIT(LOGF_MSG)) if (fmt & BIT(LOGF_MSG))
printf("%s%s", add_space ? " " : "", rec->msg); printf("%s%s", add_space ? " " : "", rec->msg);

View file

@ -91,6 +91,16 @@ config SPL_SYS_REPORT_STACK_F_USAGE
occurrence of non 0xaa bytes. occurrence of non 0xaa bytes.
This default implementation works for stacks growing down only. This default implementation works for stacks growing down only.
config SPL_SHOW_ERRORS
bool "Show more information when something goes wrong"
help
This enabled more verbose error messages and checking when something
goes wrong in SPL. For example, it shows the error code when U-Boot
cannot be located. This can help to diagnose the problem and figure
out a fix, particularly during development.
This adds a small amount to SPL code size, perhaps 100 bytes.
menu "PowerPC and LayerScape SPL Boot options" menu "PowerPC and LayerScape SPL Boot options"
config SPL_NAND_BOOT config SPL_NAND_BOOT

View file

@ -593,32 +593,42 @@ static int spl_load_image(struct spl_image_info *spl_image,
* @spl_image: Place to put the image details if successful * @spl_image: Place to put the image details if successful
* @spl_boot_list: List of boot devices to try * @spl_boot_list: List of boot devices to try
* @count: Number of elements in spl_boot_list * @count: Number of elements in spl_boot_list
* @return 0 if OK, -ve on error * @return 0 if OK, -ENODEV if there were no boot devices
* if CONFIG_SHOW_ERRORS is enabled, returns -ENXIO if there were
* devices but none worked
*/ */
static int boot_from_devices(struct spl_image_info *spl_image, static int boot_from_devices(struct spl_image_info *spl_image,
u32 spl_boot_list[], int count) u32 spl_boot_list[], int count)
{ {
int ret = -ENODEV;
int i; int i;
for (i = 0; i < count && spl_boot_list[i] != BOOT_DEVICE_NONE; i++) { for (i = 0; i < count && spl_boot_list[i] != BOOT_DEVICE_NONE; i++) {
struct spl_image_loader *loader; struct spl_image_loader *loader;
int bootdev = spl_boot_list[i];
loader = spl_ll_find_loader(spl_boot_list[i]); if (CONFIG_IS_ENABLED(SHOW_ERRORS))
#if defined(CONFIG_SPL_SERIAL_SUPPORT) \ ret = -ENXIO;
&& defined(CONFIG_SPL_LIBCOMMON_SUPPORT) \ loader = spl_ll_find_loader(bootdev);
&& !defined(CONFIG_SILENT_CONSOLE) if (CONFIG_IS_ENABLED(SERIAL_SUPPORT) &&
if (loader) CONFIG_IS_ENABLED(LIBCOMMON_SUPPORT) &&
printf("Trying to boot from %s\n", loader->name); !IS_ENABLED(CONFIG_SILENT_CONSOLE)) {
else if (loader)
puts(SPL_TPL_PROMPT "Unsupported Boot Device!\n"); printf("Trying to boot from %s\n",
#endif spl_loader_name(loader));
else if (CONFIG_IS_ENABLED(SHOW_ERRORS))
printf(SPL_TPL_PROMPT
"Unsupported Boot Device %d\n", bootdev);
else
puts(SPL_TPL_PROMPT "Unsupported Boot Device!\n");
}
if (loader && !spl_load_image(spl_image, loader)) { if (loader && !spl_load_image(spl_image, loader)) {
spl_image->boot_device = spl_boot_list[i]; spl_image->boot_device = bootdev;
return 0; return 0;
} }
} }
return -ENODEV; return ret;
} }
#if defined(CONFIG_SPL_FRAMEWORK_BOARD_INIT_F) #if defined(CONFIG_SPL_FRAMEWORK_BOARD_INIT_F)
@ -710,9 +720,15 @@ void board_init_r(gd_t *dummy1, ulong dummy2)
spl_image.boot_device = BOOT_DEVICE_NONE; spl_image.boot_device = BOOT_DEVICE_NONE;
board_boot_order(spl_boot_list); board_boot_order(spl_boot_list);
if (boot_from_devices(&spl_image, spl_boot_list, ret = boot_from_devices(&spl_image, spl_boot_list,
ARRAY_SIZE(spl_boot_list))) { ARRAY_SIZE(spl_boot_list));
puts(SPL_TPL_PROMPT "failed to boot from all boot devices\n"); if (ret) {
if (CONFIG_IS_ENABLED(SHOW_ERRORS) &&
CONFIG_IS_ENABLED(LIBCOMMON_SUPPORT))
printf(SPL_TPL_PROMPT "failed to boot from all boot devices (err=%d)\n",
ret);
else
puts(SPL_TPL_PROMPT "failed to boot from all boot devices\n");
hang(); hang();
} }

View file

@ -510,7 +510,7 @@ that are mapped into that memory:
Addr Config Usage Addr Config Usage
======= ======================== =============================== ======= ======================== ===============================
0 CONFIG_SYS_FDT_LOAD_ADDR Device tree 0 CONFIG_SYS_FDT_LOAD_ADDR Device tree
e000 CONFIG_BLOBLIST_ADDR Blob list c000 CONFIG_BLOBLIST_ADDR Blob list
10000 CONFIG_MALLOC_F_ADDR Early memory allocation 10000 CONFIG_MALLOC_F_ADDR Early memory allocation
f0000 CONFIG_PRE_CON_BUF_ADDR Pre-console buffer f0000 CONFIG_PRE_CON_BUF_ADDR Pre-console buffer
100000 CONFIG_TRACE_EARLY_ADDR Early trace buffer (if enabled). Also used 100000 CONFIG_TRACE_EARLY_ADDR Early trace buffer (if enabled). Also used

View file

@ -597,6 +597,59 @@ as a macro included in the driver definition::
Problems
--------
In some cases you will you see something like this::
WARNING: the driver rockchip_rk3188_grf was not found in the driver list
The driver list is a list of drivers, each with a name. The name is in the
U_BOOT_DRIVER() declaration, repeated twice, one in brackets and once as the
.name member. For example, in the following declaration the driver name is
`rockchip_rk3188_grf`::
U_BOOT_DRIVER(rockchip_rk3188_grf) = {
.name = "rockchip_rk3188_grf",
.id = UCLASS_SYSCON,
.of_match = rk3188_syscon_ids + 1,
.bind = rk3188_syscon_bind_of_plat,
};
The first name U_BOOT_DRIVER(xx) is used to create a linker symbol so that the
driver can be accessed at build-time without any overhead. The second one
(.name = "xx") is used at runtime when something wants to print out the driver
name.
The dtoc tool expects to be able to find a driver for each compatible string in
the devicetree. For example, if the devicetree has::
grf: grf@20008000 {
compatible = "rockchip,rk3188-grf", "syscon";
reg = <0x20008000 0x200>;
u-boot,dm-spl;
};
then dtoc looks at the first compatible string ("rockchip,rk3188-grf"),
converts that to a C identifier (rockchip_rk3188_grf) and then looks for that.
Various things can cause dtoc to fail to find the driver and it tries to
warn about these. For example:
rockchip_rk3188_uart: Missing .compatible in drivers/serial/serial_rockchip.c
: WARNING: the driver rockchip_rk3188_uart was not found in the driver list
Without a compatible string a driver cannot be used by dtoc, even if the
compatible string is not actually needed at runtime.
If the problem is simply that there are multiple compatible strings, the
DM_DRIVER_ALIAS() macro can be used to tell dtoc about this and avoid a problem.
Checks are also made to confirm that the referenced driver has a .compatible
member and a .id member. The first provides the array of compatible strings and
the second provides the uclass ID.
Caveats Caveats
------- -------

View file

@ -540,6 +540,55 @@ int blk_next_free_devnum(enum if_type if_type)
return ret + 1; return ret + 1;
} }
static int blk_flags_check(struct udevice *dev, enum blk_flag_t req_flags)
{
const struct blk_desc *desc = dev_get_uclass_plat(dev);
enum blk_flag_t flags;
flags = desc->removable ? BLKF_REMOVABLE : BLKF_FIXED;
return flags & req_flags ? 0 : 1;
}
int blk_first_device_err(enum blk_flag_t flags, struct udevice **devp)
{
int ret;
for (ret = uclass_first_device_err(UCLASS_BLK, devp);
!ret;
ret = uclass_next_device_err(devp)) {
if (!blk_flags_check(*devp, flags))
return 0;
}
return -ENODEV;
}
int blk_next_device_err(enum blk_flag_t flags, struct udevice **devp)
{
int ret;
for (ret = uclass_next_device_err(devp);
!ret;
ret = uclass_next_device_err(devp)) {
if (!blk_flags_check(*devp, flags))
return 0;
}
return -ENODEV;
}
int blk_count_devices(enum blk_flag_t flag)
{
struct udevice *dev;
int count = 0;
blk_foreach_probe(flag, dev)
count++;
return count;
}
static int blk_claim_devnum(enum if_type if_type, int devnum) static int blk_claim_devnum(enum if_type if_type, int devnum)
{ {
struct udevice *dev; struct udevice *dev;

View file

@ -87,8 +87,10 @@ static int device_bind_common(struct udevice *parent, const struct driver *drv,
if (CONFIG_IS_ENABLED(OF_CONTROL) && if (CONFIG_IS_ENABLED(OF_CONTROL) &&
!CONFIG_IS_ENABLED(OF_PLATDATA)) { !CONFIG_IS_ENABLED(OF_PLATDATA)) {
if (uc->uc_drv->name && ofnode_valid(node)) { if (uc->uc_drv->name && ofnode_valid(node)) {
if (!dev_read_alias_seq(dev, &dev->seq_)) if (!dev_read_alias_seq(dev, &dev->seq_)) {
auto_seq = false; auto_seq = false;
log_debug(" - seq=%d\n", dev->seq_);
}
} }
} }
} }

View file

@ -31,6 +31,8 @@ int ofnode_read_fmap_entry(ofnode node, struct fmap_entry *entry)
if (prop) { if (prop) {
if (!strcmp(prop, "lz4")) if (!strcmp(prop, "lz4"))
entry->compress_algo = FMAP_COMPRESS_LZ4; entry->compress_algo = FMAP_COMPRESS_LZ4;
else if (!strcmp(prop, "lzma"))
entry->compress_algo = FMAP_COMPRESS_LZMA;
else else
return log_msg_ret("compression algo", -EINVAL); return log_msg_ret("compression algo", -EINVAL);
} else { } else {

View file

@ -329,7 +329,8 @@ static fdt_addr_t __ofnode_get_addr_size_index(ofnode node, int index,
{ {
int na, ns; int na, ns;
*size = FDT_SIZE_T_NONE; if (size)
*size = FDT_SIZE_T_NONE;
if (ofnode_is_np(node)) { if (ofnode_is_np(node)) {
const __be32 *prop_val; const __be32 *prop_val;
@ -340,6 +341,7 @@ static fdt_addr_t __ofnode_get_addr_size_index(ofnode node, int index,
&flags); &flags);
if (!prop_val) if (!prop_val)
return FDT_ADDR_T_NONE; return FDT_ADDR_T_NONE;
if (size) if (size)
*size = size64; *size = size64;
@ -359,8 +361,6 @@ static fdt_addr_t __ofnode_get_addr_size_index(ofnode node, int index,
index, na, ns, size, index, na, ns, size,
translate); translate);
} }
return FDT_ADDR_T_NONE;
} }
fdt_addr_t ofnode_get_addr_size_index(ofnode node, int index, fdt_size_t *size) fdt_addr_t ofnode_get_addr_size_index(ofnode node, int index, fdt_size_t *size)

View file

@ -754,17 +754,6 @@ int cros_ec_flash_protect(struct udevice *dev, uint32_t set_mask,
return 0; return 0;
} }
int cros_ec_entering_mode(struct udevice *dev, int mode)
{
int rc;
rc = ec_command(dev, EC_CMD_ENTERING_MODE, 0, &mode, sizeof(mode),
NULL, 0);
if (rc)
return -1;
return 0;
}
static int cros_ec_check_version(struct udevice *dev) static int cros_ec_check_version(struct udevice *dev)
{ {
struct cros_ec_dev *cdev = dev_get_uclass_priv(dev); struct cros_ec_dev *cdev = dev_get_uclass_priv(dev);
@ -1661,6 +1650,23 @@ int cros_ec_get_switches(struct udevice *dev)
return ret; return ret;
} }
int cros_ec_read_batt_charge(struct udevice *dev, uint *chargep)
{
struct ec_params_charge_state req;
struct ec_response_charge_state resp;
int ret;
req.cmd = CHARGE_STATE_CMD_GET_STATE;
ret = ec_command(dev, EC_CMD_CHARGE_STATE, 0, &req, sizeof(req),
&resp, sizeof(resp));
if (ret)
return log_msg_ret("read", ret);
*chargep = resp.get_state.batt_state_of_charge;
return 0;
}
UCLASS_DRIVER(cros_ec) = { UCLASS_DRIVER(cros_ec) = {
.id = UCLASS_CROS_EC, .id = UCLASS_CROS_EC,
.name = "cros-ec", .name = "cros-ec",

View file

@ -343,15 +343,13 @@ static int process_cmd(struct ec_state *ec,
switch (req->op) { switch (req->op) {
case EC_VBNV_CONTEXT_OP_READ: case EC_VBNV_CONTEXT_OP_READ:
/* TODO(sjg@chromium.org): Support full-size context */
memcpy(resp->block, ec->vbnv_context, memcpy(resp->block, ec->vbnv_context,
EC_VBNV_BLOCK_SIZE); EC_VBNV_BLOCK_SIZE_V2);
len = 16; len = EC_VBNV_BLOCK_SIZE_V2;
break; break;
case EC_VBNV_CONTEXT_OP_WRITE: case EC_VBNV_CONTEXT_OP_WRITE:
/* TODO(sjg@chromium.org): Support full-size context */
memcpy(ec->vbnv_context, req->block, memcpy(ec->vbnv_context, req->block,
EC_VBNV_BLOCK_SIZE); EC_VBNV_BLOCK_SIZE_V2);
len = 0; len = 0;
break; break;
default: default:
@ -496,9 +494,6 @@ static int process_cmd(struct ec_state *ec,
case EC_CMD_MKBP_STATE: case EC_CMD_MKBP_STATE:
len = cros_ec_keyscan(ec, resp_data); len = cros_ec_keyscan(ec, resp_data);
break; break;
case EC_CMD_ENTERING_MODE:
len = 0;
break;
case EC_CMD_GET_NEXT_EVENT: { case EC_CMD_GET_NEXT_EVENT: {
struct ec_response_get_next_event *resp = resp_data; struct ec_response_get_next_event *resp = resp_data;
@ -629,15 +624,19 @@ void cros_ec_check_keyboard(struct udevice *dev)
struct ec_state *ec = dev_get_priv(dev); struct ec_state *ec = dev_get_priv(dev);
ulong start; ulong start;
printf("Press keys for EC to detect on reset (ESC=recovery)..."); printf("\nPress keys for EC to detect on reset (ESC=recovery)...");
start = get_timer(0); start = get_timer(0);
while (get_timer(start) < 1000) while (get_timer(start) < 2000) {
; if (tstc()) {
putc('\n'); int ch = getchar();
if (!sandbox_sdl_key_pressed(KEY_ESC)) {
ec->recovery_req = true; if (ch == 0x1b) {
printf(" - EC requests recovery\n"); ec->recovery_req = true;
printf("EC requests recovery");
}
}
} }
putc('\n');
} }
/* Return the byte of EC switch states */ /* Return the byte of EC switch states */

View file

@ -136,14 +136,31 @@ static const struct dm_mmc_ops sandbox_mmc_ops = {
.get_cd = sandbox_mmc_get_cd, .get_cd = sandbox_mmc_get_cd,
}; };
int sandbox_mmc_probe(struct udevice *dev) static int sandbox_mmc_of_to_plat(struct udevice *dev)
{
struct sandbox_mmc_plat *plat = dev_get_plat(dev);
struct mmc_config *cfg = &plat->cfg;
struct blk_desc *blk;
int ret;
ret = mmc_of_parse(dev, cfg);
if (ret)
return ret;
blk = mmc_get_blk_desc(&plat->mmc);
if (blk)
blk->removable = !(cfg->host_caps & MMC_CAP_NONREMOVABLE);
return 0;
}
static int sandbox_mmc_probe(struct udevice *dev)
{ {
struct sandbox_mmc_plat *plat = dev_get_plat(dev); struct sandbox_mmc_plat *plat = dev_get_plat(dev);
return mmc_init(&plat->mmc); return mmc_init(&plat->mmc);
} }
int sandbox_mmc_bind(struct udevice *dev) static int sandbox_mmc_bind(struct udevice *dev)
{ {
struct sandbox_mmc_plat *plat = dev_get_plat(dev); struct sandbox_mmc_plat *plat = dev_get_plat(dev);
struct mmc_config *cfg = &plat->cfg; struct mmc_config *cfg = &plat->cfg;
@ -158,7 +175,7 @@ int sandbox_mmc_bind(struct udevice *dev)
return mmc_bind(dev, &plat->mmc, cfg); return mmc_bind(dev, &plat->mmc, cfg);
} }
int sandbox_mmc_unbind(struct udevice *dev) static int sandbox_mmc_unbind(struct udevice *dev)
{ {
mmc_unbind(dev); mmc_unbind(dev);
@ -177,6 +194,7 @@ U_BOOT_DRIVER(mmc_sandbox) = {
.ops = &sandbox_mmc_ops, .ops = &sandbox_mmc_ops,
.bind = sandbox_mmc_bind, .bind = sandbox_mmc_bind,
.unbind = sandbox_mmc_unbind, .unbind = sandbox_mmc_unbind,
.of_to_plat = sandbox_mmc_of_to_plat,
.probe = sandbox_mmc_probe, .probe = sandbox_mmc_probe,
.priv_auto = sizeof(struct sandbox_mmc_priv), .priv_auto = sizeof(struct sandbox_mmc_priv),
.plat_auto = sizeof(struct sandbox_mmc_plat), .plat_auto = sizeof(struct sandbox_mmc_plat),

View file

@ -19,6 +19,8 @@ typedef ulong lbaint_t;
#define LBAF "%" LBAFlength "x" #define LBAF "%" LBAFlength "x"
#define LBAFU "%" LBAFlength "u" #define LBAFU "%" LBAFlength "u"
struct udevice;
/* Interface types: */ /* Interface types: */
enum if_type { enum if_type {
IF_TYPE_UNKNOWN = 0, IF_TYPE_UNKNOWN = 0,
@ -683,4 +685,58 @@ const char *blk_get_if_type_name(enum if_type if_type);
int blk_common_cmd(int argc, char *const argv[], enum if_type if_type, int blk_common_cmd(int argc, char *const argv[], enum if_type if_type,
int *cur_devnump); int *cur_devnump);
enum blk_flag_t {
BLKF_FIXED = 1 << 0,
BLKF_REMOVABLE = 1 << 1,
BLKF_BOTH = BLKF_FIXED | BLKF_REMOVABLE,
};
/**
* blk_first_device_err() - Get the first block device
*
* The device returned is probed if necessary, and ready for use
*
* @flags: Indicates type of device to return
* @devp: Returns pointer to the first device in that uclass, or NULL if none
* @return 0 if found, -ENODEV if not found, other -ve on error
*/
int blk_first_device_err(enum blk_flag_t flags, struct udevice **devp);
/**
* blk_next_device_err() - Get the next block device
*
* The device returned is probed if necessary, and ready for use
*
* @flags: Indicates type of device to return
* @devp: On entry, pointer to device to lookup. On exit, returns pointer
* to the next device in the uclass if no error occurred, or -ENODEV if
* there is no next device.
* @return 0 if found, -ENODEV if not found, other -ve on error
*/
int blk_next_device_err(enum blk_flag_t flags, struct udevice **devp);
/**
* blk_foreach_probe() - Helper function to iteration through block devices
*
* This creates a for() loop which works through the available devices in
* a uclass in order from start to end. Devices are probed if necessary,
* and ready for use.
*
* @flags: Indicates type of device to return
* @dev: struct udevice * to hold the current device. Set to NULL when there
* are no more devices.
*/
#define blk_foreach_probe(flags, pos) \
for (int _ret = blk_first_device_err(flags, &(pos)); \
!_ret && pos; \
_ret = blk_next_device_err(flags, &(pos)))
/**
* blk_count_devices() - count the number of devices of a particular type
*
* @flags: Indicates type of device to find
* @return number of devices matching those flags
*/
int blk_count_devices(enum blk_flag_t flag);
#endif #endif

View file

@ -64,10 +64,10 @@ enum bloblist_tag_t {
* first bloblist_rec starts at this offset from the start of the header * first bloblist_rec starts at this offset from the start of the header
* @flags: Space for BLOBLISTF_... flags (none yet) * @flags: Space for BLOBLISTF_... flags (none yet)
* @magic: BLOBLIST_MAGIC * @magic: BLOBLIST_MAGIC
* @size: Total size of all records (non-zero if valid) including this header. * @size: Total size of the bloblist (non-zero if valid) including this header.
* The bloblist extends for this many bytes from the start of this header. * The bloblist extends for this many bytes from the start of this header.
* @alloced: Total size allocated for this bloblist. When adding new records, * When adding new records, the bloblist can grow up to this size.
* the bloblist can grow up to this size. This starts out as * @alloced: Total size allocated so far for this bloblist. This starts out as
* sizeof(bloblist_hdr) since we need at least that much space to store a * sizeof(bloblist_hdr) since we need at least that much space to store a
* valid bloblist * valid bloblist
* @spare: Spare space (for future use) * @spare: Spare space (for future use)
@ -179,6 +179,19 @@ void *bloblist_ensure(uint tag, int size);
*/ */
int bloblist_ensure_size_ret(uint tag, int *sizep, void **blobp); int bloblist_ensure_size_ret(uint tag, int *sizep, void **blobp);
/**
* bloblist_resize() - resize a blob
*
* Any blobs above this one are relocated up or down. The resized blob remains
* in the same place.
*
* @tag: Tag to add (enum bloblist_tag_t)
* @new_size: New size of the blob (>0 to expand, <0 to contract)
* @return 0 if OK, -ENOSPC if the bloblist does not have enough space, -ENOENT
* if the tag is not found
*/
int bloblist_resize(uint tag, int new_size);
/** /**
* bloblist_new() - Create a new, empty bloblist of a given size * bloblist_new() - Create a new, empty bloblist of a given size
* *
@ -217,6 +230,10 @@ int bloblist_finish(void);
* bloblist_get_stats() - Get information about the bloblist * bloblist_get_stats() - Get information about the bloblist
* *
* This returns useful information about the bloblist * This returns useful information about the bloblist
*
* @basep: Returns base address of bloblist
* @sizep: Returns the number of bytes used in the bloblist
* @allocedp: Returns the total space allocated to the bloblist
*/ */
void bloblist_get_stats(ulong *basep, ulong *sizep, ulong *allocedp); void bloblist_get_stats(ulong *basep, ulong *sizep, ulong *allocedp);

View file

@ -198,15 +198,6 @@ int cros_ec_flash_protect(struct udevice *dev, uint32_t set_mask,
uint32_t set_flags, uint32_t set_flags,
struct ec_response_flash_protect *resp); struct ec_response_flash_protect *resp);
/**
* Notify EC of current boot mode
*
* @param dev CROS-EC device
* @param vboot_mode Verified boot mode
* @return 0 if ok, <0 on error
*/
int cros_ec_entering_mode(struct udevice *dev, int mode);
/** /**
* Run internal tests on the cros_ec interface. * Run internal tests on the cros_ec interface.
* *
@ -652,4 +643,12 @@ int cros_ec_vstore_read(struct udevice *dev, int slot, uint8_t *data);
int cros_ec_vstore_write(struct udevice *dev, int slot, const uint8_t *data, int cros_ec_vstore_write(struct udevice *dev, int slot, const uint8_t *data,
size_t size); size_t size);
/**
* cros_ec_read_batt_charge() - Read the battery-charge state
*
* @dev: CROS-EC device
* @chargep: Return battery-charge state as a percentage
*/
int cros_ec_read_batt_charge(struct udevice *dev, uint *chargep);
#endif #endif

View file

@ -327,9 +327,12 @@ int os_jump_to_image(const void *dest, int size);
* @fname: place to put full path to U-Boot * @fname: place to put full path to U-Boot
* @maxlen: maximum size of @fname * @maxlen: maximum size of @fname
* @use_img: select the 'u-boot.img' file instead of the 'u-boot' ELF file * @use_img: select the 'u-boot.img' file instead of the 'u-boot' ELF file
* @cur_prefix: prefix of current executable, e.g. "spl" or "tpl"
* @next_prefix: prefix of executable to find, e.g. "spl" or ""
* Return: 0 if OK, -NOSPC if the filename is too large, -ENOENT if not found * Return: 0 if OK, -NOSPC if the filename is too large, -ENOENT if not found
*/ */
int os_find_u_boot(char *fname, int maxlen, bool use_img); int os_find_u_boot(char *fname, int maxlen, bool use_img,
const char *cur_prefix, const char *next_prefix);
/** /**
* os_spl_to_uboot() - Run U-Boot proper * os_spl_to_uboot() - Run U-Boot proper

View file

@ -176,6 +176,27 @@ static inline const char *spl_phase_name(enum u_boot_phase phase)
} }
} }
/**
* spl_phase_prefix() - Get the prefix of the current phase
*
* @phase: Phase to look up
* @return phase prefix ("spl", "tpl", etc.)
*/
static inline const char *spl_phase_prefix(enum u_boot_phase phase)
{
switch (phase) {
case PHASE_TPL:
return "tpl";
case PHASE_SPL:
return "spl";
case PHASE_BOARD_F:
case PHASE_BOARD_R:
return "";
default:
return "phase?";
}
}
/* A string name for SPL or TPL */ /* A string name for SPL or TPL */
#ifdef CONFIG_SPL_BUILD #ifdef CONFIG_SPL_BUILD
# ifdef CONFIG_TPL_BUILD # ifdef CONFIG_TPL_BUILD
@ -484,6 +505,16 @@ struct spl_image_loader {
struct spl_boot_device *bootdev); struct spl_boot_device *bootdev);
}; };
/* Helper function for accessing the name */
static inline const char *spl_loader_name(const struct spl_image_loader *loader)
{
#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT
return loader->name;
#else
return NULL;
#endif
}
/* Declare an SPL image loader */ /* Declare an SPL image loader */
#define SPL_LOAD_IMAGE(__name) \ #define SPL_LOAD_IMAGE(__name) \
ll_entry_declare(struct spl_image_loader, __name, spl_image_loader) ll_entry_declare(struct spl_image_loader, __name, spl_image_loader)

View file

@ -3,7 +3,9 @@
# (C) Copyright 2012 The Chromium Authors # (C) Copyright 2012 The Chromium Authors
obj-y += test-main.o obj-y += test-main.o
ifdef CONFIG_SPL_LOAD_FIT
obj-$(CONFIG_SANDBOX) += image/ obj-$(CONFIG_SANDBOX) += image/
endif
ifneq ($(CONFIG_$(SPL_)BLOBLIST),) ifneq ($(CONFIG_$(SPL_)BLOBLIST),)
obj-$(CONFIG_$(SPL_)CMDLINE) += bloblist.o obj-$(CONFIG_$(SPL_)CMDLINE) += bloblist.o

View file

@ -33,6 +33,9 @@ enum {
ERASE_BYTE = '\xff', ERASE_BYTE = '\xff',
}; };
static const char test1_str[] = "the eyes are open";
static const char test2_str[] = "the mouth moves";
static struct bloblist_hdr *clear_bloblist(void) static struct bloblist_hdr *clear_bloblist(void)
{ {
struct bloblist_hdr *hdr; struct bloblist_hdr *hdr;
@ -384,6 +387,218 @@ static int bloblist_test_reloc(struct unit_test_state *uts)
} }
BLOBLIST_TEST(bloblist_test_reloc, 0); BLOBLIST_TEST(bloblist_test_reloc, 0);
/* Test expansion of a blob */
static int bloblist_test_grow(struct unit_test_state *uts)
{
const uint small_size = 0x20;
void *blob1, *blob2, *blob1_new;
struct bloblist_hdr *hdr;
void *ptr;
ptr = map_sysmem(TEST_ADDR, TEST_BLOBLIST_SIZE);
hdr = ptr;
memset(hdr, ERASE_BYTE, TEST_BLOBLIST_SIZE);
/* Create two blobs */
ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0));
blob1 = bloblist_add(TEST_TAG, small_size, 0);
ut_assertnonnull(blob1);
ut_assertok(check_zero(blob1, small_size));
strcpy(blob1, test1_str);
blob2 = bloblist_add(TEST_TAG2, small_size, 0);
ut_assertnonnull(blob2);
strcpy(blob2, test2_str);
ut_asserteq(sizeof(struct bloblist_hdr) +
sizeof(struct bloblist_rec) * 2 + small_size * 2,
hdr->alloced);
/* Resize the first one */
ut_assertok(bloblist_resize(TEST_TAG, small_size + 4));
/* The first one should not have moved, just got larger */
blob1_new = bloblist_find(TEST_TAG, small_size + 4);
ut_asserteq_ptr(blob1, blob1_new);
/* The new space should be zeroed */
ut_assertok(check_zero(blob1 + small_size, 4));
/* The second one should have moved */
blob2 = bloblist_find(TEST_TAG2, small_size);
ut_assertnonnull(blob2);
ut_asserteq_str(test2_str, blob2);
/* The header should have more bytes in use */
hdr = ptr;
ut_asserteq(sizeof(struct bloblist_hdr) +
sizeof(struct bloblist_rec) * 2 + small_size * 2 +
BLOBLIST_ALIGN,
hdr->alloced);
return 0;
}
BLOBLIST_TEST(bloblist_test_grow, 0);
/* Test shrinking of a blob */
static int bloblist_test_shrink(struct unit_test_state *uts)
{
const uint small_size = 0x20;
void *blob1, *blob2, *blob1_new;
struct bloblist_hdr *hdr;
int new_size;
void *ptr;
ptr = map_sysmem(TEST_ADDR, TEST_BLOBLIST_SIZE);
/* Create two blobs */
ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0));
blob1 = bloblist_add(TEST_TAG, small_size, 0);
ut_assertnonnull(blob1);
strcpy(blob1, test1_str);
blob2 = bloblist_add(TEST_TAG2, small_size, 0);
ut_assertnonnull(blob2);
strcpy(blob2, test2_str);
hdr = ptr;
ut_asserteq(sizeof(struct bloblist_hdr) +
sizeof(struct bloblist_rec) * 2 + small_size * 2,
hdr->alloced);
/* Resize the first one */
new_size = small_size - BLOBLIST_ALIGN - 4;
ut_assertok(bloblist_resize(TEST_TAG, new_size));
/* The first one should not have moved, just got smaller */
blob1_new = bloblist_find(TEST_TAG, new_size);
ut_asserteq_ptr(blob1, blob1_new);
/* The second one should have moved */
blob2 = bloblist_find(TEST_TAG2, small_size);
ut_assertnonnull(blob2);
ut_asserteq_str(test2_str, blob2);
/* The header should have fewer bytes in use */
hdr = ptr;
ut_asserteq(sizeof(struct bloblist_hdr) +
sizeof(struct bloblist_rec) * 2 + small_size * 2 -
BLOBLIST_ALIGN,
hdr->alloced);
return 0;
}
BLOBLIST_TEST(bloblist_test_shrink, 0);
/* Test failing to adjust a blob size */
static int bloblist_test_resize_fail(struct unit_test_state *uts)
{
const uint small_size = 0x20;
struct bloblist_hdr *hdr;
void *blob1, *blob2;
int new_size;
void *ptr;
ptr = map_sysmem(TEST_ADDR, TEST_BLOBLIST_SIZE);
/* Create two blobs */
ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0));
blob1 = bloblist_add(TEST_TAG, small_size, 0);
ut_assertnonnull(blob1);
blob2 = bloblist_add(TEST_TAG2, small_size, 0);
ut_assertnonnull(blob2);
hdr = ptr;
ut_asserteq(sizeof(struct bloblist_hdr) +
sizeof(struct bloblist_rec) * 2 + small_size * 2,
hdr->alloced);
/* Resize the first one, to check the boundary conditions */
ut_asserteq(-EINVAL, bloblist_resize(TEST_TAG, -1));
new_size = small_size + (hdr->size - hdr->alloced);
ut_asserteq(-ENOSPC, bloblist_resize(TEST_TAG, new_size + 1));
ut_assertok(bloblist_resize(TEST_TAG, new_size));
return 0;
}
BLOBLIST_TEST(bloblist_test_resize_fail, 0);
/* Test expanding the last blob in a bloblist */
static int bloblist_test_resize_last(struct unit_test_state *uts)
{
const uint small_size = 0x20;
struct bloblist_hdr *hdr;
void *blob1, *blob2, *blob2_new;
int alloced_val;
void *ptr;
ptr = map_sysmem(TEST_ADDR, TEST_BLOBLIST_SIZE);
memset(ptr, ERASE_BYTE, TEST_BLOBLIST_SIZE);
hdr = ptr;
/* Create two blobs */
ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0));
blob1 = bloblist_add(TEST_TAG, small_size, 0);
ut_assertnonnull(blob1);
blob2 = bloblist_add(TEST_TAG2, small_size, 0);
ut_assertnonnull(blob2);
/* Check the byte after the last blob */
alloced_val = sizeof(struct bloblist_hdr) +
sizeof(struct bloblist_rec) * 2 + small_size * 2;
ut_asserteq(alloced_val, hdr->alloced);
ut_asserteq_ptr((void *)hdr + alloced_val, blob2 + small_size);
ut_asserteq((u8)ERASE_BYTE, *((u8 *)hdr + hdr->alloced));
/* Resize the second one, checking nothing changes */
ut_asserteq(0, bloblist_resize(TEST_TAG2, small_size + 4));
blob2_new = bloblist_find(TEST_TAG2, small_size + 4);
ut_asserteq_ptr(blob2, blob2_new);
/*
* the new blob should encompass the byte we checked now, so it should
* be zeroed. This zeroing should affect only the four new bytes added
* to the blob.
*/
ut_asserteq(0, *((u8 *)hdr + alloced_val));
ut_asserteq((u8)ERASE_BYTE, *((u8 *)hdr + alloced_val + 4));
/* Check that the new top of the allocated blobs has not been touched */
alloced_val += BLOBLIST_ALIGN;
ut_asserteq(alloced_val, hdr->alloced);
ut_asserteq((u8)ERASE_BYTE, *((u8 *)hdr + hdr->alloced));
return 0;
}
BLOBLIST_TEST(bloblist_test_resize_last, 0);
/* Check a completely full bloblist */
static int bloblist_test_blob_maxsize(struct unit_test_state *uts)
{
void *ptr;
int size;
/* At the start there should be no records */
clear_bloblist();
ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0));
/* Add a blob that takes up all space */
size = TEST_BLOBLIST_SIZE - sizeof(struct bloblist_hdr) -
sizeof(struct bloblist_rec);
ptr = bloblist_add(TEST_TAG, size, 0);
ut_assertnonnull(ptr);
ptr = bloblist_add(TEST_TAG, size + 1, 0);
ut_assertnull(ptr);
return 0;
}
BLOBLIST_TEST(bloblist_test_blob_maxsize, 0);
int do_ut_bloblist(struct cmd_tbl *cmdtp, int flag, int argc, int do_ut_bloblist(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[]) char *const argv[])
{ {

View file

@ -162,3 +162,58 @@ static int dm_test_blk_get_from_parent(struct unit_test_state *uts)
return 0; return 0;
} }
DM_TEST(dm_test_blk_get_from_parent, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); DM_TEST(dm_test_blk_get_from_parent, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
/* Test iteration through block devices */
static int dm_test_blk_iter(struct unit_test_state *uts)
{
struct udevice *dev;
int i;
/*
* See sandbox test.dts - it has:
*
* mmc0 - removable
* mmc1 - removable
* mmc2 - fixed
*/
ut_assertok(blk_first_device_err(BLKF_FIXED, &dev));
ut_asserteq_str("mmc2.blk", dev->name);
ut_asserteq(-ENODEV, blk_next_device_err(BLKF_FIXED, &dev));
ut_assertok(blk_first_device_err(BLKF_REMOVABLE, &dev));
ut_asserteq_str("mmc1.blk", dev->name);
ut_assertok(blk_next_device_err(BLKF_REMOVABLE, &dev));
ut_asserteq_str("mmc0.blk", dev->name);
ut_asserteq(-ENODEV, blk_next_device_err(BLKF_REMOVABLE, &dev));
ut_assertok(blk_first_device_err(BLKF_BOTH, &dev));
ut_asserteq_str("mmc2.blk", dev->name);
ut_assertok(blk_next_device_err(BLKF_BOTH, &dev));
ut_asserteq_str("mmc1.blk", dev->name);
ut_assertok(blk_next_device_err(BLKF_BOTH, &dev));
ut_asserteq_str("mmc0.blk", dev->name);
ut_asserteq(-ENODEV, blk_next_device_err(BLKF_FIXED, &dev));
ut_asserteq(1, blk_count_devices(BLKF_FIXED));
ut_asserteq(2, blk_count_devices(BLKF_REMOVABLE));
ut_asserteq(3, blk_count_devices(BLKF_BOTH));
i = 0;
blk_foreach_probe(BLKF_FIXED, dev)
ut_asserteq_str((i++, "mmc2.blk"), dev->name);
ut_asserteq(1, i);
i = 0;
blk_foreach_probe(BLKF_REMOVABLE, dev)
ut_asserteq_str(i++ ? "mmc0.blk" : "mmc1.blk", dev->name);
ut_asserteq(2, i);
i = 0;
blk_foreach_probe(BLKF_BOTH, dev)
ut_asserteq_str((++i == 1 ? "mmc2.blk" : i == 2 ?
"mmc1.blk" : "mmc0.blk"), dev->name);
ut_asserteq(3, i);
return 0;
}
DM_TEST(dm_test_blk_iter, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);

View file

@ -1171,6 +1171,7 @@ static int dm_test_all_have_seq(struct unit_test_state *uts)
} }
DM_TEST(dm_test_all_have_seq, UT_TESTF_SCAN_PDATA); DM_TEST(dm_test_all_have_seq, UT_TESTF_SCAN_PDATA);
#if CONFIG_IS_ENABLED(DM_DMA)
static int dm_test_dma_offset(struct unit_test_state *uts) static int dm_test_dma_offset(struct unit_test_state *uts)
{ {
struct udevice *dev; struct udevice *dev;
@ -1200,3 +1201,4 @@ static int dm_test_dma_offset(struct unit_test_state *uts)
return 0; return 0;
} }
DM_TEST(dm_test_dma_offset, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); DM_TEST(dm_test_dma_offset, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
#endif

View file

@ -56,6 +56,7 @@ struct image_header *spl_get_load_buffer(ssize_t offset, size_t size)
static int spl_test_load(struct unit_test_state *uts) static int spl_test_load(struct unit_test_state *uts)
{ {
const char *cur_prefix, *next_prefix;
struct spl_image_info image; struct spl_image_info image;
struct image_header *header; struct image_header *header;
struct text_ctx text_ctx; struct text_ctx text_ctx;
@ -68,7 +69,10 @@ static int spl_test_load(struct unit_test_state *uts)
load.bl_len = 512; load.bl_len = 512;
load.read = read_fit_image; load.read = read_fit_image;
ret = os_find_u_boot(fname, sizeof(fname), true); cur_prefix = spl_phase_prefix(spl_phase());
next_prefix = spl_phase_prefix(spl_next_phase());
ret = os_find_u_boot(fname, sizeof(fname), true, cur_prefix,
next_prefix);
if (ret) { if (ret) {
printf("(%s not found, error %d)\n", fname, ret); printf("(%s not found, error %d)\n", fname, ret);
return ret; return ret;

View file

@ -62,9 +62,9 @@ static int do_check_log_entries(struct unit_test_state *uts, int flags, int min,
for (i = min; i <= max; i++) { for (i = min; i <= max; i++) {
if (flags & EXPECT_LOG) if (flags & EXPECT_LOG)
ut_assert_nextline("do_log_run() log %d", i); ut_assert_nextline(" do_log_run() log %d", i);
if (flags & EXPECT_DIRECT) if (flags & EXPECT_DIRECT)
ut_assert_nextline("func() _log %d", i); ut_assert_nextline(" func() _log %d", i);
if (flags & EXPECT_DEBUG) { if (flags & EXPECT_DEBUG) {
ut_assert_nextline("log %d", i); ut_assert_nextline("log %d", i);
ut_assert_nextline("_log %d", i); ut_assert_nextline("_log %d", i);
@ -72,11 +72,12 @@ static int do_check_log_entries(struct unit_test_state *uts, int flags, int min,
} }
if (flags & EXPECT_EXTRA) if (flags & EXPECT_EXTRA)
for (; i <= LOGL_MAX ; i++) for (; i <= LOGL_MAX ; i++)
ut_assert_nextline("func() _log %d", i); ut_assert_nextline(" func() _log %d", i);
for (i = LOGL_FIRST; i < LOGL_COUNT; i++) { for (i = LOGL_FIRST; i < LOGL_COUNT; i++) {
if (flags & EXPECT_FORCE) if (flags & EXPECT_FORCE)
ut_assert_nextline("func() _log force %d", i); ut_assert_nextline(" func() _log force %d",
i);
if (flags & EXPECT_DEBUG) if (flags & EXPECT_DEBUG)
ut_assert_nextline("_log force %d", i); ut_assert_nextline("_log force %d", i);
} }
@ -277,7 +278,8 @@ int do_log_test_helpers(struct unit_test_state *uts)
log_io("level %d\n", LOGL_DEBUG_IO); log_io("level %d\n", LOGL_DEBUG_IO);
for (i = LOGL_EMERG; i <= _LOG_MAX_LEVEL; i++) for (i = LOGL_EMERG; i <= _LOG_MAX_LEVEL; i++)
ut_assert_nextline("%s() level %d", __func__, i); ut_assert_nextline("%*s() level %d", CONFIG_LOGF_FUNC_PAD,
__func__, i);
ut_assert_console_end(); ut_assert_console_end();
return 0; return 0;
} }
@ -297,14 +299,14 @@ int do_log_test_disable(struct unit_test_state *uts)
{ {
ut_assertok(console_record_reset_enable()); ut_assertok(console_record_reset_enable());
log_err("default\n"); log_err("default\n");
ut_assert_nextline("%s() default", __func__); ut_assert_nextline("%*s() default", CONFIG_LOGF_FUNC_PAD, __func__);
ut_assertok(log_device_set_enable(LOG_GET_DRIVER(console), false)); ut_assertok(log_device_set_enable(LOG_GET_DRIVER(console), false));
log_err("disabled\n"); log_err("disabled\n");
ut_assertok(log_device_set_enable(LOG_GET_DRIVER(console), true)); ut_assertok(log_device_set_enable(LOG_GET_DRIVER(console), true));
log_err("enabled\n"); log_err("enabled\n");
ut_assert_nextline("%s() enabled", __func__); ut_assert_nextline("%*s() enabled", CONFIG_LOGF_FUNC_PAD, __func__);
ut_assert_console_end(); ut_assert_console_end();
return 0; return 0;
} }

View file

@ -45,7 +45,7 @@ static int dm_test_pre_run(struct unit_test_state *uts)
uts->force_fail_alloc = false; uts->force_fail_alloc = false;
uts->skip_post_probe = false; uts->skip_post_probe = false;
gd->dm_root = NULL; gd->dm_root = NULL;
if (IS_ENABLED(CONFIG_UT_DM) && !CONFIG_IS_ENABLED(OF_PLATDATA)) if (CONFIG_IS_ENABLED(UT_DM) && !CONFIG_IS_ENABLED(OF_PLATDATA))
memset(dm_testdrv_op_count, '\0', sizeof(dm_testdrv_op_count)); memset(dm_testdrv_op_count, '\0', sizeof(dm_testdrv_op_count));
arch_reset_for_test(); arch_reset_for_test();

View file

@ -1142,6 +1142,22 @@ adds a -v<level> option to the call to binman::
make BINMAN_VERBOSE=5 make BINMAN_VERBOSE=5
Building sections in parallel
-----------------------------
By default binman uses multiprocessing to speed up compilation of large images.
This works at a section level, with one thread for each entry in the section.
This can speed things up if the entries are large and use compression.
This feature can be disabled with the '-T' flag, which defaults to a suitable
value for your machine. This depends on the Python version, e.g on v3.8 it uses
12 threads on an 8-core machine. See ConcurrentFutures_ for more details.
The special value -T0 selects single-threaded mode, useful for debugging during
development, since dealing with exceptions and problems in threads is more
difficult. This avoids any use of ThreadPoolExecutor.
History / Credits History / Credits
----------------- -----------------
@ -1190,3 +1206,5 @@ Some ideas:
-- --
Simon Glass <sjg@chromium.org> Simon Glass <sjg@chromium.org>
7/7/2016 7/7/2016
.. _ConcurrentFutures: https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.ThreadPoolExecutor

View file

@ -32,6 +32,10 @@ controlled by a description in the board device tree.'''
default=False, help='Display the README file') default=False, help='Display the README file')
parser.add_argument('--toolpath', type=str, action='append', parser.add_argument('--toolpath', type=str, action='append',
help='Add a path to the directories containing tools') help='Add a path to the directories containing tools')
parser.add_argument('-T', '--threads', type=int,
default=None, help='Number of threads to use (0=single-thread)')
parser.add_argument('--test-section-timeout', action='store_true',
help='Use a zero timeout for section multi-threading (for testing)')
parser.add_argument('-v', '--verbosity', default=1, parser.add_argument('-v', '--verbosity', default=1,
type=int, help='Control verbosity: 0=silent, 1=warnings, 2=notices, ' type=int, help='Control verbosity: 0=silent, 1=warnings, 2=notices, '
'3=info, 4=detail, 5=debug') '3=info, 4=detail, 5=debug')

View file

@ -628,9 +628,13 @@ def Binman(args):
tools.PrepareOutputDir(args.outdir, args.preserve) tools.PrepareOutputDir(args.outdir, args.preserve)
tools.SetToolPaths(args.toolpath) tools.SetToolPaths(args.toolpath)
state.SetEntryArgs(args.entry_arg) state.SetEntryArgs(args.entry_arg)
state.SetThreads(args.threads)
images = PrepareImagesAndDtbs(dtb_fname, args.image, images = PrepareImagesAndDtbs(dtb_fname, args.image,
args.update_fdt, use_expanded) args.update_fdt, use_expanded)
if args.test_section_timeout:
# Set the first image to timeout, used in testThreadTimeout()
images[list(images.keys())[0]].test_section_timeout = True
missing = False missing = False
for image in images.values(): for image in images.values():
missing |= ProcessImage(image, args.update_fdt, args.map, missing |= ProcessImage(image, args.update_fdt, args.map,
@ -642,6 +646,9 @@ def Binman(args):
if missing: if missing:
tout.Warning("\nSome images are invalid") tout.Warning("\nSome images are invalid")
# Use this to debug the time take to pack the image
#state.TimingShow()
finally: finally:
tools.FinaliseOutputDir() tools.FinaliseOutputDir()
finally: finally:

View file

@ -6,6 +6,7 @@
# #
from binman.entry import Entry from binman.entry import Entry
from binman import state
from dtoc import fdt_util from dtoc import fdt_util
from patman import tools from patman import tools
from patman import tout from patman import tout
@ -59,8 +60,12 @@ class Entry_blob(Entry):
the data in chunks and avoid reading it all at once. For now the data in chunks and avoid reading it all at once. For now
this seems like an unnecessary complication. this seems like an unnecessary complication.
""" """
state.TimingStart('read')
indata = tools.ReadFile(self._pathname) indata = tools.ReadFile(self._pathname)
state.TimingAccum('read')
state.TimingStart('compress')
data = self.CompressData(indata) data = self.CompressData(indata)
state.TimingAccum('compress')
self.SetContents(data) self.SetContents(data)
return True return True

View file

@ -40,7 +40,7 @@ class Entry_collection(Entry):
""" """
# Join up all the data # Join up all the data
self.Info('Getting contents, required=%s' % required) self.Info('Getting contents, required=%s' % required)
data = b'' data = bytearray()
for entry_phandle in self.content: for entry_phandle in self.content:
entry_data = self.section.GetContentsByPhandle(entry_phandle, self, entry_data = self.section.GetContentsByPhandle(entry_phandle, self,
required) required)

View file

@ -34,6 +34,9 @@ class Entry_files(Entry_section):
from binman import state from binman import state
super().__init__(section, etype, node) super().__init__(section, etype, node)
def ReadNode(self):
super().ReadNode()
self._pattern = fdt_util.GetString(self._node, 'pattern') self._pattern = fdt_util.GetString(self._node, 'pattern')
if not self._pattern: if not self._pattern:
self.Raise("Missing 'pattern' property") self.Raise("Missing 'pattern' property")

View file

@ -9,10 +9,12 @@ images to be created.
""" """
from collections import OrderedDict from collections import OrderedDict
import concurrent.futures
import re import re
import sys import sys
from binman.entry import Entry from binman.entry import Entry
from binman import state
from dtoc import fdt_util from dtoc import fdt_util
from patman import tools from patman import tools
from patman import tout from patman import tout
@ -164,7 +166,7 @@ class Entry_section(Entry):
pad_byte = (entry._pad_byte if isinstance(entry, Entry_section) pad_byte = (entry._pad_byte if isinstance(entry, Entry_section)
else self._pad_byte) else self._pad_byte)
data = b'' data = bytearray()
# Handle padding before the entry # Handle padding before the entry
if entry.pad_before: if entry.pad_before:
data += tools.GetBytes(self._pad_byte, entry.pad_before) data += tools.GetBytes(self._pad_byte, entry.pad_before)
@ -198,7 +200,7 @@ class Entry_section(Entry):
Returns: Returns:
Contents of the section (bytes) Contents of the section (bytes)
""" """
section_data = b'' section_data = bytearray()
for entry in self._entries.values(): for entry in self._entries.values():
entry_data = entry.GetData(required) entry_data = entry.GetData(required)
@ -525,15 +527,43 @@ class Entry_section(Entry):
def GetEntryContents(self): def GetEntryContents(self):
"""Call ObtainContents() for each entry in the section """Call ObtainContents() for each entry in the section
""" """
def _CheckDone(entry):
if not entry.ObtainContents():
next_todo.append(entry)
return entry
todo = self._entries.values() todo = self._entries.values()
for passnum in range(3): for passnum in range(3):
threads = state.GetThreads()
next_todo = [] next_todo = []
for entry in todo:
if not entry.ObtainContents(): if threads == 0:
next_todo.append(entry) for entry in todo:
_CheckDone(entry)
else:
with concurrent.futures.ThreadPoolExecutor(
max_workers=threads) as executor:
future_to_data = {
entry: executor.submit(_CheckDone, entry)
for entry in todo}
timeout = 60
if self.GetImage().test_section_timeout:
timeout = 0
done, not_done = concurrent.futures.wait(
future_to_data.values(), timeout=timeout)
# Make sure we check the result, so any exceptions are
# generated. Check the results in entry order, since tests
# may expect earlier entries to fail first.
for entry in todo:
job = future_to_data[entry]
job.result()
if not_done:
self.Raise('Timed out obtaining contents')
todo = next_todo todo = next_todo
if not todo: if not todo:
break break
if todo: if todo:
self.Raise('Internal error: Could not complete processing of contents: remaining %s' % self.Raise('Internal error: Could not complete processing of contents: remaining %s' %
todo) todo)

View file

@ -308,7 +308,8 @@ class TestFunctional(unittest.TestCase):
def _DoTestFile(self, fname, debug=False, map=False, update_dtb=False, def _DoTestFile(self, fname, debug=False, map=False, update_dtb=False,
entry_args=None, images=None, use_real_dtb=False, entry_args=None, images=None, use_real_dtb=False,
use_expanded=False, verbosity=None, allow_missing=False, use_expanded=False, verbosity=None, allow_missing=False,
extra_indirs=None): extra_indirs=None, threads=None,
test_section_timeout=False):
"""Run binman with a given test file """Run binman with a given test file
Args: Args:
@ -331,6 +332,8 @@ class TestFunctional(unittest.TestCase):
allow_missing: Set the '--allow-missing' flag so that missing allow_missing: Set the '--allow-missing' flag so that missing
external binaries just produce a warning instead of an error external binaries just produce a warning instead of an error
extra_indirs: Extra input directories to add using -I extra_indirs: Extra input directories to add using -I
threads: Number of threads to use (None for default, 0 for
single-threaded)
""" """
args = [] args = []
if debug: if debug:
@ -342,6 +345,10 @@ class TestFunctional(unittest.TestCase):
if self.toolpath: if self.toolpath:
for path in self.toolpath: for path in self.toolpath:
args += ['--toolpath', path] args += ['--toolpath', path]
if threads is not None:
args.append('-T%d' % threads)
if test_section_timeout:
args.append('--test-section-timeout')
args += ['build', '-p', '-I', self._indir, '-d', self.TestFile(fname)] args += ['build', '-p', '-I', self._indir, '-d', self.TestFile(fname)]
if map: if map:
args.append('-m') args.append('-m')
@ -412,7 +419,7 @@ class TestFunctional(unittest.TestCase):
def _DoReadFileDtb(self, fname, use_real_dtb=False, use_expanded=False, def _DoReadFileDtb(self, fname, use_real_dtb=False, use_expanded=False,
map=False, update_dtb=False, entry_args=None, map=False, update_dtb=False, entry_args=None,
reset_dtbs=True, extra_indirs=None): reset_dtbs=True, extra_indirs=None, threads=None):
"""Run binman and return the resulting image """Run binman and return the resulting image
This runs binman with a given test file and then reads the resulting This runs binman with a given test file and then reads the resulting
@ -439,6 +446,8 @@ class TestFunctional(unittest.TestCase):
function. If reset_dtbs is True, then the original test dtb function. If reset_dtbs is True, then the original test dtb
is written back before this function finishes is written back before this function finishes
extra_indirs: Extra input directories to add using -I extra_indirs: Extra input directories to add using -I
threads: Number of threads to use (None for default, 0 for
single-threaded)
Returns: Returns:
Tuple: Tuple:
@ -463,7 +472,8 @@ class TestFunctional(unittest.TestCase):
try: try:
retcode = self._DoTestFile(fname, map=map, update_dtb=update_dtb, retcode = self._DoTestFile(fname, map=map, update_dtb=update_dtb,
entry_args=entry_args, use_real_dtb=use_real_dtb, entry_args=entry_args, use_real_dtb=use_real_dtb,
use_expanded=use_expanded, extra_indirs=extra_indirs) use_expanded=use_expanded, extra_indirs=extra_indirs,
threads=threads)
self.assertEqual(0, retcode) self.assertEqual(0, retcode)
out_dtb_fname = tools.GetOutputFilename('u-boot.dtb.out') out_dtb_fname = tools.GetOutputFilename('u-boot.dtb.out')
@ -4542,5 +4552,30 @@ class TestFunctional(unittest.TestCase):
data = self._DoReadFile('201_opensbi.dts') data = self._DoReadFile('201_opensbi.dts')
self.assertEqual(OPENSBI_DATA, data[:len(OPENSBI_DATA)]) self.assertEqual(OPENSBI_DATA, data[:len(OPENSBI_DATA)])
def testSectionsSingleThread(self):
"""Test sections without multithreading"""
data = self._DoReadFileDtb('055_sections.dts', threads=0)[0]
expected = (U_BOOT_DATA + tools.GetBytes(ord('!'), 12) +
U_BOOT_DATA + tools.GetBytes(ord('a'), 12) +
U_BOOT_DATA + tools.GetBytes(ord('&'), 4))
self.assertEqual(expected, data)
def testThreadTimeout(self):
"""Test handling a thread that takes too long"""
with self.assertRaises(ValueError) as e:
self._DoTestFile('202_section_timeout.dts',
test_section_timeout=True)
self.assertIn("Node '/binman/section@0': Timed out obtaining contents",
str(e.exception))
def testTiming(self):
"""Test output of timing information"""
data = self._DoReadFile('055_sections.dts')
with test_util.capture_sys_output() as (stdout, stderr):
state.TimingShow()
self.assertIn('read:', stdout.getvalue())
self.assertIn('compress:', stdout.getvalue())
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()

View file

@ -36,6 +36,8 @@ class Image(section.Entry_section):
fdtmap_data: Contents of the fdtmap when loading from a file fdtmap_data: Contents of the fdtmap when loading from a file
allow_repack: True to add properties to allow the image to be safely allow_repack: True to add properties to allow the image to be safely
repacked later repacked later
test_section_timeout: Use a zero timeout for section multi-threading
(for testing)
Args: Args:
copy_to_orig: Copy offset/size to orig_offset/orig_size after reading copy_to_orig: Copy offset/size to orig_offset/orig_size after reading
@ -74,6 +76,7 @@ class Image(section.Entry_section):
self.allow_repack = False self.allow_repack = False
self._ignore_missing = ignore_missing self._ignore_missing = ignore_missing
self.use_expanded = use_expanded self.use_expanded = use_expanded
self.test_section_timeout = False
if not test: if not test:
self.ReadNode() self.ReadNode()

View file

@ -5,8 +5,11 @@
# Holds and modifies the state information held by binman # Holds and modifies the state information held by binman
# #
from collections import defaultdict
import hashlib import hashlib
import re import re
import time
import threading
from dtoc import fdt from dtoc import fdt
import os import os
@ -55,6 +58,30 @@ allow_entry_expansion = True
# to the new ones, the compressed size increases, etc. # to the new ones, the compressed size increases, etc.
allow_entry_contraction = False allow_entry_contraction = False
# Number of threads to use for binman (None means machine-dependent)
num_threads = None
class Timing:
"""Holds information about an operation that is being timed
Properties:
name: Operation name (only one of each name is stored)
start: Start time of operation in seconds (None if not start)
accum:: Amount of time spent on this operation so far, in seconds
"""
def __init__(self, name):
self.name = name
self.start = None # cause an error if TimingStart() is not called
self.accum = 0.0
# Holds timing info for each name:
# key: name of Timing info (Timing.name)
# value: Timing object
timing_info = {}
def GetFdtForEtype(etype): def GetFdtForEtype(etype):
"""Get the Fdt object for a particular device-tree entry """Get the Fdt object for a particular device-tree entry
@ -420,3 +447,71 @@ def AllowEntryContraction():
raised raised
""" """
return allow_entry_contraction return allow_entry_contraction
def SetThreads(threads):
"""Set the number of threads to use when building sections
Args:
threads: Number of threads to use (None for default, 0 for
single-threaded)
"""
global num_threads
num_threads = threads
def GetThreads():
"""Get the number of threads to use when building sections
Returns:
Number of threads to use (None for default, 0 for single-threaded)
"""
return num_threads
def GetTiming(name):
"""Get the timing info for a particular operation
The object is created if it does not already exist.
Args:
name: Operation name to get
Returns:
Timing object for the current thread
"""
threaded_name = '%s:%d' % (name, threading.get_ident())
timing = timing_info.get(threaded_name)
if not timing:
timing = Timing(threaded_name)
timing_info[threaded_name] = timing
return timing
def TimingStart(name):
"""Start the timer for an operation
Args:
name: Operation name to start
"""
timing = GetTiming(name)
timing.start = time.monotonic()
def TimingAccum(name):
"""Stop and accumlate the time for an operation
This measures the time since the last TimingStart() and adds that to the
accumulated time.
Args:
name: Operation name to start
"""
timing = GetTiming(name)
timing.accum += time.monotonic() - timing.start
def TimingShow():
"""Show all timing information"""
duration = defaultdict(float)
for threaded_name, timing in timing_info.items():
name = threaded_name.split(':')[0]
duration[name] += timing.accum
for name, seconds in duration.items():
print('%10s: %10.1fms' % (name, seconds * 1000))

View file

@ -0,0 +1,21 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
pad-byte = <0x26>;
size = <0x28>;
section@0 {
read-only;
size = <0x10>;
pad-byte = <0x21>;
u-boot {
};
};
};
};

View file

@ -21,7 +21,7 @@ options. For more information about the use of this options and tool please
see doc/driver-model/of-plat.rst see doc/driver-model/of-plat.rst
""" """
from optparse import OptionParser from argparse import ArgumentParser
import os import os
import sys import sys
import unittest import unittest
@ -51,7 +51,7 @@ def run_tests(processes, args):
result = unittest.TestResult() result = unittest.TestResult()
sys.argv = [sys.argv[0]] sys.argv = [sys.argv[0]]
test_name = args and args[0] or None test_name = args.files and args.files[0] or None
test_dtoc.setup() test_dtoc.setup()
@ -66,47 +66,50 @@ def RunTestCoverage():
"""Run the tests and check that we get 100% coverage""" """Run the tests and check that we get 100% coverage"""
sys.argv = [sys.argv[0]] sys.argv = [sys.argv[0]]
test_util.RunTestCoverage('tools/dtoc/dtoc', '/main.py', test_util.RunTestCoverage('tools/dtoc/dtoc', '/main.py',
['tools/patman/*.py', '*/fdt*', '*test*'], options.build_dir) ['tools/patman/*.py', '*/fdt*', '*test*'], args.build_dir)
if __name__ != '__main__': if __name__ != '__main__':
sys.exit(1) sys.exit(1)
parser = OptionParser() epilog = '''Generate C code from devicetree files. See of-plat.rst for details'''
parser.add_option('-B', '--build-dir', type='string', default='b',
parser = ArgumentParser(epilog=epilog)
parser.add_argument('-B', '--build-dir', type=str, default='b',
help='Directory containing the build output') help='Directory containing the build output')
parser.add_option('-c', '--c-output-dir', action='store', parser.add_argument('-c', '--c-output-dir', action='store',
help='Select output directory for C files') help='Select output directory for C files')
parser.add_option('-C', '--h-output-dir', action='store', parser.add_argument('-C', '--h-output-dir', action='store',
help='Select output directory for H files (defaults to --c-output-di)') help='Select output directory for H files (defaults to --c-output-di)')
parser.add_option('-d', '--dtb-file', action='store', parser.add_argument('-d', '--dtb-file', action='store',
help='Specify the .dtb input file') help='Specify the .dtb input file')
parser.add_option('-i', '--instantiate', action='store_true', default=False, parser.add_argument('-i', '--instantiate', action='store_true', default=False,
help='Instantiate devices to avoid needing device_bind()') help='Instantiate devices to avoid needing device_bind()')
parser.add_option('--include-disabled', action='store_true', parser.add_argument('--include-disabled', action='store_true',
help='Include disabled nodes') help='Include disabled nodes')
parser.add_option('-o', '--output', action='store', parser.add_argument('-o', '--output', action='store',
help='Select output filename') help='Select output filename')
parser.add_option('-p', '--phase', type=str, parser.add_argument('-p', '--phase', type=str,
help='set phase of U-Boot this invocation is for (spl/tpl)') help='set phase of U-Boot this invocation is for (spl/tpl)')
parser.add_option('-P', '--processes', type=int, parser.add_argument('-P', '--processes', type=int,
help='set number of processes to use for running tests') help='set number of processes to use for running tests')
parser.add_option('-t', '--test', action='store_true', dest='test', parser.add_argument('-t', '--test', action='store_true', dest='test',
default=False, help='run tests') default=False, help='run tests')
parser.add_option('-T', '--test-coverage', action='store_true', parser.add_argument('-T', '--test-coverage', action='store_true',
default=False, help='run tests and check for 100% coverage') default=False, help='run tests and check for 100%% coverage')
(options, args) = parser.parse_args() parser.add_argument('files', nargs='*')
args = parser.parse_args()
# Run our meagre tests # Run our meagre tests
if options.test: if args.test:
ret_code = run_tests(options.processes, args) ret_code = run_tests(args.processes, args)
sys.exit(ret_code) sys.exit(ret_code)
elif options.test_coverage: elif args.test_coverage:
RunTestCoverage() RunTestCoverage()
else: else:
dtb_platdata.run_steps(args, options.dtb_file, options.include_disabled, dtb_platdata.run_steps(args.files, args.dtb_file, args.include_disabled,
options.output, args.output,
[options.c_output_dir, options.h_output_dir], [args.c_output_dir, args.h_output_dir],
options.phase, instantiate=options.instantiate) args.phase, instantiate=args.instantiate)

View file

@ -13,6 +13,7 @@ U_BOOT_DRIVER(), UCLASS_DRIVER and all struct declarations in header files.
See doc/driver-model/of-plat.rst for more informaiton See doc/driver-model/of-plat.rst for more informaiton
""" """
import collections
import os import os
import re import re
import sys import sys
@ -190,6 +191,9 @@ class Scanner:
value: Driver name declared with U_BOOT_DRIVER(driver_name) value: Driver name declared with U_BOOT_DRIVER(driver_name)
_drivers_additional (list or str): List of additional drivers to use _drivers_additional (list or str): List of additional drivers to use
during scanning during scanning
_warnings: Dict of warnings found:
key: Driver name
value: Set of warnings
_of_match: Dict holding information about compatible strings _of_match: Dict holding information about compatible strings
key: Name of struct udevice_id variable key: Name of struct udevice_id variable
value: Dict of compatible info in that variable: value: Dict of compatible info in that variable:
@ -217,6 +221,7 @@ class Scanner:
self._driver_aliases = {} self._driver_aliases = {}
self._drivers_additional = drivers_additional or [] self._drivers_additional = drivers_additional or []
self._missing_drivers = set() self._missing_drivers = set()
self._warnings = collections.defaultdict(set)
self._of_match = {} self._of_match = {}
self._compat_to_driver = {} self._compat_to_driver = {}
self._uclass = {} self._uclass = {}
@ -267,7 +272,10 @@ class Scanner:
aliases_c.remove(compat_c) aliases_c.remove(compat_c)
return compat_c, aliases_c return compat_c, aliases_c
self._missing_drivers.add(compat_list_c[0]) name = compat_list_c[0]
self._missing_drivers.add(name)
self._warnings[name].add(
'WARNING: the driver %s was not found in the driver list' % name)
return compat_list_c[0], compat_list_c[1:] return compat_list_c[0], compat_list_c[1:]
@ -444,8 +452,8 @@ class Scanner:
# Collect the compatible string, e.g. 'rockchip,rk3288-grf' # Collect the compatible string, e.g. 'rockchip,rk3288-grf'
compat = None compat = None
re_compat = re.compile(r'{\s*.compatible\s*=\s*"(.*)"\s*' re_compat = re.compile(r'{\s*\.compatible\s*=\s*"(.*)"\s*'
r'(,\s*.data\s*=\s*(\S*))?\s*},') r'(,\s*\.data\s*=\s*(\S*))?\s*},')
# This is a dict of compatible strings that were found: # This is a dict of compatible strings that were found:
# key: Compatible string, e.g. 'rockchip,rk3288-grf' # key: Compatible string, e.g. 'rockchip,rk3288-grf'
@ -460,7 +468,7 @@ class Scanner:
# Matches the references to the udevice_id list # Matches the references to the udevice_id list
re_of_match = re.compile( re_of_match = re.compile(
r'\.of_match\s*=\s*(of_match_ptr\()?([a-z0-9_]+)(\))?,') r'\.of_match\s*=\s*(of_match_ptr\()?([a-z0-9_]+)([^,]*),')
re_phase = re.compile('^\s*DM_PHASE\((.*)\).*$') re_phase = re.compile('^\s*DM_PHASE\((.*)\).*$')
re_hdr = re.compile('^\s*DM_HEADER\((.*)\).*$') re_hdr = re.compile('^\s*DM_HEADER\((.*)\).*$')
@ -506,6 +514,11 @@ class Scanner:
driver.uclass_id = m_id.group(1) driver.uclass_id = m_id.group(1)
elif m_of_match: elif m_of_match:
compat = m_of_match.group(2) compat = m_of_match.group(2)
suffix = m_of_match.group(3)
if suffix and suffix != ')':
self._warnings[driver.name].add(
"%s: Warning: unexpected suffix '%s' on .of_match line for compat '%s'" %
(fname, suffix, compat))
elif m_phase: elif m_phase:
driver.phase = m_phase.group(1) driver.phase = m_phase.group(1)
elif m_hdr: elif m_hdr:
@ -533,7 +546,12 @@ class Scanner:
# The driver does not have a uclass or compat string. # The driver does not have a uclass or compat string.
# The first is required but the second is not, so just # The first is required but the second is not, so just
# ignore this. # ignore this.
pass if not driver.uclass_id:
warn = 'Missing .uclass'
else:
warn = 'Missing .compatible'
self._warnings[driver.name].add('%s in %s' %
(warn, fname))
driver = None driver = None
ids_name = None ids_name = None
compat = None compat = None
@ -555,7 +573,7 @@ class Scanner:
if ids_m: if ids_m:
ids_name = ids_m.group(1) ids_name = ids_m.group(1)
elif m_alias: elif m_alias:
self._driver_aliases[m_alias[2]] = m_alias[1] self._driver_aliases[m_alias.group(2)] = m_alias.group(1)
# Make the updates based on what we found # Make the updates based on what we found
for driver in drivers.values(): for driver in drivers.values():
@ -577,9 +595,18 @@ class Scanner:
def show_warnings(self): def show_warnings(self):
"""Show any warnings that have been collected""" """Show any warnings that have been collected"""
for name in sorted(list(self._missing_drivers)): used_drivers = [drv.name for drv in self._drivers.values() if drv.used]
print('WARNING: the driver %s was not found in the driver list' missing = self._missing_drivers.copy()
% name) for name in sorted(self._warnings.keys()):
if name in missing or name in used_drivers:
warns = sorted(list(self._warnings[name]))
print('%s: %s' % (name, warns[0]))
indent = ' ' * len(name)
for warn in warns[1:]:
print('%-s: %s' % (indent, warn))
if name in missing:
missing.remove(name)
print()
def scan_driver(self, fname): def scan_driver(self, fname):
"""Scan a driver file to build a list of driver names and aliases """Scan a driver file to build a list of driver names and aliases

View file

@ -20,6 +20,9 @@ from patman import tools
OUR_PATH = os.path.dirname(os.path.realpath(__file__)) OUR_PATH = os.path.dirname(os.path.realpath(__file__))
EXPECT_WARN = {'rockchip_rk3288_grf':
{'WARNING: the driver rockchip_rk3288_grf was not found in the driver list'}}
class FakeNode: class FakeNode:
"""Fake Node object for testing""" """Fake Node object for testing"""
def __init__(self): def __init__(self):
@ -152,6 +155,7 @@ class TestSrcScan(unittest.TestCase):
'nvidia,tegra20-i2c-dvc': 'TYPE_DVC'}, drv.compat) 'nvidia,tegra20-i2c-dvc': 'TYPE_DVC'}, drv.compat)
self.assertEqual('i2c_bus', drv.priv) self.assertEqual('i2c_bus', drv.priv)
self.assertEqual(1, len(scan._drivers)) self.assertEqual(1, len(scan._drivers))
self.assertEqual({}, scan._warnings)
def test_normalized_name(self): def test_normalized_name(self):
"""Test operation of get_normalized_compat_name()""" """Test operation of get_normalized_compat_name()"""
@ -171,8 +175,8 @@ class TestSrcScan(unittest.TestCase):
self.assertEqual([], aliases) self.assertEqual([], aliases)
self.assertEqual(1, len(scan._missing_drivers)) self.assertEqual(1, len(scan._missing_drivers))
self.assertEqual({'rockchip_rk3288_grf'}, scan._missing_drivers) self.assertEqual({'rockchip_rk3288_grf'}, scan._missing_drivers)
#'WARNING: the driver rockchip_rk3288_grf was not found in the driver list', self.assertEqual('', stdout.getvalue().strip())
#stdout.getvalue().strip()) self.assertEqual(EXPECT_WARN, scan._warnings)
i2c = 'I2C_UCLASS' i2c = 'I2C_UCLASS'
compat = {'rockchip,rk3288-grf': 'ROCKCHIP_SYSCON_GRF', compat = {'rockchip,rk3288-grf': 'ROCKCHIP_SYSCON_GRF',
@ -189,6 +193,7 @@ class TestSrcScan(unittest.TestCase):
self.assertEqual('', stdout.getvalue().strip()) self.assertEqual('', stdout.getvalue().strip())
self.assertEqual('rockchip_rk3288_grf', name) self.assertEqual('rockchip_rk3288_grf', name)
self.assertEqual([], aliases) self.assertEqual([], aliases)
self.assertEqual(EXPECT_WARN, scan._warnings)
prop.value = 'rockchip,rk3288-srf' prop.value = 'rockchip,rk3288-srf'
with test_util.capture_sys_output() as (stdout, _): with test_util.capture_sys_output() as (stdout, _):
@ -196,6 +201,7 @@ class TestSrcScan(unittest.TestCase):
self.assertEqual('', stdout.getvalue().strip()) self.assertEqual('', stdout.getvalue().strip())
self.assertEqual('rockchip_rk3288_grf', name) self.assertEqual('rockchip_rk3288_grf', name)
self.assertEqual(['rockchip_rk3288_srf'], aliases) self.assertEqual(['rockchip_rk3288_srf'], aliases)
self.assertEqual(EXPECT_WARN, scan._warnings)
def test_scan_errors(self): def test_scan_errors(self):
"""Test detection of scanning errors""" """Test detection of scanning errors"""
@ -490,3 +496,126 @@ U_BOOT_DRIVER(%s) = {
self.assertEqual(3, seq) self.assertEqual(3, seq)
self.assertEqual({'mypath': 3}, uc.alias_path_to_num) self.assertEqual({'mypath': 3}, uc.alias_path_to_num)
self.assertEqual({2: node, 3: node}, uc.alias_num_to_node) self.assertEqual({2: node, 3: node}, uc.alias_num_to_node)
def test_scan_warnings(self):
"""Test detection of scanning warnings"""
buff = '''
static const struct udevice_id tegra_i2c_ids[] = {
{ .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 },
{ }
};
U_BOOT_DRIVER(i2c_tegra) = {
.name = "i2c_tegra",
.id = UCLASS_I2C,
.of_match = tegra_i2c_ids + 1,
};
'''
# The '+ 1' above should generate a warning
prop = FakeProp()
prop.name = 'compatible'
prop.value = 'rockchip,rk3288-grf'
node = FakeNode()
node.props = {'compatible': prop}
# get_normalized_compat_name() uses this to check for root node
node.parent = FakeNode()
scan = src_scan.Scanner(None, None)
scan._parse_driver('file.c', buff)
self.assertEqual(
{'i2c_tegra':
{"file.c: Warning: unexpected suffix ' + 1' on .of_match line for compat 'tegra_i2c_ids'"}},
scan._warnings)
tprop = FakeProp()
tprop.name = 'compatible'
tprop.value = 'nvidia,tegra114-i2c'
tnode = FakeNode()
tnode.props = {'compatible': tprop}
# get_normalized_compat_name() uses this to check for root node
tnode.parent = FakeNode()
with test_util.capture_sys_output() as (stdout, _):
scan.get_normalized_compat_name(node)
scan.get_normalized_compat_name(tnode)
self.assertEqual('', stdout.getvalue().strip())
self.assertEqual(2, len(scan._missing_drivers))
self.assertEqual({'rockchip_rk3288_grf', 'nvidia_tegra114_i2c'},
scan._missing_drivers)
with test_util.capture_sys_output() as (stdout, _):
scan.show_warnings()
self.assertIn('rockchip_rk3288_grf', stdout.getvalue())
# This should show just the rockchip warning, since the tegra driver
# is not in self._missing_drivers
scan._missing_drivers.remove('nvidia_tegra114_i2c')
with test_util.capture_sys_output() as (stdout, _):
scan.show_warnings()
self.assertIn('rockchip_rk3288_grf', stdout.getvalue())
self.assertNotIn('tegra_i2c_ids', stdout.getvalue())
# Do a similar thing with used drivers. By marking the tegra driver as
# used, the warning related to that driver will be shown
drv = scan._drivers['i2c_tegra']
drv.used = True
with test_util.capture_sys_output() as (stdout, _):
scan.show_warnings()
self.assertIn('rockchip_rk3288_grf', stdout.getvalue())
self.assertIn('tegra_i2c_ids', stdout.getvalue())
# Add a warning to make sure multiple warnings are shown
scan._warnings['i2c_tegra'].update(
scan._warnings['nvidia_tegra114_i2c'])
del scan._warnings['nvidia_tegra114_i2c']
with test_util.capture_sys_output() as (stdout, _):
scan.show_warnings()
self.assertEqual('''i2c_tegra: WARNING: the driver nvidia_tegra114_i2c was not found in the driver list
: file.c: Warning: unexpected suffix ' + 1' on .of_match line for compat 'tegra_i2c_ids'
rockchip_rk3288_grf: WARNING: the driver rockchip_rk3288_grf was not found in the driver list
''',
stdout.getvalue())
self.assertIn('tegra_i2c_ids', stdout.getvalue())
def scan_uclass_warning(self):
"""Test a missing .uclass in the driver"""
buff = '''
static const struct udevice_id tegra_i2c_ids[] = {
{ .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 },
{ }
};
U_BOOT_DRIVER(i2c_tegra) = {
.name = "i2c_tegra",
.of_match = tegra_i2c_ids,
};
'''
scan = src_scan.Scanner(None, None)
scan._parse_driver('file.c', buff)
self.assertEqual(
{'i2c_tegra': {'Missing .uclass in file.c'}},
scan._warnings)
def scan_compat_warning(self):
"""Test a missing .compatible in the driver"""
buff = '''
static const struct udevice_id tegra_i2c_ids[] = {
{ .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 },
{ }
};
U_BOOT_DRIVER(i2c_tegra) = {
.name = "i2c_tegra",
.id = UCLASS_I2C,
};
'''
scan = src_scan.Scanner(None, None)
scan._parse_driver('file.c', buff)
self.assertEqual(
{'i2c_tegra': {'Missing .compatible in file.c'}},
scan._warnings)

View file

@ -169,11 +169,11 @@ class Popen(subprocess.Popen):
self.stdin.close() self.stdin.close()
if self.stdout: if self.stdout:
read_set.append(self.stdout) read_set.append(self.stdout)
stdout = b'' stdout = bytearray()
if self.stderr and self.stderr != self.stdout: if self.stderr and self.stderr != self.stdout:
read_set.append(self.stderr) read_set.append(self.stderr)
stderr = b'' stderr = bytearray()
combined = b'' combined = bytearray()
input_offset = 0 input_offset = 0
while read_set or write_set: while read_set or write_set:

View file

@ -466,6 +466,9 @@ def Compress(indata, algo, with_header=True):
This requires 'lz4' and 'lzma_alone' tools. It also requires an output This requires 'lz4' and 'lzma_alone' tools. It also requires an output
directory to be previously set up, by calling PrepareOutputDir(). directory to be previously set up, by calling PrepareOutputDir().
Care is taken to use unique temporary files so that this function can be
called from multiple threads.
Args: Args:
indata: Input data to compress indata: Input data to compress
algo: Algorithm to use ('none', 'gzip', 'lz4' or 'lzma') algo: Algorithm to use ('none', 'gzip', 'lz4' or 'lzma')
@ -475,14 +478,16 @@ def Compress(indata, algo, with_header=True):
""" """
if algo == 'none': if algo == 'none':
return indata return indata
fname = GetOutputFilename('%s.comp.tmp' % algo) fname = tempfile.NamedTemporaryFile(prefix='%s.comp.tmp' % algo,
dir=outdir).name
WriteFile(fname, indata) WriteFile(fname, indata)
if algo == 'lz4': if algo == 'lz4':
data = Run('lz4', '--no-frame-crc', '-B4', '-5', '-c', fname, data = Run('lz4', '--no-frame-crc', '-B4', '-5', '-c', fname,
binary=True) binary=True)
# cbfstool uses a very old version of lzma # cbfstool uses a very old version of lzma
elif algo == 'lzma': elif algo == 'lzma':
outfname = GetOutputFilename('%s.comp.otmp' % algo) outfname = tempfile.NamedTemporaryFile(prefix='%s.comp.otmp' % algo,
dir=outdir).name
Run('lzma_alone', 'e', fname, outfname, '-lc1', '-lp0', '-pb0', '-d8') Run('lzma_alone', 'e', fname, outfname, '-lc1', '-lp0', '-pb0', '-d8')
data = ReadFile(outfname) data = ReadFile(outfname)
elif algo == 'gzip': elif algo == 'gzip':