[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