1 // SPDX-License-Identifier: GPL-2.0
2 
3 /* Small "fetch" utility for U-Boot */
4 
5 #ifdef CONFIG_ARM64
6 #include <asm/system.h>
7 #endif
8 #include <dm/device.h>
9 #include <dm/uclass-internal.h>
10 #include <display_options.h>
11 #include <mmc.h>
12 #include <time.h>
13 #include <asm/global_data.h>
14 #include <cli.h>
15 #include <command.h>
16 #include <dm/ofnode.h>
17 #include <env.h>
18 #include <rand.h>
19 #include <vsprintf.h>
20 #include <linux/delay.h>
21 #include <linux/kernel.h>
22 #include <version.h>
23 
24 DECLARE_GLOBAL_DATA_PTR;
25 
26 #define LINE_WIDTH 40
27 #define BLUE "\033[34m"
28 #define YELLOW "\033[33m"
29 #define BOLD "\033[1m"
30 #define RESET "\033[0m"
31 static const char * const logo_lines[] = {
32 	BLUE BOLD "                  ......::......                   ",
33 	BLUE BOLD "             ...::::::::::::::::::...              ",
34 	BLUE BOLD "          ..::::::::::::::::::::::::::..           ",
35 	BLUE BOLD "        .::::.:::::::::::::::...::::.::::.         ",
36 	BLUE BOLD "      .::::::::::::::::::::..::::::::::::::.       ",
37 	BLUE BOLD "    .::.:::::::::::::::::::" YELLOW "=*%#*" BLUE "::::::::::.::.     ",
38 	BLUE BOLD "   .:::::::::::::::::....." YELLOW "*%%*-" BLUE ":....::::::::::.    ",
39 	BLUE BOLD "  .:.:::...:::::::::.:-" YELLOW "===##*---==-" BLUE "::::::::::.:.   ",
40 	BLUE BOLD " .::::..::::........" YELLOW "-***#****###****-" BLUE "...::::::.:.  ",
41 	BLUE BOLD " ::.:.-" YELLOW "+***+=" BLUE "::-" YELLOW "=+**#%%%%%%%%%%%%###*= " BLUE "-::...::::. ",
42 	BLUE BOLD ".:.::-" YELLOW "*****###%%%%%%%%%%%%%%%%%%%%%%%%%%#*=" BLUE ":..:::: ",
43 	BLUE BOLD ".::" YELLOW "##" BLUE ":" YELLOW "***#%%%%%%#####%%%%%%%####%%%%%####%%%*" BLUE "-.::. ",
44 	BLUE BOLD ":.:" YELLOW "#%" BLUE "::" YELLOW "*%%%%%%%#*****##%%%#*****##%%##*****#%%+" BLUE ".::.",
45 	BLUE BOLD ".::" YELLOW "**==#%%%%%%%##****#%%%%##****#%%%%#****###%%" BLUE ":.. ",
46 	BLUE BOLD "..:" YELLOW "#%" BLUE "::" YELLOW "*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#%%%%%+ " BLUE ".:.",
47 	BLUE BOLD " ::" YELLOW "##" BLUE ":" YELLOW "+**#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%* " BLUE "-.:: ",
48 	BLUE BOLD " ..::-" YELLOW "#****#%#%%%%%%%%%%%%%%%%%%%%%%%%%%#*=" BLUE "-..::.  ",
49 	BLUE BOLD "  ...:=" YELLOW "*****=" BLUE "::-" YELLOW "=+**###%%%%%%%%###**+=  " BLUE "--:...:::  ",
50 	BLUE BOLD "   .::.::--:........::::::--::::::......::::::.    ",
51 	BLUE BOLD "    .::.....::::::::::...........:::::::::.::.     ",
52 	BLUE BOLD "      .::::::::::::::::::::::::::::::::::::.       ",
53 	BLUE BOLD "        .::::.::::::::::::::::::::::.::::.         ",
54 	BLUE BOLD "          ..::::::::::::::::::::::::::..           ",
55 	BLUE BOLD "             ...::::::::::::::::::...              ",
56 	BLUE BOLD "                  ......::......                   ",
57 };
58 
59 enum output_lines {
60 	FIRST,
61 	SECOND,
62 	KERNEL,
63 	SYSINFO,
64 	HOST,
65 	UPTIME,
66 	IP,
67 	CMDS,
68 	CONSOLES,
69 	FEATURES,
70 	RELOCATION,
71 	CORES,
72 	MEMORY,
73 	STORAGE,
74 
75 	/* Up to 10 storage devices... Should be enough for anyone right? */
76 	_LAST_LINE = (STORAGE + 10),
77 #define LAST_LINE (_LAST_LINE - 1UL)
78 };
79 
80 /*
81  * TODO/ideas:
82  * - Refactor to not use a for loop
83  * - Handle multiple network interfaces
84  * - Include stats about number of bound/probed devices
85  * - Show U-Boot's size and malloc usage, fdt size, etc.
86  */
87 
88 
do_ufetch(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])89 static int do_ufetch(struct cmd_tbl *cmdtp, int flag, int argc,
90 		     char *const argv[])
91 {
92 	int num_lines = max((size_t)LAST_LINE + 1, ARRAY_SIZE(logo_lines));
93 	const char *model, *compatible;
94 	char *ipaddr;
95 	int n_cmds, n_cpus = 0, compatlen;
96 	size_t size = 0;
97 	ofnode np;
98 	bool skip_ascii = false;
99 
100 	if (argc > 1 && strcmp(argv[1], "-n") == 0) {
101 		skip_ascii = true;
102 		num_lines = LAST_LINE;
103 	}
104 
105 	for (int line = 0; line < num_lines; line++) {
106 		if (!skip_ascii) {
107 			if (line < ARRAY_SIZE(logo_lines))
108 				printf("%s  ", logo_lines[line]);
109 			else
110 				printf("%*c  ", LINE_WIDTH, ' ');
111 		}
112 		switch (line) {
113 		case FIRST:
114 			compatible = ofnode_read_string(ofnode_root(), "compatible");
115 			if (!compatible)
116 				compatible = "unknown";
117 			printf(RESET "%s\n", compatible);
118 			compatlen = strlen(compatible);
119 			break;
120 		case SECOND:
121 			for (int j = 0; j < compatlen; j++)
122 				putc('-');
123 			putc('\n');
124 			break;
125 		case KERNEL:
126 			printf("Kernel:" RESET " %s\n", U_BOOT_VERSION);
127 			break;
128 		case SYSINFO:
129 			printf("Config:" RESET " %s_defconfig\n", CONFIG_SYS_CONFIG_NAME);
130 			break;
131 		case HOST:
132 			model = ofnode_read_string(ofnode_root(), "model");
133 			if (model)
134 				printf("Host:" RESET " %s\n", model);
135 			break;
136 		case UPTIME:
137 			printf("Uptime:" RESET " %ld seconds\n", get_timer(0) / 1000);
138 			break;
139 		case IP:
140 			ipaddr = env_get("ipaddr");
141 			if (!ipaddr)
142 				ipaddr = "none";
143 			printf("IP Address:" RESET " %s", ipaddr);
144 			ipaddr = env_get("ipv6addr");
145 			if (ipaddr)
146 				printf(", %s\n", ipaddr);
147 			else
148 				putc('\n');
149 			break;
150 		case CMDS:
151 			n_cmds = ll_entry_count(struct cmd_tbl, cmd);
152 			printf("Commands:" RESET " %d (help)\n", n_cmds);
153 			break;
154 		case CONSOLES:
155 			printf("Consoles:" RESET " %s", env_get("stdout"));
156 			if (gd->baudrate)
157 				printf(" (%d baud)", gd->baudrate);
158 			putc('\n');
159 			break;
160 		case FEATURES:
161 			printf("Features:" RESET " ");
162 			if (IS_ENABLED(CONFIG_NET))
163 				printf("Net");
164 			if (IS_ENABLED(CONFIG_EFI_LOADER))
165 				printf(", EFI");
166 			if (IS_ENABLED(CONFIG_CMD_CAT))
167 				printf(", cat :3");
168 #ifdef CONFIG_ARM64
169 			switch (current_el()) {
170 			case 2:
171 				printf(", VMs");
172 				break;
173 			case 3:
174 				printf(", full control!");
175 				break;
176 			}
177 #endif
178 			printf("\n");
179 			break;
180 		case RELOCATION:
181 			if (gd->flags & GD_FLG_SKIP_RELOC)
182 				printf("Relocated:" RESET " no\n");
183 			else
184 				printf("Relocated:" RESET " to %#011lx\n", gd->relocaddr);
185 			break;
186 		case CORES:
187 			ofnode_for_each_subnode(np, ofnode_path("/cpus")) {
188 				if (ofnode_name_eq(np, "cpu"))
189 					n_cpus++;
190 			}
191 			printf("CPU: " RESET CONFIG_SYS_ARCH " (%d cores, 1 in use)\n", n_cpus);
192 			break;
193 		case MEMORY:
194 			for (int j = 0; j < CONFIG_NR_DRAM_BANKS && gd->bd->bi_dram[j].size; j++)
195 				size += gd->bd->bi_dram[j].size;
196 			printf("Memory:" RESET " ");
197 			print_size(size, "\n");
198 			break;
199 		case STORAGE:
200 		default: {
201 #ifdef CONFIG_BLK
202 			struct udevice *dev;
203 			struct blk_desc *desc;
204 			int ret;
205 
206 			ret = uclass_find_device_by_seq(UCLASS_BLK, line - STORAGE, &dev);
207 			if (!ret && dev) {
208 				desc = dev_get_uclass_plat(dev);
209 				size = desc->lba * desc->blksz;
210 				printf("%4s %d: " RESET, blk_get_uclass_name(desc->uclass_id),
211 					desc->lun);
212 				if (size)
213 					print_size(size, "");
214 				else
215 					printf("No media");
216 			} else if (ret == -ENODEV && (skip_ascii || line > ARRAY_SIZE(logo_lines))) {
217 				break;
218 			}
219 #endif
220 			printf("\n");
221 		}
222 		}
223 	}
224 
225 	printf(RESET "\n\n");
226 
227 	return 0;
228 }
229 
230 U_BOOT_CMD(ufetch, 2, 1, do_ufetch,
231 	   "U-Boot fetch utility",
232 	   "Print information about your device.\n"
233 	   "    -n    Don't print the ASCII logo"
234 );
235