[PATCH] cmd: add a fetch utility
Simon Glass
sjg at chromium.org
Fri Aug 9 03:55:32 CEST 2024
Hi Caleb,
On Thu, 8 Aug 2024 at 10:32, Caleb Connolly <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
>
> Signed-off-by: Caleb Connolly <caleb.connolly at linaro.org>
> ---
> Ephemeral demo image: 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).
> + 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.
> +
> +#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]
> +
> +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... :-)
> + 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 ?
> + 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
> + }
> + 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
More information about the U-Boot
mailing list