Hi Caleb, On Thu, 8 Aug 2024 at 10:32, Caleb Connolly <caleb.conno...@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.conno...@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