diff mbox series

[v2] cmd: add a fetch utility

Message ID 20241113042536.1792150-2-caleb.connolly@linaro.org
State Superseded
Headers show
Series [v2] cmd: add a fetch utility | expand

Commit Message

Caleb Connolly Nov. 13, 2024, 4:22 a.m. UTC
Add a small utility for displaying some information about U-Boot and the
hardware it's running on in a similar fashion to the popular neofetch
tool for Linux [1].

While the output is meant to be useful, it should also be pleasing to
look at and perhaps entertaining. The ufetch command aims to bring this
to U-Boot, featuring a colorful ASCII art version of the U-Boot logo.

[1]: https://en.wikipedia.org/wiki/Neofetch

Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org>
---
Ephemeral screenshot: https://0x0.st/XkQU.png

Changes since v1:
 * Rework storage info to be more dynamic
 * use print_size() helper everywhere
 * manually walk RAM banks to report memory size correctly
 * minor formatting changes and fixes
 * MAINTAINERS entry
 * V1: https://lore.kernel.org/u-boot/20240808163153.2069650-1-caleb.connolly@linaro.org
---
 MAINTAINERS  |   5 ++
 cmd/Kconfig  |   7 ++
 cmd/Makefile |   1 +
 cmd/ufetch.c | 224 +++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 237 insertions(+)
 create mode 100644 cmd/ufetch.c

Comments

Ilias Apalodimas Nov. 13, 2024, 7:44 a.m. UTC | #1
On Wed, 13 Nov 2024 at 06:26, Caleb Connolly <caleb.connolly@linaro.org> wrote:
>
> Add a small utility for displaying some information about U-Boot and the
> hardware it's running on in a similar fashion to the popular neofetch
> tool for Linux [1].
>
> While the output is meant to be useful, it should also be pleasing to
> look at and perhaps entertaining. The ufetch command aims to bring this
> to U-Boot, featuring a colorful ASCII art version of the U-Boot logo.
>
> [1]: https://en.wikipedia.org/wiki/Neofetch
>
> Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org>
> ---
> Ephemeral screenshot: https://0x0.st/XkQU.png
>
> Changes since v1:
>  * Rework storage info to be more dynamic
>  * use print_size() helper everywhere
>  * manually walk RAM banks to report memory size correctly
>  * minor formatting changes and fixes
>  * MAINTAINERS entry
>  * V1: https://lore.kernel.org/u-boot/20240808163153.2069650-1-caleb.connolly@linaro.org
> ---
>  MAINTAINERS  |   5 ++
>  cmd/Kconfig  |   7 ++
>  cmd/Makefile |   1 +
>  cmd/ufetch.c | 224 +++++++++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 237 insertions(+)
>  create mode 100644 cmd/ufetch.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 38c714cf46a6..d1eb164ad590 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1724,8 +1724,13 @@ M:       Heiko Schocher <hs@denx.de>
>  S:     Maintained
>  T:     git https://source.denx.de/u-boot/custodians/u-boot-ubi.git
>  F:     drivers/mtd/ubi/
>
> +UFETCH
> +M:     Caleb Connolly <caleb.connolly@linaro.org>
> +S:     Maintained
> +F:     cmd/ufetch.c
> +
>  UFS
>  M:     Neil Armstrong <neil.armstrong@linaro.org>
>  M:     Bhupesh Sharma <bhupesh.linux@gmail.com>
>  M:     Neha Malcom Francis <n-francis@ti.com>
> diff --git a/cmd/Kconfig b/cmd/Kconfig
> index 4fba9fe67034..da736249a3cf 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"
> +       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 d1f369deec0a..1e6d3128c8ca 100644
> --- a/cmd/Makefile
> +++ b/cmd/Makefile
> @@ -52,8 +52,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..1bd0565b9f08
> --- /dev/null
> +++ b/cmd/ufetch.c
> @@ -0,0 +1,224 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +/* Small "fetch" utility for U-Boot */
> +
> +#ifdef CONFIG_ARM
> +#include <asm/system.h>
> +#endif
> +#include <dm/device.h>
> +#include <dm/uclass-internal.h>
> +#include <display_options.h>
> +#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>
> +
> +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 * const 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 "::-" YELLOW "=+**###%%%%%%%%###**+=  " 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;
> +       size_t size;
> +       ofnode np;
> +       struct udevice *dev;
> +       struct blk_desc *desc;
> +       bool skip_ascii = false;
> +
> +       if (argc > 1 && strcmp(argv[1], "-n") == 0) {
> +               skip_ascii = true;
> +               num_lines = LAST_LINE;
> +       }
> +
> +       for (int line = 0; line < num_lines; line++) {
> +               if (!skip_ascii) {
> +                       if (line < ARRAY_SIZE(logo_lines))
> +                               printf("%s  ", logo_lines[line]);
> +                       else
> +                               printf("%*c  ", LINE_WIDTH, ' ');
> +               }
> +               switch (line) {
> +               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");
> +                       break;
> +               case FEATURES:
> +                       printf("Features:" RESET " ");
> +                       if (IS_ENABLED(CONFIG_NET))
> +                               printf("Net");
> +                       if (IS_ENABLED(CONFIG_EFI_LOADER))
> +                               printf(", EFI");
> +                       if (IS_ENABLED(CONFIG_CMD_CAT))
> +                               printf(", cat :3");
> +#ifdef CONFIG_ARM
> +                       switch (current_el()) {
> +                       case 2:
> +                               printf(", VMs");
> +                               break;
> +                       case 3:
> +                               printf(", full control!");
> +                               break;
> +                       }
> +#endif
> +                       printf("\n");
> +                       break;
> +               case RELOCATION:
> +                       if (gd->flags & GD_FLG_SKIP_RELOC)
> +                               printf("Relocated:" RESET " no\n");
> +                       else
> +                               printf("Relocated:" RESET " to %#011lx\n", gd->relocaddr);
> +                       break;
> +               case CORES:
> +                       ofnode_for_each_subnode(np, ofnode_path("/cpus")) {
> +                               if (ofnode_name_eq(np, "cpu"))
> +                                       n_cpus++;
> +                       }
> +                       printf("CPU:" RESET " %d (1 in use)\n", n_cpus);
> +                       break;
> +               case MEMORY:
> +                       for (int j = 0; j < CONFIG_NR_DRAM_BANKS && gd->bd->bi_dram[j].size; j++)
> +                               size += gd->bd->bi_dram[j].size;
> +                       printf("Memory:" RESET " ");
> +                       print_size(size, "\n");
> +                       break;
> +               case STORAGE:
> +               default:
> +                       ret = uclass_find_device_by_seq(UCLASS_BLK, line - STORAGE, &dev);
> +                       if (!ret && dev) {
> +                               desc = dev_get_uclass_plat(dev);
> +                               size = desc->lba * desc->blksz;
> +                               printf("%s %d: " RESET, desc->uclass_id == UCLASS_SCSI ? "SCSI" :
> +                                               desc->uclass_id == UCLASS_MMC
> +                                               ? "MMC" : "Storage",
> +                                       desc->lun);
> +                               if (size)
> +                                       print_size(size, "");
> +                               else
> +                                       printf("No media");
> +                       }
> +                       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.47.0
>

Acked-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
Neil Armstrong Nov. 13, 2024, 1:32 p.m. UTC | #2
On 13/11/2024 05:22, Caleb Connolly wrote:
> Add a small utility for displaying some information about U-Boot and the
> hardware it's running on in a similar fashion to the popular neofetch
> tool for Linux [1].
> 
> While the output is meant to be useful, it should also be pleasing to
> look at and perhaps entertaining. The ufetch command aims to bring this
> to U-Boot, featuring a colorful ASCII art version of the U-Boot logo.
> 
> [1]: https://en.wikipedia.org/wiki/Neofetch
> 
> Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org>
> ---
> Ephemeral screenshot: https://0x0.st/XkQU.png
> 
> Changes since v1:
>   * Rework storage info to be more dynamic
>   * use print_size() helper everywhere
>   * manually walk RAM banks to report memory size correctly
>   * minor formatting changes and fixes
>   * MAINTAINERS entry
>   * V1: https://lore.kernel.org/u-boot/20240808163153.2069650-1-caleb.connolly@linaro.org
> ---
>   MAINTAINERS  |   5 ++
>   cmd/Kconfig  |   7 ++
>   cmd/Makefile |   1 +
>   cmd/ufetch.c | 224 +++++++++++++++++++++++++++++++++++++++++++++++++++
>   4 files changed, 237 insertions(+)
>   create mode 100644 cmd/ufetch.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 38c714cf46a6..d1eb164ad590 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1724,8 +1724,13 @@ M:	Heiko Schocher <hs@denx.de>
>   S:	Maintained
>   T:	git https://source.denx.de/u-boot/custodians/u-boot-ubi.git
>   F:	drivers/mtd/ubi/
>   
> +UFETCH
> +M:	Caleb Connolly <caleb.connolly@linaro.org>
> +S:	Maintained
> +F:	cmd/ufetch.c
> +
>   UFS
>   M:	Neil Armstrong <neil.armstrong@linaro.org>
>   M:	Bhupesh Sharma <bhupesh.linux@gmail.com>
>   M:	Neha Malcom Francis <n-francis@ti.com>
> diff --git a/cmd/Kconfig b/cmd/Kconfig
> index 4fba9fe67034..da736249a3cf 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"
> +	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 d1f369deec0a..1e6d3128c8ca 100644
> --- a/cmd/Makefile
> +++ b/cmd/Makefile
> @@ -52,8 +52,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..1bd0565b9f08
> --- /dev/null
> +++ b/cmd/ufetch.c
> @@ -0,0 +1,224 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +/* Small "fetch" utility for U-Boot */
> +
> +#ifdef CONFIG_ARM
> +#include <asm/system.h>
> +#endif
> +#include <dm/device.h>
> +#include <dm/uclass-internal.h>
> +#include <display_options.h>
> +#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>
> +
> +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 * const 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 "::-" YELLOW "=+**###%%%%%%%%###**+=  " 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;
> +	size_t size;
> +	ofnode np;
> +	struct udevice *dev;
> +	struct blk_desc *desc;
> +	bool skip_ascii = false;
> +
> +	if (argc > 1 && strcmp(argv[1], "-n") == 0) {
> +		skip_ascii = true;
> +		num_lines = LAST_LINE;
> +	}
> +
> +	for (int line = 0; line < num_lines; line++) {
> +		if (!skip_ascii) {
> +			if (line < ARRAY_SIZE(logo_lines))
> +				printf("%s  ", logo_lines[line]);
> +			else
> +				printf("%*c  ", LINE_WIDTH, ' ');
> +		}
> +		switch (line) {
> +		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");
> +			break;
> +		case FEATURES:
> +			printf("Features:" RESET " ");
> +			if (IS_ENABLED(CONFIG_NET))
> +				printf("Net");
> +			if (IS_ENABLED(CONFIG_EFI_LOADER))
> +				printf(", EFI");
> +			if (IS_ENABLED(CONFIG_CMD_CAT))
> +				printf(", cat :3");
> +#ifdef CONFIG_ARM
> +			switch (current_el()) {
> +			case 2:
> +				printf(", VMs");
> +				break;
> +			case 3:
> +				printf(", full control!");
> +				break;
> +			}
> +#endif
> +			printf("\n");
> +			break;
> +		case RELOCATION:
> +			if (gd->flags & GD_FLG_SKIP_RELOC)
> +				printf("Relocated:" RESET " no\n");
> +			else
> +				printf("Relocated:" RESET " to %#011lx\n", gd->relocaddr);
> +			break;
> +		case CORES:
> +			ofnode_for_each_subnode(np, ofnode_path("/cpus")) {
> +				if (ofnode_name_eq(np, "cpu"))
> +					n_cpus++;
> +			}
> +			printf("CPU:" RESET " %d (1 in use)\n", n_cpus);
> +			break;
> +		case MEMORY:
> +			for (int j = 0; j < CONFIG_NR_DRAM_BANKS && gd->bd->bi_dram[j].size; j++)
> +				size += gd->bd->bi_dram[j].size;
> +			printf("Memory:" RESET " ");
> +			print_size(size, "\n");
> +			break;
> +		case STORAGE:
> +		default:
> +			ret = uclass_find_device_by_seq(UCLASS_BLK, line - STORAGE, &dev);
> +			if (!ret && dev) {
> +				desc = dev_get_uclass_plat(dev);
> +				size = desc->lba * desc->blksz;
> +				printf("%s %d: " RESET, desc->uclass_id == UCLASS_SCSI ? "SCSI" :
> +						desc->uclass_id == UCLASS_MMC
> +						? "MMC" : "Storage",
> +					desc->lun);
> +				if (size)
> +					print_size(size, "");
> +				else
> +					printf("No media");
> +			}
> +			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"
> +);

Tested-by: Neil Armstrong <neil.armstrong@linaro.org> # on SM8560-QRD

Ephemeral screenshot: https://0x0.st/Xk2N.png

Thanks,
Neil
Simon Glass Nov. 13, 2024, 1:39 p.m. UTC | #3
On Tue, 12 Nov 2024 at 21:26, Caleb Connolly <caleb.connolly@linaro.org> wrote:
>
> Add a small utility for displaying some information about U-Boot and the
> hardware it's running on in a similar fashion to the popular neofetch
> tool for Linux [1].
>
> While the output is meant to be useful, it should also be pleasing to
> look at and perhaps entertaining. The ufetch command aims to bring this
> to U-Boot, featuring a colorful ASCII art version of the U-Boot logo.
>
> [1]: https://en.wikipedia.org/wiki/Neofetch
>
> Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org>
> ---
> Ephemeral screenshot: https://0x0.st/XkQU.png
>
> Changes since v1:
>  * Rework storage info to be more dynamic
>  * use print_size() helper everywhere
>  * manually walk RAM banks to report memory size correctly
>  * minor formatting changes and fixes
>  * MAINTAINERS entry
>  * V1: https://lore.kernel.org/u-boot/20240808163153.2069650-1-caleb.connolly@linaro.org
> ---
>  MAINTAINERS  |   5 ++
>  cmd/Kconfig  |   7 ++
>  cmd/Makefile |   1 +
>  cmd/ufetch.c | 224 +++++++++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 237 insertions(+)
>  create mode 100644 cmd/ufetch.c

Reviewed-by: Simon Glass <sjg@chromium.org>
Heinrich Schuchardt Nov. 13, 2024, 2:40 p.m. UTC | #4
Am 13. November 2024 14:32:57 MEZ schrieb Neil Armstrong <neil.armstrong@linaro.org>:
>On 13/11/2024 05:22, Caleb Connolly wrote:
>> Add a small utility for displaying some information about U-Boot and the
>> hardware it's running on in a similar fashion to the popular neofetch
>> tool for Linux [1].
>> 
>> While the output is meant to be useful, it should also be pleasing to
>> look at and perhaps entertaining. The ufetch command aims to bring this
>> to U-Boot, featuring a colorful ASCII art version of the U-Boot logo.
>> 
>> [1]: https://en.wikipedia.org/wiki/Neofetch
>> 
>> Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org>
>> ---
>> Ephemeral screenshot: https://0x0.st/XkQU.png
>> 
>> Changes since v1:
>>   * Rework storage info to be more dynamic
>>   * use print_size() helper everywhere
>>   * manually walk RAM banks to report memory size correctly
>>   * minor formatting changes and fixes
>>   * MAINTAINERS entry
>>   * V1: https://lore.kernel.org/u-boot/20240808163153.2069650-1-caleb.connolly@linaro.org
>> ---
>>   MAINTAINERS  |   5 ++
>>   cmd/Kconfig  |   7 ++
>>   cmd/Makefile |   1 +
>>   cmd/ufetch.c | 224 +++++++++++++++++++++++++++++++++++++++++++++++++++
>>   4 files changed, 237 insertions(+)
>>   create mode 100644 cmd/ufetch.c
>> 
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 38c714cf46a6..d1eb164ad590 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -1724,8 +1724,13 @@ M:	Heiko Schocher <hs@denx.de>
>>   S:	Maintained
>>   T:	git https://source.denx.de/u-boot/custodians/u-boot-ubi.git
>>   F:	drivers/mtd/ubi/
>>   +UFETCH
>> +M:	Caleb Connolly <caleb.connolly@linaro.org>
>> +S:	Maintained
>> +F:	cmd/ufetch.c
>> +
>>   UFS
>>   M:	Neil Armstrong <neil.armstrong@linaro.org>
>>   M:	Bhupesh Sharma <bhupesh.linux@gmail.com>
>>   M:	Neha Malcom Francis <n-francis@ti.com>
>> diff --git a/cmd/Kconfig b/cmd/Kconfig
>> index 4fba9fe67034..da736249a3cf 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"
>> +	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.

The information is already available in other commands with greater detail.

What is your use case?

>> +
>>   config CMD_FWU_METADATA
>>   	bool "fwu metadata read"
>>   	depends on FWU_MULTI_BANK_UPDATE
>>   	help
>> diff --git a/cmd/Makefile b/cmd/Makefile
>> index d1f369deec0a..1e6d3128c8ca 100644
>> --- a/cmd/Makefile
>> +++ b/cmd/Makefile
>> @@ -52,8 +52,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..1bd0565b9f08
>> --- /dev/null
>> +++ b/cmd/ufetch.c
>> @@ -0,0 +1,224 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +
>> +/* Small "fetch" utility for U-Boot */
>> +
>> +#ifdef CONFIG_ARM
>> +#include <asm/system.h>
>> +#endif
>> +#include <dm/device.h>
>> +#include <dm/uclass-internal.h>
>> +#include <display_options.h>
>> +#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>
>> +
>> +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 * const 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 "::-" YELLOW "=+**###%%%%%%%%###**+=  " BLUE "--:...:::  ",
>> +	BLUE BOLD "   .::.::--:........::::::--::::::......::::::.    ",
>> +	BLUE BOLD "    .::.....::::::::::...........:::::::::.::.     ",
>> +	BLUE BOLD "      .::::::::::::::::::::::::::::::::::::.       ",
>> +	BLUE BOLD "        .::::.::::::::::::::::::::::.::::.         ",
>> +	BLUE BOLD "          ..::::::::::::::::::::::::::..           ",
>> +	BLUE BOLD "             ...::::::::::::::::::...              ",
>> +	BLUE BOLD "                  ......::......                   ",
>> +};

The lines above are not informative but increase code size. They should be removable from code.

>> +
>> +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)
>> +};

Please, avoid unnecessary assumptions.

What stops me from having a few dozen drives on my NAS or in my VM?

>> +
>> +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;
>> +	size_t size;
>> +	ofnode np;
>> +	struct udevice *dev;
>> +	struct blk_desc *desc;
>> +	bool skip_ascii = false;
>> +
>> +	if (argc > 1 && strcmp(argv[1], "-n") == 0) {
>> +		skip_ascii = true;
>> +		num_lines = LAST_LINE;
>> +	}
>> +
>> +	for (int line = 0; line < num_lines; line++) {

This loop is makes adjustments to the code difficult especially for output where there are multiple devices like network and storage.

If the board supports ANSI you can easily position the cursor whereever you want.

>> +		if (!skip_ascii) {
>> +			if (line < ARRAY_SIZE(logo_lines))
>> +				printf("%s  ", logo_lines[line]);
>> +			else
>> +				printf("%*c  ", LINE_WIDTH, ' ');
>> +		}
>> +		switch (line) {
>> +		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)

Do your boards only have a single network interface? Mine have multiple. Why wouldn't you list them all?

>> +				printf(", %s\n", ipaddr);
>> +			else
>> +				putc('\n');

Why waste an output line on something that does not exist. Get rid of the loop!

>> +			break;
>> +		case CMDS:
>> +			n_cmds = ll_entry_count(struct cmd_tbl, cmd);
>> +			printf("Commands:" RESET " %d (help)\n", n_cmds);

Who cares about the number of commands if the one he needs is not provided.

This information is not helpful.

>> +			break;
>> +		case CONSOLES:
>> +			printf("Consoles:" RESET " %s (%d baud)\n", env_get("stdout"),
>> +			       gd->baudrate);

Please, do not assume that stdout relates to a serial console.

>> +			break;
>> +		case DEVICES:
>> +			printf("Devices:" RESET " ");
>> +			/* TODO: walk the DM tree */
>> +			printf("Uncountable!\n");

Please, do not write non-information to the screen.

>> +			break;
>> +		case FEATURES:
>> +			printf("Features:" RESET " ");
>> +			if (IS_ENABLED(CONFIG_NET))
>> +				printf("Net");
>> +			if (IS_ENABLED(CONFIG_EFI_LOADER))
>> +				printf(", EFI");
>> +			if (IS_ENABLED(CONFIG_CMD_CAT))
>> +				printf(", cat :3");

:3 relates to what in U-Boot?

No clue, why you consider the cat command the relevant one, needing special mention.

>> +#ifdef CONFIG_ARM
>> +			switch (current_el()) {
>> +			case 2:
>> +				printf(", VMs");

Neither full control nor VMs are provided by U-Boot.

Why don't you simply write "EL%d"?

>> +				break;
>> +			case 3:
>> +				printf(", full control!");
>> +				break;
>> +			}
>> +#endif
>> +			printf("\n");
>> +			break;
>> +		case RELOCATION:
>> +			if (gd->flags & GD_FLG_SKIP_RELOC)
>> +				printf("Relocated:" RESET " no\n");
>> +			else
>> +				printf("Relocated:" RESET " to %#011lx\n", gd->relocaddr);
>> +			break;
>> +		case CORES:
>> +			ofnode_for_each_subnode(np, ofnode_path("/cpus")) {
>> +				if (ofnode_name_eq(np, "cpu"))
>> +					n_cpus++;
>> +			}
>> +			printf("CPU:" RESET " %d (1 in use)\n", n_cpus);

Who knows how many cores are used in the secure world?

This (1 in use) is not oroviding information.

>> +			break;
>> +		case MEMORY:
>> +			for (int j = 0; j < CONFIG_NR_DRAM_BANKS && gd->bd->bi_dram[j].size; j++)
>> +				size += gd->bd->bi_dram[j].size;
>> +			printf("Memory:" RESET " ");
>> +			print_size(size, "\n");
>> +			break;
>> +		case STORAGE:
>> +		default:
>> +			ret = uclass_find_device_by_seq(UCLASS_BLK, line - STORAGE, &dev);
>> +			if (!ret && dev) {
>> +				desc = dev_get_uclass_plat(dev);
>> +				size = desc->lba * desc->blksz;
>> +				printf("%s %d: " RESET, desc->uclass_id == UCLASS_SCSI ? "SCSI" :
>> +						desc->uclass_id == UCLASS_MMC
>> +						? "MMC" : "Storage",

SCSI is so old fashioned. How about NVMe?

There is a library function to get the display name for a block device.

>> +					desc->lun);
>> +				if (size)
>> +					print_size(size, "");
>> +				else
>> +					printf("No media");
>> +			}
>> +			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"

And how I do I get rid of the other ANSI output?

Best regards

Heinrich

>> +);
>
>Tested-by: Neil Armstrong <neil.armstrong@linaro.org> # on SM8560-QRD
>
>Ephemeral screenshot: https://0x0.st/Xk2N.png
>
>Thanks,
>Neil
Neil Armstrong Nov. 13, 2024, 3:01 p.m. UTC | #5
On 13/11/2024 15:40, Heinrich Schuchardt wrote:
> Am 13. November 2024 14:32:57 MEZ schrieb Neil Armstrong <neil.armstrong@linaro.org>:
>> On 13/11/2024 05:22, Caleb Connolly wrote:
>>> Add a small utility for displaying some information about U-Boot and the
>>> hardware it's running on in a similar fashion to the popular neofetch
>>> tool for Linux [1].
>>>
>>> While the output is meant to be useful, it should also be pleasing to
>>> look at and perhaps entertaining. The ufetch command aims to bring this
>>> to U-Boot, featuring a colorful ASCII art version of the U-Boot logo.
>>>
>>> [1]: https://en.wikipedia.org/wiki/Neofetch
>>>
>>> Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org>
>>> ---
>>> Ephemeral screenshot: https://0x0.st/XkQU.png
>>>
>>> Changes since v1:
>>>    * Rework storage info to be more dynamic
>>>    * use print_size() helper everywhere
>>>    * manually walk RAM banks to report memory size correctly
>>>    * minor formatting changes and fixes
>>>    * MAINTAINERS entry
>>>    * V1: https://lore.kernel.org/u-boot/20240808163153.2069650-1-caleb.connolly@linaro.org
>>> ---
>>>    MAINTAINERS  |   5 ++
>>>    cmd/Kconfig  |   7 ++
>>>    cmd/Makefile |   1 +
>>>    cmd/ufetch.c | 224 +++++++++++++++++++++++++++++++++++++++++++++++++++
>>>    4 files changed, 237 insertions(+)
>>>    create mode 100644 cmd/ufetch.c
>>>
>>> diff --git a/MAINTAINERS b/MAINTAINERS
>>> index 38c714cf46a6..d1eb164ad590 100644
>>> --- a/MAINTAINERS
>>> +++ b/MAINTAINERS
>>> @@ -1724,8 +1724,13 @@ M:	Heiko Schocher <hs@denx.de>
>>>    S:	Maintained
>>>    T:	git https://source.denx.de/u-boot/custodians/u-boot-ubi.git
>>>    F:	drivers/mtd/ubi/
>>>    +UFETCH
>>> +M:	Caleb Connolly <caleb.connolly@linaro.org>
>>> +S:	Maintained
>>> +F:	cmd/ufetch.c
>>> +
>>>    UFS
>>>    M:	Neil Armstrong <neil.armstrong@linaro.org>
>>>    M:	Bhupesh Sharma <bhupesh.linux@gmail.com>
>>>    M:	Neha Malcom Francis <n-francis@ti.com>
>>> diff --git a/cmd/Kconfig b/cmd/Kconfig
>>> index 4fba9fe67034..da736249a3cf 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"
>>> +	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.
> 
> The information is already available in other commands with greater detail.
> 
> What is your use case?
> 
>>> +
>>>    config CMD_FWU_METADATA
>>>    	bool "fwu metadata read"
>>>    	depends on FWU_MULTI_BANK_UPDATE
>>>    	help
>>> diff --git a/cmd/Makefile b/cmd/Makefile
>>> index d1f369deec0a..1e6d3128c8ca 100644
>>> --- a/cmd/Makefile
>>> +++ b/cmd/Makefile
>>> @@ -52,8 +52,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..1bd0565b9f08
>>> --- /dev/null
>>> +++ b/cmd/ufetch.c
>>> @@ -0,0 +1,224 @@
>>> +// SPDX-License-Identifier: GPL-2.0
>>> +
>>> +/* Small "fetch" utility for U-Boot */
>>> +
>>> +#ifdef CONFIG_ARM
>>> +#include <asm/system.h>
>>> +#endif
>>> +#include <dm/device.h>
>>> +#include <dm/uclass-internal.h>
>>> +#include <display_options.h>
>>> +#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>
>>> +
>>> +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 * const 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 "::-" YELLOW "=+**###%%%%%%%%###**+=  " BLUE "--:...:::  ",
>>> +	BLUE BOLD "   .::.::--:........::::::--::::::......::::::.    ",
>>> +	BLUE BOLD "    .::.....::::::::::...........:::::::::.::.     ",
>>> +	BLUE BOLD "      .::::::::::::::::::::::::::::::::::::.       ",
>>> +	BLUE BOLD "        .::::.::::::::::::::::::::::.::::.         ",
>>> +	BLUE BOLD "          ..::::::::::::::::::::::::::..           ",
>>> +	BLUE BOLD "             ...::::::::::::::::::...              ",
>>> +	BLUE BOLD "                  ......::......                   ",
>>> +};
> 
> The lines above are not informative but increase code size. They should be removable from code.
> 
>>> +
>>> +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)
>>> +};
> 
> Please, avoid unnecessary assumptions.
> 
> What stops me from having a few dozen drives on my NAS or in my VM?
> 
>>> +
>>> +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;
>>> +	size_t size;
>>> +	ofnode np;
>>> +	struct udevice *dev;
>>> +	struct blk_desc *desc;
>>> +	bool skip_ascii = false;
>>> +
>>> +	if (argc > 1 && strcmp(argv[1], "-n") == 0) {
>>> +		skip_ascii = true;
>>> +		num_lines = LAST_LINE;
>>> +	}
>>> +
>>> +	for (int line = 0; line < num_lines; line++) {
> 
> This loop is makes adjustments to the code difficult especially for output where there are multiple devices like network and storage.
> 
> If the board supports ANSI you can easily position the cursor whereever you want.
> 
>>> +		if (!skip_ascii) {
>>> +			if (line < ARRAY_SIZE(logo_lines))
>>> +				printf("%s  ", logo_lines[line]);
>>> +			else
>>> +				printf("%*c  ", LINE_WIDTH, ' ');
>>> +		}
>>> +		switch (line) {
>>> +		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)
> 
> Do your boards only have a single network interface? Mine have multiple. Why wouldn't you list them all?
> 
>>> +				printf(", %s\n", ipaddr);
>>> +			else
>>> +				putc('\n');
> 
> Why waste an output line on something that does not exist. Get rid of the loop!
> 
>>> +			break;
>>> +		case CMDS:
>>> +			n_cmds = ll_entry_count(struct cmd_tbl, cmd);
>>> +			printf("Commands:" RESET " %d (help)\n", n_cmds);
> 
> Who cares about the number of commands if the one he needs is not provided.
> 
> This information is not helpful.
> 
>>> +			break;
>>> +		case CONSOLES:
>>> +			printf("Consoles:" RESET " %s (%d baud)\n", env_get("stdout"),
>>> +			       gd->baudrate);
> 
> Please, do not assume that stdout relates to a serial console.
> 
>>> +			break;
>>> +		case DEVICES:
>>> +			printf("Devices:" RESET " ");
>>> +			/* TODO: walk the DM tree */
>>> +			printf("Uncountable!\n");
> 
> Please, do not write non-information to the screen.
> 
>>> +			break;
>>> +		case FEATURES:
>>> +			printf("Features:" RESET " ");
>>> +			if (IS_ENABLED(CONFIG_NET))
>>> +				printf("Net");
>>> +			if (IS_ENABLED(CONFIG_EFI_LOADER))
>>> +				printf(", EFI");
>>> +			if (IS_ENABLED(CONFIG_CMD_CAT))
>>> +				printf(", cat :3");
> 
> :3 relates to what in U-Boot?
> 
> No clue, why you consider the cat command the relevant one, needing special mention.
> 
>>> +#ifdef CONFIG_ARM
>>> +			switch (current_el()) {
>>> +			case 2:
>>> +				printf(", VMs");
> 
> Neither full control nor VMs are provided by U-Boot.
> 
> Why don't you simply write "EL%d"?
> 
>>> +				break;
>>> +			case 3:
>>> +				printf(", full control!");
>>> +				break;
>>> +			}
>>> +#endif
>>> +			printf("\n");
>>> +			break;
>>> +		case RELOCATION:
>>> +			if (gd->flags & GD_FLG_SKIP_RELOC)
>>> +				printf("Relocated:" RESET " no\n");
>>> +			else
>>> +				printf("Relocated:" RESET " to %#011lx\n", gd->relocaddr);
>>> +			break;
>>> +		case CORES:
>>> +			ofnode_for_each_subnode(np, ofnode_path("/cpus")) {
>>> +				if (ofnode_name_eq(np, "cpu"))
>>> +					n_cpus++;
>>> +			}
>>> +			printf("CPU:" RESET " %d (1 in use)\n", n_cpus);
> 
> Who knows how many cores are used in the secure world?
> 
> This (1 in use) is not oroviding information.
> 
>>> +			break;
>>> +		case MEMORY:
>>> +			for (int j = 0; j < CONFIG_NR_DRAM_BANKS && gd->bd->bi_dram[j].size; j++)
>>> +				size += gd->bd->bi_dram[j].size;
>>> +			printf("Memory:" RESET " ");
>>> +			print_size(size, "\n");
>>> +			break;
>>> +		case STORAGE:
>>> +		default:
>>> +			ret = uclass_find_device_by_seq(UCLASS_BLK, line - STORAGE, &dev);
>>> +			if (!ret && dev) {
>>> +				desc = dev_get_uclass_plat(dev);
>>> +				size = desc->lba * desc->blksz;
>>> +				printf("%s %d: " RESET, desc->uclass_id == UCLASS_SCSI ? "SCSI" :
>>> +						desc->uclass_id == UCLASS_MMC
>>> +						? "MMC" : "Storage",
> 
> SCSI is so old fashioned. How about NVMe?

SCSI is used with UFS, so it's far from being old-fashioned.

Neil

> 
> There is a library function to get the display name for a block device.
> 
>>> +					desc->lun);
>>> +				if (size)
>>> +					print_size(size, "");
>>> +				else
>>> +					printf("No media");
>>> +			}
>>> +			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"
> 
> And how I do I get rid of the other ANSI output?
> 
> Best regards
> 
> Heinrich
> 
>>> +);
>>
>> Tested-by: Neil Armstrong <neil.armstrong@linaro.org> # on SM8560-QRD
>>
>> Ephemeral screenshot: https://0x0.st/Xk2N.png
>>
>> Thanks,
>> Neil
>
Tom Rini Nov. 13, 2024, 3:22 p.m. UTC | #6
On Wed, Nov 13, 2024 at 03:40:01PM +0100, Heinrich Schuchardt wrote:
> Am 13. November 2024 14:32:57 MEZ schrieb Neil Armstrong <neil.armstrong@linaro.org>:
> >On 13/11/2024 05:22, Caleb Connolly wrote:
> >> Add a small utility for displaying some information about U-Boot and the
> >> hardware it's running on in a similar fashion to the popular neofetch
> >> tool for Linux [1].
> >> 
> >> While the output is meant to be useful, it should also be pleasing to
> >> look at and perhaps entertaining. The ufetch command aims to bring this
> >> to U-Boot, featuring a colorful ASCII art version of the U-Boot logo.
> >> 
> >> [1]: https://en.wikipedia.org/wiki/Neofetch
> >> 
> >> Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org>
> >> ---
> >> Ephemeral screenshot: https://0x0.st/XkQU.png
> >> 
> >> Changes since v1:
> >>   * Rework storage info to be more dynamic
> >>   * use print_size() helper everywhere
> >>   * manually walk RAM banks to report memory size correctly
> >>   * minor formatting changes and fixes
> >>   * MAINTAINERS entry
> >>   * V1: https://lore.kernel.org/u-boot/20240808163153.2069650-1-caleb.connolly@linaro.org
> >> ---
> >>   MAINTAINERS  |   5 ++
> >>   cmd/Kconfig  |   7 ++
> >>   cmd/Makefile |   1 +
> >>   cmd/ufetch.c | 224 +++++++++++++++++++++++++++++++++++++++++++++++++++
> >>   4 files changed, 237 insertions(+)
> >>   create mode 100644 cmd/ufetch.c
> >> 
> >> diff --git a/MAINTAINERS b/MAINTAINERS
> >> index 38c714cf46a6..d1eb164ad590 100644
> >> --- a/MAINTAINERS
> >> +++ b/MAINTAINERS
> >> @@ -1724,8 +1724,13 @@ M:	Heiko Schocher <hs@denx.de>
> >>   S:	Maintained
> >>   T:	git https://source.denx.de/u-boot/custodians/u-boot-ubi.git
> >>   F:	drivers/mtd/ubi/
> >>   +UFETCH
> >> +M:	Caleb Connolly <caleb.connolly@linaro.org>
> >> +S:	Maintained
> >> +F:	cmd/ufetch.c
> >> +
> >>   UFS
> >>   M:	Neil Armstrong <neil.armstrong@linaro.org>
> >>   M:	Bhupesh Sharma <bhupesh.linux@gmail.com>
> >>   M:	Neha Malcom Francis <n-francis@ti.com>
> >> diff --git a/cmd/Kconfig b/cmd/Kconfig
> >> index 4fba9fe67034..da736249a3cf 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"
> >> +	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.
> 
> The information is already available in other commands with greater detail.
> 
> What is your use case?

The use case was outlined in v1 and maybe missed in v2. This is a "toy"
utility, like 2048, to make it easier to show off things like "I just
made U-Boot work on this new platform". So yes, it's going to bloat the
code size, but it's also not going to be enabled in production (or
hopefully, defconfigs when submitted for new boards...).

[snip]
> >
> >Ephemeral screenshot: https://0x0.st/Xk2N.png

The point is to do something like that.
Tony Dinh Nov. 13, 2024, 10:37 p.m. UTC | #7
Hi Caleb,

On Wed, Nov 13, 2024 at 7:22 AM Tom Rini <trini@konsulko.com> wrote:
>
> On Wed, Nov 13, 2024 at 03:40:01PM +0100, Heinrich Schuchardt wrote:
> > Am 13. November 2024 14:32:57 MEZ schrieb Neil Armstrong <neil.armstrong@linaro.org>:
> > >On 13/11/2024 05:22, Caleb Connolly wrote:
> > >> Add a small utility for displaying some information about U-Boot and the
> > >> hardware it's running on in a similar fashion to the popular neofetch
> > >> tool for Linux [1].
> > >>
> > >> While the output is meant to be useful, it should also be pleasing to
> > >> look at and perhaps entertaining. The ufetch command aims to bring this
> > >> to U-Boot, featuring a colorful ASCII art version of the U-Boot logo.
> > >>
> > >> [1]: https://en.wikipedia.org/wiki/Neofetch
> > >>
> > >> Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org>
> > >> ---
> > >> Ephemeral screenshot: https://0x0.st/XkQU.png
> > >>
> > >> Changes since v1:
> > >>   * Rework storage info to be more dynamic
> > >>   * use print_size() helper everywhere
> > >>   * manually walk RAM banks to report memory size correctly
> > >>   * minor formatting changes and fixes
> > >>   * MAINTAINERS entry
> > >>   * V1: https://lore.kernel.org/u-boot/20240808163153.2069650-1-caleb.connolly@linaro.org
> > >> ---
> > >>   MAINTAINERS  |   5 ++
> > >>   cmd/Kconfig  |   7 ++
> > >>   cmd/Makefile |   1 +
> > >>   cmd/ufetch.c | 224 +++++++++++++++++++++++++++++++++++++++++++++++++++
> > >>   4 files changed, 237 insertions(+)
> > >>   create mode 100644 cmd/ufetch.c
> > >>
> > >> diff --git a/MAINTAINERS b/MAINTAINERS
> > >> index 38c714cf46a6..d1eb164ad590 100644
> > >> --- a/MAINTAINERS
> > >> +++ b/MAINTAINERS
> > >> @@ -1724,8 +1724,13 @@ M:  Heiko Schocher <hs@denx.de>
> > >>   S:       Maintained
> > >>   T:       git https://source.denx.de/u-boot/custodians/u-boot-ubi.git
> > >>   F:       drivers/mtd/ubi/
> > >>   +UFETCH
> > >> +M:        Caleb Connolly <caleb.connolly@linaro.org>
> > >> +S:        Maintained
> > >> +F:        cmd/ufetch.c
> > >> +
> > >>   UFS
> > >>   M:       Neil Armstrong <neil.armstrong@linaro.org>
> > >>   M:       Bhupesh Sharma <bhupesh.linux@gmail.com>
> > >>   M:       Neha Malcom Francis <n-francis@ti.com>
> > >> diff --git a/cmd/Kconfig b/cmd/Kconfig
> > >> index 4fba9fe67034..da736249a3cf 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"
> > >> +  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.
> >
> > The information is already available in other commands with greater detail.
> >
> > What is your use case?
>
> The use case was outlined in v1 and maybe missed in v2. This is a "toy"
> utility, like 2048, to make it easier to show off things like "I just
> made U-Boot work on this new platform". So yes, it's going to bloat the
> code size, but it's also not going to be enabled in production (or
> hopefully, defconfigs when submitted for new boards...).
>
> [snip]
> > >
> > >Ephemeral screenshot: https://0x0.st/Xk2N.png
>
> The point is to do something like that.
>
> --
> Tom

This patch failed to build on ARM 32-bit boards. Looks like
current_el() is only available on ARM64, as defined in
arch/arm/include/asm/system.h.
Build log:
/usr/src/u-boot-master/cmd/ufetch.c:166: undefined reference to `current_el'

All the best,
Tony
Mattijs Korpershoek Nov. 14, 2024, 1:44 p.m. UTC | #8
Hi Caleb,

Thank you for the patch.

On mer., nov. 13, 2024 at 05:22, Caleb Connolly <caleb.connolly@linaro.org> wrote:

> Add a small utility for displaying some information about U-Boot and the
> hardware it's running on in a similar fashion to the popular neofetch
> tool for Linux [1].
>
> While the output is meant to be useful, it should also be pleasing to
> look at and perhaps entertaining. The ufetch command aims to bring this
> to U-Boot, featuring a colorful ASCII art version of the U-Boot logo.
>
> [1]: https://en.wikipedia.org/wiki/Neofetch
>
> Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org>

Tested-by: Mattijs Korpershoek <mkorpershoek@baylibre.com> # vim3

> ---
> Ephemeral screenshot: https://0x0.st/XkQU.png
>
> Changes since v1:
>  * Rework storage info to be more dynamic
>  * use print_size() helper everywhere
>  * manually walk RAM banks to report memory size correctly
>  * minor formatting changes and fixes
>  * MAINTAINERS entry
>  * V1: https://lore.kernel.org/u-boot/20240808163153.2069650-1-caleb.connolly@linaro.org
> ---
>  MAINTAINERS  |   5 ++
>  cmd/Kconfig  |   7 ++
>  cmd/Makefile |   1 +
>  cmd/ufetch.c | 224 +++++++++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 237 insertions(+)
>  create mode 100644 cmd/ufetch.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 38c714cf46a6..d1eb164ad590 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1724,8 +1724,13 @@ M:	Heiko Schocher <hs@denx.de>
>  S:	Maintained
>  T:	git https://source.denx.de/u-boot/custodians/u-boot-ubi.git
>  F:	drivers/mtd/ubi/
>  
> +UFETCH
> +M:	Caleb Connolly <caleb.connolly@linaro.org>
> +S:	Maintained
> +F:	cmd/ufetch.c
> +
>  UFS
>  M:	Neil Armstrong <neil.armstrong@linaro.org>
>  M:	Bhupesh Sharma <bhupesh.linux@gmail.com>
>  M:	Neha Malcom Francis <n-francis@ti.com>
> diff --git a/cmd/Kconfig b/cmd/Kconfig
> index 4fba9fe67034..da736249a3cf 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"
> +	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 d1f369deec0a..1e6d3128c8ca 100644
> --- a/cmd/Makefile
> +++ b/cmd/Makefile
> @@ -52,8 +52,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..1bd0565b9f08
> --- /dev/null
> +++ b/cmd/ufetch.c
> @@ -0,0 +1,224 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +/* Small "fetch" utility for U-Boot */
> +
> +#ifdef CONFIG_ARM
> +#include <asm/system.h>
> +#endif
> +#include <dm/device.h>
> +#include <dm/uclass-internal.h>
> +#include <display_options.h>
> +#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>
> +
> +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 * const 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 "::-" YELLOW "=+**###%%%%%%%%###**+=  " 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;
> +	size_t size;
> +	ofnode np;
> +	struct udevice *dev;
> +	struct blk_desc *desc;
> +	bool skip_ascii = false;
> +
> +	if (argc > 1 && strcmp(argv[1], "-n") == 0) {
> +		skip_ascii = true;
> +		num_lines = LAST_LINE;
> +	}
> +
> +	for (int line = 0; line < num_lines; line++) {
> +		if (!skip_ascii) {
> +			if (line < ARRAY_SIZE(logo_lines))
> +				printf("%s  ", logo_lines[line]);
> +			else
> +				printf("%*c  ", LINE_WIDTH, ' ');
> +		}
> +		switch (line) {
> +		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");
> +			break;
> +		case FEATURES:
> +			printf("Features:" RESET " ");
> +			if (IS_ENABLED(CONFIG_NET))
> +				printf("Net");
> +			if (IS_ENABLED(CONFIG_EFI_LOADER))
> +				printf(", EFI");
> +			if (IS_ENABLED(CONFIG_CMD_CAT))
> +				printf(", cat :3");
> +#ifdef CONFIG_ARM
> +			switch (current_el()) {
> +			case 2:
> +				printf(", VMs");
> +				break;
> +			case 3:
> +				printf(", full control!");
> +				break;
> +			}
> +#endif
> +			printf("\n");
> +			break;
> +		case RELOCATION:
> +			if (gd->flags & GD_FLG_SKIP_RELOC)
> +				printf("Relocated:" RESET " no\n");
> +			else
> +				printf("Relocated:" RESET " to %#011lx\n", gd->relocaddr);
> +			break;
> +		case CORES:
> +			ofnode_for_each_subnode(np, ofnode_path("/cpus")) {
> +				if (ofnode_name_eq(np, "cpu"))
> +					n_cpus++;
> +			}
> +			printf("CPU:" RESET " %d (1 in use)\n", n_cpus);
> +			break;
> +		case MEMORY:
> +			for (int j = 0; j < CONFIG_NR_DRAM_BANKS && gd->bd->bi_dram[j].size; j++)
> +				size += gd->bd->bi_dram[j].size;
> +			printf("Memory:" RESET " ");
> +			print_size(size, "\n");
> +			break;
> +		case STORAGE:
> +		default:
> +			ret = uclass_find_device_by_seq(UCLASS_BLK, line - STORAGE, &dev);
> +			if (!ret && dev) {
> +				desc = dev_get_uclass_plat(dev);
> +				size = desc->lba * desc->blksz;
> +				printf("%s %d: " RESET, desc->uclass_id == UCLASS_SCSI ? "SCSI" :
> +						desc->uclass_id == UCLASS_MMC
> +						? "MMC" : "Storage",
> +					desc->lun);
> +				if (size)
> +					print_size(size, "");
> +				else
> +					printf("No media");
> +			}
> +			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.47.0
diff mbox series

Patch

diff --git a/MAINTAINERS b/MAINTAINERS
index 38c714cf46a6..d1eb164ad590 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1724,8 +1724,13 @@  M:	Heiko Schocher <hs@denx.de>
 S:	Maintained
 T:	git https://source.denx.de/u-boot/custodians/u-boot-ubi.git
 F:	drivers/mtd/ubi/
 
+UFETCH
+M:	Caleb Connolly <caleb.connolly@linaro.org>
+S:	Maintained
+F:	cmd/ufetch.c
+
 UFS
 M:	Neil Armstrong <neil.armstrong@linaro.org>
 M:	Bhupesh Sharma <bhupesh.linux@gmail.com>
 M:	Neha Malcom Francis <n-francis@ti.com>
diff --git a/cmd/Kconfig b/cmd/Kconfig
index 4fba9fe67034..da736249a3cf 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"
+	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 d1f369deec0a..1e6d3128c8ca 100644
--- a/cmd/Makefile
+++ b/cmd/Makefile
@@ -52,8 +52,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..1bd0565b9f08
--- /dev/null
+++ b/cmd/ufetch.c
@@ -0,0 +1,224 @@ 
+// SPDX-License-Identifier: GPL-2.0
+
+/* Small "fetch" utility for U-Boot */
+
+#ifdef CONFIG_ARM
+#include <asm/system.h>
+#endif
+#include <dm/device.h>
+#include <dm/uclass-internal.h>
+#include <display_options.h>
+#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>
+
+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 * const 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 "::-" YELLOW "=+**###%%%%%%%%###**+=  " 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;
+	size_t size;
+	ofnode np;
+	struct udevice *dev;
+	struct blk_desc *desc;
+	bool skip_ascii = false;
+
+	if (argc > 1 && strcmp(argv[1], "-n") == 0) {
+		skip_ascii = true;
+		num_lines = LAST_LINE;
+	}
+
+	for (int line = 0; line < num_lines; line++) {
+		if (!skip_ascii) {
+			if (line < ARRAY_SIZE(logo_lines))
+				printf("%s  ", logo_lines[line]);
+			else
+				printf("%*c  ", LINE_WIDTH, ' ');
+		}
+		switch (line) {
+		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");
+			break;
+		case FEATURES:
+			printf("Features:" RESET " ");
+			if (IS_ENABLED(CONFIG_NET))
+				printf("Net");
+			if (IS_ENABLED(CONFIG_EFI_LOADER))
+				printf(", EFI");
+			if (IS_ENABLED(CONFIG_CMD_CAT))
+				printf(", cat :3");
+#ifdef CONFIG_ARM
+			switch (current_el()) {
+			case 2:
+				printf(", VMs");
+				break;
+			case 3:
+				printf(", full control!");
+				break;
+			}
+#endif
+			printf("\n");
+			break;
+		case RELOCATION:
+			if (gd->flags & GD_FLG_SKIP_RELOC)
+				printf("Relocated:" RESET " no\n");
+			else
+				printf("Relocated:" RESET " to %#011lx\n", gd->relocaddr);
+			break;
+		case CORES:
+			ofnode_for_each_subnode(np, ofnode_path("/cpus")) {
+				if (ofnode_name_eq(np, "cpu"))
+					n_cpus++;
+			}
+			printf("CPU:" RESET " %d (1 in use)\n", n_cpus);
+			break;
+		case MEMORY:
+			for (int j = 0; j < CONFIG_NR_DRAM_BANKS && gd->bd->bi_dram[j].size; j++)
+				size += gd->bd->bi_dram[j].size;
+			printf("Memory:" RESET " ");
+			print_size(size, "\n");
+			break;
+		case STORAGE:
+		default:
+			ret = uclass_find_device_by_seq(UCLASS_BLK, line - STORAGE, &dev);
+			if (!ret && dev) {
+				desc = dev_get_uclass_plat(dev);
+				size = desc->lba * desc->blksz;
+				printf("%s %d: " RESET, desc->uclass_id == UCLASS_SCSI ? "SCSI" :
+						desc->uclass_id == UCLASS_MMC
+						? "MMC" : "Storage",
+					desc->lun);
+				if (size)
+					print_size(size, "");
+				else
+					printf("No media");
+			}
+			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"
+);