[PATCH] cmd: add a fetch utility

Caleb Connolly caleb.connolly at linaro.org
Fri Aug 9 19:23:42 CEST 2024



On 09/08/2024 08:18, Heinrich Schuchardt wrote:
> 
> 
> Am 8. August 2024 18:24:24 MESZ schrieb Caleb Connolly <caleb.connolly at linaro.org>:
>> 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.
> 
> 
> This command is not very generic. E.g. it supports SCSI drives but not SATA. Many boards have eMMC and SD-card. This lks more ike an RFC tan something ready to merge.

Honestly it didn't even occur to me to send an RFC since this is purely 
a vanity thing...

I'll make it generic over UCLASS_BLK if possible for the next revision.
> 
> Why don't you use a script or an EFI program to print whatever information you need?

Well , then you'd need to have a way to load the script or EFI binary, 
which might not be possible for an early U-Boot port which doesn't have 
storage support yet (or at least, be a bunch of effort).

The only motivation to have this in U-Boot is really because

a) it's cute
b) it motivates "reward oriented" people like myself to get U-Boot 
booting on new/interesting devices because now it can be really easily 
shown off.

Maybe it's a cynical take, but I think stuff like this really has the 
potential to get more folks interested. Nobody likes doing the invisible 
background work, so why not bring some visibility to things?

Assuming I did a bad job at conveying my intent in the patch 
description: the point here is not to convey useful debugging 
information to developers, it's to show off, look pretty, and be 
amusing. Plenty of other U-Boot commands can convey information, this 
one is more like the 2048 port.
> 
>>
>> [1]: https://en.wikipedia.org/wiki/Neofetch
>>
>> Signed-off-by: Caleb Connolly <caleb.connolly at linaro.org>
>> ---
>> Ephemeral demo image: https://0x0.st/XVUa.png
>> ---
>> cmd/Kconfig  |   7 ++
>> cmd/Makefile |   1 +
>> cmd/ufetch.c | 201 +++++++++++++++++++++++++++++++++++++++++++++++++++
>> 3 files changed, 209 insertions(+)
>> create mode 100644 cmd/ufetch.c
>>
>> 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"
>> +	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 */
>> +
>> +#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 *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:
> 
> Please, do not use misleading variable names.

it is meant to be a little satirical, but I get it.
> 
> 
>> +			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);
> 
> Who cares if you have 50 commands or 51 if you don't know which?

This is a play on the neofetch "packages" counter.
> 
> 
>> +			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");
> 
> Nothing we should merge.
> 
>> +			break;
>> +		case FEATURES:
>> +			printf("Features:" RESET " ");
>> +			if (IS_ENABLED(CONFIG_NET))
>> +				printf("Net");
> 
> Wouuldn't you want to know which devices where detected?
> 
>> +			if (IS_ENABLED(CONFIG_EFI_LOADER))
>> +				printf(", EFI");
>> +			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);
>> +			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:
>> +			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);
> 
> And if my eMMC is device 1 and SD card is 0?
> 
> Why wouldn't you iterate over all UCLASS_BLK devices?
> 
> Best regards
> 
> Heinrich
> 
>> +			}
>> +			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);
>> +			}
>> +			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"
>> +);

-- 
// Caleb (they/them)


More information about the U-Boot mailing list