[PATCH] cmd: add a fetch utility

Caleb Connolly caleb.connolly at linaro.org
Fri Aug 9 19:00:15 CEST 2024


Hi Simon,

On 09/08/2024 03:55, Simon Glass wrote:
> Hi Caleb,
> 
> On Thu, 8 Aug 2024 at 10:32, Caleb Connolly <caleb.connolly at linaro.org 
> <mailto:caleb.connolly at linaro.org>> wrote:
>  >
>  > While U-Boot does a pretty good job at printing information at startup
>  > about what hardware it's running on, it can be hard to take pretty
>  > pictures of this to show off on the internet (by far the most
>  > important aspect of porting a device in 2024!).
>  >
>  > Add a small utility "ufetch" for displaying some information about 
> U-Boot and
>  > the hardware it's running on in a similar fashion to the popular 
> neofetch tool
>  > for *nix's [1].
>  >
>  > While the output is meant to be useful, it should also be pleasing to 
> look at
>  > and look good in screenshots. The ufetch command brings this to U-Boot,
>  > featuring a colorful ASCII art version of the U-Boot logo and a fancy 
> layout.
>  >
>  > Finally, tireless device porters can properly show off their hard 
> work and get
>  > the internet points they so deserve!
>  >
>  > Not everything is fully supported yet, but this seemed good enough 
> for initial
>  > inclusion. Only one MMC device is detected, and other than SCSI other 
> storage
>  > devices aren't supported.
>  >
>  > [1]: https://en.wikipedia.org/wiki/Neofetch 
> <https://en.wikipedia.org/wiki/Neofetch>
>  >
>  > Signed-off-by: Caleb Connolly <caleb.connolly at linaro.org 
> <mailto:caleb.connolly at linaro.org>>
>  > ---
>  > Ephemeral demo image: https://0x0.st/XVUa.png <https://0x0.st/XVUa.png>
>  > ---
>  >  cmd/Kconfig  |   7 ++
>  >  cmd/Makefile |   1 +
>  >  cmd/ufetch.c | 201 +++++++++++++++++++++++++++++++++++++++++++++++++++
>  >  3 files changed, 209 insertions(+)
>  >  create mode 100644 cmd/ufetch.c
> 
> Very cute!
> 
>  >
>  > diff --git a/cmd/Kconfig b/cmd/Kconfig
>  > index 978f44eda426..70acb5727b04 100644
>  > --- a/cmd/Kconfig
>  > +++ b/cmd/Kconfig
>  > @@ -175,8 +175,15 @@ config CMD_CPU
>  >           number of CPUs, type (e.g. manufacturer, architecture, 
> product or
>  >           internal name) and clock frequency. Other information may be
>  >           available depending on the CPU driver.
>  >
>  > +config CMD_UFETCH
>  > +       bool "U-Boot fetch"
> 
> How about default y if SANDBOX so that this gets built for that. You 
> should also add doc/ and test/ for the command (although the test can be 
> very basic).

I was really aiming for cmd/2048.c levels of expectation and usability 
here, not to make this a rugged part of U-Boot's feature set (if someone 
else would like to go the extra mile they're certainly welcome to).

Making it default y for sandbox so we can at least make sure it compiles 
sounds reasonable though.
> 
>  > +       depends on BLK
>  > +       help
>  > +         Fetch utility for U-Boot (akin to neofetch). Prints information
>  > +         about U-Boot and the board it is running on in a pleasing 
> format.
>  > +
>  >  config CMD_FWU_METADATA
>  >         bool "fwu metadata read"
>  >         depends on FWU_MULTI_BANK_UPDATE
>  >         help
>  > diff --git a/cmd/Makefile b/cmd/Makefile
>  > index 87133cc27a8a..ffb04c8f2e0a 100644
>  > --- a/cmd/Makefile
>  > +++ b/cmd/Makefile
>  > @@ -53,8 +53,9 @@ obj-$(CONFIG_CMD_CONSOLE) += console.o
>  >  obj-$(CONFIG_CMD_CPU) += cpu.o
>  >  obj-$(CONFIG_CMD_DATE) += date.o
>  >  obj-$(CONFIG_CMD_DEMO) += demo.o
>  >  obj-$(CONFIG_CMD_DM) += dm.o
>  > +obj-$(CONFIG_CMD_UFETCH) += ufetch.o
>  >  obj-$(CONFIG_CMD_SOUND) += sound.o
>  >  ifdef CONFIG_POST
>  >  obj-$(CONFIG_CMD_DIAG) += diag.o
>  >  endif
>  > diff --git a/cmd/ufetch.c b/cmd/ufetch.c
>  > new file mode 100644
>  > index 000000000000..f7374eb2e364
>  > --- /dev/null
>  > +++ b/cmd/ufetch.c
>  > @@ -0,0 +1,201 @@
>  > +// SPDX-License-Identifier: GPL-2.0
>  > +
>  > +/* Small "fetch" utility for U-Boot */
> 
> Isn't it a command, rather than a utility? I think of a utility as a 
> program I run.

I feel like you could ask 5 different people about this and get 5 
different answers. As a native british english speaker I would view 
"utilities" as a subset of "commands" in this context. This isn't a high 
effort contribution for me so I'm really fine with whatever.
> 
>  > +
>  > +#include <mmc.h>
>  > +#include <time.h>
>  > +#include <asm/global_data.h>
>  > +#include <cli.h>
>  > +#include <command.h>
>  > +#include <dm/ofnode.h>
>  > +#include <env.h>
>  > +#include <rand.h>
>  > +#include <vsprintf.h>
>  > +#include <linux/delay.h>
>  > +#include <linux/kernel.h>
>  > +#include <version.h>
> 
> Please check [1]

Sure, I'll order these alphabetically.
> 
>  > +
>  > +DECLARE_GLOBAL_DATA_PTR;
>  > +
>  > +#define LINE_WIDTH 40
>  > +#define BLUE "\033[38;5;4m"
>  > +#define YELLOW "\033[38;5;11m"
>  > +#define BOLD "\033[1m"
>  > +#define RESET "\033[0m"
>  > +static const char *logo_lines[] = {
>  > +       BLUE BOLD "                  ......::......                   ",
>  > +       BLUE BOLD "             ...::::::::::::::::::...              ",
>  > +       BLUE BOLD "          ..::::::::::::::::::::::::::..           ",
>  > +       BLUE BOLD "        .::::.:::::::::::::::...::::.::::.         ",
>  > +       BLUE BOLD "      .::::::::::::::::::::..::::::::::::::.       ",
>  > +       BLUE BOLD "    .::.:::::::::::::::::::" YELLOW "=*%#*" BLUE 
> "::::::::::.::.     ",
>  > +       BLUE BOLD "   .:::::::::::::::::....." YELLOW "*%%*-" BLUE 
> ":....::::::::::.    ",
>  > +       BLUE BOLD "  .:.:::...:::::::::.:-" YELLOW "===##*---==-" 
> BLUE "::::::::::.:.   ",
>  > +       BLUE BOLD " .::::..::::........" YELLOW "-***#****###****-" 
> BLUE "...::::::.:.  ",
>  > +       BLUE BOLD " ::.:.-" YELLOW "+***+=" BLUE "::-" YELLOW 
> "=+**#%%%%%%%%%%%%###*= " BLUE "-::...::::. ",
>  > +       BLUE BOLD ".:.::-" YELLOW 
> "*****###%%%%%%%%%%%%%%%%%%%%%%%%%%#*=" BLUE ":..:::: ",
>  > +       BLUE BOLD ".::" YELLOW "##" BLUE ":" YELLOW 
> "***#%%%%%%#####%%%%%%%####%%%%%####%%%*" BLUE "-.::. ",
>  > +       BLUE BOLD ":.:" YELLOW "#%" BLUE "::" YELLOW 
> "*%%%%%%%#*****##%%%#*****##%%##*****#%%+" BLUE ".::.",
>  > +       BLUE BOLD ".::" YELLOW 
> "**==#%%%%%%%##****#%%%%##****#%%%%#****###%%" BLUE ":.. ",
>  > +       BLUE BOLD "..:" YELLOW "#%" BLUE "::" YELLOW 
> "*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#%%%%%+ " BLUE ".:.",
>  > +       BLUE BOLD " ::" YELLOW "##" BLUE ":" YELLOW 
> "+**#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%* " BLUE "-.:: ",
>  > +       BLUE BOLD " ..::-" YELLOW 
> "#****#%#%%%%%%%%%%%%%%%%%%%%%%%%%%#*=" BLUE "-..::.  ",
>  > +       BLUE BOLD "  ...:=" YELLOW "*****=" BLUE 
> "::-\033[38;5;7m=+**###%%%%%%%%###**+=  " BLUE "--:...:::  ",
>  > +       BLUE BOLD "   .::.::--:........::::::--::::::......::::::.    ",
>  > +       BLUE BOLD "    .::.....::::::::::...........:::::::::.::.     ",
>  > +       BLUE BOLD "      .::::::::::::::::::::::::::::::::::::.       ",
>  > +       BLUE BOLD "        .::::.::::::::::::::::::::::.::::.         ",
>  > +       BLUE BOLD "          ..::::::::::::::::::::::::::..           ",
>  > +       BLUE BOLD "             ...::::::::::::::::::...              ",
>  > +       BLUE BOLD "                  ......::......                   ",
>  > +};
>  > +
>  > +enum output_lines {
>  > +       FIRST,
>  > +       SECOND,
>  > +       KERNEL,
>  > +       SYSINFO,
>  > +       HOST,
>  > +       UPTIME,
>  > +       IP,
>  > +       CMDS,
>  > +       CONSOLES,
>  > +       DEVICES,
>  > +       FEATURES,
>  > +       RELOCATION,
>  > +       CORES,
>  > +       MEMORY,
>  > +       STORAGE,
>  > +
>  > +       /* Up to 10 storage devices... Should be enough for anyone 
> right? */
>  > +       _LAST_LINE = (STORAGE + 10),
>  > +#define LAST_LINE (_LAST_LINE - 1UL)
>  > +};
>  > +
>  > +static int do_ufetch(struct cmd_tbl *cmdtp, int flag, int argc,
>  > +                    char *const argv[])
>  > +{
>  > +       int num_lines = max(LAST_LINE + 1, ARRAY_SIZE(logo_lines));
>  > +       const char *model, *compatible;
>  > +       char *ipaddr;
>  > +       int n_cmds, n_cpus = 0, ret, compatlen;
>  > +       ofnode np;
>  > +       struct mmc *mmc;
>  > +       struct blk_desc *scsi_desc;
>  > +       bool skip_ascii = false;
>  > +
>  > +       if (argc > 1 && strcmp(argv[1], "-n") == 0) {
>  > +               skip_ascii = true;
>  > +               num_lines = LAST_LINE;
>  > +       }
>  > +
>  > +       for (int i = 0; i < num_lines; i++) {
>  > +               if (!skip_ascii) {
>  > +                       if (i < ARRAY_SIZE(logo_lines))
>  > +                               printf("%s ", logo_lines[i]);
>  > +                       else
>  > +                               printf("%*c ", LINE_WIDTH, ' ');
>  > +               }
>  > +               switch (i) {
>  > +               case FIRST:
>  > +                       compatible = 
> ofnode_read_string(ofnode_root(), "compatible");
>  > +                       if (!compatible)
>  > +                               compatible = "unknown";
>  > +                       printf(RESET "%s\n", compatible);
>  > +                       compatlen = strlen(compatible);
>  > +                       break;
>  > +               case SECOND:
>  > +                       for (int j = 0; j < compatlen; j++)
>  > +                               putc('-');
>  > +                       putc('\n');
>  > +                       break;
>  > +               case KERNEL:
>  > +                       printf("Kernel:" RESET " %s\n", U_BOOT_VERSION);
>  > +                       break;
>  > +               case SYSINFO:
>  > +                       printf("Config:" RESET " %s_defconfig\n", 
> CONFIG_SYS_CONFIG_NAME);
>  > +                       break;
>  > +               case HOST:
>  > +                       model = ofnode_read_string(ofnode_root(), 
> "model");
>  > +                       if (model)
>  > +                               printf("Host:" RESET " %s\n", model);
>  > +                       break;
>  > +               case UPTIME:
>  > +                       printf("Uptime:" RESET " %ld seconds\n", 
> get_timer(0) / 1000);
>  > +                       break;
>  > +               case IP:
>  > +                       ipaddr = env_get("ipvaddr");
>  > +                       if (!ipaddr)
>  > +                               ipaddr = "none";
>  > +                       printf("IP Address:" RESET " %s", ipaddr);
>  > +                       ipaddr = env_get("ipv6addr");
>  > +                       if (ipaddr)
>  > +                               printf(", %s\n", ipaddr);
>  > +                       else
>  > +                               putc('\n');
>  > +                       break;
>  > +               case CMDS:
>  > +                       n_cmds = ll_entry_count(struct cmd_tbl, cmd);
>  > +                       printf("Commands:" RESET " %d (help)\n", n_cmds);
>  > +                       break;
>  > +               case CONSOLES:
>  > +                       printf("Consoles:" RESET " %s (%d baud)\n", 
> env_get("stdout"),
>  > +                              gd->baudrate);
>  > +                       break;
>  > +               case DEVICES:
>  > +                       printf("Devices:" RESET " ");
>  > +                       /* TODO: walk the DM tree */
>  > +                       printf("Uncountable!\n");
> 
> See dm_announce() for how to do that.
> 
>  > +                       break;
>  > +               case FEATURES:
>  > +                       printf("Features:" RESET " ");
>  > +                       if (IS_ENABLED(CONFIG_NET))
>  > +                               printf("Net");
>  > +                       if (IS_ENABLED(CONFIG_EFI_LOADER))
>  > +                               printf(", EFI");
> 
> How about EFI_LOADER ? After all, U-Boot can run as an EFI app so 'EFI' 
> might be better reserved for that.
> 
>  > +                       printf("\n");
>  > +                       break;
>  > +               case RELOCATION:
>  > +                       if (gd->flags & GD_FLG_SKIP_RELOC)
>  > +                               printf("Relocated:" RESET " no\n");
>  > +                       else
>  > +                               printf("Relocated:" RESET " to 
> %#09lx\n", gd->relocaddr);
> 
> Not for this patch but I'd really like to figure out a way to 
> enable/disable ANSI codes globally in U-Boot. For example, we should 
> disable them in tests, or when redirecting sandbox to a file. It affects 
> EFI tests too...so if you have any ideas... :-)

Only thing that comes to mind would be adding a compile time flag to 
printf which teaches it to parse and skip ANSI escape codes.
> 
>  > +                       break;
>  > +               case CORES:
>  > +                       ofnode_for_each_subnode(np, 
> ofnode_path("/cpus")) {
>  > +                               if (ofnode_name_eq(np, "cpu"))
>  > +                                       n_cpus++;
>  > +                       }
> 
> uclass_id_count(UCLASS_CPU)
> 
>  > +                       printf("CPU:" RESET " %d (1 in use)\n", n_cpus);
>  > +                       break;
>  > +               case MEMORY:
>  > +                       printf("Memory:" RESET " %lu MB\n", 
> (ulong)gd->ram_size >> 20);
>  > +                       break;
>  > +               case STORAGE:
>  > +               default:
>  > +                       if (i == STORAGE && get_mmc_num() > 0) {
>  > +                               mmc = find_mmc_device(0);
>  > +                               if (mmc)
>  > +                                       printf("Storage:" RESET "  
> mmc 0: %llu MB", mmc->capacity >> 20);
>  > +                       }
> 
> How about iterating through UCLASS_BLK ?

Probably a much more sensible approach tbh, can you easily determine the 
size of a block device?
> 
>  > +                       if (i >= STORAGE + 1) {
>  > +                               ret = blk_get_desc(UCLASS_SCSI, i - 
> (STORAGE + 1), &scsi_desc);
>  > +                               if (!ret && scsi_desc->type != 
> DEV_TYPE_UNKNOWN)
>  > +                                       /* The calculation here is 
> really lossy but close enough */
>  > +                                       printf("Storage:" RESET " 
> scsi %d: %lu MB", i - (STORAGE + 1),
>  > +                                              ((scsi_desc->lba >> 9) 
> * scsi_desc->blksz) >> 11);
> 
> You can use print_size() - and same above I suppose

haha yeahp i should really do that instead.

Thanks for the review!
> 
>  > +                       }
>  > +                       printf("\n");
>  > +               }
>  > +       }
>  > +
>  > +       printf(RESET "\n\n");
>  > +
>  > +       return 0;
>  > +}
>  > +
>  > +U_BOOT_CMD(ufetch, 2, 1, do_ufetch,
>  > +       "U-Boot fetch utility",
>  > +       "Print information about your device.\n"
>  > +       "    -n    Don't print the ASCII logo"
>  > +);
>  > --
>  > 2.46.0
>  >
> 
> Regards,
> Simon
> 
> 
> [1] 
> https://docs.u-boot.org/en/latest/develop/codingstyle.html#include-files 
> <https://docs.u-boot.org/en/latest/develop/codingstyle.html#include-files>

-- 
// Caleb (they/them)


More information about the U-Boot mailing list